From 80cc8ccf1abd5cdc936a9997917a6c4ed6730b53 Mon Sep 17 00:00:00 2001 From: Barry O'Donovan Date: Sat, 16 May 2020 19:34:34 +0100 Subject: [PATCH] Merge in some very old libraries for great stability --- .gitignore | 8 - application/configs/application.ini.dist | 4 +- composer.json | 21 +- composer.lock | 164 +- library/OSS/API/DAViCal.php | 932 ++++++++++ library/OSS/API/Jabber2d.php | 175 ++ library/OSS/API/RoundCube.php | 346 ++++ library/OSS/API/SOGo.php | 332 ++++ library/OSS/Acl.php | 49 + library/OSS/Array.php | 383 +++++ .../OSS/Asterisk/AMI/ConnectionException.php | 50 + library/OSS/Asterisk/AMI/Exception.php | 50 + library/OSS/Asterisk/AMI/Parser.php | 313 ++++ library/OSS/Asterisk/Exception.php | 50 + library/OSS/Auth/Doctrine2Adapter.php | 180 ++ library/OSS/Auth/DoctrineAdapter.php | 162 ++ library/OSS/Auth/Password.php | 222 +++ library/OSS/Captcha/Image.php | 99 ++ library/OSS/Controller/Action.php | 242 +++ .../Controller/Action/Trait/AsteriskMI.php | 103 ++ library/OSS/Controller/Action/Trait/Auth.php | 120 ++ .../Controller/Action/Trait/AuthRequired.php | 88 + library/OSS/Controller/Action/Trait/Cli.php | 179 ++ .../OSS/Controller/Action/Trait/Doctrine2.php | 191 +++ .../Action/Trait/Doctrine2Cache.php | 99 ++ .../Action/Trait/Doctrine2Frontend.php | 781 +++++++++ .../Controller/Action/Trait/Doctrine2User.php | 131 ++ .../Controller/Action/Trait/Freshbooks.php | 90 + .../OSS/Controller/Action/Trait/License.php | 99 ++ .../OSS/Controller/Action/Trait/Logger.php | 105 ++ .../OSS/Controller/Action/Trait/Mailer.php | 92 + .../OSS/Controller/Action/Trait/Messages.php | 122 ++ .../OSS/Controller/Action/Trait/Namespace.php | 98 ++ library/OSS/Controller/Action/Trait/News.php | 158 ++ .../OSS/Controller/Action/Trait/Paygate.php | 92 + .../Controller/Action/Trait/PdfGenerator.php | 89 + .../Controller/Action/Trait/RememberMe.php | 193 +++ .../OSS/Controller/Action/Trait/Smarty.php | 140 ++ .../OSS/Controller/Action/Trait/StatsD.php | 102 ++ .../Action/Trait/TermsAcceptance.php | 86 + library/OSS/Controller/AuthRequiredAction.php | 70 + library/OSS/Controller/CliAction.php | 82 + library/OSS/Controller/Router/Cli.php | 70 + library/OSS/Controller/Trait/Auth.php | 1112 ++++++++++++ library/OSS/Controller/Trait/Error.php | 132 ++ library/OSS/Controller/Trait/Profile.php | 106 ++ library/OSS/Countries.php | 1122 +++++++++++++ library/OSS/Crypt/Bcrypt.php | 108 ++ library/OSS/Crypt/Exception.php | 50 + library/OSS/Crypt/GibberishAES.php | 129 ++ library/OSS/Crypt/OpenSSL.php | 239 +++ library/OSS/Csv.php | 126 ++ library/OSS/Curl.php | 381 +++++ library/OSS/Date.php | 237 +++ library/OSS/DateTime.php | 242 +++ library/OSS/Debug.php | 186 ++ library/OSS/DiskUtils.php | 103 ++ library/OSS/Doctrine/Exception.php | 47 + library/OSS/Doctrine/Record.php | 63 + .../OSS/Doctrine/Record/WithPreferences.php | 681 ++++++++ .../Doctrine/Record/WithPreferencesTable.php | 68 + library/OSS/Doctrine/Table.php | 64 + library/OSS/Doctrine2/DBAL/Connection.php | 95 ++ library/OSS/Doctrine2/EntitySerializer.php | 223 +++ library/OSS/Doctrine2/Exception.php | 47 + library/OSS/Doctrine2/FirebugProfiler.php | 139 ++ library/OSS/Doctrine2/Utils.php | 81 + library/OSS/Doctrine2/WithPreferences.php | 660 ++++++++ .../WithPreferences/IndexLimitException.php | 52 + library/OSS/Exception.php | 50 + library/OSS/Filter/FileSize.php | 220 +++ library/OSS/Filter/FixAddressline.php | 65 + library/OSS/Filter/FixCompanyname.php | 66 + library/OSS/Filter/FixFirstname.php | 66 + library/OSS/Filter/FixLastname.php | 71 + library/OSS/Filter/Float.php | 61 + library/OSS/Filter/HtmlEntitiesDecode.php | 183 ++ library/OSS/Filter/IPv4.php | 71 + library/OSS/Filter/IPv6.php | 97 ++ library/OSS/Filter/StripLeadingZero.php | 65 + library/OSS/Filter/StripSlashes.php | 59 + library/OSS/Form.php | 76 + library/OSS/Form/Auth.php | 233 +++ library/OSS/Form/Captcha.php | 113 ++ library/OSS/Form/Element/Buttonlink.php | 76 + library/OSS/Form/Element/DatabaseDropdown.php | 170 ++ library/OSS/Form/Element/DateForm.php | 62 + library/OSS/Form/Trait.php | 74 + library/OSS/Form/Trait/CancelLocation.php | 105 ++ library/OSS/Form/Trait/Doctrine1Mapping.php | 97 ++ library/OSS/Form/Trait/Doctrine2.php | 212 +++ library/OSS/Form/Trait/FileSize.php | 95 ++ library/OSS/Form/Trait/GenericElements.php | 67 + library/OSS/Form/Trait/InsertElementFns.php | 131 ++ library/OSS/Form/Trait/IsEdit.php | 82 + library/OSS/Form/User.php | 80 + library/OSS/GeoIP.php | 106 ++ library/OSS/Html.php | 120 ++ library/OSS/Invoice.php | 234 +++ library/OSS/License.php | 79 + library/OSS/License/Abstract.php | 138 ++ library/OSS/License/Exception.php | 50 + library/OSS/License/ExpiredException.php | 50 + library/OSS/License/MD5.php | 91 + library/OSS/Log.php | 88 + library/OSS/Mail.php | 959 +++++++++++ library/OSS/Message.php | 191 +++ library/OSS/Message/Block.php | 121 ++ library/OSS/Message/Pop/Up.php | 97 ++ library/OSS/Net/DNS2.php | 86 + library/OSS/Net/Exception.php | 50 + library/OSS/Net/IPv4.php | 72 + library/OSS/Net/IPv6.php | 239 +++ .../PHPUnit/Extensions/SeleniumTestCase.php | 74 + .../OSS/PaymentProcessor/BaseProcessor.php | 48 + library/OSS/PaymentProcessor/Exception.php | 52 + library/OSS/PaymentProcessor/Realex.php | 1277 ++++++++++++++ .../OSS/PaymentProcessor/Realex/Exception.php | 52 + library/OSS/PaymentProcessor/Realex/Hash.php | 197 +++ .../Realex/Receipt/Exception.php | 134 ++ library/OSS/PaymentProcessor/Stripe.php | 1099 ++++++++++++ library/OSS/Pdf.php | 160 ++ library/OSS/Pdf/PdfLatex.php | 162 ++ library/OSS/Pdf/PdfLatex/Exception.php | 48 + library/OSS/Phone.php | 152 ++ library/OSS/Plugin/Observable.php | 54 + library/OSS/Plugin/Observer.php | 49 + library/OSS/Resource/AsteriskMI.php | 92 + library/OSS/Resource/Auth.php | 81 + library/OSS/Resource/Clickatell.php | 108 ++ library/OSS/Resource/Doctrine.php | 135 ++ library/OSS/Resource/Doctrine2.php | 154 ++ library/OSS/Resource/Doctrine2cache.php | 150 ++ library/OSS/Resource/Freshbooks.php | 92 + library/OSS/Resource/License.php | 80 + library/OSS/Resource/Logger.php | 190 +++ library/OSS/Resource/Mailer.php | 102 ++ library/OSS/Resource/Moduleconfig.php | 138 ++ library/OSS/Resource/Namespace.php | 115 ++ library/OSS/Resource/News.php | 109 ++ library/OSS/Resource/Paygate.php | 91 + library/OSS/Resource/Smarty.php | 124 ++ library/OSS/Resource/StatsD.php | 82 + library/OSS/Resource/View.php | 103 ++ library/OSS/Resource/Zfdebug.php | 124 ++ library/OSS/Service/Clickatell/Abstract.php | 353 ++++ library/OSS/Service/Clickatell/Exception.php | 49 + library/OSS/Service/Clickatell/Http.php | 368 ++++ library/OSS/Service/Freshbooks.php | 1496 +++++++++++++++++ .../Smarty/functions/function.LoginForm.php | 94 ++ .../Smarty/functions/function.OSS_Message.php | 154 ++ .../functions/function.addJSValidator.php | 333 ++++ .../Smarty/functions/function.currency.php | 70 + .../Smarty/functions/function.customdate.php | 72 + .../functions/function.dateDiffDays.php | 67 + .../functions/function.dynamicContent.php | 74 + .../OSS/Smarty/functions/function.genUrl.php | 83 + .../functions/function.includeIfExists.php | 120 ++ .../functions/function.modelOperation.php | 77 + .../OSS/Smarty/functions/function.phpinfo.php | 65 + .../functions/function.secondsToHMS.php | 67 + .../Smarty/functions/function.tmplinclude.php | 121 ++ .../OSS/Smarty/functions/modifier.alnum.php | 62 + .../functions/modifier.debug_print_var.php | 135 ++ .../OSS/Smarty/functions/modifier.escape.php | 139 ++ .../Smarty/functions/modifier.intlmobile.php | 63 + .../OSS/Smarty/functions/modifier.mobile.php | 62 + .../OSS/Smarty/functions/modifier.ossDate.php | 68 + .../functions/modifier.preg_replace.php | 64 + .../OSS/Smarty/functions/modifier.shorten.php | 65 + .../OSS/Smarty/functions/modifier.sizeof.php | 63 + .../functions/modifier.string_format.php | 73 + .../OSS/Smarty/functions/modifier.toInt.php | 63 + .../Smarty/functions/modifier.toValidId.php | 61 + .../prefilter.whitespace_control.php | 65 + library/OSS/StatsD.php | 184 ++ library/OSS/String.php | 363 ++++ library/OSS/Trait/Invoice.php | 314 ++++ library/OSS/Utils.php | 316 ++++ library/OSS/Validate/Exception.php | 47 + library/OSS/Validate/OSSDateTimeValidate.php | 78 + library/OSS/Validate/OSSDateValidate.php | 161 ++ library/OSS/Validate/OSSDnsRecord.php | 98 ++ .../OSS/Validate/OSSDoctrine2Uniqueness.php | 187 +++ .../OSS/Validate/OSSDoctrineUniqueness.php | 178 ++ library/OSS/Validate/OSSDomainName.php | 98 ++ library/OSS/Validate/OSSFloat.php | 77 + library/OSS/Validate/OSSIPv4.php | 77 + library/OSS/Validate/OSSIPv4Cidr.php | 88 + library/OSS/Validate/OSSIPv6.php | 102 ++ library/OSS/Validate/OSSIPv6Cidr.php | 88 + library/OSS/Validate/OSSIdenticalField.php | 192 +++ library/OSS/Validate/OSSIrishTaxNumber.php | 123 ++ library/OSS/Validate/OSSMACAddress.php | 85 + library/OSS/Validate/OSSMobileNumber.php | 79 + library/OSS/Validate/OSSNotRegex.php | 150 ++ library/OSS/Validate/OSSNumericBetween.php | 135 ++ library/OSS/Validate/OSSPSN.php | 135 ++ library/OSS/Validate/OSSPassword.php | 154 ++ library/OSS/Validate/OSSRequiredIf.php | 91 + library/OSS/Validate/OSSTimeValidate.php | 84 + library/OSS/Validate/OSSUrlValidate.php | 80 + library/OSS/View/Helper/Buttonlink.php | 95 ++ library/OSS/View/Helper/DatabaseDropdown.php | 185 ++ library/OSS/View/Helper/DateForm.php | 127 ++ library/OSS/View/Smarty.php | 462 +++++ library/OSS/Yubico.php | 310 ++++ .../Plugin/Debug/Plugin/Doctrine.php | 153 ++ library/Twitter/Form.php | 213 +++ .../Twitter/Form/Decorator/Checkboxlabel.php | 12 + library/Twitter/Form/Decorator/Errors.php | 43 + library/Twitter/Twitter-README.md | 55 + 212 files changed, 34724 insertions(+), 142 deletions(-) create mode 100644 library/OSS/API/DAViCal.php create mode 100644 library/OSS/API/Jabber2d.php create mode 100644 library/OSS/API/RoundCube.php create mode 100644 library/OSS/API/SOGo.php create mode 100644 library/OSS/Acl.php create mode 100644 library/OSS/Array.php create mode 100644 library/OSS/Asterisk/AMI/ConnectionException.php create mode 100644 library/OSS/Asterisk/AMI/Exception.php create mode 100644 library/OSS/Asterisk/AMI/Parser.php create mode 100644 library/OSS/Asterisk/Exception.php create mode 100644 library/OSS/Auth/Doctrine2Adapter.php create mode 100644 library/OSS/Auth/DoctrineAdapter.php create mode 100644 library/OSS/Auth/Password.php create mode 100644 library/OSS/Captcha/Image.php create mode 100644 library/OSS/Controller/Action.php create mode 100644 library/OSS/Controller/Action/Trait/AsteriskMI.php create mode 100644 library/OSS/Controller/Action/Trait/Auth.php create mode 100644 library/OSS/Controller/Action/Trait/AuthRequired.php create mode 100644 library/OSS/Controller/Action/Trait/Cli.php create mode 100644 library/OSS/Controller/Action/Trait/Doctrine2.php create mode 100644 library/OSS/Controller/Action/Trait/Doctrine2Cache.php create mode 100644 library/OSS/Controller/Action/Trait/Doctrine2Frontend.php create mode 100644 library/OSS/Controller/Action/Trait/Doctrine2User.php create mode 100644 library/OSS/Controller/Action/Trait/Freshbooks.php create mode 100644 library/OSS/Controller/Action/Trait/License.php create mode 100644 library/OSS/Controller/Action/Trait/Logger.php create mode 100644 library/OSS/Controller/Action/Trait/Mailer.php create mode 100644 library/OSS/Controller/Action/Trait/Messages.php create mode 100644 library/OSS/Controller/Action/Trait/Namespace.php create mode 100644 library/OSS/Controller/Action/Trait/News.php create mode 100644 library/OSS/Controller/Action/Trait/Paygate.php create mode 100644 library/OSS/Controller/Action/Trait/PdfGenerator.php create mode 100644 library/OSS/Controller/Action/Trait/RememberMe.php create mode 100644 library/OSS/Controller/Action/Trait/Smarty.php create mode 100644 library/OSS/Controller/Action/Trait/StatsD.php create mode 100644 library/OSS/Controller/Action/Trait/TermsAcceptance.php create mode 100644 library/OSS/Controller/AuthRequiredAction.php create mode 100644 library/OSS/Controller/CliAction.php create mode 100644 library/OSS/Controller/Router/Cli.php create mode 100644 library/OSS/Controller/Trait/Auth.php create mode 100644 library/OSS/Controller/Trait/Error.php create mode 100644 library/OSS/Controller/Trait/Profile.php create mode 100644 library/OSS/Countries.php create mode 100644 library/OSS/Crypt/Bcrypt.php create mode 100644 library/OSS/Crypt/Exception.php create mode 100644 library/OSS/Crypt/GibberishAES.php create mode 100644 library/OSS/Crypt/OpenSSL.php create mode 100644 library/OSS/Csv.php create mode 100644 library/OSS/Curl.php create mode 100644 library/OSS/Date.php create mode 100644 library/OSS/DateTime.php create mode 100644 library/OSS/Debug.php create mode 100644 library/OSS/DiskUtils.php create mode 100644 library/OSS/Doctrine/Exception.php create mode 100644 library/OSS/Doctrine/Record.php create mode 100644 library/OSS/Doctrine/Record/WithPreferences.php create mode 100644 library/OSS/Doctrine/Record/WithPreferencesTable.php create mode 100644 library/OSS/Doctrine/Table.php create mode 100644 library/OSS/Doctrine2/DBAL/Connection.php create mode 100644 library/OSS/Doctrine2/EntitySerializer.php create mode 100644 library/OSS/Doctrine2/Exception.php create mode 100644 library/OSS/Doctrine2/FirebugProfiler.php create mode 100644 library/OSS/Doctrine2/Utils.php create mode 100644 library/OSS/Doctrine2/WithPreferences.php create mode 100644 library/OSS/Doctrine2/WithPreferences/IndexLimitException.php create mode 100644 library/OSS/Exception.php create mode 100644 library/OSS/Filter/FileSize.php create mode 100644 library/OSS/Filter/FixAddressline.php create mode 100644 library/OSS/Filter/FixCompanyname.php create mode 100644 library/OSS/Filter/FixFirstname.php create mode 100644 library/OSS/Filter/FixLastname.php create mode 100644 library/OSS/Filter/Float.php create mode 100644 library/OSS/Filter/HtmlEntitiesDecode.php create mode 100644 library/OSS/Filter/IPv4.php create mode 100644 library/OSS/Filter/IPv6.php create mode 100644 library/OSS/Filter/StripLeadingZero.php create mode 100644 library/OSS/Filter/StripSlashes.php create mode 100644 library/OSS/Form.php create mode 100644 library/OSS/Form/Auth.php create mode 100644 library/OSS/Form/Captcha.php create mode 100644 library/OSS/Form/Element/Buttonlink.php create mode 100644 library/OSS/Form/Element/DatabaseDropdown.php create mode 100644 library/OSS/Form/Element/DateForm.php create mode 100644 library/OSS/Form/Trait.php create mode 100644 library/OSS/Form/Trait/CancelLocation.php create mode 100644 library/OSS/Form/Trait/Doctrine1Mapping.php create mode 100644 library/OSS/Form/Trait/Doctrine2.php create mode 100644 library/OSS/Form/Trait/FileSize.php create mode 100644 library/OSS/Form/Trait/GenericElements.php create mode 100644 library/OSS/Form/Trait/InsertElementFns.php create mode 100644 library/OSS/Form/Trait/IsEdit.php create mode 100644 library/OSS/Form/User.php create mode 100644 library/OSS/GeoIP.php create mode 100644 library/OSS/Html.php create mode 100644 library/OSS/Invoice.php create mode 100644 library/OSS/License.php create mode 100644 library/OSS/License/Abstract.php create mode 100644 library/OSS/License/Exception.php create mode 100644 library/OSS/License/ExpiredException.php create mode 100644 library/OSS/License/MD5.php create mode 100644 library/OSS/Log.php create mode 100644 library/OSS/Mail.php create mode 100644 library/OSS/Message.php create mode 100644 library/OSS/Message/Block.php create mode 100644 library/OSS/Message/Pop/Up.php create mode 100644 library/OSS/Net/DNS2.php create mode 100644 library/OSS/Net/Exception.php create mode 100644 library/OSS/Net/IPv4.php create mode 100644 library/OSS/Net/IPv6.php create mode 100644 library/OSS/PHPUnit/Extensions/SeleniumTestCase.php create mode 100644 library/OSS/PaymentProcessor/BaseProcessor.php create mode 100644 library/OSS/PaymentProcessor/Exception.php create mode 100644 library/OSS/PaymentProcessor/Realex.php create mode 100644 library/OSS/PaymentProcessor/Realex/Exception.php create mode 100644 library/OSS/PaymentProcessor/Realex/Hash.php create mode 100644 library/OSS/PaymentProcessor/Realex/Receipt/Exception.php create mode 100644 library/OSS/PaymentProcessor/Stripe.php create mode 100644 library/OSS/Pdf.php create mode 100644 library/OSS/Pdf/PdfLatex.php create mode 100644 library/OSS/Pdf/PdfLatex/Exception.php create mode 100644 library/OSS/Phone.php create mode 100644 library/OSS/Plugin/Observable.php create mode 100644 library/OSS/Plugin/Observer.php create mode 100644 library/OSS/Resource/AsteriskMI.php create mode 100644 library/OSS/Resource/Auth.php create mode 100644 library/OSS/Resource/Clickatell.php create mode 100644 library/OSS/Resource/Doctrine.php create mode 100644 library/OSS/Resource/Doctrine2.php create mode 100644 library/OSS/Resource/Doctrine2cache.php create mode 100644 library/OSS/Resource/Freshbooks.php create mode 100644 library/OSS/Resource/License.php create mode 100644 library/OSS/Resource/Logger.php create mode 100644 library/OSS/Resource/Mailer.php create mode 100644 library/OSS/Resource/Moduleconfig.php create mode 100644 library/OSS/Resource/Namespace.php create mode 100644 library/OSS/Resource/News.php create mode 100644 library/OSS/Resource/Paygate.php create mode 100644 library/OSS/Resource/Smarty.php create mode 100644 library/OSS/Resource/StatsD.php create mode 100644 library/OSS/Resource/View.php create mode 100644 library/OSS/Resource/Zfdebug.php create mode 100644 library/OSS/Service/Clickatell/Abstract.php create mode 100644 library/OSS/Service/Clickatell/Exception.php create mode 100644 library/OSS/Service/Clickatell/Http.php create mode 100644 library/OSS/Service/Freshbooks.php create mode 100644 library/OSS/Smarty/functions/function.LoginForm.php create mode 100644 library/OSS/Smarty/functions/function.OSS_Message.php create mode 100644 library/OSS/Smarty/functions/function.addJSValidator.php create mode 100644 library/OSS/Smarty/functions/function.currency.php create mode 100644 library/OSS/Smarty/functions/function.customdate.php create mode 100644 library/OSS/Smarty/functions/function.dateDiffDays.php create mode 100644 library/OSS/Smarty/functions/function.dynamicContent.php create mode 100644 library/OSS/Smarty/functions/function.genUrl.php create mode 100644 library/OSS/Smarty/functions/function.includeIfExists.php create mode 100644 library/OSS/Smarty/functions/function.modelOperation.php create mode 100644 library/OSS/Smarty/functions/function.phpinfo.php create mode 100644 library/OSS/Smarty/functions/function.secondsToHMS.php create mode 100644 library/OSS/Smarty/functions/function.tmplinclude.php create mode 100644 library/OSS/Smarty/functions/modifier.alnum.php create mode 100644 library/OSS/Smarty/functions/modifier.debug_print_var.php create mode 100644 library/OSS/Smarty/functions/modifier.escape.php create mode 100644 library/OSS/Smarty/functions/modifier.intlmobile.php create mode 100644 library/OSS/Smarty/functions/modifier.mobile.php create mode 100644 library/OSS/Smarty/functions/modifier.ossDate.php create mode 100644 library/OSS/Smarty/functions/modifier.preg_replace.php create mode 100644 library/OSS/Smarty/functions/modifier.shorten.php create mode 100644 library/OSS/Smarty/functions/modifier.sizeof.php create mode 100644 library/OSS/Smarty/functions/modifier.string_format.php create mode 100644 library/OSS/Smarty/functions/modifier.toInt.php create mode 100644 library/OSS/Smarty/functions/modifier.toValidId.php create mode 100644 library/OSS/Smarty/functions/prefilter.whitespace_control.php create mode 100644 library/OSS/StatsD.php create mode 100644 library/OSS/String.php create mode 100644 library/OSS/Trait/Invoice.php create mode 100644 library/OSS/Utils.php create mode 100644 library/OSS/Validate/Exception.php create mode 100644 library/OSS/Validate/OSSDateTimeValidate.php create mode 100644 library/OSS/Validate/OSSDateValidate.php create mode 100644 library/OSS/Validate/OSSDnsRecord.php create mode 100644 library/OSS/Validate/OSSDoctrine2Uniqueness.php create mode 100644 library/OSS/Validate/OSSDoctrineUniqueness.php create mode 100644 library/OSS/Validate/OSSDomainName.php create mode 100644 library/OSS/Validate/OSSFloat.php create mode 100644 library/OSS/Validate/OSSIPv4.php create mode 100644 library/OSS/Validate/OSSIPv4Cidr.php create mode 100644 library/OSS/Validate/OSSIPv6.php create mode 100644 library/OSS/Validate/OSSIPv6Cidr.php create mode 100644 library/OSS/Validate/OSSIdenticalField.php create mode 100644 library/OSS/Validate/OSSIrishTaxNumber.php create mode 100644 library/OSS/Validate/OSSMACAddress.php create mode 100644 library/OSS/Validate/OSSMobileNumber.php create mode 100644 library/OSS/Validate/OSSNotRegex.php create mode 100644 library/OSS/Validate/OSSNumericBetween.php create mode 100644 library/OSS/Validate/OSSPSN.php create mode 100644 library/OSS/Validate/OSSPassword.php create mode 100644 library/OSS/Validate/OSSRequiredIf.php create mode 100644 library/OSS/Validate/OSSTimeValidate.php create mode 100644 library/OSS/Validate/OSSUrlValidate.php create mode 100644 library/OSS/View/Helper/Buttonlink.php create mode 100644 library/OSS/View/Helper/DatabaseDropdown.php create mode 100644 library/OSS/View/Helper/DateForm.php create mode 100644 library/OSS/View/Smarty.php create mode 100644 library/OSS/Yubico.php create mode 100644 library/OSS/ZFDebug/Controller/Plugin/Debug/Plugin/Doctrine.php create mode 100644 library/Twitter/Form.php create mode 100644 library/Twitter/Form/Decorator/Checkboxlabel.php create mode 100644 library/Twitter/Form/Decorator/Errors.php create mode 100644 library/Twitter/Twitter-README.md diff --git a/.gitignore b/.gitignore index a9033c4..574cfad 100644 --- a/.gitignore +++ b/.gitignore @@ -7,14 +7,6 @@ *.DS_Store application/configs/application.ini application/configs/application.ini.old -library/Twitter -library/Bootstrap-Zend-Framework.git -library/Bootstrap-Zend-Framework -library/Doctrine -library/OSS-Framework.git -library/Smarty -library/ZFDebug -library/Zend public/.htaccess var/log/[1-9]* var/log/vimbadmin.log* diff --git a/application/configs/application.ini.dist b/application/configs/application.ini.dist index 51cb871..bb6f7f8 100644 --- a/application/configs/application.ini.dist +++ b/application/configs/application.ini.dist @@ -592,7 +592,7 @@ appnamespace = "ViMbAdmin" temporary_directory = APPLICATION_PATH "/../var/tmp" -pluginPaths.OSS_Resource = APPLICATION_PATH "/../vendor/opensolutions/oss-framework/src/OSS/Resource" +pluginPaths.OSS_Resource = APPLICATION_PATH "/../library/OSS/Resource" pluginPaths.ViMbAdmin_Resource = APPLICATION_PATH "/../library/ViMbAdmin/Resource" mini_js = 1 @@ -670,7 +670,7 @@ resources.smarty.compiled = APPLICATION_PATH "/../var/templates_c" resources.smarty.cache = APPLICATION_PATH "/../var/cache" resources.smarty.config = APPLICATION_PATH "/configs/smarty" resources.smarty.plugins[] = APPLICATION_PATH "/../library/ViMbAdmin/Smarty/functions" -resources.smarty.plugins[] = APPLICATION_PATH "/../vendor/opensolutions/oss-framework/src/OSS/Smarty/functions" +resources.smarty.plugins[] = APPLICATION_PATH "/../library/OSS/Smarty/functions" resources.smarty.plugins[] = APPLICATION_PATH "/../vendor/smarty/smarty/libs/plugins" resources.smarty.plugins[] = APPLICATION_PATH "/../vendor/smarty/smarty/libs/sysplugins" resources.smarty.debugging = 0 diff --git a/composer.json b/composer.json index afb41bb..391f906 100644 --- a/composer.json +++ b/composer.json @@ -19,21 +19,24 @@ "role": "Development Team" } ], - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/opensolutions/Bootstrap-Zend-Framework" - } - ], "require": { "php": ">=5.4.0", "doctrine/orm": "2.6.*", - "opensolutions/oss-framework": "dev-master", "shardj/zf1-future": "1.*", - "opensolutions/minify": "1.*", - "komola/bootstrap-zend-framework": "dev-master", "smarty/smarty": "~3.1" }, + "require-dev": { + "opensolutions/minify": "1.*" + }, + "autoload": { + "psr-0": { + "OSS_": "library/", + "Twitter_": "library" + } + }, + "include-path": [ + "library/" + ], "support": { "issues": "https://github.com/opensolutions/ViMbAdmin/issues", "wiki": "https://github.com/opensolutions/ViMbAdmin/wiki", diff --git a/composer.lock b/composer.lock index 5d821ac..a2c054e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "74f28618f83e50c53982304b5b4cb3fc", + "content-hash": "97e52f36368338b6aa3f8c629a25ea26", "packages": [ { "name": "doctrine/annotations", @@ -974,123 +974,6 @@ ], "time": "2020-03-27T11:06:43+00:00" }, - { - "name": "komola/bootstrap-zend-framework", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/opensolutions/Bootstrap-Zend-Framework.git", - "reference": "f81b60c1f477f4fbe4266b6d811a3eff9491f285" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opensolutions/Bootstrap-Zend-Framework/zipball/f81b60c1f477f4fbe4266b6d811a3eff9491f285", - "reference": "f81b60c1f477f4fbe4266b6d811a3eff9491f285", - "shasum": "" - }, - "require": { - "zendframework/zendframework1": "1.*" - }, - "type": "library", - "autoload": { - "psr-0": { - "Twitter_": "library" - } - }, - "include-path": [ - "library/" - ], - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Sebastian Hoitz", - "email": "hoitz@komola.de", - "homepage": "http://komola.de" - } - ], - "description": "Twitter bootstrap for ZF1", - "support": { - "source": "https://github.com/opensolutions/Bootstrap-Zend-Framework/tree/master" - }, - "time": "2014-02-03T20:52:54+00:00" - }, - { - "name": "opensolutions/minify", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/opensolutions/Minify.git", - "reference": "7463254585f3f145bb7e63e186c919591848f680" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opensolutions/Minify/zipball/7463254585f3f145bb7e63e186c919591848f680", - "reference": "7463254585f3f145bb7e63e186c919591848f680", - "shasum": "" - }, - "bin": [ - "minify.php" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Barry O'Donovan", - "email": "barry@opensolutions.ie", - "homepage": "http://www.barryodonovan.com/", - "role": "Lead Developer" - } - ], - "description": "A JS and CSS minifier tool (with additional benefits for projects using template engines such as Smarty)", - "keywords": [ - "minifier", - "minify" - ], - "time": "2014-02-05T09:45:35+00:00" - }, - { - "name": "opensolutions/oss-framework", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/opensolutions/OSS-Framework.git", - "reference": "f82b4f5dbfbcd8923c145cafe7ada1ade35eba96" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opensolutions/OSS-Framework/zipball/f82b4f5dbfbcd8923c145cafe7ada1ade35eba96", - "reference": "f82b4f5dbfbcd8923c145cafe7ada1ade35eba96", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "zendframework/zendframework1": "~1.12" - }, - "require-dev": { - "phpunit/phpunit": "~4" - }, - "type": "library", - "autoload": { - "psr-0": { - "OSS_": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Open Solutions' extension to Zend Framework 1.", - "keywords": [ - "opensolutions", - "oss" - ], - "time": "2017-02-15T11:28:10+00:00" - }, { "name": "psr/container", "version": "1.0.0", @@ -1541,13 +1424,48 @@ "time": "2019-11-18T17:27:11+00:00" } ], - "packages-dev": [], + "packages-dev": [ + { + "name": "opensolutions/minify", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/opensolutions/Minify.git", + "reference": "7463254585f3f145bb7e63e186c919591848f680" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opensolutions/Minify/zipball/7463254585f3f145bb7e63e186c919591848f680", + "reference": "7463254585f3f145bb7e63e186c919591848f680", + "shasum": "" + }, + "bin": [ + "minify.php" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Barry O'Donovan", + "email": "barry@opensolutions.ie", + "homepage": "http://www.barryodonovan.com/", + "role": "Lead Developer" + } + ], + "description": "A JS and CSS minifier tool (with additional benefits for projects using template engines such as Smarty)", + "keywords": [ + "minifier", + "minify" + ], + "time": "2014-02-05T09:45:35+00:00" + } + ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "opensolutions/oss-framework": 20, - "komola/bootstrap-zend-framework": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/library/OSS/API/DAViCal.php b/library/OSS/API/DAViCal.php new file mode 100644 index 0000000..172a668 --- /dev/null +++ b/library/OSS/API/DAViCal.php @@ -0,0 +1,932 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * A DAViCal API via direct database manipulation. + * + * @see http://davical.org/ + * + * @category OSS + * @package OSS_API + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_API_DAViCal +{ + //Use DBAL connections for database manipulation + use OSS_Doctrine2_DBAL_Connection; + + /** + * Privileges constants which refelects DAViCal database scheme + */ + const PRIVILEGES_RW = '000000001111111011100111'; + const PRIVILEGES_RO = '000000000001001000100001'; + const PRIVILEGES_NONE = '000000000000000000000000'; + const PRIVILEGES_BLOCK = '000000000001110000000000'; + + /** + * Names of Privieleges + */ + public static $PRIVILEGES = [ + self::PRIVILEGES_RW => "Read / Write", + self::PRIVILEGES_RO => "Read Only", + self::PRIVILEGES_NONE => "No Actions" + ]; + + /** + * Principal type IDs + */ + const PRINCIPAL_TYPE_PERSON = 1; + const PRINCIPAL_TYPE_RESOURCE = 2; + const PRINCIPAL_TYPE_GROUP = 3; + + /** + * Collection types + */ + const COLLECTION_TYPE_CALENDAR = 'calendar'; + const COLLECTION_TYPE_ADDRESSBOOK = 'addressbook'; + + /** + * Password hashing methods + */ + const PASSWORD_HASH_SSHA = "hash"; + const PASSWORD_HASH_MD5 = "md5"; + const PASSWORD_HASH_PLAIN = "plain"; + + + /** + * Constructor + * Initialize DBAL connection. + */ + public function __construct( $dbparams ) + { + $this->getDBAL( $dbparams ); + } + + /** + * Get all users registered in the database as an array. + * + * Returns: + * array (size=n) + * 0 => + * 'user_no' => int 1 + * 'active' => boolean true + * 'email_ok' => string '2012-12-07 00:00:00+00' (length=22) + * 'joined' => string '2012-12-07 11:49:55.231231+00' (length=29) + * 'updated' => string '2012-12-07 13:27:31.698669+00' (length=29) + * 'last_used' => string '2012-12-11 10:01:29.831451+00' (length=29) + * 'username' => string 'usrname' (length=7) + * 'password' => string 'hashed' (length=9) + * 'fullname' => string 'Name susrname' (length=13) + * 'email' => string 'example@example.ie' (length=18) + * 'config_data' => null + * 'date_format_type' => string 'E' (length=1) + * 'locale' => string 'en' (length=2) + * 1 => + * ... + * + * @return array All users registered in the database + */ + public function getAllUsers() + { + return $this->getDBAL()->fetchAll( "SELECT * FROM principal" ); + } + + /** + * Create user. + * + * params: + * array + * 'active' => boolean + * 'email_ok' => string date time e.g.'2012-12-07 00:00:00+00' + * 'joined' => string date time e.g.'2012-12-07 00:00:00+00' + * 'updated' => string date time e.g.'2012-12-07 00:00:00+00' + * 'last_used' => string date time e.g.'2012-12-07 00:00:00+00' + * 'username' => string mandatory + * 'password' => string + * 'fullname' => string + * 'email' => string + * 'config_data' => null + * 'date_format_type' => string e.g. 'E' + * 'locale' => string e.g. 'en' + * + * Returns: + * array + * 'user_no' => int 1 + * 'active' => boolean true + * 'email_ok' => string '2012-12-07 00:00:00+00' (length=22) + * 'joined' => string '2012-12-07 11:49:55.231231+00' (length=29) + * 'updated' => string '2012-12-07 13:27:31.698669+00' (length=29) + * 'last_used' => string '2012-12-11 10:01:29.831451+00' (length=29) + * 'username' => string 'usrname' (length=7) + * 'password' => string 'encrypted' (length=9) + * 'fullname' => string 'Name susrname' (length=13) + * 'email' => string 'example@example.ie' (length=18) + * 'config_data' => null + * 'date_format_type' => string 'E' (length=1) + * 'locale' => string 'en' (length=2) + * + * @param array $params User parameters + * @return array of user details + */ + public function createUser( $params ) + { + $this->getDBAL()->insert( 'usr', $params ); + return $this->getDBAL()->fetchAssoc( "SELECT * FROM usr WHERE username = '{$params['username']}'" ); + } + + /** + * Sets user password. + * + * @param int $user_id User id ( user_no ) + * @param string $hashed_password Hashed pasword. + * @return bool ture if success + */ + public function setUserPassword( $user_id, $hashed_password ) + { + $values = [ + 'password' => $hashed_password, + 'updated' => 'now()' + ]; + return $this->getDBAL()->update( 'usr', $values , [ 'user_no' => $user_id ] ); + } + + /** + * Sets user active state. + * + * @param int $user_id User id ( user_no ) + * @param bool $active Active state true for active, false for inactive. + * @return bool ture if success + */ + public function setUserActiveState( $user_id, $active ) + { + $values = [ + 'active' => $active ? "TRUE" : "FALSE", + 'updated' => 'now()' + ]; + return $this->getDBAL()->update( 'usr', $values , [ 'user_no' => $user_id ] ); + } + + /** + * Removes users + * + * @param int $user_id User id to remove (user_no) + * @return bool true if removed. + */ + public function removeUser( $user_id ) + { + return $this->getDBAL()->delete( 'usr', [ 'user_no' => $user_id ] ); + } + + /** + * Creates principal and dreturns principles data. + * + * Returns: + * array (size=5) + * 'principal_id' => int 1 + * 'type_id' => int 1 + * 'user_no' => int 1 + * 'displayname' => string 'DAViCal Administrator' (length=21) + * 'default_privileges' => string '000000000000000000000000' (length=24) + * + * + * @param array $user Array of user details. + * @param int $type Principal Type id, by default principal type is person. + * @pram string $privileges Default prinicpals privileges by default is read/write. + * @return array of prinicpal details + */ + public function createPrincipal( $user, $type, $privileges = self::PRIVILEGES_NONE ) + { + $params = [ + 'type_id' => $type, + 'user_no' => $user[ 'user_no' ], + 'displayname' => $user[ 'fullname' ], + 'default_privileges' => $privileges + ]; + + $this->getDBAL()->insert( 'principal', $params ); + return $this->getDBAL()->fetchAssoc( "SELECT * FROM principal WHERE user_no = '{$user['user_no']}'" ); + } + + /** + * Creates user, principal and calendar. + * + * Takes same parameters as $this->createUser(); + * Returns an array wich contains user, principal and calendar collection data. + * array [ + * 'user' => array users data, + * 'principal' => array principals data, + * ] + * + * @param array $params User parameters, sames as creatUser parameters. + * @param int $type Principle type id + * @return array Array wit data of user, principal and calendar collection. + * + * @see createUser() + * @see createPrincipal() + */ + public function createPrincipalUser( $params, $type = self::PRINCIPAL_TYPE_PERSON ) + { + $this->getDBAL()->beginTransaction(); + try + { + $user = $this->createUser( $params ); + $principal = $this->createPrincipal( $user, $type ); + + $this->getDBAL()->commit(); + } + catch( Exception $e ) + { + $this->getDBAL()->rollback(); + throw $e; + } + + return [ "user" => $user, "principal" => $principal ]; + } + + /** + * Creates colenction and returns it data. + * + * Returns: + * array (size=17) + * 'user_no' => int 1024 + * 'parent_container' => string '/nbdavical/' (length=11) + * 'dav_name' => string '/nbdavical/calendar/' (length=20) + * 'dav_etag' => string '-1' (length=2) + * 'dav_displayname' => string 'Davical User calendar' (length=21) + * 'is_calendar' => boolean true + * 'created' => string '2012-12-13 11:39:07.767205+00' (length=29) + * 'modified' => string '2012-12-13 11:39:07.767205+00' (length=29) + * 'public_events_only' => boolean false + * 'publicly_readable' => boolean false + * 'collection_id' => int 1034 + * 'default_privileges' => null + * 'is_addressbook' => boolean false + * 'resourcetypes' => string '' (length=60) + * 'schedule_transp' => string 'opaque' (length=6) + * 'timezone' => null + * 'description' => string '' (length=0) + * + * @param array $user Davical user data + * @param string $type Collection type + * @param string $name Calendars name + * @param string|null $privileges Default privileges to access calendar + * @param bool $public_events_only Flag to define if calendar contains only public event + * @param bool $publicly_readable Flat to define publicly readable status + * @param int|null $timezone Time zone id. + * @param string $description Calendar description. + * @return array + */ + public function createCollection( $user, $type, $name = null, $privileges = null, $public_events_only = false, $publicly_readable = false, $timezone = null, $description = "" ) + { + if( $type == self::COLLECTION_TYPE_CALENDAR ) + $calendar = true; + else if( $type == self::COLLECTION_TYPE_ADDRESSBOOK ) + $calendar = false; + else throw new OSS_Exception( "Unknown collection type." ); + + if( !$name ) + $name = $calendar ? $user[ "fullname" ] . " calendar" : $user[ "fullname" ] . " addressbook"; + + $url_name = preg_replace( "/[^a-z0-9]/", '-', strtolower( $name ) ); + $dav_name = sprintf( "/%s/%s/", $user[ 'username' ], $url_name ); + if( $calendar ) + $last = $this->getDBAL()->fetchColumn( "SELECT dav_name FROM collection WHERE user_no = {$user[ 'user_no' ]} AND is_calendar = TRUE AND dav_name LIKE '{$dav_name}%' ORDER BY collection_id DESC LIMIT 1" ); + else + $last = $this->getDBAL()->fetchColumn( "SELECT dav_name FROM collection WHERE user_no = {$user[ 'user_no' ]} AND is_addressbook = TRUE AND dav_name LIKE '{$dav_name}%' ORDER BY collection_id DESC LIMIT 1" ); + + if( $last ) + { + $last = explode( "/", $last ); + $last = (int) OSS_Filter_Float::filter( $last[2] ) + 1; + $dav_name = sprintf( "/%s/%s%s/", $user[ 'username' ], $url_name, $last ); + } + + $params = [ + 'user_no' => $user[ 'user_no' ], + 'parent_container' => "/{$user[ 'username' ]}/", + 'dav_etag' => -1, + 'dav_name' => $dav_name, + 'dav_displayname' => $name, + 'is_calendar' => $calendar ? 1 : 0, + 'created' => 'now()', + 'modified' => 'now()', + 'public_events_only' => $public_events_only ? 1 : 0, + 'publicly_readable' => $publicly_readable ? 1 : 0, + 'default_privileges' => $privileges, + 'is_addressbook' => !$calendar ? 1 : 0, + 'resourcetypes' => sprintf( "", $calendar ? "caldav": "carddav", $calendar ? "calendar": "addressbook" ), + 'timezone' => $timezone, + 'description' => $description + ]; + + if( $this->getDBAL()->insert( 'collection', $params ) ) + { + return $this->getDBAL()->fetchAssoc( "SELECT * FROM collection WHERE user_no = '{$user['user_no']}' AND dav_name = '{$params['dav_name']}'" ); + } + else + return false; + } + + /** + * Removes collection + * + * @param int $collection_id Collection id to remove + * @return bool true if removed. + */ + public function removeCollection( $collection_id ) + { + return $this->getDBAL()->delete( 'collection', [ 'collection_id' => $collection_id ] ); + } + + /** + * Creates calendar and returns calendar data. + * + * Returns same array structure as createCollection + * + * If principal has delegated principals, function iterates all of them and blocks access to new calendar. + * + * @param array $user Davical user data + * @param string $name Calendars name + * @param string|null $privileges Default privileges to access calendar + * @param bool $public_events_only Flag to define if calendar contains only public event + * @param bool $publicly_readable Flat to define publicly readable status + * @param int|null $timezone Time zone id. + * @param string $description Calendar description. + * + * @see createCollection() + */ + public function createCalendar( $user, $name = null, $privileges = null, $public_events_only = false, $publicly_readable = false, $timezone = null, $description = "" ) + { + $calendar = $this->createCollection( $user, self::COLLECTION_TYPE_CALENDAR, $name, $privileges, $public_events_only, $publicly_readable, $timezone, $description ); + + $pids = $this->getGrantedToPrincipalsIds( $user['user_no'] ); + if( $pids && $calendar ) + { + foreach( $pids as $pid ) + { + $duser = $this->getPrincipalById( $pid['to_principal'] ); + if( $duser ) + $this->grantPrivileges( $duser['user_no'], self::PRIVILEGES_BLOCK, null, $calendar['collection_id'] ); + } + } + + return $calendar; + } + + /** + * Shares / delegates calendar + * + * Then sharing calendar then function iterate all users delegate principals if new calendar's owner + * is already in list then it just update privileges. Otherwise it grants principal privileges to read only + * then iterate to all calendars and block the access for them, and finally set correct privileges to given + * calendar. + * + * @param array $user Davical user data + * @param string $description Calendar description. + * @return bool + */ + public function shareCalendar( $user_no, $collection_id, $privileges ) + { + $calendar = $this->getCalendarById( $collection_id ); + $principal = $this->getPrincipalByUserId( $user_no ); + if( !$calendar || !$principal ) + return false; + $dprincipal = $this->getPrincipalByUserId( $calendar['user_no'] ); + $pids = $this->getGrantedToPrincipalsIds( $calendar['user_no'] ); + + $usr_del = false; + if( $pids ) + { + foreach( $pids as $pid ) + { + if( $pid['to_principal'] == $principal['principal_id'] ) + { + $usr_del = true; + break; + } + } + } + + if( !$usr_del ) + { + $ret = $this->grantPrivileges( $principal['user_no'], self::PRIVILEGES_RO, $dprincipal['user_no'] ); + if( !$ret ) + return false; + $cals = $this->getCalendarsByUserId( $calendar['user_no'] ); + if( $cals ) + { + foreach( $cals as $cal ) + { + $this->grantPrivileges( $principal['user_no'], self::PRIVILEGES_BLOCK, null, $cal['collection_id'] ); + } + } + + } + + $this->removeGrantPrivileges( $principal['user_no'], null, $calendar['collection_id'] ); + return $this->grantPrivileges( $principal['user_no'], $privileges, null, $calendar['collection_id'] ); + } + + /** + * Unshare calendar. + * + * Sets existent privileges to PRIVILEGES_BLOCK. + * + * @param array $user Davical user data + * @param string $description Calendar description. + * @return bool + */ + public function unshareCalendar( $user_no, $collection_id ) + { + $calendar = $this->getCalendarById( $collection_id ); + $principal = $this->getPrincipalByUserId( $user_no ); + if( !$calendar || !$principal ) + return false; + $dprincipal = $this->getPrincipalByUserId( $calendar['user_no'] ); + $pids = $this->getGrantedToPrincipalsIds( $calendar['user_no'] ); + + $this->removeGrantPrivileges( $principal['user_no'], null, $calendar['collection_id'] ); + return $this->grantPrivileges( $principal['user_no'], self::PRIVILEGES_BLOCK, null, $calendar['collection_id'] ); + } + + /** + * Removes calendar + * + * Calls removeCollection() method + * + * @param int $collection_id Collection id to remove + * @return bool true if removed. + * @see removeCollection() + */ + public function removeCalendar( $collection_id ) + { + return $this->removeCollection( $collection_id ); + } + + /** + * Creates user, principal and calendar. + * + * Takes same parameters as $this->createUser(); + * Returns an array wich contains user, principal and calendar collection data. + * array [ + * 'user' => array users data, + * 'principal' => array principals data, + * 'calendar' => array calendar collection data + * ] + * + * @param array $params User parameters, sames as creatUser parameters. + * @param string $cname Calendar name + * @param int $type Principle type id + * @return array Array wit data of user, principal and calendar collection. + * + * @see createUser() + * @see createPrincipal() + * @see createCalendar() + */ + public function createCalendarUser( $params, $cname = null, $type = self::PRINCIPAL_TYPE_PERSON ) + { + $this->getDBAL()->beginTransaction(); + try + { + $user = $this->createUser( $params ); + $principal = $this->createPrincipal( $user, $type ); + $calendar = $this->createCalendar( $user, $cname ); + + $this->getDBAL()->commit(); + } + catch( Exception $e ) + { + $this->getDBAL()->rollback(); + throw $e; + } + + return [ "user" => $user, "principal" => $principal, "calendar" => $calendar ]; + } + + /** + * Grants privileges to principal or collection + * + * Grants privleges to principal if by_user_id is set or grant privileges to collection if by_collection_id is set. + * Users IDs should be passed, principal IDs will be loaded from database. + * + * NOTICE: $by_user_id or $by_collection_id are mandatory params, if both is null it will thorw an Exception. + * + * @param int $to_user_id Users for who privleges will be grant. + * @param string Privileges to grant. + * @param int $by_user_id User by who privileges will be grant. + * @param int $by_collection_id Collection by which privileges will be grant. + * @param bool $is_group Flag if to user is group + * @return bool + * @throws OSS_Execpion if $by_user_id and $by_collection_id is null + */ + public function grantPrivileges( $to_user_id, $privileges, $by_user_id = null, $by_collection_id = null, $is_group = null ) + { + if( !$by_user_id && !$by_collection_id ) + throw new OSS_Execption( "Missing mandatory arguments by user id or collection id" ); + + $to_principal = $this->getDBAL()->fetchAssoc( "SELECT principal_id as id FROM principal WHERE user_no = {$to_user_id}" ); + if( $by_user_id ) + $by_principal = $this->getDBAL()->fetchAssoc( "SELECT principal_id as id FROM principal WHERE user_no = {$by_user_id}" ); + + $params = [ + 'by_principal' => $by_user_id ? $by_principal['id'] : null, + 'to_principal' => $to_principal['id'], + 'privileges' => $privileges, + 'by_collection' => $by_collection_id, + 'is_group' => $is_group + ]; + + return $this->getDBAL()->insert( 'grants', $params ); + } + + /** + * Remove grant privileges + * + * Grants privleges for prinicipal to collection or for principal to principal + * Users IDs should be passed, principal IDs will be loaded from database. + * + * NOTICE: $by_user_id or $by_collection_id are mandatory params, if both is null it will thorw an Exception. + * + * @param int $to_user_id Users for who privleges will be removed. + * @param int $by_user_id User by who privileges will be removed. + * @param int $collection_id Collection by which privileges will be removed. + * @return bool + * @throws OSS_Execpion if $by_user_id and $by_collection_id is null + */ + public function removeGrantPrivileges( $to_user_id, $by_user_id = null, $by_collection_id = null ) + { + if( !$by_user_id && !$by_collection_id ) + throw new OSS_Execption( "Missing mandatory arguments by user id or collection id" ); + + $to_principal = $this->getDBAL()->fetchAssoc( "SELECT principal_id as id FROM principal WHERE user_no = {$to_user_id}" ); + $params = [ 'to_principal' => $to_principal['id'] ]; + + if( $by_collection_id ) + $params[ 'by_collection' ] = $by_collection_id; + + if( $by_user_id ) + { + $by_principal = $this->getDBAL()->fetchAssoc( "SELECT principal_id as id FROM principal WHERE user_no = {$by_user_id}" ); + $params[ 'by_principal'] = $by_user_id; + } + + return $this->getDBAL()->delete( 'grants', $params ); + } + + /** + * Update grant privileges to principal or collection + * + * Updated grants privleges to principal if by_user_id is set or grant privileges to collection if by_collection_id is set. + * Users IDs should be passed, principal IDs will be loaded from database. + * + * NOTICE: $by_user_id or $by_collection_id are mandatory params, if both is null it will thorw an Exception. + * + * @param int $to_user_id Users for who privleges will be updated. + * @param string Privileges to set. + * @param int $by_user_id User by who privileges will be updated. + * @param int $by_collection_id Collection by which privileges will be updated. + * @return bool + * @throws OSS_Execpion if $by_user_id and $by_collection_id is null + */ + public function updateGrantPrivileges( $to_user_id, $privileges, $by_user_id = null, $by_collection_id = null ) + { + if( !$by_user_id && !$by_collection_id ) + throw new OSS_Execption( "Missing mandatory arguments by user id or collection id" ); + + $to_principal = $this->getDBAL()->fetchAssoc( "SELECT principal_id as id FROM principal WHERE user_no = {$to_user_id}" ); + $params = [ + + 'to_principal' => $to_principal['id'], + 'by_collection' => $by_collection_id + ]; + if( $by_user_id ) + { + $by_principal = $this->getDBAL()->fetchAssoc( "SELECT principal_id as id FROM principal WHERE user_no = {$by_user_id}" ); + $params[ 'by_principal'] = $by_user_id; + } + + return $this->getDBAL()->update( 'grants', [ 'privileges' => $privileges ], $params ); + } + + /** + * Hash password for davical user. + * + * It hashes three davical supported types hash: + * * First and most unsecured hash is plain it just add two start infont of password. + * * Second is md5 result is ** (where is a random series of characters not including '*') + * then the rest of the string is a hash of (password + salt), i.e. a salted hash. + * * Third is SSHA result is is "**" and the + * is "{SSHA}". Read the code in /usr/share/awl/inc/AWLUtilities.php if you want to + * understand that format more deeply! + * + * @param string $password Users password to login. + * @param string $method Hash method + * @return string return hashed string. + */ + public static function hashPassword( $password, $method ) + { + $salt = OSS_String::salt( 9 ); + if( $method == self::PASSWORD_HASH_PLAIN ) + return "**" . $password; + else if( $method == self::PASSWORD_HASH_MD5 ) + return sprintf( "*%s*%s", $salt, md5( $password . $salt ) ); + else if( $method == self::PASSWORD_HASH_SSHA ) + return sprintf( "*%s*{SSHA}%s", $salt, base64_encode( sha1( $password . $salt, true ) . $salt ) ); + else throw new OSS_Exception( 'Hash password method is unknown' ); + } + + /** + * Gets user by Id ( user_no ) + * + * Returns: + * array (size=13) + * 'user_no' => int 1 + * 'active' => boolean true + * 'email_ok' => string '2012-12-07 00:00:00+00' (length=22) + * 'joined' => string '2012-12-07 11:49:55.231231+00' (length=29) + * 'updated' => string '2012-12-07 13:27:31.698669+00' (length=29) + * 'last_used' => string '2012-12-11 10:01:29.831451+00' (length=29) + * 'username' => string 'usrname' (length=7) + * 'password' => string 'encrypted' (length=9) + * 'fullname' => string 'Name susrname' (length=13) + * 'email' => string 'example@example.ie' (length=18) + * 'config_data' => null + * 'date_format_type' => string 'E' (length=1) + * 'locale' => string 'en' (length=2) + * + * @param int $user_id User id (user_no) + * @return array + */ + public function getUserById( $user_id ) + { + return $this->getDBAL()->fetchAssoc( "SELECT * FROM usr WHERE user_no = {$user_id}" ); + } + + /** + * Gets user by username + * + * Returns: + * array (size=13) + * 'user_no' => int 1 + * 'active' => boolean true + * 'email_ok' => string '2012-12-07 00:00:00+00' (length=22) + * 'joined' => string '2012-12-07 11:49:55.231231+00' (length=29) + * 'updated' => string '2012-12-07 13:27:31.698669+00' (length=29) + * 'last_used' => string '2012-12-11 10:01:29.831451+00' (length=29) + * 'username' => string 'usrname' (length=7) + * 'password' => string 'encrypted' (length=9) + * 'fullname' => string 'Name susrname' (length=13) + * 'email' => string 'example@example.ie' (length=18) + * 'config_data' => null + * 'date_format_type' => string 'E' (length=1) + * 'locale' => string 'en' (length=2) + * + * @param string $username Username + * @return array + */ + public function getUserByUsername( $username ) + { + return $this->getDBAL()->fetchAssoc( "SELECT * FROM usr WHERE username = '{$username}'" ); + } + + /** + * Gets principal data by principal id + * + * Returns: + * array (size=5) + * 'principal_id' => int 1 + * 'type_id' => int 1 + * 'user_no' => int 1 + * 'displayname' => string 'DAViCal Administrator' (length=21) + * 'default_privileges' => string '000000000000000000000000' (length=24) + * + * @param int $principal_id Principal id + * @return array + */ + public function getPrincipalById( $principal_id ) + { + return $this->getDBAL()->fetchAssoc( "SELECT * FROM principal WHERE principal_id = {$principal_id}" ); + } + + /** + * Gets principal data by user_no + * + * Returns: + * array (size=5) + * 'principal_id' => int 1 + * 'type_id' => int 1 + * 'user_no' => int 1 + * 'displayname' => string 'DAViCal Administrator' (length=21) + * 'default_privileges' => string '000000000000000000000000' (length=24) + * + * @param int $user_no User id + * @return array + */ + public function getPrincipalByUserId( $user_no ) + { + return $this->getDBAL()->fetchAssoc( "SELECT * FROM principal WHERE user_no = {$user_no}" ); + } + + /** + * Gets calendar data by collection id + * + * Returns: + * array (size=17) + * 'user_no' => int 1024 + * 'parent_container' => string '/nbdavical/' (length=11) + * 'dav_name' => string '/nbdavical/calendar/' (length=20) + * 'dav_etag' => string '-1' (length=2) + * 'dav_displayname' => string 'Davical User calendar' (length=21) + * 'is_calendar' => boolean true + * 'created' => string '2012-12-13 11:39:07.767205+00' (length=29) + * 'modified' => string '2012-12-13 11:39:07.767205+00' (length=29) + * 'public_events_only' => boolean false + * 'publicly_readable' => boolean false + * 'collection_id' => int 1034 + * 'default_privileges' => null + * 'is_addressbook' => boolean false + * 'resourcetypes' => string '' (length=60) + * 'schedule_transp' => string 'opaque' (length=6) + * 'timezone' => null + * 'description' => string '' (length=0) + * + * @param int $collection_id Calendar collection id + * @return array + */ + public function getCalendarById( $collection_id ) + { + return $this->getDBAL()->fetchAssoc( "SELECT * FROM collection WHERE collection_id = {$collection_id}" ); + } + + /** + * Gets calendars data by user id ( user_no ) + * + * Returns: + * array( size=n ) + * 0=> array (size=17) + * 'user_no' => int 1024 + * 'parent_container' => string '/nbdavical/' (length=11) + * 'dav_name' => string '/nbdavical/calendar/' (length=20) + * 'dav_etag' => string '-1' (length=2) + * 'dav_displayname' => string 'Davical User calendar' (length=21) + * 'is_calendar' => boolean true + * 'created' => string '2012-12-13 11:39:07.767205+00' (length=29) + * 'modified' => string '2012-12-13 11:39:07.767205+00' (length=29) + * 'public_events_only' => boolean false + * 'publicly_readable' => boolean false + * 'collection_id' => int 1034 + * 'default_privileges' => null + * 'is_addressbook' => boolean false + * 'resourcetypes' => string '' (length=60) + * 'schedule_transp' => string 'opaque' (length=6) + * 'timezone' => null + * 'description' => string '' (length=0) + * 1 => array ...... + * + * @param int $user_id Users id ( user_no ) + * @return array + */ + public function getCalendarsByUserId( $user_id ) + { + return $this->getDBAL()->fetchAll( "SELECT * FROM collection WHERE user_no = {$user_id} AND is_calendar = TRUE" ); + } + + /** + * Gets users who has access to shared calendar by calendar id. + * + * Users with PRIVILEGES_BLOCK are ignored. + * + * Return: + * array ( size = n ) + * 0 => array (size=22) + * 'by_principal' => int 1094 + * 'by_collection' => int 1095 + * 'to_principal' => int 1089 + * 'privileges' => string '000000001111111011100111' (length=24) + * 'is_group' => null + * 'principal_id' => int 1089 + * 'type_id' => int 1 + * 'user_no' => int 1048 + * 'displayname' => string 'Nerijus Barauskas' (length=17) + * 'default_privileges' => string '000000000000000000000000' (length=24) + * 'active' => boolean true + * 'email_ok' => null + * 'joined' => string '2012-12-14 13:25:53.173706+00' (length=29) + * 'updated' => string '2012-12-14 13:25:53.173706+00' (length=29) + * 'last_used' => null + * 'username' => string 'nerijus@opensolutions.ie' (length=24) + * 'password' => string '**qwerty78' (length=10) + * 'fullname' => string 'Nerijus Barauskas' (length=17) + * 'email' => string 'nerijus@opensolutions.ie' (length=24) + * 'config_data' => null + * 'date_format_type' => string 'E' (length=1) + * 'locale' => null + * 1 => aray .... + * + * @param int $collection_id Collection id + * @return array + */ + public function getUsersForSharedCalendar( $collection_id ) + { + return $this->getDBAL()->fetchAll( "SELECT * FROM grants as gr JOIN principal as pr ON pr.principal_id = gr.to_principal JOIN usr as us ON pr.user_no = us.user_no WHERE gr.by_collection = {$collection_id} AND gr.privileges <> '" . self::PRIVILEGES_BLOCK . "'" ); + } + + /** + * Gets shared calendars which can be accessed by user + * + * Calendars with PRIVILEGES_BLOCK are ignored. + * + * Return: + * array ( size = n ) + * 0 => array (size=25) + * 'by_principal' => int 1100 + * 'by_collection' => int 1101 + * 'to_principal' => int 1084 + * 'privileges' => string '000000001111111011100111' (length=24) + * 'is_group' => null + * 'principal_id' => int 1084 + * 'type_id' => int 1 + * 'user_no' => int 1051 + * 'displayname' => string 'Davical Box' (length=11) + * 'default_privileges' => null + * 'parent_container' => string '/sdcc@sdcc.ie/' (length=14) + * 'dav_name' => string '/sdcc@sdcc.ie/calendar/' (length=23) + * 'dav_etag' => string '-1' (length=2) + * 'dav_displayname' => string 'South Dublin County Council calendar' (length=36) + * 'is_calendar' => boolean true + * 'created' => string '2012-12-14 16:24:20.325642+00' (length=29) + * 'modified' => string '2012-12-14 16:24:20.325642+00' (length=29) + * 'public_events_only' => boolean false + * 'publicly_readable' => boolean false + * 'collection_id' => int 1101 + * 'is_addressbook' => boolean false + * 'resourcetypes' => string ''' (length=61) + * 'schedule_transp' => string 'opaque' (length=6) + * 'timezone' => null + * 'description' => string '' (length=0) + * 1 => array ... + * + * @param int $user_id User id ( user_no ) + * @return array + */ + public function getSharedCalendarsForUser( $user_id ) + { + return $this->getDBAL()->fetchAll( "SELECT * FROM grants as gr JOIN principal as pr ON pr.principal_id = gr.to_principal JOIN collection as cl ON gr.by_collection = cl.collection_id WHERE pr.user_no = {$user_id} AND gr.privileges <> '" . self::PRIVILEGES_BLOCK . "'" ); + } + + /** + * Gets principals IDs which have privileges to given users principal + * + * + * Return: + * array ( size = n ) + * 0 => ['to_principal' => int 1084 ] + * 1 => ['to_principal' => int 1084 ] + * ............. + * N => array ... + * + * @param int $user_id User id ( user_no ) + * @return array + */ + public function getGrantedToPrincipalsIds( $user_id ) + { + return $this->getDBAL()->fetchAll( "SELECT gr.to_principal FROM grants as gr JOIN principal as pr ON pr.principal_id = gr.by_principal WHERE pr.user_no = {$user_id} AND gr.by_principal IS NOT NULL" ); + } +} diff --git a/library/OSS/API/Jabber2d.php b/library/OSS/API/Jabber2d.php new file mode 100644 index 0000000..8718b61 --- /dev/null +++ b/library/OSS/API/Jabber2d.php @@ -0,0 +1,175 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * A Jabber2d API via direct database manipulation. + * + * @see http://jabberd2.org/ + * + * @category OSS + * @package OSS_API + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_API_Jabber2d +{ + // use DBAL connections for database manipulation + use OSS_Doctrine2_DBAL_Connection; + + /** + * Constructor - creates a new DBAL connection. + * + * @param array $dbparams + * @return void + */ + public function __construct( $dbparams ) + { + $this->getDBAL( $dbparams ); + } + + + /** + * Get all users registered in the database as an array. + * + * Returns: + * + * array (size=n) + * 0 => + * array (size=3) + * 'username' => string 'johndoe' (length=5) + * 'realm' => string 'example.com' (length=16) + * 'password' => string 'soopersecret' (length=9) + * 1 => + * ... + * + * @param void + * @return array All users registered in the database + * @access public + */ + public function getAllUsers() + { + return $this->getDBAL()->fetchAll( 'SELECT * FROM authreg' ); + } + + + /** + * Returns with a user's authreg entry as an assciative array, or with false if it wasn't found. + * + * @param string $username + * @param string $realm + * @return array|boolean + * @access public + */ + public function getAuthReg( $username, $realm ) + { + return $this->getDBAL()->fetchAssoc( 'SELECT * FROM authreg WHERE username = ? AND realm = ?', + array( $username, $realm ) + ); + } + + + /** + * Adds an authreg entry. + * + * @param string $username + * @param string $realm + * @param string $password + * @return int + * @access public + */ + public function addAuthReg( $username, $realm, $password ) + { + return $this->getDBAL()->insert( 'authreg', + [ 'username' => $username, 'realm' => $realm, 'password' => $password ] + ); + } + + + /** + * Updates an authreg entry. + * + * @param string $username + * @param string $realm + * @param string $password + * @return int + * @access public + */ + public function updateAuthReg( $username, $realm, $password ) + { + return $this->getDBAL()->update( 'authreg', + [ 'password' => $password ], + [ 'username' => $username, 'realm' => $realm ] + ); + } + + + /** + * Deletes a user's authreg entry, and all related entries. + * + * Encapsulates all the delete statements in one transaction. + * + * @param string $username + * @param string $realm + * @return void + * @access public + */ + public function deleteAuthReg( $username, $realm ) + { + $co = "{$username}@{$realm}"; + + $this->getDBAL()->beginTransaction(); + $this->getDBAL()->delete( 'active', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`disco-items`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( 'logout', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`motd-message`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`motd-times`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`privacy-default`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`privacy-items`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( 'private', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( 'queue', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`roster-groups`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`roster-items`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( 'status', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( '`vacation-settings`', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( 'vcard', [ '`collection-owner`' => $co ] ); + $this->getDBAL()->delete( 'authreg', [ 'username' => $username, 'realm' => $realm ] ); + $this->getDBAL()->commit(); + } + +} diff --git a/library/OSS/API/RoundCube.php b/library/OSS/API/RoundCube.php new file mode 100644 index 0000000..6508eb1 --- /dev/null +++ b/library/OSS/API/RoundCube.php @@ -0,0 +1,346 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * A RoundCube API via direct database manipulation. + * + * @category OSS + * @package OSS_API + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_API_RoundCube +{ + // use DBAL connections for database manipulation + use OSS_Doctrine2_DBAL_Connection; + + /** + * Stores users preference default language + * @var string $_language + */ + private $_language = "en_GB"; + + /** + * Stores users identity standard default value + * @var int $_standard + */ + private $_standard = 1; + + /** + * Constructor - creates a new DBAL connection. + * + * @param array $dbparams + * @return void + */ + public function __construct( $dbparams ) + { + $this->getDBAL( $dbparams ); + } + + + /** + * Get all users registered in the RoundCube database as an array. + * + * @param void + * @return array All users registered in the database + * @access public + */ + public function getAllUsers() + { + return $this->getDBAL()->fetchAll( 'SELECT * FROM users' ); + } + + + /** + * Get users data from the RoundCube database as an array. + * + * @param strint $username Users username + * @return array|bool + * @access public + */ + public function getUser( $username ) + { + return $this->getDBAL()->fetchAssoc( 'SELECT * FROM users WHERE username = ?', + array( $username ) + ); + } + + + /** + * Creates RoundCube user, and also creates identity if $identity array is passed. + * + * NOTICE: If $identity array is passed it must contain $identity['mail']. + * + * @param string $username Users username + * @param string $mail_host IMAP addres where email is stored + * @param array $preferences Users preferences such as davical details. + * @param array $identity Users identity details. + * @return bool + * @access public + * @throw Identity mail is not set. + * @see addIdentity() + */ + public function addUser( $username, $mail_host, $preferences, $identity = null ) + { + $result = $this->getDBAL()->insert( 'users', [ + 'username' => $username, + 'mail_host' => $mail_host, + 'language' => $this->_language, + 'created' => date( 'Y-m-d H:i:s' ), + 'preferences' => serialize( $preferences ) ] + ); + + if( $identity && $result ) + { + $user = $this->getUser( $username ); + if( !isset( $identity['email'] ) ) + throw new OSS_Exception( "Identity mail is not set." ); + + $reuslt = $this->addIdentity( $user, $identity['email'], $identity ); + } + + + return $result; + } + + /** + * Creates identity for user. + * + * $identity array sturcture: + * [ + * 'replay_to' => 'replay_to', + * 'bcc' => 'bcc', + * 'name' => 'name', + * 'organization' => 'organization', + * 'signature' => 'signature', + * 'html_signature' => 'html_signature' + * ] + * + * @param array $user User array loaded form OSS_API_RoundCube + * @param string $email Email address + * @param array $identity + * @return bool + * @access public + */ + public function addIdentity( $user, $email, $identity ) + { + return $this->getDBAL()->insert( 'identities', [ + 'user_id' => $user['user_id'], + 'changed' => date( 'Y-m-d H:i:s' ), + 'del' => 0, + 'standard' => $this->_standard, + '`reply-to`' => isset( $identity['reply_to'] ) ? $identity['reply_to'] : "", + 'bcc' => isset( $identity['bcc'] ) ? $identity['bcc'] : "", + 'email' => $email, + 'name' => isset( $identity['name'] ) ? $identity['name'] : "", + 'organization' => isset( $identity['organization'] ) ? $identity['organization'] : "", + 'signature' => isset( $identity['signature'] ) ? $identity['signature'] : NULL, + 'html_signature' => isset( $identity['html_signature'] ) ? $identity['html_signature'] : 0, + ] + ); + } + + /** + * Updates RoundCube users data, and also updates identity data if $identity array is passed. + * + * @param string $username Users username + * @param string $mail_host IMAP addres where email is stored + * @param array $preferences Users preferences such as davical details. + * @param array $identity Users identity details. + * @return bool + * @access public + */ + public function updateUser( $username, $mail_host, $preferences, $identity = null ) + { + $result = $this->getDBAL()->update( 'users', [ + 'mail_host' => $mail_host, + 'language' => $this->_language, + 'created' => date( 'Y-m-d H:i:s' ), + 'preferences' => serialize( $preferences ) ], + [ 'username' => $username ] + ); + + if( $identity && $reuslt ) + { + $user = $this->getUser( $username ); + $reuslt = $this->updateIdentity( $user, $identity ); + } + + + return $result; + } + + /** + * Creates identity for user. + * + * $identity array sturcture: + * [ + * 'email' => email, + * 'replay_to' => 'replay_to', + * 'bcc' => 'bcc', + * 'name' => 'name', + * 'organization' => 'organization', + * 'signature' => 'signature', + * 'html_signature' => 'html_signature' + * ] + * + * @param array $user User array loaded form OSS_API_RoundCube + * @param string $email Email address + * @param array $identity + * @return bool + * @access public + */ + public function updateIdentity( $user, $identity ) + { + $identity['changed'] = date( 'Y-m-d H:i:s' ); + if( isset( $identity['replay_to'] ) ) + { + $identity['`replay-to`'] = $identity['replay_to']; + unset( $identity['replay_to'] ); + } + + return $this->getDBAL()->insert( 'identities', $identity, + [ 'user_id' => $user['user_id'] ] + ); + } + + /** + * Implementation of RoundCube's encryption function. + * + * Based on source code from the program/include/rcmail.php + * rcmail::encrypt() function. + * + * @param string $clear The cleartext string to encrypt + * @param string $key The encryption key + * @param string $base64 Return the ciphertext as a base64 encoded string + * @return string The ciphertext + */ + public static function encrypt( $clear, $key, $base64 = true ) + { + $clear = pack( "a*H2", $clear, "80" ); + + $td = mcrypt_module_open( MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "" ); + + $iv = self::createIV( mcrypt_enc_get_iv_size( $td ) ); + mcrypt_generic_init( $td, $key, $iv ); + $cipher = $iv . mcrypt_generic( $td, $clear ); + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + + return $cipher = ( $base64 ? base64_encode( $cipher ) : $cipher ); + } + + /** + * Implementation of RoundCube's decryption function. + * + * Based on source code from the program/include/rcmail.php + * rcmail::decrypt() function. + * + * @param string $cipher The ciphertext string to decrypt + * @param string $key The encryption key + * @param string $base64 Indicates if the ciphertext is a base64 encoded string + * @return string The cleartext + */ + public static function decrypt( $cipher, $key, $base64 = true ) + { + $cipher = ( $base64 ? base64_decode( $cipher ) : $cipher ); + + $td = mcrypt_module_open( MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "" ); + + $iv_size = mcrypt_enc_get_iv_size( $td ); + $iv = substr( $cipher, 0, $iv_size ); + + $cipher = substr( $cipher, $iv_size ); + mcrypt_generic_init( $td, $key, $iv ); + $clear = mdecrypt_generic( $td, $cipher ); + mcrypt_generic_deinit( $td ); + mcrypt_module_close( $td ); + + $clear = substr(rtrim($clear, "\0"), 0, -1); + + return $clear; + } + + /** + * Implementation of RoundCube's create_iv function. + * + * Based on source code from the program/include/rcmail.php + * rcmail::create_iv() function. + * + * @param int $size The size of the initialisation vector + * @return string The initialisation vector + */ + public static function createIV( $size ) + { + $iv = ''; + for( $i = 0; $i < $size; $i++ ) + $iv .= chr( mt_rand( 0, 255 ) ); + + return $iv; + } + + + /** + * Sets language + * + * By default is en_GB + * + * @param string $lang Users preference language + * @return $this for fluent interface + */ + public function setLanguage( $lang ) + { + $this->_language = $lang; + return $this; + } + + /** + * Sets standard + * + * By default is 1 + * + * @param int $standard Identity standard + * @return $this for fluent interface + */ + public function setStandard( $standard ) + { + $this->_standard = $standard; + return $this; + } +} diff --git a/library/OSS/API/SOGo.php b/library/OSS/API/SOGo.php new file mode 100644 index 0000000..4eec9bd --- /dev/null +++ b/library/OSS/API/SOGo.php @@ -0,0 +1,332 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * A SOGo API via direct database manipulation. + * + * @category OSS + * @package OSS_API + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_API_SOGo +{ + // use DBAL connections for database manipulation + use OSS_Doctrine2_DBAL_Connection; + + /** + * Constructor - creates a new DBAL connection. + * + * @param array $dbparams + * @return void + */ + public function __construct( $dbparams ) + { + $this->getDBAL( $dbparams ); + } + + + /** + * Get all exiting users profiles form SOGo database as an array. + * + * NOTICE: c_defaults and c_settings in database is stored as json array + * + * @param void + * @return array All users profiles existing in the database + * @access public + */ + public function getAllUsersProfiles() + { + $profiles = $this->getDBAL()->fetchAll( 'SELECT * FROM sogo_user_profile' ); + + if( !$profiles ) + return false; + + foreach( $profiles as $idx => $profile ) + { + if( $profile['c_defaults'] ) + $profiles[ $idx ]['c_defaults'] = json_decode( $profile['c_defaults'], true ); + + if( $profile['c_settings'] ) + $profiles[ $idx ]['c_settings'] = json_decode( $profile['c_settings'], true ); + } + + return $profiles; + } + + + /** + * Get user's profile from the SOGo database as an array. + * + * NOTICE: c_defaults and c_settings in database is stored as json array + * + * @param strint $uid User id ( usrname ) + * @return array|bool + * @access public + */ + public function getUserProfile( $uid ) + { + $profile = $this->getDBAL()->fetchAssoc( 'SELECT * FROM sogo_user_profile WHERE c_uid = ?', + [ $uid ] + ); + + if( !$profile ) + return false; + + if( $profile['c_defaults'] ) + $profile['c_defaults'] = json_decode( $profile['c_defaults'], true ); + + if( $profile['c_settings'] ) + $profile['c_settings'] = json_decode( $profile['c_settings'], true ); + + return $profile; + } + + /** + * Adds user profile to SOGo's database + * + * NOTICE: c_defaults and c_settings in database is stored as JSON array + * + * @param strint $uid New users id ( username ) + * @param bool|array $defaults Users profile defaults. If it false it will not be added. + * @param bool|array $settings Users profile settings. If it false it will not be added. + * @return bool + * @access public + */ + public function addUserProfile( $uid, $defaults = false, $settings = false ) + { + $params = [ 'c_uid' => $uid ]; + + if( $defaults ) + $params['c_defaults'] = json_encode( $defaults ); + + if( $settings ) + $params['c_settings'] = json_encode( $settings ); + + return $this->getDBAL()->insert( 'sogo_user_profile', $params ); + } + + /** + * Updates SOGo users profile + * + * To reset defaults or settings pass empty array. If defaults and settings will be false function will return + * false without even trying to process sql query. + * + * NOTICE: c_defaults and c_settings in database is stored as JSON array + * + * @param strint $uid Users id to edit ( username ) + * @param bool|array $defaults Users profile defaults. If it false it will not be updated to null it will leave es it is. + * @param bool|array $settings Users profile settings. If it false it will not be updated to null it will leave es it is. + * @return bool + * @access public + */ + public function updateUserProfile( $uid, $defaults = false, $settings = false ) + { + if( $defaults === false && $settings === false ) + return false; + + $params = []; + if( $defaults ) + $params['c_defaults'] = json_encode( $defaults ); + + if( $settings ) + $params['c_settings'] = json_encode( $settings ); + + return $this->getDBAL()->update( 'sogo_user_profile', $params, + [ 'c_uid' => $uid ] + ); + } + + /** + * Sets access privileges to resource for another SOGo user. + * + * First script fill find acl table name for resource. then it will remove privileges + * for the user and then it will add new ones. + * + * Privileges array sample + * $privileges = ['ConfidentialDandTViewer','ObjectCreator','PublicViewer','ConfidentialViewer','ObjectEraser']; + * + * @param string $uid Users id (username) to share resource with. + * @param string $own_uid Resource owner SOGo user id (username) + * @param string $name Resource name for example personal. + * @param strig|array $privileges Privileges to set. + * @param bool $calendar If it set to true then it will look for calendar. False for contacts. + * @return bool + */ + public function setAccessPrivileges( $uid, $own_uid, $name, $privileges, $calendar = true ) + { + $type = $calendar ? 'Calendar' : 'Contacts'; + $privileges = is_array( $privileges ) ? $privileges : [ $privileges ]; + + $acl_loc = $this->getDBAL()->fetchColumn( 'SELECT c_acl_location FROM sogo_folder_info WHERE c_path2 = ? AND c_path3 = ? AND c_path4 = ?', + [ $own_uid, $type, $name ] + ); + + if( !$acl_loc ) + return false; + + $acl_name = substr( $acl_loc, strrpos( $acl_loc, "/") + 1 ); + $params = [ + 'c_uid' => $uid, + 'c_object' => sprintf( "/%s/%s/%s", $own_uid, $type, $name ) + ]; + + $this->getDBAL()->delete( $acl_name, [ 'c_uid' => $uid ] ); + foreach( $privileges as $privilege ) + { + $params['c_role'] = $privilege; + $this->getDBAL()->insert( $acl_name, $params ); + } + return true; + + } + + /** + * Unsets access privileges to resource for another SOGo user. + * + * First script fill find acl table name for resource. then it will remove privileges + * for the user. + * + * @param string $uid Users id (username) to share resource with. + * @param string $own_uid Resource owner SOGo user id (username) + * @param string $name Resource name for example personal. + * @param bool $calendar If it set to true then it will look for calendar. False for contacts. + * @return bool + */ + public function unsetAccessPrivileges( $uid, $own_uid, $name, $calendar = true ) + { + $type = $calendar ? 'Calendar' : 'Contacts'; + + $acl_loc = $this->getDBAL()->fetchColumn( 'SELECT c_acl_location FROM sogo_folder_info WHERE c_path2 = ? AND c_path3 = ? AND c_path4 = ?', + [ $own_uid, $type, $name ] + ); + + if( !$acl_loc ) + return false; + + $acl_name = substr( $acl_loc, strrpos( $acl_loc, "/") + 1 ); + $params = [ + 'c_uid' => $uid, + 'c_object' => sprintf( "/%s/%s/%s", $own_uid, $type, $name ) + ]; + + $this->getDBAL()->delete( $acl_name, [ 'c_uid' => $uid ] ); + return true; + + } + + + /** + * Subscribe resource + * + * @param string $uid Users id (username) which is suscribing + * @param string $own_uid Resource owner SOGo user id (username) + * @param string $name Resource name for subscribing + * @param bool $calendar If it set to true then it will look for calendar. False for contacts. + * @param string $color Color for new calendar. Applies only for calendars. + * @return bool + */ + public function subscribeResource( $uid, $own_uid, $name, $calendar = true , $color = false ) + { + $type = $calendar ? 'Calendar' : 'Contacts'; + + $profile = $this->getUserProfile( $uid ); + if( !$profile ) + return false; + + //FIXME: Check if resource exists + + $settings = $profile['c_settings']; + $resource = sprintf( "%s:%s/%s", $own_uid, $type, $name ); + if( !is_array( $settings ) ) + $settings = []; + + if( !isset( $settings[ $type ]['SubscribedFolders'] ) ) + $settings[ $type ]['SubscribedFolders'] = []; + + if( !in_array( $resource, $settings[ $type ]['SubscribedFolders'] ) ) + { + $settings[ $type ]['SubscribedFolders'][] = $resource; + + if( $calendar && $color ) + $settings[ $type ]['FolderColors'][ $resource ] = $color; + } + return $this->updateUserProfile( $uid, null, $settings ); + } + + /** + * Gets calendar or contacts resource list for user. + * + * Return array structure: + * array(1) { + * [0] => + * array(3) { + * 'db_table' => + * string(23) "sogoopensolu00471298964" + * 'resource_name' => + * string(8) "personal" + * 'display_name' => + * string(17) "Personal Calendar" + * } + * } + * + * @param string $uid Users id (username) to have a list of his resources. + * @param bool $calendar If it set to true then it will look for calendars. False for contacts. + * @return array|bool + */ + public function getResourceNames( $uid, $calendar = true ) + { + $type = $calendar ? 'Calendar' : 'Contacts'; + + $data = $this->getDBAL()->fetchAll( 'SELECT c_location as db_table, c_path4 as resource_name, c_foldername as display_name FROM sogo_folder_info WHERE c_path2 = ? AND c_path3 = ?', + [ $uid, $type ] + ); + + if( !$data ) + return false; + + if( isset( $data['db_table'] ) ) + $data = [ $data ]; + + foreach( $data as $idx => $row ) + $data[$idx]['db_table'] = substr( $row['db_table'], strrpos( $row['db_table'], "/") + 1 ); + + return $data; + } +} diff --git a/library/OSS/Acl.php b/library/OSS/Acl.php new file mode 100644 index 0000000..bc4001c --- /dev/null +++ b/library/OSS/Acl.php @@ -0,0 +1,49 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Acl + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Acl extends Zend_Acl +{ + +} + diff --git a/library/OSS/Array.php b/library/OSS/Array.php new file mode 100644 index 0000000..86670cd --- /dev/null +++ b/library/OSS/Array.php @@ -0,0 +1,383 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Array + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Array +{ + /** + * Reindex an array of objects by a member of that object. + * + * Typically used for Doctrine2 collections. + * + * @param array $objects Array of objects to reindex + * @param string $indexFn The method of the object that will return the new index. Must be a unique key. + * @return array + */ + public static function reindexObjects( $objects, $indexFn ) + { + $new = []; + + foreach( $objects as $o ) + $new[ $o->$indexFn() ] = $o; + + return $new; + } + + /** + * Reorder an array of objects by a member of that object. + * + * Typically used for Doctrine2 collections. + * + * @param array $objects Array of objects to reindex + * @param string $indexFn The method of the object that will return the new ordering index (should be unique!). + * @return array + */ + public static function reorderObjects( $objects, $orderFn, $orderParam = SORT_REGULAR ) + { + $new = []; + + foreach( $objects as $o ) + $new[ $o->$orderFn() ] = $o; + + ksort( $new, $orderParam ); + + return $new; + } + + /** + * Removes array elements where the value is empty(). + * + * @param array $array The array to clean + * @param boolean $keepKeys default true keep the original keys or assign new (numeric) ones + * @return array + */ + public static function removeEmptyElements( $array, $keepKeys = true ) + { + if( ( is_array( $array ) == false ) || ( sizeof( $array ) == 0 ) ) + return array(); + + $retVal = array(); + + // longer code, but faster + if( $keepKeys == true ) + { + foreach( $array as $arrayKey => $arrayValue ) + { + if( empty( $arrayValue ) == false ) + $retVal[$arrayKey] = $arrayValue; + } + } + else + { + foreach( $array as $arrayKey => $arrayValue ) + { + if( empty( $arrayValue ) == false ) + $retVal[] = $arrayValue; + } + } + + return $retVal; + } + + + /** + * Works exactly like shuffle(), but this works on associative arrays, too. + * + * @param array $array the array to shuffle, reference-type parameter, it contains the result, too + * @return boolean + */ + function shuffleAssoc( &$array ) + { + $newArray = array(); + $keys = array_keys( $array ); + + shuffle( $keys ); + + foreach( $keys as $key ) + $newArray[$key] = $array[$key]; + + $array = $newArray; + + return true; + } + + + /** + * Returns with an one-dimensional array of values from any kind of nested array. + * + * Try to avoid recursive arrays to not to go into an infinite loop. + * + * Returns with the values in an associative array by keeping the original key => value pairs. + * As it keeps the original keys, if a key exists more than once in the structure, it will be + * overwritten and will appear in the result only once, having the value of the last key-value pair. + * + * @param array $array The array to flatten + * @return array + */ + public static function flatten( $array ) + { + $arrayValues = array(); + + foreach( $array as $key => $value ) + { + if( is_scalar( $value ) || is_resource( $value ) || is_null( $value ) ) + { + $arrayValues[$key] = $value; + } + elseif( is_array( $value ) ) + { + $arrayValues = array_merge( $arrayValues, self::flatten( $value ) ); + } + } + + return $arrayValues; + } + + + /** + * Takes a nested array - typically a result set from a query - and filters out a + * specific field from it. It does not preserve the array keys. + * + * @param array $array the input array + * @param string $fieldName the field to filter out + * @param boolean $unique default true it can filter out multiple occurences of the same value + * @param boolean $removeNull default false if true then removes null values from the result set + * @return array + */ + public static function filterField( $array, $fieldName, $unique = true, $removeNull = false ) + { + $retVal = array(); + + if( ( is_array( $array ) == false ) || ( sizeof( $array ) == 0 ) ) + return array(); + + foreach( $array as $result ) + $retVal[] = $result[$pFieldName]; + + if( $unique == true ) + $retVal = array_unique( $retVal ); + + if( $removeNull == true) + { + foreach( $retVal as $retValKey => $retValValue ) + { + if( $retValValue === null ) + unset( $retVal[$retValKey] ); + } + } + + return $retVal; + } + + + /** + * Filters out key-value pairs from nested arrays, typically from Doctrine result sets. + * Both the $pKey and $pValue parameters are array keys in the $pArray. + * + * @param array $array the input array + * @param string $key key filtering + * @param string $value value filtering + * @param boolean $unique default true return with unique values only or not + * @return array + */ + public static function filterKeyValuePairs( $array, $key, $value, $unique=true) + { + $retVal = array(); + + if( ( is_array( $array) == false ) || ( sizeof( $array ) == 0 ) ) + return array(); + + foreach( $array as $result ) + $retVal[ $result[$key] ] = $result[$value]; + + return( $unique == true ? array_unique( $retVal ) : $retVal); + } + + + /** + * Removes entries from an array based on the matches of the passed regular + * expression. It handles multi-dimensional arrays. + * + * @param array $array the input array + * @param string $pattern pattern for filter + * @return array + */ + public static function removeFields( $array, $pattern) + { + if( ( is_array( $array ) == false ) || ( sizeof( $array ) == 0 ) ) + return $array; + + foreach( $array as $arrKey => $arrValue) + { + if( preg_match( "/{$pattern}/ui", $arrKey ) != 0 ) + { + unset( $array[$arrKey] ); + continue; + } + + if( is_array( $array[$arrKey] ) == true ) + $array[$arrKey] = self::removeFields( $array[$arrKey], $pattern ); + } + + return $array; + } + + + /** + * Callback function for changeValues() + * + * @param mixed &item + * @param mixed $key + * ¶m array $params + * @return void + */ + public static function cavCallback( &$item, $key, array $params ) + { + foreach( $params as $paramKey => $paramValue ) + if( $item === $paramKey ) + $item = $paramValue; + } + + + /** + * Changes values in an array. Handles nested arrays. + * + * @param array &$array + * @param array $fromToArray array('from1' => 'to1', 'from2' => 'to2', 'from3' => 'to3', ...) + * @return void + */ + public static function changeValues( array &$array, array $fromToArray) + { + if( ( is_array( $array ) == true ) && ( sizeof( $array ) >��0 ) ) + array_walk_recursive( $array, array( 'OSS_Array', 'cavCallback' ), $fromToArray ); + } + + + /** + * Recursively converts an object into an array. Empty objects become empty arrays. + * + * @param object $object + * @return array + */ + public static function objectToArray( $object ) + { + if( ( is_object( $object ) == false ) && ( is_array( $object ) == false ) ) + return $pObject; + + if( is_object( $object ) == true ) + $object = get_object_vars( $object ); + + if( sizeof( $object ) == 0 ) + return array(); + + return array_map( array( 'OSS_Array', 'objectToArray' ), $object ); + } + + /** + * Represents an array as a HTML table and returns with it as a string. It handles multi-dimensional arrays, + * those are represented as nested tables. If $pArray is not an array or an empty array, then it returns with + * an empty string. + * + * @param array $pArray + * @param string $pTitle default '' the name of the variable or any other string, it will appear as a heading + * @param string $pWidth default '' a valid CSS width, like '100px', '10%', '13em', etc. + * @param string $pHeaderBgColour default '#404040' + * @param string $pBorderColour default '#404040' + * @return string + */ + public static function toHtmlTable( $pArray, $pTitle='', $pWidth='', $pHeaderBgColour='#404040', $pBorderColour='#404040' ) + { + if( !is_array( $pArray ) || !sizeof( $pArray) ) + return ''; + + $vRetVal = ''; + + $vRetVal .= " +"; + + if( $pTitle != '' ) + { + $vRetVal .= " + + + "; + } + + foreach( $pArray as $vArrKey => $vArrValue ) + { + $vRetVal .= " + "; + + if( is_array( $vArrValue ) ) + { + $vRetVal .= " + + "; + } + else + { + $vRetVal .= " + + "; + } + + $vRetVal .= " + "; + } + + $vRetVal .= " +
{$pTitle}
    +
"; + + $vRetVal .= self::toHtmlTable( $vArrValue, $vArrKey, '100%', $pHeaderBgColour, $pBorderColour ); + + $vRetVal .= " +
+
{$vArrKey}{$vArrValue}
+"; + + return $vRetVal; + } + +} diff --git a/library/OSS/Asterisk/AMI/ConnectionException.php b/library/OSS/Asterisk/AMI/ConnectionException.php new file mode 100644 index 0000000..d54239e --- /dev/null +++ b/library/OSS/Asterisk/AMI/ConnectionException.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Asterisk + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Asterisk_AMI_ConnectionException extends OSS_Asterisk_AMI_Exception +{ + +} + + diff --git a/library/OSS/Asterisk/AMI/Exception.php b/library/OSS/Asterisk/AMI/Exception.php new file mode 100644 index 0000000..d7de6ff --- /dev/null +++ b/library/OSS/Asterisk/AMI/Exception.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Asterisk + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Asterisk_AMI_Exception extends OSS_Asterisk_Exception +{ + +} + + diff --git a/library/OSS/Asterisk/AMI/Parser.php b/library/OSS/Asterisk/AMI/Parser.php new file mode 100644 index 0000000..d202299 --- /dev/null +++ b/library/OSS/Asterisk/AMI/Parser.php @@ -0,0 +1,313 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Asterisk + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Asterisk_AMI_Parser +{ + + /** + * Parse the response to a `sip show peer $peer` command + * + * This function takes the response array of the form: + * + * Array + * ( + * [data] => "Privilege: Command + * + * * Name : pcustomer + * Secret : + * MD5Secret : + * ... + * ToHost : customer + * Addr->IP : 192.168.40.43:5060 + * ... + * Encryption : No" + * + * [Response] => Follows + * ) + * + * and converts it to an associate array: + * + * Array + * ( + * [Privilege] => Command + * [* Name] => pcustomer + * [Secret] => + * ... + * [ToHost] => customer + * [Addr->IP] => 192.168.40.43:5060 + * [Addr] = Array + * ( + * [IP] => 192.168.40.43 + * [Port] => 5060 + * ) + * ... + * [Encryption] => No + * ) + * + * + * Example usage: + * + * $resp = $ami->command( "sip show peer PEER_NAME" ); + * $result = OSS_Asterisk_AMI_Parser::parsePeerResponse( $resp ); + * + * + * @param array $response Response of AMI command + * @return array + */ + static function parseSipShowPeerResponse( $response ) + { + if( !strpos( $response['data'], ':' ) ) + return $response['data']; + + $data = []; + foreach( explode( "\n", $response['data'] ) as $line ) + { + $a = strpos( 'z'.$line, ':' ) - 1; + if( $a >= 0 ) + $data[ trim( substr( $line, 0, $a ) ) ] = trim( substr( $line, $a + 1 ) ); + } + + $addr = explode( ":", $data[ "Addr->IP" ] ); + $data["Addr"]["IP"] = $addr[0]; + $data["Addr"]["Port"] = $addr[1]; + + return( $data ); + } + + /** + * Parse channel response. + * + * It takes response array it checks if response have data. If it has it tries to parse form + * this type of array: + * Array + * ( + * [data] => Privilege: Command + * -- General -- + * Name: SIP/pcustomer-0000010a + * Type: SIP + * UniqueID: 1350570966.266 + * LinkedID: 1350570966.266 + * ... + * Indirect Bridge: SIP/ptelco-0000010b + * -- PBX -- + * Context: fwd-call + * Extension: 1201 + * ... + * Variables: + * BRIDGEPVTCALLID=4093270c7dd45f224ffff58f129f3275@192.168.40.42:5060 + * BRIDGEPEER=SIP/ptelco-0000010b + * ... + * SIPURI=sip:1101@192.168.40.43:5060 + * + * CDR Variables: + * level 1: dnid=1201 + * level 1: calldate=2012-10-18 15:36:07 + * ... + * level 1: sequence=337 + * + * [Response] => Follows + * ) + * To this type of array: + * Array + * ( + * [] => Array + * ( + * [Privilege] => Command + * ) + * [general] => Array + * ( + * [Name] => SIP/pcustomer-0000010a + * [Type] => SIP + * [UniqueID] => 1350570967.266 + * [LinkedID] => 1350570966.266 + * ... + * [Indirect Bridge] => SIP/ptelco-0000010b + * ) + * [pbx] => Array + * ( + * [Context] => fwd-call + * [Extension] => 1201 + * ... + * [Variables] => Array + * ( + * [BRIDGEPVTCALLID] => 4093270c7dd45f224ffff58f129f3275@192.168.40.42:5060 + * [BRIDGEPEER] => SIP/ptelco-0000010b + * ... + * [SIPURI] => sip:1101@192.168.40.43:5060 + * ) + * ) + * [cdr] => Array + * ( + * [dnid] => 1201 + * [calldate] => 2012-10-18 15:36:07 + * ... + * [sequence] => 337 + * ) + * ) + * + * Usage: + * $presp = $ami->command( "core show channel CHANNEL_NAME" ); + * $result = OSS_Asterisk_AMI_Parser::parseChannelResponse( $presp ); + * + * + * @var array $response Response of AMI command + * @return array + */ + static function parseChannelResponse( $response ) + { + if( !strpos( $response['data'], ':' ) ) + return $response['data']; + + $data = []; + foreach( explode( "\n", $response['data'] ) as $line ) + { + if( strpos( $line, "General") !== false ) + $name = "general"; + elseif( strpos( $line, "PBX") !== false ) + $name = "pbx"; + elseif( strpos( $line, "CDR Variables") !== false ) + $name = "cdr"; + else + { + if( $name == "variables" ) + { + $a = strpos( 'z'.$line, '=' ) - 1; + if( $a >= 0 ) + { + $data['pbx']['variables'][ trim( substr( $line, 0, $a ) ) ] = trim( substr( $line, $a + 1 ) ); + continue; + } + } + + $a = strpos( 'z'.$line, ':' ) - 1; + if( $a >= 0 ) + { + + if( $name == "cdr" ) + { + $cdrVar = explode( "=", trim( substr( $line, $a + 1 ) ) ); + $data['cdr'][ trim( $cdrVar[0] ) ] = trim( $cdrVar[1] ); + } + else + { + + if( trim( substr( $line, 0, $a ) ) != "Variables" ) + $data[$name][ trim( substr( $line, 0, $a ) ) ] = trim( substr( $line, $a + 1 ) ); + else + $name = "variables"; + } + } + } + } + return( $data ); + } + + /** + * Parse channels response when channels call had attribute concise. + * + * It takes response array it checks if response have data. If it has it tries to parse form + * this type of array: + * Array + * ( + * [data] => Privilege: Command + * SIP/ptelco-00000113!ttelco!!1!Up!AppDial!(Outgoing Line)!1201!cust1!cust1!3!15!SIP/pcustomer-00000112!1350573839.275 + * SIP/pcustomer-00000112!fwd-call!1201!6!Up!Dial!SIP/ptelco/1201!1101!cust1!cust1!3!15!SIP/ptelco-00000113!1350573837.274 + * + * [Response] => Follows + * ) + * To this type of array: + * Array + * ( + * [0] => Array + * ( + * [0] => SIP/ptelco-00000113 + * [1] => ttelco + * [2] => + * [3] => 1 + * [4] => Up + * [5] => AppDial + * [6] => (Outgoing Line) + * [7] => 1201 + * [8] => cust1 + * [9] => cust1 + * [10] => 3 + * [11] => 15 + * [12] => SIP/pcustomer-00000112 + * [13] => 1350573839.275 + * ) + * [1] => Array + * ( + * [0] => SIP/pcustomer-00000112 + * ... + * [13] => 1350573837.274 + * ) + * ) + * + * Usage: + * $resp = $ami->command( "core show channels concise" ); + * $result = OSS_Asterisk_AMI_Parser::parseChannelsConciseResponse( $resp ); + * + * + * @var array $response Response of AMI command + * @return array + */ + static function parseChannelsConciseResponse( $response ) + { + if( !strpos( $response['data'], ':' ) ) + return $response['data']; + + $data = []; + foreach( explode( "\n", $response['data'] ) as $line ) + { + $line = preg_replace('!\s+!', ' ', $line); + $a = explode( "!", $line ); + + if( count( $a ) > 3 && $a[0] != "Channel" ) + $data[] = $a; + } + return( $data ); + } +} + + diff --git a/library/OSS/Asterisk/Exception.php b/library/OSS/Asterisk/Exception.php new file mode 100644 index 0000000..aa0f17a --- /dev/null +++ b/library/OSS/Asterisk/Exception.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Asterisk + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Asterisk_Exception extends OSS_Exception +{ + +} + + diff --git a/library/OSS/Auth/Doctrine2Adapter.php b/library/OSS/Auth/Doctrine2Adapter.php new file mode 100644 index 0000000..92d3da4 --- /dev/null +++ b/library/OSS/Auth/Doctrine2Adapter.php @@ -0,0 +1,180 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Auth + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Auth_Doctrine2Adapter implements Zend_Auth_Adapter_Interface +{ + + /** + * The username + * + * @var string + */ + private $_username = null; + + /** + * The password + * + * @var string + */ + private $_password = null; + + /** + * The model + * + * @var string + */ + private $_model = null; + + /** + * The Doctrine entity manager + * + * @var object + */ + private $_em = null; + + /** + * Authentication options + * + * @var array Authentication options + */ + private $_aoptions = [ + 'pwhash' => 'bcrypt' + ]; + + /** + * If we have a cookie, we can skip password check + * + * @var object + */ + private $_haveCookie = false; + + /** + * Sets username and password for authentication + * + * @param string $username + * @param string $password + * @param string $model + * @param object $em Doctrine Entity Manager object + * @param array $opts An array of authentication options (from `application.ini - resources.auth.oss.*`) + * @throws Zend_Auth_Adapter_Exception If parameters are incorrect / not present + * @return void + */ + public function __construct( $username, $password, $model, $em, $opts = null ) + { + if( $username == null || $username == '' || $password == null || $password == '' ) + { + throw new Zend_Auth_Adapter_Exception( "No username / password specified" ); + } + + $this->_username = $username; + $this->_password = $password; + $this->_model = $model; + $this->_em = $em; + $this->_aoptions = $opts; + } + + + public function haveCookie( $h = true ) + { + $this->_haveCookie = $h; + } + + /** + * Performs an authentication attempt + * + * @throws Zend_Auth_Adapter_Exception If authentication cannot be performed + * @return Zend_Auth_Result + */ + public function authenticate() + { + $user = $this->_em->getRepository( $this->_model )->findOneBy( array( 'username' => $this->_username ) ); + + $result = array( + 'code' => Zend_Auth_Result::FAILURE, + 'identity' => array( 'username' => $this->_username ), + 'messages' => array() + ); + + if( !$user ) + return new Zend_Auth_Result( Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, $result['identity'], $result['messages'] ); + + $pwcheck = false; + + if( !$this->_haveCookie ) + { + $pwcheck = OSS_Auth_Password::verify( $this->_password, $user->getPassword(), $this->_aoptions ); + + if( !$pwcheck ) + { + if( method_exists( $user, 'setFailedLogins' ) ) + { + $user->setFailedLogins( $user->getFailedLogins() + 1 ); + $this->_em->flush( ); + + $result['identity'] = array( + 'count' => $user->getFailedLogins() + ); + } + + return new Zend_Auth_Result( Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID, $result['identity'], $result['messages'] ); + } + } + + if( $pwcheck || $this->_haveCookie ) + { + $result['code'] = Zend_Auth_Result::SUCCESS; + $result['messages'] = array(); + $result['identity'] = array( + 'username' => $this->_username, + 'user' => $user, + 'id' => $user->getId() + ); + } + else + die( 'Huh? This should not have happened....' ); + + return new Zend_Auth_Result( $result['code'], $result['identity'], $result['messages'] ); + } + +} diff --git a/library/OSS/Auth/DoctrineAdapter.php b/library/OSS/Auth/DoctrineAdapter.php new file mode 100644 index 0000000..e5a5e10 --- /dev/null +++ b/library/OSS/Auth/DoctrineAdapter.php @@ -0,0 +1,162 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Auth + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Auth_DoctrineAdapter implements Zend_Auth_Adapter_Interface +{ + + /** + * The username + * + * @var string + */ + private $_username = null; + + /** + * The password + * + * @var string + */ + private $_password = null; + + /** + * The model + * + * @var string + */ + private $_model = null; + + /** + * Sets username and password for authentication + * + * @param string $username + * @param string $password + * @param string $model + * @throws Zend_Auth_Adapter_Exception If parameters are incorrect / not present + * @return void + */ + public function __construct( $username, $password, $model ) + { + if ( ($username == null) || ($username == '') || ($password == null) || ($password == '') ) + { + throw new Zend_Auth_Adapter_Exception("No username / password specified"); + } + + $this->_username = $username; + $this->_password = $password; + $this->_model = $model; + + } + + + /** + * Performs an authentication attempt + * + * @throws Zend_Auth_Adapter_Exception If authentication cannot be performed + * @return Zend_Auth_Result + */ + public function authenticate() + { + $user = Doctrine::getTable( $this->_model )->findOneByUsername( $this->_username ); + // $user === false if no record + + $result = array( + 'code' => Zend_Auth_Result::FAILURE, + 'identity' => array( 'username' => $this->_username ), + 'messages' => array() + ); + + if( $user === false ) + { + $result['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND; + $result['messages'][] = 'Username / password invalid'; + } + else if( $user['password'] != $this->_password ) + { + + if( $this->_model == 'User' ) + $maxLogin = User::$LOGIN_ATTEMPTS_ALLOWED; + else + $maxLogin = MPSUser::$LOGIN_ATTEMPTS_ALLOWED; + + if( $user['failed_logins'] < $maxLogin ) + { + $user['failed_logins']++; + $user->save(); + } + + $result['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID; + $result['messages'][] = 'Username / password invalid'; + } + else if( $user->password == $this->_password ) + { + if( $this->_model == 'User' ) + $maxLogin = User::$LOGIN_ATTEMPTS_ALLOWED; + else + $maxLogin = MPSUser::$LOGIN_ATTEMPTS_ALLOWED; + + if( $user['failed_logins'] >= $maxLogin ) + { + $result['code'] = Zend_Auth_Result::FAILURE_UNCATEGORIZED; + $result['messages'][] = 'Your account has been locked out due to an excessive number of bad login attempts. Please follow the forgotten password link to set a new password.'; + } + else + { + if( $user['failed_logins'] != 0 ) + { + $user['failed_logins'] = 0; + $user->save(); + } + + $result['code'] = Zend_Auth_Result::SUCCESS; + $result['identity'] = array( + 'username' => $this->_username, + 'user' => $user + ); + } + } + + return new Zend_Auth_Result( $result['code'], $result['identity'], $result['messages'] ); + } + +} diff --git a/library/OSS/Auth/Password.php b/library/OSS/Auth/Password.php new file mode 100644 index 0000000..311bf5a --- /dev/null +++ b/library/OSS/Auth/Password.php @@ -0,0 +1,222 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * A class to hash and verify passwords using verious methods + * + * @category OSS + * @package OSS_Auth + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Auth_Password +{ + const HASH_PLAINTEXT = 'plaintext'; + const HASH_PLAIN = 'plain'; + const HASH_BCRYPT = 'bcrypt'; + const HASH_MD5 = 'md5'; + const HASH_MD5_SALTED = 'md5-salted'; + const HASH_SHA1 = 'sha1'; + const HASH_SHA1_SALTED = 'sha1-salted'; + const HASH_DOVECOT = 'dovecot:'; + const HASH_CRYPT = 'crypt:'; + const HASH_UNKNOWN = '*unknown*'; + + // Bad salts - in April 2016 it was pointed out that a typo in the code below meant that + // md5.salted and sha1.salted didn't actually use the requested salt string but a fixed + // salt of "md5.salted" and "sha1.salted" respectivily. + // These constants are for backwards compatibility for anyone that used those: + const HASH_MD5_BADSALT = 'md5.salted'; + const HASH_SHA1_BADSALT = 'sha1.salted'; + + /** + * A generic password hashing method using a given configuration array + * + * The parameters expected in `$config` are: + * + * * `pwhash` - a hashing method from the `HASH_` constants in this class + * * `pwsalt` - a hashing salt for HASH_SHA1_SALTED and HASH_MD5_SALTED + * * `hash_cost` - a *cost* parameter for certain hashing functions - e.g. bcrypt (defaults to 9) + * + * @param string $pw The plaintext password to hash + * @param array $config The resources.auth.oss array from `application.ini` + * @throws OSS_Exception + * @return string The hashed password + */ + public static function hash( $pw, $config ) + { + $hash = self::HASH_UNKNOWN; + + if( is_array( $config ) ) + { + if( !isset( $config['pwhash'] ) ) + throw new OSS_Exception( 'Cannot hash password without a hash method' ); + + $hash = $config['pwhash']; + } + else + $hash = $config; + + if( substr( $hash, 0, 8 ) == 'dovecot:' ) + { + return ViMbAdmin_Dovecot::password( substr( $hash, 8 ), $pw, $config['username'] ); + } + else if ( substr( $hash, 0, 6) == 'crypt:' ) + { + $indicator = ''; + $salt_len = 2; + switch( $hash ) + { + case 'crypt:md5': + $salt = '$1$' . OSS_String::randomPassword( 12 ) . '$'; + break; + + case 'crypt:blowfish': + $salt = '$2a$12$' . OSS_String::randomPassword( 22 ) . '$'; + break; + + case 'crypt:sha256': + $salt = '$5$' . OSS_String::randomPassword( 16 ) . '$'; + break; + + case 'crypt:sha512': + $salt = '$6$' . OSS_String::randomPassword( 12 ) . '$'; + break; + + default: + throw new OSS_Exception( 'Unknown crypt password hashing method' ); + } + return crypt( $pw, $salt ); + } + else + { + switch( $hash ) + { + case self::HASH_PLAINTEXT: + case self::HASH_PLAIN: + return $pw; + break; + + case self::HASH_BCRYPT: + if( !isset( $config['hash_cost'] ) ) + $config['hash_cost'] = 9; + + $bcrypt = new OSS_Crypt_Bcrypt( $config['hash_cost'] ); + return $bcrypt->hash( $pw ); + break; + + case self::HASH_MD5: + return md5( $pw ); + break; + + case self::HASH_MD5_SALTED: + return md5( $pw . $config['pwsalt'] ); + break; + + case self::HASH_SHA1: + return sha1( $pw ); + break; + + case self::HASH_SHA1_SALTED: + return sha1( $pw . $config['pwsalt'] ); + break; + + + + case self::HASH_MD5_BADSALT: + return md5( $pw . $config['pwhash'] ); + break; + + case self::HASH_SHA1_BADSALT: + return sha1( $pw . $config['pwhash'] ); + break; + + // UPDATE PHPDOC ABOVE WHEN ADDING NEW METHODS! + + default: + throw new OSS_Exception( 'Unknown password hashing method' ); + } + } + } + + /** + * A generic password verification function for various hashing methods using a given configuration array + * + * @see hash() for full documentation + * + * @param string $pwplain The plaintext password + * @param string $pwhash The hashed password to use for verification + * @param array $config The resources.auth.oss array from `application.ini` + * @throws OSS_Exception + * @return bool True if the passwords match + */ + public static function verify( $pwplain, $pwhash, $config ) + { + $hash = self::HASH_UNKNOWN; + + if( is_array( $config ) ) + { + if( !isset( $config['pwhash'] ) ) + throw new OSS_Exception( 'Cannot verify password without a hash method' ); + + $hash = $config['pwhash']; + } + else + $hash = $config; + + switch( $config['pwhash'] ) + { + case self::HASH_BCRYPT: + if( !isset( $config['hash_cost'] ) ) + $config['hash_cost'] = 9; + + $bcrypt = new OSS_Crypt_Bcrypt( $config['hash_cost'] ); + return $bcrypt->verify( $pwplain, $pwhash ); + break; + } + + if( substr( $hash, 0, 6) == 'crypt:' ) + return crypt( $pwplain, $pwhash ) == $pwhash; + + if( substr( $hash, 0, 8 ) == 'dovecot:' ) + return ViMbAdmin_Dovecot::passwordVerify( substr( $hash, 8 ), $pwhash, $pwplain, $config['username'] ); + + + return $pwhash == self::hash( $pwplain, $config ); + } +} diff --git a/library/OSS/Captcha/Image.php b/library/OSS/Captcha/Image.php new file mode 100644 index 0000000..41dc23d --- /dev/null +++ b/library/OSS/Captcha/Image.php @@ -0,0 +1,99 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Captcha + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Captcha_Image extends Zend_Captcha_Image +{ + + /** + * The constructor generates a Captcha image. + * + * @param int $dotNoise The dot noise level. Default: 100. + * @param int $lineNoise The line noise level. Default: 5. + * @param int $wordLen The length of the Captcha word. Default: 6. + * @param int $timeout The timeout in seconds. Default: 1800. + * @return OSS_Captcha_Image + */ + public function __construct( $dotNoise = 100, $lineNoise = 5, $wordLen = 6, $timeout = 1800 ) + { + parent::__construct(); + + $captchaDir = OSS_Utils::getTempDir() . '/captchas'; + + if( !file_exists( $captchaDir ) ) + mkdir( $captchaDir, 0777, true ); + + if( strpos( dirname( __FILE__ ), 'src/' ) === false ) + $font = dirname( __FILE__ ) . '/../../data/font/freeserif.ttf'; + else + $font = dirname( __FILE__ ) . '/../../../data/font/freeserif.ttf'; + + $this->setTimeout( $timeout ) + ->setWordLen( $wordLen ) + ->setHeight( 80 ) + ->setFont( $font ) + ->setFontSize( 40 ) + ->setImgDir( $captchaDir ) + ->setDotNoiseLevel( $dotNoise ) + ->setLineNoiseLevel( $lineNoise ); + + return $this; + } + + /** + * + * Validates a captcha with the given ID against the given string. + * + * @param string $id The Captcha ID + * @param string $value The captcha value from the 'user' being tested + * @return bool + */ + public static function _isValid( $id, $value ) + { + if( isset( $_SESSION[ 'Zend_Form_Captcha_' . $id ]['word'] ) && $_SESSION[ 'Zend_Form_Captcha_' . $id ]['word'] == $value ) + return true; + + return false; + } + +} diff --git a/library/OSS/Controller/Action.php b/library/OSS/Controller/Action.php new file mode 100644 index 0000000..f3112f8 --- /dev/null +++ b/library/OSS/Controller/Action.php @@ -0,0 +1,242 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @link https://github.com/opensolutions/OSS-Framework/wiki/OSS_Controller Online documentation + * @category OSS + * @package OSS_Controller + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Controller_Action extends Zend_Controller_Action +{ + // traits we want to use + // + // use OSS_Controller_Action_Trait_Namespace; + // use OSS_Controller_Action_Trait_Doctrine2User; + // use OSS_Controller_Action_Trait_Auth; + // use OSS_Controller_Action_Trait_AuthRequired; + // use OSS_Controller_Action_Trait_Doctrine2Cache; + // use OSS_Controller_Action_Trait_Doctrine2; + // use OSS_Controller_Action_Trait_Mailer; + // use OSS_Controller_Action_Trait_License; + // use OSS_Controller_Action_Trait_Logger; + // use OSS_Controller_Action_Trait_Smarty; + // use OSS_Controller_Action_Trait_StatsD; + // use OSS_Controller_Action_Trait_Freshbooks; + // use OSS_Controller_Action_Trait_Messages; + // use OSS_Controller_Action_Trait_News; + // use OSS_Controller_Action_Trait_PdfGenerator; + + + /** + * A variable to hold an instance of the bootstrap object + * + * @var Zend_Application_Bootstrap_Bootstrap An instance of the bootstrap object + */ + protected $_bootstrap = null; + + /** + * @var array An array representation of the application.ini + */ + protected $_options = null; + + + /** + * An array to hold the names of traits that have been initialised + * + * This can be used by other traits to check for dependancies. + * + * @var array An array to hold the names of traits that have been initialised + */ + protected $_initialisedTraits = []; + + + + /** + * Override the Zend_Controller_Action's constructor (which is called + * at the very beginning of this function anyway). + * + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function __construct( + Zend_Controller_Request_Abstract $request, + Zend_Controller_Response_Abstract $response, + array $invokeArgs = null ) + { + // get the bootstrap object and set it in the registry + $this->setBootstrap( $invokeArgs['bootstrap'] ); + + // get the options and set it in the registry + $this->_options = $this->getBootstrap()->getOptions(); + Zend_Registry::set( 'options', $this->_options ); + + // call the parent's version where all the Zend magic happens + parent::__construct( $request, $response, $invokeArgs ); + + // if we issue a redirect, we want it to exit immediatly + $this->getHelper( 'Redirector' )->setExit( true ); + + // ensure CLI actions are only run from the CLI + if( ( $request->getControllerName() == 'cli' || substr( $request->getActionName(), 0, 3 ) == 'cli' ) && php_sapi_name() != 'cli' ) + die( 'Invalid action - CLI only' ); + + $this->initialiseTraits( $request, $response, $invokeArgs ); + } + + + /** + * All declared traits can have their own initialisation method. This function + * iterates over the declared traits and initialises them if necessary. + * + * NB - order of initialisation is order of declaration + * + * This function should be called from the contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + private function initialiseTraits( $request, $response, $invokeArgs ) + { + foreach( get_declared_traits() as $trait ) + { + $fn = "{$trait}_Init"; + if( method_exists( $this, $fn ) ) + $this->$fn( $request, $response, $invokeArgs ); + } + } + + /** + * If a named trait has been initialised, returns true, else false + * + * @param string $trait The name of the trait to check for + * @return bool Whether the trait has been initialised or not + */ + protected function traitIsInitialised( $trait ) + { + return isset( $this->_initialisedTraits[ $trait ] ) && $this->_initialisedTraits[ $trait ]; + } + + /** + * Mark a trait as initialised + * + * @param string $trait The name of the trait to check for + */ + protected function traitSetInitialised( $trait ) + { + $this->_initialisedTraits[ $trait ] = true; + } + + /** + * A utility method to get a named resource. + * + * @param string $resource + * @return Zend_Application_Resource_ResourceAbstract + */ + public function getResource( $resource ) + { + return $this->getBootstrap()->getResource( $resource ); + } + + /** + * Set the Zend application bootstrap in this instance and in the + * global registry. + * + * @param Zend_Application_Bootstrap_Bootstrap $bs + */ + protected function setBootstrap( $bs ) + { + $this->_bootstrap = $bs; + Zend_Registry::set( 'bootstrap', $this->_bootstrap ); + } + + /** + * Get the Zend application bootstrap + * + * @return Zend_Application_Bootstrap_Bootstrap + */ + protected function getBootstrap() + { + if( $this->_bootstrap === null ) + $this->_bootstrap = Zend_Registry::get( 'bootstrap' ); + + return $this->_bootstrap; + } + + /** + * Get the application options associative array + * + * @return array The options array + */ + public function getOptions() + { + return $this->_options; + } + + + /** + * Wrapper function for Zend's _redirect() to **ensure** we end execution after the redirection. + * + * @param string $where Defaults to '' - where to redirect to (e.g. controller/action/p1/v1/...) + * @param string $from Where you redirected from for logging purposes. Defaults to requested controller/action. + * @return void This ensures execution ends. + */ + public function redirectAndEnsureDie( $where = '', $from = null ) + { + $this->_redirect( $where ); + + // if we fall through, catch it + if( $from === null ) + $from = $this->getRequest()->getControllerName() . '/' . $this->getRequest()->getActionName(); + + $this->getLogger()->err( "Unexpected fall through from redirect() from {$from}" ); + die( $this->getOptions()['messages']['unexpected_error'] ); + } + +} diff --git a/library/OSS/Controller/Action/Trait/AsteriskMI.php b/library/OSS/Controller/Action/Trait/AsteriskMI.php new file mode 100644 index 0000000..349e4d7 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/AsteriskMI.php @@ -0,0 +1,103 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for PHPAGI's Asterisk Manager Interface + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_AsteriskMI +{ + + /** + * A variable to hold instances of Asterisk Manager Interface(s) + * + * @var AGI_AsteriskManager[] Instances of Asterisk Manager Interface(s) + */ + protected $_asteriskMIs = null; + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_StatsD_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_AsteriskMI' ); + } + + /** + * Returns an Asterisk MI object + * + * Requires a configuration block such as: + * + * ondemand_rescoures.ami.default.host = 'yourhost' + * ondemand_rescoures.ami.default.username = 'username' + * ondemand_rescoures.ami.default.secret = 'secret' + * + * @param string $asterisk The Asterisk server to query (which corresponds to a configuration block); by default `default` + * @return AGI_AsteriskManager The AMI object + */ + public function getAsteriskMI( $asterisk = 'default' ) + { + if( $this->_asteriskMIs[ $asterisk ] === null || !isset( $this->_asteriskMIs[ $asterisk ] ) ) + { + $plugin = new OSS_Resource_AsteriskMI( $this->getOptions()['ondemand_resources']['ami'][$asterisk] ); + $this->getBootstrap()->registerPluginResource( $plugin ); + + $this->_asteriskMIs[ $asterisk ] = $plugin->getInstance(); + } + + return $this->_asteriskMIs[ $asterisk ]; + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/Auth.php b/library/OSS/Controller/Action/Trait/Auth.php new file mode 100644 index 0000000..9f7b22c --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Auth.php @@ -0,0 +1,120 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Auth + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Auth +{ + + /** + * A variable to hold the identity object + * + * @var Zend_Auth An instance of the user's identity or false + */ + protected $_auth = false; + + /** + * An array holding the identify of the user as returned by Zend_Auth->getIdentity(). + * + * There are two elements: + * + * ['username'] => (string) the username of the user + * ['user'] => the User object (\Entities\User or other appropriate class) + * + * Will be !false if there is a valid identity + * + * @var array An instance of the user's identity or false + */ + protected $_identity = false; + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Auth_Init( $request, $response, $invokeArgs ) + { + $this->_identity = $this->getAuth()->getIdentity(); + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Auth' ); + } + + /** + * Return the Zend_Auth instance. + * @return Zend_Auth The Zend_Auth instance or false + */ + protected function getAuth() + { + if( $this->_auth == false ) + $this->_auth = $this->getBootstrap()->getResource( 'auth' ); + + return $this->_auth; + } + + /** + * Returns the identify object for the Zend_Auth session. + * + * Will be !false if there is a valid identity + * + * @return array The Zend_Auth identity object or false + */ + protected function getIdentity() + { + if( $this->_identity === false ) + $this->_identity = $this->getAuth()->getIdentity(); + + return $this->_identity; + } + +} + diff --git a/library/OSS/Controller/Action/Trait/AuthRequired.php b/library/OSS/Controller/Action/Trait/AuthRequired.php new file mode 100644 index 0000000..a887d57 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/AuthRequired.php @@ -0,0 +1,88 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for AuthRequired + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_AuthRequired +{ + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * Works best when `OSS_Controller_Action_Trait_Messages`, `OSS_Controller_Action_Trait_Namespace` + * and `OSS_Controller_Action_Trait_Smarty` are available. + * + * @see OSS_Controller_Action_Trait_Auth + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_AuthRequired_Init( $request, $response, $invokeArgs ) + { + if( !$this->traitIsInitialised( 'OSS_Controller_Action_Trait_Auth' ) ) + die( 'OSS_Controller_Action_Trait_Auth required for OSS_Controller_Action_Trait_AuthRequired (in OSS_Controller_Action_Trait_AuthRequired_Init()' ); + + + if( !$this->getAuth()->hasIdentity() ) + { + if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Messages' ) ) + $this->addMessage( "Please login below.", OSS_Message::ERROR ); + + if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Namespace' ) ) + $this->getSessionNamespace()->postAuthRedirect = $this->getRequest()->getPathInfo(); + + $this->redirectAndEnsureDie( 'auth/login' ); + } + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_AuthRequired' ); + } + +} + diff --git a/library/OSS/Controller/Action/Trait/Cli.php b/library/OSS/Controller/Action/Trait/Cli.php new file mode 100644 index 0000000..abcbf60 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Cli.php @@ -0,0 +1,179 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for CLI controllers + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Cli +{ + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Cli_Init( $request, $response, $invokeArgs ) + { + Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' ); + if ( php_sapi_name() != 'cli' ) + { + $this->getLogger()->warn( 'Non CLI access to a CLI controller from ' . $_SERVER['REMOTE_ADDR'] . ' to ' . $_SERVER['REQUEST_URI'] ); + die( 'Unauthorised access!' ); + } + + // Used in connection with a CLI Tool script. See for example: + // https://github.com/inex/IXP-Manager/blob/master/bin/ixptool.php + + $this->_verbose = $this->getFrontController()->getParam( 'verbose', false ); + $this->_debug = $this->getFrontController()->getParam( 'debug', false ); + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Cli' ); + } + + + /** + * Verbose flag + */ + private $_verbose = false; + + /** + * Debug flag + */ + private $_debug = false; + + + /** + * True if the user has requested verbose mode + */ + public function isVerbose() + { + return $this->_verbose; + } + + /** + * If running in verbose mode, echoes the request msg + * + * @param string $msg The message + * @param bool $implicitNewline Set to false to prevent a newline from being echoed + */ + public function verbose( $msg = "", $implicitNewline = true ) + { + if( $this->_verbose ) + echo "{$msg}" . ( $implicitNewline ? "\n" : "" ); + } + + + /** + * True if the user has requested debug mode + */ + public function isDebug() + { + return $this->_debug; + } + + /** + * If running in debug mode, echoes the request msg + * + * @param string $msg The message + * @param bool $implicitNewline Set to false to prevent a newline from being echoed + */ + public function debug( $msg = "", $implicitNewline = true ) + { + if( $this->_debug ) + echo "{$msg}" . ( $implicitNewline ? "\n" : "" ); + } + + + /** + * Utility function to verify that a file passed via --config exists and is readable. + * + * No attempt is made to parse / use the file as calling functions may use this for Smarty, + * Zend, or other configuration file types. + * + * @throws Zend_Validate_Exception If no file is specified or if the file cannot be read + * @return string The specified verified (as existing and reabable) config file + */ + public function loadConfig() + { + $cfile = $this->getFrontController()->getParam( 'config', false ); + if( $cfile ) + { + if( file_exists( $cfile ) && is_readable( $cfile ) ) + return $cfile; + + throw new Zend_Validate_Exception( 'Cannot open / read specificed configuration file' ); + } + + throw new Zend_Validate_Exception( 'No configuration file specificed - please use --config=/path/to/file' ); + } + + /** + * Write text from a variable (e.g. configuration data) to a file in an atomic way. + * + * I.e. write to temporary file first (`$filename . '$$'`) and then move to the + * intended file name. + * + * @param string $filename The full path and filename of the target + * @param string $config The text / configuration data + * @return bool Success flag + */ + public function writeConfig( $filename, $config ) + { + if( @file_put_contents( $filename . '.$$', $config, LOCK_EX ) !== false ) + if( @rename( $filename . '.$$', $filename ) !== false ) + return true; + + return false; + } + +} + diff --git a/library/OSS/Controller/Action/Trait/Doctrine2.php b/library/OSS/Controller/Action/Trait/Doctrine2.php new file mode 100644 index 0000000..3eadcbd --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Doctrine2.php @@ -0,0 +1,191 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Doctine2 + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Doctrine2 +{ + /** + * A variable to hold the Doctrine2 entity manager + * + * @var \Doctrine\ORM\EntityManager An instance of the Doctrine2 entity manager + */ + static private $_d2em = null; + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Doctrine2_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Doctrine2' ); + } + + /** + * Returns an instance of the Doctrine2 entity manager + * + * @param string $db Which database entity manager to get (for use when using multiple databases) + * @return Doctrine\ORM\EntityManager The Doctrine2 Entity Manager + */ + public function getEntityManager( $db = 'default' ) + { + if( self::$_d2em === null || !isset( self::$_d2em[ $db ] ) ) + { + $plugin = new OSS_Resource_Doctrine2( $this->_options['resources']['doctrine2'] ); + $this->getBootstrap()->registerPluginResource( $plugin ); + self::$_d2em[ $db ] = $plugin->getDoctrine2( $db ); + Zend_Registry::set( 'd2em', self::$_d2em ); + } + + return self::$_d2em[ $db ]; + } + + /** + * Alias for getEntityManager() + * + * @see getEntityManager() + * @param string $db Which database entity manager to get (for use when using multiple databases) + * @return Doctrine\ORM\EntityManager The Doctrine2 Entity Manager + */ + public function getD2EM( $db = 'default' ) + { + return $this->getEntityManager( $db ); + } + + /** + * Returns an instance of the requested Doctrine2 repository class + * + * @param string $repository Which repository to instantiate + * @param string $db Which database entity manager to get (for use when using multiple databases) + * @return Doctrine\ORM\EntityRepository The Doctrine2 Entity Repository + */ + public function getD2R( $repository, $db = 'default' ) + { + return $this->getEntityManager( $db )->getRepository( $repository ); + } + + /** + * Returns an instance of the Doctrine2 entity manager + * + * @param string $db Which database entity manager to get (for use when using multiple databases) + * @return Doctrine\ORM\EntityManager The Doctrine2 Entity Manager + */ + public static function getEntityManagerStatic( $db = 'default' ) + { + if( self::$_d2em === null || !isset( self::$_d2em[ $db ] ) ) + { + $plugin = Zend_Registry::get( 'bootstrap' )->getPluginResource( 'doctrine2' ); + + if( $plugin == null ) + { + $plugin = new OSS_Resource_Doctrine2( Zend_Registry::get( 'options' )['resources']['doctrine2'] ); + $this->getBootstrap()->registerPluginResource( $plugin ); + } + + self::$_d2em[ $db ] = $plugin->getDoctrine2( $db ); + Zend_Registry::set( 'd2em', self::$_d2em ); + } + + return self::$_d2em[ $db ]; + } + + /** + * Alias for getEntityManagerStatic() + * + * @see getEntityManagerStatic() + * @param string $db Which database entity manager to get (for use when using multiple databases) + * @return Doctrine\ORM\EntityManager The Doctrine2 Entity Manager + */ + public static function getD2EMS( $db = 'default' ) + { + return self::getEntityManagerStatic( $db ); + } + + + /** + * Clear an entity manager instance from the local static property + * + * This is needed specifically in the case where one catches a Doctrine2 exception and wishes + * to used the EntityManager again - the exception closes the entity manager so it needs to be + * cleared and then recreated. + * + * @param string $db Which database entity manager to clear (for use when using multiple databases) + * @return Zend_Controller_Action For fluent interfaces + */ + public static function clearEntityManagerPropertyStatic( $db = 'default' ) + { + if( isset( self::$_d2em[ $db ] ) ) + { + self::$_d2em[ $db ] = null; + unset( self::$_d2em[ $db ] ); + } + + return $this; + } + + /** + * Clear an entity manager instance from the local static property + * + * @see clearEntityManagerPropertyStatic() + * + * @param string $db Which database entity manager to clear (for use when using multiple databases) + * @return Zend_Controller_Action For fluent interfaces + */ + public static function clearEntityManagerProperty( $db = 'default' ) + { + return self::clearEntityManagerPropertyStatic( $db ); + } + +} + diff --git a/library/OSS/Controller/Action/Trait/Doctrine2Cache.php b/library/OSS/Controller/Action/Trait/Doctrine2Cache.php new file mode 100644 index 0000000..3bd1802 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Doctrine2Cache.php @@ -0,0 +1,99 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Doctine2Cache + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Doctrine2Cache +{ + /** + * A variable to hold the Doctrine2 cache + * + * @var \Doctrine\Common\Cache\AbstractCache An instance of the Doctrine2 cache + */ + static private $_d2cache = null; + + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Doctrine2Cache_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Doctrine2Cache' ); + } + + + /** + * Returns the Doctrine2 cache object + * + * @return \Doctrine\Common\Cache\AbstractCache The Doctrine cache object + */ + public function getD2Cache() + { + if( self::$_d2cache === null ) + { + if( Zend_Registry::isRegistered( 'd2cache' ) ) + self::$_d2cache = Zend_Registry::get( 'd2cache' ); + else + { + self::$_d2cache = $this->getBootstrap()->getResource( 'doctrine2cache' ); + Zend_Registry::set( 'd2cache', self::$_d2cache ); + } + } + + return self::$_d2cache; + } + +} + diff --git a/library/OSS/Controller/Action/Trait/Doctrine2Frontend.php b/library/OSS/Controller/Action/Trait/Doctrine2Frontend.php new file mode 100644 index 0000000..ebd4a3b --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Doctrine2Frontend.php @@ -0,0 +1,781 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Doctine2Frontend + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Doctrine2Frontend +{ + /** + * Parameters used by the frontend controller + * @var array Parameters used by the frontend controller + */ + protected $_feParams = null; + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Doctrine2Frontend_Init( $request, $response, $invokeArgs ) + { + // is this controller disabled? + if( isset( $this->_options['frontend']['disabled'][ $this->getRequest()->getControllerName() ] ) + && $this->_options['frontend']['disabled'][ $this->getRequest()->getControllerName() ] ) + { + $this->addMessage( _( 'This controller has been disabled.' ), OSS_Message::ERROR ); + $this->redirectAndEnsureDie( '' ); + } + + $this->_feInit(); + $this->view->FE_COL_TYPES = self::$FE_COL_TYPES; + + // check is this action is allowed + if( isset( $this->_feParams->allowedActions ) && !in_array( $request->getActionName(), $this->_feParams->allowedActions ) ) + $this->redirectAndEnsureDie( 'error/insufficient-permissions' ); + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Doctrine2Frontend' ); + } + + static public $FE_COL_TYPES = [ + 'HAS_ONE' => 'hasOne', + 'DATETIME' => 'datetime', + 'DATE' => 'date', + 'TIME' => 'time', + 'SCRIPT' => 'script', + 'XLATE' => 'xlate', + 'YES_NO' => 'yes_no' + ]; + + + /** + * Standard initialisation tasks for all Frontend controllers + */ + public function init() + { + } + + /** + * This is meant to be overridden. + * + * @throws OSS_Exception + */ + protected function _feInit() + { + throw new OSS_Exception( 'FrontEnd controllers require an _feInit() function' ); + } + + /** + * Displays the standard Frontend template or the controllers overridden version. + * + * @see _resolveTemplate() + * @param string $tpl The template to display + * @return void + */ + protected function _display( $tpl ) + { + $this->view->display( $this->_resolveTemplate( $tpl, true ) ); + + // we don't want the template selected automatically here - we'll choose it outselves + Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' ); + } + + /** + * Resolves the standard Frontend template or the controllers overridden version. + * + * All frontend actions have their own template: `frontend/{$action}.phtml` which is + * displayed by default. You can however override these by creating a template named: + * `{$controller}/{$action}.phtml`. This function looks for an overriding template + * and displays that if it exists, otherwise it displays the default. + * + * This will also work for subdirectories: e.g. `$tpl = forms/add.phtml` is also valid. + * + * @param string $tpl The template to display + * @param bool $throw If true, throws an exception is no template is found + * @return string|bool The template to use of false if none found + * @throws OSS_Exception + */ + protected function _resolveTemplate( $tpl, $throw = false ) + { + if( $this->view->templateExists( $this->getRequest()->getControllerName() . "/{$tpl}" ) ) + return $this->getRequest()->getControllerName() . "/{$tpl}"; + else if( $this->view->templateExists( "frontend/{$tpl}" ) ) + return "frontend/{$tpl}"; + + if( $throw ) + throw new OSS_Exception( sprintf( _( "No template exists in frontend or controller's view directory for %s "), $tpl ) ); + + return false; + } + + + /** + * The index / default action _forwards to the default action as configured + * or else the list action by default. + * + * Looks for a specific action in `$_feParams['defaultAction']` + */ + public function indexAction() + { + if( $this->feGetParam( 'defaultAction' ) ) + $this->_forward( $this->feGetParam( 'defaultAction' ) ); + else + $this->_forward( 'list' ); + } + + /** + * Function which can be over-ridden to perform any pre-list tasks + * + * @return void + */ + protected function listPreamble() + {} + + + /** + * List the contents of a database table. + */ + public function listAction() + { + $this->listPreamble(); + + $this->view->data = $this->listGetData(); + + $this->view->listPreamble = $this->_resolveTemplate( 'list-preamble.phtml' ); + $this->view->listPostamble = $this->_resolveTemplate( 'list-postamble.phtml' ); + $this->view->listRowMenu = $this->_resolveTemplate( 'list-row-menu.phtml' ); + $this->view->listToolbar = $this->_resolveTemplate( 'list-toolbar.phtml' ); + $this->view->listScript = $this->_resolveTemplate( 'js/list.js' ); + $this->view->listAddonScript = $this->_resolveTemplate( 'js/list-addon.js' ); + $this->_display( 'list.phtml' ); + } + + + /** + * Provide single object for view. Uses `listGetData()` + * + * @param int $id The `id` of the row to load for `viewAction`. + */ + protected function viewGetData( $id ) + { + $data = $this->listGetData( $id ); + + if( is_array( $data ) && isset( $data[0] ) ) + return $data[0]; + + $this->addMessage( 'Could not load the requested object - it does not exist.', OSS_Message::ERROR ); + $this->redirectAndEnsureDie( $this->_getBaseUrl() . '/index' ); + } + + /** + * Function which can be over-ridden to perform any pre-view tasks + * + * @param object $object The Doctrine2 entity to delete + */ + protected function preView( $object ) + {} + + /** + * Prepares data for view and AJAX view + * + * @return void + */ + protected function viewPrepareData() + { + $object = $this->viewGetData( $this->_getParam( 'id' ) ); + $this->preView( $object ); + + $this->view->object = $object; + + // some of our stock templates also use $row instead of $object (especially SCRIPT types) + $this->view->row = $object; + } + + /** + * View an object + * + * @return void + */ + public function viewAction() + { + $this->viewPrepareData(); + + $this->view->viewPreamble = $this->_resolveTemplate( 'view-preamble.phtml' ); + $this->view->viewPostamble = $this->_resolveTemplate( 'view-postamble.phtml' ); + $this->view->viewToolbar = $this->_resolveTemplate( 'view-toolbar.phtml' ); + $this->view->viewScript = $this->_resolveTemplate( 'js/view.js' ); + $this->_display( 'view.phtml' ); + } + + /** + * Function which can be over-ridden to perform any pre-load tasks + */ + protected function preLoadObject( $id ) + { + return $id; + } + + /** + * Function which can be over-ridden to perform any post-load tasks such + * as checking ownership + */ + protected function postLoadObject( $object ) + { + return $object; + } + + + /** + * Load an object from the database with the given id + * + * @param int $id The ID of the object to load + * @param bool $redirect If set to false, returns regardless with null + * @return object|null The Entity object or null + */ + protected function loadObject( $id, $redirect = true ) + { + $object = null; + + $id = $this->preLoadObject( $id ); + + if( !$id || !( $object = $this->getD2EM()->getRepository( $this->feGetParam( 'entity') )->find( $id ) ) ) + { + if( $redirect ) + { + $this->addMessage( 'The requested object does not exist.', OSS_Message::ERROR ); + $this->redirectAndEnsureDie( $this->_getBaseUrl() . '/index' ); + } + } + + $this->postLoadObject( $object ); + + return $object; + } + + + /** + * Function which can be over-ridden to perform any pre-deletion tasks + * + * You can stop the deletion by returning false but you should also add a + * message to explain why. + * + * @param object $object The Doctrine2 entity to delete + * @return bool Return false to stop / cancel the deletion + */ + protected function preDelete( $object ) + { + return true; + } + + /** + * Function which can be over-ridden to perform any post-deletion tasks + * + * Database `flush()` has been successfully completed at this stage + * + * If you return with true, then the standard log message and OSS_Message + * will be performed. If you want to override these, return false. + * + * NB: also calls `postFlush()` + * + * @param object $object The Doctrine2 entity to delete + * @return bool Return false to stop / cancel standard log and OSS_Message + */ + protected function postDelete( $object ) + { + return $this->postFlush( $object ); + } + + + /** + * Gets the ID of the object for deletion - which, by default, returns the id parameter from the request + * + * @return int|false + */ + protected function deleteResolveId() + { + return $this->_getParam( 'id', false ); + } + + /** + * Function which can be over-ridden to perform any pre-delete tasks + * + * @return void + */ + protected function deletePreamble() + {} + + /** + * Delete and element from the table + */ + public function deleteAction() + { + $this->deletePreamble(); + + if( $this->feGetParam( 'readonly' ) === true ) + return $this->_forward( 'index' ); + + $object = $this->loadObject( $this->deleteResolveId() ); + + try + { + if( $this->preDelete( $object ) ) + { + $did = $object->getId(); + $this->getD2EM()->remove( $object ); + $this->getD2EM()->flush(); + + if( $this->postDelete( $object ) ) + { + $this->getLogger()->info( sprintf( _( 'User %d deleted %s object with id %d' ), + $this->getUser()->getId(), $this->feGetParam( 'nameSingular' ), $did ) + ); + + if( $this->deleteDestinationOnSuccess() === false ) + $this->addMessage( _( 'The requested object has been deleted' ), OSS_Message::SUCCESS ); + } + } + } + catch( Exception $e ) + { + $this->getLogger()->err( sprintf( _( 'User %s could not delete %s object with id %d' ), + $this->getUser()->getId(), $this->feGetParam( 'nameSingular' ), $object->getId() ) + ); + throw $e; + } + + $this->_redirect( $this->_getBaseUrl() . '/index' ); + } + + /** + * You can add `OSS_Message`s here and redirect to a custom destination after a + * successful deletion operation. + * + * By default it returns `false`. + * + * On `false`, the default action (`index`) is called and a standard success message is displayed. + * + * @return bool `false` for standard message and redirection, otherwise redirect within this function + */ + protected function deleteDestinationOnSuccess() + { + return false; + } + + + /** + * Preparation hook that can be overridden by subclasses for add and edit. + * + * This is called just before we process a possible POST / submission and + * will allow us to change / alter the form or object. + * + * @param OSS_Form $form The Send form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True if we are editing, otherwise false + */ + protected function addPrepare( $form, $object, $isEdit ) + {} + + /** + * Prevalidation hook that can be overridden by subclasses for add and edit. + * + * This is called if the user POSTs a form just before the form is validated by Zend + * + * @param OSS_Form $form The Send form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True if we are editing, otherwise false + * @return bool If false, the form is not validated or processed + */ + protected function addPreValidate( $form, $object, $isEdit ) + { + return true; + } + + /** + * Postvalidation hook that can be overridden by subclasses for add and edit. + * + * This is called if the user POSTs a form just after the form passes standard + * Zend_Form validation. + * + * This hook can hijack the ensure form processing by returning false. + * + * It can also cause validation to fail with a message by adding an + * `OSS_Message` and returning false. + * + * @param OSS_Form $form The Send form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True if we are editing, otherwise false + * @return bool If false, the form is not processed + */ + protected function addPostValidate( $form, $object, $isEdit ) + { + return true; + } + + /** + * Pre db flush hook that can be overridden by subclasses for add and edit. + * + * This is called if the user POSTs a valid form after the posted + * data has been assigned to the object and just before it is (persisted + * if adding) and the database is flushed. + * + * This hook can prevent flushing by returning false. + * + * **NB: You should not `flush()` here unless you know what you are doing** + * + * A call to `flush()` is made after this method returns true ensuring a + * transactional `flush()` for all. + * + * @param OSS_Form $form The Send form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True if we are editing, otherwise false + * @return bool If false, the form is not persisted + */ + protected function addPreFlush( $form, $object, $isEdit ) + { + return true; + } + + /** + * Post database flush hook that can be overridden by subclasses for add and edit. + * + * This is called if the user POSTs a valid form after the posted + * data has been flushed to the database. + * + * If you return `false`, the the standard log and OSS_Message will not be + * created / displayed and a `redirect()` will not be performed. + * + * NB: also calls `postFlush()` + * + * @param OSS_Form $form The Send form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True if we are editing, otherwise false + * @return bool If false, supress standard log and OSS_Message and the redirection + */ + protected function addPostFlush( $form, $object, $isEdit ) + { + return $this->postFlush( $object ); + } + + /** + * Post database flush hook that can be overridden by subclasses and is called by + * default for a successful add / edit / delete. + * + * Called by `addPostFlush()` and `postDelete()` - if overriding these, ensure to + * call this if you have overridden it. + * + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @return bool + */ + protected function postFlush( $object ) + { + return true; + } + + + /** + * Post process hook that can be overridden by subclasses for add and edit actions. + * + * This is called immediately after the initstantiation of the form object and, if + * editing, includes the Doctrine2 entity `$object`. + * + * If you need to have, for example, edit values set in the form, then use the + * `addPrepare()` hook rather than this one. + * + * @see addPrepare() + * @param OSS_Form $form The form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True of we are editing an object, false otherwise + * @param array $options Options passed onto Zend_Form + * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked + */ + protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null ) + { + } + + /** + * Get the `Zend_Form` object for adding / editing actions with some processing. + * + * You should not override this but rather the `formPostProcess()` function to + * make changes immediately after the form object has been instantiated. + * + * @param bool $isEdit True of we are editing an object, false otherwise + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param array $options Options passed onto Zend_Form + * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked + * @return Zend_Form + */ + protected function getForm( $isEdit, $object, $options = null, $cancelLocation = null ) + { + $options['cancelLocation'] = $cancelLocation === null ? $this->_getBaseUrl() . '/index' : $cancelLocation; + $options['isEdit'] = $isEdit; + + $formName = $this->feGetParam( 'form' ); + $form = new $formName( $options ); + + $form->setAction( + OSS_Utils::genUrl( + $this->getRequest()->getControllerName(), + ( $isEdit ? 'edit' : 'add' ), + $this->getRequest()->getModuleName() == "index" ? false : $this->getRequest()->getModuleName(), + [ 'id' => $object->getId() ] + ) + ); + + $this->formPostProcess( $form, $object, $isEdit, $options, $cancelLocation ); + return $form; + } + + /** + * You can add `OSS_Message`s here and redirect to a custom destination after a + * successful add / edit operation. + * + * By default it returns `false`. + * + * On `false`, the default action (`index`) is called and a standard success message is displayed. + * + * + * @param OSS_Form $form The form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True of we are editing an object, false otherwise + * @return bool `false` for standard message and redirection, otherwise redirect within this function + */ + protected function addDestinationOnSuccess( $form, $object, $isEdit ) + { + return false; + } + + /** + * This is a part off add action, in this section the form assign to entity + * and changes are saved. + * + * @param OSS_Form $form The form object + * @param object $object The Doctrine2 entity (being edited or blank for add) + * @param bool $isEdit True of we are editing an object, false otherwise + * @return void + */ + protected function addProcessForm( $form, $object, $isEdit ) + { + do{ + try + { + if( !$this->addPostValidate( $form, $object, $isEdit ) ) + break; + + $form->assignFormToEntity( $object, $this, $isEdit ); + + if( $this->addPreFlush( $form, $object, $isEdit ) ) + { + if( !$isEdit ) + { + // make sure we're not already persist()ed: + if( $this->getD2EM()->getUnitOfWork()->getEntityState( $object ) == \Doctrine\ORM\UnitOfWork::STATE_NEW ) + $this->getD2EM()->persist( $object ); + } + + $this->getD2EM()->flush(); + + if( $this->addPostFlush( $form, $object, $isEdit ) ) + { + $this->getLogger()->info( + sprintf( _( 'User %d %s %s object with id %d' ), + $this->getUser()->getId(), $isEdit ? _( 'edited' ) : _( 'added' ), + $this->feGetParam( 'nameSingular' ), $object->getId() + ) + ); + + return true; + } + } + } + catch( Exception $e ) + { + $this->getLogger()->err( + sprintf( _( 'ERROR - FAILED: User %d %s %s object with id %d' ) . "\n" . $e, + $this->getUser()->getId(), $isEdit ? _( 'edited' ) : _( 'added' ), + $this->feGetParam( 'nameSingular' ), $object->getId() + ) + ); + throw( $e ); + } + }while( false ); + + return false; + } + + /** + * Gets the ID of the object for editing - which, by default, returns the id parameter from the request + * + * @return int|false + */ + protected function editResolveId() + { + return $this->_getParam( 'id', false ); + } + + + /** + * Function which can be over-ridden to perform any pre-add/edit tasks + * + * @return void + */ + protected function addPreamble() + {} + + /** + * Add (or edit) an object + */ + public function addAction() + { + $this->addPreamble(); + + if( $this->feGetParam( 'readonly' ) === true ) + return $this->_forward( 'index' ); + + $this->view->isEdit = $isEdit = false; + + $eid = $this->editResolveId(); + + if( $eid && is_numeric( $eid ) ) + { + $this->view->isEdit = $isEdit = true; + + $this->view->object = $object = $this->loadObject( $eid ); + + $this->view->form = $form = $this->getForm( $isEdit, $object ); + $form->assignEntityToForm( $object, $this ); + if( $form->getElement( 'submit' ) ) + $form->getElement( 'submit' )->setLabel( 'Save Changes' ); + } + else + { + $this->view->object = $object = new $this->_feParams->entity(); + $this->view->form = $form = $this->getForm( $isEdit, $object ); + } + + $this->addPrepare( $form, $object, $isEdit ); + + if( $this->getRequest()->isPost() && $this->addPreValidate( $form, $object, $isEdit ) && $form->isValid( $_POST ) ) + { + if( $this->addProcessForm( $form, $object, $isEdit ) ) + { + if( $this->addDestinationOnSuccess( $form, $object, $isEdit ) === false ) + { + $this->addMessage( $this->feGetParam( 'titleSingular' ) . ( $isEdit ? ' edited.' : ' added.' ), OSS_Message::SUCCESS ); + $this->redirectAndEnsureDie( $this->_getBaseUrl() . "/index" ); + } + } + } + + $this->view->addPreamble = $this->_resolveTemplate( 'add-preamble.phtml' ); + $this->view->addPostamble = $this->_resolveTemplate( 'add-postamble.phtml' ); + $this->view->addToolbar = $this->_resolveTemplate( 'add-toolbar.phtml' ); + $this->view->addScript = $this->_resolveTemplate( 'js/add.js' ); + + $this->_display( 'add.phtml' ); + } + + + + /** + * Edit an object - just forwards to addAction() + */ + public function editAction() + { + $this->_forward( 'add' ); + } + + + + + /** + * Set a frontend parameter + * + * @param string $p The parameter name + * @param mixed $v The value + */ + protected function feSetParam( $p, $v ) + { + $this->_feParams->$p = $v; + } + + /** + * Get a frontend parameter + * + * @return mixed The frontend parameter (or null if null or not set) + */ + protected function feGetParam( $p ) + { + if( isset( $this->_feParams->$p ) ) + return $this->_feParams->$p; + + return null; + } + + /** + * Gets controller or module/controller string + * + * If module is index that means that its default, function will return only controller. + * Otherwise it will return module/controller. It will allow front end usage in modules. + * + * + * @return string + */ + protected function _getBaseUrl() + { + if( $this->getRequest()->getModuleName() == "index" ) + return $this->getRequest()->getControllerName(); + else + return $this->getRequest()->getModuleName() . '/' . $this->getRequest()->getControllerName(); + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/Doctrine2User.php b/library/OSS/Controller/Action/Trait/Doctrine2User.php new file mode 100644 index 0000000..bbcabae --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Doctrine2User.php @@ -0,0 +1,131 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Doctine2User + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Doctrine2User +{ + /** + * A variable to hold the user record + * + * @var \Entities\User An instance of the user record + */ + protected $_user = false; + + + /** + * The entity that represents a 'user' + */ + protected $_trait_doctrine2user_entity = '\\Entities\\User'; + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Doctrine2User_Init( $request, $response, $invokeArgs ) + { + // check if we have defined an alternative entity object than \Entities\User + if( isset( $this->_options['resources']['auth']['oss']['entity'] ) ) + $this->_trait_doctrine2user_entity = $this->_options['resources']['auth']['oss']['entity']; + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Doctrine2User' ); + } + + /** + * Get the user ORM object. + * + * Returns the instance of the Doctrine User object for the logged in user. + * + * @return User The user object or false. + */ + protected function getUser() + { + if( $this->_user === false ) + { + try + { + $this->_user = $this->getD2EM()->createQuery( + "SELECT u FROM {$this->_trait_doctrine2user_entity} u WHERE u.id = ?1" ) + ->setParameter( 1, $this->getIdentity()['id'] ) + ->useResultCache( true, 3600, 'oss_d2u_user_' . $this->getIdentity()['id'] ) + ->getSingleResult(); + } + catch( \Doctrine\ORM\NoResultException $e ) + { + if( session_status() == PHP_SESSION_ACTIVE ) + { + session_unset(); + session_destroy(); + } + die( 'User expected but none found... Please reload the page...' ); + } + } + + return $this->_user; + } + + /** + * Clear the user ORM object from the cache. + * + * @param int $id The ID of the user to clear from the cache + */ + protected function clearUserFromCache( $id = null ) + { + if( $id === null ) + $id = $this->getUser()->getId(); + + $this->getD2Cache()->delete( 'oss_d2u_user_' . $id ); + } + +} + diff --git a/library/OSS/Controller/Action/Trait/Freshbooks.php b/library/OSS/Controller/Action/Trait/Freshbooks.php new file mode 100644 index 0000000..acd2011 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Freshbooks.php @@ -0,0 +1,90 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Freshbooks + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Freshbooks +{ + + /** + * @var OSS_Service_Freshbooks An instant of freshbooks service object + */ + protected $_fbooks = null; + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Freshbooks_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Freshbooks' ); + } + + + /** + * Returns an instance of OSS_Service_Freshbooks after setting up the rss channel + * + * @return OSS_Service_Freshbooks The news object + */ + protected function getFreshbooks( ) + { + if( $this->_freshbooks === null ) + $this->_freshbooks = $this->getBootstrap()->getResource( 'freshbooks' ); + + return $this->_freshbooks; + + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/License.php b/library/OSS/Controller/Action/Trait/License.php new file mode 100644 index 0000000..2b220db --- /dev/null +++ b/library/OSS/Controller/Action/Trait/License.php @@ -0,0 +1,99 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Licensing + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_License +{ + + /** + * A variable to hold the license + * + * @var OSS_License_Abstract An instance of the license + */ + private $_license = null; + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_License_Init( $request, $response, $invokeArgs ) + { + $this->getLicense()->verify(); + + if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Smarty' ) ) + $this->view->license = $this->getLicense(); + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_License' ); + } + + + /** + * Returns an instance of OSS_License_Abstract + * + * @return OSS_License_Abstract The license + */ + protected function getLicense() + { + if( $this->_license === null ) + $this->_license = $this->getBootstrap()->getResource( 'license' ); + + if( $this->_license === null ) + throw new OSS_License_Exception( 'Invalid license configuration' ); + + return $this->_license; + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/Logger.php b/library/OSS/Controller/Action/Trait/Logger.php new file mode 100644 index 0000000..deb3202 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Logger.php @@ -0,0 +1,105 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Logger + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Logger +{ + + /** + * A variable to hold an instance of the logger object + * + * @var Zend_Log An instance of the logger object + */ + protected $_logger = null; + + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Logger_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Logger' ); + } + + + + /** + * Returns the logger object + * + * @return OSS_Log The Zend_Log object + */ + public function getLogger() + { + if( $this->_logger === null ) + { + // we may need the Mailer also... + if( isset( $this->_options['ondemand_resources']['logger']['writers']['email'] ) ) + $this->getMailer(); + + $plugin = new OSS_Resource_Logger( $this->getOptions()['ondemand_resources']['logger'] ); + $this->getBootstrap()->registerPluginResource( $plugin ); + + $this->_logger = $plugin->getLogger(); + } + + return $this->_logger; + } + + + + +} + diff --git a/library/OSS/Controller/Action/Trait/Mailer.php b/library/OSS/Controller/Action/Trait/Mailer.php new file mode 100644 index 0000000..4946531 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Mailer.php @@ -0,0 +1,92 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Mailer + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Mailer +{ + + /** + * A variable to hold the mailer + * + * @var Zend_Mail An instance of the mailer + */ + private $_mailer = null; + + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Mailer_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Mailer' ); + } + + /** + * Returns an instance of Zend_Mail after setting up the transport agent + * + * @return Zend_Mail The mailer object + */ + protected function getMailer() + { + if( $this->_mailer === null ) + $this->_mailer = $this->getBootstrap()->getResource('mailer'); + + return new Zend_Mail( 'UTF-8' ); + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/Messages.php b/library/OSS/Controller/Action/Trait/Messages.php new file mode 100644 index 0000000..51d3ecd --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Messages.php @@ -0,0 +1,122 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Freshbooks + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Messages +{ + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Messages_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Messages' ); + } + + + /** + * Adds a message to the session for display on the rendered page. + * + * Works with _redirect() + * + * @param string $message The message text + * @param int $class the message class, OSS_Message::INFO|ALERT|SUCCESS|ERROR|... + * @param int $type The message type from OSS_Message::TYPE_MESSAGE|BLOCK + * @return void + */ + public function addMessage( $message, $class = OSS_Message::SUCCESS, $type = OSS_Message::TYPE_MESSAGE ) + { + $msg = null; + + switch( $type ) + { + case OSS_Message::TYPE_BLOCK: + $msg = new OSS_Message_Block( $message, $class ); + break; + + case OSS_Message::TYPE_POP_UP: + $msg = new OSS_Message_Pop_Up( $message, $class ); + break; + + default: + $msg = new OSS_Message( $message, $class ); + } + + $this->_session->OSS_Messages[] = $msg; + return $msg; + } + + + /** + * Adds messages to the session. + * + * @see addMessage + * @param string $pMessagesArray the array of messages + * @param string $pClass the message class, OSS_Message::INFO|ALERT|SUCCESS|ERROR|... + * @return void + */ + public function addMessages( $messages, $class, $type = OSS_Message::TYPE_MESSAGE ) + { + if( !is_array( $messages ) ) + $messages = array( $messages ); + + foreach( $messages as $msg ) + $this->addMessage( $msg, $class, $type ); + } + + + +} + diff --git a/library/OSS/Controller/Action/Trait/Namespace.php b/library/OSS/Controller/Action/Trait/Namespace.php new file mode 100644 index 0000000..5e80464 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Namespace.php @@ -0,0 +1,98 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Namespace + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Namespace +{ + + /** + * A variable to hold the session namespace + * + * @var Zend_Session_Namespace An instance of the session namespace + */ + protected $_session = null; + + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Namespace_Init( $request, $response, $invokeArgs ) + { + if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Smarty' ) ) + $this->view->session = $this->getSessionNamespace(); + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Namespace' ); + } + + + /** + * Get the namespace (session). + * + * @return Zend_Session_Namespace The session namespace. + */ + protected function getSessionNamespace() + { + if( $this->_session === null ) + $this->_session = $this->getResource( 'namespace' ); + + + return $this->_session; + } + + + +} + diff --git a/library/OSS/Controller/Action/Trait/News.php b/library/OSS/Controller/Action/Trait/News.php new file mode 100644 index 0000000..9c1f61b --- /dev/null +++ b/library/OSS/Controller/Action/Trait/News.php @@ -0,0 +1,158 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Freshbooks + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_News +{ + + /** + * @var object An instant of news object + */ + protected $_news = null; + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_News_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_News' ); + } + + /** + * Gets the News Resource (which is a load ondemand resource) + * + * Then sets the local `$this->_news` to the RSS feed of all the configured + * channels in `application.ini:ondemand_rescoures.news`. + * + * Access a specific channel via: `getNews( $channel )` + * + * @return array|Zend_Feed_Rss The news object for the given channel or all news channels + */ + protected function getNews( $type = null ) + { + if( $this->_news === null ) + { + // first, see if we have it cached + if( ( $this->_news = $this->getD2Cache()->fetch( 'ondemand_resources.news' ) ) === false ) + { + $news = new OSS_Resource_News( $this->getOptions()['ondemand_resources']['news'] ); + $this->getBootstrap()->registerPluginResource( $news ); + + // $this->_news = $this->getBootstrap()->getPluginResource( 'news' )->getNews(); + $this->_news = $news->getNews(); + + // store it in the cache also + $this->getD2Cache()->save( 'ondemand_resources.news', $this->_news, $this->getOptions()['ondemand_resources']['news']['cache_period'] ); + } + } + + if( $type === null ) + return $this->_news; + else + return $this->_news[ $type ]['channel']; + } + + + /** + * Returns an key field name + * + * @return string key field name + */ + protected function getNewsKey( $type = 'general' ) + { + return $this->getNews( )[ $type ]['key']; + } + + + // FIXME Does this have any business being here? + protected function checkNews( $user ) + { + $unread = 0; + $max = intval( $this->_options['news']['max_news_items'] ); + $user_read_items = is_array( $user->getIndexedPreference( 'news.general.read' ) )? $user->getIndexedPreference( 'news.general.read' ) : array( $user->getIndexedPreference( 'news.general.read' ) ); + + $key = $this->getNewsKey(); + foreach( $this->getNews( 'general' ) as $item ) + { + if( $item->$key() == $user->getPreference( 'news.general.last_seen' ) ) + break; + + if( in_array( $item->$key(), $user_read_items ) ) + break; + + if( $unread >= $this->_options['news']['max_news_items'] ) + break; + + $unread++; + } + $this->getSessionNamespace()->unread_news = $unread; + + $key = $this->getNewsKey( 'alerts' ); + $alerts = $this->getNews( 'alerts' ); + + if( $alerts ) + { + while($alerts->current()->$key() != $user->getPreference( 'news.alerts.last_seen' ) && $alerts->current()->$key() ) + { + $this->getSessionNamespace()->unread_alert = $alerts->current()->$key(); + $alerts->next(); + } + } + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/Paygate.php b/library/OSS/Controller/Action/Trait/Paygate.php new file mode 100644 index 0000000..adc92dd --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Paygate.php @@ -0,0 +1,92 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Paygate + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Paygate +{ + + /** + * @var OSS_Service_Freshbooks An instant of freshbooks service object + */ + protected $_paygate = null; + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Paygate_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Paygate' ); + } + + /** + * Returns an instance of OSS_Service_Freshbooks after setting up the rss channel + * + * @return OSS_Service_Freshbooks The news object + */ + protected function getPaygate() + { + if( $this->_paygate === null ) + { + $plugin = new OSS_Resource_Paygate( $this->getOptions()['ondemand_resources']['paygate'] ); + $this->getBootstrap()->registerPluginResource( $plugin ); + + $this->_paygate = $plugin->getPaygate( $this->getLogger() ); + } + return $this->_paygate; + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/PdfGenerator.php b/library/OSS/Controller/Action/Trait/PdfGenerator.php new file mode 100644 index 0000000..54009d6 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/PdfGenerator.php @@ -0,0 +1,89 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for PDF generator + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_PdfGenerator +{ + + /** + * @var OSS_PDF An instant of pdf generator object + */ + protected $_pdf_gen = null; + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_PdfGenerator_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_PdfGenerator' ); + } + + /** + * Returns an instance of OSS_PDF + * + * @return OSS_PDF + */ + protected function getPdfGenerator() + { + if( $this->_pdf_gen === null ) + { + $this->_pdf_gen = new OSS_Pdf( $this->view ); + } + return $this->_pdf_gen; + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/RememberMe.php b/library/OSS/Controller/Action/Trait/RememberMe.php new file mode 100644 index 0000000..cf47612 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/RememberMe.php @@ -0,0 +1,193 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for remember me actions + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2014, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_RememberMe +{ + /** + * Check if remember me cookies are enabled and correctly configured in `application.ini` + * + * @return bool True if everything is configured correctly and enabled + */ + protected function _rememberMeEnabled() + { + if( isset( $this->_options['resources']['auth']['oss']['rememberme'] ) ) + { + $conf = $this->_options['resources']['auth']['oss']['rememberme']; + + if( isset( $conf['enabled'] ) && $conf['enabled'] ) + { + if( !isset( $conf['timeout'] ) || !$conf['timeout'] ) + { + $this->getLogger()->err( 'Remember Me cookies enabled but misconfigured: timeout not defined' ); + return false; + } + + if( !isset( $conf['salt'] ) || !strlen( $conf['salt'] ) ) + { + $this->getLogger()->err( 'Remember Me cookies enabled but misconfigured: salt not defined' ); + return false; + } + + if( !isset( $conf['secure'] ) ) + { + $this->getLogger()->err( 'Remember Me cookies enabled but misconfigured: secure not defined' ); + return false; + } + + return true; + } + } + + return false; + } + + + /** + * Process the user's cookies, if any, for valid 'remember me' authentication + * + * This will also update the user's cookie key to ensure it changes everytime it's used + * + * @return \Entities\User A user object if we have a valid cookie (otherwise false) + */ + protected function _processRememberMeCookies() + { + if( !isset( $_COOKIE['aval'] ) || !isset( $_COOKIE['bval'] ) || !$_COOKIE['aval'] || !$_COOKIE['bval'] ) + return false; + + $cookie = $this->getD2EM()->getRepository( '\\Entities\\RememberMe' )->load( $_COOKIE['aval'], $_COOKIE['bval'] ); + + if( !$cookie ) + return false; + + $user = $cookie->getUser(); + + if( $cookie->getExpires() < new DateTime() ) + { + $user->getRememberMes()->removeElement( $cookie ); + $this->getEntityManager()->remove( $cookie ); + $this->getEntityManager()->flush(); + return false; + } + + // we have a valid combination. Update the user's cookies + $this->_setRememberMeCookie( $user, $cookie ); + + return $user; + } + + + + /** + * Set cookies for Remember Me functionality + * + * The username is stored as a salted SHA1 hashed value to protect the user's username + * The key is a random 40 charater string + * + * @param \Entitues\User $user The user enitiy + * @param \Entities\RememberMe $rememberme The remember me entity with cookie details (or null to create one) + */ + protected function _setRememberMeCookie( $user, $rememberme = null ) + { + if( $rememberme == null ) + { + $rememberme = new \Entities\RememberMe(); + $rememberme->setUser( $user ); + $rememberme->setCreated( new DateTime() ); + $rememberme->setOriginalIp( $_SERVER['REMOTE_ADDR'] ); + $rememberme->setUserhash( $this->_generateCookieUserhash( $user ) ); + $this->getD2EM()->persist( $rememberme ); + } + + $expire = time() + $this->_options['resources']['auth']['oss']['rememberme']['timeout']; + + $rememberme->setExpires( new DateTime( "@{$expire}" ) ); + + $rememberme->setLastUsed( new DateTime() ); + $rememberme->setCkey( OSS_String::random( 40, true, true, true, '', '' ) ); + + $this->getD2EM()->flush(); + + setcookie( 'aval', $rememberme->getUserhash(), $expire, '/', '', $this->_options['resources']['auth']['oss']['rememberme']['secure'], true ); + setcookie( 'bval', $rememberme->getCkey(), $expire, '/', '', $this->_options['resources']['auth']['oss']['rememberme']['secure'], true ); + } + + + /** + * Generate the user hash in a secure format for storage in a client side 'remember cookie' cookie + * + * @param \Entities\User $user The user entitiy to generate the hash for + * @return string The `sha1()` hash + */ + protected function _generateCookieUserhash( $user ) + { + return sha1( $user->getId() . '+' . $user->getUsername() . '/' . $this->_options['resources']['auth']['oss']['rememberme']['salt'] ); + } + + + + /** + * Delete all stored RememberMe cookies for a user (server and client side) + * + * @param \Entities\User $user The user entity + * @return int The number of remember mes deleted from the DB (or 0 if none). Can be safely ignored. + */ + protected function _deleteRememberMeCookie( $user ) + { + if( !$this->_rememberMeEnabled() ) + return; + + setcookie( 'aval', '', time() - 100000, '/', '', $this->_options['resources']['auth']['oss']['rememberme']['secure'], true ); + setcookie( 'bval', '', time() - 100000, '/', '', $this->_options['resources']['auth']['oss']['rememberme']['secure'], true ); + + return $this->getD2EM()->createQuery( "DELETE \\Entities\\RememberMe me WHERE me.User = ?1" ) + ->setParameter( 1, $user ) + ->execute(); + } + +} diff --git a/library/OSS/Controller/Action/Trait/Smarty.php b/library/OSS/Controller/Action/Trait/Smarty.php new file mode 100644 index 0000000..cbdbc7a --- /dev/null +++ b/library/OSS/Controller/Action/Trait/Smarty.php @@ -0,0 +1,140 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Smarty + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_Smarty +{ + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_Smarty_Init( $request, $response, $invokeArgs ) + { + $this->view = $this->createView(); + + if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Namespace' ) ) + $this->view->session = $this->getSessionNamespace(); + + $this->view->options = $this->_options; + $this->view->addHelperPath( 'OSS/View/Helper', 'OSS_View_Helper' ); + $this->view->module = $this->getRequest()->getModuleName(); + $this->view->controller = $this->getRequest()->getControllerName(); + $this->view->action = $this->getRequest()->getActionName(); + $this->view->basepath = Zend_Controller_Front::getInstance()->getBaseUrl(); + + + $this->view->getEngine()->loadFilter( "pre", 'whitespace_control' ); + + if( substr( $request->getActionName(), 0, 4 ) == 'ajax' || substr( $request->getActionName(), 0, 3 ) == 'cli' ) + { + Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' ); + } + else + { + $this->view->doctype( 'HTML5' ); + $this->view->headMeta()->appendHttpEquiv('Content-Type', 'text/html;charset=utf-8'); + } + + if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Auth' ) ) + { + $this->view->auth = $this->getAuth(); + + $this->view->hasIdentity = $this->getAuth()->hasIdentity(); + $this->view->identity = $this->getIdentity(); + + if( $this->getAuth()->hasIdentity() && method_exists( $this, 'getUser' ) ) + $this->view->user = $this->getUser(); + } + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_Smarty' ); + } + + /** + * Creates and returns with a new Smarty view object. + * + * @param void + * @return OSS_View_Smarty + */ + public function createView() + { + $view = $this->getBootstrap()->getResource( 'smarty' ); + + $view->pagebase = ''; + + if( isset( $_SERVER['SERVER_NAME'] ) ) + $view->pagebase = 'http' . ( isset( $_SERVER['HTTPS'] ) ? 's' : '' ) . '://' + . $_SERVER['SERVER_NAME'] + . Zend_Controller_Front::getInstance()->getBaseUrl(); + + $view->basepath = Zend_Controller_Front::getInstance()->getBaseUrl(); + + $view->___SKIN = $view->getSkin(); + + return $view; + } + + + /** + * Get the view object + * + * @return OSS_View_Smarty + */ + public function getView() + { + return $this->view; + } + +} + diff --git a/library/OSS/Controller/Action/Trait/StatsD.php b/library/OSS/Controller/Action/Trait/StatsD.php new file mode 100644 index 0000000..9570613 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/StatsD.php @@ -0,0 +1,102 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for Mailer + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_StatsD +{ + + /** + * A variable to hold an instance of the StatsD object + * + * @var OSS_StatsD An instance of the StatsD object + */ + protected $_statsd = null; + + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_StatsD_Init( $request, $response, $invokeArgs ) + { + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_StatsD' ); + } + + /** + * Returns the StatsD object + * + * Requires a configuration block such as: + * + * ondemand_rescoures.statsd.host = 'yourhost' + * ondemand_rescoures.statsd.port = 8125 + * ondemand_rescoures.statsd.enabled = true|false + * + * @return OSS_StatsD The StatsD object + */ + public function getStatsD() + { + if( $this->_statsd === null ) + { + $plugin = new OSS_Resource_StatsD( $this->getOptions()['ondemand_resources']['statsd'] ); + $this->getBootstrap()->registerPluginResource( $plugin ); + + $this->_statsd = $plugin->getInstance(); + } + + return $this->_statsd; + } + + +} + diff --git a/library/OSS/Controller/Action/Trait/TermsAcceptance.php b/library/OSS/Controller/Action/Trait/TermsAcceptance.php new file mode 100644 index 0000000..bf2f827 --- /dev/null +++ b/library/OSS/Controller/Action/Trait/TermsAcceptance.php @@ -0,0 +1,86 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action - Trait for TermsAcceptance + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Action_Traits + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Action_Trait_TermsAcceptance +{ + + /** + * The trait's initialisation method. + * + * This function is called from the Action's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * Works best when `OSS_Controller_Action_Trait_Messages`, `OSS_Controller_Action_Trait_Namespace` + * and `OSS_Controller_Action_Trait_Smarty` are available. + * + * @see OSS_Controller_Action_Trait_Auth + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function OSS_Controller_Action_Trait_TermsAcceptance_Init( $request, $response, $invokeArgs ) + { + if( !$this->traitIsInitialised( 'OSS_Controller_Action_Trait_Doctrine2User' ) ) + die( 'OSS_Controller_Action_Trait_Doctrine2User required for OSS_Controller_Action_Trait_TermsAcceptance (in OSS_Controller_Action_Trait_TermsAcceptance_Init()' ); + + if( + $this->getUser()->getTermsAccepted() < $this->_options["terms_acceptance"]["version"] + && $this->getRequest()->getControllerName() != $this->_options["terms_acceptance"]["url"]["controller"] + && $this->getRequest()->getActionName() != $this->_options["terms_acceptance"]["url"]["action"] + && !isset( $this->getSessionNamespace()->switched_user_from ) + ) + { + $this->redirectAndEnsureDie( sprintf( "%s/%s", $this->_options["terms_acceptance"]["url"]["controller"], $this->_options["terms_acceptance"]["url"]["action"] ) ); + } + + $this->traitSetInitialised( 'OSS_Controller_Action_Trait_TermsAcceptance' ); + } + +} + diff --git a/library/OSS/Controller/AuthRequiredAction.php b/library/OSS/Controller/AuthRequiredAction.php new file mode 100644 index 0000000..5de34a8 --- /dev/null +++ b/library/OSS/Controller/AuthRequiredAction.php @@ -0,0 +1,70 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Controller_AuthRequiredAction extends OSS_Controller_Action +{ + use OSS_Controller_Action_Trait_AuthRequired; + + /** + * Override the OSS_Controller_Action's constructor (which is called + * at the very beginning of this function anyway). + * + * @param object $request See Parent class constructor + * @param object $response See Parent class constructor + * @param object $invokeArgs See Parent class constructor + */ + public function __construct( + Zend_Controller_Request_Abstract $request, + Zend_Controller_Response_Abstract $response, + array $invokeArgs = null ) + { + parent::__construct( $request, $response, $invokeArgs ); + } +} diff --git a/library/OSS/Controller/CliAction.php b/library/OSS/Controller/CliAction.php new file mode 100644 index 0000000..0beb471 --- /dev/null +++ b/library/OSS/Controller/CliAction.php @@ -0,0 +1,82 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: Action + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @link https://github.com/opensolutions/OSS-Framework/wiki/OSS_Controller Online documentation + * @category OSS + * @package OSS_Controller + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Controller_CliAction extends OSS_Controller_Action +{ + + // traits we want to use + use OSS_Controller_Action_Trait_Doctrine2Cache; + use OSS_Controller_Action_Trait_Doctrine2; + use OSS_Controller_Action_Trait_Mailer; + use OSS_Controller_Action_Trait_Logger; + use OSS_Controller_Action_Trait_Cli; + use OSS_Controller_Action_Trait_Smarty; + + /** + * Override the Zend_Controller_Action's constructor (which is called + * at the very beginning of this function anyway). + * + * @param object $request See Parent class constructer + * @param object $response See Parent class constructer + * @param object $invokeArgs See Parent class constructer + */ + public function __construct( + Zend_Controller_Request_Abstract $request, + Zend_Controller_Response_Abstract $response, + array $invokeArgs = null ) + { + // call the parent's version where all the Zend magic happens + parent::__construct( $request, $response, $invokeArgs ); + + } + +} + diff --git a/library/OSS/Controller/Router/Cli.php b/library/OSS/Controller/Router/Cli.php new file mode 100644 index 0000000..c03b7d7 --- /dev/null +++ b/library/OSS/Controller/Router/Cli.php @@ -0,0 +1,70 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Controller + * @subpackage Router + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Controller_Router_Cli extends Zend_Controller_Router_Abstract implements Zend_Controller_Router_Interface +{ + /** + * Assembles router for cli. + * + * @param array $userParams + * @param string $name + * @param bool $reset + * @param bool $encode + * @return void + */ + public function assemble( $userParams, $name = null, $reset = false, $encode = true ) + { } + + /** + * Route request. + * + * @param Zend_Controller_Request_Abstract $dispatcher + * @return void + */ + public function route( Zend_Controller_Request_Abstract $dispatcher ) + {} + +} diff --git a/library/OSS/Controller/Trait/Auth.php b/library/OSS/Controller/Trait/Auth.php new file mode 100644 index 0000000..eb64914 --- /dev/null +++ b/library/OSS/Controller/Trait/Auth.php @@ -0,0 +1,1112 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: A generic trait to implement basic functionality in an AuthController + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Trait_Auth +{ + + use OSS_Controller_Action_Trait_RememberMe; + + /** + * Template path + * + *@var string $TEMPLATE_PATH + */ + private static $TEMPLATE_PATH = "auth/email"; + + public function preDispatch() + { + // is this action enabled? + if( isset( $this->_options['resources']['auth']['oss']['disabled'][ $this->getRequest()->getActionName() ] ) + && $this->_options['resources']['auth']['oss']['disabled'][ $this->getRequest()->getActionName() ] + ) + { + $this->addMessage( 'This action has been disabled by configuration.', OSS_Message::ALERT ); + $this->redirectAndEnsureDie( '/auth/login' ); + } + } + + public function indexAction() + { + $this->_forward( 'login' ); + } + + + /** + * A pre-login function allow and pre-login processing / checks. + * + * Override if you need to add functionality. + */ + protected function _preLogin() + {} + + /** + * Tries to log the user in. + */ + public function loginAction() + { + // do we already have a valid session? + if( $this->getIdentity() ) + $this->redirectAndEnsureDie( '' ); + + // allow for a possible pre-login hook + $this->_preLogin(); + + $this->view->form = $form = $this->_getFormLogin(); + + // Are remember me cookies enabled and do we have a valid remember me cookie? + $haveCookie = false; + if( $this->_rememberMeEnabled() && $user = $this->_processRememberMeCookies() ) + { + // just hijack the form + $form->getElement( 'username' )->setValue( $user->getUsername() ); + $form->getElement( 'password' )->setValue( $user->getPassword() ); + $form->getElement( 'rememberme' )->setValue( 0 ); + $haveCookie = true; + $this->getLogger()->debug( _( "{$user->getUsername()} presented a valid cookie for logging in" ) ); + $this->getSessionNamespace()->logged_in_via = "cookie"; + } + + if( $haveCookie || ( $this->getRequest()->isPost() && $form->isValid( $_POST ) ) ) + { + $auth = Zend_Auth::getInstance(); + $authAdapter = $this->_getAuthAdapter( $form->getValue( 'username' ), $form->getValue( 'password' ) ); + + if( $haveCookie ) + $authAdapter->haveCookie( true ); + + $result = $auth->authenticate( $authAdapter ); + + switch( $result->getCode() ) + { + case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND: + case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID: + $this->addMessage( _( 'Invalid username or password' ) . '. ' . _( 'Please try again' ) . '.', OSS_Message::ERROR ); + $this->getLogger()->notice( sprintf( _( "Authentication failure for %s" ), $form->getValue( 'username' ) ) ); + return false; + break; + + case Zend_Auth_Result::SUCCESS: + $identity = $auth->getIdentity(); + $user = $identity['user']; + + $message = null; + if( !$this->_postLoginChecks( $auth, $user, $message, $form ) ) + { + if( $haveCookie ) $this->_deleteRememberMeCookie( $user ); + $auth->clearIdentity(); + $this->getSessionNamespace()->unsetAll(); + $this->getLogger()->debug( sprintf( _( "User %s denied access by custom post auth hook" ), $user->getUsername() ) ); + if( $message != null ) $this->addMessage( $message, OSS_Message::ERROR ); + $this->redirectAndEnsureDie( 'auth/login' ); + } + + if( !$haveCookie && $form->getValue( 'rememberme' ) && $this->_rememberMeEnabled() ) + $this->_setRememberMeCookie( $user ); + + if( !$haveCookie ) + $this->getSessionNamespace()->logged_in_via = "auth"; + + // record the last login IP address + $this->getSessionNamespace()->last_login_from = ''; + if( method_exists( $user, 'hasPreference' ) ) + { + if( $ip = $user->hasPreference( 'auth.last_login_from' ) ) + { + $this->getSessionNamespace()->last_login_from = $ip; + $this->getSessionNamespace()->last_login_at = $user->getPreference( 'auth.last_login_at' ); + } + $user->setPreference( 'auth.last_login_from', $_SERVER['REMOTE_ADDR'] ); + $user->setPreference( 'auth.last_login_at', time() ); + } + + if( isset( $this->_options['resources']['auth']['oss']['login_history']['enabled'] ) + && $this->_options['resources']['auth']['oss']['login_history']['enabled'] ) + { + $log = new $this->_options['resources']['auth']['oss']['login_history']['entity']; + $log->setAt( new \DateTime() ); + $log->setIp( $_SERVER['REMOTE_ADDR'] ); + $log->setUser( $user ); + $this->getD2EM()->persist( $log ); + } + + // set the timeout + $this->getSessionNamespace()->timeOfLastAction = time(); + + $this->getLogger()->info( sprintf( _( "%s logged in" ), $user->getUsername() ) ); + + $this->getD2EM()->flush(); + + if( isset( $this->getSessionNamespace()->postAuthRedirect ) ) + $this->_redirect( $this->getSessionNamespace()->postAuthRedirect ); + else + $this->_redirect( '' ); + break; + + default: + throw new OSS_Exception( 'Unknown auth response - ' . $result->getCode() ); + break; + } + } + } + + /** + * Overridable fucntion to perform custom post (successful) login checks (allowing + * the login to be cancelled). + * + * Override this function to add custom code. + * + * @param Zend_Auth $auth The authentication object + * @param \Entities\User $user The user logging in + * @param string $message A message to be displayed if returning false (cancelling the login) + * @param Zend_Form $form Login for to get more information + * @return bool False to prevent the user from logging in, else true + */ + protected function _postLoginChecks( $auth, $user, &$message, $form = null ) + { + return true; + } + + /** + * A pre-logout function allow and pre-logout processing / checks. + * + * Override if you need to add functionality. + */ + protected function _preLogout() + {} + + /** + * Logs the user out, clears the identity and the session. + */ + public function logoutAction() + { + // $this->view->clearVars(); + // $this->view->config = $this->config; + + if( !$this->getAuth()->hasIdentity() ) + $this->_redirect( '' ); + + // allow for a possible pre-logout hook + $this->_preLogout(); + + if( $this->_rememberMeEnabled() ) + $this->_deleteRememberMeCookie( $this->getUser() ); + + $this->getAuth()->clearIdentity(); + $this->getSessionNamespace()->unsetAll(); + + $this->getLogger()->info( "{$this->getUser()->getUsername()} logged out" ); + $this->addMessage( '' . _( 'You have been logged out.' ) . '', OSS_Message::SUCCESS ); + $this->_redirect( '' ); + } + + + + /** + * Asks for the email and a CAPTCHA text, then sends a validation code (link) to the email address, + * and then redirects to /reset-password + */ + public function lostPasswordAction() + { + $this->view->form = $form = $this->_getFormLostPassword(); + + $form->getElement( 'username' )->setValue( $this->_getParam( 'username', "" ) ); + + $this->view->useCaptcha = $useCaptcha = isset( $this->_options['resources']['auth']['oss']['lost_password']['use_captcha'] ) && $this->_options['resources']['auth']['oss']['lost_password']['use_captcha']; + + if( $useCaptcha ) + { + OSS_Form_Captcha::addCaptchaElements( $form ); + $captcha = new OSS_Captcha_Image( 0, 0 ); + $this->view->captchaId = $captcha->generate(); + } + + if( $this->getRequest()->isPost() ) + { + if( $useCaptcha && $this->_getParam( 'requestnewimage', 0 ) ) + { + unset( $_POST['requestnewimage'] ); + $form->setDefaults( $_POST ); + $form->getElement( 'captchatext' )->setValue( "" ); + return; + } + + if( $form->isValid( $_POST ) ) + { + if( $useCaptcha && !OSS_Captcha_Image::_isValid( $this->_getParam( 'captchaid' ), $this->_getParam( 'captchatext', '__' ) ) ) + { + $form->getElement( 'captchatext' ) + ->setValue( '' ) + ->addError( 'The entered text does not match that of the image' ); + return; + } + + $user = $this->getD2EM()->getRepository( + $this->getOptions()['resources']['auth']['oss']['entity'] ) + ->findOneByUsername( $form->getValue( 'username') ); + + if( !$user ) + { + $this->addMessage( + 'If your username was correct, then an email with a key to allow you to change your password below has been sent to you.', + OSS_Message::SUCCESS + ); + $this->redirectAndEnsureDie( 'auth/reset-password/un/' . urlencode( $form->getValue( 'username' ) ) ); + } + + // start by removing expired preferences + if( $user->cleanExpiredPreferences() ) + $this->getEntityManager()->flush(); + + $pwdResetToken = OSS_String::random( 40 ); + + try + { + $user->addIndexedPreference( 'tokens.password_reset', $pwdResetToken, '=', time() + 2*60*60, 5 ); + } + catch( OSS_Doctrine2_WithPreferences_IndexLimitException $e ) + { + $this->addMessage( + 'The limit of password reset tokens has been reached. Please try again later when the existing ones will expire or contact support.', + OSS_Message::ERROR + ); + $this->redirectAndEnsureDie( 'auth/lost-password' ); + } + + $this->getEntityManager()->flush(); + + $this->view->user = $user; + $this->view->token = $pwdResetToken; + + $mailer = $this->getMailer(); + $mailer->setFrom( $this->getOptions()['identity']['mailer']['email'], $this->getOptions()['identity']['mailer']['name'] ); + $mailer->addTo( $user->getEmail(), $user->getFormattedName() ); + $mailer->setSubject( $this->getOptions()['identity']['sitename'] . ' - Password Reset Information' ); + $this->resolveTemplate( $mailer, 'lost-password' ); + $mailer->send(); + + $this->addMessage( + 'If your username was correct, then an email with a key to allow you to change your password below has been sent to you.', + OSS_Message::SUCCESS + ); + + $this->getLogger()->info( sprintf( _( "%s requested a reset password token" ), $user->getUsername() ) ); + + $this->_redirect( 'auth/reset-password/username/' . urlencode( $form->getValue( 'username' ) ) ); + } + } + } + + /* + * You get here from either lostPasswordAction or from the email using the link. It asks for the email address, + * the key (token), and the new password. If all fine then sets the new password and redirects to /login + */ + public function resetPasswordAction() + { + $this->view->form = $form = $this->_getFormResetPassword(); + $form->getElement( 'username' )->setValue( $this->_getParam( "username", "" ) ); + + if( $this->getRequest()->isPost() && $form->isValid( $_POST ) ) + { + $user = $this->getD2EM()->getRepository( $this->getOptions()['resources']['auth']['oss']['entity'] ) + ->findOneByUsername( $form->getValue( 'username') ); + + if( !$user ) + { + $this->addMessage( + 'Invalid username / token combination. Please check your details and try again.', + OSS_Message::SUCCESS + ); + } + else + { + // start by removing expired preferences + if( $user->cleanExpiredPreferences() ) + $this->getD2EM()->flush(); + + if( !is_array( $user->getIndexedPreference( 'tokens.password_reset' ) ) || !in_array( $form->getValue( 'token' ), $user->getIndexedPreference( 'tokens.password_reset' ) ) ) + { + $this->addMessage( + 'Invalid username / token combination. Please check your details and try again.', + OSS_Message::ERROR + ); + } + else + { + $user->setPassword( OSS_Auth_Password::hash( $form->getValue( 'password' ), $this->_options['resources']['auth']['oss'] ) ); + $user->deletePreference( 'tokens.password_reset' ); + + if( method_exists( $user, 'setFailedLogins' ) ) + $user->setFailedLogins( 0 ); + + $this->_deleteRememberMeCookie( $user ); + + if( $this->resetPasswordPreFlush( $user, $form ) ) + $this->getD2EM()->flush(); + + $this->clearUserFromCache( $user->getId() ); + + $this->view->user = $user; + + $mailer = $this->getMailer(); + $mailer->setFrom( $this->_options['identity']['mailer']['email'], $this->_options['identity']['mailer']['name'] ); + $mailer->addTo( $user->getEmail(), $user->getFormattedName() ); + $mailer->setSubject( $this->_options['identity']['sitename'] . ' - Your Password Has Been Reset' ); + $this->resolveTemplate( $mailer, 'reset-password' ); + $mailer->send(); + + $this->addMessage( + 'Your password has been successfully changed. Please log in below with your new password.', + OSS_Message::SUCCESS + ); + + $this->getLogger()->info( sprintf( _( "%s has completed a password reset" ), $user->getUsername() ) ); + + $this->_redirect( 'auth/login' ); + } + } + } + else + { + $form->getElement( 'username' )->setValue( $this->_getParam( 'username', '' ) ); + $form->getElement( 'token' )->setValue( $this->_getParam( 'token', '' ) ); + } + } + + /** + * This is reset password before flush hook. + * + * Override this function to add custom code. + * + * @param Zend_Auth $auth The authentication object + * @param \Entities\User $user The User entity + * @param string $password Password for additional actions, such as data encryption. + * @return bool False to prevent the user from logging in, else true + */ + protected function resetPasswordPreFlush( $user, $form = null ) + { + return true; + } + + + public function lostUsernameAction() + { + $this->view->form = $form = $this->_getFormLostUsername(); + + $this->view->useCaptcha = $useCaptcha = isset( $this->_options['resources']['auth']['oss']['lost_username']['use_captcha'] ) && $this->_options['resources']['auth']['oss']['lost_username']['use_captcha']; + + if( $useCaptcha ) + { + OSS_Form_Captcha::addCaptchaElements( $form ); + $captcha = new OSS_Captcha_Image( 0, 0 ); + $this->view->captchaId = $captcha->generate(); + } + + if( $this->getRequest()->isPost() ) + { + if( $useCaptcha && $this->_getParam( 'requestnewimage', 0 ) ) + { + unset( $_POST['requestnewimage'] ); + $form->setDefaults( $_POST ); + $form->getElement( 'captchatext' )->setValue( "" ); + return; + } + + if( $form->isValid( $_POST ) ) + { + if( $useCaptcha && !OSS_Captcha_Image::_isValid( $this->_getParam( 'captchaid' ), $this->_getParam( 'captchatext', '__' ) ) ) + { + $form->getElement( 'captchatext' ) + ->setValue( '' ) + ->addError( 'The entered text does not match that of the image' ); + return; + } + + $this->view->users = $users = $this->getD2EM()->getRepository( + $this->getOptions()['resources']['auth']['oss']['entity'] ) + ->findByEmail( $form->getValue( 'email' ) ); + + if( count( $users ) ) + { + $mailer = $this->getMailer(); + $mailer->setFrom( $this->_options['identity']['mailer']['email'], $this->_options['identity']['mailer']['name'] ); + $mailer->addTo( $form->getValue( 'email' ) ); + $mailer->setSubject( $this->_options['identity']['sitename'] . ' - Your Accounts' ); + $this->resolveTemplate( $mailer, 'lost-username' ); + $mailer->send(); + } + + $this->addMessage( + 'If your email matches user(s) on the system, then an email listing those users has been sent to you.', + OSS_Message::SUCCESS + ); + + $this->getLogger()->info( sprintf( _( "%s requested lost usernames by email" ), $form->getValue( 'email' ) ) ); + + $this->_redirect( 'auth/login' ); + } + } + } + + + + /** + * Takes an id as a parameter, and prints the matching CAPTCHA image as a binary raw string to the output, + * or prints nothing if no matching CAPTCHA found. + */ + public function captchaImageAction() + { + $captchaId = preg_replace( "/[^0-9a-z]+/u", '', strtolower( basename( $this->_getParam( 'id' ) ) ) ); + $captchaFile = OSS_Utils::getTempDir() . "/captchas/{$captchaId}.png"; + + if( @file_exists( $captchaFile ) ) + { + Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' ); + @ob_end_clean(); + header( 'Content-type: image/png' ); + @readfile( $captchaFile ); + } + else + $this->_forward( 'error-404', 'error' ); + } + + + + /** + * Switch the logged in user to another. + * + * Allows administrators to switch to another user and operate as them temporarily. + */ + public function switchUserAction() + { + if( !$this->getAuth()->hasIdentity() ) + $this->_redirect( 'auth/login' ); + + if( isset( $this->getSessionNamespace()->switched_user_from ) && $this->getSessionNamespace()->switched_user_from ) + { + $this->addMessage( + 'You are already acting as a substituted user. Please switch back first.', + OSS_Message::ERROR + ); + $this->redirectAndEnsureDie( '' ); + } + + if( !$this->_switchUserPreCheck() ) + $this->redirectAndEnsureDie( '' ); + + // does the requested user exist + $nuser = $this->getD2EM()->getRepository( + $this->getOptions()['resources']['auth']['oss']['entity'] + )->find( $this->_getParam( 'id', 0 ) ); + + if( !$nuser ) + { + $this->addMessage( + 'Invalid user in switch user request. Please check your details and try again.', + OSS_Message::ERROR + ); + $this->redirectAndEnsureDie( 'user/list' ); + } + + if( !$this->_switchUserCheck( $nuser ) ) + $this->redirectAndEnsureDie( '' ); + + // store the fact that we're switching in the session + $this->getSessionNamespace()->switched_user_from = $this->getUser()->getId(); + + // easiest way to switch users is to just re-autenticate as the new one + // This maintains consistancy with Zend_Auth and future changes + $result = $this->_reauthenticate( $nuser ); + + if( $result->getCode() == Zend_Auth_Result::SUCCESS ) + { + $this->getLogger()->info( 'User ' . $this->getUser()->getUsername() . ' has switched to user ' + . $nuser->getUsername() + ); + + $this->addMessage( + "You are now logged in as {$nuser->getUsername()} of {$nuser->getCustomer()->getName()}.", + OSS_Message::SUCCESS + ); + } + else + { + $this->getLogger()->notice( 'User ' . $this->getUser()->getUsername() . ' has failed to switch to user ' . $nuser->getUsername() ); + $this->forward( 'logout' ); die(); + } + + $this->_redirect( '' ); + } + + /** + * Switch back to the original user when switched to another. + * + * Allows administrators to switch back from another user who they operated as them temporarily. + */ + public function switchUserBackAction() + { + if( !$this->getAuth()->hasIdentity() ) + $this->_redirect( 'auth/login' ); + + // are we really operating as another? + if( !isset( $this->getSessionNamespace()->switched_user_from ) or !$this->getSessionNamespace()->switched_user_from ) + { + $this->addMessage( + 'You are not currently logged in as another user. You are logged in as: ' . $this->getUser()->getUsername(), + OSS_Message::ERROR + ); + $this->redirectAndEnsureDie( '' ); + } + + // does the original user exist + $ouser = $this->getD2EM()->getRepository( + $this->getOptions()['resources']['auth']['oss']['entity'] + )->find( $this->getSessionNamespace()->switched_user_from ); + + if( !$ouser ) + { + $this->forward( 'logout' ); die(); + } + + if( !( $params = $this->_switchUserBackCheck( $this->getUser(), $ouser ) ) ) + $this->redirectAndEnsureDie(); + + // easiest way to switch users is to just re-autenticate as the new one + // This maintains consistancy with Zend_Auth and future changes + $result = $this->_reauthenticate( $ouser ); + + if( $result->getCode() == Zend_Auth_Result::SUCCESS ) + { + $this->getLogger()->info( 'User ' . $ouser->getUsername() . ' has switched back from user ' . $this->getUser()->getUsername() ); + $this->addMessage( "You are now logged in as {$ouser->getUsername()}.", OSS_Message::SUCCESS ); + } + else + throw new OSS_Exception( "Error for user {$ouser->getUsername()} switching back from user {$this->getUser()->getUsername()}" ); + + $this->getSessionNamespace()->switched_user_from = 0; + + $this->_redirect( isset( $params['url'] ) ? $params['url'] : '' ); + } + + /** + * A simple function to reauthenticate to a given user **Ignores password**. + * + * @param \Entities\User $nuser The user to reauthenticate as + * @return Zend_Auth_Result + */ + protected function _reauthenticate( $nuser ) + { + $auth = Zend_Auth::getInstance(); + $authAdapter = $this->_getAuthAdapter( $nuser->getUsername(), $nuser->getPassword() ); + + // trick the adapter into ignoring the password + $authAdapter->haveCookie( true ); + + return $auth->authenticate( $authAdapter ); + } + + + /** + * Instantiate the appropriate Zend Auth Adapter + * + * @param string $un The username + * @param string $pw The password + * @return OSS_Auth_Doctrine2Adapter An appropriate Zend auth adapter (not necessarily `OSS_Auth_Doctrine2Adapter` + */ + protected function _getAuthAdapter( $un, $pw ) + { + switch( $this->getOptions()['resources']['auth']['oss']['adapter'] ) + { + case 'OSS_Auth_DoctrineAdapter': + $authAdapter = new OSS_Auth_DoctrineAdapter( $un, $pw ); + break; + + case 'OSS_Auth_Doctrine2Adapter': + $authAdapter = new OSS_Auth_Doctrine2Adapter( + $un, $pw, $this->getOptions()['resources']['auth']['oss']['entity'], + $this->getD2EM(), $this->getOptions()['resources']['auth']['oss'] + ); + break; + + default: + throw new OSS_Exception( 'No such authentication adapter - ' . $this->getOptions()['resources']['auth']['oss']['adapter'] ); + } + + return $authAdapter; + } + + /** + * This function is called just before `switchUserAction()` processes anything. + * By default it will stop `switchUserAction()` and redirect with an error (that + * should be added in this function via `addMessage()`. + * + * Feel free to use you own `redirectAndEnsureDie()` destination in this function. + * + * You should override this function to ensure the user had the requisite privileges + * to even try and perform a user switch. Use `_switchUserCheck()` for more invasive + * checks after the requested user object is found and loaded. + * + * @return bool True unless you want the switch to fail. + */ + protected function _switchUserPreCheck() + { + $this->getLogger()->notice( 'User ' . $this->getUser()->getUsername() . ' illegally tried to switch to user with ID ' + . $this->_getParam( 'id', '[unknown]' ) + ); + + $this->addMessage( + 'You are not allowed to switch users! This attempt has been logged and the administrators notified.', + OSS_Message::ERROR + ); + + $this->redirectAndEnsureDie( '' ); + } + + /** + * This function is called after `switchUserAction()` loads the requested user object. + * By default it will stop `switchUserAction()` and redirect with an error (that + * should be added in this function via `addMessage()`. + * + * Feel free to use you own `redirectAndEnsureDie()` destination in this function. + * + * You should override this function to perform pre-switch checks. + * + * @param \Entities\User $nuser The user to switch to + * @return bool True unless you want the switch to fail. + */ + protected function _switchUserCheck( $nuser ) + { + return false; + } + + /** + * This function is called just before `switchUserBackAction()` actually switches + * the user back. + * + * Feel free to use you own `redirectAndEnsureDie()` destination in this function. + * + * You should override this function to perform pre-switch-back checks. + * + * You can also return an array of optional parameters to affect the switch back. Right + * now, these are: + * + * ['url'] => a redirect destination after the switch back is complete rather than '' + * + * @param \Entities\User $subUser The current user we have switched to (substituted to) + * @param \Entities\User $origUser The original user that we switched from + * @return bool|array False if you want the switch back to fail. + */ + protected function _switchUserBackCheck( $subUser, $origUser ) + { + return false; + } + + + /** + * It is the main login action, but if the user is using One Time Code or Yubikey to log in, then it is just the first step. + * If the user is using One Time Code, then redirects to auth/send-one-time-code + * If the user is using Yubikey, then redirects to auth/yubikey + * If the user is not using any additional security, then calls $this->_doLogin() + */ + public function loginSecureAction() + { + if( $this->getIdentity() ) + { + $this->addMessage( 'You are already logged in.', OSS_Message::INFO ); + $this->_redirect( 'user' ); + } + + $auth = Zend_Auth::getInstance(); + + $this->view->form = $form = new OSSPayroll_Form_Auth_LoginSecure(); + $form->getElement( 'email' )->setValue( $this->_getParam( 'email', "" ) ); + + $captcha = new OSS_Captcha_Image( 0, 0 ); + $this->view->captchaId = $captcha->generate(); + + + if( $this->getRequest()->isPost() ) + { + if( $this->_getParam( 'requestnewimage', 0 ) ) + { + unset( $_POST['requestnewimage'] ); + $this->view->form->setDefaults( $_POST ); + $form->getElement( 'captchatext' )->setValue( "" ); + return; + } + + if( $form->isValid( $_POST ) ) + { + if( !OSS_Captcha_Image::_isValid( $this->_getParam( 'captchaid' ), $this->_getParam( 'captchatext', '__' ) ) ) + { + $form->getElement( 'captchatext' ) + ->setValue( '' ) + ->addError( 'The entered text does not match that of the image' ); + return; + } + + $this->_forward( "login" ); + } + + } + + $form->getElement( 'captchatext' )->setValue( "" ); + } + + /** + * The check failed logins + * + * This functions is called after authorisation. + * + * It checks how many bad attempts had this username and redirects user to login with captcha, + * to lost password page, back to login page, or do nothing if was auth was successful and captcha was + * provided. + * + * @param int $count The count of bad login attempts + * @param string $username The username + * @param bool $success default false If function called after success authorisation + * @return bool Always true as all other actions are _redirect() + */ + protected function _checkFailedLogins( $count, $username, $success = false ) + { + if( $count >= $this->_options['login_security']['failed_logins']['locked_after'] ) + { + $this->_auth->clearIdentity(); + if( $this->_session ) $this->getSessionNamespace()->unsetAll(); + + $this->addMessage( 'Due to an excessive amount of failed logins, ' + . 'your account has been locked for your own security. ' + . 'To unlock you account, please follow the Lost Password ' + . 'procedure below which will guide you through setting a new password ' + . 'and unlocking your account.', OSS_Message::ERROR + ); + + $this->redirectAndEnsureDie( "auth/lost-password/email/" . urlencode( $username ) ); + } + else if( ( !$success || !isset( $_POST['captchatext'] ) ) && $count >= $this->_options['login_security']['failed_logins']['captcha_after'] ) + { + $this->_auth->clearIdentity(); + if( $this->_session ) $this->getSessionNamespace()->unsetAll(); + + $this->addMessage( 'While the password you provided may have been correct, due ' + . 'to an excessive number of previous failed login attempts on your account, ' + . 'we require you to login in using the form below.', OSS_Message::ERROR + ); + + $this->redirectAndEnsureDie( "auth/login-secure/email/" . urlencode( $username ) ); + } + else if( !$success ) + { + $this->_auth->clearIdentity(); + if( $this->_session ) $this->getSessionNamespace()->unsetAll(); + + $this->addMessage( 'Login failed! Invalid username / password combination', OSS_Message::ERROR ); + + $this->redirectAndEnsureDie( "auth/login/email/" . urlencode( $username ) ); + } + + return true; + } + + /** + * The get failed login by username object including failed count and lastseen time. + * + * This functions is called only then user was not found in database. + * + * It checks if this username was used before if not creates an etnry and set default values. + * + * Otherwise, if the time interval is greater than the timeout, it resets the counters, + * or if not, it increases the counter. + * + * @link https://dev.opensolutions.ie/jira/browse/EPAYROLL-11 + * + * We need to deal with brute force attempts on unknown / multiple accounts. + * + * We use the FailedLoginsByUsername database table which includes: + * + * username - (unique) the (possibly unknown) username presented + * count - the failed login count + * lastseen - when it was last seen. + * + * What happens here is: + * + * * (1) If the presented login username is a valid user, then we never refer to the Failed_Logins_By_Username table; + * * If the presented login username is not a valid user: + * ** if the username does not exist in the Failed_Logins_By_Username table, add it with + * a count of 1 and set the lastseen to now; + * ** if the username does exist in the Failed_Logins_By_Username table and the lastseen + * value is >86400 seconds ago, reset the count to 1 and set the lastseen to now; + * ** if the username does exist in the Failed_Logins_By_Username table and the lastseen + * value is <86400 seconds ago, increment the count and proceed as if it were a real user (i.e. present a captcha after 3 and a locked account message after 6). + * + * Note that because of (1) above, entries in the Failed_Logins_By_Username table will + * not affect users subsequently signing up. + * + * @param string $username The username presented for login + * @return \Entities\FailedLoginsByUsername + */ + protected function _getFailedLoginByUsername( $username ) + { + $buser = $this->getEntityManager()->getRepository( '\Entities\FailedLoginsByUsername' ) + ->findOneBy( array( 'username' => $username ) ); + + if( !$buser ) + { + $buser = new \Entities\FailedLoginsByUsername(); + $buser->setLastseen( new \DateTime() ); + $buser->setCount( 1 ); + $buser->setUsername( $username); + $this->getEntityManager()->persist( $buser ); + } + else + { + $lastseen = $buser->getLastseen()->format( 'U' ); + + if( ( time() - $lastseen ) > $this->_options['login_security']['failed_logins_by_username']['timeout'] ) + { + $buser->setLastseen( new \DateTime() ); + $buser->setCount( 1 ); + } + else + $buser->setCount( $buser->getCount() + 1 ); + } + + $this->getEntityManager()->flush(); + + return $buser; + } + + + /** + * Restricting Brute Force Logins by IP + * + * We'll use a sliding window to count the logins over a short period of time and + * in aggregate over a longer period of time. + * + * The database table has the following columns: + * + * ip - (unique) the source IP address - big enough to hold uncompressed IPv6 addresses + * count - the failed login count + * lastseen - when it was last seen + * clear_count - the number of times the count was cleared + * last_cleared - when the count was last cleared + * + * And configuration parameters: + * + * login_security.failed_logins_by_ip.timeout = 300 + * login_security.failed_logins_by_ip.block_after = 10 + * login_security.failed_logins_by_ip.timeout2 = 3600 + * login_security.failed_logins_by_ip.block_after2 = 1 + * + * + * When any login is received (and before checking the username or password): + * + * * if the source IP address does exist in Failed_Logins_By_Ip + * and lastseen >= now - timeout and count >= block_after; or + * * if the source IP address does exist in Failed_Logins_By_Ip + * and count >= block_after2 and last_cleared >= now - timeout2 and clear_count >= 1; then + * + * ** increment the count; + * ** set lastseen to now; + * ** block login and inform the user. + * + * @see https://dev.opensolutions.ie/jira/browse/EPAYROLL-12 + * + * @return \Entities\FailedLoginsByIp + */ + protected function _initialIpCheck() + { + $ipcheck = $this->getEntityManager()->getRepository( '\Entities\FailedLoginsByIp' ) + ->findOneBy( array( 'ip' => $_SERVER['REMOTE_ADDR'] ) ); + + if( $ipcheck ) + { + $lastseen = $ipcheck->getLastseen()->format( 'U' ); + $lastcleared = $ipcheck->getLastCleared()->format( 'U' ); + $now = time(); + + if( + ( ( $now - $lastseen ) < $this->_options['login_security']['failed_logins_by_ip']['timeout'] + && $ipcheck->getCount() >= $this->_options['login_security']['failed_logins_by_ip']['block_after'] + ) + || + ( ( $now - $lastcleared ) < $this->_options['login_security']['failed_logins_by_ip']['timeout2'] + && $ipcheck->getCount() >= $this->_options['login_security']['failed_logins_by_ip']['block_after2'] + && $ipcheck->getClearCount() >= 1 + ) ) + { + $ipcheck->setCount( 1 ); + $ipcheck->setLastseen( new \DateTime() ); + $this->getEntityManager()->flush(); + + $this->addMessage( "Due to an excessive amount of login attempts from your IP address, we are temporarily blocking further logins for security. Please try again after an hour has passed.", OSS_Message::ERROR ); + $this->_redirect( '' ); + die( 'Unexpected fall through from redirect()' ); + } + } + + return $ipcheck; + } + + /** + * The IP check function to rate limit / block brute force attempts + * + * This executes only if the login attempt was bad. It checks if the IP was + * seen before and if not it creates an entry in FaildeLoginsByIp. + * + * If we have seen it before, it cheks if last bad attempt was later than + * the time out and clears counter if it was. + * + * Otherwise, it increments the failed login count. + * + * @param \Entities\FailedLoginsByIp $ipcheck Object wich stores failed logins by ip data. + * @return bool Always true as all other actions issue a _redirect(). + */ + protected function _ipCheck( $ipcheck ) + { + if( !$ipcheck ) + { + $ipcheck = new \Entities\FailedLoginsByIp(); + $ipcheck->setIp( $_SERVER['REMOTE_ADDR'] ); + $ipcheck->setCount( 1 ); + $ipcheck->setLastseen( new \DateTime() ); + $ipcheck->setClearCount( 0 ); + $ipcheck->setLastCleared( new \DateTime() ); + + $this->getEntityManager()->persist( $ipcheck ); + } + else + { + $lastseen = $ipcheck->getLastseen()->format( 'U' ); + $now = time(); + + if( ( $now - $lastseen ) >= $this->_options['login_security']['failed_logins_by_ip']['timeout'] ) + { + $ipcheck->setCount( 1 ); + $ipcheck->setLastseen( new \DateTime() ); + $ipcheck->setClearCount( $ipcheck->getClearCount() + 1 ); + $ipcheck->setLastCleared( new \DateTime() ); + } + else if( ( $now - $lastseen ) < $this->_options['login_security']['failed_logins_by_ip']['timeout'] && $ipcheck->getCount() < $this->_options['login_security']['failed_logins_by_ip']['block_after'] ) + { + $ipcheck->setCount( $ipcheck->getCount() + 1 ); + $ipcheck->setLastseen( new \DateTime() ); + } + } + + $this->getEntityManager()->flush(); + return true; + } + + /** + * Adds a plaintext / HTML / both email body to a OSS_Mail object as appropriate + * + * Email type can be defined in application.ini by setting option `resources.auth.oss.email_format`. + * + * Valid email formats: + * * `both` - **this is the default**. It will look for both a html and plaintext version of `$template` + * to build an email wich contains whichever was found or both if available. If neither html + * nor plaintext templates are unable to render, it will thorw `OSS_Exception`. + * html - this email format will try to render html tamplate and set html body to mailer. + * plaintext - this email format will try to render plaintext tamplate and set text body to mailer. + * + * Template location is `application/views/$TEMPLATE_PATH`/{html,plaintext} directory is for html + * templates and template files must end with phtml. And plaintext direcotry is for text templates + * files and the extension must be `.txt`. + * + * **NB:** skin functionality works - just place the files in `_skins/auth/email/...`. + * + * Available templates are: + * + * * `lost-password` + * * `reset-password` + * * `lost-username` + * + * @param Zend_Mail $mailer Mailer object to set reolved end rendered body template. + * @param string $template Template name to resolve and render + * @return void + * @throws OSS_Exception Can not render '$template' email body + */ + protected function resolveTemplate( $mailer, $template ) + { + $format = isset( $this->_options['resources']['auth']['oss']['email_format'] ) ? $this->_options['resources']['auth']['oss']['email_format'] : "both"; + + $html = sprintf( "%s/html/%s.phtml", self::$TEMPLATE_PATH, $template ); + $text = sprintf( "%s/plaintext/%s.txt", self::$TEMPLATE_PATH, $template ); + + switch( $format ) + { + case 'html': + $mailer->setBodyHtml( $this->view->render( $html ) ); + break; + + case 'plaintext': + $mailer->setBodyText( $this->view->render( $text ) ); + break; + + case 'both': + $havefile = false; + + try { + $mailer->setBodyHtml( $this->view->render( $html ) ); + $havefile = true; + } + catch( Exception $e ){} + + try { + $mailer->setBodyText( $this->view->render( $text ) ); + $havefile = true; + } + catch( Exception $e ){} + + if( !$havefile ) + throw new OSS_Exception( "Can not render '$template' email body - neither '$html' nor '$text' found" ); + } + } +} diff --git a/library/OSS/Controller/Trait/Error.php b/library/OSS/Controller/Trait/Error.php new file mode 100644 index 0000000..26424e9 --- /dev/null +++ b/library/OSS/Controller/Trait/Error.php @@ -0,0 +1,132 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: A generic trait to implement basic functionality in an ErrorController + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller_Trait + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Trait_Error +{ + + /** + * The default error handler action + */ + public function errorAction() + { + $this->getLogger()->debug( "\n" ); + $this->getLogger()->debug( 'ErrorController::errorAction()' ); + + $log = "\n\n************************************************************************\n" + . "****************************** EXCEPTIONS *******************************\n" + . "************************************************************************\n\n"; + + $exceptions = $this->getResponse()->getException(); + + if( is_array( $exceptions ) ) + { + foreach( $exceptions as $e ) + { + $log .= "--------------------------- EXCEPTION --------------------------\n\n" + . "Message: " . $e->getMessage() + . "\nLine: " . $e->getLine() + . "\nFile: " . $e->getFile(); + + $log .= "\n\nTrace:\n\n" + . $e->getTraceAsString() . "\n\n" + . print_r( OSS_Debug::compact_debug_backtrace(), true ) + . "\n\n"; + } + } + + $log .= "------------------------\n\n" + . "HTTP_HOST : {$_SERVER['HTTP_HOST']}\n" + . "HTTP_USER_AGENT: {$_SERVER['HTTP_USER_AGENT']}\n" + . ( isset( $_SERVER['HTTP_COOKIE'] ) ? "HTTP_COOKIE: {$_SERVER['HTTP_COOKIE']}\n" : "" ) + . "REMOTE_PORT: {$_SERVER['REMOTE_PORT']}\n" + . "REQUEST_METHOD: {$_SERVER['REQUEST_METHOD']}\n" + . "REQUEST_URI: {$_SERVER['REQUEST_URI']}\n\n"; + + $this->getResponse()->setBody( 'OK: 0' ); + + if( isset( $this->view ) ) + { + if( $errors = $this->_getParam( 'error_handler', false ) ) + { + $this->getResponse()->clearBody(); + + switch( $errors->type ) + { + case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER: + case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION: + // 404 error -- controller or action not found + $this->getResponse() + ->setRawHeader( 'HTTP/1.1 404 Not Found' ); + + Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' ); + $this->view->display( 'error/error-404.phtml' ); + $this->getLogger()->debug( $log ); + break; + + default: + $this->getLogger()->err( $log ); + $this->view->exceptions = $exceptions; + break; + } + } + } + + return true; + } + + public function error404Action() + { + $this->getResponse()->setRawHeader( 'HTTP/1.1 404 Not Found' ); + } + + public function insufficientPermissionsAction() + { + } + +} diff --git a/library/OSS/Controller/Trait/Profile.php b/library/OSS/Controller/Trait/Profile.php new file mode 100644 index 0000000..e13d548 --- /dev/null +++ b/library/OSS/Controller/Trait/Profile.php @@ -0,0 +1,106 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Controller: A generic trait to implement basic functionality in a ProfileController + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Controller + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Controller_Trait_Profile +{ + use OSS_Controller_Action_Trait_RememberMe; + + /** + * Return the appropriate change password form for your application + */ + protected function _getFormChangePassword() + { + throw new OSS_Exception( 'You must override this function to return a Zend_Form for password changing' ); + } + + /** + * Action to allow a user to change their password + * + */ + public function changePasswordAction() + { + $this->view->passwordForm = $form = $this->_getFormChangePassword(); + + if( $this->getRequest()->isPost() && $form->isValid( $_POST ) ) + { + if( !OSS_Auth_Password::verify( $form->getValue( 'current_password' ), $this->getUser()->getPassword(), $this->_options['resources']['auth']['oss'] ) ) + { + $form->getElement( 'current_password' )->addError( + 'Invalid current password' + ); + return $this->forward( 'index' ); + } + + // update the users password + $this->getUser()->setPassword( OSS_Auth_Password::hash( $form->getValue( 'new_password' ), $this->_options['resources']['auth']['oss'] ) ); + $this->getD2EM()->flush(); + + if( $this->_rememberMeEnabled() ) + $this->_deleteRememberMeCookie( $this->getUser() ); + + $this->changePasswordPostFlush(); + $form->reset(); + + $this->getLogger()->info( "User {$this->getUser()->getUsername()} changed password" ); + $this->addMessage( _( 'Your password has been changed.' ), OSS_Message::SUCCESS ); + $this->redirect( 'profile/index' ); + } + + $this->forward( 'index' ); + } + + /** + * Hook that can be overridden to perform application specific actions after a password is + * saved to the database (e.g. delete cached object) + */ + protected function changePasswordPostFlush() + {} + +} diff --git a/library/OSS/Countries.php b/library/OSS/Countries.php new file mode 100644 index 0000000..59d6e24 --- /dev/null +++ b/library/OSS/Countries.php @@ -0,0 +1,1122 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Countries + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Countries +{ + /** + * Countries as an associate array with elements: 'IE' => 'Ireland' + * + * @var array + */ + private static $_countries = array( + 'AF' => 'Afghanistan', + 'AX' => 'Aland Islands', + 'AL' => 'Albania', + 'DZ' => 'Algeria', + 'AS' => 'American Samoa', + 'AD' => 'Andorra', + 'AO' => 'Angola', + 'AI' => 'Anguilla', + 'AQ' => 'Antarctica', + 'AG' => 'Antigua and Barbuda', + 'AR' => 'Argentina', + 'AM' => 'Armenia', + 'AW' => 'Aruba', + 'AP' => 'Asia/Pacific Region', + 'AU' => 'Australia', + 'AT' => 'Austria', + 'AZ' => 'Azerbaijan', + 'BS' => 'Bahamas', + 'BH' => 'Bahrain', + 'BD' => 'Bangladesh', + 'BB' => 'Barbados', + 'BY' => 'Belarus', + 'BE' => 'Belgium', + 'BZ' => 'Belize', + 'BJ' => 'Benin', + 'BM' => 'Bermuda', + 'BT' => 'Bhutan', + 'BO' => 'Bolivia', + 'BQ' => 'Bonaire, Saint Eustatius and Saba', + 'BA' => 'Bosnia and Herzegovina', + 'BW' => 'Botswana', + 'BV' => 'Bouvet Island', + 'BR' => 'Brazil', + 'IO' => 'British Indian Ocean Territory', + 'BN' => 'Brunei Darussalam', + 'BG' => 'Bulgaria', + 'BF' => 'Burkina Faso', + 'BI' => 'Burundi', + 'KH' => 'Cambodia', + 'CM' => 'Cameroon', + 'CA' => 'Canada', + 'CV' => 'Cape Verde', + 'KY' => 'Cayman Islands', + 'CF' => 'Central African Republic', + 'TD' => 'Chad', + 'CL' => 'Chile', + 'CN' => 'China', + 'CX' => 'Christmas Island', + 'CC' => 'Cocos (Keeling) Islands', + 'CO' => 'Colombia', + 'KM' => 'Comoros', + 'CG' => 'Congo', + 'CD' => 'Congo, The Democratic Republic of the', + 'CK' => 'Cook Islands', + 'CR' => 'Costa Rica', + 'CI' => 'Cote d\'Ivoire', + 'HR' => 'Croatia', + 'CU' => 'Cuba', + 'CW' => 'Curacao', + 'CY' => 'Cyprus', + 'CZ' => 'Czech Republic', + 'DK' => 'Denmark', + 'DJ' => 'Djibouti', + 'DM' => 'Dominica', + 'DO' => 'Dominican Republic', + 'EC' => 'Ecuador', + 'EG' => 'Egypt', + 'SV' => 'El Salvador', + 'GQ' => 'Equatorial Guinea', + 'ER' => 'Eritrea', + 'EE' => 'Estonia', + 'ET' => 'Ethiopia', + 'EU' => 'Europe', + 'FK' => 'Falkland Islands (Malvinas)', + 'FO' => 'Faroe Islands', + 'FJ' => 'Fiji', + 'FI' => 'Finland', + 'FR' => 'France', + 'GF' => 'French Guiana', + 'PF' => 'French Polynesia', + 'TF' => 'French Southern Territories', + 'GA' => 'Gabon', + 'GM' => 'Gambia', + 'GE' => 'Georgia', + 'DE' => 'Germany', + 'GH' => 'Ghana', + 'GI' => 'Gibraltar', + 'GR' => 'Greece', + 'GL' => 'Greenland', + 'GD' => 'Grenada', + 'GP' => 'Guadeloupe', + 'GU' => 'Guam', + 'GT' => 'Guatemala', + 'GG' => 'Guernsey', + 'GN' => 'Guinea', + 'GW' => 'Guinea-Bissau', + 'GY' => 'Guyana', + 'HT' => 'Haiti', + 'HM' => 'Heard Island and McDonald Islands', + 'VA' => 'Holy See (Vatican City State)', + 'HN' => 'Honduras', + 'HK' => 'Hong Kong', + 'HU' => 'Hungary', + 'IS' => 'Iceland', + 'IN' => 'India', + 'ID' => 'Indonesia', + 'IR' => 'Iran, Islamic Republic of', + 'IQ' => 'Iraq', + 'IE' => 'Ireland', + 'IM' => 'Isle of Man', + 'IL' => 'Israel', + 'IT' => 'Italy', + 'JM' => 'Jamaica', + 'JP' => 'Japan', + 'JE' => 'Jersey', + 'JO' => 'Jordan', + 'KZ' => 'Kazakhstan', + 'KE' => 'Kenya', + 'KI' => 'Kiribati', + 'KP' => 'Korea, Democratic People\'s Republic of', + 'KR' => 'Korea, Republic of', + 'KW' => 'Kuwait', + 'KG' => 'Kyrgyzstan', + 'LA' => 'Lao People\'s Democratic Republic', + 'LV' => 'Latvia', + 'LB' => 'Lebanon', + 'LS' => 'Lesotho', + 'LR' => 'Liberia', + 'LY' => 'Libyan Arab Jamahiriya', + 'LI' => 'Liechtenstein', + 'LT' => 'Lithuania', + 'LU' => 'Luxembourg', + 'MO' => 'Macao', + 'MK' => 'Macedonia', + 'MG' => 'Madagascar', + 'MW' => 'Malawi', + 'MY' => 'Malaysia', + 'MV' => 'Maldives', + 'ML' => 'Mali', + 'MT' => 'Malta', + 'MH' => 'Marshall Islands', + 'MQ' => 'Martinique', + 'MR' => 'Mauritania', + 'MU' => 'Mauritius', + 'YT' => 'Mayotte', + 'MX' => 'Mexico', + 'FM' => 'Micronesia, Federated States of', + 'MD' => 'Moldova, Republic of', + 'MC' => 'Monaco', + 'MN' => 'Mongolia', + 'ME' => 'Montenegro', + 'MS' => 'Montserrat', + 'MA' => 'Morocco', + 'MZ' => 'Mozambique', + 'MM' => 'Myanmar', + 'NA' => 'Namibia', + 'NR' => 'Nauru', + 'NP' => 'Nepal', + 'NL' => 'Netherlands', + 'NC' => 'New Caledonia', + 'NZ' => 'New Zealand', + 'NI' => 'Nicaragua', + 'NE' => 'Niger', + 'NG' => 'Nigeria', + 'NU' => 'Niue', + 'NF' => 'Norfolk Island', + 'MP' => 'Northern Mariana Islands', + 'NO' => 'Norway', + 'OM' => 'Oman', + 'PK' => 'Pakistan', + 'PW' => 'Palau', + 'PS' => 'Palestinian Territory', + 'PA' => 'Panama', + 'PG' => 'Papua New Guinea', + 'PY' => 'Paraguay', + 'PE' => 'Peru', + 'PH' => 'Philippines', + 'PN' => 'Pitcairn', + 'PL' => 'Poland', + 'PT' => 'Portugal', + 'PR' => 'Puerto Rico', + 'QA' => 'Qatar', + 'RE' => 'Reunion', + 'RO' => 'Romania', + 'RU' => 'Russian Federation', + 'RW' => 'Rwanda', + 'BL' => 'Saint Bartelemey', + 'SH' => 'Saint Helena', + 'KN' => 'Saint Kitts and Nevis', + 'LC' => 'Saint Lucia', + 'MF' => 'Saint Martin', + 'PM' => 'Saint Pierre and Miquelon', + 'VC' => 'Saint Vincent and the Grenadines', + 'WS' => 'Samoa', + 'SM' => 'San Marino', + 'ST' => 'Sao Tome and Principe', + 'SA' => 'Saudi Arabia', + 'SN' => 'Senegal', + 'RS' => 'Serbia', + 'SC' => 'Seychelles', + 'SL' => 'Sierra Leone', + 'SG' => 'Singapore', + 'SX' => 'Sint Maarten', + 'SK' => 'Slovakia', + 'SI' => 'Slovenia', + 'SB' => 'Solomon Islands', + 'SO' => 'Somalia', + 'ZA' => 'South Africa', + 'GS' => 'South Georgia and the South Sandwich Islands', + 'ES' => 'Spain', + 'LK' => 'Sri Lanka', + 'SD' => 'Sudan', + 'SR' => 'Suriname', + 'SJ' => 'Svalbard and Jan Mayen', + 'SZ' => 'Swaziland', + 'SE' => 'Sweden', + 'CH' => 'Switzerland', + 'SY' => 'Syrian Arab Republic', + 'TW' => 'Taiwan', + 'TJ' => 'Tajikistan', + 'TZ' => 'Tanzania, United Republic of', + 'TH' => 'Thailand', + 'TL' => 'Timor-Leste', + 'TG' => 'Togo', + 'TK' => 'Tokelau', + 'TO' => 'Tonga', + 'TT' => 'Trinidad and Tobago', + 'TN' => 'Tunisia', + 'TR' => 'Turkey', + 'TM' => 'Turkmenistan', + 'TC' => 'Turks and Caicos Islands', + 'TV' => 'Tuvalu', + 'UG' => 'Uganda', + 'UA' => 'Ukraine', + 'AE' => 'United Arab Emirates', + 'GB' => 'United Kingdom', + 'US' => 'United States', + 'UM' => 'United States Minor Outlying Islands', + 'UY' => 'Uruguay', + 'UZ' => 'Uzbekista', + 'VU' => 'Vanuatu', + 'VE' => 'Venezuela', + 'VN' => 'Vietnam', + 'VG' => 'Virgin Islands, British', + 'VI' => 'Virgin Islands, U.S.', + 'WF' => 'Wallis and Futuna', + 'EH' => 'Western Sahara', + 'YE' => 'Yemen', + 'ZM' => 'Zambia', + 'ZW' => 'Zimbabwe', + 'O1' => 'Other Country' + ); + + + /** + * Timezones as an associate array with elements: 'Europe/Dublin' => 'Europe/Dublin' + * + * @var array + */ + private static $_timezones = array( + 'Africa/Abidjan' => 'Africa/Abidjan', + 'Africa/Accra' => 'Africa/Accra', + 'Africa/Addis_Ababa' => 'Africa/Addis_Ababa', + 'Africa/Algiers' => 'Africa/Algiers', + 'Africa/Asmara' => 'Africa/Asmara', + 'Africa/Asmera' => 'Africa/Asmera', + 'Africa/Bamako' => 'Africa/Bamako', + 'Africa/Bangui' => 'Africa/Bangui', + 'Africa/Banjul' => 'Africa/Banjul', + 'Africa/Bissau' => 'Africa/Bissau', + 'Africa/Blantyre' => 'Africa/Blantyre', + 'Africa/Brazzaville' => 'Africa/Brazzaville', + 'Africa/Bujumbura' => 'Africa/Bujumbura', + 'Africa/Cairo' => 'Africa/Cairo', + 'Africa/Casablanca' => 'Africa/Casablanca', + 'Africa/Ceuta' => 'Africa/Ceuta', + 'Africa/Conakry' => 'Africa/Conakry', + 'Africa/Dakar' => 'Africa/Dakar', + 'Africa/Dar_es_Salaam' => 'Africa/Dar_es_Salaam', + 'Africa/Djibouti' => 'Africa/Djibouti', + 'Africa/Douala' => 'Africa/Douala', + 'Africa/El_Aaiun' => 'Africa/El_Aaiun', + 'Africa/Freetown' => 'Africa/Freetown', + 'Africa/Gaborone' => 'Africa/Gaborone', + 'Africa/Harare' => 'Africa/Harare', + 'Africa/Johannesburg' => 'Africa/Johannesburg', + 'Africa/Juba' => 'Africa/Juba', + 'Africa/Kampala' => 'Africa/Kampala', + 'Africa/Khartoum' => 'Africa/Khartoum', + 'Africa/Kigali' => 'Africa/Kigali', + 'Africa/Kinshasa' => 'Africa/Kinshasa', + 'Africa/Lagos' => 'Africa/Lagos', + 'Africa/Libreville' => 'Africa/Libreville', + 'Africa/Lome' => 'Africa/Lome', + 'Africa/Luanda' => 'Africa/Luanda', + 'Africa/Lubumbashi' => 'Africa/Lubumbashi', + 'Africa/Lusaka' => 'Africa/Lusaka', + 'Africa/Malabo' => 'Africa/Malabo', + 'Africa/Maputo' => 'Africa/Maputo', + 'Africa/Maseru' => 'Africa/Maseru', + 'Africa/Mbabane' => 'Africa/Mbabane', + 'Africa/Mogadishu' => 'Africa/Mogadishu', + 'Africa/Monrovia' => 'Africa/Monrovia', + 'Africa/Nairobi' => 'Africa/Nairobi', + 'Africa/Ndjamena' => 'Africa/Ndjamena', + 'Africa/Niamey' => 'Africa/Niamey', + 'Africa/Nouakchott' => 'Africa/Nouakchott', + 'Africa/Ouagadougou' => 'Africa/Ouagadougou', + 'Africa/Porto-Novo' => 'Africa/Porto-Novo', + 'Africa/Sao_Tome' => 'Africa/Sao_Tome', + 'Africa/Timbuktu' => 'Africa/Timbuktu', + 'Africa/Tripoli' => 'Africa/Tripoli', + 'Africa/Tunis' => 'Africa/Tunis', + 'Africa/Windhoek' => 'Africa/Windhoek', + 'America/Adak' => 'America/Adak', + 'America/Anchorage' => 'America/Anchorage', + 'America/Anguilla' => 'America/Anguilla', + 'America/Antigua' => 'America/Antigua', + 'America/Araguaina' => 'America/Araguaina', + 'America/Argentina/Buenos_Aires' => 'America/Argentina/Buenos_Aires', + 'America/Argentina/Catamarca' => 'America/Argentina/Catamarca', + 'America/Argentina/ComodRivadavia' => 'America/Argentina/ComodRivadavia', + 'America/Argentina/Cordoba' => 'America/Argentina/Cordoba', + 'America/Argentina/Jujuy' => 'America/Argentina/Jujuy', + 'America/Argentina/La_Rioja' => 'America/Argentina/La_Rioja', + 'America/Argentina/Mendoza' => 'America/Argentina/Mendoza', + 'America/Argentina/Rio_Gallegos' => 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta' => 'America/Argentina/Salta', + 'America/Argentina/San_Juan' => 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis' => 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman' => 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia' => 'America/Argentina/Ushuaia', + 'America/Aruba' => 'America/Aruba', + 'America/Asuncion' => 'America/Asuncion', + 'America/Atikokan' => 'America/Atikokan', + 'America/Atka' => 'America/Atka', + 'America/Bahia' => 'America/Bahia', + 'America/Bahia_Banderas' => 'America/Bahia_Banderas', + 'America/Barbados' => 'America/Barbados', + 'America/Belem' => 'America/Belem', + 'America/Belize' => 'America/Belize', + 'America/Blanc-Sablon' => 'America/Blanc-Sablon', + 'America/Boa_Vista' => 'America/Boa_Vista', + 'America/Bogota' => 'America/Bogota', + 'America/Boise' => 'America/Boise', + 'America/Buenos_Aires' => 'America/Buenos_Aires', + 'America/Cambridge_Bay' => 'America/Cambridge_Bay', + 'America/Campo_Grande' => 'America/Campo_Grande', + 'America/Cancun' => 'America/Cancun', + 'America/Caracas' => 'America/Caracas', + 'America/Catamarca' => 'America/Catamarca', + 'America/Cayenne' => 'America/Cayenne', + 'America/Cayman' => 'America/Cayman', + 'America/Chicago' => 'America/Chicago', + 'America/Chihuahua' => 'America/Chihuahua', + 'America/Coral_Harbour' => 'America/Coral_Harbour', + 'America/Cordoba' => 'America/Cordoba', + 'America/Costa_Rica' => 'America/Costa_Rica', + 'America/Cuiaba' => 'America/Cuiaba', + 'America/Curacao' => 'America/Curacao', + 'America/Danmarkshavn' => 'America/Danmarkshavn', + 'America/Dawson' => 'America/Dawson', + 'America/Dawson_Creek' => 'America/Dawson_Creek', + 'America/Denver' => 'America/Denver', + 'America/Detroit' => 'America/Detroit', + 'America/Dominica' => 'America/Dominica', + 'America/Edmonton' => 'America/Edmonton', + 'America/Eirunepe' => 'America/Eirunepe', + 'America/El_Salvador' => 'America/El_Salvador', + 'America/Ensenada' => 'America/Ensenada', + 'America/Fort_Wayne' => 'America/Fort_Wayne', + 'America/Fortaleza' => 'America/Fortaleza', + 'America/Glace_Bay' => 'America/Glace_Bay', + 'America/Godthab' => 'America/Godthab', + 'America/Goose_Bay' => 'America/Goose_Bay', + 'America/Grand_Turk' => 'America/Grand_Turk', + 'America/Grenada' => 'America/Grenada', + 'America/Guadeloupe' => 'America/Guadeloupe', + 'America/Guatemala' => 'America/Guatemala', + 'America/Guayaquil' => 'America/Guayaquil', + 'America/Guyana' => 'America/Guyana', + 'America/Halifax' => 'America/Halifax', + 'America/Havana' => 'America/Havana', + 'America/Hermosillo' => 'America/Hermosillo', + 'America/Indiana/Indianapolis' => 'America/Indiana/Indianapolis', + 'America/Indiana/Knox' => 'America/Indiana/Knox', + 'America/Indiana/Marengo' => 'America/Indiana/Marengo', + 'America/Indiana/Petersburg' => 'America/Indiana/Petersburg', + 'America/Indiana/Tell_City' => 'America/Indiana/Tell_City', + 'America/Indiana/Vevay' => 'America/Indiana/Vevay', + 'America/Indiana/Vincennes' => 'America/Indiana/Vincennes', + 'America/Indiana/Winamac' => 'America/Indiana/Winamac', + 'America/Indianapolis' => 'America/Indianapolis', + 'America/Inuvik' => 'America/Inuvik', + 'America/Iqaluit' => 'America/Iqaluit', + 'America/Jamaica' => 'America/Jamaica', + 'America/Jujuy' => 'America/Jujuy', + 'America/Juneau' => 'America/Juneau', + 'America/Kentucky/Louisville' => 'America/Kentucky/Louisville', + 'America/Kentucky/Monticello' => 'America/Kentucky/Monticello', + 'America/Knox_IN' => 'America/Knox_IN', + 'America/Kralendijk' => 'America/Kralendijk', + 'America/La_Paz' => 'America/La_Paz', + 'America/Lima' => 'America/Lima', + 'America/Los_Angeles' => 'America/Los_Angeles', + 'America/Louisville' => 'America/Louisville', + 'America/Lower_Princes' => 'America/Lower_Princes', + 'America/Maceio' => 'America/Maceio', + 'America/Managua' => 'America/Managua', + 'America/Manaus' => 'America/Manaus', + 'America/Marigot' => 'America/Marigot', + 'America/Martinique' => 'America/Martinique', + 'America/Matamoros' => 'America/Matamoros', + 'America/Mazatlan' => 'America/Mazatlan', + 'America/Mendoza' => 'America/Mendoza', + 'America/Menominee' => 'America/Menominee', + 'America/Merida' => 'America/Merida', + 'America/Metlakatla' => 'America/Metlakatla', + 'America/Mexico_City' => 'America/Mexico_City', + 'America/Miquelon' => 'America/Miquelon', + 'America/Moncton' => 'America/Moncton', + 'America/Monterrey' => 'America/Monterrey', + 'America/Montevideo' => 'America/Montevideo', + 'America/Montreal' => 'America/Montreal', + 'America/Montserrat' => 'America/Montserrat', + 'America/Nassau' => 'America/Nassau', + 'America/New_York' => 'America/New_York', + 'America/Nipigon' => 'America/Nipigon', + 'America/Nome' => 'America/Nome', + 'America/Noronha' => 'America/Noronha', + 'America/North_Dakota/Beulah' => 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center' => 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem' => 'America/North_Dakota/New_Salem', + 'America/Ojinaga' => 'America/Ojinaga', + 'America/Panama' => 'America/Panama', + 'America/Pangnirtung' => 'America/Pangnirtung', + 'America/Paramaribo' => 'America/Paramaribo', + 'America/Phoenix' => 'America/Phoenix', + 'America/Port-au-Prince' => 'America/Port-au-Prince', + 'America/Port_of_Spain' => 'America/Port_of_Spain', + 'America/Porto_Acre' => 'America/Porto_Acre', + 'America/Porto_Velho' => 'America/Porto_Velho', + 'America/Puerto_Rico' => 'America/Puerto_Rico', + 'America/Rainy_River' => 'America/Rainy_River', + 'America/Rankin_Inlet' => 'America/Rankin_Inlet', + 'America/Recife' => 'America/Recife', + 'America/Regina' => 'America/Regina', + 'America/Resolute' => 'America/Resolute', + 'America/Rio_Branco' => 'America/Rio_Branco', + 'America/Rosario' => 'America/Rosario', + 'America/Santa_Isabel' => 'America/Santa_Isabel', + 'America/Santarem' => 'America/Santarem', + 'America/Santiago' => 'America/Santiago', + 'America/Santo_Domingo' => 'America/Santo_Domingo', + 'America/Sao_Paulo' => 'America/Sao_Paulo', + 'America/Scoresbysund' => 'America/Scoresbysund', + 'America/Shiprock' => 'America/Shiprock', + 'America/Sitka' => 'America/Sitka', + 'America/St_Barthelemy' => 'America/St_Barthelemy', + 'America/St_Johns' => 'America/St_Johns', + 'America/St_Kitts' => 'America/St_Kitts', + 'America/St_Lucia' => 'America/St_Lucia', + 'America/St_Thomas' => 'America/St_Thomas', + 'America/St_Vincent' => 'America/St_Vincent', + 'America/Swift_Current' => 'America/Swift_Current', + 'America/Tegucigalpa' => 'America/Tegucigalpa', + 'America/Thule' => 'America/Thule', + 'America/Thunder_Bay' => 'America/Thunder_Bay', + 'America/Tijuana' => 'America/Tijuana', + 'America/Toronto' => 'America/Toronto', + 'America/Tortola' => 'America/Tortola', + 'America/Vancouver' => 'America/Vancouver', + 'America/Virgin' => 'America/Virgin', + 'America/Whitehorse' => 'America/Whitehorse', + 'America/Winnipeg' => 'America/Winnipeg', + 'America/Yakutat' => 'America/Yakutat', + 'America/Yellowknife' => 'America/Yellowknife', + 'Antarctica/Casey' => 'Antarctica/Casey', + 'Antarctica/Davis' => 'Antarctica/Davis', + 'Antarctica/DumontDUrville' => 'Antarctica/DumontDUrville', + 'Antarctica/Macquarie' => 'Antarctica/Macquarie', + 'Antarctica/Mawson' => 'Antarctica/Mawson', + 'Antarctica/McMurdo' => 'Antarctica/McMurdo', + 'Antarctica/Palmer' => 'Antarctica/Palmer', + 'Antarctica/Rothera' => 'Antarctica/Rothera', + 'Antarctica/South_Pole' => 'Antarctica/South_Pole', + 'Antarctica/Syowa' => 'Antarctica/Syowa', + 'Antarctica/Vostok' => 'Antarctica/Vostok', + 'Arctic/Longyearbyen' => 'Arctic/Longyearbyen', + 'Asia/Aden' => 'Asia/Aden', + 'Asia/Almaty' => 'Asia/Almaty', + 'Asia/Amman' => 'Asia/Amman', + 'Asia/Anadyr' => 'Asia/Anadyr', + 'Asia/Aqtau' => 'Asia/Aqtau', + 'Asia/Aqtobe' => 'Asia/Aqtobe', + 'Asia/Ashgabat' => 'Asia/Ashgabat', + 'Asia/Ashkhabad' => 'Asia/Ashkhabad', + 'Asia/Baghdad' => 'Asia/Baghdad', + 'Asia/Bahrain' => 'Asia/Bahrain', + 'Asia/Baku' => 'Asia/Baku', + 'Asia/Bangkok' => 'Asia/Bangkok', + 'Asia/Beirut' => 'Asia/Beirut', + 'Asia/Bishkek' => 'Asia/Bishkek', + 'Asia/Brunei' => 'Asia/Brunei', + 'Asia/Calcutta' => 'Asia/Calcutta', + 'Asia/Choibalsan' => 'Asia/Choibalsan', + 'Asia/Chongqing' => 'Asia/Chongqing', + 'Asia/Chungking' => 'Asia/Chungking', + 'Asia/Colombo' => 'Asia/Colombo', + 'Asia/Dacca' => 'Asia/Dacca', + 'Asia/Damascus' => 'Asia/Damascus', + 'Asia/Dhaka' => 'Asia/Dhaka', + 'Asia/Dili' => 'Asia/Dili', + 'Asia/Dubai' => 'Asia/Dubai', + 'Asia/Dushanbe' => 'Asia/Dushanbe', + 'Asia/Gaza' => 'Asia/Gaza', + 'Asia/Harbin' => 'Asia/Harbin', + 'Asia/Hebron' => 'Asia/Hebron', + 'Asia/Ho_Chi_Minh' => 'Asia/Ho_Chi_Minh', + 'Asia/Hong_Kong' => 'Asia/Hong_Kong', + 'Asia/Hovd' => 'Asia/Hovd', + 'Asia/Irkutsk' => 'Asia/Irkutsk', + 'Asia/Istanbul' => 'Asia/Istanbul', + 'Asia/Jakarta' => 'Asia/Jakarta', + 'Asia/Jayapura' => 'Asia/Jayapura', + 'Asia/Jerusalem' => 'Asia/Jerusalem', + 'Asia/Kabul' => 'Asia/Kabul', + 'Asia/Kamchatka' => 'Asia/Kamchatka', + 'Asia/Karachi' => 'Asia/Karachi', + 'Asia/Kashgar' => 'Asia/Kashgar', + 'Asia/Kathmandu' => 'Asia/Kathmandu', + 'Asia/Katmandu' => 'Asia/Katmandu', + 'Asia/Kolkata' => 'Asia/Kolkata', + 'Asia/Krasnoyarsk' => 'Asia/Krasnoyarsk', + 'Asia/Kuala_Lumpur' => 'Asia/Kuala_Lumpur', + 'Asia/Kuching' => 'Asia/Kuching', + 'Asia/Kuwait' => 'Asia/Kuwait', + 'Asia/Macao' => 'Asia/Macao', + 'Asia/Macau' => 'Asia/Macau', + 'Asia/Magadan' => 'Asia/Magadan', + 'Asia/Makassar' => 'Asia/Makassar', + 'Asia/Manila' => 'Asia/Manila', + 'Asia/Muscat' => 'Asia/Muscat', + 'Asia/Nicosia' => 'Asia/Nicosia', + 'Asia/Novokuznetsk' => 'Asia/Novokuznetsk', + 'Asia/Novosibirsk' => 'Asia/Novosibirsk', + 'Asia/Omsk' => 'Asia/Omsk', + 'Asia/Oral' => 'Asia/Oral', + 'Asia/Phnom_Penh' => 'Asia/Phnom_Penh', + 'Asia/Pontianak' => 'Asia/Pontianak', + 'Asia/Pyongyang' => 'Asia/Pyongyang', + 'Asia/Qatar' => 'Asia/Qatar', + 'Asia/Qyzylorda' => 'Asia/Qyzylorda', + 'Asia/Rangoon' => 'Asia/Rangoon', + 'Asia/Riyadh' => 'Asia/Riyadh', + 'Asia/Saigon' => 'Asia/Saigon', + 'Asia/Sakhalin' => 'Asia/Sakhalin', + 'Asia/Samarkand' => 'Asia/Samarkand', + 'Asia/Seoul' => 'Asia/Seoul', + 'Asia/Shanghai' => 'Asia/Shanghai', + 'Asia/Singapore' => 'Asia/Singapore', + 'Asia/Taipei' => 'Asia/Taipei', + 'Asia/Tashkent' => 'Asia/Tashkent', + 'Asia/Tbilisi' => 'Asia/Tbilisi', + 'Asia/Tehran' => 'Asia/Tehran', + 'Asia/Tel_Aviv' => 'Asia/Tel_Aviv', + 'Asia/Thimbu' => 'Asia/Thimbu', + 'Asia/Thimphu' => 'Asia/Thimphu', + 'Asia/Tokyo' => 'Asia/Tokyo', + 'Asia/Ujung_Pandang' => 'Asia/Ujung_Pandang', + 'Asia/Ulaanbaatar' => 'Asia/Ulaanbaatar', + 'Asia/Ulan_Bator' => 'Asia/Ulan_Bator', + 'Asia/Urumqi' => 'Asia/Urumqi', + 'Asia/Vientiane' => 'Asia/Vientiane', + 'Asia/Vladivostok' => 'Asia/Vladivostok', + 'Asia/Yakutsk' => 'Asia/Yakutsk', + 'Asia/Yekaterinburg' => 'Asia/Yekaterinburg', + 'Asia/Yerevan' => 'Asia/Yerevan', + 'Atlantic/Azores' => 'Atlantic/Azores', + 'Atlantic/Bermuda' => 'Atlantic/Bermuda', + 'Atlantic/Canary' => 'Atlantic/Canary', + 'Atlantic/Cape_Verde' => 'Atlantic/Cape_Verde', + 'Atlantic/Faeroe' => 'Atlantic/Faeroe', + 'Atlantic/Faroe' => 'Atlantic/Faroe', + 'Atlantic/Jan_Mayen' => 'Atlantic/Jan_Mayen', + 'Atlantic/Madeira' => 'Atlantic/Madeira', + 'Atlantic/Reykjavik' => 'Atlantic/Reykjavik', + 'Atlantic/South_Georgia' => 'Atlantic/South_Georgia', + 'Atlantic/St_Helena' => 'Atlantic/St_Helena', + 'Atlantic/Stanley' => 'Atlantic/Stanley', + 'Australia/ACT' => 'Australia/ACT', + 'Australia/Adelaide' => 'Australia/Adelaide', + 'Australia/Brisbane' => 'Australia/Brisbane', + 'Australia/Broken_Hill' => 'Australia/Broken_Hill', + 'Australia/Canberra' => 'Australia/Canberra', + 'Australia/Currie' => 'Australia/Currie', + 'Australia/Darwin' => 'Australia/Darwin', + 'Australia/Eucla' => 'Australia/Eucla', + 'Australia/Hobart' => 'Australia/Hobart', + 'Australia/LHI' => 'Australia/LHI', + 'Australia/Lindeman' => 'Australia/Lindeman', + 'Australia/Lord_Howe' => 'Australia/Lord_Howe', + 'Australia/Melbourne' => 'Australia/Melbourne', + 'Australia/North' => 'Australia/North', + 'Australia/NSW' => 'Australia/NSW', + 'Australia/Perth' => 'Australia/Perth', + 'Australia/Queensland' => 'Australia/Queensland', + 'Australia/South' => 'Australia/South', + 'Australia/Sydney' => 'Australia/Sydney', + 'Australia/Tasmania' => 'Australia/Tasmania', + 'Australia/Victoria' => 'Australia/Victoria', + 'Australia/West' => 'Australia/West', + 'Australia/Yancowinna' => 'Australia/Yancowinna', + 'Europe/Amsterdam' => 'Europe/Amsterdam', + 'Europe/Andorra' => 'Europe/Andorra', + 'Europe/Athens' => 'Europe/Athens', + 'Europe/Belfast' => 'Europe/Belfast', + 'Europe/Belgrade' => 'Europe/Belgrade', + 'Europe/Berlin' => 'Europe/Berlin', + 'Europe/Bratislava' => 'Europe/Bratislava', + 'Europe/Brussels' => 'Europe/Brussels', + 'Europe/Bucharest' => 'Europe/Bucharest', + 'Europe/Budapest' => 'Europe/Budapest', + 'Europe/Chisinau' => 'Europe/Chisinau', + 'Europe/Copenhagen' => 'Europe/Copenhagen', + 'Europe/Dublin' => 'Europe/Dublin', + 'Europe/Gibraltar' => 'Europe/Gibraltar', + 'Europe/Guernsey' => 'Europe/Guernsey', + 'Europe/Helsinki' => 'Europe/Helsinki', + 'Europe/Isle_of_Man' => 'Europe/Isle_of_Man', + 'Europe/Istanbul' => 'Europe/Istanbul', + 'Europe/Jersey' => 'Europe/Jersey', + 'Europe/Kaliningrad' => 'Europe/Kaliningrad', + 'Europe/Kiev' => 'Europe/Kiev', + 'Europe/Lisbon' => 'Europe/Lisbon', + 'Europe/Ljubljana' => 'Europe/Ljubljana', + 'Europe/London' => 'Europe/London', + 'Europe/Luxembourg' => 'Europe/Luxembourg', + 'Europe/Madrid' => 'Europe/Madrid', + 'Europe/Malta' => 'Europe/Malta', + 'Europe/Mariehamn' => 'Europe/Mariehamn', + 'Europe/Minsk' => 'Europe/Minsk', + 'Europe/Monaco' => 'Europe/Monaco', + 'Europe/Moscow' => 'Europe/Moscow', + 'Europe/Nicosia' => 'Europe/Nicosia', + 'Europe/Oslo' => 'Europe/Oslo', + 'Europe/Paris' => 'Europe/Paris', + 'Europe/Podgorica' => 'Europe/Podgorica', + 'Europe/Prague' => 'Europe/Prague', + 'Europe/Riga' => 'Europe/Riga', + 'Europe/Rome' => 'Europe/Rome', + 'Europe/Samara' => 'Europe/Samara', + 'Europe/San_Marino' => 'Europe/San_Marino', + 'Europe/Sarajevo' => 'Europe/Sarajevo', + 'Europe/Simferopol' => 'Europe/Simferopol', + 'Europe/Skopje' => 'Europe/Skopje', + 'Europe/Sofia' => 'Europe/Sofia', + 'Europe/Stockholm' => 'Europe/Stockholm', + 'Europe/Tallinn' => 'Europe/Tallinn', + 'Europe/Tirane' => 'Europe/Tirane', + 'Europe/Tiraspol' => 'Europe/Tiraspol', + 'Europe/Uzhgorod' => 'Europe/Uzhgorod', + 'Europe/Vaduz' => 'Europe/Vaduz', + 'Europe/Vatican' => 'Europe/Vatican', + 'Europe/Vienna' => 'Europe/Vienna', + 'Europe/Vilnius' => 'Europe/Vilnius', + 'Europe/Volgograd' => 'Europe/Volgograd', + 'Europe/Warsaw' => 'Europe/Warsaw', + 'Europe/Zagreb' => 'Europe/Zagreb', + 'Europe/Zaporozhye' => 'Europe/Zaporozhye', + 'Europe/Zurich' => 'Europe/Zurich', + 'Indian/Antananarivo' => 'Indian/Antananarivo', + 'Indian/Chagos' => 'Indian/Chagos', + 'Indian/Christmas' => 'Indian/Christmas', + 'Indian/Cocos' => 'Indian/Cocos', + 'Indian/Comoro' => 'Indian/Comoro', + 'Indian/Kerguelen' => 'Indian/Kerguelen', + 'Indian/Mahe' => 'Indian/Mahe', + 'Indian/Maldives' => 'Indian/Maldives', + 'Indian/Mauritius' => 'Indian/Mauritius', + 'Indian/Mayotte' => 'Indian/Mayotte', + 'Indian/Reunion' => 'Indian/Reunion', + 'Pacific/Apia' => 'Pacific/Apia', + 'Pacific/Auckland' => 'Pacific/Auckland', + 'Pacific/Chatham' => 'Pacific/Chatham', + 'Pacific/Chuuk' => 'Pacific/Chuuk', + 'Pacific/Easter' => 'Pacific/Easter', + 'Pacific/Efate' => 'Pacific/Efate', + 'Pacific/Enderbury' => 'Pacific/Enderbury', + 'Pacific/Fakaofo' => 'Pacific/Fakaofo', + 'Pacific/Fiji' => 'Pacific/Fiji', + 'Pacific/Funafuti' => 'Pacific/Funafuti', + 'Pacific/Galapagos' => 'Pacific/Galapagos', + 'Pacific/Gambier' => 'Pacific/Gambier', + 'Pacific/Guadalcanal' => 'Pacific/Guadalcanal', + 'Pacific/Guam' => 'Pacific/Guam', + 'Pacific/Honolulu' => 'Pacific/Honolulu', + 'Pacific/Johnston' => 'Pacific/Johnston', + 'Pacific/Kiritimati' => 'Pacific/Kiritimati', + 'Pacific/Kosrae' => 'Pacific/Kosrae', + 'Pacific/Kwajalein' => 'Pacific/Kwajalein', + 'Pacific/Majuro' => 'Pacific/Majuro', + 'Pacific/Marquesas' => 'Pacific/Marquesas', + 'Pacific/Midway' => 'Pacific/Midway', + 'Pacific/Nauru' => 'Pacific/Nauru', + 'Pacific/Niue' => 'Pacific/Niue', + 'Pacific/Norfolk' => 'Pacific/Norfolk', + 'Pacific/Noumea' => 'Pacific/Noumea', + 'Pacific/Pago_Pago' => 'Pacific/Pago_Pago', + 'Pacific/Palau' => 'Pacific/Palau', + 'Pacific/Pitcairn' => 'Pacific/Pitcairn', + 'Pacific/Pohnpei' => 'Pacific/Pohnpei', + 'Pacific/Ponape' => 'Pacific/Ponape', + 'Pacific/Port_Moresby' => 'Pacific/Port_Moresby', + 'Pacific/Rarotonga' => 'Pacific/Rarotonga', + 'Pacific/Saipan' => 'Pacific/Saipan', + 'Pacific/Samoa' => 'Pacific/Samoa', + 'Pacific/Tahiti' => 'Pacific/Tahiti', + 'Pacific/Tarawa' => 'Pacific/Tarawa', + 'Pacific/Tongatapu' => 'Pacific/Tongatapu', + 'Pacific/Truk' => 'Pacific/Truk', + 'Pacific/Wake' => 'Pacific/Wake', + 'Pacific/Wallis' => 'Pacific/Wallis', + 'Pacific/Yap' => 'Pacific/Yap', + 'UTC' => 'UTC' + ); + + /** + * Countries as an associate array with elements: 'IE' => 'Ireland' + * + * @var array + */ + private static $_dial_codes = array( + 'AF' => 93, + 'AL' => 355, + 'DZ' => 213, + 'AS' => 1684, + 'AD' => 376, + 'AO' => 244, + 'AI' => 1264, + 'AQ' => 672, + 'AG' => 1268, + 'AR' => 54, + 'AM' => 374, + 'AW' => 297, + 'AU' => 61, + 'AT' => 43, + 'AZ' => 994, + 'BS' => 1242, + 'BH' => 973, + 'BD' => 880, + 'BB' => 1246, + 'BY' => 375, + 'BE' => 32, + 'BZ' => 501, + 'BJ' => 229, + 'BM' => 1441, + 'BT' => 975, + 'BO' => 591, + 'BA' => 387, + 'BW' => 267, + 'BR' => 55, + 'VG' => 1284, + 'BN' => 673, + 'BG' => 359, + 'BF' => 226, + 'MM' => 95, + 'BI' => 257, + 'KH' => 855, + 'CM' => 237, + 'CA' => 1, + 'CV' => 238, + 'KY' => 1345, + 'CF' => 236, + 'TD' => 235, + 'CL' => 56, + 'CN' => 86, + 'CX' => 61, + 'CC' => 61, + 'CO' => 57, + 'KM' => 269, + 'CK' => 682, + 'CR' => 506, + 'HR' => 385, + 'CU' => 53, + 'CY' => 357, + 'CZ' => 420, + 'CD' => 243, + 'DK' => 45, + 'DJ' => 253, + 'DM' => 1767, + 'DO' => 1809, + 'EC' => 593, + 'EG' => 20, + 'SV' => 503, + 'GQ' => 240, + 'ER' => 291, + 'EE' => 372, + 'ET' => 251, + 'FK' => 500, + 'FO' => 298, + 'FJ' => 679, + 'FI' => 358, + 'FR' => 33, + 'PF' => 689, + 'GA' => 241, + 'GM' => 220, + 'GE' => 995, + 'DE' => 49, + 'GH' => 233, + 'GI' => 350, + 'GR' => 30, + 'GL' => 299, + 'GD' => 1473, + 'GU' => 1671, + 'GT' => 502, + 'GN' => 224, + 'GW' => 245, + 'GY' => 592, + 'HT' => 509, + 'VA' => 39, + 'HN' => 504, + 'HK' => 852, + 'HU' => 36, + 'IS' => 354, + 'IN' => 91, + 'ID' => 62, + 'IR' => 98, + 'IQ' => 964, + 'IE' => 353, + 'IM' => 44, + 'IL' => 972, + 'IT' => 39, + 'CI' => 225, + 'JM' => 1876, + 'JP' => 81, + 'JO' => 962, + 'KZ' => 7, + 'KE' => 254, + 'KI' => 686, + 'KW' => 965, + 'KG' => 996, + 'LA' => 856, + 'LV' => 371, + 'LB' => 961, + 'LS' => 266, + 'LR' => 231, + 'LY' => 218, + 'LI' => 423, + 'LT' => 370, + 'LU' => 352, + 'MO' => 853, + 'MK' => 389, + 'MG' => 261, + 'MW' => 265, + 'MY' => 60, + 'MV' => 960, + 'ML' => 223, + 'MT' => 356, + 'MH' => 692, + 'MR' => 222, + 'MU' => 230, + 'YT' => 262, + 'MX' => 52, + 'FM' => 691, + 'MD' => 373, + 'MC' => 377, + 'MN' => 976, + 'ME' => 382, + 'MS' => 1664, + 'MA' => 212, + 'MZ' => 258, + 'NA' => 264, + 'NR' => 674, + 'NP' => 977, + 'NL' => 31, + 'AN' => 599, + 'NC' => 687, + 'NZ' => 64, + 'NI' => 505, + 'NE' => 227, + 'NG' => 234, + 'NU' => 683, + 'NFK'=> 672, + 'KP' => 850, + 'MP' => 1670, + 'NO' => 47, + 'OM' => 968, + 'PK' => 92, + 'PW' => 680, + 'PA' => 507, + 'PG' => 675, + 'PY' => 595, + 'PE' => 51, + 'PH' => 63, + 'PN' => 870, + 'PL' => 48, + 'PT' => 351, + 'PR' => 1, + 'QA' => 974, + 'CG' => 242, + 'RO' => 40, + 'RU' => 7, + 'RW' => 250, + 'BL' => 590, + 'SH' => 290, + 'KN' => 1869, + 'LC' => 1758, + 'MF' => 1599, + 'PM' => 508, + 'VC' => 1784, + 'WS' => 685, + 'SM' => 378, + 'ST' => 239, + 'SA' => 966, + 'SN' => 221, + 'RS' => 381, + 'SC' => 248, + 'SL' => 232, + 'SG' => 65, + 'SK' => 421, + 'SI' => 386, + 'SB' => 677, + 'SO' => 252, + 'ZA' => 27, + 'KR' => 82, + 'ES' => 34, + 'LK' => 94, + 'SD' => 249, + 'SR' => 597, + 'SZ' => 268, + 'SE' => 46, + 'CH' => 41, + 'SY' => 963, + 'TW' => 886, + 'TJ' => 992, + 'TZ' => 255, + 'TH' => 66, + 'TL' => 670, + 'TG' => 228, + 'TK' => 690, + 'TO' => 676, + 'TT' => 1868, + 'TN' => 216, + 'TR' => 90, + 'TM' => 993, + 'TC' => 1649, + 'TV' => 688, + 'UG' => 256, + 'UA' => 380, + 'AE' => 971, + 'GB' => 44, + 'US' => 1, + 'UY' => 598, + 'VI' => 1340, + 'UZ' => 998, + 'VU' => 678, + 'VE' => 58, + 'VN' => 84, + 'WF' => 681, + 'YE' => 967, + 'ZM' => 260, + 'ZW' => 263 + ); + + /** + * Returns associative array of countries. + * + * @return array + */ + public static function getCountriesArray() + { + return self::$_countries; + } + + /** + * Returns array of country codes. + * + * @return array + */ + public static function getCountriesKeys() + { + return array_keys( self::$_countries ); + } + + /** + * Returns country name by given code. + * If code is not in array returns null; + * + * @param string $code The country code + * @return string|null + */ + public static function getCountryName( $code ) + { + if( isset( self::$_countries[$code] ) ) + return self::$_countries[$code]; + else + return null; + } + + /** + * Returns associative array of timezones (e.g. 'Europe/Dublin' => 'Europe/Dublin' ). + * + * @return array + */ + public static function getTimezonesArray() + { + return self::$_timezones; + } + + /** + * Returns array of timezones + * + * @return array + */ + public static function getTimezones() + { + return array_keys( self::$_timezones ); + } + + /** + * Returns country name by given code. + * If code is not in array returns null; + * + * @param int $code The country code + * @return string|null + */ + public static function getDialCode( $code ) + { + if( isset( self::$_dial_codes[$code] ) ) + return self::$_dial_codes[$code]; + else + return null; + } + + /** + * Returns array of timezones + * + * @return array + */ + public static function getDialCodes() + { + return self::$_dial_codes; + } + + + /** + * Utility function to create a select form element of countries + * + * @param string $name The element name (default `country`) + * @param string $label The element label and title (default `Country`) + * @return Zend_Form_Element_Select + */ + public static function createFormSelectElement( $name = 'country', $label = 'Country' ) + { + $country = new Zend_Form_Element_Select( $name ); + + $country->setLabel( $label ) + ->setAttrib( 'title', $label ) + ->setMultiOptions( [ '' => ' ' ] + self::getCountriesArray() ) + ->setAttrib( 'data-placeholder', 'Select your country...' ) + ->addValidator( 'InArray', true, [ self::getCountriesKeys() ] ); + + $country->getValidator( 'InArray' )->setMessage( + 'You must choose a country', Zend_Validate_InArray::NOT_IN_ARRAY + ); + + return $country; + } +} diff --git a/library/OSS/Crypt/Bcrypt.php b/library/OSS/Crypt/Bcrypt.php new file mode 100644 index 0000000..d133712 --- /dev/null +++ b/library/OSS/Crypt/Bcrypt.php @@ -0,0 +1,108 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Bcrypt (Blowfish) hashing tools for password. + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Crypt + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Crypt_Bcrypt +{ + + /** + * @var int The cost for the hashing algorithm + * + * Example values showing the cost of 10 and the average on an i7 are: + * + * 01: 0.001437 0.000144 + * 02: 0.001441 0.000144 + * 03: 0.002011 0.000201 + * 04: 0.017564 0.001756 + * 05: 0.029720 0.002972 + * 06: 0.055418 0.005542 + * 07: 0.109075 0.010908 + * 08: 0.207278 0.020728 + * 09: 0.407365 0.040737 + * 10: 0.839712 0.083971 + * 11: 1.674868 0.167487 + * 12: 3.336014 0.333601 + * 13: 6.699570 0.669957 + * 14: 15.655678 1.565568 + * 15: 26.771987 2.677199 + */ + private static $_cost = 9; + + + public function __construct( $cost = 9 ) + { + if( CRYPT_BLOWFISH != 1 ) + throw new OSS_Crypt_Exception( 'CRYPT_BLOWFISH unavailable. See http://php.net/crypt' ); + + self::$_cost = $cost; + } + + + public static function hash( $plain ) + { + $hash = crypt( $plain, self::generateSalt() ); + + if( strlen( $hash ) > 13 ) + return $hash; + + return false; + } + + + public static function verify( $plain, $hash ) + { + return $hash === crypt( $plain, $hash ); + } + + public static function generateSalt() + { + return sprintf( '$2a$%02d$%s', self::$_cost, OSS_String::random( 22, true, true, true, '', '' ) ); + } + +} + diff --git a/library/OSS/Crypt/Exception.php b/library/OSS/Crypt/Exception.php new file mode 100644 index 0000000..8cb221e --- /dev/null +++ b/library/OSS/Crypt/Exception.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Crypt + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Crypt_Exception extends OSS_Exception +{ + +} + + diff --git a/library/OSS/Crypt/GibberishAES.php b/library/OSS/Crypt/GibberishAES.php new file mode 100644 index 0000000..10384b5 --- /dev/null +++ b/library/OSS/Crypt/GibberishAES.php @@ -0,0 +1,129 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Gibberish AES encryption tools. + * + * Based on https://github.com/mdp/gibberish-aes (MIT) and notes by + * nbari at dalmp dot com on 15-Jan-2012 07:52 at + * http://www.php.net/manual/en/function.openssl-decrypt.php + * + * @link https://github.com/mdp/gibberish-aes + * @link http://www.php.net/manual/en/function.openssl-decrypt.php + * @category OSS + * @package OSS_Crypt + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Crypt_GibberishAES +{ + + /** + * Encrypt the given `$data` using AES 256 symmetrical cipher + * + * See `tests/OSS/Crypt/GibberishAESTest.php` for examples + * + * @param string $data The data to encrypt + * @param string $password The symmectrical password to use for the encryption + * @return string Base64 encrypted data with embedded (random) salt + */ + public static function encrypt( $data, $password ) + { + $salt = openssl_random_pseudo_bytes( 8 ); + + $salted = ''; + $dx = ''; + + // Salt the key(32) and iv(16) = 48 + while( strlen( $salted ) < 48 ) + { + $dx = md5( $dx . $password . $salt, true ); + $salted .= $dx; + } + + $key = substr( $salted, 0, 32 ); + $iv = substr( $salted, 32,16 ); + + $encrypted_data = openssl_encrypt( $data, 'aes-256-cbc', $key, true, $iv ); + return base64_encode( 'Salted__' . $salt . $encrypted_data ); + } + + + /** + * Decrypt the given `$edata` using AES 256 symmetrical cipher + * + * See `tests/OSS/Crypt/GibberishAESTest.php` for examples + * + * @param string $edata The encrypted data to decrypt + * @param string $password The symmectrical password used for the encryption + * @return string The original data decrypted (or false on failure) + */ + public static function decrypt( $edata, $password ) + { + $data = base64_decode($edata); + $salt = substr( $data, 8, 8 ); + $ct = substr( $data, 16 ); + + /** + * From https://github.com/mdp/gibberish-aes + * + * Number of rounds depends on the size of the AES in use + * 3 rounds for 256 + * 2 rounds for the key, 1 for the IV + * 2 rounds for 128 + * 1 round for the key, 1 round for the IV + * 3 rounds for 192 since it's not evenly divided by 128 bits + */ + $rounds = 3; + $data00 = $password.$salt; + $md5_hash = array(); + $md5_hash[0] = md5($data00, true); + $result = $md5_hash[0]; + for ($i = 1; $i < $rounds; $i++) { + $md5_hash[$i] = md5($md5_hash[$i - 1].$data00, true); + $result .= $md5_hash[$i]; + } + $key = substr($result, 0, 32); + $iv = substr($result, 32,16); + + return openssl_decrypt( $ct, 'aes-256-cbc', $key, true, $iv ); + } + +} + diff --git a/library/OSS/Crypt/OpenSSL.php b/library/OSS/Crypt/OpenSSL.php new file mode 100644 index 0000000..ac27f09 --- /dev/null +++ b/library/OSS/Crypt/OpenSSL.php @@ -0,0 +1,239 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * OpenSSL public/private key generation tools. + * + * NB: I keep getting bit by: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=586202 + * + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Crypt + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Crypt_OpenSSL +{ + /** + * A variable to store a Passphrase to encrypt / decrypt the key pair + * + * @var string Passphrase to encrypt / decrypt the key pair + */ + private $_passphrase = null; + + /** + * A variable to hold a key pair resource + * + * @var resource A key pair resource + */ + private $_keypair = null; + + /** + * A variable to hold a certificate resource + * + * @var resource A certificate resource + */ + private $_cert = null; + + /** + * Sets passphrase. + * + * Sets passphrase which will be used for key pair encryption. + * + * @param string $passphrase The passphrase for key pair encryption + * @return void + */ + public function setPassphrase( $passphrase ) + { + $this->_passphrase = $passphrase; + } + + /** + * Sets key pair. + * + * Sets key pair from key pair file content + * + * @param string $keypair The key pair file content + * @return resource of key pair + */ + public function setKeyPair( $keypair ) + { + $this->_keypair = $keypair; + return $this->_keypair; + } + + + /** + * Generates key pair. + * + * Generates public and private key pair for encryption / decryption. + * + * @param array $options The generator options. + * @return resource of key pair + */ + public function genKeyPair( $options ) + { + $this->_keypair = openssl_pkey_new( $options ); + return $this->_keypair; + } + + /** + * Exports key pair. + * + * Exports key pair which is actually private key which store public key information. + * If encrypt is set to true then export resource will be encrypted with $_passphrase + * else it will return the actual key without encryption. By default $encrypt is true. + * + * @param boo $encrypt The encrypt flag to encrypt or not the returning resource. + * @return resource of key pair + * @throws OSS_Crypt_Exception if $_keypair is not generated, or if encryption required + * and $_passphrase is not unset. + */ + public function exportKeyPair( $encrypt = true ) + { + if( !$this->_keypair ) + throw new OSS_Crypt_Exception( 'To export key pair, you need to generate it first.' + . "\n\nDid you comment out multiple instances of subjectAltName in openssl.conf?" ); + + if( $encrypt ) + { + if( !$this->_passphrase ) + throw new OSS_Crypt_Exception( 'Encrypt key pair fails because no passphrase is set.' ); + openssl_pkey_export( $this->_keypair, $out, $this->_passphrase ); + } + else + openssl_pkey_export( $this->_keypair, $out ); + + return $out; + } + + /** + * Exports public. + * + * Exports public key from $_keypair. + * + * @return resource of public key + * @throws OSS_Crypt_Exception if $_keypair is not generated. + */ + public function exportPublicKey() + { + if( !$this->_keypair ) + throw new OSS_Crypt_Exception( 'To export public key, you need to generate key pair first.' ); + + return openssl_pkey_get_details( $this->_keypair )[ 'key' ]; + } + + /** + * Generates sertificate for given information + * + * @param array $dn Certificate information + * @oaram array|null $options Options for creating certificate + * @return resource + */ + public function genCertificate( $dn, $options = null ) + { + if( !$this->_keypair ) + throw new OSS_Crypt_Exception( 'To create certificate, you need to generate key pair first.' ); + + return openssl_csr_new( $dn, $this->_keypair, $options ); + } + + /** + * Generates self signed certificate and returns it. + * + * @param array $dn Certificate information + * @param int $days Days until certificate expires + * @param array|null $options Options for creating certificate + * @param int $serial Serial number by default is 0 + * @return resource of certificate. + */ + public function genSelfSignedCert( $dn, $days, $options = null, $serial = 0 ) + { + $this->_cert = openssl_csr_sign( $this->genCertificate( $dn, $options ), null, $this->_keypair, $days, $options, $serial ); + return $this->_cert; + } + + /** + * Generates self signed certificate and returns it. + * + * @param array $dn Certificate information + * @param int $days Days until certificate expires + * @param string $cacert Encrypted issuer certifcate + * @param string $cakey Encrypted issuer private key + * @param array|null $options Options for creating certificate + * @param int $serial Serial number by default is 0 + * @return resource of certificate. + */ + public function genSignedCert( $dn, $days, $cacert, $cakey, $options = null, $serial = 0 ) + { + $this->_cert = openssl_csr_sign( $this->genCertificate( $dn, $options ), $cacert, $cakey, $days, $options, $serial ); + return $this->_cert; + } + + /** + * Exports self signed certificate as string. + * + * Converts self signed certificate to string, and returns it. + * + * @return string + * @throws OSS_Crypt_Exception if $_cert is not generated. + */ + public function exportCert() + { + if( !$this->_cert ) + throw new OSS_Crypt_Exception( 'To export self signed certificate, you need to generate it first.' ); + + openssl_x509_export( $this->_cert, $out ); + return $out; + } + + /** + * Parses encrypted certificate + * + * @param string $cert Encrypted certificate + * @return array + */ + public static function parseCertificate( $cert ) + { + return openssl_x509_parse( $cert ); + } +} + diff --git a/library/OSS/Csv.php b/library/OSS/Csv.php new file mode 100644 index 0000000..aa2104a --- /dev/null +++ b/library/OSS/Csv.php @@ -0,0 +1,126 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Csv + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Csv +{ + /** + * Generated CSV data. + */ + private $_csv = null; + + + /** + * Constructor + * + * @param array $csvArray Array to parse as CSV. + */ + public function __construct( $csvArray = null ) + { + if( $csvArray ) + $this->_processCSV( $csvArray ); + } + + /** + * Parses given array to CSV. + * + * @param array $csvArray Array to parse as CSV. + * @return void + */ + private function _processCSV( $csvArray ) + { + $buffer = fopen( 'php://temp', 'w+' ); + + foreach( $csvArray as $row) + fputcsv( $buffer, $row ); + + rewind ($buffer ); + $csv = stream_get_contents( $buffer ); + fclose( $buffer ); + + $this->_csv = $csv; + } + + /** + * Parse given array and stores new data. + * + * @param array $csvArray Array to parse as CSV. + * @return void + */ + public function setCsvArray( $csvArray ) + { + $this->_processCSV( $csvArray ); + return $this; + } + + /** + * Returns CSV data. + * + * @returns string + */ + public function getContents() + { + return $this->_csv; + } + + /** + * Gets as file. + * Sets headers and prints out content. + * + * @param string $fileName Name of file. + * @returns string + */ + public function getAsFile( $fileName = false ) + { + $name = sprintf( "%s_%s.csv", $fileName ? $fileName : 'OSSFile', date( 'YmdHis' ) ); + + header( 'Content-type: application/csv' ); + header( "Content-Disposition: attachment; filename=" . $name ); + header( 'Cache-Control: no-cache, must-revalidate' ); // HTTP/1.1 + header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' ); + + echo $this->_csv; + } +} + + diff --git a/library/OSS/Curl.php b/library/OSS/Curl.php new file mode 100644 index 0000000..dbee733 --- /dev/null +++ b/library/OSS/Curl.php @@ -0,0 +1,381 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Countries + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Curl +{ + + const RFC_1738 = 'RFC 1738'; + const RFC_3986 = 'RFC 3986'; + + const HTTP_POST = 'POST'; + const HTTP_GET = 'GET'; + const HTTP_DELETE = 'DELETE'; + + + /** + * The cURL handler. We need a global variable to store the handle between cURL operations, so we can use persistent connections. + * + * @var resource + */ + private $_curlHandler = null; + + /** + * Curl method POST or GET + * + * @var string + */ + private $_method = null; + + /** + * URL for cURL + * + * @var string + */ + private $_url = null; + + /** + * cURl response + * + * @var array + */ + private $_response = array(); + + + /** + * Constructor + * + * @param int $connectTimeOut Connection time out, default is 3 seconds. + * @param int $requestTimeOut Request time out, default is 5 seconds, + * @param bool $returnTransfer + * @param string $userName User name if page url requires authorization + * @param string $password Password if page url requires authorization + * @return void + */ + public function __construct( $connectTimeOut = 3, $requestTimeOut = 5, $returnTransfer = true, $userName = '', $password = '' ) + { + $this->init( $connectTimeOut, $requestTimeOut, $returnTransfer, $userName, $password ); + } + + + /** + * Initialize cURL object + * + * @param int $connectTimeOut Connection time out, default is 3 seconds. + * @param int $requestTimeOut Request time out, default is 5 seconds, + * @param bool $returnTransfer + * @param string $userName User name if page url requires authorization + * @param string $password Password if page url requires authorization + * @return void + */ + public function init( $connectTimeOut = 3, $requestTimeOut = 5, $returnTransfer = true, $userName = '', $password = '' ) + { + $connectTimeOut = abs( (int) $connectTimeOut ); + $requestTimeOut = abs( (int) $requestTimeOut ); + $returnTransfer = (boolean) $returnTransfer; + $userName = (string) $userName; + $password = (string) $password; + + $this->_curlHandler = curl_init(); + + curl_setopt( $this->_curlHandler, CURLOPT_CONNECTTIMEOUT, $connectTimeOut ); // The number of seconds to wait while trying to connect. Use 0 to wait indefinitely. + curl_setopt( $this->_curlHandler, CURLOPT_TIMEOUT, $requestTimeOut ); // The maximum number of seconds to allow cURL functions to execute. + curl_setopt( $this->_curlHandler, CURLOPT_RETURNTRANSFER, $returnTransfer ); // if false then curl_exec() return value is simply true or false + + if( $userName != '' ) + $this->login( $userName, $password ); + } + + /** + * Closing curl object + * + * @return void + */ + public function close() + { + curl_close( $this->_curlHandler ); + } + + + /** + * Set login parameters to cURL. If url requires authorization + * + * @param string $userName User name if page url requires authorization + * @param string $password Password if page url requires authorization + * @return void + */ + public function login( $userName, $password) + { + curl_setopt( $this->_curlHandler, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ); + curl_setopt( $this->_curlHandler, CURLOPT_USERPWD, "{$userName}:{$password}" ); + } + + + /** + * Set additional options for curl + * + * @param string $option Option name + * @param mixed $value Parameter value + * @return void + */ + public function setOption( $option, $value) + { + curl_setopt( $this->_curlHandler, $option, $value ); + } + + + /** + * Represents an associative array ('key' => 'value', 'key' => 'value', ...) in an RFC 1738 or 3986 format. It does what http_build_query() does, but + * http_build_query() only supports the older RFC 1738 format. New versions of PHP will support RFC 3986 natively, probably as of version + * 5.3.6 and 5.2.11. Check the online PHP documentation and your PHP version. By default this method is also using http_build_query() in + * RFC 1738 mode. + * + * @param array $pArray + * @param string $pRfc default self::RFC_1738 either RFC_1738 or RFC_3986, if any other value is presented then it is set to RFC_1738 + * @return string + */ + public static function httpBuildQuery($pArray, $pMode=self::RFC_1738) + { + if (in_array($pMode, array( self::RFC_1738, self::RFC_3986)) == false) $pMode=self::RFC_1738; + + if ($pMode == self::RFC_1738) return http_build_query($pArray); + + if ( (is_array($pArray) == false) || (sizeof($pArray) == 0) ) return ''; + + $vRetVal = ''; + + // urlencode() is RFC 1738 (use that for form data!!), rawurlencode() is RFC 3986 + foreach($pArray as $vKey => $vValue) $vRetVal .= rawurlencode($vKey) . '=' . rawurlencode($vValue) . '&'; + + return mb_substr($vRetVal, 0, -1); // no need for the last '&' + } + + + /** + * Sets method + * + * @param string $method One of methods POST, GET, DELETE. + * @return void + */ + public function setMethod( $method ) + { + $method = strtoupper( trim( $method ) ); + + switch( $method ) + { + case self::HTTP_GET: + $this->_method = self::HTTP_GET; + curl_setopt( $this->_curlHandler, CURLOPT_CUSTOMREQUEST, self::HTTP_GET ); + break; + + case self::HTTP_POST: + $this->_method = self::HTTP_POST; + curl_setopt( $this->_curlHandler, CURLOPT_CUSTOMREQUEST, self::HTTP_POST ); + break; + + case self::HTTP_DELETE: + $this->_method = self::HTTP_DELETE; + curl_setopt( $this->_curlHandler, CURLOPT_CUSTOMREQUEST, self::HTTP_DELETE ); + break; + + default: + $this->_method = self::HTTP_POST; + curl_setopt( $this->_curlHandler, CURLOPT_CUSTOMREQUEST, self::HTTP_POST ); + break; + } + } + + + /** + * Gets method + * + * @return string + */ + public function getMethod() + { + return $this->_method; + } + + + /** + * Sets URL + * + * @param string $url Requests URL + * @return void + */ + public function setUrl( $url ) + { + $this->_url = trim( $url ); + curl_setopt( $this->_curlHandler, CURLOPT_URL, $url ); + + if( strpos( strtolower( $url ), 'https://' ) !== false ) + curl_setopt( $this->_curlHandler, CURLOPT_SSL_VERIFYPEER, 0 ); + } + + + /** + * Gets URL + * + * @return string + */ + public function getUrl() + { + return $this->_url; + } + + + /** + * Set data + * + * Please note that it overwrites (extends with data) the URL in GET requests. + * + * + * @param array $data + * @return void + */ + public function setData( $data ) + { + // convert $pData to string if it is array or object, that step takes care of urlencode()-ing, too + if( $data === null ) + $data = ''; // IMPORTANT! + + if( is_object( $data ) ) + $pData = OSS_Array::objectToArray( $data ); + + if( is_array( $data ) ) + $data = self::httpBuildQuery( $data ); + + //OSS_Debug::prr( $pData ); die(); + + if( $this->getMethod() == self::HTTP_POST ) + { + // we need that even if there is no data to make cURL to create the "Content-Type: application/x-www-form-urlencoded" header for us + if( is_string( $data ) ) + curl_setopt( $this->_curlHandler, CURLOPT_POSTFIELDS, $data ); + } + elseif( $this->getMethod() == self::HTTP_GET ) + { + if( is_string( $data ) ) + curl_setopt( $this->_curlHandler, CURLOPT_URL, $this->getUrl() . '?' . $data ); + } + } + + + /** + * Sends a request to the the specified URL and returns with the response. + * + * If $pData is not empty in a GET request, then it will be appended to the URL. In this case arrays are passed through http_build_query() first + * (so the values are urlencode()-ed), strings just simply appended to the URL. Append means the function will add the leading '?', too. + * + * @param string $pUrl + * @param string|array|object|null $pData arrays values and object properties are automatically urlencode()-ed, otherwise you have to do it yourself if you pass a string + * @param string $pMethod default self::HTTP_POST it can be HTTP_GET, HTTP_POST or HTTP_DELETE, any other value will be replaced by HTTP_POST; case insensitive + * @param int $pResponseCode the HTTP response code is placed in this parameter + * @return string|boolean a response string if $pReturnTransfer was true in init(), otherwise true on success or false on error + */ + public function execute( $pUrl, $pData, $pMethod=self::HTTP_POST, &$pResponseCode ) + { + $this->setUrl( $pUrl ); // we set this as default, setData() may overwrite it + $this->setMethod( $pMethod ); + + if( $pData ) + $this->setData( $pData ); + + // debugging + //$f = fopen('cabcall/var/tmp/abc.txt', 'wt'); + //curl_setopt($this->_curlHandler, CURLOPT_WRITEHEADER, $f); + //curl_setopt($this->_curlHandler, CURLOPT_HEADER, 1); + //curl_setopt($this->_curlHandler, CURLOPT_VERBOSE, 1); + + $vResult = curl_exec( $this->_curlHandler ); + $this->_response = curl_getinfo( $this->_curlHandler ); + + //var_dump( $this->_response ); + //var_dump( $vResult ); + + $pResponseCode = $this->_response['http_code']; + + return $vResult; + } + + /** + * Gets response + * + * @return array + */ + public function getResponse() + { + return $this->_response; + } + + /** + * Gets response + * + * @return string + */ + public function getErrorCode() + { + return curl_errno( $this->_curlHandler ); + } + + /** + * Gets error message + * + * @return string + */ + public function getErrorMsg() + { + return curl_error( $this->_curlHandler ); + } + + /** + * Gets error message + * + * @return string + */ + public function getError() + { + return $this->getErrorCode() . ' ' . $this->getErrorMsg(); + } + +} diff --git a/library/OSS/Date.php b/library/OSS/Date.php new file mode 100644 index 0000000..69ac713 --- /dev/null +++ b/library/OSS/Date.php @@ -0,0 +1,237 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Date + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Date +{ + const DF_EUROPEAN = 1; + const DF_AMERICAN = 2; + const DF_COMPUTER = 3; + const DF_REVERSE = 4; + const DF_COMPACT = 5; + + + /** + * Date formats + * + * @var array + */ + public static $DATE_FORMATS = array( + self::DF_EUROPEAN => 'DD/MM/YYYY', + self::DF_AMERICAN => 'MM/DD/YYYY', + self::DF_COMPUTER => 'YYYY-MM-DD', + self::DF_REVERSE => 'YYYY/MM/DD', + self::DF_COMPACT => 'YYYYMMDD' + ); + + + /** + * Date formats for data picker + * + * @var array + */ + public static $DATEPICKER_FORMATS = array( + self::DF_EUROPEAN => 'dd/mm/yy', + self::DF_AMERICAN => 'mm/dd/yy', + self::DF_COMPUTER => 'yy-mm-dd', + self::DF_REVERSE => 'yy/mm/dd', + self::DF_COMPACT => 'yymmdd' + ); + + /** + * Date formats for PHP + * + * @var array + */ + public static $PHP_FORMATS = array( + self::DF_EUROPEAN => 'd/m/Y', + self::DF_AMERICAN => 'm/d/Y', + self::DF_COMPUTER => 'Y-m-d', + self::DF_REVERSE => 'Y/m/d', + self::DF_COMPACT => 'Ymd' + ); + + + + /** + * Returns an associative array of date formats. + * + * I.e. returns self::$DATE_FORMATS + * + * @return array + */ + public static function getDateFormats() + { + return self::$DATE_FORMATS; + } + + /** + * Returns array of date format codes. + * + * ie. returns the keys of self::$DATE_FORMATS + * + * @return array + */ + public static function getDateFormatKeys() + { + return array_keys( self::$DATE_FORMATS ); + } + + /** + * Returns the format for a given format code or fallback to default. + * + * @param int $code The format code + * @param int $default The default value if $code does not exist + * @return string|bool + */ + public static function getFormat( $code, $default = self::DF_EUROPEAN ) + { + if( isset( self::$DATE_FORMATS[$code] ) ) + return self::$DATE_FORMATS[$code]; + else + return self::$DATE_FORMATS[$default]; + } + + /** + * Returns the JQuery date picker format for a given format code or fallback to default. + * + * @param int $code The format code + * @param int $default The default value if $code does not exist + * @return string|bool + */ + public static function getDatepickerFormat( $code, $default = self::DF_EUROPEAN ) + { + if( isset( self::$DATEPICKER_FORMATS[$code] ) ) + return self::$DATEPICKER_FORMATS[$code]; + else + return self::$DATEPICKER_FORMATS[$default]; + } + + /** + * Returns the PHP date() format for a given format code or fallback to default. + * + * @param int $code The format code + * @param int $default The default value if $code does not exist + * @return string|bool + */ + public static function getPhpFormat( $code, $default = self::DF_EUROPEAN ) + { + if( isset( self::$PHP_FORMATS[$code] ) ) + return self::$PHP_FORMATS[$code]; + else + return self::$PHP_FORMATS[$default]; + } + + /** + * Parse a string in a given format to a UNIX timestamp + * + * @param string $string + * @param int $format + * @return int + */ + public static function getTimestamp( $string, $format = self::DF_EUROPEAN ) + { + // strtotime will parse all our (current) formats except European and compact + if( $format == self::DF_EUROPEAN ) + $string = str_replace( '/', '.', $string ); + else if( $format == self::DF_COMPACT ) + $string = substr( $string, 0, 4 ) . '-' . substr( $string, 4, 2 ) . '-' . substr( $string, 6, 2 ); + + return strtotime( $string ); + } + + /** + * Parse a date in a given format to an array of (day, month, year) + * + * @param string $string + * @param int $format + * @return array + */ + public static function dateSplit( $string, $format = self::DF_EUROPEAN ) + { + $dparts = array(); + + // case values correspond to OSS_Date DF_* constants + switch( $format ) + { + case self::DF_AMERICAN: // mm/dd/yyyy + $t = explode( '/', $string ); + $dparts[0] = $t[1]; + $dparts[1] = $t[0]; + $dparts[2] = $t[2]; + break; + + case self::DF_COMPUTER: // YYYY-MM-DD + $t = explode( '-', $string ); + $dparts[0] = $t[2]; + $dparts[1] = $t[1]; + $dparts[2] = $t[0]; + break; + + case self::DF_REVERSE: // yyyy/mm/dd + $t = explode( '/', $string ); + $dparts[0] = $t[2]; + $dparts[1] = $t[1]; + $dparts[2] = $t[0]; + break; + + case self::DF_COMPACT: // yyyymmdd + $dparts[0] = substr( $string, 6, 2 ); + $dparts[1] = substr( $string, 4, 2 ); + $dparts[2] = substr( $string, 0, 4 ); + break; + + case self::DF_EUROPEAN: // dd/mm/yyyy + default: + $t = explode( '/', $string ); + $dparts[0] = $t[0]; + $dparts[1] = $t[1]; + $dparts[2] = $t[2]; + break; + } + + return $dparts; + } + + +} diff --git a/library/OSS/DateTime.php b/library/OSS/DateTime.php new file mode 100644 index 0000000..62403f8 --- /dev/null +++ b/library/OSS/DateTime.php @@ -0,0 +1,242 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Date + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_DateTime +{ + + /** + * Returns with the difference between two dates in days. + * + * @param string $date1 default null if null then it will take the current date + * @param string $date2 default null if null then it will take the current date + * @return int + */ + public static function dateDiffDays( $date1 = null, $date2= null ) + { + if( !$date1 ) + $date1 = date("Y-m-d H:i:s"); + + if( !$date2 ) + $date2 = date("Y-m-d H:i:s"); + + $timeStamp1 = strtotime( date("Y-m-d H:i:s", strtotime( $date1 ) ) ); + $timeStamp2 = strtotime( date("Y-m-d H:i:s", strtotime( $date2 ) ) ); + + if( $timeStamp1 > $timeStamp2 ) + list( $timeStamp1, $timeStamp2 ) = array( $timeStamp2, $timeStamp1 ); + + return ( int ) round( ( $timeStamp2 - $timeStamp1 ) / ( 60 * 60 * 24 ) ); + } + + + /** + * Returns with the difference between two dates in months. + * + * @param string $date1 default null if null then it will take the current date + * @param string $date2 default null if null then it will take the current date + * @return int + */ + public static function dateDiffMonths( $date1 = null, $date2 = null ) + { + if( !$date1 ) + $date1 = date("Y-m"); + + if( !$date2 ) + $date2 = date("Y-m"); + + $timeStamp1 = strtotime( date( "Y-m", strtotime( $date1 ) ) ); + $timeStamp2 = strtotime( date( "Y-m", strtotime( $date2 ) ) ); + + if( $timeStamp1 > $timeStamp2 ) list( $timeStamp1, $timeStamp2 ) = array( $timeStamp2, $timeStamp1 ); + + return (int) round( ( $timeStamp2 - $timeStamp1 ) / ( 60 * 60 * 24 * 30.438 ) ); + } + + + /** + * Returns with the difference between two dates in years. + * + * @param string $date1 default null if null then it will take the current date + * @param string $date2 default null if null then it will take the current date + * @return int + */ + public static function dateDiffYears( $date1 = null, $date2 = null) + { + if( !$date1 ) + $date1 = date("Y"); + + if( !$date2 ) + $date2 = date("Y"); + + $timeStamp1 = strtotime( date("Y", strtotime( $date1 ) ) ); + $timeStamp2 = strtotime( date("Y", strtotime( $date2 ) ) ); + + if( $timeStamp1 > $timeStamp2 ) + list( $timeStamp1, $timeStamp2 ) = array( $timeStamp2, $timeStamp1 ); + + return (int) round( ( $timeStamp2 - $timeStamp1 ) / ( 60 * 60 * 24 * 365.256 ) ); + } + + + /** + * Converts seconds to hours, minutes and seconds. Returns with an associative array having the keys 'hours', 'minutes' and 'seconds'. + * + * @param int $seconds + * @return array + */ + public static function secondsToHMS( $seconds ) + { + $seconds = (int) $seconds; + + $hours = (int) ( $seconds / 3600 ); + + $seconds = $seconds - ( $hours * 3600 ); + + $minutes = (int) ($seconds / 60); + + $seconds = $seconds - ( $minutes * 60 ); + + return array( + 'hours' => $hours, + 'minutes' => $minutes, + 'seconds' => $seconds + ); + } + + + /** + * Converts seconds to "A hours B minutes C seconds" string, skipping the unnecessary parts, + * like it won't return with "0 hours 0 minutes 34 seconds" but with "34 seconds". + * + * @param int $seconds + * @return string + */ + public static function secondsToTimeString( $seconds ) + { + $data = OSS_Utils::secondsToHMS( $seconds ); + + $retVal = ''; + + if( $data['hours'] > 0 ) + $retVal .= "{$data['hours']} hour" . ($data['hours'] != 1 ? 's' : '' ); + + if( $data['minutes'] > 0 ) + $retVal .= " {$data['minutes']} minute" . ( $data['minutes'] != 1 ? 's' : '' ); + + if( $data['seconds'] > 0 ) + $retVal .= " {$data['seconds']} second" . ( $data['seconds'] != 1 ? 's' : '' ); + + return trim( $retVal ); + } + + + /** + * Takes a date string and returns with it as "yyyy-mm-dd". The Input can be in "yyyy-mm-dd" or "dd/mm/yyyy" format, + * where the year can be two or four characters, and both the month and day can be one or two characters. It also takes + * and formats the time part. + * + * @param string $date + * @return string + */ + public static function ISOdate( $date ) + { + if( mb_strpos( $date, ' ' ) !== false ) + { + $time = trim( mb_substr( $date, mb_strpos( $date, ' ' ) ) ); + $date = trim( mb_substr( $date, 0, mb_strpos( $date, ' ' ) ) ); + } + else + { + $date = trim( mb_substr( $date, 0, 10 ) ); + $time = ''; + } + + if( mb_strpos( $date, '/' ) !== false ) + { + if( preg_match( "/\d{4}\/\d{1,2}\/\d{1,2}/u", $date ) != 0 ) + { + $date = preg_replace( "/(\d{4})\/(\d{1,2})\/(\d{1,2})/u", "$1-$2-$3", $date ); + } + else + { + $date = preg_replace( "/(\d{1,2})\/(\d{1,2})\/(\d{2,4})/u", "$3-$2-$1", $date ); + } + } + + if( $date != '' ) + { + $dateParts = explode( '-', $date ); + + if( mb_strlen( $dateParts[0] ) == 2 ) + $dateParts[0] = '20' . $dateParts[0]; + + if( mb_strlen( $dateParts[1] ) == 1 ) + $dateParts[1] = '0' . $dateParts[1]; + + if( mb_strlen( $dateParts[2] ) == 1 ) + $dateParts[2] = '0' . $dateParts[2]; + + $date = implode('-', $vDateParts); + } + + if( $time != '' ) + { + $timeParts = explode(':', $time); + + if( mb_strlen( $timeParts[0] ) == 1 ) + $timeParts[0] = '0' . $timeParts[0]; + + if( mb_strlen( $timeParts[1] ) == 1 ) + $timeParts[1] = '0' . $timeParts[1]; + + if( mb_strlen( $timeParts[2] ) == 1 ) + $timeParts[2] = '0' . $timeParts[2]; + + $time = implode(':', $timeParts); + } + + return trim( "{$date} {$time}" ); + } + +} diff --git a/library/OSS/Debug.php b/library/OSS/Debug.php new file mode 100644 index 0000000..af5bfa9 --- /dev/null +++ b/library/OSS/Debug.php @@ -0,0 +1,186 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Debug + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Debug +{ + + /** + * This function will 'var_dump and die' - it will (if HTML) surround the + * output with
 tags.
+    *
+    * The dump command is Zend_Debug::dump()
+    *
+    *
+    * @param object $object The variable / object to dump
+    * @param bool $html If true (default) surround the output with 
 tags
+    * @return void
+    */
+    public static function dd( $object, $html = true )
+    {
+        if( $html && php_sapi_name() != 'cli' ) echo '
';
+        Zend_Debug::dump( $object );
+        if( $html && php_sapi_name() != 'cli' ) echo '
'; + die(); + } + + /** + * This function will 'print_r() and die' - it will (if HTML) surround the + * output with
 tags.
+     *
+     * @param array $array The array to dump
+     * @param bool $html If true (default) surround the output with 
 tags
+     * @return void
+     */
+    public static function pd( $array, $html = true )
+    {
+        if( $html && php_sapi_name() != 'cli' ) echo '
';
+        print_r( $array );
+        if( $html && php_sapi_name() != 'cli' ) echo '
'; + die(); + } + + + /** + * A wrapper and extension for print_r(). The output looks the same in the browser as the output of print_r() in the source, as it turns the pure + * text output of print_r() into HTML (XHTML). + * + * @param mixed $data the data to be printed or returned + * @param mixed $var_name null if we don't want to display the variable name, otherwise the name of the variable + * @param boolean $return default false; if true it returns with the result, if true then prints it + * @param boolean $addPre default true adds the '
 ... 
' tags to the output, useful for HTML output + * @param boolean $addDollarSign default true adds a $ sign to the $var_name if it is set to true + * @return void|string + */ + public static function prr( $data, $var_name = null, $return = false, $addPre = true, $addDollarSign = true ) + { + $retVal = ( $addPre == true ? "\n
\n" : '' ) .
+            ( $var_name == '' ? '' :  ($addDollarSign == true ? "\$" : '') . "{$var_name} = " ) .
+            print_r( $data, true ) .
+            ( $addPre == true ? "\n
\n" : '' ); + + + if( !$return ) + print $retVal; + else + return $retVal; + } + + + /** + * Returns with a simplified, easier-to-read version of the result of debug_backtrace() as an associative array. + * + * @param void + * @return array + */ + public static function compact_debug_backtrace() + { + $res = debug_backtrace(); + $ret_val = array(); + + foreach( $res as $res_val ) + { + $xyz = array(); + if( isset( $res_val['file'] ) ) + $xyz['file'] = $res_val['file']; + + if( isset( $res_val['line'] ) ) + $xyz['line'] = $res_val['line']; + + if( isset( $res_val['function'] ) ) + $xyz['function'] = $res_val['function']; + + if( isset( $res_val['class'] ) ) + $xyz['class'] = $res_val['class']; + + if( isset( $res_val['object']->name ) ) + $xyz['object'] = $res_val['object']->name; + + $ret_val[] = $xyz; + } + + return $ret_val; + } + + + /** + * Returns with the inheritance tree of $pClassOrObject, which can be a class name or an object. + * It returns with a simple indexed array, where index 0 is the class of $pClassOrObject, and + * index N is the name of the class at the end of the whole inheritance tree. If $pClassOrObject + * is not a string or an object, then it returns with NULL. + * + * @param string|object $classOrObject a string class name or an object + * @return array|null + */ + public static function getInheritanceTree( $classOrObject ) + { + if( ( is_string( $classOrObject ) == false) && ( is_object( $pClassOrObject ) == false ) ) + return null; + + $classList = array(); + $classList[] = get_class( $classOrObject ); + $parentClass = get_parent_class( $classOrObject ); + + while( $parentClass ) + { + $classList[] = $parentClass; + $parentClass = get_parent_class( $parentClass ); + } + + return $classList; + } + + + /** + * Putting message to log file. + * + * @param string|object $messsage Debug message + * @return array|null + */ + public static function log( $message ) + { + $message = date( 'Y-m-d H:i:s') . ' : ' . $message . "\n"; + @file_put_contents( '../var/tmp/' . date('Y-m-d') . '.log', $message, FILE_APPEND | LOCK_EX); + } + +} diff --git a/library/OSS/DiskUtils.php b/library/OSS/DiskUtils.php new file mode 100644 index 0000000..7e2aff1 --- /dev/null +++ b/library/OSS/DiskUtils.php @@ -0,0 +1,103 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_DiskUtils + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_DiskUtils +{ + const DEFAULTS_PATH_DU = "/usr/bin/du"; + + /** + * Summarize disk usage of each FILE, recursively for directories. + * + * It is also configurable (via Zend_Application config and assuming 'options' is + * available in Zend_Registry as it would be from OSS_Controller_Action). + * + * You can configure the du application path by setting config: disk_utils.binary.du + * + * If folder not exists on given path function return false. If summarize is set to + * true than it function return folder size in bytes, else it returns array with with folder + * size in bytes and folder path. + * + * @param string $path Path to folder for size checking + * @param bool $summarize If it set to true then displays only a total for each argument. + * @param string $du Path to du application, if is not set or file on given path is not existent it uses /usr/bin/du as default. + * @return mixed + * @throw OSS_Exception Cannot find du path. + */ + public static function du( $path, $summarize = true, $du = self::DEFAULTS_PATH_DU ) + { + if( !is_dir( $path ) ) + return false; + + if( !file_exists( $du ) ) + throw new OSS_Exception( "OSS_DiskUtils::du: Cannot find '{$du}'" ); + + if( $summarize ) + { + $command = sprintf( "%s -sk %s", $du, $path ); + @exec( escapeshellcmd( $command ), $output, $result ); + if( $result === 0 ) + { + $output = explode( "\t", $output[0] ); + return (int) $output[0] * 1024; + } + } + else + { + $command = sprintf( "%s -k %s", $du, $path ); + @exec( escapeshellcmd( $command ), $output, $result ); + if( $result === 0 ) + { + foreach( $output as $key => $line ) + { + $row = explode( "\t", $output[$key] ); + $row[0] = (int) $row[0] * 1024; + $output[$key] = $row; + } + return $ouput; + } + } + return false; + } + +} diff --git a/library/OSS/Doctrine/Exception.php b/library/OSS/Doctrine/Exception.php new file mode 100644 index 0000000..70220e6 --- /dev/null +++ b/library/OSS/Doctrine/Exception.php @@ -0,0 +1,47 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine_Exception extends Doctrine_Exception +{ +} diff --git a/library/OSS/Doctrine/Record.php b/library/OSS/Doctrine/Record.php new file mode 100644 index 0000000..840dd6c --- /dev/null +++ b/library/OSS/Doctrine/Record.php @@ -0,0 +1,63 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine_Record extends Doctrine_Record +{ + /** + * Constructor + * + * Calls Doctrine_Record::__construct(). + * + * @param Doctrine_Table $table + * @param bool $isNewEntry + * @return void + */ + public function __construct( $table = null, $isNewEntry = false ) + { + parent::__construct( $table, $isNewEntry ); + } + +} + + diff --git a/library/OSS/Doctrine/Record/WithPreferences.php b/library/OSS/Doctrine/Record/WithPreferences.php new file mode 100644 index 0000000..5bb75d9 --- /dev/null +++ b/library/OSS/Doctrine/Record/WithPreferences.php @@ -0,0 +1,681 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine + * @subpackage Record + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine_Record_WithPreferences extends OSS_Doctrine_Record +{ + /** + * The name of the class. Set with get_class( $this ) + * @var string + */ + protected $_className; + + /** + * The name of the preference class for this class + * @var string + */ + protected $_preferenceClassName; + + + /** + * Should we cache the preferences in the session? + * @var bool + */ + private $_cache = true; + + /** + * The namespace for the cache. + * @var Zend_Session_Namespace + */ + private $_namespace = null; + + /** + * Constructor + * + * Calls the parent class constructor and sets the class name and preference class name. + * + * @paramDoctrine_Table $table + * @param bool $isNewEntry + * @return void + */ + public function __construct( $table = null, $isNewEntry = false ) + { + parent::__construct( $table, $isNewEntry ); + + $this->_className = get_class($this); + $this->_preferenceClassName = $this->_className . '_Preference'; + } + + + /** + * Set (or update) a preference + * + * @param string $attribute The preference name + * @param string $value The value to assign to the preference + * @param string $op default '=' The operand (e.g. = (default), <, <=, :=, =, += etc) + * @param int $expires default 0 The expiry as a UNIX timestamp. Default 0 which means never. + * @param int $index default 0 If an indexed preference, set a specific index number. Default 0. + * @return OSS_Doctrine_Record_WithPreferences + */ + public function setPreference( $attribute, $value, $operator = '=', $expires = 0, $index = 0 ) + { + if( $pref = $this->loadPreference( $attribute, $index ) ) + { + $pref['value'] = $value; + $pref['op'] = $operator; + $pref['expire'] = $expires; + $pref['ix'] = $index; + $pref->save(); + + return $this; + } + + $pref = new $this->_preferenceClassName(); + $pref[ $this->_className ] = $this; + $pref['attribute'] = $attribute; + $pref['op'] = $operator; + $pref['value'] = $value; + $pref['expire'] = $expires; + $pref['ix'] = $index; + $pref->save(); + + return $this; + } + + /** + * Stores an object in the users preference table using serialize() + * + * @see setPreference() + * + * @param string $attribute The preference name + * @param mixed $object The object to store + * @param string $op default '=' The operand (e.g. = (default), <, <=, :=, =, += etc) + * @param int $expires default 0 The expiry as a UNIX timestamp. Default 0 which means never. + * @param int $index default 0 If an indexed preference, set a specific index number. Default 0. + * @return OSS_Doctrine_Record_WithPreferences + */ + public function storeObject( $attribute, $object, $operator = '=', $expires = 0, $index = 0 ) + { + return $this->setPreference( $attribute, serialize( $object ), $operator, $expires, $index ); + } + + /** + * Loads an object in the users preference table using unserialize() + * + * @see getPreference() + * + * @param string $attribute The preference name + * @param int $index If an indexed preference, set a specific index number. Default 0. + * @param bool $includeExpired If true, include preferences even if they have expired. Default: false + * @return bool|string + */ + public function loadObject( $attribute, $index = 0, $includeExpired = false ) + { + $object = $this->getPreference( $attribute, $index, $includeExpired ); + + if( $object === false ) + return false; + + return unserialize( $object ); + } + + + /** + * Get the named preference + * + * WARNING: Evaluate the return of this function using !== or === as a preference such as '0' + * will evaluate as false otherwise. + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default 0 If an indexed preference, get a specific index (default: 0) + * @param bool $includeExpired default false If true, include preferences even if they have expired. Default: false + * @return bool|string + */ + public function getPreference( $attribute, $index = 0, $includeExpired = false ) + { + $query = Doctrine_Query::create() + ->select( 'p.value' ) + ->from( $this->_preferenceClassName . ' p' ) + ->where( 'p.' . $this->_className . '_id = ?', $this['id'] ) + ->andWhere( 'p.attribute = ?', $attribute ) + ->andWhere( 'p.ix = ?', $index ); + + if ( !$includeExpired ) + $query->andWhere( '( p.expire = 0 OR p.expire >= ? )', mktime() ); + + $pref = $query->execute( null, Doctrine_Core::HYDRATE_SINGLE_SCALAR ); + + if( $pref === array() ) + return false; + + return $pref; + } + + + /** + * Load the ORM object of the named preference + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default 0 If an indexed preference, get a specific index (default: 0) + * @param bool $includeExpired default false If true, include preferences even if they have expired. Default: false + * @return bool|Doctrine_Record + */ + public function loadPreference( $attribute, $index = 0, $includeExpired = false ) + { + $query = Doctrine_Query::create() + ->from( $this->_preferenceClassName . ' p' ) + ->where( 'p.' . $this->_className . '_id = ?', $this['id'] ) + ->andWhere( 'p.attribute = ?', $attribute ) + ->andWhere( 'p.ix = ?', $index ); + + if( !$includeExpired ) + $query->andWhere( '( p.expire = 0 OR p.expire >= ? )', mktime() ); + + return $query->fetchOne( null, Doctrine_Core::HYDRATE_RECORD ); + } + + + /** + * Delete the named preference + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default null If an indexed preference then delete a specific index, if null then delete all + * @return OSS_Doctrine_Record_WithPreferences + */ + public function deletePreference( $attribute, $index = null ) + { + $vQuery = Doctrine_Query::create() + ->from( $this->_preferenceClassName . ' p' ) + ->where( 'p.' . $this->_className . '_id = ?', $this['id'] ) + ->andWhere( 'p.attribute = ?', $attribute ); + + if ($index !== null) + $vQuery->andWhere( 'p.ix = ?', $index ); + + $vQuery + ->delete() + ->execute(); + + return $this; + } + + + /** + * Does the named preference exist or not? + * + * WARNING: Evaluate the return of this function using !== or === as a preference such as '0' + * will evaluate as false otherwise. + * + * @see getPreference() + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default 0 If an indexed preference, get a specific index (default: 0) + * @param bool $includeExpired default false If true, include preferences even if they have expired. Default: false + * @return bool|string + */ + public function hasPreference( $attribute, $index = 0, $includeExpired = false ) + { + return $this->getPreference( $attribute, $index, $includeExpired ); + } + + + /** + * Get a preference if it exists or set it and return if not + * + * A useful function to replace clauses such as: + * + * $pref = $user->getPreference( 'qwerty' ); + * if( $pref === false ) + * { + * $pref = 'default'; + * $user->setPreference( 'qwerty', 'default' ); + * } + * + * with: + * + * $pref = $user->getOrSetGetPreference( 'qwerty', 'default' ); + * + * @see getPreference() + * + * @param string $attribute The preference to get or get/set + * @param mixed $default The default value to set the preference to and return if not aleady set + * @param string $operator default '=' The operand for the preference. Defaults to '=' + * @param int $expires default 0 the expiry date as a UNIX timestamp, 0 means forever + * @param int $index default 0 the index + * @return mixed + */ + public function getOrSetGetPreference( $attribute, $default, $operator = '=', $expires = 0, $index = 0 ) + { + // is the preference already set? + $pref = $this->getPreference( $attribute, $index ); + + if( $pref !== false ) + return $pref; + + return $this->setPreference( $attribute, $default, $operator, $expires, $index ); + } + + /** + * Clean expired preferences + * + * Cleans preferences with an expiry date less than $asOf but not set to 0 (never expires). + * + * @param int $asOf default null The UNIX timestamp for the expriy, null means now + * @param string $attribute default null Limit it to the specified attributes, null means all attributes + * @return OSS_Doctrine_Record_WithPreferences An instance of this object for fluid interfaces + */ + public function cleanExpiredPreferences( $asOf = null, $attribute = null ) + { + if( $asOf === null ) + $asOf = mktime(); + + $query = Doctrine_Query::create() + ->from( $this->_preferenceClassName . ' p' ) + ->where( 'p.' . $this->_className . '_id = ?', $this['id'] ) + ->andWhere( 'p.expire > 0' ) + ->andWhere( 'p.expire < ?', $asOf ) + ->delete(); + + if( $attribute != null ) + $query->andWhere( 'p.attribute = ?', $attribute ); + + $query->execute(); + + return $this; + } + + + /** + * Add an indexed preference + * + * Let's say we need to add a list of email addresses as a preference where the following is + * the list: + * + * $emails = array( 'a@b.c', 'd@e.f', 'g@h.i' ); + * + * then we could add these as an indexed preference as follows for a given User $u: + * + * $u->addPreference( 'mailing_list.goalies.email', $emails ); + * + * which would result in database entries as follows: + * + * attribute index op value + * ------------------------------------------------------ + * | mailing_list.goalies.email | 0 | = | a@b.c | + * | mailing_list.goalies.email | 1 | = | d@e.f | + * | mailing_list.goalies.email | 2 | = | g@h.i | + * ------------------------------------------------------ + * + * we could then add a fourth address as follows: + * + * $u->addPreference( 'mailing_list.goalies.email', 'j@k.l' ); + * + * which would result in database entries as follows: + * + * attribute index op value + * ------------------------------------------------------ + * | mailing_list.goalies.email | 0 | = | a@b.c | + * | mailing_list.goalies.email | 1 | = | d@e.f | + * | mailing_list.goalies.email | 2 | = | g@h.i | + * | mailing_list.goalies.email | 3 | = | j@k.l | + * ------------------------------------------------------ + * + * + * ===== BEGIN NOT IMPLEMENTED ===== + * + * If out list was to be of names and emails, then we could create an array as follows: + * + * $emails = array( + * array( 'name' => 'John Smith', 'email' => 'a@b.c' ), + * array( 'name' => 'David Blue', 'email' => 'd@e.f' ) + * ); + * + * then we could add these as an indexed preference as follows for a given User $u: + * + * $u->addPreference( 'mailing_list.goalies', $emails ); + * + * which would result in database entries as follows: + * + * attribute index op value + * -------------------------------------------------------- + * | mailing_list.goalies!email | 0 | = | a@b.c | + * | mailing_list.goalies!name | 0 | = | John Smith | + * | mailing_list.goalies!email | 1 | = | d@e.f | + * | mailing_list.goalies!name | 1 | = | David Blue | + * -------------------------------------------------------- + * + * We can further be specific on operator for each one as follows: + * + * $emails = array( + * array( 'name' => array( value = 'John Smith', operator = ':=', expires = '123456789' ) ) + * ); + * + * Note that in the above form, value is required but if either or both of operator or expires is + * not set, it will be taken from the function parameters. + * + * ===== END NOT IMPLEMENTED ===== + * + * @param string $attribute The preference name + * @param string $value The value to assign to the preference + * @param string $operator default '=' The operand (e.g. = (default), <, <=, :=, =, += etc) + * @param int $expires default 0 The expiry as a UNIX timestamp. Default 0 which means never. + * @return OSS_Doctrine_Record_WithPreferences + */ + public function addIndexedPreference( $attribute, $value, $operator = '=', $expires = 0 ) + { + $conn = Doctrine_Manager::connection(); + + $conn->beginTransaction(); + + // what's the current highest index? + $index = Doctrine_Query::create() + ->from( $this->_preferenceClassName . ' p' ) + ->select( 'p.ix' ) + ->where( 'p.' . $this->_className . '_id = ?', $this['id'] ) + ->andWhere( 'p.attribute = ?', $attribute ) + ->orderBy( 'p.ix DESC' ) + ->limit( 1 ) + ->fetchArray(); + + $index = isset( $index[0]['ix'] ) ? ( (int) $index[0]['ix'] ) + 1 : 0; + + if( is_array( $value ) ) + { + foreach( $value as $v ) + { + $pref = new $this->_preferenceClassName(); + $pref[$this->_className] = $this; + $pref['attribute'] = $attribute; + $pref['op'] = $operator; + $pref['value'] = $v; + $pref['expire'] = $expires; + $pref['ix'] = $index; + + $pref->save(); + $index++; + } + } + else + { + $pref = new $this->_preferenceClassName(); + $pref[$this->_className] = $this; + $pref['attribute'] = $attribute; + $pref['op'] = $operator; + $pref['value'] = $value; + $pref['expire'] = $expires; + $pref['ix'] = $index; + + $pref->save(); + } + + $conn->commit(); + + return $this; + } + + /** + * Get indexed preferences as an array + * + * The standard response is an array of scalar values such as: + * + * array( 'a', 'b', 'c' ); + * + * If $withIndex is set to true, then it will be an array of associated arrays with the + * index included: + * + * array( + * array( 'p_index' => '0', 'p_value' => 'a' ), + * array( 'p_index' => '1', 'p_value' => 'b' ), + * array( 'p_index' => '2', 'p_value' => 'c' ) + * ); + * + * @param string $attribute The attribute to load + * @param bool $withIndex default false Include index values. Default false. + * @return bool|array + */ + public function getIndexedPreference( $attribute, $withIndex = false ) + { + $query = Doctrine_Query::create() + ->from( $this->_preferenceClassName . ' p' ) + ->where( 'p.' . $this->_className . '_id = ?', $this['id'] ) + ->andWhere( 'p.attribute = ?', $attribute ) + ->orderBy( 'p.ix ASC' ); + + if( $withIndex ) + { + $pref = $query + ->select( 'p.ix AS index, p.value AS value' ) + ->execute( null, Doctrine_Core::HYDRATE_SCALAR ); + } + else + { + $pref = $query + ->select( 'p.value' ) + ->execute( null, Doctrine_Core::HYDRATE_SINGLE_SCALAR ); + + if( !is_array( $pref ) ) + $pref = array( $pref ); + } + + if( $pref === array() ) + return false; + + return $pref; + } + + /** + * Should we use a session cache? + * + * Unless you have a good reason to specify a namespace, leave it as null. + * + * NB: Only preferences with op = '=' are cached. All others are not. + * + * @param bool $b Set to true to enable cache, false to disable + * @param string $namespace The name of the Zend Namespace to us. If null, uses default. + * @return void; + */ + public function setPreferenceCache( $b = true, $namespace = null ) + { + $this->_cache = $b; + + if( $namespace === null ) + { + $this->setPreferenceNamespace( new Zend_Session_Namespace( 'Pref_' . $this->_preferenceClassName ) ); + } + } + + /** + * Do we use a session cache? + * + * Returns true if cache enabled, false if disabled + * + * @return bool + */ + public function getPreferenceCache() + { + return $this->_cache; + } + + /** + * Get the Zend_Session_Namespace for caching + * + * @return Zend_Session_Namespace + */ + public function getPreferenceNamespace() + { + return $this->_namespace; + } + + /** + * Set the Zend_Session_Namespace to use for caching + * + * @param Zend_Session_Namespace $_namespace The Zend_Session_Namespace to use for caching + * @return void + */ + public function setPreferenceNamespace( $_namespace ) + { + $this->_namespace = $_namespace; + } + + + /** + * Get an array value from an associative array for a given array of indexes + * + * @see _setAssocElementViaArray() + * + * @param array $haystack The associative array in which to get the value + * @param array $needle An ordered array of keys in the associative array + * @throws OSS_Doctrine_Exception + * @return mixed The value requested + */ + public function _getAssocElementViaArray( &$haystack, $needle ) + { + if( !is_array( $haystack ) or !is_array( $needle ) ) + throw new OSS_Doctrine_Exception( 'Both $haystack and $needle must be arrays' ); + + if( count( $needle ) == 0 ) + return $haystack; + + $e = array_pop( $needle ); + $h = $haystack; + + foreach( $needle as $n ) + { + if( !isset( $h[$n] ) ) + throw new OSS_Doctrine_Exception( 'Invalid array key specified.' ); + + $h = $h[$n]; + } + + return $h[$e]; + } + + /** + * Set an array value in an associative array for a given array of indexes + * + * @param array $haystack The associative array in which to set the value + * @param array $needle An ordered array of keys in the associative array + * @param mixed $value The value to set + * @throws OSS_Doctrine_Exception + * @return void + */ + public function _setAssocElementViaArray( &$haystack, $needle, $value ) + { + if( !is_array( $haystack ) or !is_array( $needle ) ) + throw new OSS_Doctrine_Exception( 'Both $haystack and $needle must be arrays' ); + + $e = array_pop( $needle ); + $h = &$haystack; + + foreach( $needle as $n ) + $h = &$h[$n]; + + $h[$e] = $value; + } + + + /** + * Returns with all the preferences of $pAttribute attribute as a Doctrine_Collection object. + * + * @param string $pAttribute + * @return object + */ + public function getPreferences($pAttribute) + { + return Doctrine_Query::create() + ->select( '*' ) + ->from( $this->_preferenceClassName . ' p' ) + ->where( 'p.attribute = ?', $pAttribute ) + ->orderBy( 'p.ix asc, p.id asc' ) + ->execute(); + } + + /** + * Get a preferences count + * + * A useful function to replace clauses such as: + * + * $vResults = Doctrine_Query::create() + * ->select( '*' ) + * ->from( 'User_Preference up' ) + * ->where( 'up.User_id = ?', $this->_identity['user']->id ) + * ->andWhere( 'up.attribute = ?', 'tokens.mobile_confirm' ) + * ->andWhere( 'up.created_at >= ?', date( "Y-m-d H:i:s", time() - 3600 ) ) // last 1 hour + * ->fetchArray(); + * + * $cnt = sizeof( $vResults ); + * + * with: + * + * $cnt = $user->getCounttPreferences( 'tokens.mobile_confirm', date( "Y-m-d H:i:s", time() - 3600 ) ); + * + * @param string $attribute The preferences to count + * @param date $dateFrom The date from witch created + * @param date $dateFrom The date to witch created + * @return int + */ + public static function getCountPreferences( $attribute, $dateFrom = null, $dateTo = null) + { + $q = Doctrine_Query::create() + ->select( 'COUNT(up.id)' ) + ->from( 'User_Preference up' ) + ->where( 'p.' . $this->_className . '_id = ?', $this['id'] ) + ->andWhere( 'up.attribute = ?', 'tokens.mobile_confirm' ); + + if( $dateFrom != null && $dateFrom instanceof date ) + $q->andWhere( 'up.created_at >= ?', $dateFrom ); + + if( $dateFrom != null && $dateFrom instanceof date ) + $q->andWhere( 'up.created_at <= ?', $dateTo ); + + $res = $q->execute( null, Doctrine_Core::HYDRATE_SINGLE_SCALAR ); + + if( !$res ) + $res = 0; + + return $res; + } + +} diff --git a/library/OSS/Doctrine/Record/WithPreferencesTable.php b/library/OSS/Doctrine/Record/WithPreferencesTable.php new file mode 100644 index 0000000..ed0c73b --- /dev/null +++ b/library/OSS/Doctrine/Record/WithPreferencesTable.php @@ -0,0 +1,68 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine + * @subpackage Record + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine_Record_WithPreferencesTable extends OSS_Doctrine_Table +{ + /** + * Constructor + * + * Calls Doctrine_Table::__construct(). + * + * @param string $name + * @param Doctrine_Connection $conn + * @param bool $initDefinition + * @return void + */ + public function __construct( $name, Doctrine_Connection $conn, $initDefinition = false ) + { + parent::__construct( $name, $conn, $initDefinition ); + } + +} + + diff --git a/library/OSS/Doctrine/Table.php b/library/OSS/Doctrine/Table.php new file mode 100644 index 0000000..9a4502e --- /dev/null +++ b/library/OSS/Doctrine/Table.php @@ -0,0 +1,64 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine_Table extends Doctrine_Table +{ + /** + * Constructor + * + * Calls Doctrine_Table::__construct(). + * + * @param string $name + * @param Doctrine_Connection $conn + * @param bool $initDefinition + * @return void + */ + public function __construct( $name, Doctrine_Connection $conn, $initDefinition = false ) + { + parent::__construct( $name, $conn, $initDefinition ); + } + +} + + diff --git a/library/OSS/Doctrine2/DBAL/Connection.php b/library/OSS/Doctrine2/DBAL/Connection.php new file mode 100644 index 0000000..82fea1b --- /dev/null +++ b/library/OSS/Doctrine2/DBAL/Connection.php @@ -0,0 +1,95 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * A trait for classes that want to use DBAL connections + * + * @category OSS + * @package OSS_Doctrine2 + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Doctrine2_DBAL_Connection +{ + /** + * Doctrine2 DBAL Connections + * @var \Doctrine\DBAL\Connection[] + */ + private $_dbalConnections = []; + + /** + * DBAL uses named connections and we must try to avoid a clash with 'default' + * which is what the main application may already be using. + */ + private $_dbalAltDefaultName = null; + + /** + * Instantiates a new DBAL connection (or returns an existing one. + * + * @param array|Zend_Config $params The Doctrine2 DBAL params (@see http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html) + * @param string $name The name of the connection (used especially when managing multiple connections). Default: ''default'' + * @returns \Doctrine\DBAL\Connection + * @throws OSS_Doctrine2_Exception + */ + protected function getDBAL( $params = null, $name = null ) + { + // resolve name + if( $name === null ) + { + if( $this->_dbalAltDefaultName === null ) + $this->_dbalAltDefaultName = '__OSS_FW_' . OSS_String::random( 32, true, true, true, '', '' ); + + $name = $this->_dbalAltDefaultName; + } + + if( !isset( $this->_dbalConnections[ $name ] ) ) + { + if( $params === null ) + throw new OSS_Doctrine2_Exception( "No parameters for new DBAL connection" ); + + if( $params instanceof Zend_Config ) + $params = $params->toArray(); + + $config = new \Doctrine\DBAL\Configuration(); + $this->_dbalConnections[ $name ] = \Doctrine\DBAL\DriverManager::getConnection( $params, $config ); + } + + return $this->_dbalConnections[ $name ]; + } + +} diff --git a/library/OSS/Doctrine2/EntitySerializer.php b/library/OSS/Doctrine2/EntitySerializer.php new file mode 100644 index 0000000..9ffc72f --- /dev/null +++ b/library/OSS/Doctrine2/EntitySerializer.php @@ -0,0 +1,223 @@ + + */ + +use Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\Common\Util\Inflector, + Doctrine\ORM\EntityManager; + +/** + * A Doctrine2 Entity Serializer + * + * Based on: + * @link https://github.com/borisguery/bgylibrary/blob/master/library/Bgy/Doctrine/EntitySerializer.php + * which is licensed under http://sam.zoy.org/wtfpl/COPYING. This is turn was based + * on the Gist: + * @link https://gist.github.com/1034079#file_serializable_entity.php + * + * @category OSS + * @package OSS_Doctrine2 + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine2_EntitySerializer +{ + + /** + * @var Doctrine\ORM\EntityManager + */ + protected $_em; + + /** + * @var int + */ + protected $_recursionDepth = 0; + + /** + * @var int + */ + protected $_maxRecursionDepth = 0; + + public function __construct($em) + { + $this->setEntityManager($em); + } + + /** + * + * @return Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + public function setEntityManager( EntityManager $em ) + { + $this->_em = $em; + + return $this; + } + + protected function _serializeEntity( $entity ) + { + $className = get_class( $entity ); + $metadata = $this->_em->getClassMetadata( $className ); + + $data = array(); + + foreach( $metadata->fieldMappings as $field => $mapping ) + { + $value = $metadata->reflFields[$field]->getValue( $entity ); + $field = Inflector::tableize( $field ); + if( $value instanceof \DateTime ) + { + // We cast DateTime to array to keep consistency with array result + $data[$field] = (array)$value; + } + elseif( is_object( $value ) ) + { + $data[$field] = (string)$value; + } + else + { + $data[$field] = $value; + } + } + + foreach( $metadata->associationMappings as $field => $mapping ) + { + $key = Inflector::tableize( $field ); + if( $mapping['isCascadeDetach'] ) + { + $data[$key] = $metadata->reflFields[$field]->getValue( $entity ); + if( null !== $data[$key] ) + { + $data[$key] = $this->_serializeEntity($data[$key]); + } + } + elseif( $mapping['isOwningSide'] && $mapping['type'] & ClassMetadata::TO_ONE ) + { + if( null !== $metadata->reflFields[$field]->getValue( $entity ) ) + { + if( $this->_recursionDepth < $this->_maxRecursionDepth ) + { + $this->_recursionDepth++; + $data[$key] = $this->_serializeEntity( + $metadata->reflFields[$field] + ->getValue( $entity ) + ); + $this->_recursionDepth--; + } + else + { + $data[$key] = $this->getEntityManager() + ->getUnitOfWork() + ->getEntityIdentifier( + $metadata->reflFields[$field] + ->getValue( $entity ) + ); + } + } + else + { + // In some case the relationship may not exist, but we want + // to know about it + $data[$key] = null; + } + } + } + + return $data; + } + + /** + * Serialize an entity to an array + * + * @param The entity $entity + * @return array + */ + public function toArray( $entity ) + { + return $this->_serializeEntity( $entity ); + } + + + /** + * Convert an entity to a JSON object + * + * @param The entity $entity + * @return string + */ + public function toJson( $entity ) + { + return json_encode( $this->toArray( $entity ) ); + } + + /** + * Convert an entity to XML representation + * + * @param The entity $entity + * @throws OSS_Doctrine2_Exception + */ + public function toXml( $entity ) + { + throw new OSS_Doctrine2_Exception( 'Not yet implemented' ); + } + + /** + * Set the maximum recursion depth + * + * @param int $maxRecursionDepth + * @return void + */ + public function setMaxRecursionDepth( $maxRecursionDepth ) + { + $this->_maxRecursionDepth = $maxRecursionDepth; + } + + /** + * Get the maximum recursion depth + * + * @return int + */ + public function getMaxRecursionDepth() + { + return $this->_maxRecursionDepth; + } + +} diff --git a/library/OSS/Doctrine2/Exception.php b/library/OSS/Doctrine2/Exception.php new file mode 100644 index 0000000..eff0336 --- /dev/null +++ b/library/OSS/Doctrine2/Exception.php @@ -0,0 +1,47 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine2 + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine2_Exception extends OSS_Exception +{ +} diff --git a/library/OSS/Doctrine2/FirebugProfiler.php b/library/OSS/Doctrine2/FirebugProfiler.php new file mode 100644 index 0000000..7ca4041 --- /dev/null +++ b/library/OSS/Doctrine2/FirebugProfiler.php @@ -0,0 +1,139 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine2 + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine2_FirebugProfiler implements \Doctrine\DBAL\Logging\SQLLogger +{ + + /** + * Sum of query times + * + * @var float + */ + protected $_totalMS = 0; + + /** + * Total number of queries logged + * + * @var integer + */ + protected $_queryCount = 0; + + /** + * Table of queries and their times + * + * @var \Zend_Wildfire_Plugin_FirePhp_TableMessage + */ + protected $_message; + + /** + * Currentquery + * + * @var stdClass + */ + protected $_curQuery = null; + + /** + * Constructor + * + * @return void + */ + public function __construct() + { + $this->_message = new \Zend_Wildfire_Plugin_FirePhp_TableMessage( 'Doctrine Queries' ); + $this->_message->setBuffered( false ); + $this->_message->setHeader( array( 'Time', 'Event', 'Parameters' ) ); + $this->_message->setOption( 'includeLineNumbers', true ); + \Zend_Wildfire_Plugin_FirePhp::getInstance()->send( $this->_message, 'Doctrine Queries' ); + } + + /** + * Starts query + * + * @param string $sql The SQL statement that was executed + * @param array $params Arguments for SQL + * @param float $executionMS Time for query to return + * @return void + */ + public function startQuery($sql, array $params = null, array $types = null) + { + $this->_curQuery = new \stdClass(); + $this->_curQuery->sql = $sql; + $this->_curQuery->params = $params; + $this->_curQuery->types = $types; + $this->_curQuery->startTime = \microtime(true); + } + + /** + * Stops query + * + * @return void + */ + public function stopQuery() + { + $executionMS = \microtime(true) - $this->_curQuery->startTime; + $this->_totalMS += $executionMS; + ++$this->_queryCount; + $this->_message->addRow(array( + number_format($executionMS, 5), + $this->_curQuery->sql, + $this->_curQuery->params + )); + $this->updateLabel(); + } + + /** + * Sets the label for the FireBug entry + * + * @return void + */ + public function updateLabel() + { + $this->_message->setLabel( + sprintf('Doctrine Queries (%d @ %f sec)', + $this->_queryCount, + number_format($this->_totalMS, 5)) + ); + } +} + diff --git a/library/OSS/Doctrine2/Utils.php b/library/OSS/Doctrine2/Utils.php new file mode 100644 index 0000000..82caa03 --- /dev/null +++ b/library/OSS/Doctrine2/Utils.php @@ -0,0 +1,81 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Doctrine2 + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine2_Utils +{ + + /** + * Assign values to Doctrine2 entities from an array. + * + * The array indexes must match the setter function names less 'set'. + * + * E.g. with array( 'Name' => 'Joe' ) we'd call setName() + * + * @param Doctrine\ORM\Mapping $entity The entity to assign values to + * @param array $array The associative array to take values from + * @param bool $throw If no setter method exists in the entity for an array index, throw an exception + * @throws OSS_Doctrine2_Exception If $trow param is passed and is true + * @return Doctrine\ORM\Mapping The entity as passed for fluent interfaces + */ + static public function assignFromArray( $entity, $array, $throw = true ) + { + foreach( $array as $k => $v ) + { + $fn = 'set' . $k; + + if( !method_exists( $entity, $fn ) ) + { + if( $throw ) + throw new OSS_Doctrine2_Exception( "Property / setter $fn does not exist." ); + else + continue; + } + + $entity->$fn( $v ); + } + + return $entity; + } + +} diff --git a/library/OSS/Doctrine2/WithPreferences.php b/library/OSS/Doctrine2/WithPreferences.php new file mode 100644 index 0000000..b75786c --- /dev/null +++ b/library/OSS/Doctrine2/WithPreferences.php @@ -0,0 +1,660 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +use Doctrine\ORM\Mapping as ORM; + +/** + * Functions to add preference functionality to users / customers / companies / etc + * + * @category OSS + * @package OSS_Doctrine2 + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Doctrine2_WithPreferences +{ + + /** + * The name of the class. Set with get_class( $this ) + * @var string + */ + protected $_className; + + /** + * The name of the preference class for this class + * @var string + */ + protected $_preferenceClassName; + + + /** + * Should we cache the preferences in the session? + * @var boolean + */ + private $_cache = true; + + /** + * The namespace for the cache. + * @var Zend_Session_Namespace + */ + private $_namespace = null; + + + /** + * Return the entity object of the named preference + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default 0 If an indexed preference, get a specific index (default: 0) + * @param boolean $includeExpired default false If true, include preferences even if they have expired. Default: false + * @return WithPreference If the named preference is not defined, returns FALSE; otherwise it returns the Doctrine_Record + */ + public function loadPreference( $attribute, $index = 0, $includeExpired = false ) + { + foreach( $this->_getPreferences() as $pref ) + { + if( $pref->getAttribute() == $attribute && $pref->getIx() == $index ) + { + if( !$includeExpired ) + { + if( $pref->getExpire() == 0 || $pref->getExpire() > time() ) + return $pref; + else + return false; + } + else + { + return $pref; + } + } + } + + return false; + } + + /** + * Does the named preference exist or not? + * + * WARNING: Evaluate the return of this function using !== or === as a preference such as '0' + * will evaluate as false otherwise. + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default 0 If an indexed preference, get a specific index (default: 0) + * @param boolean $includeExpired default false If true, include preferences even if they have expired. Default: false + * @return boolean|string If the named preference is not defined or has expired, returns FALSE; otherwise it returns the preference + * @see getPreference() + */ + public function hasPreference( $attribute, $index = 0, $includeExpired = false ) + { + return $this->getPreference( $attribute, $index, $includeExpired ); + } + + /** + * Get the named preference + * + * WARNING: Evaluate the return of this function using !== or === as a preference such as '0' + * will evaluate as false otherwise. + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default 0 If an indexed preference, get a specific index (default: 0) + * @param boolean $includeExpired default false If true, include preferences even if they have expired. Default: false + * @return boolean|string If the named preference is not defined or has expired, returns FALSE; otherwise it returns the preference + */ + public function getPreference( $attribute, $index = 0, $includeExpired = false ) + { + foreach( $this->_getPreferences() as $pref ) + { + if( $pref->getAttribute() == $attribute && $pref->getIx() == $index ) + { + if( !$includeExpired && $pref->getExpire() != 0 && $pref->getExpire() < time() ) + return false; + + return $pref->getValue(); + } + } + + return false; + } + + /** + * Set (or update) a preference + * + * @param string $attribute The preference name + * @param string $value The value to assign to the preference + * @param string $op default '=' The operand (e.g. = (default), <, <=, :=, =, += etc) + * @param int $expires default 0 The expiry as a UNIX timestamp. Default 0 which means never. + * @param int $index default 0 If an indexed preference, set a specific index number. Default 0. + * @return OSS_Doctrine_Record_WithPreferences An instance of this object for fluid interfaces. + */ + public function setPreference( $attribute, $value, $operator = '=', $expires = 0, $index = 0 ) + { + $pref = $this->loadPreference( $attribute, $index ); + + if( $pref ) + { + $pref->setValue( $value ); + $pref->setOp( $operator ); + $pref->setExpire( $expires ); + $pref->setIx( $index ); + + return $this; + } + + $pref = $this->_createPreferenceEntity( $this ); + $pref->setAttribute( $attribute ); + $pref->setOp( $operator ); + $pref->setValue( $value ); + $pref->setExpire( $expires ); + $pref->setIx( $index ); + + $em = \Zend_Registry::get( 'd2em' )[ 'default' ]; + $em->persist( $pref ); + return $this; + } + + + + /** + * Add an indexed preference + * + * Let's say we need to add a list of email addresses as a preference where the following is + * the list: + * + * $emails = array( 'a@b.c', 'd@e.f', 'g@h.i' ); + * + * then we could add these as an indexed preference as follows for a given User $u: + * + * $u->addPreference( 'mailing_list.goalies.email', $emails ); + * + * which would result in database entries as follows: + * + * attribute index op value + * ------------------------------------------------------ + * | mailing_list.goalies.email | 0 | = | a@b.c | + * | mailing_list.goalies.email | 1 | = | d@e.f | + * | mailing_list.goalies.email | 2 | = | g@h.i | + * ------------------------------------------------------ + * + * we could then add a fourth address as follows: + * + * $u->addPreference( 'mailing_list.goalies.email', 'j@k.l' ); + * + * which would result in database entries as follows: + * + * attribute index op value + * ------------------------------------------------------ + * | mailing_list.goalies.email | 0 | = | a@b.c | + * | mailing_list.goalies.email | 1 | = | d@e.f | + * | mailing_list.goalies.email | 2 | = | g@h.i | + * | mailing_list.goalies.email | 3 | = | j@k.l | + * ------------------------------------------------------ + * + * + * ===== BEGIN NOT IMPLEMENTED ===== + * + * If out list was to be of names and emails, then we could create an array as follows: + * + * $emails = array( + * array( 'name' => 'John Smith', 'email' => 'a@b.c' ), + * array( 'name' => 'David Blue', 'email' => 'd@e.f' ) + * ); + * + * then we could add these as an indexed preference as follows for a given User $u: + * + * $u->addPreference( 'mailing_list.goalies', $emails ); + * + * which would result in database entries as follows: + * + * attribute index op value + * -------------------------------------------------------- + * | mailing_list.goalies!email | 0 | = | a@b.c | + * | mailing_list.goalies!name | 0 | = | John Smith | + * | mailing_list.goalies!email | 1 | = | d@e.f | + * | mailing_list.goalies!name | 1 | = | David Blue | + * -------------------------------------------------------- + * + * We can further be specific on operator for each one as follows: + * + * $emails = array( + * array( 'name' => array( value = 'John Smith', operator = ':=', expires = '123456789' ) ) + * ); + * + * Note that in the above form, value is required but if either or both of operator or expires is + * not set, it will be taken from the function parameters. + * + * ===== END NOT IMPLEMENTED ===== + * + * @param string $attribute The preference name + * @param string $value The value to assign to the preference + * @param string $operator default '=' The operand (e.g. = (default), <, <=, :=, =, += etc) + * @param int $expires default 0 The expiry as a UNIX timestamp. Default 0 which means never. + * @param int $max The maximum index allowed. Defaults to 0 meaning no limit. + * @return OSS_Doctrine_Record_WithPreferences An instance of this object for fluid interfaces. + * @throws OSS_Doctrine2_WithPreferences_IndexLimitException If $max is set and limit exceeded + */ + public function addIndexedPreference( $attribute, $value, $operator = '=', $expires = 0, $max = 0 ) + { + // what's the current highest index and how many is there? + $highest = -1; $count = 0; + + foreach( $this->getPreferences() as $pref ) + { + if( $pref->getAttribute() == $attribute && $pref->getOp() == $operator ) + { + ++$count; + if( $pref->getIx() > $highest ) + $highest = $pref->getIx(); + } + } + + if( $max != 0 && $count >= $max ) + throw new \OSS_Doctrine2_WithPreferences_IndexLimitException( 'Requested maximum number of indexed preferences reached' ); + + $em = \Zend_Registry::get( 'd2em' )[ 'default' ]; + if( is_array( $value ) ) + { + foreach( $value as $v ) + { + $pref = $this->_createPreferenceEntity( $this ); + $pref->setAttribute( $attribute ); + $pref->setOp( $operator ); + $pref->setValue( $v ); + $pref->setExpire( $expires ); + $pref->setIx( ++$highest ); + + $em->persist( $pref ); + } + } + else + { + $pref = $this->_createPreferenceEntity( $this ); + $pref->setAttribute( $attribute ); + $pref->setOp( $operator ); + $pref->setValue( $value ); + $pref->setExpire( $expires ); + $pref->setIx( ++$highest ); + + $em->persist( $pref ); + } + + return $this; + } + + + /** + * Clean expired preferences + * + * Cleans preferences with an expiry date less than $asOf but not set to 0 (never expires). + * + * WARNING: You need to EntityManager#flush() if the return >0! + * + * @param int $asOf default null The UNIX timestamp for the expriy, null means now + * @param string $attribute default null Limit it to the specified attributes, null means all attributes + * @return int The number of preferences deleted + */ + public function cleanExpiredPreferences( $asOf = null, $attribute = null ) + { + $count = 0; + + if( $asOf === null ) + $asOf = time(); + + $em = \Zend_Registry::get( 'd2em' )[ 'default' ]; + foreach( $this->_getPreferences() as $pref ) + { + if( $attribute !== null && $pref->getAttribute() != $attribute ) + continue; + + if( $pref->getExpire() != 0 && $pref->getExpire() < $asOf ) + { + $count++; + $this->getPreferences()->removeElement( $pref ); + $em->remove( $pref ); + } + } + + return $count; + } + + /** + * Delete the named preference + * + * WARNING: You need to EntityManager#flush() if the return >0! + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default null If an indexed preference then delete a specific index, if null then delete all + * @return int The number of preferences deleted + */ + public function deletePreference( $attribute, $index = null ) + { + $count = 0; + + $em = \Zend_Registry::get( 'd2em' )[ 'default' ]; + foreach( $this->_getPreferences() as $pref ) + { + if( $pref->getAttribute() == $attribute ) + { + if( $index === null || $pref->getIx() == $index ) + { + $count++; + $this->getPreferences()->removeElement( $pref ); + $em->remove( $pref ); + } + } + } + + return $count; + } + + + /** + * Delete all preferences for a user + * + * @return int The number of preferences deleted + */ + public function expungePreferences() + { + $em = \Zend_Registry::get( 'd2em' )[ 'default' ]; + + return $em->createQuery( "DELETE \\Entities\\UserPreference up WHERE up.User = ?1" ) + ->setParameter( 1, $this ) + ->execute(); + } + + + /** + * Get indexed preferences as an array + * + * The standard response is an array of scalar values such as: + * + * array( 'a', 'b', 'c' ); + * + * If $withIndex is set to true, then it will be an array of associated arrays with the + * index included: + * + * array( + * array( 'p_index' => '0', 'p_value' => 'a' ), + * array( 'p_index' => '1', 'p_value' => 'b' ), + * array( 'p_index' => '2', 'p_value' => 'c' ) + * ); + * + * @param string $attribute The attribute to load + * @param boolean $withIndex default false Include index values. Default false. + * @param boolean $ignoreExpired If set to false, include expired preferences + * @return boolean|array False if no such preference(s) exist, otherwise an array. + */ + public function getIndexedPreference( $attribute, $withIndex = false, $ignoreExpired = true ) + { + $values = array(); + + foreach( $this->getPreferences() as $pref ) + { + if( $pref->getAttribute() == $attribute ) + { + if( !$ignoreExpired && $pref->getExpire() != 0 && $pref->getExpire() < time() ) + continue; + + if( $withIndex ) + $values[ $pref->getIx() ] = array( 'p_index' => $pref->getIx(), 'p_value' => $pref->getValue() ); + else + $values[ $pref->getIx() ] = $pref->getValue(); + } + } + + if( $values === array() ) + return false; + + ksort( $values, SORT_NUMERIC ); + return $values; + } + + + /** + * Get associative preferences as an array. + * + * For example, if we have preferences: + * + * attribute email.address idx=0 value=1email + * attribute email.confirmed idx=0 value=false + * attribute email.tokens.0 idx=0 value=fwfddwde + * attribute email.tokens.1 idx=0 value=fwewec4r + * attribute email.address idx=1 value=2email + * attribute email.confirmed idx=1 value=true + * + * and if we search by `$attribute = 'email'` we will get: + * + * [ + * 0 => [ + * 'address' => '1email', + * 'confirmed' => false, + * 'tokens' => [ + * 0 => 'fwfddwde', + * 1 => 'fwewec4r' + * ] + * ], + + * 1 => [ + * 'address' => '2email', + * 'confirmed' => true + * ] + * ] + * + * + * @param string $attribute The attribute to load + * @param int $index If an indexed preference, get a specific index, null means all indexes alowed (default: null) + * @param boolean $ignoreExpired If set to false, include expired preferences + * @return boolean|array False if no such preference(s) exist, otherwise an array. + */ + public function getAssocPreference( $attribute, $index = null, $ignoreExpired = true ) + { + $values = array(); + + foreach( $this->_getPreferences() as $pref ) + { + if( strpos( $pref->getAttribute(), $attribute ) === 0 ) + { + if( $index == null || $pref->getIx() == $index ) + { + if( !$ignoreExpired && $pref->getExpire() != 0 && $pref->getExpire() < time() ) + continue; + + if( strpos( $pref->getAttribute(), "." ) !== false ) + $key = substr( $pref->getAttribute(), strlen( $attribute )+1 ); + + if( $key ) + { + $key = "{$pref->getIx()}.{$key}"; + $values = $this->_processKey( $values, $key, $pref->getValue() ); + } + else + $values[ $pref->getIx() ] = $pref->getValue(); + } + } + } + + if( $values === array() ) + return false; + + return $values; + } + + /** + * Delete the named preference + * + * WARNING: You need to EntityManager#flush() if the return >0! + * + * @param string $attribute The named attribute / preference to check for + * @param int $index default null If an indexed preference then delete a specific index, if null then delete all + * @return int The number of preferences deleted + */ + public function deleteAssocPreference( $attribute, $index = null ) + { + $cnt = 0; + + $em = \Zend_Registry::get( 'd2em' )[ 'default' ]; + foreach( $this->_getPreferences() as $pref ) + { + if( strpos( $pref->getAttribute(), $attribute ) === 0 ) + { + if( $index == null || $pref->getIx() == $index) + { + $this->getPreferences()->removeElement( $pref ); + $em->remove( $pref ); + $cnt++; + } + } + } + + return $cnt; + } + + + /** + * Gets full class name. e.g. \Entities\User + * It can be used when writing doctrine 2 queries. + * + * @return string + */ + private function _getFullClassname() + { + $this->_className = get_called_class(); + if( strpos( $this->_className, "__CG__" ) !== false ) + { + $this->_className = substr( $this->_className, strpos( $this->_className, "__CG__" ) + 6 ); + } + return $this->_className; + } + + /** + * Gets shorten class name. e.g. If full class name is\Entities\User + * then shorten will be User. + * + * It can be used when writing doctrine 2 queries. + * + * @return string + */ + private function _getShortClassname() + { + return substr( $this->_className, strrpos( $this->_className, '\\' ) + 1 ); + } + + /** + * Creates preference object. + * New preference object depends current class. e.g. If we extending + * \Entities\Customer functionality then our preference object will be + * \Entities\CustomerPreference. + * + * @return object + */ + private function _createPreferenceEntity( $owner = null ) + { + $prefClass = $this->_getFullClassname() . 'Preference'; + $pref = new $prefClass(); + + if( $owner != null ) + { + $setEntity = 'set' . $this->_getShortClassname(); + $pref->$setEntity( $owner ); + $owner->addPreference( $pref ); + } + + return $pref; + } + + /** + * This is similar to the `getPreference()` method but it creates + * and executes DQL instead of simply returning `$this->getPreferences()`. + * + * NOTICE: Function required due to `$this->getPreferences()` iteration failure. + * FIXME This should not be necessary + * + * @return \Doctrine\Common\Collections\ArrayCollection + */ + public function _getPreferences() + { + $query = sprintf( + "SELECT p FROM %sPreference p WHERE p.%s = %d", + $this->_getFullClassname(), $this->_getShortClassname(), $this->getId() + ); + + return \Zend_Registry::get( 'd2em' )['default']->createQuery( $query )->getResult(); + } + + /** + * Assign the key's value to the property list. Handles the + * nest separator for sub-properties. + * + * @param array $config + * @param string $key + * @param string $value + * @throws Zend_Config_Exception + * @return array + */ + private function _processKey($config, $key, $value) + { + if( strpos( $key, "." ) !== false) + { + $pieces = explode( ".", $key, 2 ); + if( strlen( $pieces[0] ) && strlen( $pieces[1] ) ) + { + if( !isset( $config[ $pieces[0] ] ) ) + { + if( $pieces[0] === '0' && !empty( $config ) ) + $config = array($pieces[0] => $config); + else + $config[ $pieces[0] ] = array(); + } + elseif( !is_array( $config[$pieces[0]] ) ) + { + //die("Cannot create sub-key for '{$pieces[0]}' as key already exists"); + } + $config[ $pieces[0] ] = $this->_processKey( $config[ $pieces[0] ], $pieces[1], $value ); + } + else + { + //die("Invalid key '$key'"); + } + } + else + { + $config[$key] = $value; + } + return $config; + } + +} diff --git a/library/OSS/Doctrine2/WithPreferences/IndexLimitException.php b/library/OSS/Doctrine2/WithPreferences/IndexLimitException.php new file mode 100644 index 0000000..b0f956c --- /dev/null +++ b/library/OSS/Doctrine2/WithPreferences/IndexLimitException.php @@ -0,0 +1,52 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Exception for adding more than the maximum allowed indexed preferences + * + * @category OSS + * @package OSS_Doctrine2 + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Doctrine2_WithPreferences_IndexLimitException extends \Exception +{ + +} + + diff --git a/library/OSS/Exception.php b/library/OSS/Exception.php new file mode 100644 index 0000000..f4ebba0 --- /dev/null +++ b/library/OSS/Exception.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Exception + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Exception extends Exception +{ + +} + + diff --git a/library/OSS/Filter/FileSize.php b/library/OSS/Filter/FileSize.php new file mode 100644 index 0000000..ce9375e --- /dev/null +++ b/library/OSS/Filter/FileSize.php @@ -0,0 +1,220 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_FileSize implements Zend_Filter_Interface +{ + const SIZE_BYTES = "B"; + const SIZE_KILOBYTES = "KB"; + const SIZE_MEGABYTES = "MB"; + const SIZE_GIGABYTES = "GB"; + + public static $SIZE_MULTIPLIERS = [ + self::SIZE_BYTES => 1.0, + self::SIZE_KILOBYTES => 1024.0, + self::SIZE_MEGABYTES => 1048576.0, + self::SIZE_GIGABYTES => 1073741824.0 + ]; + + public static $SIZE_PRECISION = [ + self::SIZE_BYTES => 1, + self::SIZE_KILOBYTES => 3, + self::SIZE_MEGABYTES => 6, + self::SIZE_GIGABYTES => 9 + ]; + + /** + * Corresponds to default multiplier + * + * @var string + */ + protected $_multiplier = self::SIZE_BYTES; + + /** + * Constructor + * + * Sets filter options + * Valid multiplier options: B, KB, MB, GB. Where case is not sensitive. + * + * @param string $multiplier Sets default multiplier it's not set in value. + * @return void + * @throws OSS_Exception If multiplier is not one of $SIZE_MULTIPLIERS KEY. + */ + public function __construct( $multiplier = null ) + { + if( $multiplier !== null ) + $this->setMultiplier( $multiplier ); + } + + + /** + * Set the multipler (the value by which integers are multiplied when, for + * example, a text field makes it clear to the user than units are MB rather + * than B). + * + * @param string $multiplier A key from self::$SIZE_MULTIPLIERS + * @throws OSS_Exception + * @return OSS_Filter_FileSize for fluent interfaces + */ + public function setMultiplier( $multiplier ) + { + if( array_key_exists( strtoupper( $multiplier ), self::$SIZE_MULTIPLIERS ) ) + $this->_multiplier = strtoupper( $multiplier ); + else + throw new OSS_Exception( "Trying to set unknown multiplier for FileSize filter." ); + + return $this; + } + + /** + * Get the multipler (the value by which integers are multiplied when, for + * example, a text field makes it clear to the user than units are MB rather + * than B). + * + * @return string A key from self::$SIZE_MULTIPLIERS + */ + public function getMultiplier() + { + return $this->_multiplier; + } + + /** + * Takes string input and returns size in bytes. + * + * 10KB, 10Kb, 10kb, 10k input will return 10240 value. + * 1000 KB, 1000K, 0.98MB input will return 1024000 + * 0.9MB, 0.9m, 0.9mb, 0.9 MB input will return 943718. + * 2B, 2b, 2 B input will return 2. + * 0.978GSM, 0.8S7M input will return false; + * 20 will look for parameter defaults.quota.multiplier in application.ini and use as subfix. + * else it will return 20. + * + * @param string $value String to parse size in bytes + * @return int|bool + */ + public function filter( $value ) + { + $debug = debug_backtrace(); + + foreach( $debug as $info ) + { + if( $info['function'] == "render" ) + { + if( is_numeric( $value ) ) + return self::unfilter( $value ); + else + return $value; + } + } + + $value = str_replace( " ", "", $value ); + + if( substr_count( $value, "." ) > 1 ) + return false; + + $numericValue = preg_replace( "/[^0123456789\.]/", '', (string) $value ); + + if( $numericValue == "" || $numericValue == 0 ) + return 0; + + $subfix = false; + if( strlen( $value ) == strlen( $numericValue ) ) + $subfix = $this->_multiplier; + else if( strlen( $value ) - strlen( $numericValue ) == 2 ) + $subfix = strtoupper( substr( $value, -2 ) ); + else if( strlen( $value ) - strlen( $numericValue ) == 1 ) + { + $subfix = strtoupper( substr( $value, -1 ) ); + if( $subfix != self::SIZE_BYTES ) + $subfix .= self::SIZE_BYTES; + } + else + return false; + + + if( isset( self::$SIZE_MULTIPLIERS[ $subfix ] ) ) + $value = $numericValue * self::$SIZE_MULTIPLIERS[ $subfix ]; + else + return false; + + return $value; + } + + /** + * Takes size input and returns formatted string. + * + * 10240 input will return 100.00KB value. + * 1024000 input will return 0.98MB value. + * 943718 input will 0.90MB return . + * 20 input will return 20B. + * + * @param int $value String to parse size in bytes + * @return string + */ + public static function unfilter( $value ) + { + if( !$value ) + return $value; + + if( $value / self::$SIZE_MULTIPLIERS[ self::SIZE_KILOBYTES ] < 0.1 ) + $value = (float)"{$value}" . self::SIZE_BYTES; + elseif( $value / self::$SIZE_MULTIPLIERS[ self::SIZE_KILOBYTES ] >= 0.1 && $value / self::$SIZE_MULTIPLIERS[ self::SIZE_KILOBYTES ] < 900 ) + { + $value = $value / self::$SIZE_MULTIPLIERS[ self::SIZE_KILOBYTES ]; + $value = (float)"{$value}" . self::SIZE_KILOBYTES; + } + elseif( $value / self::$SIZE_MULTIPLIERS[ self::SIZE_MEGABYTES ] >= 0.1 && $value / self::$SIZE_MULTIPLIERS[ self::SIZE_MEGABYTES ] < 900 ) + { + $value = $value / self::$SIZE_MULTIPLIERS[ self::SIZE_MEGABYTES ]; + $value = (float)"{$value}" . self::SIZE_MEGABYTES; + } + elseif( $value / self::$SIZE_MULTIPLIERS[ self::SIZE_GIGABYTES ] >= 0.1 && $value / self::$SIZE_MULTIPLIERS[ self::SIZE_GIGABYTES ] < 900 ) + { + $value = $value / self::$SIZE_MULTIPLIERS[ self::SIZE_GIGABYTES ]; + $value = (float)"{$value}" . self::SIZE_GIGABYTES; + } + + return $value; + } + +} diff --git a/library/OSS/Filter/FixAddressline.php b/library/OSS/Filter/FixAddressline.php new file mode 100644 index 0000000..5db2bc1 --- /dev/null +++ b/library/OSS/Filter/FixAddressline.php @@ -0,0 +1,65 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_FixAddressline implements Zend_Filter_Interface +{ + + /** + * Fix the formatting of an address line. + * + * @param string $value The value to filter + * @return string + */ + public function filter( $value ) + { + if( $value == '' ) + return $value; + + foreach( preg_split( "/[ \-\']/", mb_strtolower( $value ) ) as $vOneValue ) + $value = OSS_String::mb_str_replace( $vOneValue, OSS_String::mb_ucfirst( $vOneValue ), $value ); + + return $value; + } + +} diff --git a/library/OSS/Filter/FixCompanyname.php b/library/OSS/Filter/FixCompanyname.php new file mode 100644 index 0000000..a3c2727 --- /dev/null +++ b/library/OSS/Filter/FixCompanyname.php @@ -0,0 +1,66 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_FixCompanyname implements Zend_Filter_Interface +{ + + /** + * Fix the formatting of a company name. + * + * @param string $value The value to filter + * @return string + */ + public function filter( $value ) + { + if( $value == '' ) + return $value; + + foreach( preg_split( "/[ \-]/", mb_strtolower( $value ) ) as $vOneValue ) + if( $vOneValue ) + $value = OSS_String::mb_str_replace( $vOneValue, OSS_String::mb_ucfirst( $vOneValue ), $value ); + + return $value; + } + +} diff --git a/library/OSS/Filter/FixFirstname.php b/library/OSS/Filter/FixFirstname.php new file mode 100644 index 0000000..f75a519 --- /dev/null +++ b/library/OSS/Filter/FixFirstname.php @@ -0,0 +1,66 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_FixFirstname implements Zend_Filter_Interface +{ + + /** + * Fix the formatting of a person's forename. + * + * @param string $value The value to filter + * @return string + */ + public function filter( $value ) + { + if( $value == '' ) + return $value; + + $value = mb_strtolower( $value ); + foreach( preg_split( "/[ \-]/", $value ) as $vOneValue ) + $value = OSS_String::mb_str_replace( $vOneValue, OSS_String::mb_ucfirst( $vOneValue ), $value ); + + return $value; + } + +} diff --git a/library/OSS/Filter/FixLastname.php b/library/OSS/Filter/FixLastname.php new file mode 100644 index 0000000..b38624e --- /dev/null +++ b/library/OSS/Filter/FixLastname.php @@ -0,0 +1,71 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_FixLastname implements Zend_Filter_Interface +{ + + /** + * Fix the formatting of a person's lastname. + * + * @param string $value The value to filter + * @return string + */ + public function filter($value) + { + if( $value == '' ) + return $value; + + foreach( preg_split( "/[ \-]/", mb_strtolower( $value ) ) as $vOneName ) + $value = OSS_String::mb_str_replace( $vOneName, OSS_String::mb_ucfirst( $vOneName ), $value ); + + if( mb_strpos( mb_strtoupper( $value ), "O'" ) !== false ) + { + preg_match( "/O\'./", $value, $vMatches ); + $value = OSS_String::mb_str_replace( $vMatches[0], mb_strtoupper( $vMatches[0] ), $value ); + } + + return $value; + } + +} diff --git a/library/OSS/Filter/Float.php b/library/OSS/Filter/Float.php new file mode 100644 index 0000000..4b47798 --- /dev/null +++ b/library/OSS/Filter/Float.php @@ -0,0 +1,61 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_Float implements Zend_Filter_Interface +{ + + /** + * Extracts float form string + * + * NOTICE: Return value is string not float + * + * @param string $value The value to filter + * @return string + */ + public function filter($value) + { + return preg_replace( "/[^0123456789\.\-]/", '', (string) $value ); + } + +} diff --git a/library/OSS/Filter/HtmlEntitiesDecode.php b/library/OSS/Filter/HtmlEntitiesDecode.php new file mode 100644 index 0000000..dd0bd06 --- /dev/null +++ b/library/OSS/Filter/HtmlEntitiesDecode.php @@ -0,0 +1,183 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_HtmlEntitiesDecode implements Zend_Filter_Interface +{ + /** + * Corresponds to the second html_entity_decode() argument + * + * @var integer + */ + protected $_quoteStyle; + + /** + * Corresponds to the third html_entity_decode() argument + * + * @var string + */ + protected $_encoding; + + /** + * Constructor + * + * Sets filter options + * + * @param array $options + * @return void + */ + public function __construct( $options = array() ) + { + if( $options instanceof Zend_Config ) + { + $options = $options->toArray(); + } + else if( !is_array( $options ) ) + { + $options = func_get_args(); + $temp['quotestyle'] = array_shift( $options ); + if( !empty( $options ) ) + { + $temp['charset'] = array_shift( $options ); + } + + $options = $temp; + } + + if( !isset( $options['quotestyle'] ) ) + $options['quotestyle'] = ENT_COMPAT; + + if( !isset( $options['encoding'] ) ) + $options['encoding'] = 'UTF-8'; + + if( isset( $options[ 'charset'] ) ) + $options['encoding'] = $options['charset']; + + $this->setQuoteStyle($options['quotestyle']); + $this->setEncoding($options['encoding']); + } + + /** + * Returns the quoteStyle option + * + * @return int + */ + public function getQuoteStyle() + { + return $this->_quoteStyle; + } + + /** + * Sets the quoteStyle option + * + * @param integer $quoteStyle + * @return OSS_Filter_HtmlEntitiesDecode + */ + public function setQuoteStyle( $quoteStyle ) + { + $this->_quoteStyle = $quoteStyle; + return $this; + } + + + /** + * Get encoding + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Set encoding + * + * @param string $value + * @return OSS_Filter_HtmlEntitiesDecode + */ + public function setEncoding( $value ) + { + $this->_encoding = (string) $value; + return $this; + } + + /** + * Returns the charSet option + * + * Proxies to {@link getEncoding()} + * + * @return string + */ + public function getCharSet() + { + return $this->getEncoding(); + } + + /** + * Sets the charSet option + * + * Proxies to {@link setEncoding()} + * + * @param string $charSet + * @return OSS_Filter_HtmlEntitiesDecode + */ + public function setCharSet ($charSet ) + { + return $this->setEncoding( $charSet ); + } + + /** + * Defined by Zend_Filter_Interface + * + * Returns the string $value, converting HTML entities to their characters + * equivalents where they exist + * + * @param string $value + * @return string + */ + public function filter( $value ) + { + return html_entity_decode( (string) $value, $this->getQuoteStyle(), $this->getEncoding() ); + } +} diff --git a/library/OSS/Filter/IPv4.php b/library/OSS/Filter/IPv4.php new file mode 100644 index 0000000..dc04a65 --- /dev/null +++ b/library/OSS/Filter/IPv4.php @@ -0,0 +1,71 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @subpackage DNS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_IPv4 implements Zend_Filter_Interface +{ + /** + * Filtes IPv4 address. + * + * 192.168.002.016 filters to 192.168.2.16 + * 192.168.000.016 filters to 192.168.0.16 + * + * @param string $value String to parse size in bytes + * @return string + */ + public function filter( $value ) + { + $parts = explode( ".", $value ); + + foreach( $parts as $ix => $part ) + { + $part = ltrim( $part, "0" ); + $parts[$ix] = $part ? $part : "0"; + } + + return implode( ".", $parts ); + } + +} diff --git a/library/OSS/Filter/IPv6.php b/library/OSS/Filter/IPv6.php new file mode 100644 index 0000000..ff1a59c --- /dev/null +++ b/library/OSS/Filter/IPv6.php @@ -0,0 +1,97 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @subpackage DNS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_IPv6 implements Zend_Filter_Interface +{ + /** + * @var int $_type Type of the IPv6 address we want to get + * @see OSS_Net_IPv6 + */ + protected $_type = OSS_Net_IPv6::TYPE_SHORT; + + /** + * Set type + * + * @param int $type Set new type of the IPv6 address we want to get + * @see OSS_Net_IPv6 + */ + public function setType( $type ) + { + $this->_type = $type; + } + + /** + * Get type + * + * @return int + * @see OSS_Net_IPv6 + */ + public function getType() + { + return $this->_type; + } + + /** + * Filtes IPv6 address. + * + * 2001:07f8:0018:0002:0000:0000:0000:0147 filters to 2001:7f8:18:2::147 + * 2A01:7F8:18:0:0:0:0:0147 filters to 2a01:7f8:18::147 + * + * @param string $value String to parse size in bytes + * @return string + */ + public function filter( $value ) + { + try{ + $value = OSS_Net_IPv6::formatAddress( $value, $this->_type ); + } + catch( Exception $e ) + { + } + return $value; + } + +} diff --git a/library/OSS/Filter/StripLeadingZero.php b/library/OSS/Filter/StripLeadingZero.php new file mode 100644 index 0000000..63d914d --- /dev/null +++ b/library/OSS/Filter/StripLeadingZero.php @@ -0,0 +1,65 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_StripLeadingZero implements Zend_Filter_Interface +{ + + /** + * Strips leading zero + * + * @param string $value The value to filter + * @return string + */ + public function filter( $value ) + { + if( $value == '' ) + return $value; + + if( substr( $value, 0, 1 ) == "0" ) + $value = substr( $value, 1 ); + + return $value; + } + +} diff --git a/library/OSS/Filter/StripSlashes.php b/library/OSS/Filter/StripSlashes.php new file mode 100644 index 0000000..9b3fd8e --- /dev/null +++ b/library/OSS/Filter/StripSlashes.php @@ -0,0 +1,59 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Filter + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Filter_StripSlashes implements Zend_Filter_Interface +{ + + /** + * Filters strip slashes. + * + * @param string $value The value to filter + * @return string + */ + public function filter( $value ) + { + return stripslashes( (string) $value ); + } + +} diff --git a/library/OSS/Form.php b/library/OSS/Form.php new file mode 100644 index 0000000..0d1951d --- /dev/null +++ b/library/OSS/Form.php @@ -0,0 +1,76 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Form extends Zend_Form +{ + + // possible traits + // + use OSS_Form_Trait; // *** REQUIRED if using traits + // use OSS_Form_Trait_CancelLocation; + use OSS_Form_Trait_GenericElements; + // use OSS_Form_Trait_InsertElementFns; + // use OSS_Form_Trait_IsEdit; + // use OSS_Form_Trait_Doctrine1Mapping; + // use OSS_Form_Trait_Doctrine2Mapping; + + /** + * Constructor + * + * @param null|array $options An array of options + * @param bool $isEdit True if the form is for editing as opposed to adding + * @return void + */ + public function __construct( $options = null ) + { + $this->addElementPrefixPath( 'OSS_Filter', 'OSS/Filter/', 'filter' ); + $this->addElementPrefixPath( 'OSS_Validate', 'OSS/Validate/', 'validate' ); + + parent::__construct( $options ); + + if( method_exists( $this, 'initialiseTraits' ) ) + $this->initialiseTraits( $options ); + } + +} diff --git a/library/OSS/Form/Auth.php b/library/OSS/Form/Auth.php new file mode 100644 index 0000000..5430751 --- /dev/null +++ b/library/OSS/Form/Auth.php @@ -0,0 +1,233 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Untility methods to add common Auth/Login elements to an authentication form + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Form_Auth +{ + const USERNAME_TYPE_EMAIL = 0; + const USERNAME_TYPE_NONEMAIL = 1; + + + /** + * A utility function for creating a standard username element for login forms. + * + * You must specify the type (`USERNAME_TYPE_EMAIL` or `USERNAME_TYPE_NONEMAIL` and + * this will add additional validators, etc. + * + * @param string $type The type of username expected + * @param string $name The element name + * @return Zend_Form_Element_Text The username element + */ + public static function createUsernameElement( $type = self::USERNAME_TYPE_NONEMAIL, $name = 'username' ) + { + $un = new Zend_Form_Element_Text( $name ); + + $un->setRequired( true ) + ->addValidator( 'NotEmpty', true ) + ->addFilter( 'HtmlEntitiesDecode' ) + ->addFilter( 'StripSlashes' ) + ->addFilter( 'StripTags' ) + ->addFilter( 'StringToLower' ) + ->addFilter( 'StringTrim' ); + + switch( $type ) + { + case self::USERNAME_TYPE_EMAIL: + $un->addValidator( 'StringLength', false, array( 5, 255 ) ) + ->addValidator( 'EmailAddress', true, array( 'mx' => false ) ) + ->setAttrib( 'class', 'span5 required' ) + ->setLabel( _( 'Email' ) ) + ->setAttrib( 'title', _( 'Email' ) ); + break; + + case self::USERNAME_TYPE_NONEMAIL: + $un->setLabel( _( 'Username' ) ) + ->setAttrib( 'title', _( 'Username' ) ) + ->setAttrib( 'class', 'span3 required' ); + break; + + default: + die( 'Unknown username element type in OSS_Form_Auth_Login::getUsernameElement' ); + } + + $un->getValidator( 'NotEmpty' )->setMessage( _( 'You must enter your username' ), Zend_Validate_NotEmpty::IS_EMPTY ); + + return $un; + } + + + /** + * A utility function for creating a standard password element for login forms. + * + * @param string $name The element name + * @return Zend_Form_Element_Password The password element + */ + public static function createPasswordElement( $name = 'password' ) + { + + $pw = new Zend_Form_Element_Password( $name ); + + return $pw->setLabel( _( 'Password' ) ) + ->setAttrib( 'title', _( 'Password' ) ) + ->setAttrib( 'size', 30 ) + ->setAttrib( 'class', 'span3 required' ) + ->setRequired( true ) + ->addValidator( 'NotEmpty', true ) + ->addValidator( 'StringLength', true, array( 1, 1024 ) ) + ->addFilter( 'HtmlEntitiesDecode' ) + ->addFilter( 'StripSlashes' ) + ->addFilter( 'StringTrim' ); + } + + /** + * A utility function for creating a standard password confirmation element + * + * Also adds a validator to match it to the password field + * + * @param string $name The element name + * @param string $pwname The matching password element name + * @return Zend_Form_Element_Password The password element + */ + public static function createPasswordConfirmElement( $name = 'confirm_password', $pwname = 'password' ) + { + + $pwc = new Zend_Form_Element_Password( $name ); + + $pwc->setLabel( _( 'Confirm Password' ) ) + ->setAttrib( 'title', _( 'Confirm Password' ) ) + ->setAttrib( 'size', 30 ) + ->setAttrib( 'class', 'span3 required' ) + ->setRequired( true ) + ->addValidator( 'NotEmpty', true ) + ->addValidator( 'OSSIdenticalField', true, array( 'fieldName' => $pwname, 'fieldTitle' => _( 'the password' ) ) ) + ->addFilter( 'StripSlashes' ); + + $pwc->getValidator( 'NotEmpty' )->setMessage( _( 'The confirmation password is required and must match the password' ), Zend_Validate_NotEmpty::IS_EMPTY ); + + return $pwc; + } + + + /** + * A utility function for creating a standard password reset token element + * + * @param string $name The element name + * @return Zend_Form_Element_Text The password reset token element + */ + public static function createPasswordResetTokenElement( $name = 'token' ) + { + + $token = new Zend_Form_Element_Text( $name ); + + return $token->setLabel( _( 'Token' ) ) + ->setAttrib( 'title', _( 'Token' ) ) + ->setAttrib( 'size', 44 ) + ->setAttrib( 'class', 'span3 required' ) + ->setAttrib( 'maxlength', 40 ) + ->setRequired( true ) + ->addValidator( 'StringLength', true, array( 40, 40 ) ) + ->addFilter( 'StringTrim' ) + ->addFilter( 'StripSlashes' ); + } + + + /** + * A utility function for creating a standard 'remember me' element for login forms. + * + * @param string $name The element name + * @return Zend_Form_Element_Checkbox The remember me element + */ + public static function createRememberMeElement( $name = 'rememberme' ) + { + $rm = new Zend_Form_Element_Checkbox( $name ); + + return $rm->setLabel( _( 'Remember me on this computer' ) ) + ->setRequired( false ) + ->addFilter( 'Int' ); + } + + + /** + * A utility function for creating a standard 'lost password' button link. + * + * @param string $name The element name + * @return OSS_Form_Element_Buttonlink - The button link element + */ + public static function createLostPasswordElement( $name = 'lost_password' ) + { + $fpw = new OSS_Form_Element_Buttonlink( $name ); + return $fpw->setAttrib( 'href', OSS_Utils::genUrl( 'auth', 'lost-password' ) ) + ->setAttrib( 'label', _( 'Lost Password' ) ); + } + + /** + * A utility function for creating a standard 'lost username' button link. + * + * @param string $name The element name + * @return OSS_Form_Element_Buttonlink - The button link element + */ + public static function createLostUsernameElement( $name = 'lost_username' ) + { + $fpw = new OSS_Form_Element_Buttonlink( $name ); + return $fpw->setAttrib( 'href', OSS_Utils::genUrl( 'auth', 'lost-username' ) ) + ->setAttrib( 'label', _( 'Lost Username' ) ); + } + + /** + * A utility function for creating a standard 'return to login' button link. + * + * @param string $name The element name + * @return OSS_Form_Element_Buttonlink - The button link element + */ + public static function createReturnToLoginElement( $name = 'return_to_login' ) + { + $fpw = new OSS_Form_Element_Buttonlink( $name ); + return $fpw->setAttrib( 'href', OSS_Utils::genUrl( 'auth', 'login' ) ) + ->setAttrib( 'label', _( 'Return to Login' ) ); + } +} diff --git a/library/OSS/Form/Captcha.php b/library/OSS/Form/Captcha.php new file mode 100644 index 0000000..5c9315f --- /dev/null +++ b/library/OSS/Form/Captcha.php @@ -0,0 +1,113 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Untility methods to add common Auth/Login elements to an authentication form + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Form_Captcha +{ + /** + * A utility function for creating a standard CAPTCHA id element + * + * @param string $name The element name + * @return Zend_Form_Element_Hidden The CAPTCHA id element + */ + public static function createIdElement( $name = 'captchaid' ) + { + $captchaid = new Zend_Form_Element_Hidden( $name ); + return $captchaid ->setValue( '' ) + ->setRequired( false ); + } + + /** + * A utility function for creating a standard request new CAPTCHA image element + * + * @param string $name The element name + * @return Zend_Form_Element_Hidden The CAPTCHA id element + */ + public static function createRequestNewImageElement( $name = 'requestnewimage' ) + { + $requestNewImg = new Zend_Form_Element_Hidden( $name ); + return $requestNewImg->setValue( '0' ) + ->setRequired( false ); + } + + /** + * A utility function for creating a standard CAPTCHA input element + * + * @param string $name The element name + * @return Zend_Form_Element_Text The CAPTCHA input element + */ + public static function createInputElement( $name = 'captchatext' ) + { + $captchaText = new Zend_Form_Element_Text( $name ); + + return $captchaText->setLabel( 'Copy the text from the image above' ) + ->setAttrib( 'title', 'CAPTCHA' ) + ->setAttrib( 'size', 32) + ->setAttrib( 'maxlength', 10 ) + ->setRequired( true ) + ->addValidator( 'StringLength', true, array( 6, 10 ) ) + ->addFilter( 'StringTrim' ) + ->addFilter( 'StripSlashes' ); + } + + /** + * Add the required CAPTCHA elements to a form + * + * @param OSS_Form $form The form to add the elements to + * @return OSS_Form The same form for fluent interfaces + */ + public static function addCaptchaElements( $form ) + { + $form->addElement( self::createIdElement() ); + $form->addElement( self::createRequestNewImageElement() ); + $form->addElement( self::createInputElement() ); + return $form; + } + + +} diff --git a/library/OSS/Form/Element/Buttonlink.php b/library/OSS/Form/Element/Buttonlink.php new file mode 100644 index 0000000..deaeb4f --- /dev/null +++ b/library/OSS/Form/Element/Buttonlink.php @@ -0,0 +1,76 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Form element button-link + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @subpackage OSS_Form_Element + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Form_Element_Buttonlink extends Zend_Form_Element_Xhtml +{ + /** + * Default view helper to use + * @var string + */ + public $helper = 'buttonlink'; + + /** + * Default decorators + * + * Uses only 'Submit' and 'DtDdWrapper' decorators by default. + * + * @return Zend_Form_Element_Submit + */ + public function loadDefaultDecorators() + { + $decorators = $this->getDecorators(); + if( empty( $decorators ) ) + { + $this->clearDecorators(); + } + return $this; + } + +} diff --git a/library/OSS/Form/Element/DatabaseDropdown.php b/library/OSS/Form/Element/DatabaseDropdown.php new file mode 100644 index 0000000..63d001e --- /dev/null +++ b/library/OSS/Form/Element/DatabaseDropdown.php @@ -0,0 +1,170 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Form element text field with database dropdown + * + * NOTICE: It requires chosen library + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @subpackage OSS_Form_Element + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Form_Element_DatabaseDropdown extends Zend_Form_Element_Xhtml +{ + + public $helper = 'databaseDropdown'; + + private $_chznOptions = false; + + /** + * Constructor + * + * $spec may be: + * - string: name of element + * - array: options with which to configure element + * - Zend_Config: Zend_Config with options for configuring element + * + * + * Chosen options can be set in array two ways first by DQL second simple array. + * + * To set options by DQL $options array structure should be like: + * [ + * 'dql' => "select u.username from \\Entities\\User where u.username IS NOT NULL", //mandatory + * 'db' => "default" //Optional if db is not default. Some project may have more then one. + * ] + * + * To set options by array $options array structure should be like: + * [ + * 'options' => [ 'option1' => 'option1', 'option2' => 'option2', 'option3' => 'option3' ], //mandatory + * ] + * + * + * @param string|array|Zend_Config $spec + * @return void + * + * @see setChosenOptions() + * @see setChosenOptionsByDql() + */ + public function __construct( $spec, $options = null ) + { + + if( isset( $options['options'] ) ) + { + $this->setChosenOptions( $options['options'] ); + unset( $options['options'] ); + } + else if( isset( $options['dql'] ) ) + { + if( isset( $options['db'] ) ) + { + $this->setChosenOptionsByDql( $options['dql'], $options['db'] ); + unset( $options['db'] ); + } + else + $this->setChosenOptionsByDql( $options['dql'] ); + + unset( $options['dql'] ); + } + parent::__construct( $spec, $options ); + + } + + /** + * Sets chosen list options from DQL + * + * Queries database with given DQL query then makes a key value array + * where key equals to value. And then calls setChosesOptions. + * + * NOTE: DQL query must request data from only one field. e.g.: + * select u.username from \Entities\User u WHERE u.username IS NOT NULL + * + * @param string $dql DQL query to get chosen options. + * @param string $db Database name if not default, some project may have more then one. + * @return OSS_Form_Element_DatabaseDropdown + * + * @see setChosenOptions() + */ + public function setChosenOptionsByDql( $dql, $db = 'default' ) + { + $em = Zend_Registry::get( "d2em" ); + $query = $em[ $db ]->createQuery( $dql ); + $result = $query->getScalarResult(); + if( is_array( $result ) && count( $result ) > 0 ) + { + $data = array_map( 'current', $result ); + $data = array_combine( $data, $data ); + $this->setChosenOptions( $data ); + } + + return $this; + } + + /** + * Sets chosen options array + * + * @param array $options Key value pair options for chosen. + * @return OSS_Form_Element_DatabaseDropdown + */ + public function setChosenOptions( $options ) + { + $options = [ "" => "" ] + $options; + + $this->_chznOptions = $options; + if( is_array( $options ) && count( $options ) > 1 ) + $this->setAttrib( 'data-osschzn-options', json_encode( $options ) ); + + return $this; + } + + /** + * Gets chosen options array + * + * @return array + */ + public function getChosenOptions() + { + return $this->_chznOptions; + } +} diff --git a/library/OSS/Form/Element/DateForm.php b/library/OSS/Form/Element/DateForm.php new file mode 100644 index 0000000..4967e7c --- /dev/null +++ b/library/OSS/Form/Element/DateForm.php @@ -0,0 +1,62 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Form element Date form + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @subpackage OSS_Form_Element + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Form_Element_DateForm extends Zend_Form_Element_Xhtml +{ + + public $helper = 'dateForm'; + + + public function init() + { + } + +} diff --git a/library/OSS/Form/Trait.php b/library/OSS/Form/Trait.php new file mode 100644 index 0000000..7f960cc --- /dev/null +++ b/library/OSS/Form/Trait.php @@ -0,0 +1,74 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Functionality for form traits + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait +{ + /** + * All declared traits can have their own initialisation method. This function + * iterates over the declared traits and initialises them if necessary. + * + * NB - order of initialisation is order of declaration + * + * This function should be called from the contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param null|array $options An array of options + */ + private function initialiseTraits( $options ) + { + foreach( get_declared_traits() as $trait ) + { + $fn = "{$trait}_Init"; + if( method_exists( $this, $fn ) ) + $this->$fn( $options ); + } + } + +} diff --git a/library/OSS/Form/Trait/CancelLocation.php b/library/OSS/Form/Trait/CancelLocation.php new file mode 100644 index 0000000..af60009 --- /dev/null +++ b/library/OSS/Form/Trait/CancelLocation.php @@ -0,0 +1,105 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Functionality for setting, getting and creating 'Cancel' buttons + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait_CancelLocation +{ + /** + * Where to go it the add / edit is cancelled. + * @var string Where to go it the add / edit is cancelled. + */ + public $cancelLocation = ''; + + /** + * The trait's initialisation method. + * + * This function is called from the form's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param null|array $options An array of options + * @param bool $isEdit True if the form is for editing as opposed to adding + */ + public function OSS_Form_Trait_CancelLocation_Init( $options ) + { + if( is_array( $options ) && isset( $options['cancelLocation'] ) ) + $this->cancelLocation = $options['cancelLocation']; + } + + /** + * A utility function for creating a standard cancel button for forms. + * + * @param string $name The element name + * @param string $cancelLocation The cancel location URL + * @return Zend_Form_Element_Submit The cancel element + */ + public function createCancelElement( $name = 'cancel', $cancelLocation = null ) + { + if( $cancelLocation === null ) + $cancelLocation = $this->cancelLocation; + + $cancel = new OSS_Form_Element_Buttonlink( $name ); + + return $cancel->setAttrib( 'href', $cancelLocation ) + ->setAttrib( 'label', _( 'Cancel' ) ); + } + + /** + * Set / change the cancel location + * + * @param string $cancelLocation The cancel location URL + * @param string $name The element name + * @return Zend_Form The form object for fluent interfaces + */ + public function updateCancelLocation( $cancelLocation, $name = 'cancel' ) + { + $this->getElement( $name )->setAttrib( 'href', $cancelLocation ); + $this->cancelLocation = $cancelLocation; + return $this; + } +} diff --git a/library/OSS/Form/Trait/Doctrine1Mapping.php b/library/OSS/Form/Trait/Doctrine1Mapping.php new file mode 100644 index 0000000..83838c7 --- /dev/null +++ b/library/OSS/Form/Trait/Doctrine1Mapping.php @@ -0,0 +1,97 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Form - map entries between form and ORM + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait_Doctrine1Mapping +{ + + /** + * Assigns form to model ( Doctrine 1 ). + * + * @param object $model + * @param object $controller + * @param bool $isEdit + * @return object + */ + public function assignFormToModel( $model, $controller, $isEdit ) + { + $columns = Doctrine::getTable( $controller->getModelName() )->getFieldNames(); + + foreach( $this->getElements() as $elementName => $elementConfig ) + { + if( in_array( $elementName, $columns ) ) + $model->$elementName = $this->getValue( $elementName ); + } + + return $model; + } + + + /** + * Assigns model to form ( Doctrine 1 ). + * + * @param object $model + * @param object $controller + * @return OSS_Form + */ + public function assignModelToForm( $model, $controller ) + { + $columns = Doctrine::getTable( $controller->getModelName() )->getFieldNames(); + + foreach( $this->getElements() as $elementName => $elementConfig ) + { + if( in_array( $elementName, $columns ) ) + $this->getElement( $elementName )->setValue( $model->$elementName ); + } + + return $this; + } + + +} diff --git a/library/OSS/Form/Trait/Doctrine2.php b/library/OSS/Form/Trait/Doctrine2.php new file mode 100644 index 0000000..b22f6a0 --- /dev/null +++ b/library/OSS/Form/Trait/Doctrine2.php @@ -0,0 +1,212 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Functionality for creating / editing elements and other functionality using a Doctrine2 backend + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait_Doctrine2 +{ + /** + * Assigns form to entity ( Doctrine 2 ). + * + * @param object $entity The Doctrine2 Entity + * @param OSS_Controller_Action $controller An instance of OSS_Controller_Action + * @param bool $isEdit + * @return object The Doctrine2 Entity + */ + public function assignFormToEntity( $entity, $controller, $isEdit ) + { + $fields = $controller->getD2EM()->getClassMetadata( get_class( $entity ) )->getFieldNames(); + + foreach( $this->getElements() as $eName => $eConfig ) + { + if( in_array( $eName, $fields ) && !$eConfig->getAttrib( 'readonly' ) ) + { + $fn = 'set' . Doctrine\Common\Util\Inflector::classify( $eName ); + $entity->$fn( $this->getValue( $eName ) ); + } + } + + return $entity; + } + + + /** + * Assigns entity to form ( Doctrine 2 ). + * + * @param object $entity The Doctrine2 Entity + * @param OSS_Controller_Action $controller An instance of OSS_Controller_Action + * @param bool $isEdit + * @return OSS_Form The form object + */ + public function assignEntityToForm( $entity, $controller, $isEdit = true ) + { + $fields = $controller->getD2EM()->getClassMetadata( get_class( $entity ) )->getFieldNames(); + + foreach( $this->getElements() as $eName => $eConfig ) + { + if( in_array( $eName, $fields ) ) + { + $fn = 'get' . Doctrine\Common\Util\Inflector::classify( $eName ); + + if( $entity->$fn() instanceof DateTime ) + $this->getElement( $eName )->setValue( $entity->$fn()->format( 'Y-m-d H:i:s' ) ); + else + $this->getElement( $eName )->setValue( $entity->$fn() ); + } + } + + return $this; + } + + + /** + * Populate a Zend_Form SELECT element from a database table + * + * This function essential crafts a basic query and then calls `populateSelectFromDatabaseQuery()` + * + * @see populateSelectFromDatabaseQuery() + * @param Zend_Form_Element_Select $element The form element to populate + * @param string $entity The Doctrine2 entity class to select items from + * @param string $indexElement The element with which to set the select value attributes with (typically `id`) + * @param string|array $displayElements If a string, then the database column element to show in the select + * dropdown. If an array, the contents of these elements will be concatenated with dashes + * @param string $orderBy The element to order by + * @param string $orderDir The order direction + * @return int The maximum value of the $indexElement (asuming integer!) + */ + public static function populateSelectFromDatabase( $element, $entity, $indexElement, $displayElements, $orderBy = null, $orderDir = 'ASC' ) + { + if( !is_array( $displayElements ) ) + $displayElements = [ $displayElements ]; + + $select = "e.{$indexElement} AS {$indexElement}"; + foreach( $displayElements as $idx => $de ) + { + if( is_array( $de ) ) + $select .= ", e.{$idx} AS {$idx}"; + else + $select .= ", e.{$de} AS {$de}"; + } + + $qb = Zend_Registry::get( 'd2em' )['default']->createQueryBuilder() + ->select( $select )->from( $entity, 'e' ); + + if( $orderBy !== null ) + $qb->orderBy( "e.{$orderBy}", $orderDir == 'DESC' ? 'DESC' : 'ASC' ); + + return self::populateSelectFromDatabaseQuery( $qb->getQuery(), $element, $entity, $indexElement, $displayElements, $orderBy, $orderDir ); + } + + /** + * Populate a Zend_Form SELECT element from a database table + * + * @param \Doctrine\ORM\Query $query The query to for the database select + * @param Zend_Form_Element_Select $element The form element to populate + * @param string $entity The Doctrine2 entity class to select items from + * @param string $indexElement The element with which to set the select value attributes with (typically `id`) + * @param string|array $displayElements If a string, then the database column element to show in the select + * dropdown. If an array, the contents of these elements will be concatenated with dashes + * @param string $orderBy The element to order by + * @param string $orderDir The order direction + * @return int The maximum value of the $indexElement (asuming integer!) + */ + public static function populateSelectFromDatabaseQuery( $query, $element, $entity, $indexElement, $displayElements, $orderBy = null, $orderDir = 'ASC' ) + { + if( !is_array( $displayElements ) ) + $displayElements = [ $displayElements ]; + + $rows = $query->getResult(); + + $options = array( '0' => '' ); + $maxId = 0; + + foreach( $rows as $r ) + { + $text = ''; + + foreach( $displayElements as $idx => $de ) + { + if( is_array( $de ) ) + { + switch( $de['type'] ) + { + case 'STRING': + $str = $r[$idx]; + break; + + case 'DATE': + case 'TIME': + case 'DATETIME': + $str = $r[$idx]->format( $de['format'] ); + break; + + default: + die( 'Unhandled type in OSS/Form/Trait/Doctrine2::populateSelectFromDatabaseQuery()' ); + } + } + else + $str = $r[$de]; + + $text .= "{$str} - "; + } + + $text = substr( $text, 0, strlen( $text ) - 3 ); + + $options[ $r[$indexElement] ] = $text; + + if( $r[$indexElement] > $maxId ) + $maxId = $r[$indexElement]; + } + + $element->setMultiOptions( $options ); + + return( $maxId ); + } + + +} diff --git a/library/OSS/Form/Trait/FileSize.php b/library/OSS/Form/Trait/FileSize.php new file mode 100644 index 0000000..00b7cb3 --- /dev/null +++ b/library/OSS/Form/Trait/FileSize.php @@ -0,0 +1,95 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Functionality for setting and getting FileSize filter options + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait_FileSize +{ + /** + * FilterSize default multplier + * @var string + */ + private $filter_filesize_multiplier = OSS_Filter_FileSize::SIZE_BYTES; + + /** + * Sets multiplier for FileSize filter + * + * Valid multiplier options: B, KB, MB, GB. Where case is not sensitive. + * + * @param string $multiplier Sets default multiplier it's not set by user. + * @return void + * @throws OSS_Exception If multiplier is not one of $SIZE_MULTIPLIERS KEY. + */ + public function setFilterFileSizeMultiplier( $multiplier ) + { + if( array_key_exists( strtoupper( $multiplier ), OSS_Filter_FileSize::$SIZE_MULTIPLIERS ) ) + $this->filter_filesize_multiplier = strtoupper( $multiplier ); + else + throw new OSS_Exception( "Trying to set unknown multiplier for FileSize filter." ); + + foreach( $this->getElements() as $name => $element ) + { + if( $element->getFilter( 'FileSize' ) ) + { + $element->removeFilter( 'FileSize' ); + $element->addFilter( new OSS_Filter_FileSize( $this->getFilterFileSizeMultiplier() ) ); + } + } + } + + /** + * Return multiplier for FileSize filter + * + * @param void + * @return string + */ + public function getFilterFileSizeMultiplier() + { + return $this->filter_filesize_multiplier; + } +} diff --git a/library/OSS/Form/Trait/GenericElements.php b/library/OSS/Form/Trait/GenericElements.php new file mode 100644 index 0000000..c2d61fe --- /dev/null +++ b/library/OSS/Form/Trait/GenericElements.php @@ -0,0 +1,67 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Functionality for creating generic / common / standard elements + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait_GenericElements +{ + + /** + * A utility function for creating a standard submit button for forms. + * + * @param string $name The element name + * @param string $label The element label + * @return Zend_Form_Element_Submit The submit element + */ + public static function createSubmitElement( $name = 'submit_element', $label = 'Submit' ) + { + $sb = new Zend_Form_Element_Submit( $name ); + return $sb->setLabel( $label ); + } + +} diff --git a/library/OSS/Form/Trait/InsertElementFns.php b/library/OSS/Form/Trait/InsertElementFns.php new file mode 100644 index 0000000..423d9bc --- /dev/null +++ b/library/OSS/Form/Trait/InsertElementFns.php @@ -0,0 +1,131 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Functionality for adding elements at specific positions + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait_InsertElementFns +{ + /** + * Inserts an element into the form after an already existing element. + * + * @param string|object $afterElement an element name or an instance of Zend_Form_Element, the new element will be placed after that one + * @param string|object $element an element name or an instance of Zend_Form_Element, the new element which will be inserted + * @param string $name default null the name for the new element + * @param array $options default null the options for the new element + * @param int $order default null if there are ordered subforms in the form, then passing an order number might be necessary to position the element correctly + * @return void + */ + public function addElementAfter( $afterElement, $element, $name = null, $options = null, $order = null ) + { + $this->addElement( $element, $name, $options ); + + if( $afterElement instanceof Zend_Form_Element ) + $aename = $afterElement->getName(); + else + $aename = $afterElement; + + if( $element instanceof Zend_Form_Element ) + $ename = $element->getName(); + else + $ename = $element; + + unset( $this->_order[$ename] ); + + $newOrder = array(); + foreach( $this->_order as $iname => $item ) + { + $newOrder[$iname] = $item; + + if( $iname == $aename ) + $newOrder[$ename] = $order; + } + + $this->_order = $newOrder; + } + + + /** + * Inserts an element into the form before an already existing element. + * + * @param string|object $beforeElement an element name or an instance of Zend_Form_Element, the new element will be placed before that one + * @param string|object $element an element name or an instance of Zend_Form_Element, the new element which will be inserted + * @param string $name default null the name for the new element + * @param array $options default null the options for the new element + * @param int $order default null if there are ordered subforms in the form, then passing an order number might be necessary to position the element correctly + * @return void + */ + public function addElementBefore( $beforeElement, $element, $name = null, $options = null, $order = null ) + { + $this->addElement( $element, $name, $options ); + + if( $beforeElement instanceof Zend_Form_Element ) + $bename = $beforeElement->getName(); + else + $bename = $beforeElement; + + if( $element instanceof Zend_Form_Element ) + $ename = $element->getName(); + else + $ename = $element; + + unset( $this->_order[$ename] ); + + $newOrder = array(); + + foreach( $this->_order as $iname => $item) + { + if( $iname == $bename ) + $newOrder[$ename] = $order; + + $newOrder[$iname] = $item; + } + + $this->_order = $newOrder; + } + +} diff --git a/library/OSS/Form/Trait/IsEdit.php b/library/OSS/Form/Trait/IsEdit.php new file mode 100644 index 0000000..af51ecf --- /dev/null +++ b/library/OSS/Form/Trait/IsEdit.php @@ -0,0 +1,82 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Functionality for defining whether a form is for editing an object or creating a new object + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Form_Trait_IsEdit +{ + /** + * Are we editing or creating? + * @var bool Are we editing or creating? + */ + public $isEdit = false; + + /** + * The trait's initialisation method. + * + * This function is called from the form's contructor and it passes those + * same variables used for construction to the traits' init methods. + * + * @param null|array $options An array of options + */ + public function OSS_Form_Trait_IsEdit_Init( $options ) + { + if( is_array( $options ) && isset( $options['isEdit'] ) ) + $this->isEdit = $options['isEdit']; + } + + /** + * Returns true/false if we are editing/not editing an existing object + * + * @return bool true/false if we are editing/not editing an existing object + */ + public function isEdit() + { + return $this->isEdit; + } +} diff --git a/library/OSS/Form/User.php b/library/OSS/Form/User.php new file mode 100644 index 0000000..a8e5898 --- /dev/null +++ b/library/OSS/Form/User.php @@ -0,0 +1,80 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Utility methods to add user elements to forms + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Form + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Form_User +{ + + + /** + * A utility function for creating a standard email element for forms. + * + * @param string $name The element name + * @param bool $mx Whether to enable DNS MX record checking + * @return Zend_Form_Element_Text The email element + */ + public static function createEmailElement( $name = 'email' ) + { + $em = new Zend_Form_Element_Text( $name, $mx = false ); + + return $em->setAttrib( 'size', 32) + ->setLabel( _( 'Email' ) ) + ->setAttrib( 'data-prompt', 'Add an email address' ) + ->setAttrib( 'title', _( 'Email' ) ) + ->setAttrib( 'class', 'span3 required' ) + ->setRequired( true ) + ->addValidator( 'EmailAddress', true, array( 'mx' => $mx ) ) + ->addValidator( 'StringLength', false, array( 5, 90 ) ) + ->addFilter( 'StringTrim' ) + ->addFilter( 'HtmlEntitiesDecode' ) + ->addFilter( 'StripTags' ) + ->addFilter( 'StripSlashes' ); + } + + +} diff --git a/library/OSS/GeoIP.php b/library/OSS/GeoIP.php new file mode 100644 index 0000000..2f3251a --- /dev/null +++ b/library/OSS/GeoIP.php @@ -0,0 +1,106 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Geo IP class. + * + * @category OSS + * @package OSS_GeoIP + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_GeoIP +{ + + /** + * Returns the two letter country code of a given IP (or an error code) + * + * Error codes: + * 10 - GeoIP is not installed + * 11 - Country database is not available + * 12 - Unknown / empty result. RFC1918? + * + * @param string $ip The IP to look up + * @return string The two letter country code (or error code) + */ + public static function getCountryCode( $ip ) + { + if( !defined( 'GEOIP_COUNTRY_EDITION' ) ) + return '10'; + + if( !geoip_db_avail( GEOIP_COUNTRY_EDITION ) ) + return '11'; + + $code = @geoip_country_code_by_name( $ip ); + + if( $code == '' ) + return '12'; + + return $code; + } + + + /** + * Returns the timezone for a IP (or the default on error) + * + * @param string $ip The IP to look up + * @param string $default The default timezone to use on error + * @return string The timezone (e.g. 'Europe/Dublin') + */ + public static function getTimezone( $ip, $default ) + { + if( !defined( 'GEOIP_COUNTRY_EDITION' ) ) + return $default; + + if( !geoip_db_avail( GEOIP_COUNTRY_EDITION ) ) + return $default; + + $tz = @geoip_time_zone_by_country_and_region( + @geoip_country_code_by_name( $ip ), + @geoip_region_by_name( $ip ) + ); + + if( $tz === false ) + $tz = @geoip_time_zone_by_country_and_region( @geoip_country_code_by_name( $ip ) ); + + if( $tz === false ) + return $default; + + return $tz; + } +} diff --git a/library/OSS/Html.php b/library/OSS/Html.php new file mode 100644 index 0000000..a83b09f --- /dev/null +++ b/library/OSS/Html.php @@ -0,0 +1,120 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Html + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Html +{ + /** + * Object to rendering HTMLs. + */ + private $_view = null; + + /** + * Path to template for rendering. + */ + private $_viewscript = null; + + /** + * Constructor + * + * @param object $view Object for rendering HTMLs. + * @param string $viewscript Path to template. + */ + public function __construct( $view, $viewscript = null ) + { + $this->_viewscript = $viewscript; + $this->_view = $view; + } + + /** + * Sets view script for rendering + * + * @param string $viewscript Path to template. + */ + public function setViewScript( $viewscript ) + { + $this->_viewscript = $viewscript; + } + + /** + * Renders the view script + * + * @returns string + */ + public function render() + { + return $this->_view->render( $this->_viewscript ); + } + + /** + * Get contents it's the same as render(). + * + * @returns string + * @see OSS_Html::render() + */ + public function getContents() + { + return $this->render(); + } + + /** + * Gets as file. + * Sets headers and prints out content. + * + * @param string $fileName Name of file. + * @returns string + */ + public function getAsFile( $fileName = false ) + { + $name = sprintf( "%s_%s.html", $fileName ? $fileName : 'OSSFile', date( 'YmdHis' ) ); + + header( 'Content-type: text/html' ); + header( "Content-Disposition: attachment; filename=" . $name ); + header( 'Cache-Control: no-cache, must-revalidate' ); // HTTP/1.1 + header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' ); + + echo $this->render(); + } +} + + diff --git a/library/OSS/Invoice.php b/library/OSS/Invoice.php new file mode 100644 index 0000000..7798347 --- /dev/null +++ b/library/OSS/Invoice.php @@ -0,0 +1,234 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Invoice class. + * + * NOTICE: Supports only Doctrine2 database engine. + * + * @category OSS + * @package OSS_Invoice + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Invoice +{ + + /** + * Getting Doctrine2 entity manager + * + * @return EntityManager + */ + private static function getStaticD2EM() + { + return Zend_Registry::get( "d2em" )[ 'default' ]; + } + + /** + * Returns with a simple statistics about non- and partially paid invoices as an associative array in which the key + * 'sum_total' contains the sum of the total to be paid, 'sum_received' the sum of the already received money and 'howmany' + * the number of invoices. + * + * @param \Entities\Customer $customer + * @return array + */ + public static function getOutstandingStat( $customer = null ) + { + $qb = self::getStaticD2EM()->createQueryBuilder() + ->select( 'sum( i.total ) as sum_total, sum( i.received ) as sum_received, count( i.id ) as howmany' ) + ->from( '\\Entities\\Invoice', "i" ) + ->where( 'i.total > i.received' ) + ->orWhere( 'i.received IS NULL' ); + + if( $customer ) + $qb->andWhere( 'i.Customer = ?1' ) + ->setParameter( 1, $customer ); + + return $qb->getQuery()->getResult()[0]; + } + + + /** + * Returns with the non- and partially paid invoices as a Doctrine collection object. The Item objects in the collection + * are sorted by the due date, ascending. + * + * @param \Entities\Customer $customer default null if not null then will limit the serach to invoices for a given user + * @param string $maxDate default null if not null then will limit the serach to invoices having tax_date <= $pMaxDate, must be in "YYYY-MM-DD" format + * @param string $orderBy Order by field + * @param string $orderByDir Order by direction e.g. ASC, DESC + * @return object + */ + public static function getOutstanding( $customer = null, $maxDate = null, $orderBy = 'due_date', $orderByDir = "ASC" ) + { + $qb = self::getStaticD2EM()->createQueryBuilder() + ->select( 'i.*' ) + ->from( '\\Entities\\Invoice', "i" ) + ->where( 'i.received < i.total' ) + ->orWhere( 'i.received IS NULL' ); + + if( $customer ) + $qb->andWhere( 'i.Customer = ?1' ) + ->setParameter( 1, $customer ); + + if( $maxDate ) + $qb->andWhere( 'i.tax_date <= ?2' ) + ->setParameter( 2, $maxDate ); + + if( $orderBy ) + $qb->orderBy( $pOrderBy, $orderByDir ); + + return $qb->getQuery()->getResult(); + } + + + /** + * Returns with the sum of received amount from all invoices. + * + * @param \Entities\Customer|null $customer Customer for filtering invoice + * @return float + */ + public static function getReceivedSumValue( $customer = null ) + { + $qb = self::getStaticD2EM()->createQueryBuilder() + ->select( 'sum( i.received) as i.sum_received' ) + ->from( '\\Entities\\Invoice', 'i' ); + + if( $customer ) + $qb->andWhere( 'i.Customer = ?', $customer ); + + return $qb->getQuery()->getSingleScalarResult(); + } + + /** + * Returns with a Doctrine_Collection object of all the invoices of which the + * start_date and end_date falls between $minDate and $maxDate respectively, ordered by start_date asc. + * + * @param string $minDate + * @param string $maxDate + * @param \Entities\Customer|null $customer Customer for filtering invoices. + * @return object Doctrine_Collection + */ + public static function getInvoicesBetweenStartEnd( $minDate, $maxDate, $customer = null ) + { + $qb = self::getStaticD2EM()->createQueryBuilder() + ->select( 'i.*' ) + ->from( '\\Entities\\Invoice', 'i' ) + ->where( 'i.start_date >= ?1' ) + ->andWhere( 'i.end_date <= ?2' ) + ->setParameter( 1, $minDate ) + ->setParameter( 2, $maxDate ); + + if( $customer ) + $qb->andWhere( 'i.Customer = ?3' ) + ->setParameter( 3, $customer ); + + $qb->orderBy( 'i.start_date', 'ASC' ); + + return $qb->getQuery()->getResult(); + } + + + /** + * Returns with a Doctrine_Collection object of all the invoices for which the start_date and end_date are in this month, + * ordered by start_date asc. Calls getInvoicesBetweenStartEnd() . + * + * @param \Entities\Customer|null $customer Customer for filtering invoices. + * @return object Doctrine_Collection + * @see self::getInvoicesBetweenStartEnd + */ + public static function getInvoicesFromThisMonth( $customer = null ) + { + return self::getInvoicesBetweenStartEnd( date( "Y-m-01" ), $date( "Y-m-t" ), $customer ); + } + + + /** + * Returns with this month's invoice to add new invoice items to. If it does not exist then creates one. + * Returns with an Invoice model object. + * + * @param \Entities\Customer $customer Invoice for customer + * @return object Invoice model + * @see self::getInvoicesFromThisMonth + */ + public static function getCurrentInvoice( $customer ) + { + $invoices = self::getInvoicesFromThisMonth( $customer ); + + if( $invoice->id == 0 ) + { + $today = new \DateTime(); + $taxDate = clone $today; + $taxDate->add( new DateInterval( 'P1M' ) ); + + $invoice = $customer->createInvoice( $taxDate ) + ->setPeriod( $today, $taxDate ) + ->setDueDate( $today ); + $invoice->addEvent( \Entities\INVOCE_EVENT::EVENT_CREATED ); + } + else + { + $invoice = $invoices[0]; + } + + return $invoice; + } + + /** + * Returns unpaid invoices. + * If $customer not null it will filter unpaid invoices for customer. + * + * @param \Entites\Customer|null $customer Customer for invoice filtering. + * @return array + */ + public static function getUnpaidInvoices( $customer = null ) + { + $qb = self::getStaticD2EM()->createQueryBuilder() + ->select( 'i.*' ) + ->from( '\\Entities\\Invoice', 'i' ) + ->where( "i.paid_date IS NULL" ) + ->orderBy( 'i.start_date', 'ASC' ); + + if( $customer ) + $qb->andWhere( 'i.Customer = ?1' ) + ->setParam( 1, $customer ); + + return $qb->getQuery()->getResult(); + + } +} + diff --git a/library/OSS/License.php b/library/OSS/License.php new file mode 100644 index 0000000..385dcc2 --- /dev/null +++ b/library/OSS/License.php @@ -0,0 +1,79 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * A class for creating and verifying software licenses. + * + * @category OSS + * @package OSS_License + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_License +{ + /** + * Load a given license file and return with the appropriate license object + * + * @param string $file The full path to the license file + * @throws OSS_License_Exception + * @return OSS_License_Abstract An instance of an appropriate license type + */ + public static function load( $file ) + { + if( ( $license = @parse_ini_file( $file ) ) === false ) + throw new OSS_License_Exception( 'Could not open / parse license file' ); + + + if( !isset( $license['Type'] ) ) + throw new OSS_License_Exception( 'Invalid license file format.' ); + + if( !isset( $license['Key'] ) ) + throw new OSS_License_Exception( 'Invalid license file format.' ); + + switch( $license['Type'] ) + { + case 'OSS_License_MD5': + return new OSS_License_MD5( $license ); + break; + + default: + throw new OSS_License_Exception( 'Unsupprted license type.' ); + } + } +} diff --git a/library/OSS/License/Abstract.php b/library/OSS/License/Abstract.php new file mode 100644 index 0000000..3d7d331 --- /dev/null +++ b/library/OSS/License/Abstract.php @@ -0,0 +1,138 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * A base class for creating and verifying software licenses. + * + * @category OSS + * @package OSS_License + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +abstract class OSS_License_Abstract +{ + /** + * @var array The licnese key value pairs + */ + protected $_license = null; + + public function __construct( $license = [] ) + { + $this->_license = $license; + } + + + /** + * Set a license parameter + * + * @param string $p The parameter name (INI compatible value) + * @param string $v The parameter value (INI compatible value) + * @return OSS_License_Abstract Instance of the license for fluent interfaces + */ + public function setParam( $p, $v ) + { + $this->_license[ $p ] = $v; + return $this; + } + + /** + * Get a license parameter + * + * @param string $p The parameter name (INI compatible value) + * @return string The parameter value (or null) + */ + public function getParam( $p ) + { + return isset( $this->_license[ $p ] ) ? $this->_license[ $p ] : null; + } + + /** + * Get all license parameters as an array + * + * @return array The license parameters + */ + public function getParams() + { + return $this->_license; + } + + /** + * Create a single string of all license parameters for key verification / generation + * + * @return string + */ + protected function _amalgamate() + { + // amalgamate all license parameters + $l = ''; + foreach( $this->_license as $p => $v ) + if( $p != 'Key' ) $l .= "{$p}:{$v};"; + + return $l; + } + + /** + * Create an INI format of the license + * + * @return string + */ + protected function _createIni() + { + $l = ''; + foreach( $this->_license as $p => $v ) + $l .= "{$p} = \"{$v}\"\n"; + + return $l; + } + + /** + * Verify that the license is valid + * + * @throws OSS_License_Exception An exception with a public error message + * @return bool True if it is valid (exception on failure) + */ + abstract function verify(); + + /** + * Generate the INI license + * + * @return string The license + */ + abstract function generate(); +} diff --git a/library/OSS/License/Exception.php b/library/OSS/License/Exception.php new file mode 100644 index 0000000..23ce217 --- /dev/null +++ b/library/OSS/License/Exception.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_License + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_License_Exception extends OSS_Exception +{ + +} + + diff --git a/library/OSS/License/ExpiredException.php b/library/OSS/License/ExpiredException.php new file mode 100644 index 0000000..8222d73 --- /dev/null +++ b/library/OSS/License/ExpiredException.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_License + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_License_ExpiredException extends OSS_Exception +{ + +} + + diff --git a/library/OSS/License/MD5.php b/library/OSS/License/MD5.php new file mode 100644 index 0000000..22dec30 --- /dev/null +++ b/library/OSS/License/MD5.php @@ -0,0 +1,91 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * A class for creating and verifying software licenses. + * + * **This license model is based on TRUST under the commercial open + * source model.** It is trivial to work around this license model. + * + * @category OSS + * @package OSS_License + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_License_MD5 extends OSS_License_Abstract +{ + + /** + * Verify that the license is valid and in date + * + * @throws OSS_License_Exception + * @return boolean True if valid, else an exception is thrown + */ + public function verify() + { + $calcKey = md5( $this->_amalgamate() ); + + if( $calcKey != strtolower( str_replace( '-', '', $this->getParam( 'Key' ) ) ) ) + throw new OSS_License_Exception( 'Invalid license key' ); + + if( $this->getParam( 'Expires' ) !== null && $this->getParam( 'Expires' ) != '0' ) + { + if( new DateTime() > new DateTime( $this->getParam( 'Expires' ) . ' 23:59:59' ) ) + throw new OSS_License_ExpiredException( 'Your license has expired' ); + } + + return true; + } + + + public function generate() + { + $this->setParam( 'Type', 'OSS_License_MD5' ); + $key = strtoupper( md5( $this->_amalgamate() ) ); + + $p = []; + for( $i = 0; $i < 8; $i++ ) + $p[] = substr( $key, $i * 4, 4 ); + + $key = implode( '-', $p ); + + $this->setParam( 'Key', $key ); + return $this->_createIni(); + } +} diff --git a/library/OSS/Log.php b/library/OSS/Log.php new file mode 100644 index 0000000..8a08637 --- /dev/null +++ b/library/OSS/Log.php @@ -0,0 +1,88 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Log + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Log extends Zend_Log +{ + + /** + * Inserts an element into the form after an already existing element. + * + * @param string|object $afterElement an element name or an instance of Zend_Form_Element, the new element will be placed after that one + * @param string|object $element an element name or an instance of Zend_Form_Element, the new element which will be inserted + * @param string $name default null the name for the new element + * @param array $options default null the options for the new element + * @param int $order default null if there are ordered subforms in the form, then passing an order number might be necessary to position the element correctly + * @return void + */ + public function alert( $message ) + { + if ( php_sapi_name() == 'cli' ) + { + $return = $message; + } + else + { + $return = $message . " + + host : {$_SERVER['HTTP_HOST']} + user agent : {$_SERVER['HTTP_USER_AGENT']} + remote addr : {$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} +script filename : {$_SERVER['SCRIPT_FILENAME']} + request method : {$_SERVER['REQUEST_METHOD']} + query string : {$_SERVER['QUERY_STRING']} + request uri : {$_SERVER['REQUEST_URI']} +"; + } + + try + { + $this->log( $message, Zend_Log::ALERT ); + } + catch( Exception $e ) + { + $this->debug( $e->getMessage() ); + } + } + +} diff --git a/library/OSS/Mail.php b/library/OSS/Mail.php new file mode 100644 index 0000000..b19b268 --- /dev/null +++ b/library/OSS/Mail.php @@ -0,0 +1,959 @@ + 'Johhny', 'Email' => 'johnny@email.com'); + + $pParams['From'] = 'johnny@email.com'; + + $pParams['ReturnTo'] = 'johnny@email.com'; // only Email is used + + $pParams['ReturnTo'] = array('Email' => 'johnny@email.com'); // only Email is used + + $pParams['ReturnTo'] = array('Name' => 'Johhny', 'Email' => 'johnny@email.com'); // only Email is used + + $pParams['To'] = 'johnny@email.com'; + + $pParams['To'] = array('Email' => 'johnny@email.com'); + + $pParams['To'] = array('Name' => 'Johhny', 'Email' => 'johnny@email.com'); + + $pParams['To'] = array( + 'johnny@email.com', + array('Name' => 'Johhny', 'Email' => 'johnny@email.com'), + array('Email' => 'johnny@email.com') + ); + + $pParams['Cc'] = 'johnny@email.com'; + + $pParams['Cc'] = array('Email' => 'johnny@email.com'); + + $pParams['Cc'] = array('Name' => 'Johhny', 'Email' => 'johnny@email.com'); + + $pParams['Cc'] = array( + 'johnny@email.com', + array('Email' => 'johnny@email.com'), + array('Name' => 'Johhny', 'Email' => 'johnny@email.com') + ); + + $pParams['Bcc'] = 'johnny@email.com'; + + $pParams['Bcc'] = array('Email' => 'johnny@email.com'); + + $pParams['Bcc'] = array('Name' => 'Johhny', 'Email' => 'johnny@email.com'); + + $pParams['Bcc'] = array( + 'johnny@email.com', + array('Email' => 'johnny@email.com'), + array('Name' => 'Johhny', 'Email' => 'johnny@email.com') + ); + + $pParams['Attachment'] = 'path/to/dir/thefile.ext'; + + $pParams['Attachment'] = array( + 'path/to/dir/thefile.ext', + 'path/to/dir/thefile.ext', + 'path/to/dir/thefile.ext' + ); + + $pParams['TextBody'] = 'test email'; + + $pParams['HtmlBody'] = 'test email'; + + $pParams['Subject'] = 'test'; + + $pParams['Charset'] = 'iso-8859-1'; + +*/ + +/** + * OSS Framework + * + * This file is part of the "OSS Framework" - a library of tools, utilities and + * extensions to the Zend Framework V1.x used for PHP application development. + * + * Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * All rights reserved. + * + * Open Source Solutions Limited is a company registered in Dublin, + * Ireland with the Companies Registration Office (#438231). We + * trade as Open Solutions with registered business name (#329120). + * + * Contact: Barry O'Donovan - info (at) opensolutions (dot) ie + * http://www.opensolutions.ie/ + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * + * It is also available through the world-wide-web at this URL: + * http://www.opensolutions.ie/licenses/new-bsd + * + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to info@opensolutions.ie so we can send you a copy immediately. + * + * @category OSS + * @package OSS_Mail + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + * @link http://www.opensolutions.ie/ Open Source Solutions Limited + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Mail + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Mail extends Zend_Mail +{ + + const ERR_INSUFFICIENT_DATA = 'You must have a sender, a recipient and a message body to send an e-mail.'; + + + /** + * Array of mime types + * + * @var array + */ + protected $_mimeTypes = array( + '323' => 'text/h323', + '3dmf' => 'x-world/x-3dmf', + '3dm' => 'x-world/x-3dmf', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abc' => 'text/vnd.abc', + 'acgi' => 'text/html', + 'acx' => 'application/internet-property-stream', + 'afl' => 'video/animaflex', + 'ai' => 'application/postscript', + 'aif' => 'audio/aiff', + 'aifc' => 'audio/aiff', + 'aiff' => 'audio/aiff', + 'aim' => 'application/x-aim', + 'aip' => 'text/x-audiosoft-intra', + 'ani' => 'application/x-navi-animation', + 'aos' => 'application/x-nokia-9000-communicator-add-on-software', + 'aps' => 'application/mime', + 'arj' => 'application/arj', + 'art' => 'image/x-jg', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'asm' => 'text/x-asm', + 'asp' => 'text/asp', + 'asr' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'atom' => 'application/atom+xml', + 'au' => 'audio/basic', + 'au' => 'audio/x-au', + 'avi' => 'video/avi', + 'avs' => 'video/avs-video', + 'axs' => 'application/olescript', + 'bas' => 'text/plain', + 'bcpio' => 'application/x-bcpio', + 'bin' => 'application/x-binary', + 'bm' => 'image/bmp', + 'bmp' => 'image/bmp', + 'boo' => 'application/book', + 'book' => 'application/book', + 'boz' => 'application/x-bzip2', + 'bsh' => 'application/x-bsh', + 'bz2' => 'application/x-bzip2', + 'bz' => 'application/x-bzip', + 'cat' => 'application/vnd.ms-pki.seccat', + 'ccad' => 'application/clariscad', + 'cco' => 'application/x-cocoa', + 'cc' => 'text/plain', + 'cdf' => 'application/cdf', + 'cer' => 'application/pkix-cert', + 'cgm' => 'image/cgm', + 'cha' => 'application/x-chat', + 'chat' => 'application/x-chat', + 'class' => 'application/java', + 'clp' => 'application/x-msclip', + 'cmx' => 'image/x-cmx', + 'cod' => 'image/cis-cod', + 'com' => 'text/plain', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/plain', + 'cpt' => 'application/x-cpt', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkcs-crl', + 'crt' => 'application/pkix-cert', + 'csh' => 'text/x-script.csh', + 'css' => 'text/css', + 'c++' => 'text/plain', + 'c' => 'text/plain', + 'cxx' => 'text/plain', + 'dcr' => 'application/x-director', + 'deepv' => 'application/x-deepv', + 'def' => 'text/plain', + 'der' => 'application/x-x509-ca-cert', + 'dif' => 'video/x-dv', + 'dir' => 'application/x-director', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dl' => 'video/dl', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/commonground', + 'drw' => 'application/drafting', + 'dtd' => 'application/xml-dtd', + 'dvi' => 'application/x-dvi', + 'dv' => 'video/x-dv', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dwg', + 'dxr' => 'application/x-director', + 'elc' => 'application/x-elc', + 'el' => 'text/x-script.elisp', + 'env' => 'application/x-envoy', + 'eps' => 'application/postscript', + 'es' => 'application/x-esrehber', + 'etx' => 'text/x-setext', + 'evy' => 'application/envoy', + 'ez' => 'application/andrew-inset', + 'f77' => 'text/x-fortran', + 'f90' => 'text/plain', + 'fdf' => 'application/vnd.fdf', + 'fif' => 'image/fif', + 'fli' => 'video/fli', + 'flo' => 'image/florian', + 'flr' => 'x-world/x-vrml', + 'flx' => 'text/vnd.fmi.flexstor', + 'fmf' => 'video/x-atomic3d-feature', + 'for' => 'text/plain', + 'fpx' => 'image/vnd.fpx', + 'frl' => 'application/freeloader', + 'f' => 'text/plain', + 'funk' => 'audio/make', + 'g3' => 'image/g3fax', + 'gif' => 'image/gif', + 'gl' => 'video/gl', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'gsd' => 'audio/x-gsm', + 'gsm' => 'audio/x-gsm', + 'gsp' => 'application/x-gsp', + 'gss' => 'application/x-gss', + 'gtar' => 'application/x-gtar', + 'g' => 'text/plain', + 'gz' => 'application/x-gzip', + 'gzip' => 'application/x-gzip', + 'hdf' => 'application/x-hdf', + 'help' => 'application/x-helpfile', + 'hgl' => 'application/vnd.hp-hpgl', + 'hh' => 'text/plain', + 'hlb' => 'text/x-script', + 'hlp' => 'application/winhlp', + 'hpg' => 'application/vnd.hp-hpgl', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hqx' => 'application/binhex', + 'hta' => 'application/hta', + 'htc' => 'text/x-component', + 'h' => 'text/plain', + 'htmls' => 'text/html', + 'html' => 'text/html', + 'htm' => 'text/html', + 'htt' => 'text/webviewhtml', + 'htx' => 'text/html', + 'ice' => 'x-conference/x-cooltalk', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'idc' => 'text/plain', + 'ief' => 'image/ief', + 'iefs' => 'image/ief', + 'ifb' => 'text/calendar', + 'iges' => 'application/iges', + 'igs' => 'application/iges', + 'iii' => 'application/x-iphone', + 'ima' => 'application/x-ima', + 'imap' => 'application/x-httpd-imap', + 'inf' => 'application/inf', + 'ins' => 'application/x-internet-signup', + 'ip' => 'application/x-ip2', + 'isp' => 'application/x-internet-signup', + 'isu' => 'video/x-isvideo', + 'it' => 'audio/it', + 'iv' => 'application/x-inventor', + 'ivr' => 'i-world/i-vrml', + 'ivy' => 'application/x-livescreen', + 'jam' => 'audio/x-jam', + 'java' => 'text/plain', + 'jav' => 'text/plain', + 'jav' => 'text/x-java-source', + 'jcm' => 'application/x-java-commerce', + 'jfif' => 'image/jpeg', + 'jfif-tbnl' => 'image/jpeg', + 'jnlp' => 'application/x-java-jnlp-file', + 'jp2' => 'image/jp2', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jps' => 'image/x-jps', + 'js' => 'text/javascript', + 'jut' => 'image/jutvision', + 'kar' => 'audio/midi', + 'ksh' => 'application/x-ksh', + 'la' => 'audio/nspaudio', + 'lam' => 'audio/x-liveaudio', + 'latex' => 'application/x-latex', + 'lha' => 'application/lha', + 'list' => 'text/plain', + 'lma' => 'audio/nspaudio', + 'log' => 'text/plain', + 'lsf' => 'video/x-la-asf', + 'lsp' => 'application/x-lisp', + 'lst' => 'text/plain', + 'lsx' => 'video/x-la-asf', + 'ltx' => 'application/x-latex', + 'lzh' => 'application/x-lzh', + 'lzx' => 'application/lzx', + 'lzx' => 'application/x-lzx', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm4a' => 'audio/mp4a-latm', + 'm4b' => 'audio/mp4a-latm', + 'm4p' => 'audio/mp4a-latm', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'mac' => 'image/x-macpaint', + 'man' => 'application/x-troff-man', + 'map' => 'application/x-navimap', + 'mar' => 'text/plain', + 'mathml' => 'application/mathml+xml', + 'mbd' => 'application/mbedlet', + 'mc$' => 'application/x-magic-cap-package-1.0', + 'mcd' => 'application/mcad', + 'mcf' => 'text/mcf', + 'mcp' => 'application/netmc', + 'mdb' => 'application/x-msaccess', + 'me' => 'application/x-troff-me', + 'mesh' => 'model/mesh', + 'mht' => 'message/rfc822', + 'mhtml' => 'message/rfc822', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mjf' => 'audio/x-vnd.audioexplosion.mjuicemediafile', + 'mjpg' => 'video/x-motion-jpeg', + 'mm' => 'application/base64', + 'mme' => 'application/base64', + 'mny' => 'application/x-msmoney', + 'mod' => 'audio/mod', + 'moov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mov' => 'video/quicktime', + 'mp2' => 'video/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mpa' => 'audio/mpeg', + 'mpc' => 'application/x-project', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpga' => 'audio/mpeg', + 'mpg' => 'video/mpeg', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/x-project', + 'mpv2' => 'video/mpeg', + 'mpv' => 'application/x-project', + 'mpx' => 'application/x-project', + 'mrc' => 'application/marc', + 'ms' => 'application/x-troff-ms', + 'msh' => 'model/mesh', + 'm' => 'text/plain', + 'mvb' => 'application/x-msmediaview', + 'mv' => 'video/x-sgi-movie', + 'mxu' => 'video/vnd.mpegurl', + 'my' => 'audio/make', + 'mzz' => 'application/x-vnd.audioexplosion.mzz', + 'nap' => 'image/naplps', + 'naplps' => 'image/naplps', + 'nc' => 'application/x-netcdf', + 'ncm' => 'application/vnd.nokia.configuration-message', + 'niff' => 'image/x-niff', + 'nif' => 'image/x-niff', + 'nix' => 'application/x-mix-transfer', + 'nsc' => 'application/x-conference', + 'nvd' => 'application/x-navidoc', + 'nws' => 'message/rfc822', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'omc' => 'application/x-omc', + 'omcd' => 'application/x-omcdatamaker', + 'omcr' => 'application/x-omcregerator', + 'p10' => 'application/pkcs10', + 'p12' => 'application/pkcs-12', + 'p7a' => 'application/x-pkcs7-signature', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'part' => 'application/pro_eng', + 'pas' => 'text/pascal', + 'pbm' => 'image/x-portable-bitmap', + 'pcl' => 'application/vnd.hp-pcl', + 'pct' => 'image/pict', + 'pcx' => 'image/x-pcx', + 'pdb' => 'chemical/x-pdb', + 'pdf' => 'application/pdf', + 'pfunk' => 'audio/make', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pic' => 'image/pict', + 'pict' => 'image/pict', + 'pkg' => 'application/x-newton-compatible-pkg', + 'pko' => 'application/vnd.ms-pki.pko', + 'pl' => 'text/plain', + 'pl' => 'text/x-script.perl', + 'plx' => 'application/x-pixclscript', + 'pm4' => 'application/x-pagemaker', + 'pm5' => 'application/x-pagemaker', + 'pma' => 'application/x-perfmon', + 'pmc' => 'application/x-perfmon', + 'pm' => 'image/x-xpixmap', + 'pml' => 'application/x-perfmon', + 'pmr' => 'application/x-perfmon', + 'pm' => 'text/x-script.perl-module', + 'pmw' => 'application/x-perfmon', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'pntg' => 'image/x-macpaint', + 'pnt' => 'image/x-macpaint', + 'pot' => 'application/mspowerpoint', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'pov' => 'model/x-pov', + 'ppa' => 'application/vnd.ms-powerpoint', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/mspowerpoint', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ppz' => 'application/mspowerpoint', + 'pre' => 'application/x-freelance', + 'prf' => 'application/pics-rules', + 'prt' => 'application/pro_eng', + 'ps' => 'application/postscript', + 'p' => 'text/x-pascal', + 'pub' => 'application/x-mspublisher', + 'pvu' => 'paleovu/x-pv', + 'pwz' => 'application/vnd.ms-powerpoint', + 'pyc' => 'applicaiton/x-bytecode.python', + 'py' => 'text/x-script.phyton', + 'qcp' => 'audio/vnd.qcelp', + 'qd3d' => 'x-world/x-3dmf', + 'qd3' => 'x-world/x-3dmf', + 'qif' => 'image/x-quicktime', + 'qtc' => 'video/x-qtc', + 'qtif' => 'image/x-quicktime', + 'qti' => 'image/x-quicktime', + 'qt' => 'video/quicktime', + 'ra' => 'audio/x-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/cmu-raster', + 'rast' => 'image/cmu-raster', + 'rdf' => 'application/rdf+xml', + 'rexx' => 'text/x-script.rexx', + 'rf' => 'image/vnd.rn-realflash', + 'rgb' => 'image/x-rgb', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/mid', + 'rmm' => 'audio/x-pn-realaudio', + 'rmp' => 'audio/x-pn-realaudio', + 'rng' => 'application/ringing-tones', + 'rnx' => 'application/vnd.rn-realplayer', + 'roff' => 'application/x-troff', + 'rp' => 'image/vnd.rn-realpix', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'rtf' => 'text/richtext', + 'rt' => 'text/richtext', + 'rtx' => 'text/richtext', + 'rtx' => 'text/richtext', + 'rv' => 'video/vnd.rn-realvideo', + 's3m' => 'audio/s3m', + 'sbk' => 'application/x-tbook', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/x-lotusscreencam', + 'sct' => 'text/scriptlet', + 'sdml' => 'text/plain', + 'sdp' => 'application/sdp', + 'sdr' => 'application/sounder', + 'sea' => 'application/sea', + 'set' => 'application/set', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sgml' => 'text/sgml', + 'sgm' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'sh' => 'text/x-script.sh', + 'shtml' => 'text/html', + 'sid' => 'audio/x-psid', + 'silo' => 'model/mesh', + 'sit' => 'application/x-sit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'skp' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'sl' => 'application/x-seelogo', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'snd' => 'audio/basic', + 'sol' => 'application/solids', + 'spc' => 'text/x-speech', + 'spl' => 'application/futuresplash', + 'spr' => 'application/x-sprite', + 'sprite' => 'application/x-sprite', + 'src' => 'application/x-wais-source', + 'ssi' => 'text/x-server-parsed-html', + 'ssm' => 'application/streamingmedia', + 'sst' => 'application/vnd.ms-pkicertstore', + 'step' => 'application/step', + 's' => 'text/x-asm', + 'stl' => 'application/sla', + 'stm' => 'text/html', + 'stp' => 'application/step', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svf' => 'image/vnd.dwg', + 'svg' => 'image/svg+xml', + 'svr' => 'x-world/x-svr', + 'swf' => 'application/x-shockwave-flash', + 'talk' => 'text/x-speech', + 't' => 'application/x-troff', + 'tar' => 'application/x-tar', + 'tbk' => 'application/toolbook', + 'tcl' => 'application/x-tcl', + 'tcsh' => 'text/x-script.tcsh', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tgz' => 'application/x-compressed', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'tr' => 'application/x-troff', + 'trm' => 'application/x-msterminal', + 'tsi' => 'audio/tsp-audio', + 'tsp' => 'audio/tsplayer', + 'tsv' => 'text/tab-separated-values', + 'turbot' => 'image/florian', + 'txt' => 'text/plain', + 'uil' => 'text/x-uil', + 'uls' => 'text/iuls', + 'unis' => 'text/uri-list', + 'uni' => 'text/uri-list', + 'unv' => 'application/i-deas', + 'uris' => 'text/uri-list', + 'uri' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'ustar' => 'multipart/x-ustar', + 'uue' => 'text/x-uuencode', + 'uu' => 'text/x-uuencode', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcs' => 'text/x-vcalendar', + 'vda' => 'application/vda', + 'vdo' => 'video/vdo', + 'vew' => 'application/groupwise', + 'vivo' => 'video/vivo', + 'viv' => 'video/vivo', + 'vmd' => 'application/vocaltec-media-desc', + 'vmf' => 'application/vocaltec-media-file', + 'voc' => 'audio/voc', + 'vos' => 'video/vosaic', + 'vox' => 'audio/voxware', + 'vqe' => 'audio/x-twinvq-plugin', + 'vqf' => 'audio/x-twinvq', + 'vql' => 'audio/x-twinvq-plugin', + 'vrml' => 'application/x-vrml', + 'vrt' => 'x-world/x-vrt', + 'vsd' => 'application/x-visio', + 'vst' => 'application/x-visio', + 'vsw' => 'application/x-visio', + 'vxml' => 'application/voicexml+xml', + 'w60' => 'application/wordperfect6.0', + 'w61' => 'application/wordperfect6.1', + 'w6w' => 'application/msword', + 'wav' => 'audio/wav', + 'wb1' => 'application/x-qpro', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbmxl' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'web' => 'application/vnd.xara', + 'wiz' => 'application/msword', + 'wk1' => 'application/x-123', + 'wks' => 'application/vnd.ms-works', + 'wmf' => 'windows/metafile', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wml' => 'text/vnd.wap.wml', + 'word' => 'application/msword', + 'wp5' => 'application/wordperfect', + 'wp6' => 'application/wordperfect', + 'wp' => 'application/wordperfect', + 'wpd' => 'application/wordperfect', + 'wps' => 'application/vnd.ms-works', + 'wq1' => 'application/x-lotus', + 'wri' => 'application/mswrite', + 'wrl' => 'model/vrml', + 'wrz' => 'model/vrml', + 'wsc' => 'text/scriplet', + 'wsrc' => 'application/x-wais-source', + 'wtk' => 'application/x-wintalk', + 'xaf' => 'x-world/x-vrml', + 'xbm' => 'image/xbm', + 'xdr' => 'video/x-amt-demorun', + 'xgz' => 'xgl/drawing', + 'xht' => 'application/xhtml+xml', + 'xhtm' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/excel', + 'xl' => 'application/excel', + 'xlb' => 'application/excel', + 'xlc' => 'application/excel', + 'xld' => 'application/excel', + 'xlk' => 'application/excel', + 'xll' => 'application/excel', + 'xll' => 'application/x-excel', + 'xlm' => 'application/excel', + 'xls' => 'application/excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/excel', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlv' => 'application/excel', + 'xlw' => 'application/excel', + 'xm' => 'audio/xm', + 'xml' => 'text/xml', + 'xmz' => 'xgl/movie', + 'xof' => 'x-world/x-vrml', + 'xpix' => 'application/x-vnd.ls-xpix', + 'xpm' => 'image/xpm', + 'x-png' => 'image/png', + 'xslt' => 'application/xslt+xml', + 'xsl' => 'text/xml', + 'xsr' => 'video/x-amt-showrun', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xwd' => 'image/x-xwd', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'z' => 'application/x-compressed', + 'zip' => 'application/zip', + 'zsh' => 'text/x-script.zsh' + ); + + + /** + * Constructor + * + * Calls Zend_Mail::__construct(). + * + * @param string $pCharSet default 'UTF-8' + * @return void + */ + public function __construct( $charSet='UTF-8' ) + { + parent::__construct( $charSet ); + } + + + /** + * This method takes an array of parameters, and sends out the email. Throws a Zend_Mail_Exception on error. Returns with a Zend_Mail object on success. + * The images in the HtmlBody will automatically be embedded. + * + * @param array $params + * @return object Zend_Mail + * @throws Zend_Mail_Exception + * @return void + */ + public function sendMail( $params ) + { + if ( + !isset( $params['From'] ) || !isset( $params['To'] ) || + ( ( isset( $params['TextBody'] ) || isset( $params['HtmlBody'] ) ) == false ) + ) + { + throw new Zend_Mail_Exception( OSS_Mail::ERR_INSUFFICIENT_DATA ); + } + + if( !isset( $params['Charset'] ) ) + $params['Charset'] = 'UTF-8'; + + if( !in_array( mb_strtoupper( $params['Charset']), array( '', 'UTF-8' ) ) ) + $this->_charset = $params['Charset']; + + if( !isset( $params['TextBody'] ) ) + $this->setBodyText( $params['TextBody'], null, Zend_Mime::TYPE_TEXT ); + + if( !isset( $params['HtmlBody'] ) ) + $this->setBodyHtml( $params['HtmlBody'], null, Zend_Mime::TYPE_TEXT ); + + $this->setSubject( htmlspecialchars_decode( $params['Subject'] ) ); + + if( is_array( $params['From'] ) ) + { + if( isset( $params['From']['Email'] ) ) + $this->setFrom( $params['From']['Email'], $this->amendstring( $params['From']['Name'] ) ); + } + else + { + $this->setFrom( $this->amendstring( $params['From'] ) ); + } + + if( isset( $params['ReturnTo'] ) ) + { + if( is_array( $params['ReturnTo'] ) ) + { + if( isset( $params['ReturnTo']['Email'] ) ) + $this->setReturnPath( $this->amendstring( $params['ReturnTo']['Email'] ) ); + } + else + { + $this->setReturnPath( $this->amendstring( $params['ReturnTo'] ) ); + } + } + + if( !is_array($params['To'] ) ) + { + $this->addTo( $this->amendstring( $params['To'] ) ); + } + else if( is_array( $params['To'] ) && isset( $params['To']['Email'] ) ) + { + if( isset( $params['To']['Name'] ) ) + { + $this->addTo( $params['To']['Email'], $this->amendstring( $params['To']['Name'] ) ); + } + else + { + $this->addTo( $this->amendstring( $params['To']['Email'] ) ); + } + } + else + { + $firstTo = true; + foreach( $params['To'] as $to ) + { + if( !is_array( $to ) ) + { + if( $firstTo ) + { + $this->addTo( $this->amendstring( $to ) ); + $firstTo = false; + } + else + { + $this->addCc( $this->amendstring( $to ) ); + } + } + else + { + if( isset( $to['Email']) ) + { + if ($firstTo ) + { + $this->addTo( $to['Email'], $this->amendstring( $to['Name'] ) ); + $firstTo = false; + } + else + { + $this->addCc( $to['Email'], $this->amendstring( $to['Name'] ) ); + } + } + } + } + } + + if( isset( $params['Cc'] ) ) + { + if( !is_array( $params['Cc'] ) ) + { + $this->addCc( $this->amendstring( $params['Cc'] ) ); + } + else if( is_array( $params['Cc'] ) && isset( $params['Cc']['Email'] ) ) + { + if( isset( $params['Cc']['Name'] ) ) + { + $this->addCc( $params['Cc']['Email'], $this->amendstring( $params['Cc']['Name'] ) ); + } + else + { + $this->addCc( $this->amendstring( $params['Cc']['Email'] ) ); + } + } + else + { + foreach( $params['Cc'] as $cc ) + { + if( is_array( $cc ) ) + { + $this->addCc($this->amendstring( $cc ) ); + } + else + { + if( isset( $cc['Email'] ) ) + $this->addCc( $cc['Email'], $this->amendstring( $cc['Name'] ) ); + } + } + } + } + + if( isset( $params['Bcc'] ) ) + { + if( !is_array( $params['Bcc'] ) ) + { + $this->addBcc( htmlspecialchars_decode( $params['Bcc'] ) ); + } + else if( is_array( $params['Bcc'] ) && isset( $params['Bcc']['Email'] ) ) + { + $this->addBcc( $this->amendstring( $params['Bcc']['Email'] ) ); + } + else + { + foreach( $params['Bcc'] as $bcc) + { + if( !is_array( $bcc ) ) + { + $this->addBcc( $this->amendstring( $bcc ) ); + } + else + { + if( isset( $bcc['Email'] ) ) + $this->addBcc( $this->amendstring( $bcc['Email'] ) ); + } + } + } + } + + // embed html images inline + if( isset( $params['HtmlBody'] ) && $params['HtmlBody'] != '' ) + { + $matchCount = preg_match_all("/ 0 ) // if there is any + { + $this->setType( Zend_Mime::MULTIPART_RELATED ); + + $matches = array_unique( $matches[1] ); + + foreach( $matches as $fname ) + $this->attachFile( $fileName, true ); + + //$params['HtmlBody'] = $this->getBodyHtml(true); // $params must be &$params to make sense + } + } + + if( !empty( $params['Attachment'] ) ) + { + if( !is_array( $params['Attachment'] ) ) + $params['Attachment'] = array( $params['Attachment'] ); + + foreach( $params['Attachment'] as $attachFile ) + $this->attachFile( $attachFile, false ); + } + + //OSS_Debug::prr( $this ); die(); + + return $this->send(); + } // function SendEmail + + + /** + * Attaches or embeds a file to/into an email. Embedding the images into an HTML letter happens automatically. + * + * @param string $filePath the path to the file to attach + * @param boolean $embed default false if true then the file will be embedded instead of attached + * @return boolean + */ + public function attachFile( $filePath, $embed = false ) + { + if( $filePath == '' || $filePath == array() ) + return true; //[FIXME] I thing here should be false + + if( !@is_readable( $filePath ) ) + { + $filePath = OSS_String::mb_str_replace( Zend_Controller_Front::getInstance()->getBaseUrl() . '/', '', $filePath ); + } + + if( @is_readable( $filePath ) ) + { + $pathInfo = pathinfo( $filePath ); + + $attachment = $this->createAttachment( @file_get_contents( $filePath ) ); + $attachment->type = $this->getMimeByExtension( $pathInfo['extension'] ); + $attachment->encoding = Zend_Mime::ENCODING_BASE64; + + if( $embed == false) + { + $attachment->disposition = Zend_Mime::DISPOSITION_ATTACHMENT; + $attachment->filename = basename( $filePath ); + } + else + { + $attachment->disposition = Zend_Mime::DISPOSITION_INLINE; + $attachment->id = 'cid_' . md5_file( $filePath ); + $this->setBodyHtml( OSS_String::mb_str_replace( $filePath, "cid:{$attachment->id}", $this->getBodyHtml( true ) ) ); + } + + return true; + } + + return false; + } + + + /** + * Takes a file extension WITHOUT the leading dot and returns with the corresponding MIME type. + * Please note that a good few file formats have more than one MIME types, depending on the OS or the browser, etc, but this method only returns with the "best", most compatible one. + * Returns with "application/octet-stream" if did not find a matching MIME type. + * + * @param string $extension + * @return string + */ + public function getMimeByExtension( $extension ) + { + $extension = trim( mb_strtolower( $extension ) ); + + if( array_key_exists( $extension, $this->_mimeTypes ) ) + return $this->_mimeTypes[$pExtension]; + else + return 'application/octet-stream'; + } + + + /** + * Takes a string and returns with a more RFC compatible version of it. Takes care of the UTF-8 characters and most importantly the double quotes, which can cause + * serious issues if appear in from, to, cc, bcc or returnto. Automatically called by send() where necessary. + * + * @param string $string + * @return string + */ + public function amendString( $string ) + { + return str_replace( array( '"' ), array( '\"' ), htmlspecialchars_decode( $string ) ); + } + +} // class + diff --git a/library/OSS/Message.php b/library/OSS/Message.php new file mode 100644 index 0000000..33fddcb --- /dev/null +++ b/library/OSS/Message.php @@ -0,0 +1,191 @@ +view->ossAddMessage( new OSS_Message( 'This is a info message!', OSS_Message::INFO ) ); + * } + * + * Multiple messages can be added of different kinds (INFO, ALERT, etc). + * + * Then to display these messages in your view (Smarty template) just include the following + * text (i.e. Smarty function): + * + * {OSS_Message} + * + */ + + /** + * OSS: Message + * + * @author Barry O'Donovan + * @author Roland Huszti + * @author Nerijus Barauskas + * @category OSS + * @package OSS_Message + * @copyright Copyright (c) 2009 - 2012 Open Source Solutions Limited, Dublin, Ireland + */ +class OSS_Message +{ + + const INFO = 'info'; + const WARNING = 'warning'; + const ALERT = 'warning'; + const SUCCESS = 'success'; + const ERROR = 'error'; + + const TYPE_MESSAGE = 0; + const TYPE_BLOCK = 1; + const TYPE_POP_UP = 2; + + + /** + * The type of OSS_Message + * + * @var int + */ + protected $type = self::TYPE_MESSAGE; + + + /** + * A variable to hold the message (either scalar string or array of strings ) + * + * @var mixed + */ + protected $message; + + /** + * A variable to hold the appropriate HTML class (e.g. error, success, info) + * + * @var string + */ + protected $class = ''; + + /** + * A variable to indicate whether the message is HTML or not + * + * @var bool + */ + protected $isHTML = true; + + /** + * The constructor + * + * @param string $request The message + * @param string $response The HTML div class + * @param bool $invokeArgs Is the message HTML? (default: true) + * @return void + */ + public function __construct( $message = '', $class = '', $isHTML = true ) + { + $this->message = $message; + $this->setClass( $class ); + $this->isHTML = $isHTML; + } + + + /** + * Get the message as plaintext - essentially strips the tags from the + * message if it is an HTML message + * + * @return string + */ + public function getPlaintext() + { + if( $this->isHTML ) + return( strip_tags( $this->message ) ); + else + return( $this->message ); + } + + /** + * Get the message + * + * @return string + */ + public function getMessage() + { + return( $this->message ); + } + + /** + * Get the message type + * + * @return int + */ + public function getType() + { + return( $this->type ); + } + + /** + * Set the message type + * + * @param int $type Message type + * @return void + */ + public function setType( $type ) + { + $this->type = $type; + } + + /** + * Get the class + * + * @return string the class + */ + public function getClass() + { + return( $this->class ); + } + + /** + * Set the class + * + * @param string $class the class + */ + public function setClass( $class ) + { + if( $class == self::ALERT ) + $class = self::WARNING; + + $this->class = $class; + } +} + + diff --git a/library/OSS/Message/Block.php b/library/OSS/Message/Block.php new file mode 100644 index 0000000..5142b73 --- /dev/null +++ b/library/OSS/Message/Block.php @@ -0,0 +1,121 @@ +view->ossAddMessage( new OSS_Message( 'This is a info message!', OSS_Message::INFO ) ); + * } + * + * Multiple messages can be added of different kinds (INFO, ALERT, etc). + * + * Then to display these messages in your view (Smarty template) just include the following + * text (i.e. Smarty function): + * + * {OSS_Message} + * + */ +/** + * OSS Framework + * + * This file is part of the "OSS Framework" - a library of tools, utilities and + * extensions to the Zend Framework V1.x used for PHP application development. + * + * Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * All rights reserved. + * + * Open Source Solutions Limited is a company registered in Dublin, + * Ireland with the Companies Registration Office (#438231). We + * trade as Open Solutions with registered business name (#329120). + * + * Contact: Barry O'Donovan - info (at) opensolutions (dot) ie + * http://www.opensolutions.ie/ + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * + * It is also available through the world-wide-web at this URL: + * http://www.opensolutions.ie/licenses/new-bsd + * + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to info@opensolutions.ie so we can send you a copy immediately. + * + * @category OSS + * @package OSS_Message + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + * @link http://www.opensolutions.ie/ Open Source Solutions Limited + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * OSS_Message block type class for displaying messages for users. + * + * @category OSS + * @package OSS_Message + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Message_Block extends OSS_Message +{ + + /** + * Elements for the action area + * + * @var null|array + */ + private $actions = null; + + + /** + * Constructor + * + * @param string $message Message to display + * @param string $class Message class + * @param bool $isHTML Htmk flag + * @return void + */ + public function __construct( $message = '', $class = '', $isHTML = true ) + { + parent::__construct( $message, $class, $isHTML ); + $this->setType( self::TYPE_BLOCK ); + } + + /** + * Adding message box + * + * @param string $str Action description + * @return void + */ + public function addAction( $str ) + { + if( $this->actions === null ) + $this->actions = array(); + + $this->actions[] = $str; + } + + /** + * Getting messages + * + * @return array + */ + public function getActions() + { + return $this->actions; + } +} + + diff --git a/library/OSS/Message/Pop/Up.php b/library/OSS/Message/Pop/Up.php new file mode 100644 index 0000000..b454fdc --- /dev/null +++ b/library/OSS/Message/Pop/Up.php @@ -0,0 +1,97 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * OSS_Message pop up type class for displaying messages for users. + * + * @category OSS + * @package OSS_Message + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Message_Pop_Up extends OSS_Message +{ + + /** + * Elements for the action area + * + * @var null|array + */ + private $actions = null; + + + /** + * Constructor + * + * @param string $message Message to display + * @param string $class Message class + * @param bool $isHTML Htmk flag + * @return void + */ + public function __construct( $message = '', $class = '', $isHTML = true ) + { + parent::__construct( $message, $class, $isHTML ); + $this->setType( self::TYPE_POP_UP ); + } + + /** + * Adding message box + * + * @param string $str Action description + * @return void + */ + public function addAction( $str ) + { + if( $this->actions === null ) + $this->actions = array(); + + $this->actions[] = $str; + } + + /** + * Getting messages + * + * @return array + */ + public function getActions() + { + return $this->actions; + } +} + + diff --git a/library/OSS/Net/DNS2.php b/library/OSS/Net/DNS2.php new file mode 100644 index 0000000..727c041 --- /dev/null +++ b/library/OSS/Net/DNS2.php @@ -0,0 +1,86 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Requires pear package Net_DNS2. + * + * @see http://pear.php.net/package/Net_DNS2/docs/1.3.0/ + */ +require_once 'Net/DNS2.php'; + +/** + * Utility methods for NET_DNS2 + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Net + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Net_DNS2 +{ + /** + * Gets records form name server for given dns zone + * + * @param string $zone DNS zone to look for e.g. example.com + * @param string $ns Name server ip + * @param string $type Record type + * @return array + */ + public static function getRecords( $zone, $ns, $type ) + { + $r = new Net_DNS2_Resolver( [ 'nameservers' => [ gethostbyname( $ns ) ] ] ); + + try + { + $records = $r->query( $zone, $type )->answer; + } + catch( Net_DNS2_Exception $e ) + { + if( $e->getCode() == Net_DNS2_Lookups::RCODE_NXDOMAIN ) + return []; + else if( $e->getCode() == Net_DNS2_Lookups::RCODE_NOTAUTH ) + return false; + else + return $e->getMessage() . " {$ns} quered by query( {$zone}, {$type} )"; + } + + return $records; + } +} diff --git a/library/OSS/Net/Exception.php b/library/OSS/Net/Exception.php new file mode 100644 index 0000000..8a5818a --- /dev/null +++ b/library/OSS/Net/Exception.php @@ -0,0 +1,50 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Exception + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Net_Exception extends OSS_Exception +{ + +} + + diff --git a/library/OSS/Net/IPv4.php b/library/OSS/Net/IPv4.php new file mode 100644 index 0000000..d4d311c --- /dev/null +++ b/library/OSS/Net/IPv4.php @@ -0,0 +1,72 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Utility methods for IPv4 addresses + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Net + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Net_IPv4 +{ + + /** + * Converts IPv4 address to its in-addr.arpa format + * + * E.g. IPv4 address like `1.2.3.4` will be converted + * to `4.3.2.1.in-addr.arpa`. + * + * @param string $ip IPv4 address to convert + * @return string The in-addr.arpa version of the IPv4 address + * @throws OSS_Net_Execption On lose checking of IPv4 address format (four octets) + */ + public static function ipv4ToARPA( $ip ) + { + $parts = explode( '.', $ip ); + + if( count( $parts ) != 4 ) + throw new OSS_Net_Exception( 'Invalid IPv4 address - ' . $ip ); + + return sprintf( '%d.%d.%d.%d.in-addr.arpa', $parts[3], $parts[2], $parts[1], $parts[0] ); + } +} diff --git a/library/OSS/Net/IPv6.php b/library/OSS/Net/IPv6.php new file mode 100644 index 0000000..25c1fd1 --- /dev/null +++ b/library/OSS/Net/IPv6.php @@ -0,0 +1,239 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + + +/** + * Utility methods for IPv6 addresses + * + * @author Barry O'Donovan + * @author The Skilled Team of PHP Developers at Open Solutions + * @category OSS + * @package OSS_Net + * @copyright Copyright (c) 2007 - 2013, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Net_IPv6 +{ + //e.g. 2001:7f8:18:2::147 + const TYPE_SHORT = "0"; + + //e.g. 2001:07f8:0018:0002:0000:0000:0000:0147 + const TYPE_LONG_FULL = "1"; + + //e.g. 2001:7f8:18:2:0:0:0:147 + const TYPE_LONG = "2"; + + //e.g. 2001:07f8:0018:0002::0147 + const TYPE_SHORT_FULL = "3"; + + /** + * Changes format of IPv6 address. + * + * @param string $address IPv6 address to change format + * @param int $type Type to convert. One of OSS_Net_Ipv6::TYPE_ + * @return string + * @throws OSS_Net_Exception Bad IPv6 format + */ + static function formatAddress( $address, $type = self::TYPE_SHORT ) + { + + // 20170215 - barryo - I didn't write this and it doesn't work properly :-( + // shortcutting for TYPE_SHORT: + if( $type == self::TYPE_SHORT ) { + return( inet_ntop( inet_pton( $address ) ) ); + } + + $address = strtolower( $address ); + $parts = explode( ":", $address ); + if( count( $parts ) > 8 || count( $parts ) < 4 ) + throw new OSS_Net_Exception( "Bad IPv6 format" ); + + $tmp = $parts; + $diff = 0; + + foreach( $tmp as $ix => $part) + { + if( $part === "" ) + { + $diff = 8 - count( $parts ); + for( $i = $ix; $i <= $ix + $diff; $i++ ) + $parts[$i] = '0'; + } + else + $parts[ $ix + $diff ] = ltrim( $part, '0' ) != '' ? ltrim( $part, '0' ) : '0'; + } + + $rm = false; + foreach( $parts as $ix => $part ) + { + if( ( $part != "" && !ctype_xdigit( $part ) ) || count( $part ) > 4 ) + throw new OSS_Net_Exception( "Bad IPv6 format: {$address}" ); + + $part = self::formatPart( $part, $type, $rm ); + $parts[$ix] = $part; + + if( $part === false ) + unset( $parts[$ix] ); + else if ( $part == '' ) + $rm = true; + else + $rm = false; + } + return implode( ":", $parts ); + } + + /** + * Converts IPv6 address to ARPA name. + * + * E.g. IPv6 address like 2001:7f8:18:2::147 will be converted + * to 7.4.1.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.8.1.0.0.8.f.7.0.1.0.0.2.ip6.arpa, + * + * @param string $ip IP address to convert + * @return string + */ + public static function ipv6ToARPA( $ip ) + { + $ip = self::formatAddress( $ip, self::TYPE_LONG_FULL ); + $ip = strrev( str_replace( ':', '', $ip ) ); + return implode( '.', str_split( $ip ) ) . '.ip6.arpa'; + } + + /** + * Converts ARPA name to IPv6 address. + * + * E.g. IPv6 address like 7.4.1.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.8.1.0.0.8.f.7.0.1.0.0.2.ip6.arpa + * will be converted to 2001:7f8:18:2::147, + * + * @param string $ip IP address to convert + * @param int $type Type to convert. One of OSS_Net_Ipv6::TYPE_ + * @return string + */ + public static function arpaNameToIpv6( $ip, $type = self::TYPE_SHORT ) + { + $ip = substr( strrev( $ip ), 9 ); + $ip = explode( ".", $ip ); + $tmp = "" ; + for( $i = 0; $i < count( $ip ) / 4; $i++ ) + { + $tmp .= $ip[ 0 + $i * 4 ] . $ip[ 1 + $i * 4 ]; + $tmp .= $ip[ 2 + $i * 4 ] . $ip[ 3 + $i * 4 ]; + if( $i + 1 != count( $ip ) / 4 ) + $tmp .= ':'; + } + + return self::formatAddress( $tmp, $type ); + } + + /** + * Format part of IPv6 + * + * @param string $part Part of IPv6 address + * @param int $type Type to convert. One of OSS_Net_Ipv6::TYPE_ + * @param bool $remove If remove returns false. + * @return string|bool + * @throws OSS_Net_Exception Unknown type given + */ + private static function formatPart( $part, $type, $remove ) + { + switch( $type ){ + case self::TYPE_SHORT: + if( $part == '0' ) + { + if( !$remove ) + $ret = ''; + else + $ret = false; + } + else + $ret = $part; + break; + + case self::TYPE_LONG_FULL: + $ret = sprintf( "%04s", $part ); + break; + + case self::TYPE_LONG: + $ret = $part; + break; + + case self::TYPE_SHORT_FULL: + if( $part == '0' ) + { + if( !$remove ) + $ret = ''; + else + $ret = false; + } + else + $ret = sprintf( "%04s", $part ); + break; + + default: + throw new OSS_Net_Exception( "Unknown type given." ); + }; + + return $ret; + } + + /** + * Converts IPv6 address to numerical expresnion. + * + * This function is usefull then need to sort IPv6 addresses. + * Function will takes IPv6 address converts it to long full type then removes ':' + * and it becomes heximal number. Then function converts it to decimal. + * + * e.g. 2a01:8f80:5::9 => 55835678645609170133392336604536766473 + * 2a01:8f80:5::10 => 55835678645609170133392336604536766480 + * + * @param string $ip IPv6 address to convert to numerical expresnion + * @return string + */ + public static function ip2numeric( $ip ) + { + $ip = self::formatAddress( $ip, self::TYPE_LONG_FULL ); + $hex = str_replace( ":", "", $ip ); + + $len = strlen($hex); + $dec = ""; + + for( $i = 1; $i <= $len; $i++ ) + $dec = bcadd( $dec, bcmul( strval( hexdec( $hex[$i - 1] ) ), bcpow( '16', strval( $len - $i ) ) ) ); + + return $dec; + } +} diff --git a/library/OSS/PHPUnit/Extensions/SeleniumTestCase.php b/library/OSS/PHPUnit/Extensions/SeleniumTestCase.php new file mode 100644 index 0000000..21b3cf4 --- /dev/null +++ b/library/OSS/PHPUnit/Extensions/SeleniumTestCase.php @@ -0,0 +1,74 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PHPUnit + * @subpackage Extenesions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_PHPUnit_Extensions_SeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase +{ + + /** + * Constructor + * + * @param null|string $name + * @param array $data + * @param string $dataName + * @param array $browser + * @return void + */ + function __construct( $name = NULL, array $data = array(), $dataName = '', array $browser = array() ) + { + parent::__construct( $name, $data, $dataName, $browser ); + + if( defined( 'CC_SELENIUM_OPT_CaptureScreenshotOnFailure' ) ) + { + $this->captureScreenshotOnFailure = CC_SELENIUM_OPT_CaptureScreenshotOnFailure; + $this->screenshotPath = CC_SELENIUM_OPT_ScreenshotPath; + $this->screenshotUrl = CC_SELENIUM_OPT_ScreenshotUrl; + } + + $this->setBrowser("*firefox"); + $this->setBrowserUrl( CC_SELENIUM_OPT_URL ); + + } +} diff --git a/library/OSS/PaymentProcessor/BaseProcessor.php b/library/OSS/PaymentProcessor/BaseProcessor.php new file mode 100644 index 0000000..c81c362 --- /dev/null +++ b/library/OSS/PaymentProcessor/BaseProcessor.php @@ -0,0 +1,48 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PaymentProcessor + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_PaymentProcessor_BaseProcessor +{ + +} diff --git a/library/OSS/PaymentProcessor/Exception.php b/library/OSS/PaymentProcessor/Exception.php new file mode 100644 index 0000000..f88b980 --- /dev/null +++ b/library/OSS/PaymentProcessor/Exception.php @@ -0,0 +1,52 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PaymentProcessor + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_PaymentProcessor_Exception extends Zend_Exception +{ + const ERR_COULD_NOT_SAVE_CARD = "Could not save credit card."; + const ERR_COULD_NOT_SAVE_PAYER = "Could not save payer."; + const ERR_INCONSISTANT_PAYGATE_STATE = "The paygate state is inconsistant for this operation"; + const ERR_TRANSACTION_NOT_FOUND = "Invalid card number."; +} diff --git a/library/OSS/PaymentProcessor/Realex.php b/library/OSS/PaymentProcessor/Realex.php new file mode 100644 index 0000000..649551e --- /dev/null +++ b/library/OSS/PaymentProcessor/Realex.php @@ -0,0 +1,1277 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PaymentProcessor + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_PaymentProcessor_Realex extends OSS_PaymentProcessor_BaseProcessor +{ + const CARD_TYPE_VISA = 'visa'; + const CARD_TYPE_LASER = 'laser'; + const CARD_TYPE_MASTERCARD = 'mastercard'; + + /** + * Valid credit/debit card types + * @var array + */ + public static $CARD_TYPES = [ + self::CARD_TYPE_VISA => 'visa', + Self::CARD_TYPE_LASER => 'laser', + self::CARD_TYPE_MASTERCARD => 'mc' + ]; + + /** + * An error code to indicate if the response hash is invalid + * + * @var integer An error code to indicate if the response hash is invalid + */ + const ERR_RESPONSE_INVALID_HASH = 905; + + /** + * An array of error strings for the ERR_RESPONSE... codes + * @var array Array of error strings for the ERR_RESPONSE... codes + */ + public static $ERR_TEXT = array( + self::ERR_RESPONSE_INVALID_HASH => 'Response security hash invalid' + ); + + /** + * An array of the mandatory application.ini parameters. + * + * @var array The mandatory application.ini parameters + */ + public static $REQUIRED_PARAMS = [ + 'cgi_url', 'user_agent', 'merchant_id', 'merchant_secret', 'account', 'refund_password' + ]; + + /** + * An array of the optional application.ini parameters (defaults set with their definitions). + * + * @var array The optional application.ini parameters + * @see $_cgi_timeout + * @see $_fake_transactions + * @see $_keep_request_xml + */ + public static $OPTIONAL_PARAMS = [ 'fake_transactions', 'cgi_timeout', 'keep_request_data' ]; + + /** + * The Doctrine2 entity manager + * @var EntityManager The Doctrine2 entity manager + */ + private $_d2em = null; + + /** + * The CGI Url + * @var string The CGI Url + */ + private $_cgi_url; + + /** + * @var string The user agent to report to the CGI + */ + private $_user_agent; + + /** + * @var string Our assigned merchant ID + * @see getMerchantId() + */ + private $_merchant_id; + + /** + * @var string Our assigned shared secret + */ + private $_merchant_secret; + + /** + * @var string Our assigned refund password + */ + private $_refund_password; + + /** + * @var string The account to use for receipt-in / receipt-out + */ + private $_account; + + /** + * @var int The maximum number of seconds for the cURL operation + */ + private $_cgi_timeout = 10; + + /** + * For security reasons, we clear the request data from the database after a sucessful + * Realex API/CGI call. + * + * Some transactions require non-stored data (e.g. credit card number) to replay a failed + * transaction (such as createCreditCard()) which we will store and delete when we + * successfully replay the transaction. Setting this to true means we don't remove it. + * + * WARNING: Set to true only for testing! + * + * @var bool Whether we should clear the request data or not + */ + private $_keep_request_data = false; + + /** + * Fake transactions allows all operations to appear to succeed including + * updates to local database tables but it never contacts Realex and fakes + * all operations. + * + * @var bool Fake all transactions and never contact Realex + * @see _completeFakeTransaction() + */ + private $_fake_transactions = false; + + /** + * An instance of OSS_Log for logging. + * + * @var OSS_Log An instance of OSS_Log for logging. + */ + private $_logger = null; + + + /** + * Realex Payment gateway processor + * + * @param array $config An associated array of realex.* parameters from application.ini + * @param OSS_Log $logger An optional instance of an OSS_Log object if you want logging + * @throws OSS_PaymentProcessor_Realex_Exception + */ + public function __construct( array $config, OSS_Log $logger = null ) + { + // check for required parameters and set member variables accordingly + foreach( self::$REQUIRED_PARAMS as $param ) + { + if( !isset( $config["{$param}"] ) ) + { + throw new OSS_PaymentProcessor_Realex_Exception( + OSS_PaymentProcessor_Realex_Exception::ERR_REQUIRED_PARAMETER_MISSING . ' - ' . $param + ); + } + + $member = "_{$param}"; + + $this->$member = $config[ $param ]; + } + + // check for optional parameters and set member variables accordingly + foreach( self::$OPTIONAL_PARAMS as $param ) + { + if( isset( $config[ "{$param}" ] ) ) + { + $member = "_{$param}"; + $this->$member = $config[ $param ]; + } + } + + $this->_logger = $logger; + } + + /** + * Access method for the merchant ID + * + * @return string The merchant ID + * @see $_merchant_id + */ + protected function getD2EM() + { + if( !$this->_d2em ) + $this->_d2em = Zend_Registry::get( "d2em" )[ 'default' ]; + + return $this->_d2em; + } + + + /** + * Access method for the merchant ID + * + * @return string The merchant ID + * @see $_merchant_id + */ + protected function getMerchantId() + { + return $this->_merchant_id; + } + + + /** + * Access method for the merchant secret + * + * @return string The merchant secret + * @see $_merchant_secret + */ + protected function getMerchantSecret() + { + return $this->_merchant_secret; + } + + + /** + * Access method for the refund password + * + * @return string The refund password + * @see $_refund_password + */ + protected function getRefundPassword() + { + return $this->_refund_password; + } + + + /** + * Returns with a YYYYmmddhhmmss format timestamp used in Realex transactions. + * + * @return string YYYYmmddhhmmss formated timestamp + */ + static public function getTimeStamp() + { + return date( 'YmdHis' ); + } + + + /** + * Log a message if we have an instance of a logger + * + * @param string $msg The log message + * @param int $pri The priority of the message (defaults: OSS_Log::DEBUG) + */ + private function _log( $msg, $pri = OSS_Log::DEBUG ) + { + if( $this->_logger instanceof OSS_Log ) + $this->_logger->log( $msg, $pri ); + } + + /** + * Create a unique Realex order id. + * + * Every Realex transaction must have a unique order id. We use the integer primary + * key of the \Entities\RealexTransaction table (which records all Realex transactions) + * for this. + * + * @param \Entities\RealexTrasaction $rtrans An instance of a save()'d object + * @return string The unique transaction ID + * @see \Entities\RealexTransaction + * @throws OSS_PaymentProcessor_Realex_Exception + */ + static public function createOrderId( $rtrans ) + { + if( !( $rtrans instanceof \Entities\RealexTransaction ) ) + throw new OSS_PaymentProcessor_Realex_Exception( OSS_PaymentProcessor_Realex_Exception::ERR_BAD_OBJECT_FOR_REF ); + + // Realex has a limitation of a maximum of 40 characters. I don't think this'll be an issue... + return 'O_' . $rtrans->getId(); + } + + + /** + * Creates a unique Realex payer reference ID + * + * All payers that we create must be identifiable by us with a unique ID. For this we + * use the integer primary key from the \Etnities\Customer object. + * + * Later this ID can be used in RealEFT transactions for recurring payments, etc. + * + * @param \Entities\Customer $customer The instance of the payer's \Entities\Customer object + * @return string The unique payer ID + */ + static public function createPayerRef( $customer ) + { + if( !( $customer instanceof \Entities\Customer ) ) + throw new OSS_PaymentProcessor_Realex_Exception( OSS_PaymentProcessor_Realex_Exception::ERR_BAD_OBJECT_FOR_REF ); + + // maximum 50 characters + return 'P_' . $customer->getId(); + } + + + /** + * Created a unique Relaex credit card reference ID + * + * All cards added to the Realex store (linked to a payer) must be + * uniquely identifiable with a unique key. We use the integer primary + * key from the \Entities\RealexCard table. + * + * These cards are added to payers in the Realex system. + * + * @param \Entities\RealexCard $card The instance of the \Entities\RelaexCard object + * @return string The unqiue card ID + * @see OSS_PaymentProcessor_Realex::createCreditCardRef() + */ + static public function createCardRef( $card ) + { + if( !( $card instanceof \Entities\RealexCard ) ) + throw new OSS_PaymentProcessor_Realex_Exception( OSS_PaymentProcessor_Realex_Exception::ERR_BAD_OBJECT_FOR_REF ); + + // maximum 30 characters + return 'C_' . $card->getId(); + } + + + /** + * Sends an XML formatted request to Realex using cURL + * + * This function does the heavy lifting for sending a request to + * Realex and parsing and processing the reponse. Specifically: + * + * - creates the cURL object (and throws an exception if it cannot) + * - sends the request to Realex + * + * If the request fails: + * - updates the $rtrans object as STATE_FAILED or STATE_TIMEOUT + * - returns false + * + * If the request succeeds: + * - parses and processes the response (@see _parseResponse()) + * - returns true + * + * @param \Entities\RealexTransaction $rtrans The instance of \Entities\RealexTransaction to send to Realex + * @param string $reqXML The XML request package to send to Realex + * @return bool True if the API/CGI request succeeded (IT DOES NOT MEAN THE REALEX TRANSACTION SUCCEEDED!) + * @throws OSS_PaymentProcessor_Realex_Exception + */ + private function _sendRequest( $rtrans, $reqXML ) + { + $curl = @curl_init(); + + if( $curl === false ) + throw new OSS_PaymentProcessor_Realex_Exception( OSS_PaymentProcessor_Realex_Exception::ERR_CURL_INSTANTIATION_FAILED ); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::_sendRequest() - cURL initialised" ); + + @curl_setopt( $curl, CURLOPT_URL, $this->_cgi_url ); + @curl_setopt( $curl, CURLOPT_POST, 1 ); + @curl_setopt( $curl, CURLOPT_USERAGENT, $this->_user_agent ); + @curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 ); + @curl_setopt( $curl, CURLOPT_POSTFIELDS, $reqXML ); + @curl_setopt( $curl, CURLOPT_TIMEOUT, $this->_cgi_timeout ); + + $respXML = @curl_exec( $curl ); + + if( $respXML === false ) + { + if( @curl_getinfo( $curl, CURLINFO_TOTAL_TIME ) >= $this->_cgi_timeout ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::_sendRequest() - cURL request timeout.", OSS_Log::ALERT ); + $rtrans->setState( \Entities\RealexTransaction::STATE_TIMEOUT ); + } + else + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::_sendRequest() - cURL request failed.", OSS_Log::ALERT ); + $rtrans->setState( \Entities\RealexTransaction::STATE_FAILED ); + } + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + @curl_close( $curl ); + return false; + } + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::_sendRequest() - cURL request executed. Response:\n\n{$respXML}\n\n" ); + @curl_close( $curl ); + + $this->_parseResponse( $respXML, $rtrans ); + + return true; + } + + + /** + * Parse the XML response from Realex and update the transaction object + * + * This function: + * + * - transforms the XML response to an array + * - validates the response hash + * - updates the authcode, pasref, state and response fields of the \Entities\RealexTransaction object + * + * @param string $xml The XML response from Realex for processing + * @param \Entities\RealexTransaction $rtrans An instance of the \Entities\RealexTransaciton object + * @return \Entities\ReleaxTransaction The updated $rtrans object for fluent interface + * @throws OSS_PaymentProcessor_Realex_Receipt_Exception + */ + private function _parseResponse( $xml, $rtrans ) + { + $resp = OSS_Array::objectToArray( OSS_Utils::parseXML( $xml ) ); + + $rtrans->setResult( $this->_checkResponse( $resp ) ); + + if( isset( $resp['authcode'] ) ) + $rtrans->setAuthcode( $resp['authcode'] ); + + //if( isset( $resp['pasref'] ) ) + // $rtrans['pasref'] = $resp['pasref']; + + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::_parseResponse() - Realex result: {$rtrans->getResult()}" ); + + //FIXME: Barry + if( $rtrans->isSuccessful() && !$this->_keep_request_data ) + $rtrans->setRequest( '' ); + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + //FIXME Transaction stats. + //if( !CreditcardTransactionStatsTable::update( $cctrans['request_type'], $cctrans['result'], $cctrans['amount'] ) ) + //$this->_log( "CreditcardTrasactionStatTable::update() failed for [CCTRANS: {$cctrans['id']}].", OSS_Log::ALERT ); + + if( in_array( $rtrans->getRequestType(), array( 'receipt-in', 'payment-out' ) ) && !$rtrans->isSuccessful() ) + throw new OSS_PaymentProcessor_Realex_Receipt_Exception( $rtrans, $resp['message'] ); + + return $rtrans; + } + + + /** + * Takes a Realex response and returns with its response code after additional checks + * + * Outside of the possible checks that Realex performs, we need to allow for one more: an invalid + * hash of the received XML response indicated a bad transmission / man in the middle attack. + * + * @param string $resp The parsed Realex XML response as an array + * @param string The response code (NB: STRING) + */ + private function _checkResponse( $resp ) + { + /* WARNING: This part of the Realex response is kind of messy, because the 'sha1hash' field is either present or not, so + we cannot depend on it's existence. If the code below ( most likely in OSS_PaymentProcessor_Realex_Hash::response() ) still + fails, then use this instead: + + if( $resp['result'] != '00' ) + return $resp['result']; + + if( OSS_PaymentProcessor_Realex_Hash::response( $resp, $this->_merchant_secret ) != $resp['sha1hash'] ) + return self::ERR_RESPONSE_INVALID_HASH; // Response Authentication Failed + */ + + if( isset( $resp['sha1hash'] ) && ( OSS_PaymentProcessor_Realex_Hash::response( $resp, $this->_merchant_secret ) != $resp['sha1hash'] ) ) + return self::ERR_RESPONSE_INVALID_HASH; // Response Authentication Failed + + return $resp['result']; + } + + + /** + * Set fake CreditcardTransactions entries for a complete fake transaction + * + * If $this->_fake_transactions is set to true, the system will + * not perform any Realex requests but fake the transactions in + * the database meaning we: + * + * - set is_fake to true + * - set state to STATE_COMPLETE + * - set pasref and authcode to FAKETRANS + * + * @param \Entities\RealexTransaction $rtrans The transaction object to create the fake entries in + * @return \Entities\RealexTransaction The modified (and save()'d) transaction object + */ + private function _completeFakeTransation( $rtrans ) + { + $rtrans->setIsFake( true ); + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + if( !$this->_keep_request_data ) + $rtrans->setRequest( '' ); + + //$cctrans['pasref'] = 'FAKETRANS'; //FIXME Pasref + $rtrans->setAuthcode( 'FAKETRANS' ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setResult( '00' ); + + $this->getD2EM()->flush(); + + //FIXME + //if( !CreditcardTransactionStatsTable::update( $cctrans['request_type'], $cctrans['result'], $cctrans['amount'] ) ) + //$this->_log( "CreditcardTrasactionStatTable::update() failed for[CCTRANS: {$cctrans['id']}].", OSS_Log::ALERT ); + + return $rtrans; + } + + /** + * Creates a new "payer" in Realex's systems. + * + * Sends a 'payer-new' request to Realex which creates a new payer at Realex + * which later can be used by RealEFT for recurring payments ('receipt-in'). + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'payer-new' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * An example usage is: + * + * + * $payer = $this->getD2Em()->getRepostory( '\\Entities\\RealexPayer' )->find( $id ); + * ... + * $rtrans = $this->getPaygate()->newPayer( $payer ); + * + * // if we're interested in the Realex result then: + * if( $rtrans->isSuccessful() ) + * // do something + * + * + * @param \Entities\Realex $payer The payer who will be associated as payer in Realex + * @return \Entities\RealexTransaction The resultant \Entities\RealexTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function newPayer( $payer ) + { + $timestamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $payer); + $rtrans->setRequestType( 'payer-new' ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::newPayer() - transaction set to STATE_INIT" ); + + // we're about to create a Realex payer which means the paygate state should be none + if( $payer->getState() != \Entities\RealexPayer::STATE_NONE ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $orderId = self::createOrderId( $rtrans ); + $payerRef = $payer->getPayerref(); + $hash = OSS_PaymentProcessor_Realex_Hash::payer( $timestamp, $this->getMerchantId(), $orderId, $payerRef, $this->getMerchantSecret() ); + + $reqXML = " + {$this->getMerchantId()} + {$orderId} + + {$payer->getFirstname()} + {$payer->getLastname()} + + {$hash} + "; + + //FIXME: payer first name and last name + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::newPayer() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::newPayer() - faking transaction" ); + $payer->setState( \Entities\RealexPayer::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); + + if( $rtrans->isSuccessful() ) + { + $payer->setState( \Entities\RealexPayer::STATE_INSYNC ); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + + /** + * Creates a new "credit card" in Realex's systems. + * + * Sends a 'card-new' request to Realex which creates a new credit card at Realex + * which later can be used by RealEFT for recurring payments ('receipt-in'). + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'card-new' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * @param \Entities\RealexCard $card The credit card object to add to Realex + * @param string $cardNumber the credit card number, any non digit character is removed from it in the method + * @return \Entities\RealexTransaction The resultant \Entities\RealexTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function newCard( $card ) + { + // we need a payer to add a credit card to + if( $card->getPayer()->getState() == \Entities\RealexPayer::STATE_NONE ) + $this->newPayer( $card->getPayer() ); + + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setRequestType( 'card-new' ); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setAccount( $this->_account ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::newCard() - transaction set to STATE_INIT" ); + + // we're about to create a Realex credit card which means the paygate state should be none + if( $card->getState() != \Entities\RealexCard::STATE_NONE ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $rqOrderId = self::createOrderId( $rtrans ); + $cardRef = $card->getCardref(); + $payerRef = $card->getPayer()->getPayerref(); + $expiryDate = $card->getValidTo()->format( "my"); + $cardType = $this->getCardType( $card->getType() ); + $rqHash = OSS_PaymentProcessor_Realex_Hash::creditCard( + $rqTimeStamp, + $this->getMerchantId(), + $rqOrderId, + $payerRef, + $card->getHolder(), + $card->getNumber(), + $this->getMerchantSecret() + ); + + $reqXML = " + {$this->getMerchantId()} + {$rqOrderId} + + {$cardRef} + {$payerRef} + {$card->getNumber()} + {$expiryDate} + {$card->getHolder()} + {$cardType} + + {$rqHash} + "; + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setRequest( $card->getNumber() ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::newCard() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::newCard() - faking transaction" ); + $card->setState( \Entities\RealexCard::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); + + if( $rtrans->isSuccessful() ) + { + $card->setState( \Entities\RealexCard::State_INSYNC ); + $card->setNumber( substr( $card->getNumber(), 0, 4 ) . '...' . substr( $card->getNumber(), -3 ) ); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + /** + * Creates a new "payment" in Realex's systems. + * + * Sends a 'receipt-in' request to Realex which creates a new payment at Realex. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * @param \Entities\RealexCard $card + * @param int|float $amount the amount to be withdrawn from the card, in the biggest unit of the currency, + * e.g. in euro or dollar and not in cent, then conversion is made inside the method + * @return \Entities\RealexTransaction The resultant \Entities\RealexTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function receiptIn( $card, $amount ) + { + $rqTimeStamp = $this->getTimeStamp(); + + // we're about to perform payment which means that the cerdit card should exist + if( !( $card instanceof \Entities\RealexCard ) ) + { + $this->_log( "PROG ERROR - expecting instance of \Entities\RealexCard", OSS_Log::ERR ); + throw new OSS_PaymentProcessor_Exception( 'System Failure - our technical staff have been notified' ); + } + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setRequestType( 'receipt-in' ); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setAmount( $amount ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::receiptIn() - transaction set to STATE_INIT" ); + + // we're about to perform payment which means that the cerdit card paygate state should be insync + if( $card->getState() != \Entities\RealexCard::STATE_INSYNC ) + { + $rtrans->setResult( 999 ); + throw new OSS_PaymentProcessor_Realex_Receipt_Exception( $rtrans, OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + } + + $amount = (int) ( $amount * 100 ); + $rqOrderId = self::createOrderId( $rtrans ); + $payerRef = $card->getPayer()->getPayerref(); + $cardRef = $card->getCardref(); + $rqHash = OSS_PaymentProcessor_Realex_Hash::payment( + $rqTimeStamp, + $this->getMerchantId(), + $rqOrderId, + $amount, + 'EUR', + $payerRef, + $this->getMerchantSecret() + ); + + $reqXML = " + {$this->getMerchantId()} + {$this->_account} + {$rqOrderId} + {$amount} + {$payerRef} + {$cardRef} + + {$rqHash} + "; + + $rtrans->setRequest( $amount ); + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::receiptIn() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::receiptIn() - faking transaction" ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); // can throw OSS_PaymentProcessor_Realex_Receipt_Exception() + + return $rtrans; + } + + + /** + * Creates a new "refund" in Realex's systems. + * + * Sends a 'receipt-out' request to Realex which creates a new payment at Realex. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * @param \Entities\RealexCard $card + * @param int|float $amount the amount to be withdrawn from the card, in the biggest unit of the currency, + * e.g. in euro or dollar and not in cent, then conversion is made inside the method + * @return CreditcardTransaction The resultant CreditcardTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function paymentOut( $card, $amount ) + { + $rqTimeStamp = $this->getTimeStamp(); + + // we're about to perform payment which means that the cerdit card should exist + if( !( $card instanceof Creditcard ) ) + { + $this->_log( "PROG ERROR - expecting instance of Creditcard", OSS_Log::ERR ); + throw new OSS_PaymentProcessor_Exception( 'System Failure - our technical staff have been notified' ); + } + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setRequestType( 'payment-out' ); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->getcard( $card ); + $rtrans->setAmount( $amount ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::paymentOut() - transaction set to STATE_INIT" ); + + // we're about to perform payment which means that the cerdit card paygate state should be insync + if( $card->etState() != \Entities\RealexCard::STATE_INSYNC ) + { + $rtrans->setResult( 999 ); + throw new OSS_PaymentProcessor_Realex_Receipt_Exception( $rtrans, OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + } + + $amount = (int) ( $amount * 100 ); + $rqOrderId = self::createOrderId( $rtrans ); + $payerRef = $card->getPayer()->getPayerref(); + $cardRef = $card->getCardref(); + $rqHash = OSS_PaymentProcessor_Realex_Hash::payment( + $rqTimeStamp, + $this->getMerchantId(), + $rqOrderId, + $amount, + 'EUR', + $payerRef, + $this->getMerchantSecret() + ); + + $refundHash = OSS_PaymentProcessor_Realex_Hash::refund( $this->getRefundPassword(), $this->getMerchantSecret() ); + + $reqXML =" + " . $this->getMerchantId() . " + {$this->_account} + {$rqOrderId} + {$amount} + {$payerRef} + {$cardRef} + {$rqHash} + {$refundHash} + "; + + $rtrans->setRequest( $amount ); + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::paymentOut() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::paymentOut() - faking transaction" ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); // can throw OSS_PaymentProcessor_Realex_Receipt_Exception() + + return $rtrans; + } + + /** + * Updates a "payer" in Realex's systems. + * + * Sends a 'edit-payer' request to Realex which updates payer data in Realex. + * + * This also logs the transaction into the \Entities\ReaelxTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'edit-payer' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * @param \Entities\RealexPayer $payer + * @return boolean|int the result (true on success, otherwise integer on error) + */ + public function editPayer( $payer ) + { + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $payer ); + $rtrans->setRequestType( 'payer-edit' ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::editPayer() - transaction set to STATE_INIT" ); + + // we're about to update a Realex payer which means the paygate state should be dirty + if( $payer->getState() != \Entities\RealexPayer::STATE_DIRTY ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $payerRef = $payer->getPayerref(); + $rqOrderId = self::createOrderId( $rtrans ); + $rqHash = OSS_PaymentProcessor_Realex_Hash::payer( $rqTimeStamp, $this->getMerchantId(), $rqOrderId, $payerRef, $this->getMerchantSecret() ); + + $reqXML = " + {$this->getMerchantId()} + {$rqOrderId} + + {$payer->getFirstname()} + {$payer->getLastname()} + + {$rqHash} + "; + //FIXME: Firstname Lastname + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::editPayer() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::editPayer() - faking transaction" ); + $payer->SetState( \Entities\RealexPayer::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); + + if( $rtrans->isSuccessful() ) + { + $payer->setState( \Entities\RealexPayer::STATE_INSYNC ); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + /** + * Updates a "credit card" in Realex's systems. + * + * Sends a 'update-card' request to Realex which updates credit card data in Realex. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'edit-payer' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * @param \Entities\RealexCard $card + * @return boolean|int true on success, otherwise an error code + */ + public function updateCard( $card ) + { + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setRequestType( 'card-update-card' ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setAccount( $this->_account ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::updateCard() - transaction set to STATE_INIT" ); + + // we're about to update a Realex credit card which means the paygate state should be dirty + if( $card->getState() != \Entities\RealexCard::STATE_DIRTY ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $expiryDate = $expiryDate = $card->getValidTo()->format( "my" ); + $cardRef = $card->getCardref(); + $payerRef = $card->getPayer()->getPayerref(); + $cardType = $this->getCardType( $card->getType() ); + $rqHash = OSS_PaymentProcessor_Realex_Hash::updateCreditCard( + $rqTimeStamp, + $this->getMerchantId(), + $payerRef, + $cardRef, + $expiryDate, + $this->getMerchantSecret() + ); + + $reqXML = " + {$this->getMerchantId()} + + {$cardRef} + {$payerRef} + {$card->getHolder()} + {$expiryDate} + {$cardType} + + {$rqHash} + "; + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::updateCard() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::updateCard() - faking transaction" ); + $card->setState( \Entities\RealexCard::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); + + if( $rtrans->isSuccessful() ) + { + $card->stateState( \Entitites\RealexCard::STATE_INSYNC ); + $this->getD2EM()->flush(); + } + + return $rtrans; + + } + + /** + * Removes a "card" from Realex's systems. + * + * Sends a 'card-cancel-card' request to Realex which remove credit card from Realex. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * @param \Entities\RealexCard $card + * @return CreditcardTrasaction $rtrans + */ + public function cancelCard( $card ) + { + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setRequestType( 'card-cancel-card' ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setAccount( $this->_account ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + + if( $card->getState() == \Entities\RealexCard::STATE_NONE ) + { + $rtrans->setResult( '00' ); + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + return $rtrans; + } + + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::cancelCard() - transaction set to STATE_INIT" ); + + $cardRef = $card->getCardref(); + $payerRef = $card->getPayer()->getPayerref(); + $rqHash = OSS_PaymentProcessor_Realex_Hash::removeCreditCard( $rqTimeStamp, $this->getMerchantId(), $payerRef, $cardRef, $this->getMerchantSecret() ); + + $reqXML = " + {$this->getMerchantId()} + + {$cardRef} + {$payerRef} + {$card->getHolder()} + + {$rqHash} + "; + + $rtrans->getState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::cancelCard() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::cancelCard() - faking transaction" ); + $card->setState( \Entities\RealexCard::STATE_NONE ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); + + if( $rtrans->isSuccessful() ) + { + $card->setState( \Entities\RealexCard::STATE_NONE ); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + + /** + * Returns with the realEx code for credit card type. + * + * @param string $pCardType see Creditcard::$CREDIT_CARD_TYPES array keys + * @return string + */ + public function getCardType( $cardType ) + { + return isset( self::$CARD_TYPES[ mb_strtolower( trim( $cardType ) ) ] ) ? self::$CARD_TYPES[ mb_strtolower( trim( $cardType ) ) ] : false; + } + + /** + * Takes money from "payer" in Realex's systems. + * + * It's wrapper function for receiptIn function. It takes three parameters. Payer from ho + * it will take money, the amount of money and arrow to transaction object which will be + * created in receiptIn function. + * + * function will return true or false, depends on transaction status. + * + * An example usage is: + * + * + * $rtrans = null; + * $payer = $this->getD2Em()->getRepostory( '\\Entities\\RealexPayer' )->find( $id ); + * ... + * + * if( $this->getPaygate()->getMoneyFromCustomer( $payer, 100, $rtrans ) ) + * // do something + * else + * $result = $rtrans->getResult(); //for other actions. + * + * + * @param \Entities\RealexPayer $payer The payer to take money + * @param int|float $amonut The amount of money to transfer + * @param mixed &$rtrans Arrow to new \Entities\RealexTransaction + * @return bool + * @throws OSS_PaymentProcessor_Exception + * @see self::receiptIn() + */ + public function getMoneyFromCustomer( $payer, $amount, &$rtrans ) + { + $rtrans = $this->receiptIn( $payer->getCard(), $amount ); + + if( $rtrans->isSuccessful() ) + return true; + + return false; + } + + /** + * Gives money from "payer" in Realex's systems. + * + * It's wrapper function for paymentOut function. It takes three parameters. Payer for ho + * it will give money, the amount of money and arrow to transaction object which will be + * created in receiptIn function. + * + * function will return true or false, depends on transaction status. + * + * An example usage is: + * + * + * $rtrans = null; + * $payer = $this->getD2Em()->getRepostory( '\\Entities\\RealexPayer' )->find( $id ); + * ... + * + * if( $this->getPaygate()->getMoneyFromCustomer( $payer, 100, $rtrans ) ) + * // do something + * else + * $result = $rtrans->getResult(); //for other actions. + * + * + * @param \Entities\RealexPayer $payer The payer to give money + * @param int|float $amonut The amount of money to transfer + * @param mixed &$rtrans Arrow to new \Entities\RealexTransaction + * @return bool + * @throws OSS_PaymentProcessor_Exception + * @see self::paymentOut() + */ + public function giveMoneyToCustomer( $payer, $amount, &$rtrans ) + { + $rtrans = $this->paymentOut( $payer->getCard(), $amount ); + + if( $rtrans->isSuccessful() ) + return true; + + return false; + } + +} diff --git a/library/OSS/PaymentProcessor/Realex/Exception.php b/library/OSS/PaymentProcessor/Realex/Exception.php new file mode 100644 index 0000000..c70f1ba --- /dev/null +++ b/library/OSS/PaymentProcessor/Realex/Exception.php @@ -0,0 +1,52 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PaymentProcessor + * @subpackage OSS_PaymentProcessor_Realex + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_PaymentProcessor_Realex_Exception extends Zend_Exception +{ + const ERR_CURL_INSTANTIATION_FAILED = "cURL instanitation failed"; + const ERR_BAD_OBJECT_FOR_REF = "An incorrect object was passed for order / card / payer reference generation"; + const ERR_REQUIRED_PARAMETER_MISSING = "A required parameter is missing"; +} diff --git a/library/OSS/PaymentProcessor/Realex/Hash.php b/library/OSS/PaymentProcessor/Realex/Hash.php new file mode 100644 index 0000000..d59303c --- /dev/null +++ b/library/OSS/PaymentProcessor/Realex/Hash.php @@ -0,0 +1,197 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PaymentProcessor + * @subpackage OSS_PaymentProcessor_Realex + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_PaymentProcessor_Realex_Hash extends OSS_PaymentProcessor_BaseProcessor +{ + + /** + * Calculate the SHA1 hash from a Realex response (as an array) + * + * @param array $resp The parsed Realex XML response as an array + * @param string $merchantId The merchant ID + * @param string $secret The shared secret + * @return string The calculated SHA1 hash + */ + public static function response( $resp, $secret ) + { + $str = $resp['@attributes']['timestamp'] . '.'; + $str .= $resp['merchantid'] . '.'; + $str .= ( isset( $resp['orderid'] ) ? $resp['orderid'] : '' ); + $str .= '.'; + $str .= $resp['result'] . '.'; + $str .= $resp['message'] . '.'; + $str .= $resp['pasref'] . '.'; + $str .= ( isset( $resp['authcode'] ) ? $resp['authcode'] : '' ); + + return sha1( sha1( $str ) . '.' . $secret ); + } + + + /** + * Creates an SHA1 hash for 'payer-new' realEx transaction. + * + * @param string $timeStamp in YYYYmmddhhmmss format + * @param string $merchantId this merchant id in Realex + * @param string $orderId a unique realEx order id + * @param string $payerRef unique payer reference id + * @param string $secret The shared secret + * @return string + */ + public static function payer( $timeStamp, $merchantId, $orderId, $payerRef, $secret ) + { + $str = $timeStamp . '.'; + $str .= $merchantId . '.'; + $str .= $orderId; + $str .= '...'; + $str .= $payerRef; + + return sha1( sha1( $str ) . '.' . $secret ); + } + + + /** + * Creates an SHA1 hash for a 'card-new' realEx transaction. + * + * @param string $timeStamp in YYYYmmddhhmmss format + * @param string $merchantId this merchant id in Realex + * @param string $orderId a unique realEx order id + * @param string $payerRef unique payer reference id + * @param string $cardHolder the card holder's name + * @param string $cardNumber the credit card number + * @param string $secret The shared secret + * @return string + */ + public static function creditCard( $timeStamp, $merchantId, $orderId, $payerRef, $cardHolder, $cardNumber, $secret ) + { + $str = $timeStamp . '.'; + $str .= $merchantId . '.'; + $str .= $orderId; + $str .= '...'; + $str .= $payerRef . '.'; + $str .= $cardHolder . '.'; + $str .= preg_replace( "/\D/", '', $cardNumber ); + return sha1( sha1( $str ) . '.' . $secret ); + } + + + /** + * Creates an SHA1 hash for a 'receipt-in' realEx transaction. + * + * @param string $timeStamp in YYYYmmddhhmmss format + * @param string $merchantId this merchant id in Realex + * @param string $orderId a unique realEx order id + * @param string|int|float $amount the value of the transaction in the smallest unit of the currency, e.g. in cent for euro or dollar + * @param string $payerRef unique payer reference id + * @param string $secret The shared secret + * @return string + */ + public static function payment( $timeStamp, $merchantId, $orderId, $amount, $currency, $payerRef, $secret ) + { + $str = $timeStamp . '.'; + $str .= $merchantId . '.'; + $str .= $orderId . '.'; + $str .= $amount . '.'; + $str .= $currency . '.'; + $str .= $payerRef; + + return sha1( sha1( $str ) . '.' . $secret ); + } + + + /** + * Creates an SHA1 hash for an 'eft-update-expiry-date' realEx transition. + * + * @param string $timeStamp in YYYYmmddhhmmss format + * @param string $merchantId this merchant id in Realex + * @param string $payerRef unique payer reference id + * @param string $creditCardRef unique creditcard reference id + * @param string $validTo the expiry date of the card in 'YYYY-MM' or 'YYYY-mm-dd' format + * @param string $secret The shared secret + * @return string + */ + public static function updateCreditCard( $timeStamp, $merchantId, $payerRef, $creditCardRef, $validTo, $secret ) + { + $str = $timeStamp . '.'; + $str .= $merchantId . '.'; + $str .= $payerRef . '.'; + $str .= $creditCardRef . '.'; + $str .= date( 'm', strtotime( $validTo ) ) . date( 'y', strtotime ($validTo ) ) . '.'; + return sha1( sha1( $str ) . '.' . $secret ); + } + + + /** + * Creates an SHA1 hash for an 'card-cancel-card' realEx transition. + * + * @param string $timeStamp in YYYYmmddhhmmss format + * @param string $merchantId this merchant id in Realex + * @param string $payerRef unique payer reference id + * @param string $creditCardRef unique creditcard reference id + * @param string $secret The shared secret + * @return string + */ + public static function removeCreditCard( $timeStamp, $merchantId, $payerRef, $creditCardRef, $secret ) + { + $str = $timeStamp . '.'; + $str .= $merchantId . '.'; + $str .= $payerRef . '.'; + $str .= $creditCardRef; + + return sha1( sha1( $str ) . '.' . $secret ); + } + + /** + * Creates an refund hash for an 'card-cancel-card' realEx transition. + * @param string $refundPassword refund password assigned by Realex + * @param string $secret The shared secret + * @return string + */ + public static function refund( $refundPassword, $secret ) + { + return sha1( sha1( $refundPassword ) . '.' . $secret ); + } + +} diff --git a/library/OSS/PaymentProcessor/Realex/Receipt/Exception.php b/library/OSS/PaymentProcessor/Realex/Receipt/Exception.php new file mode 100644 index 0000000..817fc94 --- /dev/null +++ b/library/OSS/PaymentProcessor/Realex/Receipt/Exception.php @@ -0,0 +1,134 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PaymentProcessor + * @subpackage OSS_PaymentProcessor_Realex + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_PaymentProcessor_Realex_Receipt_Exception extends Zend_Exception +{ + const ERR_UNABLE_COMMUNICATE = 'Payment failed. We are unable to communicate with our merchant or your bank at this time.'; + const ERR_NON_EXISTANT_CREDIT_CARD = 'Payment failed. You have not added any payment method.'; + const ERR_PAYMENT_DECLINE = 'Payment declined by your bank.'; + + private $_msg = self::ERR_UNABLE_COMMUNICATE; + private $_type = OSS_Message::ERROR; + private $_logMsg = ''; + private $_logLevel = OSS_Log::CRIT; + + + function __construct( $rtrans, $message ) + { + parent::__construct(); + $this->_logMsg = "Payment with [CCTRANS {$rtrans->getId()}] was unsuccessful, got response code {$rtrans->getId()} and message '{$message}'"; + $this->setParams( $rtrans->getResult() ); + } + + + public function toString() + { + return $this->_msg; + } + + + public function __toString() + { + return $this->_msg; + } + + + public function messageType() + { + return $this->_type; + } + + + public function logMessage() + { + return $this->_logMsg; + } + + + public function logLevel() + { + return $this->_logLevel; + } + + private function setParams( $code ) + { + switch( $code ) + { + case '80': + case '101': + case '102': + case '103': + case '106': + case '107': + case '108': + case '109': + $this->_msg = self::ERR_PAYMENT_DECLINE; + $this->_type = OSS_Message::ALERT; + $this->_logLevel = OSS_Log::INFO; + break; + + case '200': + case '202': + case '205': + case '301': + case '302': + case '304': + case '305': + $this->_logLevel = OSS_Log::INFO; + break; + + default: + $this->_msg = self::ERR_UNABLE_COMMUNICATE; + $this->_type = OSS_Message::ERROR; + $this->_logLevel = OSS_Log::DEBUG; + break; + } + + $this->message = $this->_msg; + $this->code = (int) $code; + } + +} diff --git a/library/OSS/PaymentProcessor/Stripe.php b/library/OSS/PaymentProcessor/Stripe.php new file mode 100644 index 0000000..80b95cf --- /dev/null +++ b/library/OSS/PaymentProcessor/Stripe.php @@ -0,0 +1,1099 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_PaymentProcessor + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_PaymentProcessor_Stripe extends OSS_PaymentProcessor_BaseProcessor +{ + const CARD_TYPE_VISA = 'visa'; + const CARD_TYPE_MASTERCARD = 'mastercard'; + + /** + * Valid credit/debit card types + * @var array + */ + public static $CARD_TYPES = [ + self::CARD_TYPE_VISA => 'visa', + self::CARD_TYPE_MASTERCARD => 'mc' + ]; + + + /** + * An array of the mandatory application.ini parameters. + * + * @var array The mandatory application.ini parameters + */ + public static $REQUIRED_PARAMS = [ 'ak_secret', 'currency' ]; + + /** + * An array of the optional application.ini parameters (defaults set with their definitions). + * + * @var array The optional application.ini parameters + */ + public static $OPTIONAL_PARAMS = [ 'fake_transactions', 'ak_public', 'keep_request_data', 'account' ]; + + /** + * The Doctrine2 entity manager + * @var EntityManager The Doctrine2 entity manager + */ + private $_d2em = null; + + /** + * The Stripe API secret key + * @var string The Stripe API secret key + */ + private $_ak_secret; + + /** + * The Stripe API public key + * @var string The Stripe API public key + */ + private $_ak_public; + + /** + * The currency then charging payer ( only 3 characters ) + * @var string The currency then charging payer + */ + private $_currency; + + /** + * The currency then charging payer ( only 3 characters ) + * @var string The currency then charging payer + */ + private $_account = ''; + + + /** + * For security reasons, we clear the request data from the database after a sucessful + * Stripe API/CGI call. + * + * Some transactions require non-stored data (e.g. credit card number) to replay a failed + * transaction (such as createCreditCard()) which we will store and delete when we + * successfully replay the transaction. Setting this to true means we don't remove it. + * + * WARNING: Set to true only for testing! + * + * @var bool Whether we should clear the request data or not + */ + private $_keep_request_data = false; + + /** + * Fake transactions allows all operations to appear to succeed including + * updates to local database tables but it never contacts Stripe and fakes + * all operations. + * + * @var bool Fake all transactions and never contact Stripe + * @see _completeFakeTransaction() + */ + private $_fake_transactions = false; + + /** + * An instance of OSS_Log for logging. + * + * @var OSS_Log An instance of OSS_Log for logging. + */ + private $_logger = null; + + + /** + * Stripe Payment gateway processor + * + * @param array $config An associated array of stripe.* parameters from application.ini + * @param OSS_Log $logger An optional instance of an OSS_Log object if you want logging + * @throws OSS_PaymentProcessor_Realex_Exception + */ + public function __construct( array $config, OSS_Log $logger = null ) + { + if( !class_exists( 'Stripe' ) ) + require_once( 'Stripe.php' ); + + // check for required parameters and set member variables accordingly + foreach( self::$REQUIRED_PARAMS as $param ) + { + if( !isset( $config["{$param}"] ) ) + { + throw new OSS_PaymentProcessor_Realex_Exception( + OSS_PaymentProcessor_Realex_Exception::ERR_REQUIRED_PARAMETER_MISSING . ' - ' . $param + ); + } + + $member = "_{$param}"; + + $this->$member = $config[ $param ]; + } + + // check for optional parameters and set member variables accordingly + foreach( self::$OPTIONAL_PARAMS as $param ) + { + if( isset( $config[ "{$param}" ] ) ) + { + $member = "_{$param}"; + $this->$member = $config[ $param ]; + } + } + + $this->_logger = $logger; + Stripe::setApiKey( $this->_ak_secret ); + } + + /** + * Access method for the merchant ID + * + * @return string The merchant ID + * @see $_merchant_id + */ + protected function getD2EM() + { + if( !$this->_d2em ) + $this->_d2em = Zend_Registry::get( "d2em" )[ 'default' ]; + + return $this->_d2em; + } + + /** + * Returns with a YYYYmmddhhmmss format timestamp used in Stripe transactions. + * + * @return string YYYYmmddhhmmss formated timestamp + */ + static public function getTimeStamp() + { + return date( 'YmdHis' ); + } + + + /** + * Log a message if we have an instance of a logger + * + * @param string $msg The log message + * @param int $pri The priority of the message (defaults: OSS_Log::DEBUG) + */ + private function _log( $msg, $pri = OSS_Log::DEBUG ) + { + if( $this->_logger instanceof OSS_Log ) + $this->_logger->log( $msg, $pri ); + } + + /** + * Creates a unique Stripe payer reference ID which will be replaced after adding it to Stripe system + * + * All payers that we create must be identifiable by us with a unique ID. For this we + * use the integer primary key from the \Etnities\Customer object. + * + * Later this ID can be used in RealEFT transactions for recurring payments, etc. + * + * @param \Entities\Customer $customer The instance of the payer's \Entities\Customer object + * @return string The unique payer ID + */ + static public function createPayerRef( $customer ) + { + if( !( $customer instanceof \Entities\Customer ) ) + throw new OSS_PaymentProcessor_Realex_Exception( OSS_PaymentProcessor_Realex_Exception::ERR_BAD_OBJECT_FOR_REF ); + + // maximum 50 characters + return 'P_' . $customer->getId(); + } + + + /** + * Created a unique Stripe credit card reference ID which will be replaced after adding it to Stripe system + * + * All cards added to the Stripe store (linked to a payer) must be + * uniquely identifiable with a unique key. We use the integer primary + * key from the \Entities\RealexCard table. + * + * These cards are added to payers in the Stripe system. + * + * @param \Entities\RealexCard $card The instance of the \Entities\RelaexCard object + * @return string The unqiue card ID + * @see OSS_PaymentProcessor_Realex::createCreditCardRef() + */ + static public function createCardRef( $card ) + { + if( !( $card instanceof \Entities\RealexCard ) ) + throw new OSS_PaymentProcessor_Realex_Exception( OSS_PaymentProcessor_Realex_Exception::ERR_BAD_OBJECT_FOR_REF ); + + // maximum 30 characters + return 'C_' . $card->getId(); + } + + /** + * Set fake CreditcardTransactions entries for a complete fake transaction + * + * If $this->_fake_transactions is set to true, the system will + * not perform any Stripe requests but fake the transactions in + * the database meaning we: + * + * - set is_fake to true + * - set state to STATE_COMPLETE + * - set pasref and authcode to FAKETRANS + * + * @param \Entities\RealexTransaction $rtrans The transaction object to create the fake entries in + * @return \Entities\RealexTransaction The modified (and save()'d) transaction object + */ + private function _completeFakeTransation( $rtrans ) + { + $rtrans->setIsFake( true ); + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + if( !$this->_keep_request_data ) + $rtrans->setRequest( '' ); + + //$cctrans['pasref'] = 'FAKETRANS'; //FIXME Pasref + $rtrans->setAuthcode( 'FAKETRANS' ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setResult( '00' ); + + $this->getD2EM()->flush(); + + //FIXME + //if( !CreditcardTransactionStatsTable::update( $cctrans['request_type'], $cctrans['result'], $cctrans['amount'] ) ) + //$this->_log( "CreditcardTrasactionStatTable::update() failed for[CCTRANS: {$cctrans['id']}].", OSS_Log::ALERT ); + + return $rtrans; + } + + /** + * Stripe Exception handler + * + * Function will check the exception is instance of Stripe_ERROR. If it is instance of Stripe_ERROR then + * function will chek the type. If type is card_error then it sets result to 80 and saves the message to rtrans + * reference. otherwise it sets to 85 other stripe issue. If exception is not stripe one set result to 90. + * + * @param Exception $e Exception object + * @param string $function function name to complete the logs. + * @return void + */ + private function exceptionHendler( $e, $rtrans, $function ) + { + $error = $e->getJsonBody()['error']; + if( $e instanceof Stripe_Error) + { + if( $error['type'] == 'card_error' ) + { + $rtrans->setResult( '80' ); + $rtrans->setReference( $error['message'] ); + } + else + $rtrans->setResult( '85' ); + } + else + $rtrans->setResult( '90' ); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::{$function}() - result: exception: " . print_r( $error, true ) ); + } + + /** + * Creates a new "payer" in Stripe's systems. + * + * Sends a 'payer-new' request to Stripe which creates a new payer at Stripe + * which later can be used by Stripe for recurring payments ('receipt-in'). + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Stripe request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'payer-new' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * An example usage is: + * + * + * $payer = $this->getD2Em()->getRepostory( '\\Entities\\RealexPayer' )->find( $id ); + * ... + * $rtrans = $this->getPaygate()->newPayer( $payer ); + * + * // if we're interested in the Stripe result then: + * if( $rtrans->isSuccessful() ) + * // do something + * + * + * @param \Entities\RealexPayer $payer The payer who will be associated as payer in Stripe + * @return \Entities\RealexTransaction The resultant \Entities\RealexTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function newPayer( $payer ) + { + $timestamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $payer); + $rtrans->setRequestType( 'payer-new' ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newPayer() - transaction set to STATE_INIT" ); + + // we're about to create a Realex payer which means the paygate state should be none + if( $payer->getState() != \Entities\RealexPayer::STATE_NONE ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $reqData = [ + 'description' => "{{$payer->getCustomer()->getName()}, cid {$payer->getId()}}" + ]; + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newPayer() - transaction set to STATE_PRESEND\n\n" . print_r( $reqData, true ) . "\n\n" ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newPayer() - faking transaction" ); + $payer->setState( \Entities\RealexPayer::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + try{ + $res = Stripe_Customer::create( $reqData ); + $rtrans->setResult( '00' ); + + $payer->setPayerref( $res->id ); + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newPayer() - Stripe result: " . print_r( $res, true ) ); + } + catch( Exception $e ) + { + $this->exceptionHendler( $e, $rtrans, "newPayer" ); + } + + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + if( $rtrans->isSuccessful() ) + { + $payer->setState( \Entities\RealexPayer::STATE_INSYNC ); + $payer->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + + /** + * Creates a new "credit card" in Stripe's systems. + * + * Sends a 'card-new' request to Stripe which creates a new credit card at Stripe + * which later can be used by RealEFT for recurring payments ('receipt-in'). + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Stripe request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'card-new' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * @param \Entities\RealexCard $card The credit card object to add to Stripe + * @param string $cardNumber the credit card number, any non digit character is removed from it in the method + * @return \Entities\RealexTransaction The resultant \Entities\RealexTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function newCard( $card ) + { + // we need a payer to add a credit card to + if( $card->getPayer()->getState() == \Entities\RealexPayer::STATE_NONE ) + $this->newPayer( $card->getPayer() ); + + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setRequestType( 'card-new' ); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setAccount( $this->_account ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newCard() - transaction set to STATE_INIT" ); + + // we're about to create a Realex credit card which means the paygate state should be none + if( $card->getState() != \Entities\RealexCard::STATE_NONE ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $reqData = [ + 'card' => [ + 'number' => $card->getNumber(), + 'exp_month' => $card->getValidTo()->format( "m" ), + 'exp_year' => $card->getValidTo()->format( "y" ), + 'cvc' => $card->getCvv(), + 'name' => $card->getHolder() + ] + ]; + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setRequest( $card->getNumber() ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newCard() - transaction set to STATE_PRESEND\n\n" . print_r( $reqData, true ) . "\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newCard() - faking transaction" ); + $card->setState( \Entities\RealexCard::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + try{ + $cu = Stripe_Customer::retrieve( $card->getPayer()->getPayerref() ); + $res = $cu->cards->create( $reqData ); + $rtrans->setResult( '00' ); + + $card->setCardref( $res->id ); + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::newCard() - Stripe result: " . print_r( $res, true ) ); + } + catch( Exception $e ) + { + $this->exceptionHendler( $e, $rtrans, "newCard" ); + } + + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + //FIXME: Barry + if( $rtrans->isSuccessful() && !$this->_keep_request_data ) + $rtrans->setRequest( '' ); + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + if( $rtrans->isSuccessful() ) + { + $card->setState( \Entities\RealexCard::STATE_INSYNC ); + $card->setNumber( substr( $card->getNumber(), 0, 4 ) . '...' . substr( $card->getNumber(), -4 ) ); + $card->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + /** + * Creates a new "payment" in Stripe's systems. + * + * Sends a 'receipt-in' request to Stripe which creates a new payment at Stripe. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Stripe request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * @param \Entities\RealexCard $card + * @param int|float $amount the amount to be withdrawn from the card, in the biggest unit of the currency, + * e.g. in euro or dollar and not in cent, then conversion is made inside the method + * @param string $description Payment description. e.g. For services of period from 08/13 unti 09/13 + * @return \Entities\RealexTransaction The resultant \Entities\RealexTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function receiptIn( $card, $amount, $description = "" ) + { + $rqTimeStamp = $this->getTimeStamp(); + + // we're about to perform payment which means that the cerdit card should exist + if( !( $card instanceof \Entities\RealexCard ) ) + { + $this->_log( "PROG ERROR - expecting instance of \Entities\RealexCard", OSS_Log::ERR ); + throw new OSS_PaymentProcessor_Exception( 'System Failure - our technical staff have been notified' ); + } + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setRequestType( 'receipt-in' ); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setAmount( $amount ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::receiptIn() - transaction set to STATE_INIT" ); + + // we're about to perform payment which means that the cerdit card paygate state should be insync + if( $card->getState() != \Entities\RealexCard::STATE_INSYNC ) + { + $rtrans->setResult( 999 ); + throw new OSS_PaymentProcessor_Realex_Receipt_Exception( $rtrans, OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + } + + $reqData = [ + 'amount' => (int) ( $amount * 100 ), + 'currency' => strtolower( $this->_currency ), + 'card' => $card->getCardref(), + 'customer' => $card->getPayer()->getPayerref(), + 'capture' => true, + 'description' => $description + ]; + + $rtrans->setRequest( $amount ); + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::receiptIn() - transaction set to STATE_PRESEND\n\n" . print_r( $reqData, true ) . "\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::receiptIn() - faking transaction" ); + return $this->_completeFakeTransation( $rtrans ); + } + + try{ + $res = Stripe_Charge::create( $reqData ); + $rtrans->setResult( '00' ); + $rtrans->setReference( $res['id'] ); + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::receiptIn() - Stripe result: " . print_r( $res, true ) ); + } + catch( Exception $e ) + { + $this->exceptionHendler( $e, $rtrans, "receiptIn" ); + } + + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + //FIXME: Barry + if( $rtrans->isSuccessful() && !$this->_keep_request_data ) + $rtrans->setRequest( '' ); + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + return $rtrans; + } + + + /** + * Creates a new "refund" in Realex's systems. + * + * Sends a 'receipt-out' request to Realex which creates a new payment at Realex. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * @param \Entities\RealexCard $card + * @param int|float $amount the amount to be withdrawn from the card, in the biggest unit of the currency, + * e.g. in euro or dollar and not in cent, then conversion is made inside the method + * @return CreditcardTransaction The resultant CreditcardTransaction object + * @throws OSS_PaymentProcessor_Exception + */ + public function paymentOut( $card, $amount ) + { + /*$rqTimeStamp = $this->getTimeStamp(); + + // we're about to perform payment which means that the cerdit card should exist + if( !( $card instanceof Creditcard ) ) + { + $this->_log( "PROG ERROR - expecting instance of Creditcard", OSS_Log::ERR ); + throw new OSS_PaymentProcessor_Exception( 'System Failure - our technical staff have been notified' ); + } + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setRequestType( 'payment-out' ); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->getcard( $card ); + $rtrans->setAmount( $amount ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Realex::paymentOut() - transaction set to STATE_INIT" ); + + // we're about to perform payment which means that the cerdit card paygate state should be insync + if( $card->etState() != \Entities\RealexCard::STATE_INSYNC ) + { + $rtrans->setResult( 999 ); + throw new OSS_PaymentProcessor_Realex_Receipt_Exception( $rtrans, OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + } + + $amount = (int) ( $amount * 100 ); + $rqOrderId = self::createOrderId( $rtrans ); + $payerRef = $card->getPayer()->getPayerref(); + $cardRef = $card->getCardref(); + $rqHash = OSS_PaymentProcessor_Realex_Hash::payment( + $rqTimeStamp, + $this->getMerchantId(), + $rqOrderId, + $amount, + 'EUR', + $payerRef, + $this->getMerchantSecret() + ); + + $refundHash = OSS_PaymentProcessor_Realex_Hash::refund( $this->getRefundPassword(), $this->getMerchantSecret() ); + + $reqXML =" + " . $this->getMerchantId() . " + {$this->_account} + {$rqOrderId} + {$amount} + {$payerRef} + {$cardRef} + {$rqHash} + {$refundHash} + "; + + $rtrans->setRequest( $amount ); + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::paymentOut() - transaction set to STATE_PRESEND\n\n{$reqXML}\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::paymentOut() - faking transaction" ); + return $this->_completeFakeTransation( $rtrans ); + } + + $this->_sendRequest( $rtrans, $reqXML ); // can throw OSS_PaymentProcessor_Realex_Receipt_Exception() + + return $rtrans;*/ + } + + /** + * Updates a "payer" in Realex's systems. + * + * Sends a 'edit-payer' request to Realex which updates payer data in Realex. + * + * This also logs the transaction into the \Entities\ReaelxTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'edit-payer' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * @param \Entities\RealexPayer $payer + * @return boolean|int the result (true on success, otherwise integer on error) + */ + public function editPayer( $payer ) + { + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $payer ); + $rtrans->setRequestType( 'payer-edit' ); + $rtrans->setAccount( $this->_account ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::editPayer() - transaction set to STATE_INIT" ); + + // we're about to update a Realex payer which means the paygate state should be dirty + if( $payer->getState() != \Entities\RealexPayer::STATE_DIRTY ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $reqData = [ + 'description' => "{$payer->getName()}, cid {$payer->getId()}}" + ]; + + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::editPayer() - transaction set to STATE_PRESEND\n\n" . print_r( $reqData, true ) ."\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::editPayer() - faking transaction" ); + $payer->SetState( \Entities\RealexPayer::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + try{ + $cu = Stripe_Customer::retrieve( $card->getPayer()->getPayerref() ); + + foreach( $reqData as $name => $value ) + $cu->$name = $value; + + $cu->save(); + $rtrans->setResult( '00' ); + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::editPayer() - Stripe result: " . print_r( $res, true ) ); + } + catch( Exception $e ) + { + $this->exceptionHendler( $e, $rtrans, "editPayer" ); + } + + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + if( $rtrans->isSuccessful() ) + { + $payer->setState( \Entities\RealexPayer::STATE_INSYNC ); + $payer->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + /** + * Updates a "credit card" in Realex's systems. + * + * Sends a 'update-card' request to Realex which updates credit card data in Realex. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Realex request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * It doesn't matter if this transaction fails. All the information we need is in + * the database. The end user does not want to know about it. We'll pick up + * unsuccessful 'edit-payer' in offline processing. Any serious / critical issues + * will have thrown an exception. + * + * @param \Entities\RealexCard $card + * @return boolean|int true on success, otherwise an error code + */ + public function updateCard( $card ) + { + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setRequestType( 'card-update-card' ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setAccount( $this->_account ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::updateCard() - transaction set to STATE_INIT" ); + + // we're about to update a Realex credit card which means the paygate state should be dirty + if( $card->getState() != \Entities\RealexCard::STATE_DIRTY ) + throw new OSS_PaymentProcessor_Exception( OSS_PaymentProcessor_Exception::ERR_INCONSISTANT_PAYGATE_STATE ); + + $reqData = [ + 'exp_month' => $card->getValidTo()->format( "m" ), + 'exp_year' => $card->getValidTo()->format( "Y" ), + 'name' => $card->getHolder(), + ]; + + $rtrans->setState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::updateCard() - transaction set to STATE_PRESEND\n\n" . print_r( $reqData, true ) ."\n\n" ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::updateCard() - faking transaction" ); + $card->setState( \Entities\RealexCard::STATE_INSYNC ); + return $this->_completeFakeTransation( $rtrans ); + } + + try{ + $cu = Stripe_Customer::retrieve( $card->getPayer()->getPayerref() ); + $crd = $cu->cards->retrieve( $card->getCardref() ); + + foreach( $reqData as $name => $value ) + $crd->$name = $value; + + $res = $crd->save(); + $rtrans->setResult( '00' ); + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::updateCard() - Stripe result: " . print_r( $res, true ) ); + } + catch( Exception $e ) + { + $this->exceptionHendler( $e, $rtrans, "updateCard" ); + } + + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + if( $rtrans->isSuccessful() ) + { + $card->setState( \Entities\RealexCard::STATE_INSYNC ); + $card->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + } + + return $rtrans; + + } + + /** + * Removes a "card" from Stripe's systems. + * + * Sends a 'card-cancel-card' request to Stripe which remove credit card from Stripe. + * + * This also logs the transaction into the \Entities\RealexTransaction table, + * and returns that object which can be queried for the request and transaction + * state. + * + * If $this->_fake_transactions is true then it skips the Stripe request and returns with + * success after performing all normal database operations as if the request was sent. + * @see $_fake_transactions + * + * @param \Entities\RealexCard $card + * @return CreditcardTrasaction $rtrans + */ + public function cancelCard( $card ) + { + $rqTimeStamp = $this->getTimeStamp(); + + $rtrans = new \Entities\RealexTransaction(); + $rtrans->setPayer( $card->getPayer() ); + $rtrans->setCard( $card ); + $rtrans->setRequestType( 'card-cancel-card' ); + $rtrans->setState( \Entities\RealexTransaction::STATE_INIT ); + $rtrans->setAccount( $this->_account ); + $rtrans->setRequest( "" ); + $rtrans->setCreated( new \DateTime() ); + $rtrans->setUpdated( new \DateTime() ); + $rtrans->setIsFake( 0 ); + + if( $card->getState() == \Entities\RealexCard::STATE_NONE ) + { + $rtrans->setResult( '00' ); + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + return $rtrans; + } + + $this->getD2EM()->persist( $rtrans ); + $this->getD2EM()->flush(); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::cancelCard() - transaction set to STATE_INIT" ); + + $rtrans->getState( \Entities\RealexTransaction::STATE_PRESEND ); + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + $this->_log( + sprintf( "[RTRANS: %s] Stripe::cancelCard() - transaction set to STATE_PRESEND\n\n [ customer => '%s', card => '%s' ] \n\n", + $rtrans->getId(), + $card->getPayer()->getPayerref(), + $card->getCardref() + ) + ); + + if( $this->_fake_transactions ) + { + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::cancelCard() - faking transaction" ); + $card->setState( \Entities\RealexCard::STATE_NONE ); + return $this->_completeFakeTransation( $rtrans ); + } + + try{ + $cu = Stripe_Customer::retrieve( $card->getPayer()->getPayerref() ); + $res = $cu->cards->retrieve( $card->getCardref() )->delete(); + $rtrans->setResult( '00' ); + + $this->_log( "[RTRANS: {$rtrans->getId()}] Stripe::cancelCard() - Stripe result: " . print_r( $res, true ) ); + } + catch( Exception $e ) + { + $this->exceptionHendler( $e, $rtrans, "cancelCard" ); + } + + $rtrans->setState( \Entities\RealexTransaction::STATE_COMPLETE ); + + $rtrans->setUpdated( new \DateTime() ); + $this->getD2EM()->flush(); + + if( $rtrans->isSuccessful() ) + { + $card->setState( \Entities\RealexCard::STATE_NONE ); + $card->setUpdated(); + $this->getD2EM()->flush(); + } + + return $rtrans; + } + + + /** + * Returns with the realEx code for credit card type. + * + * @param string $pCardType see Creditcard::$CREDIT_CARD_TYPES array keys + * @return string + */ + public function getCardType( $cardType ) + { + return isset( self::$CARD_TYPES[ mb_strtolower( trim( $cardType ) ) ] ) ? self::$CARD_TYPES[ mb_strtolower( trim( $cardType ) ) ] : false; + } + + /** + * Takes money from "payer" in Realex's systems. + * + * It's wrapper function for receiptIn function. It takes three parameters. Payer from ho + * it will take money, the amount of money and arrow to transaction object which will be + * created in receiptIn function. + * + * function will return true or false, depends on transaction status. + * + * An example usage is: + * + * + * $rtrans = null; + * $payer = $this->getD2Em()->getRepostory( '\\Entities\\RealexPayer' )->find( $id ); + * ... + * + * if( $this->getPaygate()->getMoneyFromCustomer( $payer, 100, $rtrans ) ) + * // do something + * else + * $result = $rtrans->getResult(); //for other actions. + * + * + * @param \Entities\RealexPayer $payer The payer to take money + * @param int|float $amonut The amount of money to transfer + * @param mixed &$rtrans Arrow to new \Entities\RealexTransaction + * @return bool + * @throws OSS_PaymentProcessor_Exception + * @see self::receiptIn() + */ + public function getMoneyFromCustomer( $payer, $amount, &$rtrans ) + { + $rtrans = $this->receiptIn( $payer->getCard(), $amount ); + + if( $rtrans->isSuccessful() ) + return true; + + return false; + } + + /** + * Gives money from "payer" in Realex's systems. + * + * It's wrapper function for paymentOut function. It takes three parameters. Payer for ho + * it will give money, the amount of money and arrow to transaction object which will be + * created in receiptIn function. + * + * function will return true or false, depends on transaction status. + * + * An example usage is: + * + * + * $rtrans = null; + * $payer = $this->getD2Em()->getRepostory( '\\Entities\\RealexPayer' )->find( $id ); + * ... + * + * if( $this->getPaygate()->getMoneyFromCustomer( $payer, 100, $rtrans ) ) + * // do something + * else + * $result = $rtrans->getResult(); //for other actions. + * + * + * @param \Entities\RealexPayer $payer The payer to give money + * @param int|float $amonut The amount of money to transfer + * @param mixed &$rtrans Arrow to new \Entities\RealexTransaction + * @return bool + * @throws OSS_PaymentProcessor_Exception + * @see self::paymentOut() + */ + public function giveMoneyToCustomer( $payer, $amount, &$rtrans ) + { + $rtrans = $this->paymentOut( $payer->getCard(), $amount ); + + if( $rtrans->isSuccessful() ) + return true; + + return false; + } + +} diff --git a/library/OSS/Pdf.php b/library/OSS/Pdf.php new file mode 100644 index 0000000..621b900 --- /dev/null +++ b/library/OSS/Pdf.php @@ -0,0 +1,160 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Pdf + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Pdf +{ + /** + * OSS_Html object. + */ + private $_html = null; + + /** + * Constructor + * + * @param object $view Object for rendering HTMLs. + * @param string $viewscript Path to template. + */ + public function __construct( $view, $viewscript = null ) + { + $this->_html = new OSS_Html( $view, $viewscript ); + } + + /** + * Sets view script for rendering + * + * @param string $viewscript Path to template. + * @return OSS_Pdf + */ + public function setViewScript( $viewscript ) + { + $this->_html->setViewScript( $viewscript ); + return $this; + } + + /** + * Creates PDF and returns path. + * + * First it renders and creates HTML file and then it creates pdf file. + * It removes new HTML file and returns path to new pdf file. + * + * PDF file removing is added in all other methods where _processPDF are called. + * + * @return string + */ + private function _processPDF() + { + $ts = date( 'YmdHis' ); + $tn = APPLICATION_PATH . "/../var/tmp/OSS_PDF_" . $ts . '_' . OSS_String::random( 8, true, true, true, '', '' ); + + $fhtml = "{$tn}.html"; + $fpdf = "{$tn}.pdf"; + + if( @file_put_contents( $fhtml, $this->_html->render() ) === false ) + return false; + + $path = Zend_Registry::get('options')['includePaths']['osslibrary'] . "/bin"; + + @exec( escapeshellcmd( $path . "/wkhtmltopdf-amd64 -q '{$fhtml}' '{$fpdf}'" ) ); + + @unlink( $fhtml ); + + if( !file_exists( $fpdf ) || !filesize( $fpdf ) ) + return false; + + return $fpdf; + } + + /** + * Renders the view script + * + * @returns string + */ + public function render() + { + $fpdf = $this->_processPDF(); + $pdf = Zend_Pdf::load( $fpdf ); + $ret = $pdf->render(); + @unlink( $fpdf ); + return $ret; + } + + /** + * Get contents it's the same as render(). + * + * @returns string + */ + public function getContents() + { + $fpdf = $this->_processPDF(); + $ret = file_get_contents( $fpdf ); + @unlink( $fpdf ); + return $ret; + } + + /** + * Gets as file. + * Sets headers and prints out contents. + * This function is usable then you want to export in browser. + * + * @param string $fileName Name of file. + * @returns string + */ + public function getAsFile( $fileName = false ) + { + $fpdf = $this->_processPDF(); + + $name = sprintf( "%s_%s.pdf", $fileName ? $fileName : 'OSSFile', date( 'YmdHis' ) ); + + header( 'Content-type: application/pdf' ); + header( "Content-Disposition: attachment; filename=" . $name ); + header( 'Cache-Control: no-cache, must-revalidate' ); + header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' ); + + echo file_get_contents( $fpdf ); + @unlink( $fpdf ); + } + +} + + diff --git a/library/OSS/Pdf/PdfLatex.php b/library/OSS/Pdf/PdfLatex.php new file mode 100644 index 0000000..c82be4f --- /dev/null +++ b/library/OSS/Pdf/PdfLatex.php @@ -0,0 +1,162 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Pdf + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Pdf_PdfLatex +{ + /** + *Options required for the class. + * + * @var array + */ + private $_options; + + + /** + * The Smarty view object (may also work with Zend_View but needs to be tested) + * + * @var Zend_View + */ + private $_view; + + + /** + * Take and set configuration parameters. + * + * @param array $config default null An associative array of configuration options + * @param object $view default null a Smarty view object + * @return void + */ + function __construct( $config = null, $view = null ) + { + $this->_options = $config; + $this->_view = $view; + } + + + /** + * Generate the PDF file using the given template + * + * All required style files, includes, etc are either provided in the + * template as absolute paths or as relative paths to the template file. + * + * @param string $pTemplate + * @param $pViewVariables default null + * @param &$pPdfFileName this will contain the full path to the PDF + * @throws OSS_Pdf_PdfLatex_Exception + * @return string a binary string, the content of the PDF file + */ + public function generatePdf( $pTemplate, $pViewVariables = null, &$pPdfFileName ) + { + $this->_changeDelimiters(); + + foreach( $pViewVariables as $var => $val ) $this->_view->$var = $val; + + // try and create a temporary file for the generated LaTeX + + // tempnam() doesn't like '..' in the filename + $filename = "{$this->_options['executables']['pdflatex']['output_directory']}/auto-gen-" . mt_rand(); + $pPdfFileName = "{$filename}.pdf"; + + if (@touch($filename) === false) throw new OSS_Pdf_PdfLatex_Exception( 'Could not create temporary file for the PDF document. Check permissions.' ); + + @file_put_contents( $filename, $this->_view->render( "../../data/pdflatex/templates/{$pTemplate}.tpl" ) ); + + // generate the PDF + $cmd = 'cd ' + . escapeshellarg( $this->_options['executables']['pdflatex']['output_directory'] ) + . ' && ' + . escapeshellcmd( $this->_options['executables']['pdflatex']['cmd'] ) + . ' -output-directory ' . escapeshellarg( $this->_options['executables']['pdflatex']['output_directory'] ) . ' ' + . escapeshellarg( $filename ); + + $lastline = exec( $cmd, $output, $retval ); + $lastline = exec( $cmd, $output, $retval ); + + if ( $retval != 0 ) throw new OSS_Pdf_PdfLatex_Exception( 'Could not compile LaTeX file ' . $filename ); + + $pdf = @file_get_contents( $filename . '.pdf' ); + + @unlink( $filename ); + @unlink( $filename . '.aux' ); + @unlink( $filename . '.log' ); + //@unlink( $filename . '.pdf' ); // the file might be needed later + + $this->_resetDelimiters(); + + return $pdf; + } + + + /** + * LaTeX syntax will conflict badly with Smarty. As such, this function changes + * the Smarty delimiters to something unlikely to appear in a LaTeX file. + * + * @param string $ld Left delimiter + * @param string $rd Right delimiter + * @return void + */ + private function _changeDelimiters( $ld = '' ) + { + $this->_view->getEngine()->left_delimiter = $ld; + $this->_view->getEngine()->right_delimiter = $rd; + } + + /** + * LaTeX syntax will conflict badly with Smarty. This function resets + * the Smarty delimiters to their originals after _changeDelimiters() + * was used. + * + * @see _changeDelimiters() + * + * @param string $ld Left delimiter + * @param string $rd Right delimiter + * @retur void + */ + private function _resetDelimiters( $ld = '{', $rd = '}' ) + { + $this->_view->getEngine()->left_delimiter = $ld; + $this->_view->getEngine()->right_delimiter = $rd; + } +} + diff --git a/library/OSS/Pdf/PdfLatex/Exception.php b/library/OSS/Pdf/PdfLatex/Exception.php new file mode 100644 index 0000000..1fccaa7 --- /dev/null +++ b/library/OSS/Pdf/PdfLatex/Exception.php @@ -0,0 +1,48 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Pdf + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Pdf_PdfLatex_Exception extends Zend_Exception +{ + +} diff --git a/library/OSS/Phone.php b/library/OSS/Phone.php new file mode 100644 index 0000000..b23accc --- /dev/null +++ b/library/OSS/Phone.php @@ -0,0 +1,152 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Log + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Phone +{ + + /** + * Returns true if the passed phone number is a mobile number. + * + * @param string $pNumber + * @param string $pCountryCode default '353' + * @return boolean + */ + public static function isMobileNumber( $pNumber, $pCountryCode='353' ) + { + $pNumber = preg_replace( "/\D/", '', $pNumber ); + + if( ( strlen( $pNumber ) == 10 ) && ( substr( $pNumber, 0, 2 ) == '08' ) ) + return true; + + if( ( strlen( $pNumber ) == 12 ) && ( substr( $pNumber, 0, 4 ) == "{$pCountryCode}8" ) ) + return true; + + return false; + } + + /** + * Converts a phone number to its national format, stripping the intl. country code + * from the beginning, and adding the leading local prefix if missing - for example + * '353' and '0' for Ireland. + * + * @param string $pNumber + * @param string|int $pCountryCode default '353' + * @param string|int $pLocalPrefix default '0' + * @return string + */ + public static function localPhoneNumber( $pNumber, $pCountryCode = '353', $pLocalPrefix = '0' ) + { + $pNumber = preg_replace( "/\D/", '', $pNumber ); + + if( $pNumber == '' ) + return ''; + + $pCountryCode = (string) $pCountryCode; + $pLocalPrefix = (string) $pLocalPrefix; + + if( substr( $pNumber, 0, strlen( $pCountryCode ) ) == $pCountryCode ) + $pNumber = substr( $pNumber, strlen( $pCountryCode ) ); + + if( ( $pLocalPrefix != '' ) && ( substr( $pNumber, 0, strlen( $pLocalPrefix ) ) != $pLocalPrefix ) ) + $pNumber = $pLocalPrefix . $pNumber; + + return $pNumber; + } + + + /** + * Converts a phone number to its international format, adding the intl. country code + * to the beginning, and removing the leading local prefix if present - for example + * '353' and '0' for Ireland. + * + * @param string $pNumber + * @param string|int $pCountryCode default '353' + * @param string|int $pLocalPrefix default '0' + * @return string + */ + public static function intlPhoneNumber( $pNumber, $pCountryCode = '353', $pLocalPrefix = '0' ) + { + $pNumber = preg_replace( "/\D/", '', $pNumber ); + + if( $pNumber == '' ) + return ''; + + $pCountryCode = (string) $pCountryCode; + $pLocalPrefix = (string) $pLocalPrefix; + + if( substr( $pNumber, 0, strlen( $pLocalPrefix ) ) == $pLocalPrefix ) + $pNumber = substr( $pNumber, strlen( $pLocalPrefix ) ); + + if( substr( $pNumber, 0, strlen( $pCountryCode ) ) != $pCountryCode ) + $pNumber = $pCountryCode . $pNumber; + + return $pNumber; + } + + + /** + * Converts a phone number to its international format, adding the intl. country code + * to the beginning, and removing the leading local prefix if present - for example + * '353' and '0' for Ireland. + * + * @param string $pNumber + * @param bool $pInternational default false + * @param string|int $pCountryCode default '353' + * @param string|int $pLocalPrefix default '0' + * @return string + */ + public static function formatPhoneNumber( $pNumber, $pInternational = false, $pCountryCode = '353', $pLocalPrefix = '0' ) + { + if( $pInternational ) + { + $pNumber = self::intlPhoneNumber( $pNumber, $pCountryCode, $pLocalPrefix ); + return preg_replace( "/({$pCountryCode})(\d{2})(\d{3})(\d{4})/", "+$1 $2 $3 $4", $pNumber ); + } + else + { + $pNumber = self::localPhoneNumber( $pNumber, $pCountryCode, $pLocalPrefix ); + return preg_replace( "/(\d{3})(\d{3})(\d{4})/", "$1 $2 $3", $pNumber ); + } + } +} diff --git a/library/OSS/Plugin/Observable.php b/library/OSS/Plugin/Observable.php new file mode 100644 index 0000000..2898052 --- /dev/null +++ b/library/OSS/Plugin/Observable.php @@ -0,0 +1,54 @@ + + */ + +/** + * An interface to be implemented by classes which can be "observed" - i.e. classes + * which for which plugins can be developed. + * + * @category OSS + * @package OSS_Plugin + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +interface OSS_Plugin_Observable +{ + + function attach( OSS_Plugin_Observer $observer ); + function detach( OSS_Plugin_Observer $observer ); + function notify( $controller, $action, $hook, OSS_Controller_Action $controllerObject, $params = null ); + +} diff --git a/library/OSS/Plugin/Observer.php b/library/OSS/Plugin/Observer.php new file mode 100644 index 0000000..681c8ff --- /dev/null +++ b/library/OSS/Plugin/Observer.php @@ -0,0 +1,49 @@ + + */ + +/** + * An interface to be implemented by classes which "observe" - i.e. plugins + * + * @category OSS + * @package OSS_Plugin + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +interface OSS_Plugin_Observer +{ + function update( $controller, $action, $hook, OSS_Controller_Action $controllerObject, $params = null ); +} diff --git a/library/OSS/Resource/AsteriskMI.php b/library/OSS/Resource/AsteriskMI.php new file mode 100644 index 0000000..6ec2b7f --- /dev/null +++ b/library/OSS/Resource/AsteriskMI.php @@ -0,0 +1,92 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_AsteriskMI extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the instance + * + * @var AGI_AsteriskManager + */ + protected $_instance = null; + + + /** + * Initialisation function + * + * @return AGI_AsteriskManager + */ + public function init() + { + return $this->getInstance(); + } + + + /** + * Get the instance + * + * @throws OSS_Asterisk_AMI_ConnectionException + * @return AGI_AsteriskManager + */ + public function getInstance() + { + if( $this->_instance === null ) + { + $conf = $this->getOptions(); + + if( !class_exists( 'AGI_AsteriskManager' ) ) + require_once( 'phpagi-asmanager.php' ); + + $this->_instance = new AGI_AsteriskManager(); + + if( !$this->_instance->connect( $conf["host"], $conf["username"], $conf["secret"] ) ) + { + throw new OSS_Asterisk_AMI_ConnectionException( 'Could not connect to the Asterisk server ' . $conf['host'] ); + } + } + + return $this->_instance; + } + +} diff --git a/library/OSS/Resource/Auth.php b/library/OSS/Resource/Auth.php new file mode 100644 index 0000000..ef73c1d --- /dev/null +++ b/library/OSS/Resource/Auth.php @@ -0,0 +1,81 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Auth extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Auth instance + * + * @var null|Zend_Auth + */ + protected $_auth; + + + /** + * Initialisation function + * + * @return Zend_Auth + */ + public function init() + { + // Return Doctrine so bootstrap will store it in the registry + return $this->getAuth(); + } + + /** + * Get auth + * + * @return Zend_Auth + */ + public function getAuth() + { + // Get Doctrine configuration options from the application.ini file + $authConfig = $this->getOptions(); + + $this->_auth = Zend_Auth::getInstance(); + + return $this->_auth; + } + +} diff --git a/library/OSS/Resource/Clickatell.php b/library/OSS/Resource/Clickatell.php new file mode 100644 index 0000000..9adee5c --- /dev/null +++ b/library/OSS/Resource/Clickatell.php @@ -0,0 +1,108 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Clickatell extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the clickatell instance + * + * @var null|object + */ + protected $_clickatell = null; + + + /** + * Initialisation function + * + * @return object + */ + public function init() + { + // Return Doctrine so bootstrap will store it in the registry + return $this->getClickatell(); + } + + + /** + * Get auth + * + * @return object + */ + public function getClickatell() + { + if( null === $this->_clickatell ) + { + // Get Clickatell configuration options from the application.ini file + $clickatellConfig = $this->getOptions(); + + $haveValidMethod = true; + + switch( strtolower( $clickatellConfig['handler'] ) ) + { + case 'http': + $class = 'OSS_Service_Clickatell_Http'; + break; + + default: + $haveValidMethod = false; + } + + if( $haveValidMethod ) + { + $this->_clickatell = new $class( $clickatellConfig['options'] ); + + /* + if( (bool) $clickatellConfig['log'] ) + { + $this->_clickatell->setLogger( $this->getBootstrap()->bootstrap( 'Logger' ) ); + $this->_clickatell->setLogLevel( $clickatellConfig['loglevel'] ); + } + */ + } + } + + return $this->_clickatell; + } + +} diff --git a/library/OSS/Resource/Doctrine.php b/library/OSS/Resource/Doctrine.php new file mode 100644 index 0000000..8a97ef4 --- /dev/null +++ b/library/OSS/Resource/Doctrine.php @@ -0,0 +1,135 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Doctrine extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Doctrine instance + * + * @var null|Doctrine_Manager + */ + protected $_doctrine; + + /** + * Initialisation function + * + * @return Doctrine_Manager + */ + public function init() + { + // Return Doctrine so bootstrap will store it in the registry + return $this->getDoctrine(); + } + + + /** + * Get doctrine + * + * @return Doctrine_Manager + */ + public function getDoctrine() + { + if ( null === $this->_doctrine ) + { + // Get Doctrine configuration options from the application.ini file + $doctrineConfig = $this->getOptions(); + + /** + * @see Doctrine + */ + require_once 'Doctrine.php'; + + $loader = Zend_Loader_Autoloader::getInstance(); + $loader->pushAutoloader( array( 'Doctrine', 'autoload' ) ); + $loader->pushAutoloader( array( 'Doctrine', 'modelsAutoload' ) ); + + $manager = Doctrine_Manager::getInstance(); + + if( isset( $doctrineConfig['extensions_path'] ) && is_array( $doctrineConfig['extensions'] ) ) + { + Doctrine_Core::setExtensionsPath( $doctrineConfig['extensions_path'] ); + $loader->pushAutoloader( array( 'Doctrine', 'extensionsAutoload' ) ); + + foreach( $doctrineConfig['extensions'] as $e ) + $manager->registerExtension( $e ); + } + + $manager->setAttribute( Doctrine::ATTR_MODEL_LOADING, Doctrine::MODEL_LOADING_CONSERVATIVE ); + $manager->setAttribute( Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true ); + $manager->setAttribute( Doctrine::ATTR_USE_DQL_CALLBACKS, true ); + + $manager->setCollate( 'utf8_unicode_ci' ); + $manager->setCharset( 'utf8' ); + + Doctrine::loadModels( $doctrineConfig['models_path'] ); + + $db_profiler = new Doctrine_Connection_Profiler(); + + $manager->openConnection( $doctrineConfig['connection_string'] ); + $manager->connection()->setListener( $db_profiler ); + + $manager->connection()->setCollate('utf8_unicode_ci'); + $manager->connection()->setCharset('utf8'); + + Zend_Registry::set( 'db_profiler', $db_profiler ); + + $this->_doctrine = $manager; + } + + return $this->_doctrine; + } + + /** + * Set the classes $_doctrine member + * + * @param Doctrine_Manager $doctrine The object to set + * @return void + */ + public function setDoctrine( $doctrine ) + { + $this->_doctrine = $doctrine; + } + + +} diff --git a/library/OSS/Resource/Doctrine2.php b/library/OSS/Resource/Doctrine2.php new file mode 100644 index 0000000..6342d09 --- /dev/null +++ b/library/OSS/Resource/Doctrine2.php @@ -0,0 +1,154 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Doctrine2 extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Doctrine instance(s) + * + * @var null|Doctrine\ORM\EntityManager + */ + protected $_doctrine2 = null; + + /** + * Initialisation function + * + * @return Doctrine\ORM\EntityManager + */ + public function init() + { + // Return Doctrine so bootstrap will store it in the registry + return $this->getDoctrine2(); + } + + + /** + * Get Doctrine2 + * + * @param string $db The database instance to get (if using multiple databases) + * @return Doctrine\ORM\EntityManager + */ + public function getDoctrine2( $db = 'default' ) + { + if( $this->_doctrine2 === null || !isset( $this->_doctrine2[ $db ] ) ) + { + // Get Doctrine configuration options from the application.ini file + $dconfig = $this->getOptions(); + + if( $db != 'default' ) + $dconfig = $dconfig[ $db ]; + + try + { + if( Zend_Registry::isRegistered( 'd2cache' ) ) + $cache = Zend_Registry::get( 'd2cache' ); + else + { + $d2cacheOptions = $this->getBootstrap()->getApplication()->getOptions()['resources']['doctrine2cache']; + + if( !$d2cacheOptions || !isset( $d2cacheOptions['type'] ) ) + throw new Zend_Exception( 'force err' ); + + $plugin = new OSS_Resource_Doctrine2cache( $d2cacheOptions ); + $this->getBootstrap()->registerPluginResource( $plugin ); + $cache = $plugin->getDoctrine2cache(); + } + } + catch( Zend_Exception $e ) + { + die( _( 'ERROR: Doctrine2 requires Doctrine2Cache to have been already bootstrapped' ) ); + } + + $config = new Doctrine\ORM\Configuration(); + $config->setMetadataCacheImpl( $cache ); + + $driver = new \Doctrine\ORM\Mapping\Driver\XmlDriver( + array( $dconfig['xml_schema_path'] ) + ); + $config->setMetadataDriverImpl( $driver ); + + $config->setQueryCacheImpl( $cache ); + $config->setResultCacheImpl( $cache ); + $config->setProxyDir( $dconfig['proxies_path'] ); + $config->setProxyNamespace( $dconfig['proxies_namespace'] ); + $config->setAutoGenerateProxyClasses( $dconfig['autogen_proxies'] ); + + if( isset( $dconfig['logger'] ) && $dconfig['logger'] ) + $config->setSQLLogger( new OSS_Doctrine2_FirebugProfiler() ); + + $this->_doctrine2[ $db ] = Doctrine\ORM\EntityManager::create( $dconfig['connection']['options'], $config ); + + + $modelAutoLoader = new \Doctrine\Common\ClassLoader( $dconfig['models_namespace'], + realpath( $dconfig['models_path'] ) + ); + + $repositoryAutoLoader = new \Doctrine\Common\ClassLoader( $dconfig['repositories_namespace'], + realpath( $dconfig['repositories_path'] ) + ); + + $autoloader = Zend_Loader_Autoloader::getInstance(); + $autoloader->pushAutoloader( array( $modelAutoLoader, 'loadClass' ), $dconfig['models_namespace'] ); + $autoloader->pushAutoloader( array( $repositoryAutoLoader, 'loadClass' ), $dconfig['repositories_namespace'] ); + + // http://docs.doctrine-project.org/en/latest/reference/configuration.html#autoloading-proxies + Doctrine\ORM\Proxy\Autoloader::register( $dconfig['proxies_path'], $dconfig['proxies_namespace'] ); + } + + return $this->_doctrine2[ $db ]; + } + + /** + * Set the classes $_doctrine member + * + * @param Doctrine\ORM\EntityManager $doctrine The object to set + * @param string $db The database instance to set if using multiple databases. + * @return void + */ + public function setDoctrine( $doctrine2, $db = 'default' ) + { + $this->_doctrine2[ $db ] = $doctrine2; + } + +} diff --git a/library/OSS/Resource/Doctrine2cache.php b/library/OSS/Resource/Doctrine2cache.php new file mode 100644 index 0000000..77a14a2 --- /dev/null +++ b/library/OSS/Resource/Doctrine2cache.php @@ -0,0 +1,150 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Doctrine2cache extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Doctrine instance + * + * @var null|Doctrine\ORM\EntityManager + */ + protected $_d2cache = null; + + /** + * Initialisation function + * + * @return Doctrine\Common\Cache + */ + public function init() + { + return $this->getDoctrine2cache(); + } + + + /** + * Get Doctrine2Cache + * + * @return Doctrine\Common\Cache + */ + public function getDoctrine2cache() + { + if( $this->_d2cache === null ) + { + // Get Doctrine configuration options from the application.ini file + $config = $this->getOptions(); + + if( !isset( $config['autoload_method'] ) ) + $config['autoload_method'] = 'git'; + + switch( $config['autoload_method'] ) + { + case 'pear': + require_once( $config['path'] . '/Tools/Setup.php' ); + Doctrine\ORM\Tools\Setup::registerAutoloadPEAR(); + break; + + case 'dir': + require_once( $config['path'] . '/Tools/Setup.php' ); // FIXME + Doctrine\ORM\Tools\Setup::registerAutoloadDirectory(); + break; + + case 'composer': + break; + + default: + require_once( $config['path'] . '/lib/Doctrine/ORM/Tools/Setup.php' ); + Doctrine\ORM\Tools\Setup::registerAutoloadGit( $config['path'] ); + } + + if( $config['type'] == 'ApcCache' ) + $cache = new \Doctrine\Common\Cache\ApcCache(); + elseif( $config['type'] == 'MemcacheCache' ) + { + $memcache = new Memcache(); + + for( $cnt = 0; $cnt < count( $config['memcache']['servers'] ); $cnt++ ) + { + $server = $config['memcache']['servers'][$cnt]; + + $memcache->addServer( + isset( $server['host'] ) ? $server['host'] : '127.0.0.1', + isset( $server['port'] ) ? $server['port'] : 11211, + isset( $server['persistent'] ) ? $server['persistent'] : false, + isset( $server['weight'] ) ? $server['weight'] : 1, + isset( $server['timeout'] ) ? $server['timeout'] : 1, + isset( $server['retry_int'] ) ? $server['retry_int'] : 15 + ); + } + + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $cache->setMemcache( $memcache ); + } + else + $cache = new \Doctrine\Common\Cache\ArrayCache(); + + if( isset( $config['namespace'] ) ) + $cache->setNamespace( $config['namespace'] ); + + + // stick the cache in the registry + Zend_Registry::set( 'd2cache', $cache ); + $this->setDoctrine2Cache( $cache ); + } + + return $this->_d2cache; + } + + /** + * Set the classes $_d2cache member + * + * @param Doctrine\Common\Cache $c The object to set + * @return void + */ + public function setDoctrine2Cache( $c ) + { + $this->_d2cache = $c; + } + + +} diff --git a/library/OSS/Resource/Freshbooks.php b/library/OSS/Resource/Freshbooks.php new file mode 100644 index 0000000..71de7b9 --- /dev/null +++ b/library/OSS/Resource/Freshbooks.php @@ -0,0 +1,92 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Freshbooks extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Frehsbook service instance + * + * @var null|OSS_Service_Freshbooks + */ + protected $_fbooks = null; + + + /** + * Initialisation function + * + * @return OSS_Service_Freshbooks + */ + public function init() + { + // Return Doctrine so bootstrap will store it in the registry + return $this->getFreshbooks(); + } + + + /** + * Get Freshbooks + * + * @return OSS_Service_Freshbooks + */ + public function getFreshbooks() + { + if( !$this->_fbooks ) + { + // Get Freshbooks configuration options from the application.ini file + $fbooksConfig = $this->getOptions(); + + if( $fbooksConfig['enabled'] ) + { + if( !isset( $fbooksConfig['accessToken'] ) || !file_exists( $fbooksConfig['accessToken'] ) ) + die( 'Freshbooks resource enabled but mis-configured - no access token available' ); + + $fbooksConfig['accessToken'] = unserialize( file_get_contents( $fbooksConfig['accessToken'] ) ); + $this->_fbooks = new OSS_Service_Freshbooks( $fbooksConfig ); + } + } + + return $this->_fbooks; + } + +} diff --git a/library/OSS/Resource/License.php b/library/OSS/Resource/License.php new file mode 100644 index 0000000..f766567 --- /dev/null +++ b/library/OSS/Resource/License.php @@ -0,0 +1,80 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_License extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the instance + * + * @var OSS_License_Abstract + */ + protected $_resource = null; + + + /** + * Initialisation function + * + * @return OSS_License_Abstract + */ + public function init() + { + // Return session so bootstrap will store it in the registry + return $this->getResource(); + } + + + /** + * Get resource + * + * @return OSS_License_Abstract + */ + public function getResource() + { + if( null === $this->_resource ) + $this->_resource = OSS_License::load( $this->getOptions()['file'] ); + + return $this->_resource; + } + +} diff --git a/library/OSS/Resource/Logger.php b/library/OSS/Resource/Logger.php new file mode 100644 index 0000000..bbe038b --- /dev/null +++ b/library/OSS/Resource/Logger.php @@ -0,0 +1,190 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Logger extends Zend_Application_Resource_ResourceAbstract +{ + protected $_session; + + /** + * Holds the Logger instance + * + * @var null|OSS_Log + */ + protected $_logger; + + + /** + * Initialisation function + * + * @return OSS_Log + */ + public function init() + { + // Return logger so bootstrap will store it in the registry + return $this->getLogger(); + } + + + /** + * get Logger + * + * @return OSS_Log + */ + public function getLogger() + { + if( null === $this->_logger ) + { + // Get Doctrine configuration options from the application.ini file + $options = $this->getOptions(); + + $logger = new OSS_Log(); + + if( isset( $options['enabled'] ) && $options['enabled'] ) + { + foreach( $options['writers'] as $writer => $writerOptions ) + { + switch( $writer ) + { + case 'stream': + if( isset( $writerOptions['mode'] ) && $writerOptions['mode'] = 'single' ) + { + $log_path = $writerOptions['path']; + $log_file = $log_path . DIRECTORY_SEPARATOR . ( isset( $writerOptions['logname'] ) ? $writerOptions['logname'] : 'log.log' ); + } + else + { + $log_path = $writerOptions['path'] + . DIRECTORY_SEPARATOR . date( 'Y' ) + . DIRECTORY_SEPARATOR . date( 'm' ); + + $log_file = $log_path . DIRECTORY_SEPARATOR . date( 'Ymd') . '.log'; + } + + if( file_exists( $log_path ) == false ) + { + mkdir( $log_path, 0755, true ); + @chmod( $log_path, 0755 ); + @chown( $log_path, $writerOptions['owner'] ); + @chgrp( $log_path, $writerOptions['group'] ); + } + + if( file_exists( $log_file ) == false ) + { + touch( $log_file ); + @chmod( $log_file, 0777 ); + @chown( $log_file, $writerOptions['owner'] ); + @chgrp( $log_file, $writerOptions['group'] ); + } + + $streamWriter = new Zend_Log_Writer_Stream( $log_file ); + $streamWriter->setFormatter( + new Zend_Log_Formatter_Simple( + '%timestamp% %priorityName% (%priority%) ' . (isset($_SERVER['REMOTE_ADDR']) == true ? "[{$_SERVER['REMOTE_ADDR']}]" : "") . ': %message%' . PHP_EOL + ) + ); + + $logger->addWriter( $streamWriter ); + + if ( isset($writerOptions['level']) ) $logger->addFilter( (int)$writerOptions['level'] ); + + break; + + case 'email': + $mail = new Zend_Mail(); + $mail->setFrom( $writerOptions['from'] ) + ->addTo( $writerOptions['to'] ); + + $mailWriter = new Zend_Log_Writer_Mail( $mail ); + + // Set subject text for use; summary of number of errors is appended to the + // subject line before sending the message. + $mailWriter->setSubjectPrependText( "[{$writerOptions['prefix']}]" ); + + // Only email entries with level requested and higher. + $mailWriter->addFilter( (int)$writerOptions['level'] ); + + $logger->addWriter( $mailWriter ); + break; + + case 'firebug': + if( $writerOptions['enabled'] ) + { + $firebugWriter = new Zend_Log_Writer_Firebug(); + $firebugWriter->addFilter( (int)$writerOptions['level'] ); + $logger->addWriter( $firebugWriter ); + } + break; + + default: + try { + $logger->log( "Unknown log writer: {$writer}", Zend_Log::WARN ); + } catch( Zend_Log_Exception $e ) { + die( "Unknown log writer [{$writer}] during application bootstrap" ); + } + break; + } + } + + } + else + { + $logger->addWriter( new Zend_Log_Writer_Null() ); + } + + try + { + $logger->debug( 'Logger instantiated', Zend_Log::INFO ); + } + catch( Zend_Log_Exception $e ) + { + die( "Unknown log writer [{$writer}] during application bootstrap" ); + } + + $this->_logger = $logger; + } + + return $this->_logger; + } + +} diff --git a/library/OSS/Resource/Mailer.php b/library/OSS/Resource/Mailer.php new file mode 100644 index 0000000..9f514bf --- /dev/null +++ b/library/OSS/Resource/Mailer.php @@ -0,0 +1,102 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Mailer extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Mailer instance + * + * @var null|Zend_Mail_Transport_Smtp + */ + protected $_mailer; + + + /** + * Initialisation function + * + * @return Zend_Mail_Transport_Smtp + */ + public function init() + { + // Return mailer so bootstrap will store it in the registry + return $this->getMailer(); + } + + + /** + * Get mailer + * + * @return Zend_Mail_Transport_Smtp + */ + public function getMailer() + { + if( null === $this->_mailer ) + { + $options = $this->getOptions(); + + if( count( $options ) ) + { + if( isset( $options['auth'] ) ) + { + $config = array( + 'auth' => $options['auth'], + 'username' => $options['username'], + 'password' => $options['password'] + ); + } + else + $config = array(); + + $transport = new Zend_Mail_Transport_Smtp( $options['smtphost'], $config ); + Zend_Mail::setDefaultTransport( $transport ); + + $this->_mailer = $transport; + } + } + + return $this->_mailer; + } + + +} diff --git a/library/OSS/Resource/Moduleconfig.php b/library/OSS/Resource/Moduleconfig.php new file mode 100644 index 0000000..ada47d6 --- /dev/null +++ b/library/OSS/Resource/Moduleconfig.php @@ -0,0 +1,138 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Moduleconfig extends Zend_Application_Resource_ResourceAbstract +{ + + /** + * Initialize + * + * @return Zend_Config + */ + public function init() + { + return $this->_getModuleConfig(); + } + + + /** + * Load the module's config + * + * @return Zend_Config + */ + protected function _getModuleConfig() + { + $bootstrap = $this->getBootstrap(); + + if (!($bootstrap instanceof Zend_Application_Bootstrap_Bootstrap)) throw new Zend_Application_Exception('Invalid bootstrap class'); + + $path = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $bootstrap->getModuleName() . DIRECTORY_SEPARATOR . 'configs' . DIRECTORY_SEPARATOR; + + $cfgdir = new DirectoryIterator($path); + $modOptions = $this->getBootstrap()->getOptions(); + + foreach ($cfgdir as $file) + { + if ($file->isFile()) + { + $filename = $file->getFilename(); + + if (in_array(substr(trim(strtolower($filename)), -3), array('ini', 'xml')) == true) + { + $options = $this->_loadOptions($path . $filename); + + if (($len = strpos($filename, '.')) !== false) + { + $cfgtype = substr($filename, 0, $len); + } + else + { + $cfgtype = $filename; + } + + if (strtolower($cfgtype) == 'module') + { + $modOptions = array_merge($modOptions, $options); + } + else + { + //$modOptions['resources'][$cfgtype] = $options; + $modOptions = array_merge($modOptions, $options); + } + } + } + } + + $this->getBootstrap()->setOptions($modOptions); + } + + + /** + * Load the config file + * + * @param string $fullpath + * throws Zend_Config_Exception + * @return array + */ + protected function _loadOptions($fullpath) + { + if (file_exists($fullpath)) + { + switch (substr(trim(strtolower($fullpath)), -3)) + { + case 'ini': $cfg = new Zend_Config_Ini($fullpath, $this->getBootstrap()->getEnvironment()); break; + case 'xml': $cfg = new Zend_Config_Xml($fullpath, $this->getBootstrap()->getEnvironment()); break; + default: throw new Zend_Config_Exception('Invalid format for config file'); break; + } + } + else + { + throw new Zend_Application_Resource_Exception('Ini file does not exist.'); + } + + return $cfg->toArray(); + } + +} + diff --git a/library/OSS/Resource/Namespace.php b/library/OSS/Resource/Namespace.php new file mode 100644 index 0000000..220614b --- /dev/null +++ b/library/OSS/Resource/Namespace.php @@ -0,0 +1,115 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Namespace extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Logger instance + * + * @var null|Zend_Session_Namespace + */ + protected $_session; + + + /** + * Initialisation function + * + * @return Zend_Session_Namespace + */ + public function init() + { + // Return session so bootstrap will store it in the registry + return $this->getSession(); + } + + + /** + * Get session namespace + * + * @return Zend_Session_Namespace + */ + public function getSession() + { + if( null === $this->_session ) + { + $this->getBootstrap()->bootstrap( 'Session' ); + + // Get session configuration options from the application.ini file + $options = $this->getOptions(); + + $ApplicationNamespace = new Zend_Session_Namespace( 'Application' ); + + // Secutiry tip from http://framework.zend.com/manual/en/zend.session.global_session_management.html + if( !isset( $ApplicationNamespace->initialised ) ) + { + // FIXME Zend_Session::regenerateId(); + $ApplicationNamespace->initialized = true; + } + + // ensure IP consistancy + if ( (isset($options['checkip'])) && ($options['checkip']) && (isset($_SERVER['REMOTE_ADDR'])) ) + { + if( !isset( $ApplicationNamespace->clientIP ) ) + { + $ApplicationNamespace->clientIP = $_SERVER['REMOTE_ADDR']; + } + else if( $ApplicationNamespace->clientIP != $_SERVER['REMOTE_ADDR'] ) + { + // security violation - client IP has changed indicating a possible hijacked session + Zend_Session::destroy( true, true ); + die( + "Your IP address has changed indication a possible session hijack attempt. Your session has been destroyed for your own security." + ); + } + } + + $this->_session = $ApplicationNamespace; + + } + + return $this->_session; + } + + +} diff --git a/library/OSS/Resource/News.php b/library/OSS/Resource/News.php new file mode 100644 index 0000000..4467913 --- /dev/null +++ b/library/OSS/Resource/News.php @@ -0,0 +1,109 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_News extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the Doctrine instance + * + * @var null|array + */ + protected $_news = null; + + + /** + * Initialisation function + * + * @return array + */ + public function init() + { + // Return Doctrine so bootstrap will store it in the registry + return $this->getNews(); + } + + + /** + * Get News + * + * @return array + */ + public function getNews() + { + if( $this->_news === null ) + { + // Get News configuration options from the application.ini file + $newsConfig = $this->getOptions(); + + Zend_Feed::setHttpClient( + new Zend_Http_Client( + null, + array( + 'timeout' => $newsConfig['poll_timeout'] + ) + ) + ); + + foreach( $newsConfig['channels'] as $name => $cconf ) + { + try + { + $class = 'Zend_Feed_' . ucfirst( $cconf['type'] ); + $channel[ $name ]['channel'] = new $class( $cconf['source'] ); + $channel[ $name ]['key'] = $cconf['key']; + } + catch( Exception $e ) + { + $channel[ $name ]['channel'] = array(); + $channel[ $name ]['key'] = $cconf['key']; + } + } + + $this->_news = $channel; + } + + return $this->_news; + } + +} diff --git a/library/OSS/Resource/Paygate.php b/library/OSS/Resource/Paygate.php new file mode 100644 index 0000000..adb1b8c --- /dev/null +++ b/library/OSS/Resource/Paygate.php @@ -0,0 +1,91 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Paygate extends Zend_Application_Resource_ResourceAbstract +{ + protected $_session; + + /** + * Holds the Logger instance + * + * @var null|OSS_PaymentProcessor + */ + protected $_paygate = null; + + + /** + * Initialisation function + * + * @return OSS_PaymentProcessor + */ + public function init() + { + die( "init" ); + return $this->getPaygate(); + } + + + /** + * get paygate + * + * @return OSS_PaymentProcessor + */ + public function getPaygate( $logger = null) + { + if( !$this->_paygate ) + { + // Get Doctrine configuration options from the application.ini file + $configs = $this->getOptions(); + + $paygate = 'OSS_PaymentProcessor_' . $configs["name"]; + + $options = Zend_Registry::get('options')[ strtolower( $configs["name"] ) ]; + + $this->_paygate = new $paygate( $options, $logger ); + } + + return $this->_paygate; + } + +} diff --git a/library/OSS/Resource/Smarty.php b/library/OSS/Resource/Smarty.php new file mode 100644 index 0000000..228ca81 --- /dev/null +++ b/library/OSS/Resource/Smarty.php @@ -0,0 +1,124 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Smarty extends Zend_Application_Resource_ResourceAbstract +{ + + /** + * Holds the View instance + * + * @var null|OSS_View_Smarty + */ + protected $_view; + + /** + * Initialisation function + * + * @return OSS_View_Smarty + */ + public function init() + { + // Return view so bootstrap will store it in the registry + return $this->getView(); + } + + /** + * Get view + * + * @return OSS_View_Smarty + */ + public function getView() + { + // Get session configuration options from the application.ini file + $options = $this->getOptions(); + + if( $options['enabled'] ) + { + if( null === $this->_view ) // this cannot be &&'d with the above! + { + @include_once( 'Smarty' . DIRECTORY_SEPARATOR . 'Smarty.class.php' ); + + // Create directories of necessary + if( !file_exists( $options['cache'] ) ) + { + mkdir( $options['cache'], 0770, true ); + chmod( $options['cache'], 0770 ); + } + + if( !file_exists( $options['compiled'] ) ) + { + mkdir( $options['compiled'], 0770, true ); + chmod( $options['compiled'], 0770 ); + } + + // Initialize view + $view = new OSS_View_Smarty( + $options['templates'], + array( + 'cache_dir' => $options['cache'], + 'config_dir' => isset( $options['config'] ) ? $options['config'] : null, + 'compile_dir' => $options['compiled'], + 'plugins_dir' => $options['plugins'] + ) + ); + + if( isset( $options['skin'] ) && strlen( $options['skin'] ) ) + $view->setSkin( $options['skin'] ); + + $view->getEngine()->debugging = $options['debugging']; + + // Add it to the ViewRenderer + $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper( 'ViewRenderer' ); + $viewRenderer->setView( $view ); + + $this->_view = $view; + } + + $this->_view->OSS_Messages = array(); + + return $this->_view; + } + + } +} diff --git a/library/OSS/Resource/StatsD.php b/library/OSS/Resource/StatsD.php new file mode 100644 index 0000000..b02995a --- /dev/null +++ b/library/OSS/Resource/StatsD.php @@ -0,0 +1,82 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_StatsD extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the instance + * + * @var OSS_StatsD + */ + protected $_instance = null; + + + /** + * Initialisation function + * + * @return OSS_StatsD + */ + public function init() + { + return $this->getInstance(); + } + + + /** + * Get the instance + * + * @return OSS_StatsD + */ + public function getInstance() + { + if( $this->_instance === null ) + { + $conf = $this->getOptions(); + $this->_instance = new OSS_StatsD( $conf['host'], $conf['port'], $conf['enabled'] ); + } + + return $this->_instance; + } + +} diff --git a/library/OSS/Resource/View.php b/library/OSS/Resource/View.php new file mode 100644 index 0000000..c1dca88 --- /dev/null +++ b/library/OSS/Resource/View.php @@ -0,0 +1,103 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_View extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the View instance + * + * @var null|Zend_View + */ + protected $_view; + + + /** + * Initialisation function + * + * @return Zend_View + */ + public function init() + { + // Return view so bootstrap will store it in the registry + return $this->getView(); + } + + + /** + * Get view + * + * @return Zend_View + */ + public function getView() + { + // Get session configuration options from the application.ini file + $options = $this->getOptions(); + + if( isset( $options['enabled'] ) && $options['enabled'] ) + { + if( null === $this->_view ) // this cannot be &&'d with the above! + { + // Initialize view + $view = new Zend_View(); + $view->doctype( $options['doctype'] ); + $view->headTitle( $options['title'] ); + + // Add it to the ViewRenderer + $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper( 'ViewRenderer' ); + $viewRenderer->setView( $view ); + + Zend_Layout::startMvc( + array( + 'layout' => 'layout', + 'layoutPath' => APPLICATION_PATH . '/layouts/scripts', + ) + ); + + $this->_view = $view; + } + + return $this->_view; + } + } +} diff --git a/library/OSS/Resource/Zfdebug.php b/library/OSS/Resource/Zfdebug.php new file mode 100644 index 0000000..3ddc6a5 --- /dev/null +++ b/library/OSS/Resource/Zfdebug.php @@ -0,0 +1,124 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Resource + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Resource_Zfdebug extends Zend_Application_Resource_ResourceAbstract +{ + /** + * Holds the ZFDebug Instance + * + * @var null|ZFDebug_Controller_Plugin_Debug + */ + protected $_zfdebug; + + + /** + * Initialisation function + * + * @return ZFDebug_Controller_Plugin_Debug + */ + public function init() + { + // Return session so bootstrap will store it in the registry + return $this->getZfdebug(); + } + + + /** + * Get Zfdebug + * + * @return ZFDebug_Controller_Plugin_Debug + */ + public function getZfdebug() + { + if( null === $this->_zfdebug ) + { + $this->getBootstrap()->bootstrap( 'Session' ); + + // Get Zfdebug configuration options from the application.ini file + $zfdebugConfig = $this->getOptions(); + + if( $zfdebugConfig['enabled'] ) + { + $autoloader = Zend_Loader_Autoloader::getInstance(); + $autoloader->registerNamespace('ZFDebug'); + + $options = array( + 'plugins' => $zfdebugConfig['plugins'] + ); + + # Instantiate the database adapter and setup the plugin. + # Alternatively just add the plugin like above and rely on the autodiscovery feature. + if( $this->getBootstrap()->hasPluginResource( 'db' ) ) + { + $this->getBootstrap()->bootstrap('db'); + $db = $this->getBootstrap()->getPluginResource( 'db' )->getDbAdapter(); + $options['plugins']['Database']['adapter'] = $db; + } + + # Setup the cache plugin + if( $this->getBootstrap()->hasPluginResource( 'cache' ) ) + { + $this->getBootstrap()->bootstrap( 'cache' ); + $cache = $this->getBootstrap()->getPluginResource( 'cache' )->getDbAdapter(); + $options['plugins']['Cache']['backend'] = $cache->getBackend(); + } + + $this->getBootstrap()->bootstrap( 'OSSAutoLoader' ); + $this->getBootstrap()->bootstrap( 'Doctrine' ); + $options['plugins']['OSS_ZFDebug_Controller_Plugin_Debug_Plugin_Doctrine']['manager'] + = $this->getBootstrap()->getResource( 'Doctrine' ); + + $this->_zfdebug = new ZFDebug_Controller_Plugin_Debug( $options ); + + $this->getBootstrap()->bootstrap( 'FrontController' ); + $frontController = $this->getBootstrap()->getResource('FrontController'); + $frontController->registerPlugin($this->_zfdebug); + } + } + + return $this->_zfdebug; + } + + +} diff --git a/library/OSS/Service/Clickatell/Abstract.php b/library/OSS/Service/Clickatell/Abstract.php new file mode 100644 index 0000000..028256e --- /dev/null +++ b/library/OSS/Service/Clickatell/Abstract.php @@ -0,0 +1,353 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Service + * @subpackage Clickatell + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +abstract class OSS_Service_Clickatell_Abstract extends Zend_Service_Abstract +{ + + /** + * An array of Clickatell delivery states (e.g. @see OSS_Service_Clickatell_Http::getQueryMessage() ) + * + * @var array + */ + public static $MESSAGE_STATES = array( + '001' => 'Message unknown', + '002' => 'Message queued', + '003' => 'Delivered to gateway', + '004' => 'Received by recipient', + '005' => 'Error with message', + '006' => 'User cancelled message delivery', + '007' => 'Error delivering message', + '008' => 'OK', + '009' => 'Routing error', + '010' => 'Message expired', + '011' => 'Message queued for later delivery', + '012' => 'Out of credit' + ); + + + /** + * API access username + * @var string + */ + protected $_username; + + /** + * API access password + * @var string + */ + protected $_password; + + /** + * API access ID + * @var string + */ + protected $_api_id; + + /** + * Use an SSL connection? + * @var bool + */ + protected $_ssl = false; + + /** + * A logger object + * @var Zend_Logger + */ + protected $_logger = null; + + /** + * The log level + * @var integer + */ + protected $_loglevel = 7; + + /** + * Send IDs of the last batch of messages sent + * Keys are the numbers and values are the IDs + * + * @var array + */ + protected $_send_ids; + + /** + * An array of errors per phone number from the last send + * Keys are the numbers and values are the error messages + * + * @var array + */ + protected $_send_errors; + + + + /** + * Constructor + * + * Accepted associated array parameters: + * username, password, ssl, api_id + * + * @param $options array An associated array of options (see above for accepted keys). + * @retrun void + */ + public function __construct( $options = null ) + { + if( is_array( $options ) ) + { + foreach( $options as $param => $value ) + { + switch( $param ) + { + case 'username': + $this->setUsername( $value ); + break; + + case 'password': + $this->setPassword( $value ); + break; + + case 'api_id': + $this->setApiId( $value ); + break; + + case 'ssl': + $this->setSSL( (bool)$value ); + break; + + + } + } + } + } + + /** + * Set SSL enabled + * + * @param $ssl Pass boolean true to set SSL enabled + * @return void + */ + public function setSSL( $ssl = true ) + { + $this->_ssl = (bool) $ssl; + } + + /** + * Set the API ID + * + * @param $api_id the API ID to set + * @return void + */ + public function setApiId( $api_id ) + { + $this->_api_id = $api_id; + } + + /** + * Set the API password + * + * @param $password the password to set + * @return void + */ + public function setPassword( $password ) + { + $this->_password = $password; + } + + /** + * Set the username parameter + * + * @param $username The username to set + * @retrn void + */ + public function setUsername( $username ) + { + $this->_username = $username; + } + + /** + * Is SSL enabled? + * Returns a boolean indicating whether SSL is enabled or not + * + * @return bool + */ + public function getSSL() + { + return (bool)$this->_ssl; + } + + /** + * Get the API ID + * + * @return string + */ + public function getApiId() + { + return $this->_api_id; + } + + /** + * Get the password + * + * @return string + */ + public function getPassword() + { + return $this->_password; + } + + /** + * Get the username + * + * @return string + */ + public function getUsername() + { + return $this->_username; + } + + + + /** + * Set the logger object + * + * @param Zend_Logger $logger The Zend_Logger object + * @return void + */ + public function setLogger( &$logger ) + { + $this->_logger = $logger; + } + + /** + * Get the logger object + * + * @return Zend_Logger + */ + protected function getLogger() + { + return $this->_logger; + } + + + /** + * Set the log level + * + * @param int $loglevel The log level + * @return void + */ + public function setLogLevel( $loglevel ) + { + $this->_loglevel = $loglevel; + } + + /** + * Get the log level + * + * @return int + */ + protected function getLogLevel() + { + return $this->_loglevel; + } + + + /** + * Add an entry to the log + * + * @param string $log Log message + * @return void + */ + protected function log( $log ) + { + if( $this->getLogger() === null ) + return; + + $this->getLogger()->log( 'Clickatell: ' . $log, $this->getLogLevel() ); + } + + + + /** + * Get the last batch of send IDs + * + * Returns an associated array of number => ID where number is the destination SMS + * number and ID is the send ID from the Clickatell API. + * + * @return array + */ + public function getSendIds() + { + return $this->_send_ids; + } + + /** + * Get the last batch of send errors + * + * Returns an associated array of number => error_message where number is the destination SMS + * number and error is that as returned from the Clickatell API. + * + * @return array + */ + public function getSendErrors() + { + return $this->_send_errors; + } + + /** + * Get the text description for a given state + * + * @see OSS_Service_Clickatell_Abstract::$MESSAGE_STATES + * @see OSS_Service_Clickatell_Http::queryMessage() + * + * @param string The state to query + * @return void + */ + public function getMessageStateDescription( $state ) + { + return OSS_Service_Clickatell_Abstract::$MESSAGE_STATES[$state]; + } + +} + + diff --git a/library/OSS/Service/Clickatell/Exception.php b/library/OSS/Service/Clickatell/Exception.php new file mode 100644 index 0000000..00f5035 --- /dev/null +++ b/library/OSS/Service/Clickatell/Exception.php @@ -0,0 +1,49 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Service + * @subpackage Clickatell + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Service_Clickatell_Exception extends Zend_Service_Exception +{ +} diff --git a/library/OSS/Service/Clickatell/Http.php b/library/OSS/Service/Clickatell/Http.php new file mode 100644 index 0000000..330c03c --- /dev/null +++ b/library/OSS/Service/Clickatell/Http.php @@ -0,0 +1,368 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Service + * @subpackage Clickatell + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Service_Clickatell_Http extends OSS_Service_Clickatell_Abstract +{ + + /** + * API identifiers for different tasks + */ + const API_METHOD_BALANCE = 'getbalance'; + const API_METHOD_SENDSMS = 'sendmsg'; + const API_METHOD_QUERY = 'querymsg'; + + const FEAT_TEXT = 1; // Text – set by default. + + /** + * Features. + * + * for more than one feature request add the values up and pass the sum (FEAT_8BIT + FEAT_ALPHA + FEAT_CONCAT) + */ + const FEAT_8BIT = 2; // 8-bit messaging – set by default. + const FEAT_UDH = 4; // UDH (Binary) - set by default. + const FEAT_UCS2 = 8; // UCS2 / Unicode – set by default. + const FEAT_ALPHA = 16; // Alpha source address (from parameter). Sender ID + const FEAT_NUMER = 32; // Numeric source address (from parameter). Sender ID + const FEAT_FLASH = 512; // Flash messaging. + const FEAT_DELIVACK = 8192; // Delivery acknowledgments. + const FEAT_CONCAT = 16384; // Concatenation – set by default + + + /** + * The API URL for Clickatell + * + * @var string + */ + protected $_api_url = 'api.clickatell.com/http/'; + + /** + * Use sessions or authenticate for each request + * + * @var bool + */ + protected $_use_session = false; + + /** + * HTTP reponse object + * + * @var object + */ + protected $_response; + + + /** + * A string containing the Sender ID, which is a short ( <= 11 characters) string + * + * @var string + */ + protected $_sender_id; + + + /** + * Constructor + * + * Accepted associated array parameters: + * api_url use_ession + * Also see parent constructor for additional options. + * + * @param $options array An associated array of options (see above for accepted keys). + * @return void + */ + public function __construct( $options = null ) + { + if ( is_array( $options ) ) + { + parent::__construct( $options ); + + foreach( $options as $param => $value ) + { + switch( $param ) + { + case 'api_url': + $this->setApiUrl( $value ); + break; + + case 'use_session': + $this->setUseSession( $value ); + break; + + case 'sender_id': + $this->_sender_id = $value; + break; + } + } + } + + if ( !is_array( $options ) || !array_key_exists( 'api_url', $options ) ) $this->setApiUrl( $this->_api_url ); + } + + /** + * A generic function to place API calls and catch common errors early (e.g. auth failure) + * + * @param string $method The method to call (see OSS_Service_Clickatell_Http::API_METHOD_XXX) + * @throws OSS_Service_Clickatell_Exception + * @return void + */ + protected function call( $method ) + { + $this->getHttpClient()->setUri( $this->getApiUrl() . $method ); + $this->setAuthParams(); + $this->_response = $this->getHttpClient()->request(); + + // ensure we have valid authentication parameters + $auth = explode( ': ', $this->_response->getBody(), 2 ); + + if ( $auth[0] == 'ERR' ) + { + $error = explode( ',', $auth[1], 2 ); + + if ( $error[0] == '001' ) + { + throw new OSS_Service_Clickatell_Exception( 'Clickatell authentication failed' ); + } + else + { + throw new OSS_Service_Clickatell_Exception( 'Clickatell Error: ' . $auth[1] ); + } + } + } + + + /** + * Get the Clickatell account balance. + * + * This returns the number of credits remaining. + * + * @throws OSS_Service_Clickatell_Exception + * @return string + */ + public function getBalance() + { + $this->call( OSS_Service_Clickatell_Http::API_METHOD_BALANCE ); + + $credit = explode( ': ', $this->_response->getBody(), 2 ); + + if ( $credit[0] != 'Credit' ) throw new OSS_Service_Clickatell_Exception( 'Unknown response for Clickatell credit check' ); + + $this->log( 'Retrieved balance: ' . $credit[1] ); + + return $credit[1]; + } + + + /** + * Send a one off SMS to a single or multiple users + * + * If $number is an array, then the SMS will be sent to multple users. + * + * The success IDs or send errors will be found referenced by the destination number + * via getSendIds() and getSendErrors(). + * + * You cannot just use any sender id you like, first you have to register and approve it by Clickatell. + * + * Return true if all messages sent successfully, false if there were errors (@see OSS_Service_Clickatell_Abstract::$_send_errors) + * + * @param string|array $number The full international version of the number to send the SMS to (or an array of multiple numnbers) + * @param string $message The message to send + * @param string $senderid NULL to use the default from application.ini, false to not to use any, a valid international format number between 1 and 16 characters long, or an 11 character alphanumeric string + * @return bool + */ + public function sendSms( $number, $message, $senderid=null ) + { + $this->_send_errors = array(); + $this->_send_ids = array(); + + if ( !is_array( $number ) ) $number = array( $number ); + + foreach( array_chunk( $number, 100 ) as $numbers ) + { + if ( ($senderid === null) && ($this->_sender_id != '') ) + { + $this->getHttpClient()->setParameterGet( 'req_feat', OSS_Service_Clickatell_Http::FEAT_ALPHA ); + $this->getHttpClient()->setParameterGet( 'from', $this->_sender_id ); + } + elseif ($senderid !== false) + { + $this->getHttpClient()->setParameterGet( 'req_feat', OSS_Service_Clickatell_Http::FEAT_ALPHA ); + $this->getHttpClient()->setParameterGet( 'from', trim(mb_substr($senderid, 0, 11)) ); + } + + $this->getHttpClient()->setParameterGet( 'to', implode( ',', $number ) ); + $this->getHttpClient()->setParameterGet( 'text', $message ); + + $this->call( OSS_Service_Clickatell_Http::API_METHOD_SENDSMS ); + + if ( count( $number ) == 1 ) + { + $send = explode( ': ', $this->_response->getBody(), 2 ); + + if( $send[0] != 'ID' ) + { + $this->_send_errors[$number[0]] = $this->_response->getBody(); + //$this->log( 'Sent SMS to : ' . $number[0] . ' with send ID: ' . $send[1] ); + $this->log( 'SMS sent to : ' . $number[0] . ' returned with error: ' . $this->_response->getBody() ); + return false; + } + + $this->log( 'Sent SMS to: ' . $number[0] . ' with send ID: ' . $send[1] ); + $this->_send_ids = array( $numbers[0] => $send[1] ); + return true; + } + + foreach( explode( "\n", $this->_response->getBody() ) as $line ) + { + $data = explode( ' ', $line ); + + if( $data[0] == 'ID:' ) + $this->_send_ids[$data[3]] = $data[1]; + else + $this->_send_errors[$data[ count($data) - 1]] = $line; + } + } + + $this->log( 'Sent ' . count( $this->_send_ids ) . '/' . count($number) . ' SMS messages' ); + + return ( count($this->_send_errors) == 0 ? true : false); + } + + + + /** + * Query the status of a message previously sent. + * + * Use OSS_Service_Clickatll_Abstract::$MESSAGE_STATES and OSS_Service_Clickatll_Abstract::getMessageStateDescription() + * to interpret the state. + * + * @throws OSS_Service_Clickatell_Exception + * @param $msgid string The message ID to query + * @returns string + */ + public function queryMessage( $msgid ) + { + $this->getHttpClient()->setParameterGet( 'apimsgid', $msgid ); + $this->call( OSS_Service_Clickatell_Http::API_METHOD_QUERY ); + + $data = explode( ' ', $this->_response->getBody() ); + + if( $data[0] == 'ID:' ) + return $data[3]; + + throw new OSS_Service_Clickatell_Exception( 'Unknown response for Clickatell message query: ' + . $this->_response->getBody() ); + } + + + /** + * Set the API URL + * @param $api_url the API ID to set + * @return void + */ + public function setApiUrl( $api_url ) + { + $this->_api_url = $api_url; + + $this->setHttpClient( new Zend_Http_Client( $this->getApiUrl() ) ); + } + + + /** + * Sets the authentication params for the URI on the HTTP client object + * + * Specifically, if we are using sessions, then a session ID parameter will + * be set, otherwise we'll set a username, password and API ID. + * + * @return void + */ + public function setAuthParams() + { + if( $this->getUseSession() ) + { + // FIXME Need to add session functionality + } + else + { + $this->getHttpClient()->setParameterGet( 'user', $this->getUsername() ); + $this->getHttpClient()->setParameterGet( 'password', $this->getPassword() ); + $this->getHttpClient()->setParameterGet( 'api_id', $this->getApiId() ); + } + } + + + /** + * Get the API URL + * + * @return string + */ + public function getApiUrl() + { + return ( $this->getSSL() ? 'https://' : 'http://' ) . $this->_api_url; + } + + + /** + * Should we use sessions? + * + * @param $api_url the API ID to set + * @return void + */ + public function setUseSession( $use_session = true ) + { + $this->_use_session = (bool)$use_session; + } + + + /** + * Should we use sessions? + * Returns true if we will use sessions + * + * @return bool + */ + public function getUseSession() + { + return $this->_use_session; + } + +} diff --git a/library/OSS/Service/Freshbooks.php b/library/OSS/Service/Freshbooks.php new file mode 100644 index 0000000..9a8246e --- /dev/null +++ b/library/OSS/Service/Freshbooks.php @@ -0,0 +1,1496 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @see Zend_Rest_Client + */ +require_once 'Zend/Rest/Client.php'; + +/** + * @see Zend_Rest_Client_Result + */ +require_once 'Zend/Rest/Client/Result.php'; + +/** + * @see Zend_Oauth_Consumer + */ +require_once 'Zend/Oauth/Consumer.php'; + +/** + * @category OSS + * @package OSS_Service + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Service_Freshbooks extends Zend_Rest_Client +{ + + /** + * Cookie jar + * + * @var Zend_Http_CookieJar + */ + protected $_cookieJar; + + /** + * Date format for 'since' strings + * + * @var string + */ + protected $_dateFormat = 'D, d M Y H:i:s T'; + + /** + * Subomain + * + * @var string + */ + protected $_subdomain; + + /** + * Current method type (for method proxying) + * + * @var string + */ + protected $_methodType; + + /** + * Zend_Oauth Consumer + * + * @var Zend_Oauth_Consumer + */ + protected $_oauthConsumer = null; + + /** + * Types of API methods + * + * @var array + */ + protected $_methodTypes = array( + 'system.current', + 'client.create', + 'client.update', + 'client.get', + 'client.delete', + 'client.list', + 'invoice.create', + 'invoice.update', + 'invoice.get', + 'invoice.delete', + 'invoice.list', + 'invoice.sendByEmail', + //'invoice.sendBySnailMail', + 'invoice.lines.add', + 'invoice.lines.delete', + 'invoice.lines.update', + 'recurring.create', + 'recurring.update', + 'recurring.get', + 'recurring.delete', + 'recurring.list', + 'recurring.lines.add', + 'recurring.lines.delete', + 'recurring.lines.update' + ); + + /** + * Options passed to constructor + * + * @var array + */ + protected $_options = array(); + + /** + * Local HTTP Client cloned from statically set client + * + * @var Zend_Http_Client + */ + protected $_localHttpClient = null; + + /** + * Constructor + * + * @param array $options Options array + * @param null|Zend_Oauth_Consumer Optional consumer + * @return void + */ + public function __construct( $options, Zend_Oauth_Consumer $consumer = null ) + { + if( $options instanceof Zend_Config ) + $options = $options->toArray(); + + if( !is_array( $options ) ) + $options = array(); + + $options['signatureMethod'] = 'PLAINTEXT'; + + $this->_options = $options; + if( isset( $options[ 'subdomain' ] ) ) + { + + $this->setSubdomain( $options[ 'subdomain' ] ); + $options['consumerKey'] = $options[ 'subdomain' ]; + } + $this->setUri( 'https://'. $this->getSubdomain() . '.freshbooks.com/api/2.1/xml-in' ); + + $options[ 'siteUrl' ] = 'https://'. $this->getSubdomain() . '.freshbooks.com/oauth'; + + if( isset( $options[ 'accessToken' ] ) + && $options[ 'accessToken' ] instanceof Zend_Oauth_Token_Access ) + { + $this->setLocalHttpClient( $options['accessToken']->getHttpClient( $options ) ); + } + else + { + $this->setLocalHttpClient( clone self::getHttpClient() ); + if( $consumer === null ) + $this->_oauthConsumer = new Zend_Oauth_Consumer( $options ); + else + $this->_oauthConsumer = $consumer; + } + } + + /** + * Set local HTTP client as distinct from the static HTTP client + * as inherited from Zend_Rest_Client. + * + * @param Zend_Http_Client $client + * @return OSS_Service_Freshbooks + */ + public function setLocalHttpClient( Zend_Http_Client $client ) + { + $this->_localHttpClient = $client; + $this->_localHttpClient->setHeaders( 'Accept-Charset', 'ISO-8859-1,utf-8' ); + return $this; + } + + /** + * Get the local HTTP client as distinct from the static HTTP client + * inherited from Zend_Rest_Client + * + * @return Zend_Http_Client + */ + public function getLocalHttpClient() + { + return $this->_localHttpClient; + } + + /** + * Checks for an authorised state + * + * @return bool + */ + public function isAuthorised() + { + if( $this->getLocalHttpClient() instanceof Zend_Oauth_Client ) + return true; + + return false; + } + + /** + * Retrieve username + * + * @return string + */ + public function getSubdomain() + { + return $this->_subdomain; + } + + /** + * Set username + * + * @param string $value + * @return OSS_Service_Freshbooks + */ + public function setSubdomain( $value ) + { + $this->_subdomain = $value; + return $this; + } + + /** + * Proxy service methods + * + * @param string $type + * @throws Zend_Service_Exception If method not in method types list + * @return OSS_Service_Freshbooks + */ + public function __get( $type ) + { + if( !in_array( $type, $this->_methodTypes ) ) + { + include_once 'Zend/Service/Exception.php'; + throw new Zend_Service_Exception( + 'Invalid method type "' . $type . '"' + ); + } + $this->_methodType = $type; + return $this; + } + + /** + * Method overloading + * + * @param string $method + * @param array $params + * @throws Zend_Service_Exception if unable to find method + * @return mixed + */ + public function __call( $method, $params ) + { + if( method_exists( $this->_oauthConsumer, $method ) ) + { + $return = call_user_func_array( array( $this->_oauthConsumer, $method ), $params ); + if( $return instanceof Zend_Oauth_Token_Access ) + { + $this->setLocalHttpClient( $return->getHttpClient( $this->_options ) ); + } + return $return; + } + if( empty( $this->_methodType ) ) + { + include_once 'Zend/Service/Exception.php'; + throw new Zend_Service_Exception( + 'Invalid method "' . $method . '"' + ); + } + $test = $this->_methodType . ucfirst($method); + if( !method_exists( $this, $test ) ) + { + include_once 'Zend/Service/Exception.php'; + throw new Zend_Service_Exception( + 'Invalid method "' . $test . '"' + ); + } + + return call_user_func_array( array( $this, $test ), $params ); + } + + /** + * Initialize HTTP authentication + * + * @throws Zend_Service_Exception if not authorised + * @return void + */ + protected function _init() + { + if( !$this->isAuthorised() && $this->getUsername() !== null ) + { + require_once 'Zend/Service/Exception.php'; + throw new Zend_Service_Exception( + 'Freshbooks session is unauthorised. You need to initialize ' + . 'OSS_Service_Freshbooks with an OAuth Access Token or use ' + . 'its OAuth functionality to obtain an Access Token before ' + . 'attempting any API actions that require authorisation' + ); + } + $client = $this->_localHttpClient; + $client->resetParameters(); + if( null == $this->_cookieJar ) + { + $client->setCookieJar(); + $this->_cookieJar = $client->getCookieJar(); + } + else + { + $client->setCookieJar( $this->_cookieJar ); + } + } + + /** + * Set date header + * + * @param int|string $value + * @return void + */ + protected function _setDate( $value ) + { + if( is_int( $value ) ) + $date = date( $this->_dateFormat, $value ); + else + $date = date( $this->_dateFormat, strtotime( $value ) ); + + $this->_localHttpClient->setHeaders( 'If-Modified-Since', $date ); + } + + /** + * Public system current + * + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function systemCurrent() + { + $this->_init(); + + $xml = ''. + '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + + /** + * Public client create + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function clientCreate( $params ) + { + $this->_init(); + + $xml = '' . + '' . + ''; + if( $params && is_array( $params ) ) + $xml .= $this->_makeClientXml( $params ); + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public client create + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function clientUpdate( $params ) + { + $this->_init(); + + $xml = '' . + '' . + ''; + if( $params && is_array( $params ) ) + $xml .= $this->_makeClientXml( $params ); + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public client get + * + * @param int $client_id Id of the client + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function clientGet( $client_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $client_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public client delete + * + * @param int $client_id Id of the client + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function clientDelete( $client_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $client_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public client list + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function clientList( $params = null ) + { + $this->_init(); + + $xml = '' . + ''; + if( $params && is_array( $params ) ) + { + //Filter by email address (Optional) + if( isset( $params['email'] ) ) + $xml .= '' . $params['email'] . ''; + + //Filter by username (Optional) + if( isset( $params['username'] ) ) + $xml .= '' . $params['username'] . ''; + + //Return only clients modified since this date (Optional) + if( isset( $params['updated_from'] ) ) + $xml .= '' . $params['updated_from']->format('Y-m-d H:i:s') . ''; + + //Return only clients modified before this date (Optional) + if( isset( $params['updated_to'] ) ) + $xml .= '' . $params['updated_to']->format('Y-m-d H:i:s') . ''; + + //The page number to show (Optional) + if( isset( $params['page'] ) ) + $xml .= '' . $params['page'] . ''; + + //Number of results per page, default 25 (Optional) + if( isset( $params['per_page'] ) ) + $xml .= '' . $params['per_page'] . ''; + + //One of 'active', 'archived', 'deleted' (Optional) + if( isset( $params['folder'] ) ) + $xml .= '' . $params['folder'] . ''; + + //Return only clients with this text in their 'notes' (Optional) + if( isset( $params['notes'] ) ) + $xml .= '' . $params['notes'] . ''; + } + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice create + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceCreate( $params ) + { + $this->_init(); + + $xml = '' . + '' . + ''; + if( $params && is_array( $params ) ) + $xml .= $this->_makeInvoiceXml( $params ); + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice update + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceUpdate( $params ) + { + $this->_init(); + + $xml = '' . + '' . + ''; + if( $params && is_array( $params ) ) + $xml .= $this->_makeInvoiceXml( $params ); + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice get + * + * @param int $invoice_id Id of the invoice + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceGet( $invoice_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $invoice_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice delete + * + * @param int $invoice_id Id of the invoice + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceDelete( $invoice_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $invoice_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + + /** + * Public invoice list + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceList( $params = null) + { + $this->_init(); + + $xml = '' . + ''; + if( $params && is_array( $params ) ) + { + //Filter by client (Optional) + if( isset( $params['client_id'] ) ) + $xml .= '' . $params['client_id'] . ''; + + //Filter by recurring id (Optional) + if( isset( $params['requirring_id'] ) ) + $xml .= '' . $params['requirring_id'] . ''; + + //Filter by status (Optional) + if( isset( $params['status'] ) ) + $xml .= '' . $params['status'] . ''; + + //Returns invoices with a number like this arg (Optional) + if( isset( $params['number'] ) ) + $xml .= '' . $params['number'] . ''; + + //Return invoices dated after this arg (Optional) + if( isset( $params['date_from'] ) ) + $xml .= '' . $params['date_from']->format('Y-m-d H:i:s') . ''; + + //Return invoices dated before this arg (Optional) + if( isset( $params['date_to'] ) ) + $xml .= '' . $params['date_to']->format('Y-m-d H:i:s') . ''; + + //Return invoices modified after this arg (Optional) + if( isset( $params['updated_from'] ) ) + $xml .= '' . $params['updated_from']->format('Y-m-d H:i:s') . ''; + + //Return invoices modified before this arg (Optional) + if( isset( $params['updated_to'] ) ) + $xml .= '' . $params['updated_to']->format('Y-m-d H:i:s') . ''; + + //Page number to return, default is 1 (Optional) + if( isset( $params['page'] ) ) + $xml .= '' . $params['page'] . ''; + + //Number of results per page, default is 25 (Optional) + if( isset( $params['per_page'] ) ) + $xml .= '' . $params['per_page'] . ''; + + //One of 'active', 'archived', 'deleted' (Optional) + if( isset( $params['folder'] ) ) + $xml .= '' . $params['folder'] . ''; + } + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice send by email + * + * @param int $invoice_id Id of the invoice + * @param string $subject Email subject + * @param string $message Email body to add invoice + * link write '::invoice link::'. + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceSendByEmail( $invoice_id, $subject = null, $message = null ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $invoice_id . ''; + if( $subject ) + $xml .= '' . $subject . ''; + + if( $message ) + $xml .= '' . $message . ''; + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice lines add + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceLinesAdd( $params ) + { + $this->_init(); + + $xml = '' . + ''; + if( $params && is_array( $params ) ) + { + //Invoice to update + if( isset( $params['invoice_id'] ) ) + $xml .= '' . $params['invoice_id'] . ''; + + if( isset( $params['lines'] ) ) + $xml .= $this->_makeLinesXml( $params['lines'] ); + + } + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice line delete + * + * @param int $invoice_id Id of the invoice + * @param int $line_id Id of the line to delete + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceLinesDelete( $invoice_id, $line_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $invoice_id . '' . + '' . $line_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public invoice lines update + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function invoiceLinesUpdate( $params ) + { + $this->_init(); + + $xml = '' . + ''; + if( $params && is_array( $params ) ) + { + //Invoice to update + if( isset( $params['invoice_id'] ) ) + $xml .= '' . $params['invoice_id'] . ''; + + if( isset( $params['lines'] ) ) + $xml .= $this->_makeLinesXml( $params['lines'] ); + } + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring create + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringCreate( $params ) + { + $this->_init(); + + $xml = '' . + '' . + ''; + if( $params && is_array( $params ) ) + $xml .= $this->_makeRecurringXml( $params ); + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring update + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringUpdate( $params ) + { + $this->_init(); + + $xml = '' . + '' . + ''; + if( $params && is_array( $params ) ) + $xml .= $this->_makeRecurringXml( $params ); + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring get + * + * @param int $recurring_id Id of the recurring + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringGet( $recurring_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $recurring_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring delete + * + * @param int $recurring_id Id of the recurring + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringDelete( $recurring_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $recurring_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring list + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringList( $params = null) + { + $this->_init(); + + $xml = '' . + ''; + if( $params && is_array( $params ) ) + { + //Filter by client (Optional) + if( isset( $params['client_id'] ) ) + $xml .= '' . $params['client_id'] . ''; + + //Return auto-bills dated after this arg (Optional) + if( isset( $params['date_from'] ) ) + $xml .= '' . $params['date_from']->format('Y-m-d H:i:s') . ''; + + //Return auto-bills dated before this arg (Optional) + if( isset( $params['date_to'] ) ) + $xml .= '' . $params['date_to']->format('Y-m-d H:i:s') . ''; + + //Return auto-bills modified after this arg (Optional) + if( isset( $params['updated_from'] ) ) + $xml .= '' . $params['updated_from']->format('Y-m-d H:i:s') . ''; + + //Return auto-bills modified before this arg (Optional) + if( isset( $params['updated_to'] ) ) + $xml .= '' . $params['updated_to']->format('Y-m-d H:i:s') . ''; + + //Filter auto-bill profiles (Optional) + if( isset( $params['autobill'] ) ) + $xml .= '' . $params['autobill'] . ''; + + //Page number to return, default is 1 (Optional) + if( isset( $params['page'] ) ) + $xml .= '' . $params['page'] . ''; + + //Number of results per page, default is 25 (Optional) + if( isset( $params['per_page'] ) ) + $xml .= '' . $params['per_page'] . ''; + + //One of 'active', 'archived', 'deleted' (Optional) + if( isset( $params['folder'] ) ) + $xml .= '' . $params['folder'] . ''; + } + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring lines add + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringLinesAdd( $params ) + { + $this->_init(); + + $xml = '' . + ''; + if( $params && is_array( $params ) ) + { + //Invoice to update + if( isset( $params['recurring_id'] ) ) + $xml .= '' . $params['recurring_id'] . ''; + + if( isset( $params['lines'] ) ) + $xml .= $this->_makeLinesXml( $params['lines'] ); + + } + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring line delete + * + * @param int $recurring_id Id of the recurring + * @param int $line_id Id of the line to delete + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringLinesDelete( $recurring_id, $line_id ) + { + $this->_init(); + + $xml = ''. + '' . + '' . $recurring_id . '' . + '' . $line_id . '' . + ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Public recurring lines update + * + * @params araray $params Params of request + * @throws Zend_Http_Client_Exception if HTTP request fails or times out + * @return Zend_Rest_Client_Result + */ + public function recurringLinesUpdate( $params ) + { + $this->_init(); + + $xml = '' . + ''; + if( $params && is_array( $params ) ) + { + //Invoice to update + if( isset( $params['recurring_id'] ) ) + $xml .= '' . $params['recurring_id'] . ''; + + if( isset( $params['lines'] ) ) + $xml .= $this->_makeLinesXml( $params['lines'] ); + } + + $xml .= ''; + + $response = $this->_post( "", $xml ); + + return new Zend_Rest_Client_Result( $response->getBody() ); + } + + /** + * Call a remote REST web service URI and return the Zend_Http_Response object + * + * @param string $path The path to append to the URI + * @throws Zend_Rest_Client_Exception + * @return void + */ + protected function _prepare( $path = "" ) + { + // Get the URI object and configure it + if( !$this->_uri instanceof Zend_Uri_Http ) + { + require_once 'Zend/Rest/Client/Exception.php'; + throw new Zend_Rest_Client_Exception( + 'URI object must be set before performing call' + ); + } + + if( $path != "" ) + { + $uri = $this->_uri->getUri(); + + if( $path[0] != '/' && $uri[ strlen( $uri ) - 1 ] != '/' ) + $path = '/' . $path; + + $this->_uri->setPath( $path ); + } + + /** + * Get the HTTP client and configure it for the endpoint URI. + * Do this each time because the Zend_Http_Client instance is shared + * among all Zend_Service_Abstract subclasses. + */ + $this->_localHttpClient->resetParameters()->setUri( ( string ) $this->_uri ); + } + + /** + * Performs an HTTP GET request to the $path. + * + * @param string $path + * @param array $query Array of GET parameters + * @throws Zend_Http_Client_Exception + * @return Zend_Http_Response + */ + protected function _get( $path = "", array $query = null ) + { + $this->_prepare( $path ); + $this->_localHttpClient->setParameterGet( $query ); + return $this->_localHttpClient->request( Zend_Http_Client::GET ); + } + + /** + * Performs an HTTP POST request to $path. + * + * @param string $path + * @param mixed $data Raw data to send + * @throws Zend_Http_Client_Exception + * @return Zend_Http_Response + */ + protected function _post( $path = "", $data = null ) + { + $this->_prepare( $path ); + return $this->_performPost( Zend_Http_Client::POST, $data ); + } + + /** + * Perform a POST or PUT + * + * Performs a POST or PUT request. Any data provided is set in the HTTP + * client. String data is pushed in as raw POST data; array or object data + * is pushed in as POST parameters. + * + * @param mixed $method + * @param mixed $data + * @return Zend_Http_Response + */ + protected function _performPost( $method, $data = null ) + { + $client = $this->_localHttpClient; + + if( is_string( $data ) ) + $client->setRawData( $data ); + elseif( is_array( $data ) || is_object( $data ) ) + $client->setParameterPost( (array) $data ); + + return $client->request( $method ); + } + + + /** + * Makes addres string xml from address array + * + * @param array $address The address data. + * @param string $type The type of address( primary(p | secondary (s) ) + * @return string + */ + protected function _makeAddressXml( $address, $type ) + { + $xml = ""; + $type = substr( $type, 0, 1 ); + if( isset( $address['street1'] ) ) + $xml .= "<{$type}_street1>" . $address['street1'] . ""; + + if( isset( $address['street2'] ) ) + $xml .= "<{$type}_street2>" . $address['street2'] . ""; + + if( isset( $address['city'] ) ) + $xml .= "<{$type}_city>" . $address['city'] . ""; + + if( isset( $address['state'] ) ) + $xml .= "<{$type}_state>" . $address['state'] . ""; + + if( isset( $address['country'] ) ) + $xml .= "<{$type}_country>" . $address['country'] . ""; + + if( isset( $address['code'] ) ) + $xml .= "<{$type}_code>" . $address['code'] . ""; + + return $xml; + } + + + /** + * Makes contacts string xml from contacts array + * + * @param array $contacts The contacts data. + * @return string + */ + protected function _makeContactsXml( $contacts ) + { + $xml .= ''; + foreach( $contacts as $key => $value ) + { + $xml .= ''; + + if( isset( $value['contact_id'] ) ) + $xml .= '' . $value['contact_id'] . ''; + + if( isset( $value['username'] ) ) + $xml .= '' . $value['username'] . ''; + + if( isset( $value['first_name'] ) ) + $xml .= '' . $value['first_name'] . ''; + + if( isset( $value['last_name'] ) ) + $xml .= '' . $value['last_name'] . ''; + + //Email address is the only required field + if( isset( $value['email'] ) ) + $xml .= '' . $value['email'] . ''; + + if( isset( $value['phone1'] ) ) + $xml .= '' . $value['phone1'] . ''; + + if( isset( $value['phone2'] ) ) + $xml .= '' . $value['phone2'] . ''; + + $xml .= ''; + } + $xml .= '/'; + + return $xml; + } + + /** + * Makes lines string xml from lines array + * + * @param array $lines The lines data. + * @return string + */ + protected function _makeLinesXml( $lines) + { + $xml = ''; + foreach( $lines as $key => $value ) + { + $xml .= ''; + + //(Optional) + if( isset( $value['line_id'] ) ) + $xml .= '' . $value['line_id'] . ''; + + //(Optional) + if( isset( $value['amount'] ) ) + $xml .= '' . $value['amount'] . ''; + + //(Optional) + if( isset( $value['name'] ) ) + $xml .= '' . $value['name'] . ''; + + //(Optional) + if( isset( $value['description'] ) ) + $xml .= '' . $value['description'] . ''; + + //Default is 0 + if( isset( $value['unit_cost'] ) ) + $xml .= '' . $value['unit_cost'] . ''; + + //Default is 0 + if( isset( $value['quantity'] ) ) + $xml .= '' . $value['quantity'] . ''; + + //(Optional) + if( isset( $value['tax1_name'] ) ) + $xml .= '' . $value['tax1_name'] . ''; + + //(Optional) + if( isset( $value['tax1_percent'] ) ) + $xml .= '' . $value['tax1_percent'] . ''; + + //(Optional) + if( isset( $value['tax2_name'] ) ) + $xml .= '' . $value['tax2_name'] . ''; + + //(Optional) + if( isset( $value['tax2_percent'] ) ) + $xml .= '' . $value['tax2_percent'] . ''; + + //One of 'Item' or 'Time'. If omitted, the line's type defaults to 'Item' + if( isset( $value['type'] ) ) + $xml .= '' . $value['type'] . ''; + + $xml .= ''; + } + $xml .= ''; + + return $xml; + } + + /** + * Makes client string xml from params + * + * @param array $params The client params. + * @return string + */ + protected function _makeClientXml( $params ) + { + $xml = ""; + + if( isset( $params['client_id'] ) ) + $xml .= '' . $params['client_id'] . ''; + + if( isset( $params['first_name'] ) ) + $xml .= '' . $params['first_name'] . ''; + + if( isset( $params['last_name'] ) ) + $xml .= '' . $params['last_name'] . ''; + + if( isset( $params['organization'] ) ) + $xml .= '' . $params['organization'] . ''; + + if( isset( $params['email'] ) ) + $xml .= '' . $params['email'] . ''; + + //Defaults to first name + last name (Optional) + if( isset( $params['username'] ) ) + $xml .= '' . $params['username'] . ''; + + //Defaults to random password (Optional) + if( isset( $params['password'] ) ) + $xml .= '' . $params['password'] . ''; + + //(Optional) + if( isset( $params['contacts'] ) ) + $xml .= $this->_makeContactsXml( $params['contacts'] ); + //(Optional) + if( isset( $params['work_phone'] ) ) + $xml .= '' . $params['work_phone'] . ''; + + //(Optional) + if( isset( $params['home_phone'] ) ) + $xml .= '' . $params['home_phone'] . ''; + + //(Optional) + if( isset( $params['mobile'] ) ) + $xml .= '' . $params['mobile'] . ''; + + //(Optional) + if( isset( $params['fax'] ) ) + $xml .= '' . $params['fax'] . ''; + + //See language.list for codes. (Optional) + if( isset( $params['language'] ) ) + $xml .= '' . $params['language'] . ''; + + //(Optional) + if( isset( $params['currency_code'] ) ) + $xml .= '' . $params['currency_code'] . ''; + + //(Optional) + if( isset( $params['notes'] ) ) + $xml .= '' . $params['notes'] . ''; + + //Primary address (All optional) + if( isset( $params['primary_address'] ) ) + $xml .= $this->_makeAddressXml( $params['primary_address'], 'priamry' ); + + //secondary address (All optional) + if( isset( $params['secondary_address'] ) ) + $xml .= $this->_makeAddressXml( $params['secondary_address'], 'secondary' ); + + //e.g. 'VAT Number' (Optional) + if( isset( $params['vat_name'] ) ) + $xml .= '' . $params['vat_name'] . ''; + + //If set, shown with vat_name under client address (Optional) + if( isset( $params['vat_number'] ) ) + $xml .= '' . $params['vat_number'] . ''; + + return $xml; + } + + + /** + * Makes invoice string xml from params + * + * @param array $params The invoice params. + * @return string + */ + protected function _makeInvoiceXml( $params ) + { + $xml = ""; + + //Invoice to update + if( isset( $params['invoice_id'] ) ) + $xml .= '' . $params['invoice_id'] . ''; + + //Client being invoiced + if( isset( $params['client_id'] ) ) + $xml .= '' . $params['client_id'] . ''; + + //(Optional) + if( isset( $params['contacts'] ) ) + $xml .= $this->_makeContactsXml( $params['contacts'] ); + + //Number, as it appears on the invoice (Optional) + if( isset( $params['number'] ) ) + $xml .= '' . $params['number'] . ''; + + //One of sent, viewed or draft [default] + if( isset( $params['status'] ) ) + $xml .= '' . $params['status'] . ''; + + //If not supplied, defaults to today's date (Optional) + if( isset( $params['date'] ) ) + $xml .= '' . $params['date']->format( 'Y-m-d' ) . ''; + + //Purchase order number (Optional) + if( isset( $params['po_number'] ) ) + $xml .= '' . $params['po_number'] . ''; + + //Percent discount (Optional) + if( isset( $params['discount'] ) ) + $xml .= '' . $params['discount'] . ''; + + //Notes (Optional) + if( isset( $params['notes'] ) ) + $xml .= '' . $params['notes'] . ''; + + //Currency Code, defaults to your base currency (Optional) + if( isset( $params['currency_code'] ) ) + $xml .= '' . $params['currency_code'] . ''; + + //Language code, defaults to the client's language; see language.list for codes (Optional) + if( isset( $params['language'] ) ) + $xml .= '' . $params['language'] . ''; + + //Terms (Optional) + if( isset( $params['terms'] ) ) + $xml .= '' . $params['terms'] . ''; + + //Return URI (Optional) + if( isset( $params['return_uri'] ) ) + $xml .= '' . $params['return_uri'] . ''; + + //(Optional) + if( isset( $params['first_name'] ) ) + $xml .= '' . $params['first_name'] . ''; + + //(Optional) + if( isset( $params['last_name'] ) ) + $xml .= '' . $params['last_name'] . ''; + + //(Optional) + if( isset( $params['organization'] ) ) + $xml .= '' . $params['organization'] . ''; + + //Primary address (All optional) + if( isset( $params['primary_address'] ) ) + $xml .= $this->_makeAddressXml( $params['primary_address'], 'primary' ); + + //e.g. 'VAT Number' (Optional) + if( isset( $params['vat_name'] ) ) + $xml .= '' . $params['vat_name'] . ''; + + //If set, shown with vat_name under client address (Optional) + if( isset( $params['vat_number'] ) ) + $xml .= '' . $params['vat_number'] . ''; + + if( isset( $params['lines'] ) ) + $xml .= $this->_makeLinesXml( $params['lines'] ); + + return $xml; + } + + + /** + * Makes recurring string xml from params + * + * @param array $params The recurring params. + * @return string + */ + protected function _makeRecurringXml( $params ) + { + $xml = ""; + + //Recurring to update + if( isset( $params['recurring_id'] ) ) + $xml .= '' . $params['recurring_id'] . ''; + + //Number of invoices to generate; 0 infinite (default 0) + if( isset( $params['occurrences'] ) ) + $xml .= '' . $params['occurrences'] . ''; + + //One of 'weekly', '2 weeks', '4 weeks', 'monthly', '2 months', '3 months', '6 months', 'yearly', '2 years' + if( isset( $params['frequency'] ) ) + $xml .= '' . $params['frequency'] . ''; + + //Send email notification(Default 1) + if( isset( $params['send_email'] ) ) + $xml .= '' . $params['send_email'] . ''; + + //Send copy by snail mail (Default 0) + if( isset( $params['send_snail_mail'] ) ) + $xml .= '' . $params['send_snail_mail'] . ''; + + $xml .= $this->_makeInvoiceXml( $params ); + + //(Optional) + if( isset( $params['autobill'] ) ) + { + $xml .= ''; + $autobill = $params['autobill']; + + //Case insensitive gateway name from gateway.list (Must be auto-bill capable) + if( isset( $autobill['gateway_name'] ) ) + $xml .= '' . $autobill['gateway_name'] . ''; + + if( isset( $autobill['card'] ) ) + { + $xml .= ''; + $card = $autobill['card']; + + //Can include spaces, hyphens and other punctuation marks + if( isset( $card['number'] ) ) + $xml .= '' . $card['number'] . ''; + + if( isset( $card['name'] ) ) + $xml .= '' . $card['name'] . ''; + + $xml .= ''; + if( isset( $card['expiration']['month'] ) ) + $xml .= '' . $card['expiration']['month'] . ''; + + if( isset( $card['expiration']['year'] ) ) + $xml .= '' . $card['expiration']['year'] . ''; + + $xml .= ''; + $xml .= ''; + } + $xml .= ''; + } + + return $xml; + } +} diff --git a/library/OSS/Smarty/functions/function.LoginForm.php b/library/OSS/Smarty/functions/function.LoginForm.php new file mode 100644 index 0000000..a97cbd2 --- /dev/null +++ b/library/OSS/Smarty/functions/function.LoginForm.php @@ -0,0 +1,94 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function to generate a login form based on a ZendForm object. + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params + * @param Smarty $smarty A reference to the Smarty template object + * @return string + */ + function smarty_function_LoginForm( $params, &$smarty ) + { + $url = Zend_Controller_Front::getInstance()->getBaseUrl(); + + $loginForm = new OSS_Form_User_Login; + + $loginForm + ->setAction( $url . '/user/login' ) + ->setDecorators( + array( + array( 'ViewScript', array( 'viewScript' => 'form/login.phtml' ) ) + ) + ) + ->setMethod('post'); + + $loginForm->getElement('username')->setLabel('E-Mail'); + $loginForm->addElement('submit', 'signup', array('label' => 'Sign Up')); + + if( isset( $params['layout'] ) && $params['layout'] = 'sidebar' ) + { + $loginForm->username->setAttrib( 'size', 20 ); + + $loginForm->setDecorators( + array( + array( 'ViewScript', array( 'viewScript' => 'form/login-sidebar.phtml' ) ) + ) + ); + } + else + { + $loginForm->username->setAttrib( 'size', 40 ); + $loginForm->addElement( 'submit', 'forgottenPassword', array('label' => 'Forgotten Password?')); + } + + return $loginForm->render(); + } diff --git a/library/OSS/Smarty/functions/function.OSS_Message.php b/library/OSS/Smarty/functions/function.OSS_Message.php new file mode 100644 index 0000000..e38a5a6 --- /dev/null +++ b/library/OSS/Smarty/functions/function.OSS_Message.php @@ -0,0 +1,154 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + + /** + * Function to display OSS_Message for user + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params + * @param Smarty $smarty A reference to the Smarty template object + * @return string + */ + function smarty_function_OSS_Message( $params, &$smarty ) + { + $ossms = $smarty->getTemplateVars( 'OSS_Messages' ); + + if( $ossms === null ) $ossms = array(); + + if( isset( $_SESSION['Application']['OSS_Messages'] ) && is_array( $_SESSION['Application']['OSS_Messages'] ) + && sizeof( $_SESSION['Application']['OSS_Messages'] ) > 0 ) + { + $ossms = array_merge($ossms, $_SESSION['Application']['OSS_Messages']); + unset($_SESSION['Application']['OSS_Messages']); + } + + if ( $ossms == array() ) return ''; + + $count = 0; + $message = ''; + + foreach( $ossms as $ossm ) + { + if( isset( $params['randomid'] ) && $params['randomid'] ) + $count = mt_rand(); + + if( $ossm instanceof OSS_Message_Block ) + { + $message .= << + × + {$ossm->getMessage()} +END_MESSAGE; + if( count( $ossm->getActions() ) ) + { + $message .= "
\n"; + + foreach( $ossm->getActions() as $a ) + $message .= $a . "\n"; + + $message .= "
\n"; + } + + $message .= << + +END_MESSAGE; + } + else if( $ossm instanceof OSS_Message_Pop_Up ) + { + + $items = $ossm->getMessage(); + + if( !is_array( $items ) ) + $items = array( $items ); + + foreach( $items as $item ) + { + $message .= << + $( document ).ready( function() + { + bootbox.alert( '{$item}' ); + }) + + +END_MESSAGE; + } + } + else + { + + $items = $ossm->getMessage(); + + if( !is_array( $items ) ) + $items = array( $items ); + + foreach( $items as $item ) + { + $message .= << + × + {$item} + + +END_MESSAGE; + } + } // end inner foreach + + $count++; + } // end foreach() + + + return $message; + } diff --git a/library/OSS/Smarty/functions/function.addJSValidator.php b/library/OSS/Smarty/functions/function.addJSValidator.php new file mode 100644 index 0000000..c80137c --- /dev/null +++ b/library/OSS/Smarty/functions/function.addJSValidator.php @@ -0,0 +1,333 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Get form elements + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param OSS_Form $formObj Form to get elements + * @param array &$validationRules Array of rules for validate elements + * @return void + */ + function __vimbadmin_getFormElements( $formObj, &$validationRules ) + { + foreach( $formObj->getElements() as $oneElement ) + { + $fieldId = $oneElement->getID(); + + if( get_class($oneElement) == 'Zend_Form_Element_Checkbox' ) + $validationRules[$fieldId]['field_type'] = get_class( $oneElement ); + + if( $oneElement->isRequired() == true ) + $validationRules[$fieldId]['required'] = true; + + foreach( $oneElement->getValidators() as $oneValidator ) + { + switch( get_class($oneValidator) ) + { + case 'Zend_Validate_NotEmpty': + $validationRules[$fieldId]['notEmpty'] = true; + break; + + case 'Zend_Validate_StringLength': + $validationRules[$fieldId]['minlength'] = $oneValidator->getMin(); + $validationRules[$fieldId]['maxlength'] = $oneValidator->getMax(); + break; + + case 'Zend_Validate_EmailAddress': + $validationRules[$fieldId]['email'] = true; + break; + + case 'Zend_Validate_Digits': + $validationRules[$fieldId]['digits'] = true; + break; + + case 'Zend_Validate_Int': + $validationRules[$fieldId]['integer'] = true; + break; + + case 'Zend_Validate_Float': + $validationRules[$fieldId]['number'] = true; + break; + + case 'Zend_Validate_Ccnum': + $validationRules[$fieldId]['creditcard'] = true; + break; + + case 'Zend_Validate_InArray': + $validationRules[$fieldId]['inArray'] = $oneValidator->getHaystack(); + break; + + case 'Zend_Validate_Between': + if( $oneValidator->getInclusive() ) + $validationRules[$fieldId]['betweenIn'] = array( $oneValidator->getMin() , $oneValidator->getMax() ); + else + $validationRules[$fieldId]['betweenEx'] = array( $oneValidator->getMin() , $oneValidator->getMax() ); + break; + + case 'Zend_Validate_LessThan': + $validationRules[$fieldId]['lessThan'] = $oneValidator->getMax(); + break; + + case 'Zend_Validate_GreaterThan': + $validationRules[$fieldId]['greaterThan'] = $oneValidator->getMin(); + break; + + case 'Zend_Validate_Hostname': + $validationRules[$fieldId]['hostname'] = true; + break; + + case 'ViMbAdmin_Validate_IdenticalField': + $validationRules[$fieldId]['equalTo'] = '#' . $oneValidator->getFieldName(); + break; + + default: + break; + } // switch ( validator class ) + } // foreach validators + } // foreach elements + } + + + /** + * Function to add the JQuery form validator to a form. + * + * the parameters in $params are: + * + * 'form' - the form object, must be a Zend_Form or an inherited class of that + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params An array of the parameters to make up the URL + * @param Smarty $smarty A reference to the Smarty template object + * @return string + */ + function smarty_function_addJSValidator( $params, &$smarty ) + { + if( !isset( $params['form'] ) || !is_object( $params['form'] ) || !is_subclass_of( $params['form'], 'Zend_Form' ) ) + return ''; + + $validationRules = array(); + $formObj = $params['form']; + + __vimbadmin_getFormElements( $formObj, $validationRules ); + + if( sizeof( $formObj->getSubForms() ) != 0 ) + { + foreach( $formObj->getSubForms() as $subForm ) + __vimbadmin_getFormElements( $subForm, $validationRules ); + } + + $ruleStr = " + + + +"; + + return $ruleStr; + } diff --git a/library/OSS/Smarty/functions/function.currency.php b/library/OSS/Smarty/functions/function.currency.php new file mode 100644 index 0000000..40dc99f --- /dev/null +++ b/library/OSS/Smarty/functions/function.currency.php @@ -0,0 +1,70 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function to generate a currency string from a string, int or float + * + * parameters: + * 'value' string, int, float, default 0.00 + * 'currency' string, default '€' + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params + * @param Smarty $smarty A reference to the Smarty template object + * @return string + */ + function smarty_function_currency( $params, &$smarty ) + { + $value = isset( $params['value'] ) ? $params['value'] : 0; + $currency = isset( $params['currency'] ) ? $params['currency'] : '€'; + + return ( $value < 0 ? '-' : '' ) . $currency . sprintf( "%.2f", abs( $params['value'] ) ); + } + diff --git a/library/OSS/Smarty/functions/function.customdate.php b/library/OSS/Smarty/functions/function.customdate.php new file mode 100644 index 0000000..e2cc3b7 --- /dev/null +++ b/library/OSS/Smarty/functions/function.customdate.php @@ -0,0 +1,72 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function to generate a custom date string using date( 'format', strtotime( 'offset', now ) ) + * + * parameters: + * 'format' string, default 'Y-m-d' + * 'offset' string, default 'now' + * 'now' string, default time() + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params + * @param Smarty $smarty A reference to the Smarty template object + * @return string + */ + function smarty_function_customdate( $params, &$smarty ) + { + $format = isset( $params['format'] ) ? $params['format'] : 'Y-m-d'; + $offset = isset( $params['offset'] ) ? $params['offset'] : 'now'; + $now = isset( $params['now'] ) ? $params['now'] : time(); + + return date( $format, strtotime( $offset, $now ) ); + } + diff --git a/library/OSS/Smarty/functions/function.dateDiffDays.php b/library/OSS/Smarty/functions/function.dateDiffDays.php new file mode 100644 index 0000000..4cc3f44 --- /dev/null +++ b/library/OSS/Smarty/functions/function.dateDiffDays.php @@ -0,0 +1,67 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function to to calculate diffrence between two days + * + * parameters: + * 'date1' string, default 'Y-m-d' + * 'date2' string, default 'Y-m-d' + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params + * @param Smarty $smarty A reference to the Smarty template object + * @return string + */ + function smarty_function_dateDiffDays( $params, &$smarty ) + { + return OSS_Utils::dateDiffDays( $params['date1'], $params['date2'] ); + } + diff --git a/library/OSS/Smarty/functions/function.dynamicContent.php b/library/OSS/Smarty/functions/function.dynamicContent.php new file mode 100644 index 0000000..90c034b --- /dev/null +++ b/library/OSS/Smarty/functions/function.dynamicContent.php @@ -0,0 +1,74 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + require_once("../library/Smarty/plugins/function.eval.php"); + + + /** + * Function to load and display dynamic content from the database. + * + * The URL is made up of parameters as supplied in the $params associative array. + * 'controller' and 'action' are special parameters which indicate the controller + * and action to call. Any other parameters are added as additional name / value + * pairs. + * + * NOTICE: Works with Doctrine1 + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params An array of the parameters to make up the URL + * @param Smarty $smarty A reference to the Smarty template object + * @return string + */ + function smarty_function_dynamicContent( $params, &$smarty ) + { + $dynamicContent = Doctrine::getTable( 'DynamicContent' )->findByName( $params['name'] ); + + return ( $dynamicContent[0]['content'] != '' ? smarty_function_eval( array( 'var' => $dynamicContent[0]['content'] ), $smarty ) : '' ); + } diff --git a/library/OSS/Smarty/functions/function.genUrl.php b/library/OSS/Smarty/functions/function.genUrl.php new file mode 100644 index 0000000..f5abdae --- /dev/null +++ b/library/OSS/Smarty/functions/function.genUrl.php @@ -0,0 +1,83 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function to generate a Zend Controller URL from Smarty templates. + * + * The URL is made up of parameters as supplied in the $params associative array. + * 'module', 'controller' and 'action' are special parameters which indicate the module, + * controller and action to call. Any other parameters are added as additional name / value + * pairs. + * + * Calls OSS_Utils::genUrl() + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params An array of the parameters to make up the URL + * @param Smarty $smarty A reference to the Smarty object + * @return string + */ + function smarty_function_genUrl( $params, &$smarty ) + { + if( !isset( $params['controller'] ) ) + $params['controller'] = false; + + if( !isset( $params['action'] ) ) + $params['action'] = false; + + if( !isset( $params['module'] ) || $params['module'] == 'default' ) + $params['module'] = false; + + $p = $params; + unset( $p['controller'] ); + unset( $p['action'] ); + unset( $p['module'] ); + + return OSS_Utils::genUrl( $params['controller'], $params['action'], $params['module'], $p ); + } diff --git a/library/OSS/Smarty/functions/function.includeIfExists.php b/library/OSS/Smarty/functions/function.includeIfExists.php new file mode 100644 index 0000000..e5e20e3 --- /dev/null +++ b/library/OSS/Smarty/functions/function.includeIfExists.php @@ -0,0 +1,120 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Smarty plugin + * Purpose: Similar with "include" function, but only include the + * template file when it exists. Otherwise, a default file passed + * by parameter "else" will be included. + * Example: + * 1 {includeIfExists file="foo.tpl" assign="foo"} + * 2 {includeIfExists file="foo.tpl" else="default.tpl"} + * ------------------------------------------------------------- + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param int $timestamp + * @return string + */ + +function smarty_function_includeIfExists( $params, $smarty ) +{ + if( !isset( $params['file'] ) ) + throw new SmartyCompilerException( "Missing 'file' attribute in tmplinclude tag" ); + + $original_values = array(); + + foreach( $params as $arg => $value ) + { + if( is_bool( $value ) ) + $params[ $arg ] = $value ? 'true' : 'false'; + + if( !in_array( $arg, array( 'file', 'assign', 'else' ) ) ) + { + $original_values[ $arg ] = $value; + $smarty->assign( $arg, $value ); + } + } + + $params['file'] = str_replace( array( '\'', '"' ), '', $params['file'] ); + $params['else'] = str_replace( array( '\'', '"' ), '', $params['else'] ); + + if( $smarty->getTemplateVars( '___SKIN' ) ) + $skin = $smarty->getTemplateVars( '___SKIN' ); + else + $skin = false; + + if( $skin && $smarty->templateExists( '_skins/' . $skin . '/' . $params['file'] ) ) + $params['file'] = '_skins/' . $skin . '/' . $params['file']; + elseif( $skin && $smarty->templateExists( '_skins/' . $skin . '/' . $params['else'] ) ) + $params['file'] = '_skins/' . $skin . '/' . $params['else']; + elseif( $smarty->templateExists( $params['file'] ) ) + $params['file'] = $params['file']; + elseif( $smarty->templateExists( $params['else'] ) ) + $params['file'] = $params['else']; + else + throw new SmartyCompilerException( "Template file nor alternative does not exist for all skins - [{$params['file']}]" ); + + $output = ''; + + if( isset( $params['assign'] ) ) + $smarty->assign( $params['assign'], $smarty->fetch( $params['file'] ) ); + else + $output = $smarty->fetch( $params['file'] ); + + foreach( $original_values as $arg => $value ) + { + $smarty->assign( $arg, $value ); + } + + return $output; +} + + diff --git a/library/OSS/Smarty/functions/function.modelOperation.php b/library/OSS/Smarty/functions/function.modelOperation.php new file mode 100644 index 0000000..d291f33 --- /dev/null +++ b/library/OSS/Smarty/functions/function.modelOperation.php @@ -0,0 +1,77 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function to execute a model method with (optional) parameters + * + * NOTICE: Works only with Doctrine1 + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params (model, id, method, params) + * @param Smarty $smarty A reference to the Smarty template object + * @return mixed + */ + function smarty_function_modelOperation( $params, &$smarty ) + { + //print "Doctrine::getTable('{$params['model']}')->find({$params['id']})->{$params['method']}({$params['params']});"; + + $params['id'] = (int) $params['id']; + + if( $params['id'] <= 0 ) + return; + + if( !in_array( $_SESSION['Zend_Auth']['storage']['user']['type'], array( 'SUPERADMIN' ) ) ) + return; + + if( in_array( $params['method'], array( 'save', 'delete', '__construct', 'setUp', 'construct', 'deleteNode' ) ) ) + return; + + return Doctrine::getTable( $params['model'] )->find( $params['id'] )->$params['method']( $params['params'] ); + } diff --git a/library/OSS/Smarty/functions/function.phpinfo.php b/library/OSS/Smarty/functions/function.phpinfo.php new file mode 100644 index 0000000..88a3121 --- /dev/null +++ b/library/OSS/Smarty/functions/function.phpinfo.php @@ -0,0 +1,65 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function to execute the PHP native phpinfo() function + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param array $params An array of the parameters to make up the URL + * @param Smarty $smarty A reference to the Smarty object + * @return string + */ + function smarty_function_phpinfo( $params, &$smarty ) + { + phpinfo(); + + return ''; + } + diff --git a/library/OSS/Smarty/functions/function.secondsToHMS.php b/library/OSS/Smarty/functions/function.secondsToHMS.php new file mode 100644 index 0000000..49dda5a --- /dev/null +++ b/library/OSS/Smarty/functions/function.secondsToHMS.php @@ -0,0 +1,67 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + /** + * Function converts given int to time string assuming that hours can be more than 24. + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param int $timestamp + * @return string + */ + function smarty_function_secondsToHMS( $params, &$smarty ) + { + $time = OSS_DateTime::secondsToHMS( $params[ "seconds"] ); + + $str = $time[ "hours" ]; + $str .= ":" . ( $time["minutes"] > 9 ? $time["minutes"] : "0" .$time["minutes"] ); + $str .= ":" . ( $time["seconds"] > 9 ? $time["seconds"] : "0" .$time["seconds"] ); + + return $str; + } diff --git a/library/OSS/Smarty/functions/function.tmplinclude.php b/library/OSS/Smarty/functions/function.tmplinclude.php new file mode 100644 index 0000000..9b24d80 --- /dev/null +++ b/library/OSS/Smarty/functions/function.tmplinclude.php @@ -0,0 +1,121 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Function icludes template form skin, if file is not existing in skin folder + * it displays default one. + * + * @category OSS + * @package OSS_Smarty + * @subpackage Functions + * + * @param int $timestamp + * @return string + */ +function smarty_function_tmplinclude( $params, $smarty ) +{ + if( !isset( $params['file'] ) ) + throw new SmartyCompilerException( "Missing 'file' attribute in tmplinclude tag" ); + + $original_values = array(); + + foreach( $params as $arg => $value ) + { + if( is_bool( $value ) ) + $params[ $arg ] = $value ? 'true' : 'false'; + + if( !in_array( $arg, array( 'file', 'assign' ) ) ) + { + $original_values[ $arg ] = $value; + $smarty->assign( $arg, $value ); + } + } + + if( substr( $params['file'], 0, 24 ) == '$_smarty_tpl->tpl_vars[\'' ) + { + $params['file'] = substr( $params['file'], 24 ); + $params['file'] = substr( $params['file'], 0, strpos( $params['file'], '\'' ) ); + $params['file'] = $smarty->getTemplateVars( $params['file'] ); + } + elseif( substr( $params['file'], 0, 24 ) == '($_smarty_tpl->tpl_vars[' ) + { + $params['file'] = substr( $params['file'], 24 ); + $params['file'] = substr( $params['file'], 0, strpos( $params['file'], ']' ) ); + $params['file'] = $smarty->getTemplateVars( $params['file'] ); + } + elseif( substr( $params['file'], 0, 23 ) == '$_smarty_tpl->tpl_vars[' ) + { + $params['file'] = substr( $params['file'], 23 ); + $params['file'] = substr( $params['file'], 0, strpos( $params['file'], ']' ) ); + $params['file'] = $smarty->getTemplateVars( $params['file'] ); + } + else + $params['file'] = str_replace( array( '\'', '"' ), '', $params['file'] ); + + if( $smarty->getTemplateVars( '___SKIN' ) ) + $skin = $smarty->getTemplateVars( '___SKIN' ); + else + $skin = false; + + if( $skin && $smarty->templateExists( '_skins/' . $skin . '/' . $params['file'] ) ) + $params['file'] = '_skins/' . $skin . '/' . $params['file']; + elseif( !$smarty->templateExists( $params['file'] ) ) + throw new SmartyCompilerException( "Template file does not exist - [{$params['file']}]" ); + + + if( isset( $params['assign'] ) ) + $smarty->assign( $params['assign'], $smarty->fetch( $params['file'] ) ); + else + $output = $smarty->fetch( $params['file'] ); + + foreach( $original_values as $arg => $value ) + { + $smarty->assign( $arg, $value ); + } + + return $output; +} diff --git a/library/OSS/Smarty/functions/modifier.alnum.php b/library/OSS/Smarty/functions/modifier.alnum.php new file mode 100644 index 0000000..baddf8f --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.alnum.php @@ -0,0 +1,62 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Alnum modifier + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string Strint to modify + * @return string + */ +function smarty_modifier_alnum( $string ) +{ + return preg_replace( "/[^a-zA-Z0-9]/", '', $string ); +} + diff --git a/library/OSS/Smarty/functions/modifier.debug_print_var.php b/library/OSS/Smarty/functions/modifier.debug_print_var.php new file mode 100644 index 0000000..1dcdb9d --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.debug_print_var.php @@ -0,0 +1,135 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + +/** + * Smarty debug_print_var modifier plugin + * + * Type: modifier
+ * Name: debug_print_var
+ * Purpose: formats variable contents for display in the console + * @link http://smarty.php.net/manual/en/language.modifier.debug.print.var.php + * debug_print_var (Smarty online manual) + * @author Monte Ohrt + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param array|object + * @param integer + * @param integer + * @return string + */ +function smarty_modifier_debug_print_var( $var, $depth = 0, $length = 40 ) +{ + $_replace = array( + "\n" => '\n', + "\r" => '\r', + "\t" => '\t' + ); + + switch (gettype($var)) { + case 'array' : + $results = 'Array (' . count($var) . ')'; + foreach ($var as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) + . '' . strtr($curr_key, $_replace) . ' => ' + . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); + $depth--; + } + break; + case 'object' : + $object_vars = get_object_vars($var); + $results = '' . get_class($var) . ' Object (' . count($object_vars) . ')'; + foreach ($object_vars as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) + . ' ->' . strtr($curr_key, $_replace) . ' = ' + . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); + $depth--; + } + break; + case 'boolean' : + case 'NULL' : + case 'resource' : + if (true === $var) { + $results = 'true'; + } elseif (false === $var) { + $results = 'false'; + } elseif (null === $var) { + $results = 'null'; + } else { + $results = htmlspecialchars((string) $var); + } + $results = '' . $results . ''; + break; + case 'integer' : + case 'float' : + $results = htmlspecialchars((string) $var); + break; + case 'string' : + $results = strtr($var, $_replace); + if (strlen($var) > $length ) { + $results = substr($var, 0, $length - 3) . '...'; + } + $results = htmlspecialchars('"' . $results . '"'); + break; + case 'unknown type' : + default : + $results = strtr((string) $var, $_replace); + if (strlen($results) > $length ) { + $results = substr($results, 0, $length - 3) . '...'; + } + $results = htmlspecialchars($results); + } + + return $results; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/OSS/Smarty/functions/modifier.escape.php b/library/OSS/Smarty/functions/modifier.escape.php new file mode 100644 index 0000000..f884acc --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.escape.php @@ -0,0 +1,139 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + +/** + * Smarty escape modifier plugin + * + * Type: modifier
+ * Name: escape
+ * Purpose: Escape the string according to escapement type + * @link http://smarty.php.net/manual/en/language.modifier.escape.php + * escape (Smarty online manual) + * @author Monte Ohrt + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string + * @param html|htmlall|url|quotes|hex|hexentity|javascript $esc_type + * @param string $char_set + * @return string + */ +function smarty_modifier_escape($string, $esc_type = 'html', $char_set = 'ISO-8859-1') +{ + switch ($esc_type) { + case 'html': + return htmlspecialchars($string, ENT_QUOTES, $char_set); + + case 'htmlall': + return htmlentities($string, ENT_QUOTES, $char_set); + + case 'url': + return rawurlencode($string); + + case 'urlpathinfo': + return str_replace('%2F','/',rawurlencode($string)); + + case 'quotes': + // escape unescaped single quotes + return preg_replace("%(?'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n',''<\/')); + + case 'mail': + // safe way to display e-mail address on a web page + return str_replace(array('@', '.'),array(' [AT] ', ' [DOT] '), $string); + + case 'nonstd': + // escape non-standard chars, such as ms document quotes + $_res = ''; + for($_i = 0, $_len = strlen($string); $_i < $_len; $_i++) { + $_ord = ord(substr($string, $_i, 1)); + // non-standard char, escape it + if($_ord >= 126){ + $_res .= '&#' . $_ord . ';'; + } + else { + $_res .= substr($string, $_i, 1); + } + } + return $_res; + + default: + return $string; + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/OSS/Smarty/functions/modifier.intlmobile.php b/library/OSS/Smarty/functions/modifier.intlmobile.php new file mode 100644 index 0000000..bc6b299 --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.intlmobile.php @@ -0,0 +1,63 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + + +/** + * International mobile number modifier + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string Strint to modify + * @return string + */ +function smarty_modifier_intlmobile( $string ) +{ + return preg_replace( "/(353)(\d{2})(\d{3})(\d{4})/", "+$1 $2 $3 $4", preg_replace( "/\D/", '', $string ) ); +} + diff --git a/library/OSS/Smarty/functions/modifier.mobile.php b/library/OSS/Smarty/functions/modifier.mobile.php new file mode 100644 index 0000000..2fad06a --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.mobile.php @@ -0,0 +1,62 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Mobile number modifier + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string Strint to modify + * @return string + */ +function smarty_modifier_mobile($string) +{ + return preg_replace("/(353)(\d{2})(\d{3})(\d{4})/", "0$2 $3 $4", preg_replace("/\D/", '', $string)); +} + diff --git a/library/OSS/Smarty/functions/modifier.ossDate.php b/library/OSS/Smarty/functions/modifier.ossDate.php new file mode 100644 index 0000000..7fe0356 --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.ossDate.php @@ -0,0 +1,68 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * OSS_Date modifier + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string String to modify + * @param string|int $format + * @return string + */ +function smarty_modifier_ossDate( $string, $format = "d/m/Y" ) +{ + $date = new DateTime( $string ); + if( !$format ) + $format = "d/m/Y"; + + return is_numeric( $format ) ? $date->format( OSS_Date::getPhpFormat( $format ) ) : $date->format( $format ); +} + +?> diff --git a/library/OSS/Smarty/functions/modifier.preg_replace.php b/library/OSS/Smarty/functions/modifier.preg_replace.php new file mode 100644 index 0000000..96a7bf2 --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.preg_replace.php @@ -0,0 +1,64 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Replace by regular expresion modifier + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string Strint to modify + * @param string $search Pattern to search + * @param string $replace Replacement string + * @return string + */ +function smarty_modifier_preg_replace( $string, $search, $replace ) +{ + return preg_replace( $search, $replace, $string ); +} + diff --git a/library/OSS/Smarty/functions/modifier.shorten.php b/library/OSS/Smarty/functions/modifier.shorten.php new file mode 100644 index 0000000..f09957e --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.shorten.php @@ -0,0 +1,65 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Shorten modifier + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string Strint to modify + * @param int $length Wanted max string length + * @return string + */ +function smarty_modifier_shorten( $string, $length ) +{ + if( mb_strlen( $string ) > $length ) + return rtrim( mb_substr( $string, 0, $length ) ) . '...'; + else + return $string; +} diff --git a/library/OSS/Smarty/functions/modifier.sizeof.php b/library/OSS/Smarty/functions/modifier.sizeof.php new file mode 100644 index 0000000..19d1117 --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.sizeof.php @@ -0,0 +1,63 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Size of modifier + * Counts all elements in an array, or something in an object. + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param mixed $input + * @return int + */ +function smarty_modifier_sizeof( $input ) +{ + return sizeof( $input ); +} + diff --git a/library/OSS/Smarty/functions/modifier.string_format.php b/library/OSS/Smarty/functions/modifier.string_format.php new file mode 100644 index 0000000..8e4b4c5 --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.string_format.php @@ -0,0 +1,73 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * Smarty string_format modifier plugin + * + * Type: modifier
+ * Name: string_format
+ * Purpose: format strings via sprintf + * @link http://smarty.php.net/manual/en/language.modifier.string.format.php + * string_format (Smarty online manual) + * @author Monte Ohrt + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string + * @param string + * @return string + */ +function smarty_modifier_string_format( $string, $format ) +{ + return sprintf( $format, $string ); +} + +/* vim: set expandtab: */ + +?> diff --git a/library/OSS/Smarty/functions/modifier.toInt.php b/library/OSS/Smarty/functions/modifier.toInt.php new file mode 100644 index 0000000..797c7a4 --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.toInt.php @@ -0,0 +1,63 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * To int modifier + * Multicasting given string to int + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string + * @return int + */ +function smarty_modifier_toInt( $string ) +{ + return (int) $string; +} + diff --git a/library/OSS/Smarty/functions/modifier.toValidId.php b/library/OSS/Smarty/functions/modifier.toValidId.php new file mode 100644 index 0000000..484a1d3 --- /dev/null +++ b/library/OSS/Smarty/functions/modifier.toValidId.php @@ -0,0 +1,61 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +/** + * To valid id + * + * @category OSS + * @package OSS_Smarty + * @subpackage Modifier + * + * @param string $string + * @return string + */ +function smarty_modifier_toValidId( $string ) +{ + return preg_replace( "[^a-zA-Z-0-9\_]", '_', $string ); +} diff --git a/library/OSS/Smarty/functions/prefilter.whitespace_control.php b/library/OSS/Smarty/functions/prefilter.whitespace_control.php new file mode 100644 index 0000000..d1a7cb8 --- /dev/null +++ b/library/OSS/Smarty/functions/prefilter.whitespace_control.php @@ -0,0 +1,65 @@ + "text \n\n{tag}" + * "text \n\n\t text\t {-tag}" -> "text \n\n\t text{tag}" + * {--tag} remove white space infront of tag up to the previous non-whitespace character + * "text \n\n\t {--tag}" -> "text{tag}" + * "text \n\n\t text\t {--tag}" -> "text \n\n\t text{tag}" + * {+-tag} + * {-+tag} replace white space infront of tag up to the previous non-whitespace character by a single line-break + * "text \n\n\t {-+tag}" -> "text\n{tag}" + * "text \n\n\t text\t {-+tag}" -> "text \n\n\t text\n{tag}" + * + * {tag-} remove white space after tag up to the next non-whitespace character or end of the line + * "{tag-} \n\n\t text" -> "{tag}\n\n\t text" + * "{tag-} text \n\n\t text" -> "{tag}text \n\n\t text" + * {tag--} remove white space after tag up to the next non-whitespace character + * "{tag--} \n\n\t text" -> "{tag}text" + * "{tag--} text \n\n\t text" -> "{tag}text \n\n\t text" + * {tag+-} + * {tag-+} replace white space after tag up to the next non-whitespace character by a single line-break + * "{tag-+} \n\n\t text" -> "{tag}\n\ntext" + * "{tag-+} text \n\n\t text" -> "{tag}\n\ntext \n\n\t text" + * + * {tag+} replace white space after tag up to the end of the line with an additional line-break + * "{tag+} \n\t text" -> "{tag}\n\n\t text" + * "{tag+} text \n\n\t text" -> "{tag}\n\ntext \n\n\t text" + * + * Any combination of the above, say {--tag+} is possible. Any + modifiers are executed before - modifiers, so + * "{tag+-}{--tag}" will lead to "{tag}{tag}" + * + * NOTE: {tag+} and {tag-+} cause two trailing \n. This is done because PHP itself throws away the first \n. + * So \n\n in the template will lead to \n in the output + * + * @param string $string raw template source + * @param Smarty_Internal_Template $template Template instance + * @return string raw template source after whitespace control was applied + * @author Rodney Rehm + */ +function smarty_prefilter_whitespace_control($string, Smarty_Internal_Template $template) { + $ldelim = $template->smarty->left_delimiter; + $rdelim = $template->smarty->right_delimiter; + $_ldelim = preg_quote($ldelim); + $_rdelim = preg_quote($rdelim); + + // remove preceeding whitepsace preserving a single line-break + $string = preg_replace('#\s*'. $_ldelim .'(?:-\+|\+-)#', "\n" . $ldelim, $string); + // remove trailing whitespace preserving s single line-break + $string = preg_replace('#(?:\+-|-\+)'. $_rdelim .'\s*#', $rdelim . "\n\n", $string); + + // remove preceeding whitepsace + $string = preg_replace('#\s*'. $_ldelim .'--|[^\S\r\n]*'. $_ldelim .'-#', $ldelim, $string); + // remove trailing whitespace + $string = preg_replace('#--'. $_rdelim .'\s*|-'. $_rdelim .'[^\S\r\n]*#', $rdelim, $string); + + // force trailing line-break + $string = preg_replace('#\+'. $_rdelim .'(?:\s*[\r\n]|[^\S\r\n]*)#', $rdelim . "\n\n", $string); + + return $string; +} diff --git a/library/OSS/StatsD.php b/library/OSS/StatsD.php new file mode 100644 index 0000000..be5d834 --- /dev/null +++ b/library/OSS/StatsD.php @@ -0,0 +1,184 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_StatsD + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + * @link https://github.com/etsy/statsd/blob/master/examples/php-example.php + */ +class OSS_StatsD +{ + /** + * @var bool Determines whther we are gathering statistics or not + */ + private $_enabled = true; + + /** + * @var string The StatsD host + */ + private $_host = null; + + /** + * @var int The StatsD port + */ + private $_port = null; + + + /** + * Construct the object with the host and port to use. + * + * @param string $host The StatsD host + * @param int $port The StatsD port + * @param bool $enabled Whether we should sent updates or not + */ + public function __construct( $host, $port, $enabled = true ) + { + $this->_host = $host; + $this->_port = $port; + $this->_enabled = $enabled; + } + + /** + * Log timing information + * + * @param string $stat The metric to in log timing info for. + * @param float $time The ellapsed time (ms) to log + * @param float|1 $sampleRate the rate (0-1) for sampling. + **/ + public function timing( $stat, $time, $sampleRate=1 ) + { + $this->send(array($stat => "$time|ms"), $sampleRate); + } + + /** + * Increments one or more stats counters + * + * @param string|array $stats The metric(s) to increment. + * @param float|1 $sampleRate the rate (0-1) for sampling. + * @return boolean + **/ + public function increment( $stats, $sampleRate=1 ) + { + $this->updateStats( $stats, 1, $sampleRate ); + } + + /** + * Decrements one or more stats counters. + * + * @param string|array $stats The metric(s) to decrement. + * @param float|1 $sampleRate the rate (0-1) for sampling. + * @return boolean + **/ + public function decrement( $stats, $sampleRate=1 ) + { + $this->updateStats( $stats, -1, $sampleRate ); + } + + /** + * Updates one or more stats counters by arbitrary amounts. + * + * @param string|array $stats The metric(s) to update. Should be either a string or array of metrics. + * @param int|1 $delta The amount to increment/decrement each metric by. + * @param float|1 $sampleRate the rate (0-1) for sampling. + * @return boolean + **/ + public function updateStats( $stats, $delta=1, $sampleRate=1 ) + { + if ( !is_array( $stats ) ) + $stats = array($stats); + + $data = array(); + foreach( $stats as $stat ) + $data[$stat] = "$delta|c"; + + $this->send( $data, $sampleRate ); + } + + /* + * Squirt the metrics over UDP + **/ + public function send( $data, $sampleRate = 1 ) + { + if( !$this->isEnabled() ) + return; + + // sampling + $sampledData = array(); + + if( $sampleRate < 1 ) + { + foreach( $data as $stat => $value ) + if( ( mt_rand() / mt_getrandmax()) <= $sampleRate ) + $sampledData[$stat] = "$value|@$sampleRate"; + } + else + $sampledData = $data; + + if( empty( $sampledData ) ) + return; + + // Wrap this in a try/catch - failures in any of this should be silently ignored + try + { + if( !( $fp = fsockopen( "udp://" . $this->getHost(), $this->getPort(), $errno, $errstr ) ) ) + return; + + foreach( $sampledData as $stat => $value ) + fwrite( $fp, "$stat:$value" ); + + fclose($fp); + + }catch( Exception $e ) {} + } + + /** + * Indicates whether StatsD is enabled by configuration or not + * + * @return bool + */ + public function isEnabled() + { + return (bool)$this->_enabled; + } + +} + diff --git a/library/OSS/String.php b/library/OSS/String.php new file mode 100644 index 0000000..ef74e54 --- /dev/null +++ b/library/OSS/String.php @@ -0,0 +1,363 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Class for string actions + * + * @category OSS + * @package OSS_String + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_String +{ + + /** + * The Unicode version of ucfirst(). + * + * @param string $string the input string + * @param string $encoding default null the character encoding, if omitted the the PHP internal encoding is used + * @return string + */ + public static function mb_ucfirst( $string, $encoding = null ) + { + if( function_exists( 'mb_strtoupper' ) && !empty( $string ) ) + { + if ($encoding === null) + $encoding = mb_internal_encoding(); + + return mb_strtoupper( mb_substr( $string, 0, 1, $encoding ) ) . mb_substr( $string, 1, mb_strlen( $string, $encoding ) ); + } + else + { + return ucfirst( $string ); + } + } + + + /** + * The Unicode version of ucwords(). + * + * @param string $string the input string + * @param string $encoding default null the character encoding, if omitted the the PHP internal encoding is used + * @return string + */ + public static function mb_ucwords( $string, $encoding = null ) + { + if( $encoding === null ) + $encoding = mb_internal_encoding(); + + return mb_convert_case( $string, MB_CASE_TITLE, $encoding ); + } + + + /** + * The Unicode version of str_replace(). + * + * @param string $needle The string portion to replace in the haystack + * @param string $replacement The replacement for the string portion + * @param string $haystack The haystack + * @return string + */ + public static function mb_str_replace( $needle, $replacement, $haystack ) + { + $needle_len = mb_strlen( $needle ); + $replacement_len = mb_strlen( $replacement ); + $pos = mb_strpos( $haystack, $needle ); + + while( $pos !== false ) + { + $haystack = mb_substr( $haystack, 0, $pos ) . $replacement . mb_substr( $haystack, $pos + $needle_len ); + $pos = mb_strpos( $haystack, $needle, $pos + $replacement_len ); + } + + return $haystack; + } + + + /** + * Generates a random string. + * + * @param int $length The length of the random string we want to generate. Default: 16 + * @param bool $lowerCase If true then lowercase characters will be used. Default: true + * @param bool $upperCase If true then uppercase characters will be used. Default: true + * @param bool $numbers If true then numbers will be used. Default: true + * @param string $additional These characters also will be used. Default: '' + * @param string $exclude These characters will be excluded. Default: '1iIl0O' + * @return string + */ + public static function random( $length=16, $lowerCase = true, $upperCase = true, $numbers = true, $additional = '', $exclude = '1iIl0O' ) + { + $str = ''; + + if( $length == 0 ) + return ''; + + if( $lowerCase == true ) + $str .= 'abcdefghijklmnopqrstuvwxyz'; + + if( $upperCase == true ) + $str .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + if( $numbers == true ) + $str .= '0123456789'; + + $str .= $additional; + + if( $exclude != '' ) + { + foreach( str_split( $exclude ) as $char ) + { + $str = OSS_String::mb_str_replace( $char, '', $str ); + } + } + + $repeat = ceil( ( 1 + ( $length / mb_strlen( $str ) ) ) ); + $retVal = substr( str_shuffle( str_repeat( $str, $repeat ) ), 1, $length ); + + return $retVal; + } + + + /** + * Returns with a random string using the characters found in $charSet only. + * + * @param string $charSet + * @param int $length + * @return string + */ + public static function randomFromSet( $charSet, $length = 16 ) + { + $repeat = ceil( ( 1 + ( $length / mb_strlen( $charSet ) ) ) ); + return substr( str_shuffle( str_repeat( $charSet, $repeat ) ), 1, $length ); + } + + + /** + * Creates a random password string of a given length. Not the fastest way of generating random passwords, but ensures that it contains + * both lowercase and uppercase letters and digits, so complies with our password strength "policy". + * + * Some letters are excluded from the character set: 1, 0, O, I, l + * + * @param int $length The length of the password to be generated. + * @return string The password string. + */ + public static function randomPassword( $length = 8 ) + { + $chars = "23456789abcdefghijkmnopqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; + + while( true ) + { + $password = substr( str_shuffle( $chars ), 0, $length ); + + // "/[a-zA-Z0-9]/" is NOT the same! + if( preg_match( "/[a-z]/", $password ) && preg_match( "/[A-Z]/", $password ) && preg_match( "/[0-9]/", $password ) ) + return $password; + } + } + + + /** + * Takes a string, converts it to lowercase and creates a valid file name from it by replacing any + * character by an underscore which is not [0-9a-z] (so only the standard neolatin (english) alphabet is + * supported), then replaces any consecutive underscores with only one underscore. Leading and trailing + * underscores are also removed. + * + * @param string $string + * @return string + */ + public static function toValidFieldName( $string ) + { + $string = mb_strtolower( trim( $string ) ); + $string = preg_replace( "/[^0-9a-z]+/u", '_', $string ); + $string = preg_replace ("/[_]+/u", '_', $string ); + + if( $string[0] == '_' ) + $string = mb_substr( $string, 1 ); + + if( mb_substr( $string, -1 ) == '_') + $string = mb_substr( $string, 0, -1 ); + + return 'cf_' . $string; + } + + + /** + * Removes any diacritic, accent and combining characters from the string. + * + * These settings should be set for working results: + * mb_language('uni'); + * mb_internal_encoding('UTF-8'); + * setlocale(LC_ALL, "en_IE.utf8"); //or any other locale, as long as it's utf8 + * + * @param string $input the original input string + * @param bool $keepSpaces By default is false and it will remove spaces from string. Then it is set true it will keep spaces in string. + * @return string + */ + public static function normalise( $input, $keepSpaces = false ) + { + iconv_set_encoding( 'internal_encoding', 'utf-8' ); + iconv_set_encoding( 'input_encoding', 'utf-8' ); + iconv_set_encoding( 'output_encoding', 'utf-8' ); + + /** + * Special cases + * AE + * ae + * U+00F0 ð c3 b0 LATIN SMALL LETTER ETH + * U+00D8 Ø c3 98 LATIN CAPITAL LETTER O WITH STROKE + * U+00F8 ø c3 b8 LATIN SMALL LETTER O WITH STROKE + * 00DF ß Latin Small Letter Sharp S (German) + * 00DE Þ Latin Capital Letter Thorn (Icelandic) + * 00FE þ latin small letter thorn + * Ł, ł, đ, Đ, € + * @see http://www.utf8-chartable.de/ + */ + + $from = array( "\xC3\x86", "\xC3\xA6", "\xC3\xB0", "\xC3\x98", "\xC3\xB8", "\xC3\x9F", "\xC3\x9E", "\xC3\xBE", "\xC5\x81", "\xC5\x82", "\xC4\x91", "\xC4\x90", "\xE2\x82\xAC" ); + $to = array( 'AE', 'ae', 'd', 'O', 'o', 'ss', 'Th', 'th', 'L', 'l', "d", "D", "EUR" ); + + $retVal = iconv( 'UTF-8', 'ASCII//TRANSLIT', str_replace( $from, $to, $input ) ); // TRANSLIT does the whole job + if( !$keepSpaces ) + $retVal = preg_replace( "/[^a-z]/", '', mb_strtolower( $retVal ) ); + else + $retVal = preg_replace( "/[^a-z\s]/", '', mb_strtolower( $retVal ) ); + + return $retVal; + } + + /** + * Generates a random salt. + * + * @param int $len Length of return salt. + * @return string + */ + public static function salt( $len ) + { + return self::random( $len, true, true, true, '!$%^&*()_+-=[]{};#:@~\\|<>?,./', '' ); + } + + /** + * Generates a random MAC address. + * + * @param bool $upperCase default false + * @return string + */ + public static function randomMacAddress( $upperCase = false ) + { + $retArr = array(); + + for( $x = 1; $x <= 6; $x++ ) + $retArr[] = OSS_String::random( 2, false, false, false, '0123456789abcdef', '' ); + + $retVal = implode( ':', $retArr ); + + return ( $upperCase ? strtoupper( $retVal ) : $retVal ); + } + + + /** + * If the parameter is an array, then runs stripslashes() on every item of it, recursively. + * Otherwise treats the input as a string, and runs stripslashes() on it. If the input is an + * object, then the return value will be an array of the public object properties. + * The return value is either a string or an array. + * + * @param mixed $input + * @return array|string + */ + public static function stripSlashes( $input ) + { + if( is_string( $input ) ) + return stripslashes( $input ); + + if( !is_array( $input ) && !is_object( $input ) ) + { + $input = (string) $input; + return stripslashes( $input ); + } + + if( is_object( $input ) ) + $input = (array) $input; + + foreach( $input as $key => $item ) + { + if ( is_scalar( $item ) ) + $input[ $key ] = stripslashes( $item ); + else + $input[ $key ] = self::stripSlashes( $item ); + } + + return $input; + } + + + /** + * If the parameter is an array, then runs html_entity_decode() on every item of it, recursively. + * Otherwise treats the input as a string, and runs html_entity_decode() on it. If the input is an + * object, then the return value will be an array of the public object properties. + * The return value is either a string or an array. + * + * @param mixed $input + * @return array|string + */ + public static function htmlEntityDecode( $input ) + { + if( is_string( $input ) ) + return html_entity_decode( $input ); + + if( !is_array( $input ) && !is_object( $input ) ) + { + $input = (string) $input; + return html_entity_decode( $input ); + } + + if( is_object( $input ) ) + $input = (array) $input; + + foreach( $input as $key => $item ) + { + if ( is_scalar( $vItem ) ) + $input[ $key ] = html_entity_decode( $item ); + else + $input[ $key ] = self::htmlEntityDecode( $item ); + } + + return $input; + } + +} diff --git a/library/OSS/Trait/Invoice.php b/library/OSS/Trait/Invoice.php new file mode 100644 index 0000000..43b9999 --- /dev/null +++ b/library/OSS/Trait/Invoice.php @@ -0,0 +1,314 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Invoice trait. + * + * NOTICE: Supports only Doctrine2 database engine. + * + * @category OSS + * @package OSS_trait + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +trait OSS_Trait_Invoice +{ + /** + * The Doctrine2 entity manager + * @var EntityManager The Doctrine2 entity manager + */ + private $_d2em = null; + + /** + * Getting Doctrine2 entity manager + * + * @return EntityManager + */ + protected function getD2EM() + { + if( !$this->_d2em ) + $this->_d2em = Zend_Registry::get( "d2em" )[ 'default' ]; + + return $this->_d2em; + } + + /** + * Persisting invoice + * + * @return void + */ + public function persist() + { + $this->getD2EM()->persist( $this ); + } + + + /** + * Cycles through all the invoice entries for the current invoice, calculates the subtotal, + * vat and total values and sets them to invoice. Returns with the updated OSS_Invoice object. + * + * @param void + * @return \Entities\Invoice + */ + public function updateInvoice() + { + $subTotals = 0; + $tax = 0; + $totals = 0; + + foreach( $this->getEntries() as $entry ) + { + $subTotals += $entry->getSubtotal(); + $tax += $entry->getTax(); + $totals += $entry->getTotal(); + } + + $this->setSubtotal( $subTotals ); + $this->setTax( $tax ); + $this->setTotal( $totals ); + + return $this; + } + + /** + * Finalises an invoice (sets the finalised date and the subtotal, vat and total fields) and returns with the updated OSS_Invoice object. + * Calls updateInvoice() for the calculation. + * WARNING: IT IS REALLY DRIVER-BASED, IT WILL FAIL FOR CUSTOMERS. + * + * @param void + * @return \Entities\Invoice + */ + public function finalise() + { + $this->setFinalised( new \DateTime() ); + $this->updateInvoice(); + + $this->addEvent( \Entities\InvoiceEvent::EVENT_FINALISED ); + + return $this; + } + + /** + * Updates the received field by $amount, and if received >= total then sets the paid_date to + * the current date and time. Returns with the updated Invoice model ($this). + * + * @param int|float $amount Received amount + * @return \Entities\Invoice + */ + public function updateReceivedBy( $amount ) + { + $this->setReceived( $this->getReceived() + $amount ); + return $this; + } + + /** + * Set start date and end date for invoice + * + * @param DateTime $start Start date + * @param DateTime $end End date + * @return \Entities\Invoice + */ + public function setPeriod( $start, $end ) + { + $this->setStartDate( $start ); + $this->setEndDate( $end ); + return $this; + } + + + /** + * Returns with the number of days the invoice is overdue. + * + * @return int + */ + public function overdueDays() + { + return OSS_DateTime::dateDiffDays( $this->getDueDate()->format( "Y-m-d" ), null, false ); + } + + + /** + * Adds a new event to the invoice and returns with the OSS_Invoice object. + * + * @param string $type Type of event + * @param string $data default null + * @return \Entities\Invoice + */ + public function addEvent( $type, $data = null ) + { + $event = new \Entities\InvoiceEvent(); + $event->setEvent( $type ); + $event->setCreated( new \DateTime() ); + $event->setInvoice( $this ); + + if( $data ) + $event->setData( $data ); + + $this->getD2EM()->persist( $event ); + $this->addInvoiceEvent( $event ); + + return $this; + } + + + /** + * Sets the paid_date of the invoice, saves to the database and returns with the updated Invoice + * model. + * + * If $paidDate is null then the current date and time will be used, otherwise expects a "yyyy-mm-dd" + * or "mm/dd/yyyy" (ISO or USA) date (with optional "hh:mm:ss" time part) + * + * @param datetime|null $paidDate Paid Date + * @return \Entities\Invoice + */ + public function setPaidDate( $paidDate = null ) + { + $this->setPaidDate( !$paidDate ? new \DateTime() : $paidDate ); + return $this; + } + + + /** + * Adds an invoice item to the invoice and returns with the new InvoiceItem model object. Calls InvoiceItem::addItem() . + * + * If vat code is not passed it will take first latest VAT Rate from item's category. + * + * @param string $itemCode Item code to add in invoice. + * @param int $quantity Quantity of items. + * @param int|float|null $cost If not null then it will overwrite the items default price + * @param string $vatCode VAT code to set vat for item. + * @return \Entities\Invoice + */ + public function addItem( $itemCode, $quantity = 1, $cost = null, $vatCode = null ) + { + if( $this->getFinalised() ) + throw new Exception( 'Invoice is finalised, you cannot add new items to it.' ); + + $query = $this->getD2EM()->createQuery( sprintf( "SELECT it FROM \\Entities\\InvoiceItem it WHERE it.code = '%s' ORDER BY it.created DESC", $itemCode) ) + ->setMaxResults( 1 ); + $item = $query->getResult(); + + + if( !$item ) + throw new Exception( sprintf( "Item with code '%s' is not existent.", $itemCode ) ); + else + $item = $item[0]; + + if( $vatCode ) + { + $query = $this->getD2EM()->createQuery( sprintf( "SELECT tx FROM \\Entities\\InvoiceTaxRate tx WHERE tx.category = '%s' AND tx.code = %s ORDER BY tx.effective_from DESC", $item->getTaxCategory(), $vatCode ) ) + ->setMaxResults( 1 ); + } + else + { + $query = $this->getD2EM()->createQuery( sprintf( "SELECT tx FROM \\Entities\\InvoiceTaxRate tx WHERE tx.category = '%s' ORDER BY tx.effective_from DESC", $item->getTaxCategory() ) ) + ->setMaxResults( 1 ); + } + $taxRate = $query->getResult(); + + if( !$taxRate ) + throw new Exception( sprintf( "Tax rate with code '%s' is not existent.", $vatCode ) ); + else + $taxRate = $taxRate[0]; + + $cost = !$cost ? $item->getCost() : $cost; + $subtotal = round( $cost * $quantity, 2 ); + $tax = round( $subtotal * $taxRate->getRate(), 2 ); + + $entry = new \Entities\InvoiceEntry(); + $entry->setInvoice( $this ); + $entry->setQuantity( $quantity ); + $entry->setUnitCost( $cost ); + $entry->setItem( $item ); + $entry->setTaxRate( $taxRate ); + $entry->setSubtotal( $subtotal ); + $entry->setTax( $tax ); + $entry->setTotal( $subtotal + $tax ); + $entry->setCreated( new \DateTime() ); + + $this->getD2EM()->persist( $entry ); + $this->addInvoiceEntry( $entry ); + + $this->updateInvoice(); + + return $this; + } + + /** + * Gets latest event for current invoice. + * + * @return \Entities\InvoiceEvent|bool + */ + public function getLastEvent() + { + $query = $this->getD2EM()->createQuery( "SELECT e FROM \\Entities\\InvoiceEvent e WHERE e.Invoice = ?1 ORDER BY e.created DESC, e.id DESC" ) + ->setParameter( 1, $this ) + ->setMaxResults( 1 ); + $event = $query->getResult()[0]; + + return isset( \Entities\InvoiceEvent::$EVENTS[ $event->getEvent() ] ) ? \Entities\InvoiceEvent::$EVENTS[ $event->getEvent() ] : false; + } + + /** + * Gets invoice VAT amounts grouped by categories. + * + * Returned array structure: + * $vats = [ + * "S" => [ "rate" => "21.0", "15.14" ], + * "E" => [ "rate" => "15.0", "0" ] + * ] + * + * @return array + */ + public function getTaxDetailed() + { + $vats = []; + foreach( $this->getEntries() as $entry ) + { + $rate = $entry->getTaxRate(); + if( !isset( $vats[ $rate->getCategory() ] ) ) + $vats[ $rate->getCategory() ] = [ "rate" => sprintf( "%.1f", $rate->getRate() * 100 ), "amount" => 0 ]; + + $vats[ $rate->getCategory() ][ "amount" ] += $entry->getTax(); + } + + return $vats; + } +} + +?> \ No newline at end of file diff --git a/library/OSS/Utils.php b/library/OSS/Utils.php new file mode 100644 index 0000000..662a475 --- /dev/null +++ b/library/OSS/Utils.php @@ -0,0 +1,316 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Utils + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Utils +{ + + /** + * Parses an XML using SimpleXML and returns with the result object, or false on error. + * + * @param string $XML + * @return array|bool + */ + public static function parseXML( $XML ) + { + libxml_use_internal_errors( true ); + + $parsedXML = simplexml_load_string( $XML ); + + if( !$parsedXML ) + { + //foreach ( libxml_get_errors() as $error) logError( 'SimpleXML error: ' . $error->message, null, false, false ); + return false; + } + + return $parsedXML; + } + + + /** + * Converts a HTML file to PDF using htmldoc. Please note that the current stable version of htmldoc (v1.8.27) does not support CSS. Make sure PHP has write permission in the + * output directory. Also don't forget that relative paths are relative to the currently executing PHP script (include() and require() doesn't count in that). + * Using command line OpenOffice might be a better solution (a necessary macro and a tiny, one line shell script are available on the internet), as it supports everything in the HTML, + * CSS, images, etc, while the htmldoc 1.9.x is still in "developer snapshot" state. + * + * @param string $html the input HTML file name + * @param string $pdf the output PDF file name + * @param bool $embedFonts default false embed the used fonts to the genereated PDF or not - can save 100's of kBytes even in a plain PDF (think of email sending) + * @return bool + */ + public static function HtmlToPdf( $html, $pdf, $embedFonts = false ) + { + $tempFile = OSS_Utils::getTempDir() . '/html_to_pdf_' . md5( mt_rand() ) . '.html'; + + $res = file_put_contents( $tempFile, mb_convert_encoding( file_get_contents( $html ), 'HTML-ENTITIES' ) ); + + if( !$res ) + return false; + + // a ' &> /dev/null' at the end of the command should work, but it doesn't + $command = 'htmldoc --webpage --continuous --compression=9' . + ($embedFonts == false ? ' --no-embedfonts' : '') . + ' --fontsize 10 --size A4 --top 2cm --bottom 2cm --left 2cm --right 2cm -t pdf14 -f ' + . escapeshellarg( $pdf ) . ' ' . escapeshellarg( $tempFile ); + + //print "\n\n" . $vCommand . "\n\n"; die(); + //file_put_contents('../var/tmp/command.txt', $vCommand); + + $res = @exec( $command ); + + if( !$res ) + return false; + + return @file_exists( $pdf ); + } + + /** + * Converts a HTML file to PDF using wkhtmltopdf + * + * @param string $html the input HTML file name + * @param string $pdf the output PDF file name + * @return bool + */ + public static function wkhtmltopdf( $html, $pdf ) + { + // WARNING: You have to use the static version of wkhtmltopdf + + $command = dirname( __FILE__ ) . '/../../bin/wkhtmltopdf -q -n -O Portrait -s A4 ' . escapeshellarg( $html ) . ' ' . escapeshellarg( $pdf ); + + @unlink( $pdf ); + + $res = @exec( $command, $output, $ret ); + + if( $res === false ) + return false; + + return @file_exists( $pdf ); + } + + + /** + * A generally available function to retrieve options from the application.ini, anywhere in the program. Don't need to use this + * in the controllers, the $this->_options array is available there, this is useful in models, forms and other places. + * + * @todo: this method is rarely called, but a way to speed it up is to store the key and value in session, and then look for it when called + * + * @param string $option + * @return mixed + */ + public static function getIniOption( $option ) + { + return Zend_Controller_Front::getInstance()->getParam( 'bootstrap' )->getApplication()->getOption( $option ); + } + + + /** + * Returns with the resource object. Use if Zend_Registry::get() doesn't work. (And in the OSS framework it doesn't.) + * + * @param string $resource + * @return object + */ + public static function getResource( $resource ) + { + return Zend_Controller_Front::getInstance()->getParam( 'bootstrap ')->getResource( $resource ); + } + + + /** + * Returns with the temporary directory set in the application.ini, or if it is not set, then with the result of sys_get_temp_dir(). + * + * @todo: this method is rarely called, but a way to speed it up is to store the path in session, and then look for it when called + * + * @return string + */ + public static function getTempDir() + { + $tempDir = OSS_Utils::getIniOption( 'temporary_directory' ); + + return ( $tempDir == '' ? sys_get_temp_dir() : $tempDir ); + } + + + /** + * A function to generate a URL with the given parameters. + * + * This is a useful function as no knowledge of the application's path is required. + * + * It is also configurable (via Zend_Application config and assuming 'options' is + * available in Zend_Registry as it would be from OSS_Controller_Action). + * + * You can configure the hostname by setting config: utils.genurl.host_mode + * + * * Default (no config / invalid config): ''$_SERVER['HTTP_HOST']'' + * * ''HTTP_X_FORWARDED_HOST'': Use ''$_SERVER['HTTP_X_FORWARDED_HOST']'' + * * ''REPLEACE'': set with value from utils.genurl.host_replace + * + * @param string|bool $controller default false The controller to call. + * @param string|bool $action default false The action to call (controller must be set if setting action) + * @param string|bool $module default false The module to use. Set to false to ignore. + * @param array $params default array() An array of key value pairs to add to the URL. + * @param string $host Defaults to null. Hostname (including http[s]://) to override url with + * @return string + */ + public static function genUrl( $controller = false, $action = false, $module = false, $params = array(), $host = null ) + { + $options = Zend_Registry::get( 'options' ); + + $url = Zend_Controller_Front::getInstance()->getBaseUrl(); + + if( $host !== null ) + { + // strip out http[s]:// + if( strpos( $url, 'https://' ) === 0 ) + $url = substr( $url, 8 ); + else if( strpos( $url, 'http://' ) === 0 ) + $url = substr( $url, 7 ); + + $pos = strpos( $url, '/' ); + + if( $pos !== false ) + $url = substr( $url, $pos ); + } + else + { + if( isset( $options['utils']['genurl']['host_mode'] ) ) + { + switch( $options['utils']['genurl']['host_mode'] ) + { + case 'HTTP_X_FORWARDED_HOST': + $host = $_SERVER['HTTP_X_FORWARDED_HOST']; + break; + + case 'REPLACE': + $host = $options['utils']['genurl']['host_replace']; + break; + + default: + $host = $_SERVER['HTTP_HOST']; + } + } + else + $host = $_SERVER['HTTP_HOST']; + } + + $url = $host . $url; + + // when the webpage is directly under "xyz.com/", and not in "xyz.com/wherever" + // an empty href attribute in an anchor tag means "the current URL", which is not always good + //if( $url == '' ) + + if( strpos( $url, 'http' ) !== 0 ) + { + $protocol = 'http'; + + if( isset( $_SERVER['HTTPS'] ) && ( $_SERVER['HTTPS'] == 'on' ) ) + { + $protocol = 'https'; + } + elseif( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) + { + $protocol = $_SERVER['HTTP_X_FORWARDED_PROTO']; + } + + $url = "{$protocol}://{$url}"; + } + + if( $module ) + $url .= "/{$module}"; + + if( $controller ) + $url .= "/{$controller}"; + + if ( $action ) + $url .= "/{$action}"; + + if( sizeof( $params ) > 0 ) + { + foreach( $params as $var => $value ) + $url .= "/{$var}/{$value}"; + } + + return $url; + } + + /** + * Returns number with ordinal postfix. + * + * @param int $number + * @return string + */ + public static function ordinal( $number ) + { + $ends = array( 'th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th' ); + + return ( in_array( $number % 100, array( 11, 12, 13 ) ) ? $number . 'th' : $number . $ends[ $number % 10 ] ); + } + + + /** + * Creates a uniformly distributed directory path from given numeric id. + * + * First it turn given id to hex. Then it appends new hex with leading + * zeros to reach given length ( 3 by default ) if necessary. And then + * new string is reversed. From reversed string function takes as many + * characters as defined in length. + * + * e.g. `uniformDistHash( 216 )` returns `8/d/0/216/` + * `uniformDistHash( 7 )` returns `7/0/0/7/` + * `uniformDistHash( 5057 )` returns `1/c/3/5057/` + * + * @param int $id Id for making file structure + * @param int $length How many levels should be created + * @return string + */ + public static function uniformDistHash( $id, $length = 3 ) + { + $tmpstr = strrev( str_pad( dechex( $id ), $length, 0, STR_PAD_LEFT ) ); + $str = ""; + for( $i = 0; $i < $length; $i++ ) + $str .= $tmpstr[ $i ] . "/"; + $str .= $id . "/"; + + return $str; + } + +} diff --git a/library/OSS/Validate/Exception.php b/library/OSS/Validate/Exception.php new file mode 100644 index 0000000..1ed4e14 --- /dev/null +++ b/library/OSS/Validate/Exception.php @@ -0,0 +1,47 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_Exception extends Zend_Validate_Exception +{ +} diff --git a/library/OSS/Validate/OSSDateTimeValidate.php b/library/OSS/Validate/OSSDateTimeValidate.php new file mode 100644 index 0000000..c3c7135 --- /dev/null +++ b/library/OSS/Validate/OSSDateTimeValidate.php @@ -0,0 +1,78 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSDateTimeValidate extends Zend_Validate_Abstract +{ + + const INVALID_DATE = 'invalidDate'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_DATE => 'Invalid date.' + ); + + + /** + * It will report error for the valid '1970-01-01 01:00:00' date. + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + if( $value != date( "Y-m-d H:i:s", strtotime( $value ) ) ) + { + $this->_error( self::INVALID_DATE ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSDateValidate.php b/library/OSS/Validate/OSSDateValidate.php new file mode 100644 index 0000000..974ee24 --- /dev/null +++ b/library/OSS/Validate/OSSDateValidate.php @@ -0,0 +1,161 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Validate_OSSDateValidate extends Zend_Validate_Abstract +{ + + const INVALID_DATE = 'invalidDate'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_DATE => 'Invalid date.' + ); + + /** + * Ignore empty flag + * + * @var bool + */ + protected $_ignore_empty = false; + + /** + * Date format + * + * @var string + */ + protected $_dateFormat = OSS_Date::DF_EUROPEAN; + + /** + * Constructor + * + * Sets validator options + * + * @param array $options + * @return void + */ + public function __construct( $options = array() ) + { + if( !array_key_exists( 'ignore_empty', $options ) ) + { + $options['ignore_empty'] = false; + } + + $this->setIgnoreEmpty( $options['ignore_empty'] ); + + if( isset( $options['dateformat'] ) ) + $this->_dateFormat = $options['dateformat']; + } + + /** + * Set ingore empty flag + * + * @param bool $b Flag value + * @return void + */ + public function setIgnoreEmpty( $b = true ) + { + $this->_ignore_empty = $b; + } + + /** + * Get ingore empty flag + * + * @return bool + */ + public function getIgnoreEmpty() + { + return (bool)$this->_ignore_empty; + } + + /** + * Set the format of the date to validate against + * + * @see OSS_Date::$DATE_FORMATS + * @param $f int The format + */ + public function setDateFormat( $f ) + { + $this->_dateFormat = $f; + } + + /** + * It will if check if given date is European + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + + if( ( trim( $value ) == "" || trim( $value ) == "--" ) && $this->getIgnoreEmpty() ) + return true; + + $ts = OSS_Date::getTimestamp( $value, $this->_dateFormat ); + + if( $ts === false ) + { + $this->_error( self::INVALID_DATE ); + return false; + } + + $dparts = OSS_Date::dateSplit( $value, $this->_dateFormat ); + + $value = sprintf( "%02d/%02d/%d", $dparts[0], $dparts[1], $dparts[2] ); + + if( $value != date( "d/m/Y", mktime( 0, 0, 0, $dparts[1], $dparts[0], $dparts[2] ) ) ) + { + $this->_error( self::INVALID_DATE ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSDnsRecord.php b/library/OSS/Validate/OSSDnsRecord.php new file mode 100644 index 0000000..de7483d --- /dev/null +++ b/library/OSS/Validate/OSSDnsRecord.php @@ -0,0 +1,98 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Validate_OSSDnsRecord extends Zend_Validate_Abstract +{ + + const INVALID_REC = 'invalidRec'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_REC => 'Invalid DNS record content.' + ); + + /** + * It will return true if given value is valid for record by its type + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + if( is_array( $context ) && isset( $context['type'] ) ) + { + switch( strtoupper( $context['type'] ) ) + { + case 'A': + $validator = new OSS_Validate_OSSIPv4(); + break; + + case 'AAAA': + $validator = new OSS_Validate_OSSIPv6(); + break; + + case 'CNAME': + case 'NS': + case 'MX': + $validator = new OSS_Validate_OSSDomainName(); + break; + + default: + return true; + } + + return $validator->isValid( $value ); + } + + + return true; + } + +} diff --git a/library/OSS/Validate/OSSDoctrine2Uniqueness.php b/library/OSS/Validate/OSSDoctrine2Uniqueness.php new file mode 100644 index 0000000..dbfb45b --- /dev/null +++ b/library/OSS/Validate/OSSDoctrine2Uniqueness.php @@ -0,0 +1,187 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSDoctrine2Uniqueness extends Zend_Validate_Abstract +{ + const NO_ENTMGR = 'noEntityManager'; + const NOT_UNIQUE = 'notUnique'; + const NO_ENTITY = 'noEntity'; + const NO_PROPERTY = 'noProperty'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::NO_ENTMGR => "There is no Doctrine2 Entity Manager available", + self::NOT_UNIQUE => "'%value%' already exists in the database", + self::NO_ENTITY => "Entity name is a required parameter in OSSDoctrineUniqueness", + self::NO_PROPERTY => "Property name is a required parameter in OSSDoctrineUniqueness", + ); + + /** + * The entity to use + * @var string + */ + protected $_entity; + + /** + * The entity property to use + * @var string + */ + protected $_property; + + /** + * The database to use + * @var string + */ + protected $_database = 'default'; + + + /** + * Constructor + * + * $table and $column are required parameters. + * + * @param string $entity The database entity to use + * @param string $property The entity property to check for uniqueness + * @throws OSS_Validate_Exception + * @return void + */ + public function __construct( $params ) + { + if( $params['entity'] == '' ) + { + $this->_error( self::NO_ENTITY ); + return false; + } + + if( $params['property'] == '' ) + { + $this->_error( self::NO_PROPERTY ); + return false; + } + + if( isset( $params['database'] ) ) + $this->_database = $params['database']; + + $this->setEntity( $params['entity'] ); + $this->setProperty( $params['property'] ); + } + + + /** + * Defined by Zend_Validate_Interface + * + * Returns true if and only if $value is unique + * + * @param string $value + * @throws Doctrine_Exception + * @retrun bool + */ + public function isValid( $value ) + { + $this->_setValue( $value ); + + $em = Zend_Registry::get( "d2em" )[ $this->_database ]; + + $users = $em->getRepository( $this->getEntity() ) + ->findBy( [ $this->getProperty() => $value ] ); + + if( count( $users ) ) + { + $this->_error( self::NOT_UNIQUE ); + return false; + } + + return true; + } + + + /** + * Setter method for $_property + * + * @param $p The property to set + * @return void + */ + public function setProperty( $p ) + { + $this->_property = $p; + } + + + /** + * Setter method for $_entity + * + * @param $e The entity to set + * @return void + */ + public function setEntity( $e ) + { + $this->_entity = $e; + } + + + /** + * Getter method for $_column + * + * @return string + */ + public function getProperty() + { + return $this->_property; + } + + + /** + * Getter method for $_table + * + * @return string + */ + public function getEntity() + { + return $this->_entity; + } + +} diff --git a/library/OSS/Validate/OSSDoctrineUniqueness.php b/library/OSS/Validate/OSSDoctrineUniqueness.php new file mode 100644 index 0000000..edaad61 --- /dev/null +++ b/library/OSS/Validate/OSSDoctrineUniqueness.php @@ -0,0 +1,178 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSDoctrineUniqueness extends Zend_Validate_Abstract +{ + + const NOT_UNIQUE = 'notUnique'; + const NO_TABLE = 'noTable'; + const NO_COLUMN = 'noColumn'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::NOT_UNIQUE => "'%value%' already exists in the database", + self::NO_TABLE => "Table name is a required parameter in OSSDoctrineUniqueness", + self::NO_COLUMN => "Column name is a required parameter in OSSDoctrineUniqueness", + ); + + /** + * The database table to use + * @var string + */ + protected $_table; + + /** + * The table column to use + * @var string + */ + protected $_column; + + + /** + * Constructor + * + * $table and $column are required parameters. + * + * @param array $params Array of params kontainig table and column + * @throws OSS_Validate_Exception + * @return bool|void + */ + public function __construct( $params ) + { + if( $params['table'] == '' ) + { + $this->_error( self::NO_TABLE ); + return false; + } + + if( $params['column'] == '' ) + { + $this->_error( self::NO_COLUMN ); + return false; + } + + $this->setTable( $params['table']) ; + $this->setColumn( $params['column']) ; + } + + + /** + * Defined by Zend_Validate_Interface + * + * Returns true if and only if $value is unique + * + * @param string $value + * @throws Doctrine_Exception + * @return bool + */ + public function isValid( $value ) + { + $this->_setValue( $value ); + + $table = Doctrine::getTable( $this->getTable() ); + + $col = $this->getColumn(); + + if ( !$table->hasColumn( $col ) ) throw new Doctrine_Exception( "Column {$col} does not exist on table " . $this->getTable() ); + + $fn = "findBy{$col}"; + $rows = $table->$fn( $value ); + + if ( $rows->count() != 0 ) + { + $this->_error(self::NOT_UNIQUE); + return false; + } + + return true; + } + + + /** + * Setter method for $_column + * + * @param $_column the $_column to set + * @return void + */ + public function setColumn($_column) + { + $this->_column = $_column; + } + + + /** + * Setter method for $_table + * + * @param $_table the $_table to set + * @return void + */ + public function setTable($_table) + { + $this->_table = $_table; + } + + + /** + * Getter method for $_column + * @return string + */ + public function getColumn() + { + return $this->_column; + } + + + /** + * Getter method for $_table + * @return string + */ + public function getTable() + { + return $this->_table; + } + +} diff --git a/library/OSS/Validate/OSSDomainName.php b/library/OSS/Validate/OSSDomainName.php new file mode 100644 index 0000000..bf813a5 --- /dev/null +++ b/library/OSS/Validate/OSSDomainName.php @@ -0,0 +1,98 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Validate_OSSDomainNAme extends Zend_Validate_Abstract +{ + + const INVALID_REC = 'invalidRec'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_REC => 'Invalid domain name.' + ); + + /** + * It will return true if given value is valid domain + * + * e.g. Valid names: exmaple.com, 02exmaple.com, test.example.ie + * Invalid names ex$ample.ie, example.ie23, test.example, test..ie + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + $parts = explode( '.', strtolower( $value ) ); + + if( count( $parts ) < 2 ) + return false; + + foreach( $parts as $ix => $part ) + { + if( strlen( $part ) < 1 ) + return false; + + if( $ix == count( $parts ) - 1 ) + { + if( !preg_match( '/^[a-z]+$/', $part ) || strlen( $part ) > 6 || strlen( $part ) < 2 ) + return false; + } + + if( !preg_match( '/^[a-z0-9\-]+$/', $part ) ) + return false; + + if( substr( $part, 0, 1 ) == '-' || substr( $part, -1, 1 ) == '-' ) + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSFloat.php b/library/OSS/Validate/OSSFloat.php new file mode 100644 index 0000000..5b86ee9 --- /dev/null +++ b/library/OSS/Validate/OSSFloat.php @@ -0,0 +1,77 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSFloat extends Zend_Validate_Abstract +{ + + const MSG_NUMERIC = 'msgNumeric'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::MSG_NUMERIC => "'%value%' is not float" + ); + + /** + * Returns true if and only if $value is float + * + * @param mixed $value + * @throws Doctrine_Exception + * @return bool + */ + public function isValid( $value ) + { + $this->_setValue( $value ); + + if( is_float( $value ) ) + { + $this->_error( self::MSG_NUMERIC ); + return false; + } + + return true; + } +} diff --git a/library/OSS/Validate/OSSIPv4.php b/library/OSS/Validate/OSSIPv4.php new file mode 100644 index 0000000..ba1fcb0 --- /dev/null +++ b/library/OSS/Validate/OSSIPv4.php @@ -0,0 +1,77 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Validate_OSSIPv4 extends Zend_Validate_Abstract +{ + + const INVALID_REC = 'invalidRec'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_REC => 'Invalid IPv4 address' + ); + + /** + * It will return true if given value is valid A record (IPv4 address) + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + $ip2long = ip2long( $value ); + + if( $ip2long === false ) + return false; + + return $value == long2ip( $ip2long ); + } + +} diff --git a/library/OSS/Validate/OSSIPv4Cidr.php b/library/OSS/Validate/OSSIPv4Cidr.php new file mode 100644 index 0000000..b31d62c --- /dev/null +++ b/library/OSS/Validate/OSSIPv4Cidr.php @@ -0,0 +1,88 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Validate_OSSIPv4Cidr extends Zend_Validate_Abstract +{ + + const INVALID_CIDR = 'invalidCidr'; + const INVALID_IP = 'invalidIp'; + const INVALID_NET = 'invalidNet'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_CIDR => 'Invalid IPv4 CIDR format', + self::INVALID_IP => 'Invalid IPv4 address', + self::INVALID_NET => 'Invalid IPv4 subnet size' + ); + + /** + * It will return true if given value is valid A record (IPv4 address) + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value ) + { + if( !( $pos = ( strpos( $value, '/' ) ) ) ) + return false; + + $ip = substr( $value, 0, $pos ); + $net = substr( $value, $pos + 1 ); + + if( !strlen( $net ) || !preg_match( '/^[0-9]+$/', $net ) ) + return false; + + if( $net > 32 ) + return false; + + return Zend_Validate::is( $ip, 'OSS_Validate_OSSIPv4' ); + } + +} diff --git a/library/OSS/Validate/OSSIPv6.php b/library/OSS/Validate/OSSIPv6.php new file mode 100644 index 0000000..10df4b1 --- /dev/null +++ b/library/OSS/Validate/OSSIPv6.php @@ -0,0 +1,102 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Validate_OSSIPv6 extends Zend_Validate_Abstract +{ + + const INVALID_REC = 'invalidRec'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_REC => 'Invalid IPv6 address.' + ); + + /** + * It will return true if given value is valid AAAA record (IPv6 address) + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + if( strlen( $value ) < 3 ) + return $value == '::'; + + if( strpos( $value, '.' ) ) + { + $lastcolon = strrpos( $value, ':' ); + + $validator = new OSS_Validate_OSSIPv4(); + + if( !( $lastcolon && $validator->isValid( substr( $value, $lastcolon + 1 ) ) ) ) + return false; + + $value = substr( $value, 0, $lastcolon ) . ':0:0'; + } + + $dc = strpos( $value, '::' ); + + if( $dc === false ) { + return preg_match( '/\A(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}\z/i', $value ) == 1; + } + + $colonCount = substr_count( $value, ':' ); + if( $colonCount < 8 && $dc ) { + return preg_match( '/\A(?::|(?:[a-f0-9]{1,4}:)+):(?:(?:[a-f0-9]{1,4}:)*[a-f0-9]{1,4})?\z/i', $value ) == 1; + } + + // special case with ending or starting double colon + if( $colonCount == 8 ) + return preg_match( '/\A(?:::)?(?:[a-f0-9]{1,4}:){6}[a-f0-9]{1,4}(?:::)?\z/i', $value ) == 1; + + return false; + } + +} diff --git a/library/OSS/Validate/OSSIPv6Cidr.php b/library/OSS/Validate/OSSIPv6Cidr.php new file mode 100644 index 0000000..47b0f99 --- /dev/null +++ b/library/OSS/Validate/OSSIPv6Cidr.php @@ -0,0 +1,88 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ + +class OSS_Validate_OSSIPv6Cidr extends Zend_Validate_Abstract +{ + + const INVALID_CIDR = 'invalidCidr'; + const INVALID_IP = 'invalidIp'; + const INVALID_NET = 'invalidNet'; + + /** + * Possible error messages + * + * @var array + */ + protected $_messages = array( + self::INVALID_CIDR => 'Invalid IPv4 CIDR format', + self::INVALID_IP => 'Invalid IPv4 address', + self::INVALID_NET => 'Invalid IPv4 subnet size' + ); + + /** + * It will return true if given value is valid A record (IPv4 address) + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid( $value ) + { + if( !( $pos = ( strpos( $value, '/' ) ) ) ) + return false; + + $ip = substr( $value, 0, $pos ); + $net = substr( $value, $pos + 1 ); + + if( !strlen( $net ) || !preg_match( '/^[0-9]+$/', $net ) ) + return false; + + if( $net > 128 ) + return false; + + return Zend_Validate::is( $ip, 'OSS_Validate_OSSIPv6' ); + } + +} diff --git a/library/OSS/Validate/OSSIdenticalField.php b/library/OSS/Validate/OSSIdenticalField.php new file mode 100644 index 0000000..bccbec9 --- /dev/null +++ b/library/OSS/Validate/OSSIdenticalField.php @@ -0,0 +1,192 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSIdenticalField extends Zend_Validate_Abstract +{ + const NOT_MATCH = 'notMatch'; + const NO_FIELD_NAME = 'noFieldName'; + const FIELD_DOESNT_EXIST = 'fieldDoesntExist'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::NOT_MATCH => 'This field does not match %fieldTitle%.', + self::NO_FIELD_NAME => 'No field name to match against provided', + self::FIELD_DOESNT_EXIST => 'The given field name does not exist in the context' + ); + + /** + * Error message variables + * @var array + */ + protected $_messageVariables = array( + 'fieldName' => '_fieldName', + 'fieldTitle' => '_fieldTitle' + ); + + /** + * Name of the field as it appear in the $context array. + * + * @var string + */ + protected $_fieldName; + + /** + * Title of the field to display in an error message. + * + * If evaluates to null then will be set to $this->_fieldName. + * + * @var string + */ + protected $_fieldTitle; + + + /** + * Constructor + * + * $fieldName is required and defines the element name to match the value + * against in the current form / subform context. + * + * @param array $params Array of params containing fieldName and fieldTitle fields + * @return void + */ + public function __construct( $params ) + { + $this->setFieldName( $params['fieldName'] ); + $this->setFieldTitle( + isset( $params['fieldName'] ) ? $params['fieldName'] : $params['fieldTitle'] + ); + } + + + /** + * Returns the field name. + * + * @return string + */ + public function getFieldName() + { + return $this->_fieldName; + } + + + /** + * Sets the field name. + * + * @param string $fieldName + * @return OSS_Validate_OSSIdenticalField + */ + public function setFieldName( $fieldName ) + { + $this->_fieldName = $fieldName; + return $this; + } + + + /** + * Returns the field title. + * + * @return int + */ + public function getFieldTitle() + { + return $this->_fieldTitle; + } + + + /** + * Sets the field title. + * + * @param string:null $fieldTitle + * @return OSS_Validate_OSSIdenticalField + */ + public function setFieldTitle($fieldTitle = null) + { + $this->_fieldTitle = $fieldTitle ? $fieldTitle : $this->_fieldName; + + return $this; + } + + + /** + * Checks to see if the given $value matches the value of the given $fieldName in $context. + * + * Returns true if and only if a field name has been set, the field name is available in the + * context, and the value of that field name matches the provided value. + * + * @param string $value + * @param null|array $context + * @throws OSS_Validate_Exception + * @return bool + */ + public function isValid( $value, $context = null ) + { + $this->_setValue( $value ); + + $field = $this->getFieldName(); + + if ( empty( $field ) ) + { + $this->_error( self::NO_FIELD_NAME ); + return false; + } + + if ( !isset( $context[$field] ) ) + { + $this->_error( self::FIELD_DOESNT_EXIST ); + return false; + } + + if ( $value != $context[$field] ) + { + $this->_error( self::NOT_MATCH ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSIrishTaxNumber.php b/library/OSS/Validate/OSSIrishTaxNumber.php new file mode 100644 index 0000000..8ace4e9 --- /dev/null +++ b/library/OSS/Validate/OSSIrishTaxNumber.php @@ -0,0 +1,123 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSIrishTaxNumber extends Zend_Validate_Abstract +{ + + const NOT_VALID = 'notValid'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::NOT_VALID => 'This is not a valid Irish tax / PAYE number.' + ); + + + /** + * Validates a given tax / VAT / PAYE number for the correct format. This + * includes number and position of letters / digits and MOD23. + * + * @link http://www.iecomputersystems.com/ordering/eu_vat_numbers.htm + * @link http://www.braemoor.co.uk/software/vat.shtml + * + * @param string $value The tax number to validate + * @return boo + */ + public function isValid( $value ) + { + // 1234567X or 1X34567X are valid (includes one or two letters either the last or second + last) + + if( preg_match( '/^\d{7}[a-z]$/i', $value ) ) + { + if( self::checkMOD23( $value ) === false ) + { + $this->_error( self::NOT_VALID ); + return false; + } + + return true; + } + + if( preg_match( '/^\d[a-z]\d{5}[a-z]$/i', $value ) ) + { + $vRecombined = "0" . substr( $value, 2, 5 ) . substr( $value, 0, 1 ) . substr( $value, 7, 1 ); + + if( self::checkMOD23( $vRecombined ) === false ) + { + $this->_error( self::NOT_VALID ); + return false; + } + + return true; + } + + $this->_error( self::NOT_VALID ); + return false; + } + + + /** + * Returns true if the checksum in the specified PPSN or tax number, + * without the 'IE' prefix, is valid. + * + * @param string $value Value to perform modulus 23 checksum on. + * @return bool + */ + private static function checkMOD23( $value ) + { + $total = 0; + + for( $i = 0; $i < 7; ++$i ) + $total += (int) $value[$i] * ( 8 - $i ); + + $mod = $total % 23; + + if( $mod === 0 ) $mod = 23; + + return( chr( 64 + $mod ) == strtoupper( $value[7] ) ); + } + +} diff --git a/library/OSS/Validate/OSSMACAddress.php b/library/OSS/Validate/OSSMACAddress.php new file mode 100644 index 0000000..fc24539 --- /dev/null +++ b/library/OSS/Validate/OSSMACAddress.php @@ -0,0 +1,85 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSMACAddress extends Zend_Validate_Abstract +{ + + const INVALID_MAC = 'invalidMAC'; + + /** + * Error message templates + * @var array + */ + protected $_messages = array( + self::INVALID_MAC => 'Invalid MAC address.' + ); + + + /** + * Cheks is given value is MAC address + * + * @param string $value + * @param null $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + $vMAC = strtoupper( $value ); + + // valid MAC address formats: 0123456789AB | 01-23-45-67-89-AB | 01:23:45:67:89:AB | 0123.4567.89AB + $vMatch0 = preg_match( "/^[0-9A-F]{12}$/", $vMAC ); + $vMatch1 = preg_match( "/^([0-9A-F]{2}\-){5}([0-9A-F]{2})$/", $vMAC ); + $vMatch2 = preg_match( "/^([0-9A-F]{2}\:){5}([0-9A-F]{2})$/", $vMAC ); + $vMatch3 = preg_match( "/^([0-9A-F]{4}\.){2}([0-9A-F]{4})$/", $vMAC ); + + if( $vMatch0 + $vMatch1 + $vMatch2 + $vMatch3 == 0 ) + { + $this->_error( self::INVALID_MAC ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSMobileNumber.php b/library/OSS/Validate/OSSMobileNumber.php new file mode 100644 index 0000000..f2207de --- /dev/null +++ b/library/OSS/Validate/OSSMobileNumber.php @@ -0,0 +1,79 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSMobileNumber extends Zend_Validate_Abstract +{ + + const INVALID = 'invalid'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::INVALID => 'This is not a valid mobile phone number' + ); + + + /** + * Checks is given value is mobile number + * + * @param string $value + * @param null $context + * @return bool + */ + public function isValid( $value, $context = null ) + { + $number = preg_replace( "/\D/", '', (string) $value ); + + if( !preg_match( "/^08[35679]\d{7}$/", $number ) ) + { + $this->_error( self::INVALID ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSNotRegex.php b/library/OSS/Validate/OSSNotRegex.php new file mode 100644 index 0000000..0b714e3 --- /dev/null +++ b/library/OSS/Validate/OSSNotRegex.php @@ -0,0 +1,150 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @see Zend_Validate_Abstract + */ +require_once 'Zend/Validate/Abstract.php'; + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSNotRegex extends Zend_Validate_Abstract +{ + const INVALID = 'regexInvalid'; + const MATCH = 'regexMatch'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::INVALID => "Invalid type given, value should be string, integer or float", + self::MATCH => "'%value%' matches against pattern '%pattern%'" + ); + + /** + * Error message variables + * @var array + */ + protected $_messageVariables = array( + 'pattern' => '_pattern' + ); + + /** + * Regular expression pattern + * + * @var string + */ + protected $_pattern; + + /** + * Constructor + * + * Sets validator options + * + * @param string $pattern + * @return void + */ + public function __construct( $pattern ) + { + $this->setPattern( $pattern ); + } + + /** + * Returns the pattern option + * + * @return string + */ + public function getPattern() + { + return $this->_pattern; + } + + /** + * Sets the pattern option + * + * @param string $pattern + * @return OSS_Validate_OSSNotRegex + */ + public function setPattern( $pattern ) + { + $this->_pattern = (string) $pattern; + return $this; + } + + /** + * Defined by Zend_Validate_Interface + * + * Returns true if and only if $value matches against the pattern option + * + * @param string $value + * @throws Zend_Validate_Exception if there is a fatal error in pattern matching + * @return bool + */ + public function isValid( $value ) + { + if( !is_string( $value ) && !is_int( $value ) && !is_float( $value ) ) + { + $this->_error( self::INVALID ); + return false; + } + + $this->_setValue( $value ); + + $status = @preg_match( $this->_pattern, $value ); + + if( $status === false ) + { + require_once 'Zend/Validate/Exception.php'; + throw new Zend_Validate_Exception( "Internal error matching pattern '$this->_pattern' against value '$value'" ); + } + + if( $status ) + { + $this->_error( self::MATCH ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSNumericBetween.php b/library/OSS/Validate/OSSNumericBetween.php new file mode 100644 index 0000000..4ecf25f --- /dev/null +++ b/library/OSS/Validate/OSSNumericBetween.php @@ -0,0 +1,135 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSNumericBetween extends Zend_Validate_Abstract +{ + + const MSG_NUMERIC = 'msgNumeric'; + const MSG_MINIMUM = 'msgMinimum'; + const MSG_MAXIMUM = 'msgMaximum'; + + /** + * Minimum value + * @var int + */ + public $minimum = 0; + + /** + * Minimum value + * @var int + */ + public $maximum = 100; + + /** + * Error message variables + * @var array + */ + protected $_messageVariables = array( + 'min' => 'minimum', + 'max' => 'maximum' + ); + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::MSG_NUMERIC => "'%value%' is not integer", + self::MSG_MINIMUM => "'%value%' must be at least '%min%'", + self::MSG_MAXIMUM => "'%value%' must be no more than '%max%'" + ); + + /** + * Constructor + * + * $table and $column are required parameters. + * + * @param string $entity The database entity to use + * @param string $property The entity property to check for uniqueness + * @throws OSS_Validate_Exception + * @return void + */ + public function __construct( $params ) + { + if( is_array( $params ) ) + { + if( isset( $params['min'] ) ) + $this->minimum = $params['min']; + + if( isset( $params['max'] ) ) + $this->maximum = $params['max']; + } + } + + /** + * Returns true if and only if $value is integer and between $minimum and $maximum + * + * @param mixed $value + * @return bool + */ + public function isValid( $value ) + { + $this->_setValue( $value ); + + if( !is_int( (int)$value ) ) + { + $this->_error( self::MSG_NUMERIC ); + return false; + } + + if( $value < $this->minimum ) + { + $this->_error( self::MSG_MINIMUM ); + return false; + } + + if( $value > $this->maximum ) + { + $this->_error( self::MSG_MAXIMUM ); + return false; + } + + return true; + } +} diff --git a/library/OSS/Validate/OSSPSN.php b/library/OSS/Validate/OSSPSN.php new file mode 100644 index 0000000..b7a9214 --- /dev/null +++ b/library/OSS/Validate/OSSPSN.php @@ -0,0 +1,135 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSPSN extends Zend_Validate_Abstract +{ + + const NOT_VALID = 'notValid'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::NOT_VALID => 'This is not a valid PPS number.' + ); + + + /** + * Validates a given PPS number for the correct format. This + * includes number and position of letters / digits and MOD23. + * + * @link http://en.wikipedia.org/wiki/Personal_Public_Service_Number + * @link http://pear.php.net/package/Validate_IE/docs/latest/__filesource/fsource_Validate_IE__Validate_IE-1.0.2ValidateIE.php.html#a443 + * + * @param string $value The tax number to validate + * @return bool + */ + public function isValid( $value ) + { + // 1234567T or 1234567TW are valid (includes one or two letters either at the end) + + if( preg_match( '/^\d{7}[a-zA-Z]$/i', $value ) ) + { + if( self::checkMOD23( $value ) === false ) + { + $this->_error( self::NOT_VALID ); + return false; + } + + return true; + } + + if( preg_match( '/^\d{7}[a-zA-Z][\ wtxWTX]$/i', $value ) ) + { + if( self::checkMOD23( substr( $value, 0, 8 ) ) === false ) + { + $this->_error( self::NOT_VALID ); + return false; + } + + return true; + } + + if( preg_match( '/^\d{7}[a-zA-Z][a-zA-Z]$/i', $value ) ) + { + if( self::checkMOD23( $value ) === false ) + { + $this->_error( self::NOT_VALID ); + return false; + } + + return true; + } + + $this->_error( self::NOT_VALID ); + return false; + } + + + /** + * Returns true if the checksum in the specified PPSN or tax number, + * without the 'IE' prefix, is valid. + * + * @param string $value Value to perform modulus 23 checksum on. + * @return bool + */ + private static function checkMOD23( $value ) + { + $total = 0; + + for( $i = 0; $i < 7; ++$i ) + $total += (int) $value[$i] * ( 8 - $i ); + + if( strlen( $value ) == 9 ) + $total += ( ord( strtoupper( $value[8] ) ) - 64 ) * 9; + + $mod = $total % 23; + + if( $mod === 0 ) $mod = 23; + + return( chr( 64 + $mod ) == strtoupper( $value[7] ) ); + } + +} diff --git a/library/OSS/Validate/OSSPassword.php b/library/OSS/Validate/OSSPassword.php new file mode 100644 index 0000000..e79c0e3 --- /dev/null +++ b/library/OSS/Validate/OSSPassword.php @@ -0,0 +1,154 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSPassword extends Zend_Validate_Abstract +{ + + const DEFAULT_STRENGTH = 2; + + const NOT_VALID = 'notValid'; + + /** + * List of regular expressions (character classes) that the password is checked against + * @var array + */ + protected static $REGEXPS = array( + 'LC_LETTER' => '/[a-z]{1,}+/u', + 'UC_LETTER' => '/[A-Z]{1,}+/u', + 'DIGIT' => '/[0-9]{1,}+/u', + 'PUNCTUATION' => '/[\!\"\£\$\%\^\&\*\(\)\-\=\_\+\{\}\[\]\;\’\#\:\@\~\,\.\/\<\>\?\|]{1,}+/u' + ); + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::NOT_VALID => 'The given password is not strong enough. Please choose a password that is a mixture of upper and lower case letters or also contains a number of punctuation mark.' + ); + + /** + * The number of character classes to test for + * + * @var int + */ + private $_strength; + + /** + * Sets validator options + * Accepts the following option keys: + * 'strength' => scalar, number of different character classes to test for + * + * @param array|Zend_Config $options + * @return void + */ + public function __construct( $options = null ) + { + if( $options instanceof Zend_Config ) + { + $options = $options->toArray(); + } + + if( isset( $options['strength'] ) ) + $this->setStrength( $options['strength'] ); + else + $this->setStrength( self::DEFAULT_STRENGTH ); + + } + + + /** + * Set the password validator strength. + * + * @param int $s The strength to test for. + * @throws OSS_Validate_Exception + * @return void + */ + public function setStrength( $s = self::DEFAULT_STRENGTH ) + { + if( !is_numeric( $s ) || $s <= 0 || $s > count( self::$REGEXPS ) ) + { + throw new OSS_Validate_Exception( 'Invalid strength provided' ); + } + + $this->_strength = $s; + } + + /** + * Get the password validator strength. + * + * @return int + */ + public function getStrength() + { + return $this->_strength; + } + + /** + * Returns true if $valid (the password) meets the minimum strength requirements. + * + * @param string $value + * @return bool + */ + public function isValid( $value ) + { + $this->_setValue( $value ); + + $strength = 0; + + foreach( self::$REGEXPS as $regexp ) + { + if( Zend_Validate::is( $value, 'Regex', array( $regexp ) ) ) $strength++; + } + + if( $strength < $this->getStrength() ) + { + $this->_error( self::NOT_VALID ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSRequiredIf.php b/library/OSS/Validate/OSSRequiredIf.php new file mode 100644 index 0000000..ff57bf3 --- /dev/null +++ b/library/OSS/Validate/OSSRequiredIf.php @@ -0,0 +1,91 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Validates with special condition. + * + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSRequiredIf extends Zend_Validate_Abstract +{ + + /** + * Options array + * + * @var array + */ + private $_options = null; + + /** + * Constructor + * + * @param array|Zend_Config $options + * @return void + */ + public function __construct( $options = null ) + { + $this->_options = $options; + } + + + /** + * Returns true // Something is wrong here or I'm not understanding + * + * @param string $value + * @return bool + */ + public function isValid( $value ) + { + return true; + } + + + /** + * Returns with the validator options. + * + * @return array|null + */ + public function getOptions() + { + return $this->_options; + } + +} diff --git a/library/OSS/Validate/OSSTimeValidate.php b/library/OSS/Validate/OSSTimeValidate.php new file mode 100644 index 0000000..326a676 --- /dev/null +++ b/library/OSS/Validate/OSSTimeValidate.php @@ -0,0 +1,84 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSTimeValidate extends Zend_Validate_Abstract +{ + + const INVALID_TIME = 'invalidTime'; + + /** + * Error message templates + * @var array + */ + protected $_messages = array( + self::INVALID_TIME => 'Invalid time.' + ); + + + /** + * It will if check if given time is validate + * + * @param strig $value Date string + * @param null|mixed $context + * @return bool + */ + public function isValid($value, $context = null) + { + $vTimeParts = explode(':', $value); + + if ( + (sizeof($vTimeParts) < 2) || (sizeof($vTimeParts) > 3) || // hh:mm or hh:mm:ss + ((int) $vTimeParts[0] != $vTimeParts[0]) || ($vTimeParts[0] < 0) || ($vTimeParts[0] > 23) || // hours + ((int) $vTimeParts[1] != $vTimeParts[1]) || ($vTimeParts[1] < 0) || ($vTimeParts[1] > 59) || // minutes + ( (isset($vTimeParts[2]) == true) && ( ((int) $vTimeParts[2] != $vTimeParts[2]) || ($vTimeParts[2] < 0) || ($vTimeParts[2] > 59) ) ) // seconds + ) + { + $this->_error( self::INVALID_TIME ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/Validate/OSSUrlValidate.php b/library/OSS/Validate/OSSUrlValidate.php new file mode 100644 index 0000000..499adc5 --- /dev/null +++ b/library/OSS/Validate/OSSUrlValidate.php @@ -0,0 +1,80 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Validate + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Validate_OSSUrlValidate extends Zend_Validate_Abstract +{ + + const INVALID_URL = 'invalidUrl'; + + /** + * Error message templates + * @var array + */ + protected $_messageTemplates = array( + self::INVALID_URL => "'%value%' is not a valid URL.", + ); + + + /** + * Checks is given value is valid URL + * + * @param string $value + * @param null $context + * @return bool + */ + public function isValid( $value ) + { + $valueString = (string) $value; + $this->_setValue( $valueString ); + + if( !Zend_Uri::check( $value ) ) + { + $this->_error( self::INVALID_URL ); + return false; + } + + return true; + } + +} diff --git a/library/OSS/View/Helper/Buttonlink.php b/library/OSS/View/Helper/Buttonlink.php new file mode 100644 index 0000000..3af7144 --- /dev/null +++ b/library/OSS/View/Helper/Buttonlink.php @@ -0,0 +1,95 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ +/** + * Abstract class for extension + */ +require_once 'Zend/View/Helper/FormElement.php'; + +/** + * Helper to render button-link element. + * + * @category OSS + * @package OSS_View + * @subpackage Helper + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_View_Helper_Buttonlink extends Zend_View_Helper_FormElement +{ + /** + * Generates a link button. + * + * @param string|array $name If a string, the element name. If an array, + * all other parameters are ignored, and the array elements are + * extracted in place of added parameters. + * @param mixed $value The element value. + * @param array $attribs Attributes for the element tag. + * @return string The element XHTML. + */ + public function buttonlink( $name, $value = null, $attribs = null ) + { + $info = $this->_getInfo( $name, $value, $attribs ); + extract( $info ); // name, value, attribs, options, listsep, disable, id + + // check if disabled + $disabled = ''; + if( $disable ) + $disabled = ' disabled="disabled"'; + + if( $id ) + $id = ' id="' . $this->view->escape( $id ) . '"'; + + $label = $attribs["label"]; + unset( $attribs["label"] ); + + if( isset( $attribs["class"] ) ) + { + $class = ' class="' . $attribs["class"] . '" '; + unset( $attribs["class"] ); + } + else + $class = ' class="btn" '; + + // Render the button. + $xhtml = '_htmlAttribs( $attribs ) + . $this->getClosingBracket() . $label . ''; + + return $xhtml; + } +} diff --git a/library/OSS/View/Helper/DatabaseDropdown.php b/library/OSS/View/Helper/DatabaseDropdown.php new file mode 100644 index 0000000..c67309c --- /dev/null +++ b/library/OSS/View/Helper/DatabaseDropdown.php @@ -0,0 +1,185 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Helper to render database dropdown form element + * + * NOTICE: It requires chosen library + * + * @category OSS + * @package OSS_View + * @subpackage Helper + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_View_Helper_DatabaseDropdown extends Zend_View_Helper_FormElement +{ + + /** + * Helper for database dropdown + * + * @param string $name + * @param null|strint $value + * @param null|array $attribs + * @return string + */ + public function databaseDropdown( $name, $value = null, $attribs = null ) + { + // are we in a subform? + if( mb_strpos( $name, ']' ) !== false ) + { + $elId = mb_substr( $name, strrpos( $name, '[' ) + 1, -1 ); + $elName = mb_substr( $name, 0, -1 ); + } + else + { + $elId = $name; + $elName = $name; + } + + + if( isset( $attribs['data-osschzn-options'] ) ) + { + $options = json_decode( $attribs['data-osschzn-options'] ); + unset( $attribs['data-osschzn-options'] ); + } + else + $options = []; + + $html = $this->view->formText( $elName, $value, + array_merge( + $attribs, + array( + 'id' => $elId + ) + ) + ); + if( count( $options ) && ( !isset( $attribs['disabled'] ) || $attribs['disabled'] = false ) && ( !isset( $attribs['class'] ) || strpos( $attribs['class'], "disable" ) === false ) ) + { + $html .= ' +
+ +
'; + } + + return $html; + } + +} diff --git a/library/OSS/View/Helper/DateForm.php b/library/OSS/View/Helper/DateForm.php new file mode 100644 index 0000000..0f0603c --- /dev/null +++ b/library/OSS/View/Helper/DateForm.php @@ -0,0 +1,127 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * Helper to render Date form element + * + * @category OSS + * @package OSS_View + * @subpackage Helper + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_View_Helper_DateForm extends Zend_View_Helper_FormElement +{ + + /** + * Helper for date form + * + * @param string $name + * @param null|strint $value + * @param null|array $attribs + * @return string + */ + public function dateForm( $name, $value = null, $attribs = null ) + { + // are we in a subform? + if( mb_strpos( $name, ']' ) !== false ) + { + $dateId = mb_substr( $name, strrpos( $name, '[' ) + 1, -1 ); + $dateName = mb_substr( $name, 0, -1 ); + } + else + { + $dateId = $name; + $dateName = $name; + } + + if( !isset( $attribs['data-dateformat'] ) ) + $attribs['data-dateformat'] = OSS_Date::DF_EUROPEAN; + + $attribs['placeholder'] = OSS_Date::getFormat( $attribs['data-dateformat'] ); + $jqDateFormat = OSS_Date::getDatepickerFormat( $attribs['data-dateformat'] ); + + $html = $this->view->formText( $dateName, $value, + array_merge( + $attribs, + array( + 'id' => $dateId, + 'style' => 'width: 90px;', + 'maxlength' => 10 + ) + ) + ); + + $html .= ' + '; + + return $html; + } + +} diff --git a/library/OSS/View/Smarty.php b/library/OSS/View/Smarty.php new file mode 100644 index 0000000..4600f4b --- /dev/null +++ b/library/OSS/View/Smarty.php @@ -0,0 +1,462 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_View + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_View_Smarty extends Zend_View_Abstract +{ + + /** + * Smarty object + * @var Smarty + */ + protected $_smarty; + + /** + * Should we use a custom skin? + * @var string + */ + protected $_skin = false; + + + /** + * Is being cloned? + * + * @see OSS_View_Smarty::clearVars() + * @var bool + */ + protected $_isBeingCloned = false; + + + /** + * Constructor + * + * @param string $tmplPath + * @param array $extraParams + * @return void + */ + public function __construct( $tmplPath = null, $extraParams = array() ) + { + $this->_smarty = new Smarty(); + + if( null !== $tmplPath ) + $this->setScriptPath( $tmplPath ); + + foreach( $extraParams as $key => $value ) + $this->_smarty->$key = $value; + } + + + /** + * This is needed for {@link clearVars()} to work properly. It automatically runs (PHP 5) after the "clone" operator did it's job. More descriptions at {@link clearVars()} . + * + * @return void + */ + public function __clone() + { + // see the comments in clearVars() for an explanation + $this->_isBeingCloned = true; + + $tpl_vars = $this->_smarty->tpl_vars; + $template_dir = $this->_smarty->template_dir; + $compile_dir = $this->_smarty->compile_dir; + $config_dir = $this->_smarty->config_dir; + $plugins_dir = $this->_smarty->plugins_dir; + + $this->__construct(); + + $this->_smarty->tpl_vars = $tpl_vars; + $this->_smarty->template_dir = $template_dir; + $this->_smarty->compile_dir = $compile_dir; + $this->_smarty->config_dir = $config_dir; + $this->_smarty->plugins_dir = $plugins_dir; + } + + + /** + * Clear all assigned variables + * + * Clears all variables assigned to Zend_View either via {@link assign()} or property + * overloading ({@link __get()}/{@link __set()}). + * + * Both Zend_View_Helper_Action::cloneView() and Zend_View_Helper_Partial::cloneView() + * executes a "$view->clearVars();" line after a "$view = clone $this->view;" . Because + * of how the "clone" operator works internally (object references are also copied, so a + * clone of this object will point to the same Smarty object instance as this, the + * "$view->clearVars();" unsets all the Smarty template variables. To solve this, + * there is the {@link __clone()} method in this class which is called by the "clone" + * operator just after it did it's cloning job. + * + * This sets a flag ($this->_isBeingCloned) for use below to avoid clearing the template + * variables in the cloned object. + * + * If for any reason this doesn't work, neither after amending {@link __clone()}, an + * other "solution" is in the method, but commented out. That will also work, but it is + * relatively slow and, not nice at all. That takes a look on it's backtrace, and if + * finds a function name "cloneView" then does NOT execute Smarty's clearAllAssign(). + * + * Or just make this an empty function if neither the above works. + * + * @param void + */ + public function clearVars() + { + //if (in_array('cloneView', OSS_Utils::filterFieldFromResult(OSS_Debug::compact_debug_backtrace(), 'function', false, false)) == false) $this->_smarty->clear_all_assign(); + if( !$this->_isBeingCloned ) + $this->_smarty->clearAllAssign(); + else + $this->_isBeingCloned = false; + } + + + /** + * Return the template engine object + * + * @return Smarty + */ + public function getEngine() + { + return $this->_smarty; + } + + + /** + * Set the path to the templates + * + * @param string $path The directory to set as the path. + * @throw Exception If path is not readable + * @return void + */ + public function setScriptPath( $path ) + { + if( is_readable( $path ) ) + { + $this->_smarty->template_dir = $path; + return; + } + + throw new Exception( 'Invalid path provided' ); + } + + + /** + * Retrieve the current template directory + * + * @return string + */ + public function getScriptPaths() + { + return $this->_smarty->template_dir; + } + + + /** + * Alias for setScriptPath + * + * @param string $path + * @param string $prefix Unused + * @return void + */ + public function setBasePath( $path, $prefix = 'Zend_View' ) + { + return $this->setScriptPath( $path ); + } + + + /** + * Alias for setScriptPath + * + * @param string $path + * @param string $prefix Unused + * @return void + */ + public function addBasePath( $path, $prefix = 'Zend_View' ) + { + return $this->setScriptPath( $path ); + } + + + /** + * Assign a variable to the template + * + * @param string $key The variable name. + * @param mixed $val The variable value. + * @return void + */ + public function __set( $key, $val ) + { + $this->_smarty->assignByRef( $key, $val ); + } + + + /** + * Retrieve an assigned variable + * + * @param string $key The variable name. + * @return mixed The variable value. + */ + public function __get( $key ) + { + return $this->_smarty->getTemplateVars( $key ); + } + + + /** + * Allows testing with empty() and isset() to work + * + * @param string $key + * @return bool + */ + public function __isset( $key ) + { + return ( null !== $this->_smarty->getTemplateVars( $key ) ); + } + + + /** + * Allows unset() on object properties to work + * + * @param string $key + * @return void + */ + public function __unset( $key ) + { + $this->_smarty->clearAssign( $key ); + } + + + /** + * Assign variables to the template + * + * Allows setting a specific key to the specified value, OR passing + * an array of key => value pairs to set en masse. + * + * @see __set() + * @param string|array $spec The assignment strategy to use (key or + * array of key => value pairs) + * @param mixed $value (Optional) If assigning a named variable, + * use this as the value. + * @return void + */ + public function assign( $spec, $value = null ) + { + if( is_array( $spec ) ) + { + $this->_smarty->assign( $spec ); + return; + } + + $this->_smarty->assign( $spec, $value ); + } + + + /** + * Processes a template and returns the output. + * + * @param string $name The template to process. + * @return string + */ + public function render( $name ) + { + return $this->_smarty->fetch( $this->resolveTemplate( $name ) ); + } + + + /** + * Processes a template and sends the output. + * + * @param string $name The template to process. + * @return void + */ + public function display( $name ) + { + return $this->_smarty->display( $this->resolveTemplate( $name ) ); + } + + + /** + * Checks to see if the named template exists + * + * @param string $name The template to look for + * @return bool + */ + public function templateExists( $name ) + { + return $this->_smarty->templateExists( $this->resolveTemplate( $name ) ); + } + + + /** + * Register a class for access to static methods / constants in a template. Especially + * useful for classes outside the standard namespace. + * + * From the Smarty developers: + * + * > We have decided not to integrate namespace support into the template syntax. + * > The goal of Smarty is to speparate the design as much as possible from the + * > business logic. With namespace syntax we would more and more of business + * > logic into the templates. + * > + * > Instead ou can register a class with optional namespace for the use in the template like: + * > + * > `$smarty->registerClass("FOO","\Fully\Qualified\Name\Foo");` + * + * @see http://www.smarty.net/forums/viewtopic.php?p=65279 + * + * @param string $n The variable name for the class within the template + * @param string $c The fully qualified class name + */ + public function registerClass( $n, $c ) + { + $this->_smarty->registerClass( $n, $c ); + } + + /** + * Checks to see if the named template exists in the current skin + * + * @param string $name The template to look for + * @return boolean + */ + public function skinTemplateExists( $name ) + { + if( $this->_skin && is_readable( $this->_smarty->template_dir[0] . '/_skins/' . $this->_skin . '/' . $name ) ) + return true; + + return $this->templateExists( $name ); + } + + + /** + * This function "resolves" a given template name into an appropriate + * template file depending on whether we're using skins or not. + * + * If we're using skins and if a template exists in the skin, then + * it'll be used. Otherwise we'll use the default templates. + * + * + * @param string $name The name of the template to use + * @return string The resolved template name + */ + public function resolveTemplate( $name ) + { + // if we're using a skin see if a skin file exists. + // if so, use it, otherwise use the default skin files + if( $this->_skin && is_readable( $this->_smarty->template_dir[0] . '/_skins/' . $this->_skin . '/' . $name ) ) + return '_skins/' . $this->_skin . '/' . $name; + + return $name; + } + + /** + * Run + * + * @return void + */ + protected function _run() + { + include func_get_arg(0); + } + + + /** + * Add a new message to the stack + * + * @param OSS_Message An instance of the OSS_Message class + * @return bool + */ + public function ossAddMessage( OSS_Message &$message ) + { + $OSS_Messages = $this->_smarty->getTemplateVars( 'OSS_Messages' ); + + if ( $OSS_Messages == null ) + $OSS_Messages = array(); + + $OSS_Messages[] = $message; + $this->_smarty->assign( 'OSS_Messages', $OSS_Messages ); + + return true; + } + + /** + * + * Set the skin to use + * @param string $s The name of the skin + * @throws Exception + */ + public function setSkin( $s ) + { + + // does the skin exist? + if( is_readable( $this->_smarty->template_dir[0] . "/_skins/$s" ) ) + { + $this->_skin = $s; + return true; + } + + throw new Exception( "Specified skin directory does not exist or is not readable (" + . $this->_smarty->template_dir[0] . "/_skins/$s" . ")" + ); + } + + /** + * + * Return the name of the skin in use or false if default. + * @return string The name of the skin in use or false if default. + */ + public function getSkin() + { + return $this->_skin; + } + + /** + * Load a config file, optionally load just selected sections + * + * @param string $cfile filename + * @param mixed $sections array of section names, single section or null + * @return Smarty_Internal_Data current Smarty_Internal_Data (or Smarty or Smarty_Internal_Template) instance for chaining + */ + public function configLoad( $cfile, $sections = null ) + { + return $this->_smarty->configLoad( $cfile, $sections ); + } + +} diff --git a/library/OSS/Yubico.php b/library/OSS/Yubico.php new file mode 100644 index 0000000..97bf54c --- /dev/null +++ b/library/OSS/Yubico.php @@ -0,0 +1,310 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_Yubico + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_Yubico +{ + + const ERR_NO_OTP_PROVIDED = -1; + const ERR_CORRUPT_OTP = -2; + const ERR_DATABASE_READ = -3; + const ERR_NO_SUCH_USER = -4; + const ERR_WRONG_AES_KEY = -5; + const ERR_USER_ID_MISMATCH = -6; + const ERR_REPLAY_ATTACK = -7; + const ERR_DATABASE_WRITE = -8; + + + /** + * Contains the possible validation error codes and messages. + * @var array + */ + public static $VALIDATION_ERROR = array( + -1 => 'No OTP provided', + -2 => 'Corrupt OTP', + -3 => 'Database error while trying to read user data', + -4 => 'User does not exist', + -5 => 'Wrong AES key', + -6 => 'User ID mismatch', + -7 => 'Replay attack (session counter)', + -8 => 'Database error while trying to update user data' + ); + + + /** + * Converts a hexadecimal number (string) to its binary number representation. + * + * @param string $hex + * @return string + */ + public static function hex2bin( $hex ) + { + if( !is_string( $hex ) ) + return null; + + $r = ''; + + for( $a = 0; $a < strlen( $hex ); $a += 2 ) + $r .= chr( hexdec( $hex[$a] . $hex[$a + 1] ) ); + + return $r; + } + + + /** + * Converts a ModHex string to hexadecimal. + * + * @param string $pModHex + * @return string + */ + public static function modhex2hex( $modHex ) + { + return strtr( $modHex, 'cbdefghijklnrtuv', '0123456789abcdef' ); + } + + + /** + * Converts a hexadecimal string to ModHex. + * + * @param string $hex + * @return string + */ + public static function hex2modhex( $hex ) + { + return strtr( $hex, '0123456789abcdef', 'cbdefghijklnrtuv' ); + } + + + /** + * Converts a hexadecimal string to Base64. + * + * @param string $hex + * @return string + */ + public static function hex2base64( $hex ) + { + return base64_encode( pack( "H*", $hex ) ); + } + + + /** + * Converts a ModHex string to Base64. + * + * @param string $modHex + * @return string + */ + public static function modhex2base64( $modHex ) + { + return self::hex2base64( self::modhex2hex( $modHex ) ); + } + + + /** + * Decrypts an AES128-ECB encrypted string. + * + * @param string $cipherText the encrypted string, a string of hexadecimal values + * @param string $key the AES key, a string of hexadecimal values + * @return string a string of hexadecimal values + */ + public static function aes128EcbDecrypt( $cipherText, $key) + { + $mcrypt = mcrypt_module_open ( MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '' ); + mcrypt_generic_init( $mcrypt, self::hex2bin( $key ), self::hex2bin( '00000000000000000000000000000000' ) ); + + $decrypted = mdecrypt_generic( $mcrypt, self::hex2bin( $cipherText ) ); + + mcrypt_generic_deinit( $mcrypt ); + mcrypt_module_close( $mcrypt ); + + return bin2hex( $decrypted ); + } + + + /** + * Calculates the CRC16 value of a string. + * + * @param string $string + * @return string the CRC16 value as a hexadecimal number + */ + public static function crc16( $string ) + { + $crc = 0xffff; + + for( $i = 0; $i < 16; $i++ ) + { + $b = hexdec( $string[$i * 2] . $string[( $i * 2 ) + 1] ); + $crc = $crc ^ ( $b & 0xff ); + + for( $j = 0; $j < 8; $j++ ) + { + $n = $crc & 1; + $crc = $crc >> 1; + + if( $n != 0 ) + $crc = $crc ^ 0x8408; + } + } + + return $crc; + } + + + /** + * Checks if the CRC16 value is correct, and returns boolean true or false, respectively. + * + * @param string $string + * @return bool + */ + public static function isCrcGood( $string ) + { + return ( self::crc16( $string ) == 0xf0b8 ); + } + + + /** + * Validates the YubiKey OTP. Checks it against rules and the database, too. + * Returns with an associative array containing the following fields (extracted from the OTP): secret_id, session_counter, session_use, clock, random + * Retruns with a negative integer on error. + * + * -1: No OTP provided + * -2: Corrupt OTP + * -3: DB failure while trying to read user data + * -4: User does not exist + * -5: Wrong AES key + * -6: User ID mismatch + * -7: Replay attack + * -8: DB failure while trying to update user data + * + * @param string $pOTP + * @return array|integer array on success, negative integer on error + */ + public static function validate($pOTP) + { + $pOTP = trim( $pOTP ); + + if ($pOTP == '') return self::ERR_NO_OTP_PROVIDED; + + /* + TODO : see the document YubiKey_Configuration_Manual_xxxx.pdf and come up with a proper validation + + "Mix upper- and lower case", "Mix characters and numeric digits" and both together + + jfdkibjkegielgbgjkbejktuhurirnjt + JFdkibjkegielgbgjkbejktuhurirnjt + j53kibjkegielgbgjkbejktuhurirnjt + J53Kibjkegielgbgjkbejktuhurirnjt + 1U1Cccccccccjjeuufllhfkckvullcbbnbhfjvbglbcv + */ + + //if (!preg_match("/^([cbdefghijklnrtuv]{0,16})([cbdefghijklnrtuv]{32})$/", $pOTP, $matches)) return self::ERR_CORRUPT_OTP; + if (!preg_match("/^([12345678cbdefghijklnrtuvCBDEFGHIJKLNRTUV]{0,16})([12345678cbdefghijklnrtuvCBDEFGHIJKLNRTUV]{32})$/", $pOTP, $matches)) return self::ERR_CORRUPT_OTP; + + $id = $matches[1]; // public id + $modhex_ciphertext = $matches[2]; + + try + { + $row = Doctrine::getTable('User_Yubikey')->findOneByPublicId( $id ); // find by public id + } + catch(Exception $e) + { + return self::ERR_DATABASE_READ; + } + + if (!$row) return self::ERR_NO_SUCH_USER; + + $aeskey = $row->aes_key; + + $ciphertext = self::modhex2hex($modhex_ciphertext); + $plaintext = self::aes128EcbDecrypt( $ciphertext, $aeskey ); + + if (self::isCrcGood($plaintext) == false) return self::ERR_WRONG_AES_KEY; // wrong CRC means wrong AES key + + $secret_id = hexdec( substr($plaintext, 0, 12) ); + $session_counter = hexdec( substr( $plaintext, 14, 2 ) . substr( $plaintext, 12, 2 ) ); + $session_use = hexdec( substr( $plaintext, 22, 2 ) ); + $clock = substr( $plaintext, 18, 2 ) . substr( $plaintext, 16, 2 ); + $random = substr( $plaintext, 20, 2 ); + + //print "plain text: {$plaintext} | secret id: {$secret_id} | session counter: {$session_counter} | session use: {$session_use} | clock: {$clock} | random: {$random} |"; + //die(); + + if ($row->User_id != $secret_id) return self::ERR_USER_ID_MISMATCH; + if ($row->session_counter > $session_counter) return self::ERR_REPLAY_ATTACK; // replay attack + if ( ($row->session_counter == $session_counter) && ($row->session_use >= $session_use) ) return self::ERR_REPLAY_ATTACK; // replay attack + + try + { + if ($row->status == 'A_NEW') $row->status = 'A_ACTIVE'; + + $row->session_counter = $session_counter; + $row->session_use = $session_use; + $row->save(); + } + catch(Exception $e) + { + return self::ERR_DATABASE_WRITE; + } + + return array( + 'secret_id' => $secret_id, + 'session_counter' => $session_counter, + 'session_use' => $session_use, + 'clock' => $clock, + 'random' => $random, + ); + } + + + /** + * Returns error text by given code + * + * @param int $errorCode Eror code is negative value from one to eight + * @return string + */ + public static function getErrorMessage( $errorCode ) + { + return self::$VALIDATION_ERROR[$errorCode]; + } + +} diff --git a/library/OSS/ZFDebug/Controller/Plugin/Debug/Plugin/Doctrine.php b/library/OSS/ZFDebug/Controller/Plugin/Debug/Plugin/Doctrine.php new file mode 100644 index 0000000..83a3a10 --- /dev/null +++ b/library/OSS/ZFDebug/Controller/Plugin/Debug/Plugin/Doctrine.php @@ -0,0 +1,153 @@ + + * @author The Skilled Team of PHP Developers at Open Solutions + */ + +/** + * @category OSS + * @package OSS_ZFDebug + * @subpackage Doctrine + * @copyright Copyright (c) 2007 - 2012, Open Source Solutions Limited, Dublin, Ireland + * @license http://www.opensolutions.ie/licenses/new-bsd New BSD License + */ +class OSS_ZFDebug_Controller_Plugin_Debug_Plugin_Doctrine extends ZFDebug_Controller_Plugin_Debug_Plugin implements ZFDebug_Controller_Plugin_Debug_Plugin_Interface +{ + /** + * Contains plugin identifier name + * + * @var string + */ + protected $_identifier = 'doctrine'; + + /** + * Doctrine connection profiler that will listen to events + * + * @var array + */ + protected $_profilers = array(); + + /** + * Constructor + * + * Create ZFDebug_Controller_Plugin_Debug_Plugin_Variables + * + * @param Doctrine_Manager|array $options + * @return void + */ + public function __construct(array $options = array()) + { + if(!isset($options['manager']) || !count($options['manager'])) { + if (Doctrine_Manager::getInstance()) { + $options['manager'] = Doctrine_Manager::getInstance(); + } + } + + foreach ($options['manager']->getIterator() as $connection) { + $this->_profilers[$connection->getName()] = new Doctrine_Connection_Profiler(); + $connection->setListener($this->_profilers[$connection->getName()]); + } + } + + /** + * Gets identifier for this plugin + * + * @return string + */ + public function getIdentifier() + { + return $this->_identifier; + } + + /** + * Gets menu tab for the Debugbar + * + * @return string + */ + public function getTab() + { + if (!$this->_profilers) + return 'No Profiler'; + + foreach ($this->_profilers as $profiler) { + $time = 0; + foreach ($profiler as $event) { + $time += $event->getElapsedSecs(); + } + $profilerInfo[] = $profiler->count() . ' in ' . round($time*1000, 2) . ' ms'; + } + $html = implode(' / ', $profilerInfo); + + return $html; + } + + /** + * Gets content panel for the Debugbar + * + * @return string + */ + public function getPanel() + { + if (!$this->_profilers) + return ''; + + $html = '

Database queries

'; + + foreach ($this->_profilers as $name => $profiler) { + $html .= '

Connection '.$name.'

    '; + foreach ($profiler as $event) { + if (in_array($event->getName(), array('query', 'execute', 'exec'))) { + $info = htmlspecialchars($event->getQuery()); + } else { + $info = '' . htmlspecialchars($event->getName()) . ''; + } + + $html .= '
  1. [' . round($event->getElapsedSecs()*1000, 2) . ' ms] '; + $html .= $info; + + $params = $event->getParams(); + if(!empty($params)) { + $html .= '
      bindings:
    • '. implode('
    • ', $params) . '
    '; + } + $html .= '
  2. '; + } + $html .= '
'; + } + + return $html; + } + +} diff --git a/library/Twitter/Form.php b/library/Twitter/Form.php new file mode 100644 index 0000000..3bb83fd --- /dev/null +++ b/library/Twitter/Form.php @@ -0,0 +1,213 @@ +addPrefixPath("Twitter_Form_Decorator", "Twitter/Form/Decorator/", "decorator"); + + // Get rid of all the pre-defined decorators + $this->clearDecorators(); + + // Decorators for all the form elements + $this->setElementDecorators($this->_getElementDecorators()); + + // Decorators for the form itself + $this->addDecorator("FormElements") + ->addDecorator("Fieldset"); + + parent::__construct($options); + } + + protected function _getElementDecorators() + { + return array( + "ViewHelper", + array("Errors", array("placement" => "append")), + array("Description", array("tag" => "span", "class" => "help-block")), + array(array("innerwrapper" => "HtmlTag"), array("tag" => "div", "class" => "controls")), + array("Label", array("class" => "control-label")), + array(array("outerwrapper" => "HtmlTag"), array("tag" => "div", "class" => "control-group")) + ); + } + + /** + * @see Zend_Form::addElement + * + * We have to override this, because we have to set some special decorators + * on a per-element basis (checkboxes and submit buttons have different + * decorators than other elements) + */ + public function addElement($element, $name = null, $options = null) + { + parent::addElement($element, $name, $options); + + if(!$element instanceof Zend_Form_Element && $name != null) + { + $element = $this->getElement($name); + } + // An existing instance of a form element was added to the form + // We need to reset its decorators + else + { + $element->clearDecorators(); + $element->setDecorators($this->_getElementDecorators()); + } + + if($element instanceof Zend_Form_Element_File) + { + $decorators = $this->_getElementDecorators(); + $decorators[0] = "File"; + $element->setDecorators($decorators); + } + + // Special style for Zend + if($element instanceof Zend_Form_Element_Submit + || $element instanceof Zend_Form_Element_Reset + || $element instanceof Zend_Form_Element_Button) + { + $class = ""; + + if($element instanceof Zend_Form_Element_Submit + && !($element instanceof Zend_Form_Element_Reset) + && !($element instanceof Zend_Form_Element_Button)) + { + $class = "btn-primary"; + } + + $element->setAttrib("class", trim("btn $class " . $element->getAttrib("class"))); + $element->removeDecorator("Label"); + $element->removeDecorator("outerwrapper"); + $element->removeDecorator("innerwrapper"); + + $this->_addActionsDisplayGroupElement($element); + + //$element->addDecorator(array( + //"outerwrapper" => "HtmlTag"), array("tag" => "div", "class" => "actions") + //); + } + + if($element instanceof Zend_Form_Element_Checkbox) + { + $element->setDecorators(array( + array(array("labelopening" => "HtmlTag"), array("tag" => "label", "class" => "checkbox", "id" => $element->getId()."-label", "for" => $element->getName(), "openOnly" => true)), + "ViewHelper", + array("Checkboxlabel"), + array(array("labelclosing" => "HtmlTag"), array("tag" => "label", "closeOnly" => true)), + array("Errors", array("placement" => "append")), + array("Description", array("tag" => "span", "class" => "help-block")), + array(array("innerwrapper" => "HtmlTag"), array("tag" => "div", "class" => "controls")), + array(array("outerwrapper" => "HtmlTag"), array("tag" => "div", "class" => "control-group")) + )); + } + + if($element instanceof Zend_Form_Element_Radio + || $element instanceof Zend_Form_Element_MultiCheckbox) + { + $multiOptions = array(); + foreach($element->getMultiOptions() as $value => $label) + { + $multiOptions[$value] = " ".$label; + } + + $element->setMultiOptions($multiOptions); + + $element->setAttrib("labelclass", "checkbox"); + + if($element->getAttrib("inline")) + { + $element->setAttrib("labelclass", "checkbox inline"); + } + + if ($element instanceof Zend_Form_Element_Radio) + { + $element->setAttrib("labelclass", "radio"); + } + + if($element->getAttrib("inline")) + { + $element->setAttrib("labelclass", "radio inline"); + } + + $element->setOptions(array("separator" => "")); + $element->setDecorators(array( + "ViewHelper", + array("Errors", array("placement" => "append")), + array("Description", array("tag" => "span", "class" => "help-block")), + array(array("innerwrapper" => "HtmlTag"), array("tag" => "div", "class" => "controls")), + array("Label", array("class" => "control-label")), + array(array("outerwrapper" => "HtmlTag"), array("tag" => "div", "class" => "control-group")) + )); + } + + if($element instanceof Zend_Form_Element_Hidden) + { + $element->setDecorators(array("ViewHelper")); + } + + if($element instanceof Zend_Form_Element_Textarea && !$element->getAttrib('rows')) + { + $element->setAttrib('rows', '3'); + } + + if($element instanceof Zend_Form_Element_Captcha) + { + $element->removeDecorator("viewhelper"); + } + + return $this; + } + + private function _addActionsDisplayGroupElement($element) + { + $displayGroup = $this->getDisplayGroup("zfBootstrapFormActions"); + + if($displayGroup === null) + { + $displayGroup = $this->addDisplayGroup( + array($element), + "zfBootstrapFormActions", + array( + "decorators" => array( + "FormElements", + array("HtmlTag", array("tag" => "div", "class" => "form-actions")) + ) + )); + } + else + { + $displayGroup->addElement($element); + } + + return $displayGroup; + } + /** + * Render + * @param Zend_View_Interface $view + * @return Zend_View + */ + public function render(Zend_View_Interface $view = null) + { + $formTypes = array( // avaible form types of Twitter Bootstrap form (i.e. classes) + 'horizontal', + 'inline', + 'vertical', + 'search' + ); + + $set = false; + + foreach($formTypes as $type) { + if($this->getAttrib($type)) { + $this->addDecorator("Form", array("class" => "form-$type")); + $set = true; + } + } + if(true !== $set) { // if neither type was set, we set the default vertical class + $this->addDecorator("Form", array("class" => "form-vertical")); + } + + return parent::render($view); + } +} diff --git a/library/Twitter/Form/Decorator/Checkboxlabel.php b/library/Twitter/Form/Decorator/Checkboxlabel.php new file mode 100644 index 0000000..d7a344d --- /dev/null +++ b/library/Twitter/Form/Decorator/Checkboxlabel.php @@ -0,0 +1,12 @@ +getElement(); + $separator = $this->getSeparator(); + + return $content . $separator . $element->getLabel(); + } +} diff --git a/library/Twitter/Form/Decorator/Errors.php b/library/Twitter/Form/Decorator/Errors.php new file mode 100644 index 0000000..c8371ce --- /dev/null +++ b/library/Twitter/Form/Decorator/Errors.php @@ -0,0 +1,43 @@ +getElement(); + $view = $element->getView(); + + if (null === $view) { + return $content; + } + + $errors = $element->getMessages(); + if (empty($errors)) { + return $content; + } + + $element->setAttrib("class", trim("error " . $element->getAttrib("class"))); + + $wrapper = $element->getDecorator("outerwrapper"); + if($wrapper) + { + $wrapper->setOption("class", trim("error " . $wrapper->getOption("class"))); + } + + $separator = $this->getSeparator(); + $placement = $this->getPlacement(); + $errorHtml = ""; + foreach($errors as $currentError) + { + $errorHtml .= ''.$currentError.''; + } + //$errors = $view->formErrors($errors, $this->getOptions()); + + switch ($placement) { + case self::APPEND: + return $content . $separator . $errorHtml; + case self::PREPEND: + return $errorHtml . $separator . $content; + } + } +} diff --git a/library/Twitter/Twitter-README.md b/library/Twitter/Twitter-README.md new file mode 100644 index 0000000..3cef7aa --- /dev/null +++ b/library/Twitter/Twitter-README.md @@ -0,0 +1,55 @@ + +From: https://github.com/komola/Bootstrap-Zend-Framework (MIT) + + +An easy way to display forms with Zend Framework + Bootstrap +============================================================ + +This is designed as an easy drop-in replacement for the normal Zend Forms to +work together with Twitter Bootstrap (http://twitter.github.com/bootstrap). + +Getting started +--------------- + +Instaliation +------------ + +Composer way +------------ + +* Add this to your composer.json: + + komola/bootstrap-zend-framework + +Old way +------- + +* Add this to your application.ini config: + + autoloaderNamespaces.Twitter = "Twitter_" + +* Add the library/Twitter folder to your library + +Usage +----- + +* Instead of extending from Zend\_Form extend from Twitter\_Form + +We included a small example application that shows you what you can do with +this. +The interesting parts for our "library" are in /library/Twitter. + +Have fun! + +If you encounter any errors, please report them here on Github. Thanks! + +License +------- + +Copyright (c) 2012-2013 Sebastian Hoitz, komola GmbH + +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