diff --git a/.gitignore b/.gitignore index 838a0a8a..4404b7d9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /config.php /build /docs/_build +/vendor/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst old mode 100755 new mode 100644 index 1b035fce..b270fb28 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,8 +1,41 @@ Changelog ========= +v3.1.0 (released August 7, 2014) +-------------------------------- + +Features: + +* #52: Uptime percentage per server for the last week. +* #101: Pushover.net support. +* #54: Improved phone/tablet compatibility. +* #75: Test mode for email and SMS settings. +* #86: Different design styles on status page (list, table). +* #82: Added Danish translation. +* #103: Added Russian translation. +* #109: Custom time-out per server. +* #119: Log and archive retention period. +* #110: Support for SMSGlobal SMS gateway . +* #82: Support for Danish SMS provider Smsit + +Bugs: + +* #50: Validation on servers page. +* #62: Replace javascript confirm dialogs with Bootstrap modal dialogs. +* #66: Unable to add users with MySQL in strict mode. +* #83: Invalid redirect after switching languages and logging in. +* #105: Fixing check for websites with unverified SSL certificates. +* #107: Fixing update job for Synology DSM Task Scheduler. +* #108: URLs on Windows contained both back- and forward slashes. +* #111: Generated urls for non-default ports included the port twice. +* #28: Permission denied page. +* #53: User selection on server edit page. +* #115: Warning on server page when notifications are disabled. +* #117: Template service has been replaced by Twig. +* Composer added for dependencies. + v3.0.1 (released April 12, 2014) ----------------- +-------------------------------- * #56: Minimum PHP version is PHP 5.3.7 (not PHP 5.3.0). * #58: Server order on users page now matches the order on servers page. diff --git a/Makefile b/Makefile index e54754a6..f4c6d585 100755 --- a/Makefile +++ b/Makefile @@ -1,29 +1,45 @@ tag = $(shell git describe) +VERSION = ${subst v,,$(tag)} +RELEASE_DIR = ./build +RELEASE_FILE = phpservermon-$(VERSION) help: - @echo ' PHP Server Monitor - $(tag) ' - @echo ' - make export [tag=...] - create a new release from tag ' + @echo ' PHP Server Monitor - $(tag)' + @echo ' - make export [tag=...] - create a new release from tag ' + @echo ' - make install - install all dependencies ' + +install: + @echo 'Downloading dependencies using Composer' + php composer.phar install + @echo 'Install complete ' export: @echo 'Building release for tag $(tag) ' - mkdir -p ./build ./build/phpservermon - rm -rf ./build/phpservermon/* - git archive $(tag) | tar -xf - -C ./build/phpservermon/ + mkdir -p $(RELEASE_DIR) $(RELEASE_DIR)/$(RELEASE_FILE) + rm -rf $(RELEASE_DIR)/$(RELEASE_FILE)/* + git archive $(tag) | tar -xf - -C $(RELEASE_DIR)/$(RELEASE_FILE)/ + #find $(RELEASE_DIR)/$(RELEASE_FILE) -name "*.php" -exec sed -i "" "s/@package_version@/$(tag)/" {} \; # for osx + find $(RELEASE_DIR)/$(RELEASE_FILE) -name "*.php" -exec sed -i "s/@package_version@/$(tag)/" {} \; # for linux @echo 'Testing on syntax errors (thats all the automated testing your are going to get for now..) ' - find ./build/phpservermon -name "*.php" | xargs -I file php -l file - find ./build/phpservermon -name "*.php" -exec sed -i "" "s/@package_version@/$(tag)/" {} \; - @echo 'Building HTML documentation' - cd ./build/phpservermon/docs; make BUILDDIR=. html; cd ../../../; + find $(RELEASE_DIR)/$(RELEASE_FILE) -name "*.php" | xargs -I file php -l file + @echo 'Downloading dependencies' + cd $(RELEASE_DIR)/$(RELEASE_FILE); php composer.phar install; php composer.phar dump-autoload --optimize; cd ../../; + rm -f $(RELEASE_DIR)/$(RELEASE_FILE)/composer.phar + rm -f $(RELEASE_DIR)/$(RELEASE_FILE)/composer.json + rm -f $(RELEASE_DIR)/$(RELEASE_FILE)/composer.lock + @echo 'Building HTML documentation using sphinx (http://sphinx-doc.org/)' + mkdir -p $(RELEASE_DIR)/$(RELEASE_FILE)/docs/html + cd $(RELEASE_DIR)/$(RELEASE_FILE)/docs; make BUILDDIR=. html; cd ../../../; @echo 'Cleaning up docs dir' - rm -f ./build/phpservermon/Makefile - rm -f ./build/phpservermon/docs/Makefile - rm -f ./build/phpservermon/docs/make.bat - rm -f ./build/phpservermon/docs/conf.py + rm -f $(RELEASE_DIR)/$(RELEASE_FILE)/Makefile + rm -f $(RELEASE_DIR)/$(RELEASE_FILE)/docs/Makefile + rm -f $(RELEASE_DIR)/$(RELEASE_FILE)/docs/make.bat + rm -f $(RELEASE_DIR)/$(RELEASE_FILE)/docs/conf.py @echo 'Setting folder and file permissions' - find ./build/phpservermon -type f | xargs chmod 0644 - find ./build/phpservermon -type d | xargs chmod 0755 + find $(RELEASE_DIR)/$(RELEASE_FILE) -type f | xargs chmod 0644 + find $(RELEASE_DIR)/$(RELEASE_FILE) -type d | xargs chmod 0755 @echo 'Creating archives' - cd ./build; zip -rq phpservermon-$(tag).zip ./phpservermon; cd ../; - cd ./build; tar -pczf phpservermon-$(tag).tar.gz ./phpservermon; cd ../; - rm -rf ./build/phpservermon + cd $(RELEASE_DIR); zip -rq $(RELEASE_FILE).zip ./$(RELEASE_FILE); cd ../; + cd $(RELEASE_DIR); tar -pczf $(RELEASE_FILE).tar.gz ./$(RELEASE_FILE); cd ../; + #rm -rf $(RELEASE_DIR)/$(RELEASE_FILE) @echo 'Building release finished ' diff --git a/README.rst b/README.rst index 4dce7196..e57bdc11 100755 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ PHP Server Monitor ================== -Version 3.0.1 +Version 3.1.0 PHP Server Monitor is a script that checks whether your websites and servers are up and running. It comes with a web based user interface where you can manage your services and websites, @@ -12,7 +12,7 @@ Features: --------- * Monitor services and websites (see below). -* Email and SMS notifications. +* Email, SMS and Pushover notifications. * View history graphs of uptime and latency. * User authentication with 2 levels (administrator and regular user). * Logs of connection errors, outgoing emails and text messages. @@ -35,14 +35,16 @@ There are two different ways to monitor a server: In both cases the script will return a "status offline", and will start sending out notifications. Each server has its own settings regarding notification. -You can choose for email notification or text message (SMS). +You can choose for email, text message (SMS) and Pushover.net notifications. The following SMS gateways are currently available: -* Mollie - -* Spryng - -* Inetworx - * Clickatell - +* Inetworx - +* Mollie - * Mosms - +* Smsglobal - +* SMSit - +* Spryng - * Textmarketer - Please note: for these gateways you will need an account with sufficient credits. @@ -70,6 +72,12 @@ Install Please see docs/install.rst. In a nutshell: unzip, upload, run install.php, enjoy. +If you have downloaded the source from GitHub (and not a pre-built package), the dependencies are not included. +To be able to run an installation from the repo, you need to run the following command to install the dependencies:: + + php composer.phar install + + Documentation ------------- diff --git a/composer.json b/composer.json new file mode 100755 index 00000000..b3331228 --- /dev/null +++ b/composer.json @@ -0,0 +1,18 @@ +{ + "name": "phpservermon/phpservermon", + "description": "PHP Server Monitor", + "homepage": "http://www.phpservermonitor.org", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/phpservermon/php-pushover" + } + ], + "require": { + "php": ">=5.3.7", + "phpmailer/phpmailer": "5.2.6", + "symfony/http-foundation": "2.4.*", + "php-pushover/php-pushover": "dev-master", + "twig/twig": "1.*" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..48e948b7 --- /dev/null +++ b/composer.lock @@ -0,0 +1,216 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "1d763e23381a086e18f83644bd89f16c", + "packages": [ + { + "name": "php-pushover/php-pushover", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpservermon/php-pushover.git", + "reference": "d13d08dbf5f1cfa73f4adca7e8d27f79c804dd7b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpservermon/php-pushover/zipball/d13d08dbf5f1cfa73f4adca7e8d27f79c804dd7b", + "reference": "d13d08dbf5f1cfa73f4adca7e8d27f79c804dd7b", + "shasum": "" + }, + "type": "library", + "autoload": { + "files": [ + "Pushover.php" + ] + }, + "description": "PHP class for the Pushover.net project", + "support": { + "source": "https://github.com/phpservermon/php-pushover/tree/master" + }, + "time": "2014-07-30 13:55:53" + }, + { + "name": "phpmailer/phpmailer", + "version": "v5.2.6", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "4d9434e394496a5bb7acd9e73046587184b413df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/4d9434e394496a5bb7acd9e73046587184b413df", + "reference": "4d9434e394496a5bb7acd9e73046587184b413df", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "phpdocumentor/phpdocumentor": "*", + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "class.phpmailer.php", + "class.pop3.php", + "class.smtp.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "time": "2013-04-11 16:45:39" + }, + { + "name": "symfony/http-foundation", + "version": "v2.4.8", + "target-dir": "Symfony/Component/HttpFoundation", + "source": { + "type": "git", + "url": "https://github.com/symfony/HttpFoundation.git", + "reference": "68abe34601c519359b60363b99c29ecfb6679bc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/68abe34601c519359b60363b99c29ecfb6679bc4", + "reference": "68abe34601c519359b60363b99c29ecfb6679bc4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/expression-language": "~2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "classmap": [ + "Symfony/Component/HttpFoundation/Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "http://symfony.com", + "time": "2014-07-15 14:07:10" + }, + { + "name": "twig/twig", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig.git", + "reference": "8ce37115802e257a984a82d38254884085060024" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/8ce37115802e257a984a82d38254884085060024", + "reference": "8ce37115802e257a984a82d38254884085060024", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.16-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "https://github.com/fabpot/Twig/graphs/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2014-07-05 12:19:05" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": { + "php-pushover/php-pushover": 20 + }, + "platform": { + "php": ">=5.3.7" + }, + "platform-dev": [ + + ] +} diff --git a/composer.phar b/composer.phar new file mode 100644 index 00000000..1e9ca731 Binary files /dev/null and b/composer.phar differ diff --git a/config.php.sample b/config.php.sample index 2246f244..667afe2e 100755 --- a/config.php.sample +++ b/config.php.sample @@ -4,4 +4,4 @@ define('PSM_DB_USER', 'db_user'); define('PSM_DB_PASS', 'db_pass'); define('PSM_DB_NAME', 'db_name'); define('PSM_DB_HOST', 'localhost'); -?> + diff --git a/cron/status.cron.php b/cron/status.cron.php index 5145b4b3..9b6ce773 100644 --- a/cron/status.cron.php +++ b/cron/status.cron.php @@ -39,12 +39,12 @@ $time = time(); if(psm_get_conf('cron_running') == 1 && ($time - psm_get_conf('cron_running_time') < PSM_CRON_TIMEOUT)) { die('Cron is already running. Exiting.'); } -if(!PSM_DEBUG) { +if(!defined('PSM_DEBUG') || !PSM_DEBUG) { psm_update_conf('cron_running', 1); } psm_update_conf('cron_running_time', $time); -$autorun = new \psm\Util\Updater\Autorun($db); +$autorun = new \psm\Util\Server\UpdateManager($db); $autorun->run(); psm_update_conf('cron_running', 0); diff --git a/docs/conf.py b/docs/conf.py index b8104397..3ab40df6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,7 +51,7 @@ copyright = u'2008-2014, Pepijn Over' # built documents. # # The short X.Y version. -version = '3.0.1' +version = '3.1.0' # The full version, including alpha/beta/rc tags. release = version diff --git a/docs/credits.rst b/docs/credits.rst index dad84d3d..850b44ed 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -9,32 +9,32 @@ Credits The following people have contributed to the development of PHP Server Monitor: -* Pepijn Over +* Pepijn Over - https://github.com/dopeh * Creator and project maintainer -* Jérôme Cabanis +* Jérôme Cabanis - https://github.com/Abawell * History graphs * Date localization * Mobile compatibility * Various fixes and improvements -* Perri Vardy-Mason +* Perri Vardy-Mason - https://github.com/VeoPVM * Textmarketer SMS gateway * Various fixes and improvements -* Luiz Alberto S. Ribeiro +* Luiz Alberto S. Ribeiro - https://github.com/madeinnordeste * Bootstrap implementation * Portuguese Brazilian translation -* Michael Greenhill +* Michael Greenhill - https://github.com/doctorjbeam * Status page -* Andreas Ek +* Andreas Ek - https://github.com/EkAndreas * Mosms SMS gateway @@ -42,6 +42,25 @@ The following people have contributed to the development of PHP Server Monitor: * Website pattern / regular expression search +* nerdalertdk - https://github.com/nerdalertdk + + * Smsit SMS gateway + +* Victor Macko - https://github.com/victormacko + + * SMSGlobal SMS gateway + +* Julien Lebouteiller - https://github.com/Halvra + + * Custom time-out per server + +* Mathias Lange - https://github.com/remmedia + + * Pushover.net support + +* Alexander Moore - http://www.famfamfam.com + + * Icon Translators +++++++++++ @@ -50,11 +69,15 @@ The following people have contributed to the translation of PHP Server Monitor: * Chinese - * manhere + * manhere - https://github.com/manhere * Bulgarian - * Plamen Vasilev + * Plamen Vasilev - https://github.com/PVasileff + +* Danish + + * nerdalertdk * French @@ -79,7 +102,13 @@ The following people have contributed to the translation of PHP Server Monitor: * Spanish - * Klemens Häckel + * Klemens Häckel - http://clickdimension.wordpress.com + +* Russian + + * Roman Beylin - https://github.com/roman-beylin + * Yuriy Lyutov - https://github.com/delysh + Vendors +++++++ @@ -88,4 +117,7 @@ The following libraries are being used by PHP Server Monitor: * jqPlot - http://www.jqplot.com * Twitter Bootstrap - http://getbootstrap.com -* PHP Mailer - https://github.com/PHPMailer/PHPMailer \ No newline at end of file +* Bootstrap Multiselect - https://github.com/davidstutz/bootstrap-multiselect +* PHP Mailer - https://github.com/PHPMailer/PHPMailer +* php-pushover - https://github.com/kryap/php-pushover +* Twig - http://twig.sensiolabs.org diff --git a/docs/developers.rst b/docs/developers.rst index 66c5417c..d0e8ed38 100644 --- a/docs/developers.rst +++ b/docs/developers.rst @@ -8,4 +8,50 @@ There is a master branch, which is stable and always reflects the latest release The develop branch is used for ongoing development and should not be considered stable. If you would like to contribute a patch or feature, please fork the develop branch and send a pull request. -More information can be found in the wiki at https://github.com/phpservermon/phpservermon/wiki. \ No newline at end of file +Languages ++++++++++ + +The server monitor uses language files, which are stored in the directory "src/lang". +The name of the language file consists of the language code (ISO 639-1) and the country code (ISO 3166-1), separated by an underscore. +The extension is ".lang.php". + +Locales +------- + +Each language file should contain a 'locale' key which can be used for formatting dates and times. This 'locale' key must include the locales for different server environments: + +* Linux / OS X: same as filename (language code and country code separated by underscore) +* Windows: http://msdn.microsoft.com/en-US/library/39cwe7zf%28v=vs.80%29.aspx + +For more information, see http://www.php.net/manual/en/function.setlocale.php + +Adding a new language +--------------------- + +To add a new language, follow these steps: + +* Create a new file in the directory "src/lang" named "{locale}.lang.php". +* Copy the contents of the file "en_US.lang.php" to your new file. +* Your new language should now be available on the config page. +* Translate :-) +* Please send a pull request on github (https://github.com/phpservermon/phpservermon) so it can be included in the next release :-) + + +Getting started ++++++++++++++++ + +All code related to phpservermon lives in the "psm" namespace, which can be found under "src/psm". + +The Router (https://github.com/phpservermon/phpservermon/blob/develop/src/psm/Router.class.php) is used to load the modules. +All modules are registered inside the Router class with a unique ID (see getModules()), and can either be loaded manually ($router->run('mod')), or if no module is given it will attempt to discover the module from the $_GET['mod'] var. +If no valid module or controller is found, it will fall back to the default module. + +The module var may exist of 2 parts, separated by an underscore. The first part is the ID of the module, and the second part is the ID of the controller registered in the module. +If no controller ID is found, it will attempt to load the controller with the same ID as the module. + +Examples :: + + $router->run('config'); // module ID "config" and controller ID also "config" (same as $router->run('config_config')) + $router->run('server_status'); // module ID "server" and controller ID "status" + +If the user is not logged in and login is required, it will automatically load the user login controller without throwing an error. \ No newline at end of file diff --git a/docs/faq.rst b/docs/faq.rst index a84d4efb..a0c216c5 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -25,6 +25,37 @@ Regular users: * Run the updater on their assigned servers. +Servers ++++++++ + +What is the difference between a service and a website? +------------------------------------------------------- + +For websites, the monitor attempts to open a regular web page, just like you do in your browser. +It will attempt to retrieve its contents, and also check the HTTP status code (for example "404 not found" will cause an error). +You can then even add a check to make sure the content of the website includes a certain string or matches a certain regular expression. +Please note, it only retrieves the contents and does not execute any Javascript. Your search pattern will not work if it depends on Javascript being executed. + +For services, the monitor only attempts to connect to the IP address and specified port to check whether the server is listening on that port. +For example, if you are running a webserver it will usually listen on port 80 for incoming connections. +So if the monitor is able to connect to the server on port 80, you know the webserver is running and accepting connections. +It does not, however, mean that your website is available to your users, because it might have PHP errors or database problems. +This can be monitored using the website type with a pattern search as described above. + +Are requests made by the monitor included in my website statistics? +------------------------------------------------------------------- + +There are two different ways to gather statistics. +One way is to include a piece of Javascript in your HTML, e.g. for Google Analytics and Piwik. +The other way is to parse the access logs created by your webserver software, which does not require any changes to your code, and is done by tools like Awstats. + +When using tools such as Google Analytics, the monitor requests will not show up in your statistics, because the monitor does not execute any Javascript. +Tools that parse your raw access logs like Awstats, will include the requests made by the monitor. +To make sure these requests can be identified, the monitor uses a custom user agent, which you can usually filter out. The user agent of the monitor looks like:: + + Mozilla/5.0 (compatible; phpservermon/3.0.1; +http://www.phpservermonitor.org) + + Configuration +++++++++++++ diff --git a/docs/install.rst b/docs/install.rst index 930468e4..ca95e03b 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -64,6 +64,15 @@ One of the new features introduced in 3.0 is a user authentication system. Becau For that reason the upgrade script will ask you to create a new account during the upgrade, which you can then use to change the password for the existing accounts. If, for whatever reason this does not work, the upgrade script automatically changes the username of all existing users to their email addresses, which you could use for the forgot password screen. + +Installing from GitHub +++++++++++++++++++++++ +If you have downloaded the source from GitHub (and not a pre-built package), the dependencies are not included. +To be able to run an installation from the repo, you need to run the following command to install the dependencies:: + + php composer.phar install + + Setting up a cronjob ++++++++++++++++++++ @@ -78,10 +87,15 @@ If it is your own server or you have shell access and permission to open the cro As you can see, this line will run the status.cron.php script every 15 minutes. Change the line to suit your needs. If you do not have shell access, ask your web hosting provider to set it up for you. +Please note that some distros have user-specific crontabs (e.g. Debian). If that is the case, you need to omit the user part:: + + */15 * * * * /usr/bin/php /var/www/html/phpservermon/cron/status.cron.php + The update script has been designed to prevent itself from running multiple times. It has a maximum timeout of 10 minutes. After that the script is assumed dead and the cronjob will run again. If you want to change the 10 minutes timeout, find the constant "PSM_CRON_TIMEOUT" in src/includes/psmconfig.inc.php. + Troubleshooting +++++++++++++++ diff --git a/docs/intro.rst b/docs/intro.rst index ac2334c9..daaea398 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -15,7 +15,7 @@ Features ++++++++ * Monitor services and websites (see below). -* Email and SMS notifications. +* Email, SMS and Pushover notifications. * View history graphs of uptime and latency. * User authentication with 2 levels (administrator and regular user). * Logs of connection errors, outgoing emails and text messages. @@ -44,14 +44,22 @@ There are two different ways to monitor a server: Notifications ------------- Each server has its own settings regarding notification. -You can choose for email notification or text message (SMS). +You can choose for email, text message (SMS) and Pushover.net notifications. The following SMS gateways are currently available: -* Mollie - http://www.mollie.nl -* Spryng - http://www.spryng.nl -* Inetworx - http://www.inetworx.ch -* Clickatell - https://www.clickatell.com -* Mosms - http://www.mosms.com -* Textmarketer - http://www.textmarketer.co.uk +* Clickatell - +* Inetworx - +* Mollie - +* Mosms - +* Smsglobal - +* SMSit - +* Spryng - +* Textmarketer - Please note: for these gateways you will need an account with sufficient credits. + + +Download +++++++++ + +The latest version can be downloaded from http://www.phpservermonitor.org/. \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 00000000..731bd77f Binary files /dev/null and b/favicon.ico differ diff --git a/favicon.png b/favicon.png new file mode 100644 index 00000000..9d4d5f28 Binary files /dev/null and b/favicon.png differ diff --git a/src/bootstrap.php b/src/bootstrap.php index 2d338091..486c9b41 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -28,7 +28,6 @@ // Include paths define('PSM_PATH_SRC', dirname(__FILE__) . DIRECTORY_SEPARATOR); -define('PSM_PATH_VENDOR', PSM_PATH_SRC . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR); define('PSM_PATH_INC', PSM_PATH_SRC . 'includes' . DIRECTORY_SEPARATOR); define('PSM_PATH_TPL', PSM_PATH_SRC . 'templates' . DIRECTORY_SEPARATOR); define('PSM_PATH_LANG', PSM_PATH_SRC . 'lang' . DIRECTORY_SEPARATOR); @@ -48,7 +47,10 @@ if(file_exists($path_conf)) { include_once $path_conf; } // check for a debug var -if(defined('PSM_DEBUG') && PSM_DEBUG) { +if(!defined('PSM_DEBUG')) { + define('PSM_DEBUG', false); +} +if(PSM_DEBUG) { error_reporting(E_ALL); ini_set('display_erors', 1); } else { @@ -56,29 +58,28 @@ if(defined('PSM_DEBUG') && PSM_DEBUG) { ini_set('display_errors', 0); } +$vendor_autoload = PSM_PATH_SRC . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; +if(!file_exists($vendor_autoload)) { + die('No dependencies found in vendor dir. Did you install the dependencies? Please run "php composer.phar install".'); +} +require_once $vendor_autoload; + // set autoloader, make sure to set $prepend = true so that our autoloader is called first -function __autoload($class) { +spl_autoload_register(function($class) { // remove leading \ $class = ltrim($class, '\\'); $path_parts = explode('\\', $class); $filename = array_pop($path_parts); - $path = implode(DIRECTORY_SEPARATOR, $path_parts) . + $path = PSM_PATH_SRC . implode(DIRECTORY_SEPARATOR, $path_parts) . DIRECTORY_SEPARATOR . $filename . '.class.php' ; - // search in these dirs: - $basedirs = array( - PSM_PATH_SRC, - PSM_PATH_VENDOR - ); - foreach($basedirs as $dir) { - if(file_exists($dir . $path)) { - require_once $dir . $path; - return; - } + if(file_exists($path)) { + require_once $path; + return; } -} +}); // auto-find all include files $includes = glob(PSM_PATH_INC . '*.inc.php'); @@ -89,14 +90,7 @@ foreach($includes as $file) { $db = new psm\Service\Database(); // sanity check! -if(defined('PSM_INSTALL') && PSM_INSTALL) { - // install mode - if($db->status()) { - // connection established, attempt to load config. - // no biggie if it doesnt work because the user is still in the install module. - psm_load_conf(); - } -} else { +if(!defined('PSM_INSTALL') || !PSM_INSTALL) { if($db->getDbHost() === null) { // no config file has been loaded, redirect the user to the install header('Location: install.php'); diff --git a/src/includes/functions.inc.php b/src/includes/functions.inc.php index 89c02fd7..829de923 100644 --- a/src/includes/functions.inc.php +++ b/src/includes/functions.inc.php @@ -111,7 +111,6 @@ function psm_get_langs() { /** * Get a setting from the config. - * The config must have been loaded first using psm_load_conf() * * @param string $key * @param mixed $alt if not set, return this alternative @@ -119,6 +118,9 @@ function psm_get_langs() { * @see psm_load_conf() */ function psm_get_conf($key, $alt = null) { + if(!isset($GLOBALS['sm_config'])) { + psm_load_conf(); + } $result = (isset($GLOBALS['sm_config'][$key])) ? $GLOBALS['sm_config'][$key] : $alt; return $result; @@ -134,9 +136,11 @@ function psm_get_conf($key, $alt = null) { function psm_load_conf() { global $db; - // load config from database into global scope $GLOBALS['sm_config'] = array(); + if(!defined('PSM_DB_PREFIX') || !$db->status()) { + return false; + } if(!$db->ifTableExists(PSM_DB_PREFIX.'config')) { return false; } @@ -163,13 +167,10 @@ function psm_load_conf() { function psm_update_conf($key, $value) { global $db; - $result = $db->save( - PSM_DB_PREFIX.'config', - array('value' => $value), - array('key' => $key) - ); - // save returns the # rows updated, if 0, key doenst exist yet - if($result === 0) { + // check if key exists + $exists = psm_get_conf($key, false); + if($exists === false) { + // add new config record $db->save( PSM_DB_PREFIX . 'config', array( @@ -177,6 +178,12 @@ function psm_update_conf($key, $value) { 'value' => $value, ) ); + } else { + $db->save( + PSM_DB_PREFIX.'config', + array('value' => $value), + array('key' => $key) + ); } $GLOBALS['sm_config'][$key] = $value; } @@ -260,16 +267,20 @@ function psm_parse_msg($status, $type, $vars) { * @param string $href * @param boolean $header return headers? * @param boolean $body return body? - * @param int $timeout connection timeout in seconds + * @param int $timeout connection timeout in seconds. defaults to PSM_CURL_TIMEOUT (10 secs). * @param boolean $add_agent add user agent? * @return string cURL result */ -function psm_curl_get($href, $header = false, $body = true, $timeout = 10, $add_agent = true) { +function psm_curl_get($href, $header = false, $body = true, $timeout = null, $add_agent = true) { + $timeout = $timeout == null ? PSM_CURL_TIMEOUT : intval($timeout); + $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, $header); curl_setopt($ch, CURLOPT_NOBODY, (!$body)); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_ENCODING, ''); @@ -335,12 +346,9 @@ function psm_date($time) { * Check if an update is available for PHP Server Monitor. * * Will only check for new version if user turned updates on in config. - * @global object $db * @return boolean */ function psm_update_available() { - global $db; - if(!psm_get_conf('show_update')) { // user does not want updates, fair enough. return false; @@ -370,15 +378,15 @@ function psm_update_available() { } /** - * Prepare a new Mailer util. + * Prepare a new phpmailer instance. * * If the from name and email are left blank they will be prefilled from the config. * @param string $from_name * @param string $from_email - * @return \psm\Util\Mailer + * @return \PHPMailer */ function psm_build_mail($from_name = null, $from_email = null) { - $phpmailer = new \psm\Util\Mailer(); + $phpmailer = new \PHPMailer(); $phpmailer->Encoding = "base64"; $phpmailer->SMTPDebug = false; @@ -409,30 +417,93 @@ function psm_build_mail($from_name = null, $from_email = null) { return $phpmailer; } +/** + * Prepare a new Pushover util. + * + * @return \Pushover + */ +function psm_build_pushover() { + $pushover = new \Pushover(); + $pushover->setToken(psm_get_conf('pushover_api_token')); + + return $pushover; +} + +/** + * Prepare a new SMS util. + * + * @return \psm\Txtmsg\TxtmsgInterface + */ +function psm_build_sms() { + $sms = null; + + // open the right class + // not making this any more dynamic, because perhaps some gateways need custom settings (like Mollie) + switch(strtolower(psm_get_conf('sms_gateway'))) { + case 'mosms': + $sms = new \psm\Txtmsg\Mosms(); + break; + case 'smsit': + $sms = new \psm\Txtmsg\Smsit(); + break; + case 'inetworx': + $sms = new \psm\Txtmsg\Inetworx(); + break; + case 'mollie': + $sms = new \psm\Txtmsg\Mollie(); + $sms->setGateway(1); + break; + case 'spryng': + $sms = new \psm\Txtmsg\Spryng(); + break; + case 'clickatell': + $sms = new \psm\Txtmsg\Clickatell(); + break; + case 'textmarketer': + $sms = new \psm\Txtmsg\Textmarketer(); + break; + case 'smsglobal': + $sms = new \psm\Txtmsg\Smsglobal(); + break; + } + + // copy login information from the config file + if($sms) { + $sms->setLogin(psm_get_conf('sms_gateway_username'), psm_get_conf('sms_gateway_password')); + $sms->setOriginator(psm_get_conf('sms_from')); + } + + return $sms; +} + /** * Generate a new link to the current monitor - * @param array $params key value pairs + * @param array|string $params key value pairs or pre-formatted string * @param boolean $urlencode urlencode all params? * @param boolean $htmlentities use entities in url? * @return string */ function psm_build_url($params = array(), $urlencode = true, $htmlentities = true) { - $defports = array(80, 443); $url = ($_SERVER['SERVER_PORT'] == 443 ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; - if(!in_array($_SERVER['SERVER_PORT'], $defports)) { - $url .= ':' . $_SERVER['SERVER_PORT']; - } + + // on Windows, dirname() adds both back- and forward slashes (http://php.net/dirname). + // for urls, we only want the forward slashes. $url .= dirname($_SERVER['SCRIPT_NAME']) . '/'; + $url = str_replace('\\', '', $url); if($params != null) { $url .= '?'; - $delim = ($htmlentities) ? '&' : '&'; + if(is_array($params)) { + $delim = ($htmlentities) ? '&' : '&'; - foreach($params as $k => $v) { - if($urlencode) { - $v = urlencode($v); + foreach($params as $k => $v) { + if($urlencode) { + $v = urlencode($v); + } + $url .= $delim . $k . '=' . $v; } - $url .= $delim . $k . '=' . $v; + } else { + $url .= $params; } } @@ -470,12 +541,11 @@ function psm_POST($key, $alt = null) { /** * Check if we are in CLI mode * - * Note, php_sapi cannot be used because cgi-fcgi returns both for web and cli - * source: https://api.drupal.org/api/drupal/includes!bootstrap.inc/function/drupal_is_cli/7 + * Note, php_sapi cannot be used because cgi-fcgi returns both for web and cli. * @return boolean */ function psm_is_cli() { - return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0))); + return (!isset($_SERVER['SERVER_SOFTWARE']) || php_sapi_name() == 'cli'); } ############################################### @@ -491,7 +561,9 @@ function psm_is_cli() { */ function pre($arr = null) { echo "
";
-	if ($arr === null) debug_print_backtrace();
+	if ($arr === null) {
+		debug_print_backtrace();
+	}
 	print_r($arr);
 	echo "
"; } diff --git a/src/includes/psmconfig.inc.php b/src/includes/psmconfig.inc.php index 58ab54ce..9876bc88 100644 --- a/src/includes/psmconfig.inc.php +++ b/src/includes/psmconfig.inc.php @@ -29,7 +29,7 @@ /** * Current PSM version */ -define('PSM_VERSION', '3.0.1'); +define('PSM_VERSION', '3.1.0'); /** * URL to check for updates. Will not be checked if turned off on config page. @@ -96,3 +96,19 @@ define('PSM_LOGIN_RESET_RUNTIME', 3600); * Number of seconds the cron is supposedly dead and we will run another cron anyway. */ define('PSM_CRON_TIMEOUT', 600); + +/** + * Default timeout in seconds for curl requests (can be overwritten per-server). + */ +define('PSM_CURL_TIMEOUT', 10); + + +/** + * Name of the default theme. + */ +define('PSM_THEME', 'default'); + +/** + * Clone URL for the Pushover.net service. + */ +define('PSM_PUSHOVER_CLONE_URL', 'https://pushover.net/apps/clone/php_server_monitor'); \ No newline at end of file diff --git a/src/lang/bg_BG.lang.php b/src/lang/bg_BG.lang.php index 1d852f24..679fc405 100644 --- a/src/lang/bg_BG.lang.php +++ b/src/lang/bg_BG.lang.php @@ -18,7 +18,7 @@ * along with PHP Server Monitor. If not, see . * * @package phpservermon - * @author Plamen Vasilev + * @author Plamen Vasilev a.k.a Paco * @copyright Copyright (c) 2008-2014 Pepijn Over * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 * @version Release: @package_version@ @@ -29,35 +29,35 @@ $sm_lang = array( 'name' => 'Български - Bulgarian', 'locale' => array('bg_BG.UTF-8', 'bg_BG', 'bulgarian'), 'system' => array( - 'title' => 'Server Monitor', + 'title' => 'Мониторинг', 'install' => 'Инсталация', 'action' => 'Действие', 'save' => 'Запиши', 'edit' => 'Редактирай', - 'delete' => 'Изтрии', - 'deleted' => 'Записът е изтрит', + 'delete' => 'Изтрий', 'date' => 'Дата', - 'message' => 'Съобщебие', + 'message' => 'Съобщение', 'yes' => 'Да', 'no' => 'Не', - 'edit' => 'Редактиране на', 'insert' => 'Добавяне', 'add_new' => 'Добави нов', - 'update_available' => 'Налична е нова версия ({version}). Може да я свалите от тук.', + 'update_available' => 'Налична е нова версия: ({version}). Може да я свалите от тук.', 'back_to_top' => 'Нагоре', - 'go_back' => 'Go back', - // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php + 'go_back' => 'Назад', + 'ok' => 'Ок', + 'cancel' => 'Отказ', + // date/time са във формат според параметъра strftime PHP функцията http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', - 'yesterday_format' => 'Yesterday at %k:%M', - 'other_day_format' => '%A at %k:%M', - 'never' => 'Never', - 'hours_ago' => '%d hours ago', - 'an_hour_ago' => 'about an hour ago', - 'minutes_ago' => '%d minutes ago', - 'a_minute_ago' => 'about a minute ago', - 'seconds_ago' => '%d seconds ago', - 'a_second_ago' => 'a second ago', + 'yesterday_format' => 'Вчера в %k:%M', + 'other_day_format' => '%A в %k:%M', + 'never' => 'Никога', + 'hours_ago' => 'преди %d часа', + 'an_hour_ago' => 'преди час', + 'minutes_ago' => 'преди %d минути', + 'a_minute_ago' => 'преди минута', + 'seconds_ago' => 'преди %d секунди', + 'a_second_ago' => 'преди секунда', ), 'menu' => array( 'config' => 'Настройки', @@ -71,29 +71,37 @@ $sm_lang = array( 'users' => array( 'user' => 'Потребител', 'name' => 'Име', - 'user_name' => 'Username', - 'password' => 'Password', - 'password_repeat' => 'Password repeat', - 'password_leave_blank' => 'Leave blank to keep unchanged', - 'level' => 'Level', - 'level_10' => 'Administrator', - 'level_20' => 'User', - 'level_description' => 'Administrators have full access: they can manage servers, users and edit the global configuration.
Users can only view and run the updater for the servers that have been assigned to them.', + 'user_name' => 'Потребител', + 'password' => 'Парола', + 'password_repeat' => 'Повторете паролата', + 'password_leave_blank' => 'Оставете празно, за да не бъде променена паролата', + 'level' => 'Ниво на достъп', + 'level_10' => 'Администратор', + 'level_20' => 'Потребител', + 'level_description' => 'Администраторите имат пълен достъп: могат да управляват сървърите, потребителите и да редактират глобалните настройки.
Потребителите могат само да виждат статуса на сървърите и да обнояват информацията за даден сървър, за който им е разрешен достъп.', 'mobile' => 'Мобилен телефон', 'email' => 'Имейл', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Изтриване на потребител', + 'delete_message' => 'Сигурни ли сте, че искате да изтриете потребител \'%1\'?', + 'deleted' => 'Потребителят е изтрит успешно.', 'updated' => 'Информацията за потребителя е обновена.', 'inserted' => 'Потребителят е добавен.', - 'profile' => 'Profile', - 'profile_updated' => 'Your profile has been updated.', - 'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.', - 'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).', - 'error_user_name_exists' => 'The given username already exists in the database.', - 'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.', - 'error_user_email_invalid' => 'The email address is invalid.', - 'error_user_level_invalid' => 'The given user level is invalid.', - 'error_user_no_match' => 'The user could not be found in the database.', - 'error_user_password_invalid' => 'The entered password is invalid.', - 'error_user_password_no_match' => 'The entered passwords do not match.', + 'profile' => 'Профил', + 'profile_updated' => 'Профилът е обновен успешно', + 'error_user_name_bad_length' => 'Потребителското име трябва да съдържа между 2 и 64 символа', + 'error_user_name_invalid' => 'Може да съдържа само латински букви (a-z, A-Z), цифри (0-9) и долна черта (_).', + 'error_user_name_exists' => 'Вече съществува акаунт с това потребителско име.', + 'error_user_email_bad_length' => 'Имейл адреса трябва да съдържа между 5 и 255 символа.', + 'error_user_email_invalid' => 'Въведения имейл адрес е грешен.', + 'error_user_level_invalid' => 'Избраното ниво на достъп е грешно.', + 'error_user_no_match' => 'Потребителят не може да бъде намерен.', + 'error_user_password_invalid' => 'Въведената парола е грешка.', + 'error_user_password_no_match' => 'Въведените пароли не съвпадат.', ), 'log' => array( 'title' => 'Записи в лога', @@ -101,42 +109,66 @@ $sm_lang = array( 'status' => 'Статус', 'email' => 'Имейл', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'Няма налични логове', ), 'servers' => array( 'server' => 'Сървър', + 'status' => 'Статус', 'label' => 'Име', 'domain' => 'Хост', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Порт', 'type' => 'Тип', - 'type_website' => 'Website', - 'type_service' => 'Service', - 'pattern' => 'Търсене на образец/схема', - 'pattern_description' => 'If this pattern is not found on the website, the server will be marked offline. Regular expressions are allowed.', + 'type_website' => 'Сайт', + 'type_service' => 'Услуга', + 'pattern' => 'Търсене на стринг/образец', + 'pattern_description' => 'Ако този текст не е намерен в интернет страницата (когато имате добавен сайт), той ще бъде маркиран като Офлайн. Регулярните изрази са разрешени.', 'last_check' => 'Последна проверка', 'last_online' => 'Последно на линия', 'monitoring' => 'Мониторинг', + 'no_monitoring' => 'Не се наблюдава', + 'email' => 'Имейл', 'send_email' => 'Имейл', + 'sms' => 'SMS', 'send_sms' => 'SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Изтриване на сървър', + 'delete_message' => 'Сигурни ли сте, че искате да изтриете сървър \'%1\'?', + 'deleted' => 'Сървъра е изтрит успешно.', 'updated' => 'Информацията за сървъра е обновена.', - 'inserted' => 'Сървърът е добвен успешно.', - 'latency' => 'Пинг', - 'latency_max' => 'Latency (maximum)', - 'latency_min' => 'Latency (minimum)', - 'latency_avg' => 'Latency (average)', - 'year' => 'Year', - 'month' => 'Month', - 'week' => 'Week', - 'day' => 'Day', - 'hour' => 'Hour', - 'warning_threshold' => 'Warning threshold', - 'warning_threshold_description' => 'Number of failed checks required before it is marked offline.', - 'chart_last_week' => 'Last week', - 'chart_history' => 'History', - // Charts date format according jqPlot date format http://www.jqplot.com/docs/files/plugins/jqplot-dateAxisRenderer-js.html + 'inserted' => 'Сървърът е добавен успешно.', + 'latency' => 'Латенция', + 'latency_max' => 'Латенция (максимална)', + 'latency_min' => 'Латенция (минимална)', + 'latency_avg' => 'Латенция (средна)', + 'uptime' => 'Ъптайм', + 'year' => 'Година', + 'month' => 'Месец', + 'week' => 'Седмица', + 'day' => 'Ден', + 'hour' => 'Час', + 'warning_threshold' => 'Предупредителен праг', + 'warning_threshold_description' => 'Брой неуспешни проверки, преди сървъра или сайта да бъдат маркирани като Офлайн.', + 'chart_last_week' => 'Последната седмица', + 'chart_history' => 'История', + // Charts формат на датата според jqPlot http://www.jqplot.com/docs/files/plugins/jqplot-dateAxisRenderer-js.html 'chart_day_format' => '%d.%m.%Y', 'chart_long_date_format' => '%d.%m.%Y %H:%M:%S', 'chart_short_date_format' => '%d.%m %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => 'Основни настройки', @@ -154,24 +186,30 @@ $sm_lang = array( 'sms_status' => 'Да се изпращат ли SMS-и', 'sms_gateway' => 'Портал за изпращане на SMS-и', 'sms_gateway_mosms' => 'Mosms', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_mollie' => 'Mollie', 'sms_gateway_spryng' => 'Spryng', 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', 'sms_gateway_username' => 'Потребител', 'sms_gateway_password' => 'Парола', 'sms_from' => 'Номер на изпращача', - 'alert_type' => 'Изберете кога желаете да получавате известия
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Изберете кога желаете да получавате известия', 'alert_type_description' => 'Промяна на сатуса:
'. 'Ще получавате известие когато има промяна със връзката на даден някой от описаните сървър или сайт. От Онлайн -> Офлайн и от Офлайн -> Онлайн.
'. '
Офлайн
'. 'Ще получите известие когато връзката до сървъра е изгубена за *ПЪРВИ ПЪТ*. Например, '. - 'вашия cron скрипт проверява всеки 15 минути и връзката до сървъра е изгубена в 1 часа през ноща и не работи до 6 часа сутринта '. + 'вашия cron скрипт проверява всеки 15 минути и връзката до сървъра е изгубена в 1 часа през нощта и не работи до 6 часа сутринта '. 'Вие ще получите едно известие в 1 часа за това
'. '
Винаги:
'. - 'Ще получавате известие при всяка проверка на Вашия cron скрипт дори когато връзката до даден сървър или сайт е била'. - 'прекъсната в продължение на часове.', + 'Ще получавате известие при всяка проверка на Вашия крон скрипт дори когато връзката до даден сървър или сайт е била прекъсната в продължение на часове.', 'alert_type_status' => 'Промяна на статуса', 'alert_type_offline' => 'Офлайн', 'alert_type_always' => 'Винаги', @@ -179,52 +217,81 @@ $sm_lang = array( 'log_status_description' => 'Ако е отметнато, системата ще записва всяка промяна.', 'log_email' => 'Да се пази ли лог на изпратените имейли от системата', 'log_sms' => 'Да се пази ли лог на изпратените SMS съобщения от системата', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => 'Настройките са обновени успешно.', 'tab_email' => 'Имейл', 'tab_sms' => 'SMS', - 'tab_log' => 'логовете', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Имейл настройки', 'settings_sms' => 'SMS настройки', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => 'Настройки на известията', 'settings_log' => 'Настройки на логовете', 'auto_refresh' => 'Автоматично опресняване', 'auto_refresh_servers' => 'Автоматично опресняване на страницата.
'. ''. - 'Времето е в секунди, ако е 0 страницата няма да се обноява.'. + 'Времето е в секунди, ако е 0 страницата няма да се обновява.'. '', - 'seconds' => 'seconds', + 'seconds' => 'секунди', + 'test' => 'Тест', + 'test_email' => 'Ще бъде изпратенo тестово съобщение до имейл адреса, който сте задали в профила си.', + 'test_sms' => 'Ще бъде изпратен тестово SMS съобщение до телефонния номер, който сте задали в профила си.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Изпрати', + 'test_subject' => 'Test', + 'test_message' => 'Тестово съобщение', + 'email_sent' => 'Тестовия имейл е изпратен успешно.', + 'email_error' => 'Възникна грешка при изпращането на тесовия имейл', + 'sms_sent' => 'Тестовото SMS съобщение е изпратеното успешно.', + 'sms_error' => 'Възникна грешка при изпращането на тестовия SMS', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // За нов ред в имейл съобщението, моля използвайте тага
'notifications' => array( - 'off_sms' => 'Syrvyryt \'%LABEL%\' e OFFLINE: ip=%IP%, port=%PORT%. Greshka=%ERROR%', + 'off_sms' => 'Сървър \'%LABEL%\' е Офлайн: ip=%IP%, port=%PORT%. Greshka=%ERROR%', 'off_email_subject' => 'Връзката до \'%LABEL%\' е ИЗГУБЕНА', 'off_email_body' => "Неуспешно свързване:

Сървър: %LABEL%
IP адрес: %IP%
Порт: %PORT%
Грешка: %ERROR%
Днес: %DATE%", - 'on_sms' => 'Syrvyryt \'%LABEL%\' e ONLINE: ip=%IP%, port=%PORT%', + 'off_pushover_title' => 'Връзката до \'%LABEL%\' е ИЗГУБЕНА', + 'off_pushover_message' => "Неуспешно свързване:

Сървър: %LABEL%
IP адрес: %IP%
Порт: %PORT%
Грешка: %ERROR%
Днес: %DATE%", + 'on_sms' => 'Сървър \'%LABEL%\' е Онлайн: ip=%IP%, port=%PORT%', 'on_email_subject' => 'Връзката до \'%LABEL%\' е ВЪЗСТАНОВЕНА', 'on_email_body' => "Връзката до '%LABEL%' беше ВЪЗСТАНОВЕНА:

Сървър: %LABEL%
IP адрес: %IP%
Порт: %PORT%
Днес: %DATE%", + 'on_pushover_title' => 'Връзката до \'%LABEL%\' е ВЪЗСТАНОВЕНА', + 'on_pushover_message' => "Връзката до '%LABEL%' беше ВЪЗСТАНОВЕНА:

Сървър: %LABEL%
IP адрес: %IP%
Порт: %PORT%
Днес: %DATE%", ), 'login' => array( - 'welcome_usermenu' => 'Welcome, %user_name%', - 'title_sign_in' => 'Please sign in', - 'title_forgot' => 'Forgot your password?', - 'title_reset' => 'Reset your password', - 'submit' => 'Submit', - 'remember_me' => 'Remember me', - 'login' => 'Login', - 'logout' => 'Logout', - 'username' => 'Username', - 'password' => 'Password', - 'password_repeat' => 'Repeat password', - 'password_forgot' => 'Forgot password?', - 'password_reset' => 'Reset password', - 'password_reset_email_subject' => 'Reset your password for PHP Server Monitor', - 'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.

%link%', - 'error_user_incorrect' => 'The provided username could not be found.', - 'error_login_incorrect' => 'The information is incorrect.', - 'error_login_passwords_nomatch' => 'The provided passwords do not match.', - 'error_reset_invalid_link' => 'The reset link you provided is invalid.', - 'success_password_forgot' => 'An email has been sent to you with information how to reset your password.', - 'success_password_reset' => 'Your password has been reset successfully. Please login.', + 'welcome_usermenu' => 'Добре дошъл, %user_name%', + 'title_sign_in' => 'Моля, влезте с профила си', + 'title_forgot' => 'Забравили сте паролата си?', + 'title_reset' => 'Възстановяване на паролата', + 'submit' => 'Вход', + 'remember_me' => 'Искам да остана логнат', + 'login' => 'Вход', + 'logout' => 'Изход', + 'username' => 'Потребител', + 'password' => 'Парола', + 'password_repeat' => 'Повторете паролата', + 'password_forgot' => 'Забравили сте паролата си?', + 'password_reset' => 'Възстановяване на паролата', + 'password_reset_email_subject' => 'Възстановяване на парола за PHP Сървър Мониторинг', + 'password_reset_email_body' => 'За да възстановите паролата си е нужно да кликнете на линка по-долу. Валидността на линка е един час.

%link%', + 'error_user_incorrect' => 'Потребителят не може да бъде намерен.', + 'error_login_incorrect' => 'Информацията е грешна.', + 'error_login_passwords_nomatch' => 'Паролите не съвпадат.', + 'error_reset_invalid_link' => 'Линкът за възстановяване на паролата не е валиден.', + 'success_password_forgot' => 'Изпратен е имейл с информация за възстановяване на паролата.', + 'success_password_reset' => 'Вашата парола е променена успешно. Моля, влезте в системата.', + ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', ), ); diff --git a/src/lang/da_DK.lang.php b/src/lang/da_DK.lang.php new file mode 100644 index 00000000..5dd633b7 --- /dev/null +++ b/src/lang/da_DK.lang.php @@ -0,0 +1,297 @@ +. + * + * @package phpservermon + * @author nerdalertdk + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + **/ + +$sm_lang = array( + 'name' => 'Dansk - Danish', + 'locale' => array('da_DK.UTF-8', 'da_DK', 'danish', 'danish-dk'), + 'system' => array( + 'title' => 'Server Monitor', + 'install' => 'Installere', + 'action' => 'Action', + 'save' => 'Gem', + 'edit' => 'Redigere', + 'delete' => 'Slet', + 'date' => 'Dato', + 'message' => 'Besked', + 'yes' => 'Ja', + 'no' => 'Nej', + 'insert' => 'Indsæt', + 'add_new' => 'Tilføj ny', + 'update_available' => 'En ny version ({version}) er tilgængelig på http://www.phpservermonitor.org.', + 'back_to_top' => 'Til toppen', + 'go_back' => 'Tilbage', + 'ok' => 'OK', + 'cancel' => 'Annuller', + // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php + 'short_day_format' => '%B %e', + 'long_day_format' => '%B %e, %Y', + 'yesterday_format' => 'Igår %k:%M', + 'other_day_format' => '%A %k:%M', + 'never' => 'Aldrig', + 'hours_ago' => '%d timer siden', + 'an_hour_ago' => 'omkring en time siden', + 'minutes_ago' => '%d minutter siden', + 'a_minute_ago' => 'omkring et minut siden', + 'seconds_ago' => '%d sekunder siden', + 'a_second_ago' => 'et sekund siden', + ), + 'menu' => array( + 'config' => 'Indstillinger', + 'server' => 'Servere', + 'server_log' => 'Log', + 'server_status' => 'Status', + 'server_update' => 'Opdatere', + 'user' => 'Brugere', + 'help' => 'Hjælp', + ), + 'users' => array( + 'user' => 'Bruger', + 'name' => 'Navn', + 'user_name' => 'Brugernavn', + 'password' => 'Adgangskode', + 'password_repeat' => 'Adgangskode igen', + 'password_leave_blank' => 'Udfyldes hvis du vil skifte kode', + 'level' => 'Level', + 'level_10' => 'Administrator', + 'level_20' => 'Bruger', + 'level_description' => 'Administratore har fuld adgang: De kan styre servere, brugere og indstillingere.
Brugere kan kun se og køre opdatere for servere som er tildelt til dem.', + 'mobile' => 'Mobil', + 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Slet bruger', + 'delete_message' => 'Er du sikker på du vil slette bruger \'%1\'?', + 'deleted' => 'Bruger slettet.', + 'updated' => 'Bruger opdateret.', + 'inserted' => 'Bruger tilføjet.', + 'profile' => 'Profil', + 'profile_updated' => 'Din profil er opdateret.', + 'error_user_name_bad_length' => 'Brugernavn skal være mellem 2 til 64 tegn.', + 'error_user_name_invalid' => 'Brugernavn må kun indholde alfabetiske tegn (a-z, A-Z), tal (0-9) og (_).', + 'error_user_name_exists' => 'Det valgte brugernavn findes allerede.', + 'error_user_email_bad_length' => 'Email addresser skal være mellem 5 til 255 tegn.', + 'error_user_email_invalid' => 'Den valgte email er ugyldig.', + 'error_user_level_invalid' => 'Det angivet bruger niveau er ugyldig.', + 'error_user_no_match' => 'Brugeren findes ikke.', + 'error_user_password_invalid' => 'Den indtastede adgangskode er ugyldig.', + 'error_user_password_no_match' => 'De to adgangskode er ikke ens.', + ), + 'log' => array( + 'title' => 'Logposter', + 'type' => 'Type', + 'status' => 'Status', + 'email' => 'Email', + 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'Intet i loggen', + ), + 'servers' => array( + 'server' => 'Server', + 'status' => 'Status', + 'label' => 'Label', + 'domain' => 'Domæne/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', + 'port' => 'Port', + 'type' => 'Type', + 'type_website' => 'Hjemmeside', + 'type_service' => 'Tjeneste', + 'pattern' => 'Søge streng/mønster', + 'pattern_description' => 'Hvis dette mønster ikke findes på hjemmesiden, vil serveren blive markeret offline. Regulære udtryk er tilladt.', + 'last_check' => 'Sidst kontrolleret', + 'last_online' => 'Sidst online', + 'monitoring' => 'Overvågning', + 'no_monitoring' => 'Ingen overvågning', + 'email' => 'Email', + 'send_email' => 'Send Email', + 'sms' => 'SMS', + 'send_sms' => 'Send SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Slet server', + 'delete_message' => 'Er du sikker på du vil slette server \'%1\'?', + 'deleted' => 'Server slettet.', + 'updated' => 'Server opdateret.', + 'inserted' => 'Server tilføjet.', + 'latency' => 'Latency', + 'latency_max' => 'Latency (maksimum)', + 'latency_min' => 'Latency (minimum)', + 'latency_avg' => 'Latency (gennemsnitlig)', + 'uptime' => 'Oppetid', + 'year' => 'År', + 'month' => 'Måned', + 'week' => 'Uge', + 'day' => 'Dag', + 'hour' => 'Time', + 'warning_threshold' => 'Advarsel grænse', + 'warning_threshold_description' => 'Antal af fejl før status skifter til offline.', + 'chart_last_week' => 'Sidste uge', + 'chart_history' => 'Historie', + // Charts date format according jqPlot date format http://www.jqplot.com/docs/files/plugins/jqplot-dateAxisRenderer-js.html + 'chart_day_format' => '%d-%m-%Y', + 'chart_long_date_format' => '%d-%m-%Y %H:%M:%S', + 'chart_short_date_format' => '%d/%m %H:%M', + 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', + ), + 'config' => array( + 'general' => 'Generelt', + 'language' => 'Sprog', + 'show_update' => 'Opdateringer', + 'email_status' => 'Tillad at sende mail', + 'email_from_email' => 'Email fra adresse', + 'email_from_name' => 'Email fra navn', + 'email_smtp' => 'Aktiver SMTP', + 'email_smtp_host' => 'SMTP vært', + 'email_smtp_port' => 'SMTP port', + 'email_smtp_username' => 'SMTP brugernavn', + 'email_smtp_password' => 'SMTP adgangskode', + 'email_smtp_noauth' => 'Efterladt blank hvis det ikke er opkrævet', + 'sms_status' => 'Tillad at sende SMS beskeder', + 'sms_gateway' => 'SMS Gateway', + 'sms_gateway_mosms' => 'Mosms', + 'sms_gateway_mollie' => 'Mollie', + 'sms_gateway_spryng' => 'Spryng', + 'sms_gateway_inetworx' => 'Inetworx', + 'sms_gateway_clickatell' => 'Clickatell', + 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', + 'sms_gateway_username' => 'Gateway brugernavn/apikey', + 'sms_gateway_password' => 'Gateway adgangskode', + 'sms_from' => 'Afsenders navn.', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Vælg hvornår du vil modtage besked', + 'alert_type_description' => 'Status ændring: '. + 'Du vil modtage en notifcation når en server har en ændring i status. Fra online -> offline eller offline -> online.
'. + '
Offline: '. + 'Du vil modtage en meddelelse, når en server går offline for * kun første gang *. for eksempel, '. + 'Hvis dit cronjob køre hvert kvatere og din server går ned kl 01 og kommer først op kl 06 '. + ' så vil du kun modtage en mail kl 01.
'. + '
Altid: '. + 'Du vil modtage en besked, hver gang scriptet kører og et websted er nede, selvom site har været offline i flere timer.', + 'alert_type_status' => 'Status ændret', + 'alert_type_offline' => 'Offline', + 'alert_type_always' => 'Altid', + 'log_status' => 'Log status', + 'log_status_description' => 'Hvis log status er sat til TRUE, vil monitoren logge hændelsen hver gang status ændre sig.', + 'log_email' => 'Log mails sendt af systemet', + 'log_sms' => 'Log SMS sendt af systemet', + 'log_pushover' => 'Log pushover messages sent by the script', + 'updated' => 'Indstillingerne er blevet opdateret.', + 'tab_email' => 'Email', + 'tab_sms' => 'SMS', + 'tab_pushover' => 'Pushover', + 'settings_email' => 'Email indstillinger', + 'settings_sms' => 'SMS indstillinger', + 'settings_pushover' => 'Pushover settings', + 'settings_notification' => 'Meddelelse indstillinger', + 'settings_log' => 'Log indstillinger', + 'auto_refresh' => 'Genopfriske automatisk', + 'auto_refresh_servers' => + 'Genopfriske automatisk server sider.
'. + ''. + 'Tid i sekunder, Hvis 0 vil siden ikke genopfriske automatisk'. + '', + 'seconds' => 'sekunder', + 'test' => 'Test', + 'test_email' => 'En email vil blive sendt til den adresse, der er angivet i din brugerprofil.', + 'test_sms' => 'En SMS vil blive sendt til det nummer, der er angivet i din brugerprofil.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test besked', + 'email_sent' => 'Email sendt', + 'email_error' => 'Fejl ved afsendelse af email', + 'sms_sent' => 'Sms sendt', + 'sms_error' => 'Fejl ved afsendelse af SMS', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', + ), + // for newlines in the email messages use
+ 'notifications' => array( + 'off_sms' => 'Server \'%LABEL%\' is DOWN: ip=%IP%, port=%PORT%. Fejl=%ERROR%', + 'off_email_subject' => 'VIGTIG: Server \'%LABEL%\' is DOWN', + 'off_email_body' => "Det lykkedes ikke at oprette forbindelse til følgende server:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Fejl: %ERROR%
Date: %DATE%", + 'off_pushover_title' => 'Server \'%LABEL%\' is DOWN', + 'off_pushover_message' => "Det lykkedes ikke at oprette forbindelse til følgende server:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Fejl: %ERROR%
Date: %DATE%", + 'on_sms' => 'Server \'%LABEL%\' is RUNNING: ip=%IP%, port=%PORT%', + 'on_email_subject' => 'VIGTIG: Server \'%LABEL%\' is RUNNING', + 'on_email_body' => "Server '%LABEL%' køre igen:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Dato: %DATE%", + 'on_pushover_title' => 'Server \'%LABEL%\' is RUNNING', + 'on_pushover_message' => "Server '%LABEL%' køre igen:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Dato: %DATE%", + ), + 'login' => array( + 'welcome_usermenu' => 'Velkommen, %user_name%', + 'title_sign_in' => 'Log ind', + 'title_forgot' => 'Glemt adgangskode?', + 'title_reset' => 'Nulstil din adgangskode', + 'submit' => 'Indsend', + 'remember_me' => 'Husk kode', + 'login' => 'Log ind', + 'logout' => 'Log ud', + 'username' => 'Brugernavn', + 'password' => 'Adgangskode', + 'password_repeat' => 'Skriv adgangskode igen', + 'password_forgot' => 'Glemt adgangskode?', + 'password_reset' => 'Nulstil adgangskode', + 'password_reset_email_subject' => 'Nulstil din adgangskode for PHP Server Monitor', + 'password_reset_email_body' => 'Brug venligst følgende link for at nulstille din adgangskode. Bemærk det udløber på 1 time.

%link%', + 'error_user_incorrect' => 'Det angivet brugernavn kunne ikke findes.', + 'error_login_incorrect' => 'Oplysningerne stemmer ikke overens.', + 'error_login_passwords_nomatch' => 'De angivet adgangskoder matcher ikke.', + 'error_reset_invalid_link' => 'Følgende link er ugyldigt.', + 'success_password_forgot' => 'En e-mail er blevet sendt til dig med oplysninger om, hvordan du nulstiller din adgangskode.', + 'success_password_reset' => 'Dit password er blevet nulstillet. venligst log ind.', + ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), +); diff --git a/src/lang/de_DE.lang.php b/src/lang/de_DE.lang.php index eb7e7599..38bc37c9 100644 --- a/src/lang/de_DE.lang.php +++ b/src/lang/de_DE.lang.php @@ -35,17 +35,17 @@ $sm_lang = array( 'save' => 'Speichern', 'edit' => 'Bearbeiten', 'delete' => 'Löschen', - 'deleted' => 'Eintrag wurde gelöscht', 'date' => 'Datum', 'message' => 'Meldung', 'yes' => 'Ja', 'no' => 'Nein', - 'edit' => 'Bearbeiten', 'insert' => 'Einfügen', 'add_new' => 'Neuen Eintrag erstellen', 'update_available' => 'Ein neues Update ({version}) ist verfügbar auf http://www.phpservermonitor.org.', 'back_to_top' => 'Back to top', 'go_back' => 'Go back', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => 'Administrators have full access: they can manage servers, users and edit the global configuration.
Users can only view and run the updater for the servers that have been assigned to them.', 'mobile' => 'Mobil', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Delete User', + 'delete_message' => 'Are you sure you want to delete user \'%1\'?', + 'deleted' => 'User deleted.', 'updated' => 'Benutzer bearbeitet.', 'inserted' => 'Benutzer eingetragen.', 'profile' => 'Profile', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => 'Status', 'email' => 'Email', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => 'Server', + 'status' => 'Status', 'label' => 'Beschriftung', 'domain' => 'Domain/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Port', 'type' => 'Type', 'type_website' => 'Website', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => 'Letzter Check', 'last_online' => 'Letztes mal Online', 'monitoring' => 'Monitoring', + 'no_monitoring' => 'No monitoring', + 'email' => 'Email', 'send_email' => 'Email', + 'sms' => 'SMS', 'send_sms' => 'SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Delete Server', + 'delete_message' => 'Are you sure you want to delete server \'%1\'?', + 'deleted' => 'Server deleted.', 'updated' => 'Server aktualisiert.', 'inserted' => 'Server eingetragen.', 'latency' => 'Antwortzeit', 'latency_max' => 'Latency (maximum)', 'latency_min' => 'Latency (minimum)', 'latency_avg' => 'Latency (average)', + 'uptime' => 'Uptime', 'year' => 'Year', 'month' => 'Month', 'week' => 'Week', @@ -137,6 +159,16 @@ $sm_lang = array( 'chart_long_date_format' => '%d.%m.%Y %H:%M:%S', 'chart_short_date_format' => '%d.%m %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => 'General', @@ -159,10 +191,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'Gateway Benutzername', 'sms_gateway_password' => 'Gateway Passwort', 'sms_from' => 'SMS Sendernummer', - 'alert_type' => 'Wann möchten Sie benachrichtig werden?
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Wann möchten Sie benachrichtig werden?', 'alert_type_description' => 'Status geändert: '. '... wenn sich der Status ändert
'. 'z.B. online -> offline oder offline -> online.
'. @@ -170,8 +209,7 @@ $sm_lang = array( 'Sie bekommen eine Benachrichtigung, wenn ein Server Offline ist.
'. 'Es wird nur eine Mitteilung versendet.
'. '
Immer: '. - 'Sie werden jedesmal wenn der CronJob oder das Script ausgeführt wird benachrichtigt,
'. - 'auch wenn der Dienst mehreres Stunden offline ist', + 'Sie werden jedesmal wenn der CronJob oder das Script ausgeführt wird benachrichtigt auch wenn der Dienst mehreres Stunden offline ist', 'alert_type_status' => 'Status geändert', 'alert_type_offline' => 'Offline', 'alert_type_always' => 'Immer', @@ -179,12 +217,14 @@ $sm_lang = array( 'log_status_description' => 'Ist der Log Status auf TRUE (ein Hacken) gesetzt, wird jeder Status protokolliert.', 'log_email' => 'Email Log per Script senden?', 'log_sms' => 'SMS Log per Script senden?', + 'log_pushover' => 'Pushover Log per Script senden?', 'updated' => 'Die Einstellungen wurden gespeichert.', 'tab_email' => 'Email', 'tab_sms' => 'SMS', - 'tab_log' => 'Log', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Email', 'settings_sms' => 'SMS Nachricht', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => 'Benachrichtigung', 'settings_log' => 'Log', 'auto_refresh' => 'Auto-refresh', @@ -194,15 +234,38 @@ $sm_lang = array( 'Time in seconds, if 0 the page won\'t refresh.'. '', 'seconds' => 'seconds', + 'test' => 'Test', + 'test_email' => 'An email will be sent to the address specified in your user profile.', + 'test_sms' => 'An SMS will be sent to the phone number specified in your user profile.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test message', + 'email_sent' => 'Email sent', + 'email_error' => 'Error in email sending', + 'sms_sent' => 'Sms sent', + 'sms_error' => 'Error in sms sending', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => 'Server \'%LABEL%\' ist Offline: ip=%IP%, port=%PORT%. Fehler=%ERROR%', 'off_email_subject' => 'Wichtig: Server \'%LABEL%\' ist Offline', 'off_email_body' => "Kann keine Verbindung zum Server aufbauen:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Fehler: %ERROR%
Datum: %DATE%", + 'off_pushover_title' => 'Server \'%LABEL%\' ist Offline', + 'off_pushover_message' => "Kann keine Verbindung zum Server aufbauen:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Fehler: %ERROR%
Datum: %DATE%", 'on_sms' => 'Server \'%LABEL%\' ist wieder Online: ip=%IP%, port=%PORT%', 'on_email_subject' => 'Wichtig: Server \'%LABEL%\' ist wieder Online', 'on_email_body' => "Server '%LABEL%' läuft wieder:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Datum: %DATE%", + 'on_pushover_title' => 'Server \'%LABEL%\' ist wieder Online', + 'on_pushover_message' => "Server '%LABEL%' läuft wieder:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Datum: %DATE%", ), 'login' => array( 'welcome_usermenu' => 'Welcome, %user_name%', @@ -227,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => 'An email has been sent to you with information how to reset your password.', 'success_password_reset' => 'Your password has been reset successfully. Please login.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/lang/en_US.lang.php b/src/lang/en_US.lang.php index 36ae7d25..9c47eca6 100644 --- a/src/lang/en_US.lang.php +++ b/src/lang/en_US.lang.php @@ -35,17 +35,17 @@ $sm_lang = array( 'save' => 'Save', 'edit' => 'Edit', 'delete' => 'Delete', - 'deleted' => 'Record has been deleted', 'date' => 'Date', 'message' => 'Message', 'yes' => 'Yes', 'no' => 'No', - 'edit' => 'Edit', 'insert' => 'Insert', 'add_new' => 'Add new', 'update_available' => 'A new version ({version}) is available from http://www.phpservermonitor.org.', 'back_to_top' => 'Back to top', 'go_back' => 'Go back', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', @@ -69,7 +69,7 @@ $sm_lang = array( 'help' => 'Help', ), 'users' => array( - 'user' => 'user', + 'user' => 'User', 'name' => 'Name', 'user_name' => 'Username', 'password' => 'Password', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => 'Administrators have full access: they can manage servers, users and edit the global configuration.
Users can only view and run the updater for the servers that have been assigned to them.', 'mobile' => 'Mobile', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Delete User', + 'delete_message' => 'Are you sure you want to delete user \'%1\'?', + 'deleted' => 'User deleted.', 'updated' => 'User updated.', 'inserted' => 'User added.', 'profile' => 'Profile', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => 'Status', 'email' => 'Email', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => 'Server', + 'status' => 'Status', 'label' => 'Label', 'domain' => 'Domain/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Port', 'type' => 'Type', 'type_website' => 'Website', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => 'Last check', 'last_online' => 'Last online', 'monitoring' => 'Monitoring', + 'no_monitoring' => 'No monitoring', + 'email' => 'Email', 'send_email' => 'Send Email', + 'sms' => 'SMS', 'send_sms' => 'Send SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Delete server', + 'delete_message' => 'Are you sure you want to delete server \'%1\'?', + 'deleted' => 'Server deleted.', 'updated' => 'Server updated.', 'inserted' => 'Server added.', 'latency' => 'Latency', 'latency_max' => 'Latency (maximum)', 'latency_min' => 'Latency (minimum)', 'latency_avg' => 'Latency (average)', + 'uptime' => 'Uptime', 'year' => 'Year', 'month' => 'Month', 'week' => 'Week', @@ -137,11 +159,21 @@ $sm_lang = array( 'chart_long_date_format' => '%Y-%m-%d %H:%M:%S', 'chart_short_date_format' => '%m/%d %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => 'General', 'language' => 'Language', - 'show_update' => 'Updates', + 'show_update' => 'Check for updates?', 'email_status' => 'Allow sending email', 'email_from_email' => 'Email from address', 'email_from_name' => 'Email from name', @@ -159,10 +191,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'Gateway username', 'sms_gateway_password' => 'Gateway password', 'sms_from' => 'Sender\'s phone number', - 'alert_type' => 'Select when you\'d like to be notified.
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Select when you\'d like to be notified.', 'alert_type_description' => 'Status change: '. 'You will receive a notifcation when a server has a change in status. So from online -> offline or offline -> online.
'. '
Offline: '. @@ -170,8 +209,7 @@ $sm_lang = array( 'your cronjob is every 15 mins and your server goes down at 1 am and stays down till 6 am. '. 'You will get 1 notification at 1 am and thats it.
'. '
Always: '. - 'You will receive a notification every time the script runs and a site is down, even if the site has been '. - 'offline for hours.', + 'You will receive a notification every time the script runs and a site is down, even if the site has been offline for hours.', 'alert_type_status' => 'Status change', 'alert_type_offline' => 'Offline', 'alert_type_always' => 'Always', @@ -179,12 +217,14 @@ $sm_lang = array( 'log_status_description' => 'If log status is set to TRUE, the monitor will log the event whenever the Notification settings are passed.', 'log_email' => 'Log emails sent by the script', 'log_sms' => 'Log text messages sent by the script', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => 'The configuration has been updated.', 'tab_email' => 'Email', 'tab_sms' => 'SMS', - 'tab_log' => 'Log', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Email settings', 'settings_sms' => 'Text message settings', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => 'Notification settings', 'settings_log' => 'Log settings', 'auto_refresh' => 'Auto-refresh', @@ -194,15 +234,38 @@ $sm_lang = array( 'Time in seconds, if 0 the page won\'t refresh.'. '', 'seconds' => 'seconds', + 'test' => 'Test', + 'test_email' => 'An email will be sent to the address specified in your user profile.', + 'test_sms' => 'An SMS will be sent to the phone number specified in your user profile.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test message', + 'email_sent' => 'Email sent', + 'email_error' => 'Error in email sending', + 'sms_sent' => 'Sms sent', + 'sms_error' => 'Error in sms sending', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => 'Server \'%LABEL%\' is DOWN: ip=%IP%, port=%PORT%. Error=%ERROR%', 'off_email_subject' => 'IMPORTANT: Server \'%LABEL%\' is DOWN', 'off_email_body' => "Failed to connect to the following server:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Error: %ERROR%
Date: %DATE%", + 'off_pushover_title' => 'Server \'%LABEL%\' is DOWN', + 'off_pushover_message' => "Failed to connect to the following server:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Error: %ERROR%
Date: %DATE%", 'on_sms' => 'Server \'%LABEL%\' is RUNNING: ip=%IP%, port=%PORT%', 'on_email_subject' => 'IMPORTANT: Server \'%LABEL%\' is RUNNING', 'on_email_body' => "Server '%LABEL%' is running again:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Date: %DATE%", + 'on_pushover_title' => 'Server \'%LABEL%\' is RUNNING', + 'on_pushover_message' => 'Server \'%LABEL%\' is running again:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Date: %DATE%', ), 'login' => array( 'welcome_usermenu' => 'Welcome, %user_name%', @@ -227,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => 'An email has been sent to you with information how to reset your password.', 'success_password_reset' => 'Your password has been reset successfully. Please login.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/lang/es_ES.lang.php b/src/lang/es_ES.lang.php index 65b3c9aa..095f3e4b 100644 --- a/src/lang/es_ES.lang.php +++ b/src/lang/es_ES.lang.php @@ -35,17 +35,17 @@ $sm_lang = array( 'save' => 'Guardar', 'edit' => 'Modificar', 'delete' => 'Eliminar', - 'deleted' => 'Registro eliminado', 'date' => 'Fecha', 'message' => 'Mensaje', 'yes' => 'Si', 'no' => 'No', - 'edit' => 'Modificar', 'insert' => 'Insertar', 'add_new' => 'Agregar nuevo', 'update_available' => 'Hay una nueva versión ({version}) disponible en http://www.phpservermonitor.org.', 'back_to_top' => 'Back to top', 'go_back' => 'Go back', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => 'Administrators have full access: they can manage servers, users and edit the global configuration.
Users can only view and run the updater for the servers that have been assigned to them.', 'mobile' => 'Mobil', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Delete User', + 'delete_message' => 'Are you sure you want to delete user \'%1\'?', + 'deleted' => 'User deleted.', 'updated' => 'Usuario actualizado.', 'inserted' => 'Usuario ingresado.', 'profile' => 'Profile', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => 'Estado', 'email' => 'Email', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => 'Servidores', + 'status' => 'Status', 'label' => 'Titulo', 'domain' => 'Domain/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Port', 'type' => 'Tipo', 'type_website' => 'Website', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => 'Ultima verificación', 'last_online' => 'Última vez en línea', 'monitoring' => 'Monitoreo', + 'no_monitoring' => 'No monitoring', + 'email' => 'Email', 'send_email' => 'Email', + 'sms' => 'SMS', 'send_sms' => 'SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Delete Server', + 'delete_message' => 'Are you sure you want to delete server \'%1\'?', + 'deleted' => 'Server deleted.', 'updated' => 'Servidor arctualizado.', 'inserted' => 'Servidor ingresado.', 'latency' => 'Tiempo de respuesta', 'latency_max' => 'Tiempo de respuesta (maximum)', 'latency_min' => 'Tiempo de respuesta (minimum)', 'latency_avg' => 'Tiempo de respuesta (average)', + 'uptime' => 'Uptime', 'year' => 'Year', 'month' => 'Month', 'week' => 'Week', @@ -137,6 +159,16 @@ $sm_lang = array( 'chart_long_date_format' => '%Y-%m-%d %H:%M:%S', 'chart_short_date_format' => '%m/%d %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => 'General', @@ -159,10 +191,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'Gateway username', 'sms_gateway_password' => 'Gateway password', 'sms_from' => 'Número origen del SMS', - 'alert_type' => 'Cuando desea recibir notificaciones ?
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Cuando desea recibir notificaciones ?', 'alert_type_description' => '... Al cambiar el estado: '. 'p.ej. online -> offline o offline -> online.
'. '
Offline: '. @@ -178,12 +217,14 @@ $sm_lang = array( 'log_status_description' => 'Al setear TRUE (marcar) se protocolan todos los estados.', 'log_email' => 'Enviar Log via email?', 'log_sms' => 'Enviar Log via SMS ?', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => 'Configuración guardada.', 'tab_email' => 'Email', 'tab_sms' => 'SMS', - 'tab_log' => 'Log', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Email', 'settings_sms' => 'Mensaje SMS', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => 'Notificación', 'settings_log' => 'Log', 'auto_refresh' => 'Refrescar automáticamente página de servidores', @@ -193,15 +234,38 @@ $sm_lang = array( 'Tiempo en segundos, indicar "0" para no actualizar.'. '', 'seconds' => 'seconds', + 'test' => 'Test', + 'test_email' => 'An email will be sent to the address specified in your user profile.', + 'test_sms' => 'An SMS will be sent to the phone number specified in your user profile.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test message', + 'email_sent' => 'Email sent', + 'email_error' => 'Error in email sending', + 'sms_sent' => 'Sms sent', + 'sms_error' => 'Error in sms sending', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => 'Servidor \'%LABEL%\' está fuera de línea: ip=%IP%, port=%PORT%. error=%ERROR%', 'off_email_subject' => 'Importante: Servidor \'%LABEL%\' está fuera de línea', 'off_email_body' => "No posible conectar al servidor:

Servidor: %LABEL%
IP: %IP%
Port: %PORT%
Error: %ERROR%
Fecha: %DATE%", + 'off_pushover_title' => 'Servidor \'%LABEL%\' está fuera de línea', + 'off_pushover_message' => "No posible conectar al servidor:

Servidor: %LABEL%
IP: %IP%
Port: %PORT%
Error: %ERROR%
Fecha: %DATE%", 'on_sms' => 'Servidor \'%LABEL%\' ya está de nuevo funcionando en línea: ip=%IP%, port=%PORT%', 'on_email_subject' => 'Importante: Servidor \'%LABEL%\' ya está de nuevo en línea', 'on_email_body' => "Servidor '%LABEL%' ya está funcionando en línea de nuevo:

Servidor: %LABEL%
IP: %IP%
Port: %PORT%
Fecha: %DATE%", + 'on_pushover_title' => 'Servidor \'%LABEL%\' ya está de nuevo en línea', + 'on_pushover_message' => "Servidor '%LABEL%' ya está funcionando en línea de nuevo:

Servidor: %LABEL%
IP: %IP%
Port: %PORT%
Fecha: %DATE%", ), 'login' => array( 'welcome_usermenu' => 'Welcome, %user_name%', @@ -226,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => 'An email has been sent to you with information how to reset your password.', 'success_password_reset' => 'Your password has been reset successfully. Please login.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/lang/fr_FR.lang.php b/src/lang/fr_FR.lang.php index 719c5e19..cab685a7 100644 --- a/src/lang/fr_FR.lang.php +++ b/src/lang/fr_FR.lang.php @@ -19,6 +19,7 @@ * * @package phpservermon * @author David Ribeiro + * @author Jérôme Cabanis * @copyright Copyright (c) 2008-2014 Pepijn Over * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 * @version Release: @package_version@ @@ -35,17 +36,17 @@ $sm_lang = array( 'save' => 'Enregistrer', 'edit' => 'Editer', 'delete' => 'Supprimer', - 'deleted' => 'L\'enregistrement a été supprimé', 'date' => 'Date', 'message' => 'Message', 'yes' => 'Oui', 'no' => 'Non', - 'edit' => 'Editer', 'insert' => 'Nouveau', 'add_new' => 'Nouveau', 'update_available' => 'Une nouvelle version ({version}) est disponible à l\'adresse http://www.phpservermonitor.org.', 'back_to_top' => 'Haut de page', 'go_back' => 'Retour', + 'ok' => 'OK', + 'cancel' => 'Annuler', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => 'Le %e %B', 'long_day_format' => 'Le %e %B %Y', @@ -79,8 +80,16 @@ $sm_lang = array( 'level_10' => 'Administrateur', 'level_20' => 'Utilisateur', 'level_description' => 'Les Administrateurs ont un accès total. Ils peuvent gérer les serveurs, les utilisateurs et éditer la configuration globale.
Les Utilisateurs ne peuvent que voir et mettre à jour les serveurs qui leur ont été assignés.', - 'mobile' => 'Numéro de téléphone', + 'mobile' => 'Téléphone', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Supprimer un utilisateur', + 'delete_message' => 'Êtes-vous sûr de vouloir supprimer l\'utilisateur \'%1\' ?', + 'deleted' => 'Utilisateur supprimé.', 'updated' => 'Utilisateur mis à jour.', 'inserted' => 'Utilisateur ajouté.', 'profile' => 'Profil', @@ -101,11 +110,16 @@ $sm_lang = array( 'status' => 'État', 'email' => 'email', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'Aucun événement', ), 'servers' => array( 'server' => 'Serveur', - 'label' => 'Description', + 'status' => 'État', + 'label' => 'Nom', 'domain' => 'Domaine/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Port', 'type' => 'Type', 'type_website' => 'Site Web', @@ -115,14 +129,23 @@ $sm_lang = array( 'last_check' => 'Dernière vérification', 'last_online' => 'Dernière fois OK', 'monitoring' => 'Serveillé', + 'no_monitoring' => 'Non serveillé', + 'email' => 'Email', 'send_email' => 'Envoyer un email', + 'sms' => 'SMS', 'send_sms' => 'Envoyer un SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Supprimer un serveur', + 'delete_message' => 'Êtes-vous sûr de vouloir supprimer le serveur \'%1\' ?', + 'deleted' => 'Serveur supprimé.', 'updated' => 'Serveur mis à jour.', 'inserted' => 'Serveur ajouté.', 'latency' => 'Temps de réponse', 'latency_max' => 'Temps de réponse maximum', 'latency_min' => 'Temps de réponse minimum', 'latency_avg' => 'Temps de réponse moyen', + 'uptime' => 'Disponibilité', 'year' => 'Année', 'month' => 'Mois', 'week' => 'Semaine', @@ -137,6 +160,16 @@ $sm_lang = array( 'chart_long_date_format' => '%d/%m/%Y %H:%M:%S', 'chart_short_date_format' => '%d/%m %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Serveur non trouvé.', + 'error_server_label_bad_length' => 'Le nom doit avoir entre 1 et 255 caractères.', + 'error_server_ip_bad_length' => 'Domaine/IP doit avoir entre 1 et 255 caractères.', + 'error_server_ip_bad_service' => 'L\'adresse IP n\'est pas valide.', + 'error_server_ip_bad_website' => 'L\'URL du site web n\'est pas valide.', + 'error_server_type_invalid' => 'Le type de service sélectionné n\'est pas valide.', + 'error_server_warning_threshold_invalid' => 'Le seuil d\'alerte doit être un nombre entier supérieur à 0.', ), 'config' => array( 'general' => 'Général', @@ -159,10 +192,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'Nom utilisateur de la passerelle', 'sms_gateway_password' => 'Mot de passe de la passerelle', 'sms_from' => 'SMS de l\'expéditeur', - 'alert_type' => 'Choisissez quand vous souhaitez être notifié.
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Choisissez quand vous souhaitez être notifié', 'alert_type_description' => 'Changement d\'état : '. 'Vous recevez une notification chaque fois que le serveur change d\'état. C\'est-à-dire passe de l\'état OK à HORS SERVICE ou de HORS SERVICE à OK.
'. '
Hors service : '. @@ -178,12 +218,14 @@ $sm_lang = array( 'log_status_description' => 'Si l\'option est activée, un événement est enregistré chaque fois qu\'une notification a lieu.', 'log_email' => 'Enregistrer tout les emails envoyés', 'log_sms' => 'Enregistrer tout les SMS envoyés', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => 'La configuration a été mise à jour.', 'tab_email' => 'Email', 'tab_sms' => 'SMS', - 'tab_log' => 'Événements', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Configuration email', 'settings_sms' => 'Configuration SMS', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => 'Configuration des notifications', 'settings_log' => 'Configuration des événements', 'auto_refresh' => 'Auto-rachaîchissement', @@ -193,15 +235,38 @@ $sm_lang = array( 'Temps en secondes. Si 0, la page n\'est pas rafraîchie.'. '', 'seconds' => 'secondes', + 'test' => 'Tester', + 'test_email' => 'Un email va vous être envoyé à l\'adresse définie dans votre profil utilisateur.', + 'test_sms' => 'Un SMS va vous être envoyé au numéro défini dans votre profil utilisateur.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Envoyer', + 'test_subject' => 'Test', + 'test_message' => 'Message de test', + 'email_sent' => 'Email envoyé', + 'email_error' => 'Erreur lors de l\'envoie de l\'email', + 'sms_sent' => 'Sms envoyé', + 'sms_error' => 'Erreur lors de l\'envoie du sms', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => 'Le Serveur \'%LABEL%\' est HORS SERVICE: IP=%IP%, Port=%PORT%. Erreur=%ERROR%', 'off_email_subject' => 'IMPORTANT: Le Serveur \'%LABEL%\' est HORS SERVICE', 'off_email_body' => "Impossible de se connecter au serveur suivant:

Serveur: %LABEL%
IP: %IP%
Port: %PORT%
Erreur: %ERROR%
Date: %DATE%", + 'off_pushover_title' => 'Le Serveur \'%LABEL%\' est HORS SERVICE', + 'off_pushover_message' => "Impossible de se connecter au serveur suivant:

Serveur: %LABEL%
IP: %IP%
Port: %PORT%
Erreur: %ERROR%
Date: %DATE%", 'on_sms' => 'Le Serveur \'%LABEL%\' est OK: IP=%IP%, Port=%PORT%', 'on_email_subject' => 'IMPORTANT: Le Serveur \'%LABEL%\' est OK', 'on_email_body' => "Le Serveur '%LABEL%' est de nouveau OK:

Serveur: %LABEL%
IP: %IP%
Port: %PORT%
Date: %DATE%", + 'on_pushover_title' => 'Le Serveur \'%LABEL%\' est OK', + 'on_pushover_message' => "Le Serveur '%LABEL%' est de nouveau OK:

Serveur: %LABEL%
IP: %IP%
Port: %PORT%
Date: %DATE%", ), 'login' => array( 'welcome_usermenu' => 'Bonjour %user_name%', @@ -226,4 +291,8 @@ $sm_lang = array( 'success_password_forgot' => 'Un email vous a été envoyé pour réinitialiser votre mot de passe.', 'success_password_reset' => 'Votre mot de passe a été réinitialisé.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/lang/it_IT.lang.php b/src/lang/it_IT.lang.php index 52f12219..86da1063 100644 --- a/src/lang/it_IT.lang.php +++ b/src/lang/it_IT.lang.php @@ -35,17 +35,17 @@ $sm_lang = array( 'save' => 'Salva', 'edit' => 'Modifica', 'delete' => 'Elimina', - 'deleted' => 'L\'oggetto è stato eliminato', 'date' => 'Data', 'message' => 'Messaggio', 'yes' => 'Sì', 'no' => 'No', - 'edit' => 'Modifica', 'insert' => 'Inserisci', 'add_new' => 'Aggiungi Nuovo?', 'update_available' => 'Un nuovo aggiornamento ({version}) è disponibile su http://www.phpservermonitor.org.', 'back_to_top' => 'Back to top', 'go_back' => 'Go back', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', @@ -69,7 +69,7 @@ $sm_lang = array( 'help' => 'Aiuto', ), 'users' => array( - 'user' => 'utente', + 'user' => 'Utente', 'name' => 'Nome', 'user_name' => 'Username', 'password' => 'Password', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => 'Administrators have full access: they can manage servers, users and edit the global configuration.
Users can only view and run the updater for the servers that have been assigned to them.', 'mobile' => 'Cellulare', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Delete User', + 'delete_message' => 'Are you sure you want to delete user \'%1\'?', + 'deleted' => 'User deleted.', 'updated' => 'Utente aggiornato.', 'inserted' => 'Utente aggiunto.', 'profile' => 'Profile', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => 'Stato', 'email' => 'Email', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => 'Server', + 'status' => 'Status', 'label' => 'Nome', 'domain' => 'Dominio/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Porta', 'type' => 'Tipo', 'type_website' => 'Website', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => 'Ultimo Controllo', 'last_online' => 'Ultima volta Online', 'monitoring' => 'Sotto Controllo', + 'no_monitoring' => 'No monitoring', + 'email' => 'Email', 'send_email' => 'Invia Email', + 'sms' => 'SMS', 'send_sms' => 'Invia SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Delete Server', + 'delete_message' => 'Are you sure you want to delete server \'%1\'?', + 'deleted' => 'Server deleted.', 'updated' => 'Server aggiornato.', 'inserted' => 'Server aggiunto.', 'latency' => 'Tempo di risposta', 'latency_max' => 'Tempo di risposta (maximum)', 'latency_min' => 'Tempo di risposta (minimum)', 'latency_avg' => 'Tempo di risposta (average)', + 'uptime' => 'Uptime', 'year' => 'Year', 'month' => 'Month', 'week' => 'Week', @@ -137,6 +159,16 @@ $sm_lang = array( 'chart_long_date_format' => '%Y-%m-%d %H:%M:%S', 'chart_short_date_format' => '%m/%d %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => 'Generale', @@ -159,10 +191,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'Nome Utente Gateway', 'sms_gateway_password' => 'Password Gateway', 'sms_from' => 'Numero di telefono del mittente', - 'alert_type' => 'Seleziona quando vuoi essere notificato.
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Seleziona quando vuoi essere notificato', 'alert_type_description' => 'Cambio di Stato: '. 'Riceverai una notifica solo quando un server cambierà stato. Quindi da online -> offline oppure da offline -> online.
'. '
Offline: '. @@ -178,12 +217,14 @@ $sm_lang = array( 'log_status_description' => 'Se lo Stato Log è impostato su VERO, il monitor registrerà nel log gli eventi appena le notifiche verranno inviate.', 'log_email' => 'Registra email inviate dallo script.', 'log_sms' => 'Registra SMS inviati dallo script.', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => 'La configurazione è stato aggiornata.', 'tab_email' => 'Email', 'tab_sms' => 'SMS', - 'tab_log' => 'Log', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Impostazioni Email', 'settings_sms' => 'Impostazioni SMS', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => 'Impostazioni Notifiche', 'settings_log' => 'Impostazioni Log', 'auto_refresh' => 'Auto-Aggiorna pagina servers', @@ -193,15 +234,38 @@ $sm_lang = array( 'Tempo in secondi, se impostato a 0 la pagina non si aggiornerà.'. '', 'seconds' => 'seconds', + 'test' => 'Test', + 'test_email' => 'An email will be sent to the address specified in your user profile.', + 'test_sms' => 'An SMS will be sent to the phone number specified in your user profile.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test message', + 'email_sent' => 'Email sent', + 'email_error' => 'Error in email sending', + 'sms_sent' => 'Sms sent', + 'sms_error' => 'Error in sms sending', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => 'Server \'%LABEL%\' è DOWN: ip=%IP%, porta=%PORT%. Errore=%ERROR%', 'off_email_subject' => 'IMPORTANTE: Server \'%LABEL%\' è DOWN', 'off_email_body' => "Impossibile connettersi al seguente server:

Server: %LABEL%
IP: %IP%
Porta: %PORT%
Errore: %ERROR%
Data: %DATE%", + 'off_pushover_title' => 'Server \'%LABEL%\' è DOWN', + 'off_pushover_message' => "Impossibile connettersi al seguente server:

Server: %LABEL%
IP: %IP%
Porta: %PORT%
Errore: %ERROR%
Data: %DATE%", 'on_sms' => 'Server \'%LABEL%\' è ATTIVO: ip=%IP%, porta=%PORT%', 'on_email_subject' => 'IMPORTANTE: Server \'%LABEL%\' è ATTIVO', 'on_email_body' => "Server '%LABEL%' è di nuovo attivo:

Server: %LABEL%
IP: %IP%
Porta: %PORT%
Data: %DATE%", + 'on_pushover_title' => 'Server \'%LABEL%\' è ATTIVO', + 'on_pushover_message' => "Server '%LABEL%' è di nuovo attivo:

Server: %LABEL%
IP: %IP%
Porta: %PORT%
Data: %DATE%", ), 'login' => array( 'welcome_usermenu' => 'Welcome, %user_name%', @@ -226,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => 'An email has been sent to you with information how to reset your password.', 'success_password_reset' => 'Your password has been reset successfully. Please login.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/lang/ko_KR.lang.php b/src/lang/ko_KR.lang.php index e56223bf..cda96b74 100644 --- a/src/lang/ko_KR.lang.php +++ b/src/lang/ko_KR.lang.php @@ -35,17 +35,17 @@ $sm_lang = array( 'save' => '저장', 'edit' => '수정', 'delete' => '삭제', - 'deleted' => '삭제되었습니다.', 'date' => '날짜', 'message' => '메세지', 'yes' => '예', 'no' => '아니오', - 'edit' => '수정', 'insert' => '삽입', 'add_new' => '새계정 추가', 'update_available' => '새로운 업데이트가 있습니다 ({version}). 다음사이트를 방문 해 주십시오. http://www.phpservermonitor.org.', 'back_to_top' => 'Back to top', 'go_back' => 'Go back', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => 'Administrators have full access: they can manage servers, users and edit the global configuration.
Users can only view and run the updater for the servers that have been assigned to them.', 'mobile' => '휴대폰', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Delete User', + 'delete_message' => 'Are you sure you want to delete user \'%1\'?', + 'deleted' => 'User deleted.', 'updated' => '수정되었습니다.', 'inserted' => '추가되었습니다.', 'profile' => 'Profile', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => '상태', 'email' => 'email', 'sms' => 'sms', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => '서버', + 'status' => 'Status', 'label' => 'Label', 'domain' => 'Domain/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Port', 'type' => 'Type', 'type_website' => 'Website', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => '최근체크', 'last_online' => '최근접속', 'monitoring' => '확인중', + 'no_monitoring' => 'No monitoring', + 'email' => '메일 전송', 'send_email' => '메일 전송', + 'sms' => 'SMS 전송', 'send_sms' => 'SMS 전송', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Delete Server', + 'delete_message' => 'Are you sure you want to delete server \'%1\'?', + 'deleted' => 'Server deleted.', 'updated' => '서버가 수정되었습니다.', 'inserted' => '서버가 추가되었습니다.', 'latency' => '응답', 'latency_max' => 'Latency (maximum)', 'latency_min' => 'Latency (minimum)', 'latency_avg' => 'Latency (average)', + 'uptime' => 'Uptime', 'year' => 'Year', 'month' => 'Month', 'week' => 'Week', @@ -137,6 +159,16 @@ $sm_lang = array( 'chart_long_date_format' => '%Y-%m-%d %H:%M:%S', 'chart_short_date_format' => '%m/%d %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => '일반', @@ -158,11 +190,18 @@ $sm_lang = array( 'sms_gateway_spryng' => 'Spryng', 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', 'sms_gateway_username' => 'Gateway username', 'sms_gateway_password' => 'Gateway password', 'sms_from' => 'Sender\'s phone number', - 'alert_type' => '알림을 원하면 다음과 같이 변경하십시오..
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => '알림을 원하면 다음과 같이 변경하십시오.', 'alert_type_description' => '상태 변경:
'. '서버 상태가 변경이되면 알림을 받습니다. online -> offline -> online.
'. '
오프라인:
'. @@ -170,6 +209,7 @@ $sm_lang = array( 'cron이 매 15분이고 오전1시 부터 오전6시까지 다운되었을때 오전1시에 한번 알림을 받습니다.
' . '
항상:
'. '사이트가 다운되었을 때 매시간 알림을 받습니다.', + 'alert_type_status' => '상태 변경', 'alert_type_offline' => '오프라인', 'alert_type_always' => '항상', @@ -177,12 +217,14 @@ $sm_lang = array( 'log_status_description' => '로그상태가 TRUE이면 알림설정이 통과할때마다 이벤트를 기록합니다.', 'log_email' => '이메일로 로그를 전송하시겠습니까?', 'log_sms' => 'SMS로 로그를 전송하시겠습니까?', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => '설정이 수정되었습니다.', 'tab_email' => 'Email', 'tab_sms' => 'SMS', - 'tab_log' => '로그', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Email 설정', 'settings_sms' => 'SMS 설정', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => '알림 설정', 'settings_log' => '로그 설정', 'auto_refresh' => 'Auto-refresh', @@ -192,15 +234,38 @@ $sm_lang = array( '시간은 초(sec)로 설정을 하고, 0은 새로고침을 하지 않습니다.'. '', 'seconds' => 'seconds', + 'test' => 'Test', + 'test_email' => 'An email will be sent to the address specified in your user profile.', + 'test_sms' => 'An SMS will be sent to the phone number specified in your user profile.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test message', + 'email_sent' => 'Email sent', + 'email_error' => 'Error in email sending', + 'sms_sent' => 'Sms sent', + 'sms_error' => 'Error in sms sending', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => '서버(\'%LABEL%\')가 다운되었습니다. : ip=%IP%, port=%PORT%. Error=%ERROR%', 'off_email_subject' => '중요: 서버(\'%LABEL%\')가 다운되었습니다.', 'off_email_body' => "서버 접속을 실패하였습니다.

Server: %LABEL%
IP: %IP%
Port: %PORT%
Error: %ERROR%
Date: %DATE%", + 'off_pushover_title' => '서버(\'%LABEL%\')가 다운되었습니다.', + 'off_pushover_message' => "서버 접속을 실패하였습니다.

Server: %LABEL%
IP: %IP%
Port: %PORT%
Error: %ERROR%
Date: %DATE%", 'on_sms' => '서버(\'%LABEL%\') 가동중: ip=%IP%, port=%PORT%', 'on_email_subject' => '중요: 서버(\'%LABEL%\')가 가동중입니다.', 'on_email_body' => "서버('%LABEL%')가 재가동됩니다.:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Date: %DATE%", + 'on_pushover_title' => '서버(\'%LABEL%\')가 가동중입니다.', + 'on_pushover_message' => "서버('%LABEL%')가 재가동됩니다.:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Date: %DATE%", ), 'login' => array( 'welcome_usermenu' => 'Welcome, %user_name%', @@ -225,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => 'An email has been sent to you with information how to reset your password.', 'success_password_reset' => 'Your password has been reset successfully. Please login.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/lang/nl_NL.lang.php b/src/lang/nl_NL.lang.php index 846db258..8a273710 100644 --- a/src/lang/nl_NL.lang.php +++ b/src/lang/nl_NL.lang.php @@ -30,22 +30,22 @@ $sm_lang = array( 'locale' => array('nl_NL.UTF-8', 'nl_NL', 'dutch'), 'system' => array( 'title' => 'Server Monitor', - 'install' => 'Install', + 'install' => 'Intalleren', 'action' => 'Actie', 'save' => 'Opslaan', 'edit' => 'Wijzig', 'delete' => 'Verwijder', - 'deleted' => 'Record is verwijderd', 'date' => 'Datum', 'message' => 'Bericht', 'yes' => 'Ja', 'no' => 'Nee', - 'edit' => 'Wijzig', 'insert' => 'Voeg toe', 'add_new' => 'Voeg toe', 'update_available' => 'Een nieuwe update ({version}) is beschikbaar op http://www.phpservermonitor.org.', 'back_to_top' => 'Terug naar boven', 'go_back' => 'Terug', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', @@ -60,7 +60,7 @@ $sm_lang = array( 'a_second_ago' => 'een seconde geleden', ), 'menu' => array( - 'config' => 'Config', + 'config' => 'Configuratie', 'server' => 'Servers', 'server_log' => 'Log', 'server_status' => 'Status', @@ -69,7 +69,7 @@ $sm_lang = array( 'help' => 'Help', ), 'users' => array( - 'user' => 'gebruiker', + 'user' => 'Gebruiker', 'name' => 'Naam', 'user_name' => 'Gebruikersnaam', 'password' => 'Wachtwoord', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => 'Beheerders hebben volledige toegang: ze kunnen servers en gebruiker beheren en de globale configuratie aanpassen.
Gebruikers kunnen alleen de servers bekijken en op fouten testen die aan hun zijn toegewezen.', 'mobile' => 'Mobiel', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is een dienst die het gemakkelijk maakt om real-time notificaties te ontvangen. Zie hun website voor meer informatie.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Apparaat waar de berichten naar toe gaan. Laat leeg voor alle apparaten.', + 'delete_title' => 'Verwijder gebruiker', + 'delete_message' => 'Weet je zeker dat je deze gebruiker wilt verwijderen: \'%1\'?', + 'deleted' => 'Gebruiker verwijderd.', 'updated' => 'Gebruiker gewijzigd.', 'inserted' => 'Gebruiker toegevoegd.', 'profile' => 'Profiel', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => 'Status', 'email' => 'Email', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => 'Server', + 'status' => 'Status', 'label' => 'Label', 'domain' => 'Domein/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Aantal seconden te wachten op een reactie van de server.', 'port' => 'Poort', 'type' => 'Type', 'type_website' => 'Website', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => 'Laatst gecontroleerd', 'last_online' => 'Laatst online', 'monitoring' => 'Monitoring', + 'no_monitoring' => 'No monitoring', + 'email' => 'Email', 'send_email' => 'Stuur email', + 'sms' => 'SMS', 'send_sms' => 'Stuur SMS', + 'pushover' => 'Pushover', + 'users' => 'Gebruikers', + 'delete_title' => 'Verwijder server', + 'delete_message' => 'Weet je zeker dat je deze server wilt verwijderen: \'%1\'?', + 'deleted' => 'Server verwijderd.', 'updated' => 'Server gewijzigd.', 'inserted' => 'Server toegevoegd.', 'latency' => 'Response tijd', 'latency_max' => 'Latency (maximum)', 'latency_min' => 'Latency (minimum)', 'latency_avg' => 'Latency (gemiddeld)', + 'uptime' => 'Uptime', 'year' => 'Jaar', 'month' => 'Maand', 'week' => 'Week', @@ -137,6 +159,16 @@ $sm_lang = array( 'chart_long_date_format' => '%d-%m-%Y %H:%M:%S', 'chart_short_date_format' => '%d-%m %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notificaties zijn uitgeschakeld.', + 'warning_notifications_disabled_email' => 'Email notificaties zijn uitgeschakeld.', + 'warning_notifications_disabled_pushover' => 'Pushover notificaties zijn uitgeschakeld.', + 'error_server_no_match' => 'Server niet gevonden.', + 'error_server_label_bad_length' => 'Het label moet tussen de 1 en 255 karakters lang zijn.', + 'error_server_ip_bad_length' => 'Het domein / IP moet tussen de 1 en 255 karakters lang zijn.', + 'error_server_ip_bad_service' => 'Het IP adres is ongeldig.', + 'error_server_ip_bad_website' => 'De website URL is ongeldig.', + 'error_server_type_invalid' => 'Het geselecteerde server type is ongeldig.', + 'error_server_warning_threshold_invalid' => 'De warning threshold moet een numerieke waarde zijn groter dan 0.', ), 'config' => array( 'general' => 'Algemeen', @@ -159,10 +191,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'Gateway gebruikersnaam', 'sms_gateway_password' => 'Gateway wachtwoord', 'sms_from' => 'Telefoonnummer afzender', - 'alert_type' => 'Selecteer wanneer je een notificatie wilt.
', + 'pushover_status' => 'Sta Pushover berichten toe?', + 'pushover_description' => 'Pushover is een dienst die het gemakkelijk maakt om real-time notificaties te ontvangen. Zie hun website voor meer informatie.', + 'pushover_clone_app' => 'Klik hier om je Pushover app te maken', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Voordat je Pushover kunt gebruiken moet je een App registreren via hun website, en daarvan de App API Token hier invullen.', + 'alert_type' => 'Selecteer wanneer je een notificatie wilt', 'alert_type_description' => 'Status change: '. 'Je ontvangt alleen bericht wanneer een server van status verandert. Dus van online -> offline of offline -> online.
'. '
Offline: '. @@ -178,12 +217,14 @@ $sm_lang = array( 'log_status_description' => 'Als de log status aan staat, zal de monitor een log aanmaken elke keer dat hij door de notificatie instellingen komt.', 'log_email' => 'Log emails verstuurd bij het script?', 'log_sms' => 'Log sms berichten verstuurd bij het script?', + 'log_pushover' => 'Log Pushover berichten verstuurd bij het script?', 'updated' => 'De configuratie is gewijzigd.', 'tab_email' => 'Email', 'tab_sms' => 'SMS', - 'tab_log' => 'Log', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Email instellingen', 'settings_sms' => 'SMS instellingen', + 'settings_pushover' => 'Pushover instellingen', 'settings_notification' => 'Notificatie instellingen', 'settings_log' => 'Log instellingen', 'auto_refresh' => 'Auto-refresh', @@ -192,16 +233,39 @@ $sm_lang = array( ''. 'Tijd in seconden, als de tijd 0 is wordt de pagina niet ververst.'. '', - 'seconds' => 'seconds', + 'seconds' => 'seconden', + 'test' => 'Test', + 'test_email' => 'Er zal een email verstuurd worden naar het email adres in je profiel.', + 'test_sms' => 'Er zal een SMS verstuurd worden naar het telefoonnummer in je profiel.', + 'test_pushover' => 'Er zal een Pushover notificatie verstuurd worden naar de user key/device in je profiel.', + 'send' => 'Verstuur', + 'test_subject' => 'Test', + 'test_message' => 'Test bericht', + 'email_sent' => 'Email verzonden', + 'email_error' => 'Er is een fout opgetreden tijdens het verzenden', + 'sms_sent' => 'SMS verzonden', + 'sms_error' => 'Er is een fout opgetreden tijdens het verzenden', + 'sms_error_nomobile' => 'Kan test SMS niet verzenden: er is geen telefoonnummer ingevuld in je profiel.', + 'pushover_sent' => 'Pushover notificatie verzonden', + 'pushover_error' => 'De volgende fout is opgetreden bij het versturen van de Pushover notificatie: %s', + 'pushover_error_noapp' => 'Kan test notificatie niet verzenden: er is geen Pushover App API token gevonden in de algemene configuratie.', + 'pushover_error_nokey' => 'Kan test notificatie niet verzenden: er is geen Pushover key gevonden in je profiel.', + 'log_retention_period' => 'Log retentie periode', + 'log_retention_period_description' => 'Aantal dagen dat logs van notificaties en archieven van server uptime worden bewaard. Vul 0 in om log opruiming uit te zetten.', + 'log_retention_days' => 'dagen', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => 'Server %LABEL% is DOWN: ip=%IP%, poort=%PORT%. Fout=%ERROR%', 'off_email_subject' => 'BELANGRIJK: Server %LABEL% is DOWN', 'off_email_body' => "De server kon niet worden bereikt:

Server: %LABEL%
IP: %IP%
Poort: %PORT%
Fout: %ERROR%
Datum: %DATE%", + 'off_pushover_title' => 'Server %LABEL% is DOWN', + 'off_pushover_message' => "De server kon niet worden bereikt:

Server: %LABEL%
IP: %IP%
Poort: %PORT%
Fout: %ERROR%
Datum: %DATE%", 'on_sms' => 'Server %LABEL% is RUNNING: ip=%IP%, poort=%PORT%', 'on_email_subject' => 'BELANGRIJK: Server %LABEL% is RUNNING', 'on_email_body' => "Server %LABEL% is weer online:

Server: %LABEL%
IP: %IP%
Poort: %PORT%
Datum: %DATE%", + 'on_pushover_title' => 'Server %LABEL% is RUNNING', + 'on_pushover_message' => "Server %LABEL% is weer online:

Server: %LABEL%
IP: %IP%
Poort: %PORT%
Datum: %DATE%", ), 'login' => array( 'welcome_usermenu' => 'Welkom, %user_name%', @@ -218,7 +282,7 @@ $sm_lang = array( 'password_forgot' => 'Wachtwoord vergeten?', 'password_reset' => 'Wachtwoord herstellen', 'password_reset_email_subject' => 'Wijzig je wachtwoord voor PHP Server Monitor', - 'password_reset_email_body' => 'Gebruik de onderstaande link om uw wachtwoord te wijzigen. Let op, deze link verloopt na 1 uur.

%link%', + 'password_reset_email_body' => 'Gebruik de onderstaande link om je wachtwoord te wijzigen. Let op, deze link verloopt na 1 uur.

%link%', 'error_user_incorrect' => 'De opgegeven gebruikersnaam is onjuist.', 'error_login_incorrect' => 'De informatie is niet juist.', 'error_login_passwords_nomatch' => 'De ingevulde wachtwoorden komen niet overeen.', @@ -226,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => 'Er is een mail verstuurd met informatie om je wachtwoord aan te passen.', 'success_password_reset' => 'Je wachtwoord is aangepast. Je kunt nu inloggen.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'U heeft niet de juiste bevoegdheden om deze pagina te bekijken.', + ), ); diff --git a/src/lang/pt_BR.lang.php b/src/lang/pt_BR.lang.php index c625f524..91450b4e 100644 --- a/src/lang/pt_BR.lang.php +++ b/src/lang/pt_BR.lang.php @@ -35,17 +35,17 @@ $sm_lang = array( 'save' => 'Salvar', 'edit' => 'Editar', 'delete' => 'Excluir', - 'deleted' => 'Registro excluído', 'date' => 'Data', 'message' => 'Mensagem', 'yes' => 'Sim', 'no' => 'Não', - 'edit' => 'Editar', 'insert' => 'Inserir', 'add_new' => 'Adicionar novo', 'update_available' => 'Uma atualização ({version}) disponível em http://www.phpservermonitor.org.', 'back_to_top' => 'Voltar ao topo', 'go_back' => 'Voltar', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%e %m', 'long_day_format' => '%e/%m/%Y', @@ -69,7 +69,7 @@ $sm_lang = array( 'help' => 'Ajuda', ), 'users' => array( - 'user' => 'usuário', + 'user' => 'Usuário', 'name' => 'Nome', 'user_name' => 'Username', 'password' => 'Senha', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => 'Administradores Tem total acesso: podem gerenciar servidores, usuários e configurações globais.
Usuários só podem executar atualizações para servidores que lhe foram atribuídos.', 'mobile' => 'Celular', 'email' => 'Email', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Delete User', + 'delete_message' => 'Are you sure you want to delete user \'%1\'?', + 'deleted' => 'User deleted.', 'updated' => 'Usuário atualizado.', 'inserted' => 'Usuário adicionado.', 'profile' => 'Perfil', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => 'Status', 'email' => 'Email', 'sms' => 'SMS', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => 'Servidor', + 'status' => 'Status', 'label' => 'Etiqueta', 'domain' => 'Domínio/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => 'Porta', 'type' => 'Tipo', 'type_website' => 'Website', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => 'Última verificação', 'last_online' => 'Última vez online', 'monitoring' => 'Monitoramento', + 'no_monitoring' => 'No monitoring', + 'email' => 'Email', 'send_email' => 'Enviar Email', + 'sms' => 'SMS', 'send_sms' => 'Enviar SMS', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Delete Server', + 'delete_message' => 'Are you sure you want to delete server \'%1\'?', + 'deleted' => 'Server deleted.', 'updated' => 'Servidor atualizado.', 'inserted' => 'Servidor adicionar.', 'latency' => 'Tempo de resposta', 'latency_max' => 'Latência (máxima)', 'latency_min' => 'Latência (minima)', 'latency_avg' => 'Latência (média)', + 'uptime' => 'Uptime', 'year' => 'Ano', 'month' => 'Mês', 'week' => 'Semana', @@ -137,6 +159,16 @@ $sm_lang = array( 'chart_long_date_format' => '%d/%m/%Y %H:%M:%S', 'chart_short_date_format' => '%d/%m %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => 'Geral', @@ -159,10 +191,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'Usuário do Gateway', 'sms_gateway_password' => 'Senha do Gateway', 'sms_from' => 'Número de telefone de envio', - 'alert_type' => 'Selecione como você gostaria de ser notificado.
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => 'Selecione como você gostaria de ser notificado.', 'alert_type_description' => 'Mudança de Status: '. 'Você receberá uma notificação quando o seridor tive uma mudança de status. De online -> offline ou offline -> online.
'. '
Offline: '. @@ -170,8 +209,7 @@ $sm_lang = array( 'A cronjob é a cada 15 minutos e seu servidor caiu em 1:00 e permanece offline até 6 am. '. 'Você receberá uma notificação a 1:00 apenas
'. '
Sempre: '. - 'Você receberá uma notificação toda vez que o script é executado e um site esta offline, mesmo se o site tenha ficado '. - 'offline por horas.', + 'Você receberá uma notificação toda vez que o script é executado e um site esta offline, mesmo se o site tenha ficado offline por horas.', 'alert_type_status' => 'Mudança de Status', 'alert_type_offline' => 'Offline', 'alert_type_always' => 'Sempre', @@ -179,12 +217,14 @@ $sm_lang = array( 'log_status_description' => 'Se o status de registro é definido como TRUE, o monitor irá registrar o evento sempre que as configurações de notificação forem passadas.', 'log_email' => 'Registrar no Log os envios de email feitos pelo script?', 'log_sms' => 'Registrar no Log os envios de mensagens de texto feitos pelo script?', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => 'A configuração foi atualizada.', 'tab_email' => 'Email', 'tab_sms' => 'Texto', - 'tab_log' => 'Logs', + 'tab_pushover' => 'Pushover', 'settings_email' => 'Configuração de email', 'settings_sms' => 'Configuração de mensagens de texto', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => 'Configuração de notificações', 'settings_log' => 'Configuração de Logs', 'auto_refresh' => 'Atualizar automaticamente', @@ -194,15 +234,38 @@ $sm_lang = array( 'Tempo em segundos, Se 0 a página não será atualizada.'. '', 'seconds' => 'segundos', + 'test' => 'Test', + 'test_email' => 'An email will be sent to the address specified in your user profile.', + 'test_sms' => 'An SMS will be sent to the phone number specified in your user profile.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test message', + 'email_sent' => 'Email sent', + 'email_error' => 'Error in email sending', + 'sms_sent' => 'Sms sent', + 'sms_error' => 'Error in sms sending', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => 'Servidor \'%LABEL%\' está OFFLINE: ip=%IP%, porta=%PORT%. Erro=%ERROR%', 'off_email_subject' => 'IMPORTANTE: Servidor \'%LABEL%\' está OFFLINE', 'off_email_body' => "Falha ao conectar ao servidor:

Servidor: %LABEL%
IP: %IP%
Porta: %PORT%
Erro: %ERROR%
Data: %DATE%", + 'off_pushover_title' => 'Servidor \'%LABEL%\' está OFFLINE', + 'off_pushover_message' => "Falha ao conectar ao servidor:

Servidor: %LABEL%
IP: %IP%
Porta: %PORT%
Erro: %ERROR%
Data: %DATE%", 'on_sms' => 'Servidor \'%LABEL%\' esta ONLINE: ip=%IP%, porta=%PORT%', 'on_email_subject' => 'IMPORTANTE: Servidor \'%LABEL%\' esta ONLINE', 'on_email_body' => "Servidor '%LABEL%' esta ONLINE novamente:

Servidor: %LABEL%
IP: %IP%
Porta: %PORT%
Data: %DATE%", + 'on_pushover_title' => 'Servidor \'%LABEL%\' esta ONLINE', + 'on_pushover_message' => "Servidor '%LABEL%' esta ONLINE novamente:

Servidor: %LABEL%
IP: %IP%
Porta: %PORT%
Data: %DATE%", ), 'login' => array( 'welcome_usermenu' => 'Bem vindo, %user_name%', @@ -227,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => 'Um email foi enviado para você com as instruções de redefinição de senha.', 'success_password_reset' => 'Sua senha foi redefinida com sucesso. Por favor faça login.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/lang/ru_RU.lang.php b/src/lang/ru_RU.lang.php new file mode 100644 index 00000000..6811305d --- /dev/null +++ b/src/lang/ru_RU.lang.php @@ -0,0 +1,297 @@ +. + * + * @package phpservermon + * @author Roman Beylin + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + **/ + +$sm_lang = array( + 'name' => 'Russian - Русский', + 'locale' => array('ru_RU.UTF-8', 'ru_RU', 'russian', 'russian'), + 'system' => array( + 'title' => 'Сервер Мониторинг', + 'install' => 'Установка', + 'action' => 'Действие', + 'save' => 'Сохранить', + 'edit' => 'Редактировать', + 'delete' => 'Удалить', + 'date' => 'Дата', + 'message' => 'Сообщение', + 'yes' => 'Да', + 'no' => 'Нет', + 'insert' => 'Добавить', + 'add_new' => 'Добавить новый', + 'update_available' => 'Новая версия ({version}) доступна по адресу http://www.phpservermonitor.org.', + 'back_to_top' => 'Наверх', + 'go_back' => 'Вернуться', + 'ok' => 'OK', + 'cancel' => 'Отмена', + // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php + 'short_day_format' => '%e %B', + 'long_day_format' => '%e %B %Y', + 'yesterday_format' => 'Вчера в %k:%M', + 'other_day_format' => '%A в %k:%M', + 'never' => 'Никогда', + 'hours_ago' => '%d часов назад', + 'an_hour_ago' => 'час назад', + 'minutes_ago' => '%d минут назад', + 'a_minute_ago' => 'минуту назад', + 'seconds_ago' => '%d секунд назад', + 'a_second_ago' => 'секунду назад', + ), + 'menu' => array( + 'config' => 'Параметры', + 'server' => 'Серверы', + 'server_log' => 'Лог', + 'server_status' => 'Статус', + 'server_update' => 'Обновить', + 'user' => 'Пользователи', + 'help' => 'Помощь', + ), + 'users' => array( + 'user' => 'Пользователь', + 'name' => 'Имя пользователя', + 'user_name' => 'Логин', + 'password' => 'Пароль', + 'password_repeat' => 'Повтор пароля', + 'password_leave_blank' => 'Оставить пустым, если не меняется', + 'level' => 'Уровень', + 'level_10' => 'Администратор', + 'level_20' => 'Пользователь', + 'level_description' => 'Администраторы имеют полный доступ: они могут управлять серверами, пользователями и изменять общую конфигурацию.
Пользователи могут только просматривать и запускать проверку для серверов, которые были к ним прикреплены.', + 'mobile' => 'Телефон', + 'email' => 'E-mail', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover - это сервис, который позволяет легко получать уведомления в режиме реального времени. Больше информации на их веб-сайте.', + 'pushover_key' => 'Pushover ключ', + 'pushover_device' => 'Pushover устройство', + 'pushover_device_description' => 'Имя устройства, на которое будут отправляться уведомления. Оставьте пустым, что бы отправлять уведомления на все устройства.', + 'delete_title' => 'Удалить пользователя', + 'delete_message' => 'Вы уверены что хотите удалить пользователя \'%1\'?', + 'deleted' => 'Пользователь удален.', + 'updated' => 'Пользователь обновлен.', + 'inserted' => 'Пользователь добавлен.', + 'profile' => 'Профиль', + 'profile_updated' => 'Ваш профиль был обновлен.', + 'error_user_name_bad_length' => 'Логин должен содержать от 2 до 64 знаков.', + 'error_user_name_invalid' => 'Имя пользователя может содержать только латинские символы (a-z, A-Z), цифры (0-9) и подчеркивание (_).', + 'error_user_name_exists' => 'Данный логин уже существует.', + 'error_user_email_bad_length' => 'E-mail может содержать от 5 до 255 знаков.', + 'error_user_email_invalid' => 'E-mail указан неверно.', + 'error_user_level_invalid' => 'Данный уровень пользователя недействителен.', + 'error_user_no_match' => 'Данного пользователя нет в базе данных.', + 'error_user_password_invalid' => 'Пароль указан неверно.', + 'error_user_password_no_match' => 'Введенные пароли не совпадают.', + ), + 'log' => array( + 'title' => 'Запись', + 'type' => 'Тип', + 'status' => 'Статус', + 'email' => 'E-mail', + 'sms' => 'СМС', + 'pushover' => 'Pushover', + 'no_logs' => 'Записей нет', + ), + 'servers' => array( + 'server' => 'Сервер', + 'status' => 'Состояние', + 'label' => 'Название', + 'domain' => 'Домен/IP', + 'timeout' => 'Тайм-аут', + 'timeout_description' => 'Количество секунд ожидания ответа сервера.', + 'port' => 'Порт', + 'type' => 'Тип', + 'type_website' => 'Веб-сайт', + 'type_service' => 'Сервис', + 'pattern' => 'Искать текст/шаблон', + 'pattern_description' => 'Если текст по шаблону не найден на сайте, сервер будет помечен как Оффлайн. Регулярные выражения допустимы.', + 'last_check' => 'Последняя проверка', + 'last_online' => 'Был онлайн', + 'monitoring' => 'Мониторинг', + 'no_monitoring' => 'Нет мониторинга', + 'email' => 'E-mail', + 'send_email' => 'Отправить E-mail', + 'sms' => 'CMC', + 'send_sms' => 'Отправить CMC', + 'pushover' => 'Pushover', + 'users' => 'Пользователи', + 'delete_title' => 'Удалить сервер', + 'delete_message' => 'Вы уверены что хотите удалить сервер \'%1\'?', + 'deleted' => 'Сервер удален.', + 'updated' => 'Сервер обновлен.', + 'inserted' => 'Север добавлен.', + 'latency' => 'Задержка', + 'latency_max' => 'Задержка (максимальная)', + 'latency_min' => 'Задержка (минимальная)', + 'latency_avg' => 'Задержка (средняя)', + 'uptime' => 'Аптайм', + 'year' => 'Год', + 'month' => 'Месяц', + 'week' => 'Неделя', + 'day' => 'День', + 'hour' => 'Час', + 'warning_threshold' => 'Порог предупреждения', + 'warning_threshold_description' => 'Количество неудачных проверок, требуемых перед тем как сервер будет помечен как Оффлайн.', + 'chart_last_week' => 'Прошлая неделя', + 'chart_history' => 'История', + // Charts date format according jqPlot date format http://www.jqplot.com/docs/files/plugins/jqplot-dateAxisRenderer-js.html + 'chart_day_format' => '%d-%m-%Y', + 'chart_long_date_format' => '%d-%m-%Y %H:%M:%S', + 'chart_short_date_format' => '%d/%m %H:%M', + 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'СМС уведомления отключены.', + 'warning_notifications_disabled_email' => 'E-mail уведомления отключены.', + 'warning_notifications_disabled_pushover' => 'Pushover уведомления отключены.', + 'error_server_no_match' => 'Сервер не найден.', + 'error_server_label_bad_length' => 'Название должно содержать от 1 до 255 знаков.', + 'error_server_ip_bad_length' => 'Домен/IP должен содержать от 1 до 255 знаков', + 'error_server_ip_bad_service' => 'IP-адрес недействителен.', + 'error_server_ip_bad_website' => 'Ссылка веб-страницы недействительна.', + 'error_server_type_invalid' => 'Выбраный тип сервера недействителен.', + 'error_server_warning_threshold_invalid' => 'Порог предупреждения должен иметь значение больше 0', + ), + 'config' => array( + 'general' => 'Основные', + 'language' => 'Язык', + 'show_update' => 'Обновления', + 'email_status' => 'Разрешить отправку E-mail', + 'email_from_email' => 'Отправлять от адреса', + 'email_from_name' => 'Отправлять от имени', + 'email_smtp' => 'Включить SMTP', + 'email_smtp_host' => 'SMTP сервер', + 'email_smtp_port' => 'SMTP порт', + 'email_smtp_username' => 'SMTP пользователь', + 'email_smtp_password' => 'SMTP пароль', + 'email_smtp_noauth' => 'Оставить пустым, если без аутентификации', + 'sms_status' => 'Разрешить отправку СМС', + 'sms_gateway' => 'Шлюз для отправки СМС', + 'sms_gateway_mosms' => 'Mosms', + 'sms_gateway_mollie' => 'Mollie', + 'sms_gateway_spryng' => 'Spryng', + 'sms_gateway_inetworx' => 'Inetworx', + 'sms_gateway_clickatell' => 'Clickatell', + 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', + 'sms_gateway_username' => 'Пользователь', + 'sms_gateway_password' => 'Пароль', + 'sms_from' => 'Номер отправителя', + 'pushover_status' => 'Разрешить отправку Pushover сообщений', + 'pushover_description' => 'Pushover - это сервис, который позволяет легко получать уведомления в режиме реального времени. Больше информации на их веб-сайте.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Прежде чем вы сможете начать пользоваться Pushover, вам необходимо зарегестрировать "App" на их веб-сайте и ввести "App API Token" сюда.', + 'alert_type' => 'Выбeрите, какие вы хотите получать уведомления', + 'alert_type_description' => 'Изменение статуса : '. + 'Вы получите уведомление об изменение статуса. Для онлайн -> оффлайн или офлайн -> онлайн.
'. + '
Оффлайн: '. + 'Вы получите уведомление только когда сервер перейдет в статус оффлайн. Например, '. + 'Задание Cron выставлено на каждые 15 минут. Сервер перейдет в статус оффлайн в 1:00 и не измениться до 6:00. '. + 'Вы получите 1 уведомление только в 1:00
'. + '
Всегда: '. + 'Вы будете получать уведомление при каждом запуске скрипта проверки, как только сервер перейдет в статус оффлайн, даже если сервер находится в этом статусе несколько часов', + 'alert_type_status' => 'Изменение статуса', + 'alert_type_offline' => 'Оффлайн', + 'alert_type_always' => 'Всегда', + 'log_status' => 'Лог статусов', + 'log_status_description' => 'Если лог установлен в TRUE, монитор будет логировать все события режим которых выбран в типе уведомлений.', + 'log_email' => 'Логировать уведомления отправленые по E-mail', + 'log_sms' => 'Логировать уведомления отправленые по СМС', + 'log_pushover' => 'Логировать Pushover уведомления', + 'updated' => 'Параметры были успешно применены.', + 'tab_email' => 'E-mail', + 'tab_sms' => 'СМС', + 'tab_pushover' => 'Pushover', + 'settings_email' => 'Настройка E-mail', + 'settings_sms' => 'Настройка SMS', + 'settings_pushover' => 'Настройка Pushover', + 'settings_notification' => 'Настройка уведомлений', + 'settings_log' => 'Настройка логирования', + 'auto_refresh' => 'Авто-обновление', + 'auto_refresh_servers' => + 'Авто-обновление страницы статуса серверов.
'. + ''. + 'Вермя в секундах, если 0 страница не будет обновляться.'. + '', + 'seconds' => 'секунд', + 'test' => 'Проверка', + 'test_email' => 'Сообщение будет отправлено на адрес указаный в профиле пользователя.', + 'test_sms' => 'Сообщение будет отправлено на номер телефона указаный в профиле пользователя.', + 'test_pushover' => 'Pushover уведомление будет отправленно на устройство указанное в профиле пользователя.', + 'send' => 'Отправить', + 'test_subject' => 'Проверка', + 'test_message' => 'Тестовое сообщение', + 'email_sent' => 'Сообщение отправлено', + 'email_error' => 'Ошибка при отправке сообщения', + 'sms_sent' => 'СМС отправлено', + 'sms_error' => 'Ошибка при отправке СМС', + 'sms_error_nomobile' => 'Не удалось отправить пробный СМС: действительный телефонный номер не был найден в вашем профиле.', + 'pushover_sent' => 'Pushover уведомление отправлено', + 'pushover_error' => 'Произошла ошибка во время отправки Pushover уведомления: %s', + 'pushover_error_noapp' => 'Не удалось отправить пробное уведомление: Pushover "App API token" не был найден в основных настройках.', + 'pushover_error_nokey' => 'Не удалось отправить пробное уведомление: Pushover ключ не был найден в вашем профиле.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', + ), + // for newlines in the email messages use
+ 'notifications' => array( + 'off_sms' => 'Сервер \'%LABEL%\' сейчас НЕДОСТУПЕН: ip=%IP%, port=%PORT%. Ошибка=%ERROR%', + 'off_email_subject' => 'ВАЖНО: сервер \'%LABEL%\' сейчас НЕДОСТУПЕН', + 'off_email_body' => "Невозможно подключиться к следующему серверу:

Сервер: %LABEL%
IP: %IP%
Порт: %PORT%
Ошибка: %ERROR%
Дата: %DATE%", + 'off_pushover_title' => 'Cервер \'%LABEL%\' сейчас НЕДОСТУПЕН', + 'off_pushover_message' => "Невозможно подключиться к следующему серверу:

Сервер: %LABEL%
IP: %IP%
Порт: %PORT%
Ошибка: %ERROR%
Дата: %DATE%", + 'on_sms' => 'Сервер \'%LABEL%\' сейчас ДОСТУПЕН: ip=%IP%, port=%PORT%', + 'on_email_subject' => 'ВАЖНО: Сервер \'%LABEL%\' сейчас ДОСТУПЕН', + 'on_email_body' => "Сервер '%LABEL%' снова доступен:

Сервер: %LABEL%
IP: %IP%
Порт: %PORT%
Дата: %DATE%", + 'on_pushover_title' => 'Сервер \'%LABEL%\' сейчас ДОСТУПЕН', + 'on_pushover_message' => "Сервер '%LABEL%' снова доступен:

Сервер: %LABEL%
IP: %IP%
Порт: %PORT%
Дата: %DATE%", + ), + 'login' => array( + 'welcome_usermenu' => 'Здравствуйте, %user_name%', + 'title_sign_in' => 'Пожалуйста, авторизуйтесь', + 'title_forgot' => 'Забыли пароль?', + 'title_reset' => 'Сбросить пароль', + 'submit' => 'Подтвердить', + 'remember_me' => 'Запомнить меня', + 'login' => 'Войти', + 'logout' => 'Выйти', + 'username' => 'Логин', + 'password' => 'Пароль', + 'password_repeat' => 'Повторить пароль', + 'password_forgot' => 'Забыли пароль?', + 'password_reset' => 'Сбросить пароль', + 'password_reset_email_subject' => 'Сбросить пароль для PHP Server Monitor', + 'password_reset_email_body' => 'Пожалуйста, используйте следующую ссылку для сброса пароля. Ссылка действительна 1 час.

%link%', + 'error_user_incorrect' => 'Пользователь с указаными данными не найден.', + 'error_login_incorrect' => 'Информация указана неверно.', + 'error_login_passwords_nomatch' => 'Пароль указан неверно.', + 'error_reset_invalid_link' => 'Ссылка для сброса пароля недействительна.', + 'success_password_forgot' => 'Вам был отправлен email, с описанием сброса пароля.', + 'success_password_reset' => 'Ваш пароль был сброшен. Пожалуйста авторизуйтесь.', + ), + 'error' => array( + '401_unauthorized' => 'Доступ закрыт', + '401_unauthorized_description' => 'У вас нет прав доступа к этой странице.', + ), +); diff --git a/src/lang/zh_CN.lang.php b/src/lang/zh_CN.lang.php index b37e2f79..81703b3a 100644 --- a/src/lang/zh_CN.lang.php +++ b/src/lang/zh_CN.lang.php @@ -35,17 +35,17 @@ $sm_lang = array( 'save' => '保存', 'edit' => '编辑', 'delete' => '删除', - 'deleted' => '纪录已删除', 'date' => '日期', 'message' => '消息', 'yes' => '是', 'no' => '否o', - 'edit' => '编辑', 'insert' => '插入', 'add_new' => '添加', 'update_available' => '发现新版本({version}) http://www.phpservermonitor.org.', 'back_to_top' => '返回顶部', 'go_back' => '后退', + 'ok' => 'OK', + 'cancel' => 'Cancel', // date/time format according the strftime php function format parameter http://php.net/manual/function.strftime.php 'short_day_format' => '%B %e', 'long_day_format' => '%B %e, %Y', @@ -81,6 +81,14 @@ $sm_lang = array( 'level_description' => '超级管理员 拥有所有权限: 管理服务器, 用户 以及修改设置.
普通用户 只能查看及更新自己名下所属的服务器.', 'mobile' => '手机', 'email' => '邮件', + 'pushover' => 'Pushover', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_key' => 'Pushover Key', + 'pushover_device' => 'Pushover Device', + 'pushover_device_description' => 'Device name to send the message to. Leave empty to send it to all devices.', + 'delete_title' => 'Delete User', + 'delete_message' => 'Are you sure you want to delete user \'%1\'?', + 'deleted' => 'User deleted.', 'updated' => '用户已更新.', 'inserted' => '用户已添加.', 'profile' => '个人资料', @@ -101,11 +109,16 @@ $sm_lang = array( 'status' => '状态', 'email' => '邮件', 'sms' => '短信', + 'pushover' => 'Pushover', + 'no_logs' => 'No logs', ), 'servers' => array( 'server' => '服务器', + 'status' => '状态', 'label' => '标签', 'domain' => '域名/IP', + 'timeout' => 'Timeout', + 'timeout_description' => 'Number of seconds to wait for the server to respond.', 'port' => '端口', 'type' => '类型', 'type_website' => '网站', @@ -115,14 +128,23 @@ $sm_lang = array( 'last_check' => '最后检查', 'last_online' => '最后在线', 'monitoring' => '监控中', + 'no_monitoring' => 'No monitoring', + 'email' => '邮件', 'send_email' => '发送邮件', + 'sms' => '短信', 'send_sms' => '发送短信', + 'pushover' => 'Pushover', + 'users' => 'Users', + 'delete_title' => 'Delete Server', + 'delete_message' => 'Are you sure you want to delete server \'%1\'?', + 'deleted' => 'Server deleted.', 'updated' => '服务器已更新.', 'inserted' => '服务器已添加.', 'latency' => '延迟', 'latency_max' => '延迟(最大)', 'latency_min' => '延迟(最小)', 'latency_avg' => '延迟(平均)', + 'uptime' => 'Uptime', 'year' => '年', 'month' => '月', 'week' => '周', @@ -137,6 +159,16 @@ $sm_lang = array( 'chart_long_date_format' => '%Y-%m-%d %H:%M:%S', 'chart_short_date_format' => '%m/%d %H:%M', 'chart_short_time_format' => '%H:%M', + 'warning_notifications_disabled_sms' => 'SMS notifications are disabled.', + 'warning_notifications_disabled_email' => 'Email notifications are disabled.', + 'warning_notifications_disabled_pushover' => 'Pushover notifications are disabled.', + 'error_server_no_match' => 'Server not found.', + 'error_server_label_bad_length' => 'The label must be between 1 and 255 characters.', + 'error_server_ip_bad_length' => 'The domain / IP must be between 1 and 255 characters.', + 'error_server_ip_bad_service' => 'The IP address is not valid.', + 'error_server_ip_bad_website' => 'The website URL is not valid.', + 'error_server_type_invalid' => 'The selected server type is invalid.', + 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', ), 'config' => array( 'general' => '通用', @@ -159,10 +191,17 @@ $sm_lang = array( 'sms_gateway_inetworx' => 'Inetworx', 'sms_gateway_clickatell' => 'Clickatell', 'sms_gateway_textmarketer' => 'Textmarketer', + 'sms_gateway_smsglobal' => 'SMSGlobal', + 'sms_gateway_smsit' => 'Smsit', 'sms_gateway_username' => 'SMS网关用户名', 'sms_gateway_password' => 'SMS网关密码', 'sms_from' => '发信人电话号', - 'alert_type' => '如果想要收到提醒请选中此项.
', + 'pushover_status' => 'Allow sending Pushover messages', + 'pushover_description' => 'Pushover is a service that makes it easy to get real-time notifications. See their website for more info.', + 'pushover_clone_app' => 'Click here to create your Pushover app', + 'pushover_api_token' => 'Pushover App API Token', + 'pushover_api_token_description' => 'Before you can use Pushover, you need to register an App at their website and enter the App API Token here.', + 'alert_type' => '如果想要收到提醒请选中此项.', 'alert_type_description' => '状态变化: '. '服务器 online -> offline 或 offline -> online 的状态变化将会收到提醒.
'. '
离线状态: '. @@ -178,12 +217,14 @@ $sm_lang = array( 'log_status_description' => '如果状态记录设置为开, 提醒发送时均会保存记录.', 'log_email' => '记录脚本所发邮件?', 'log_sms' => '记录脚本所发短信SMS?', + 'log_pushover' => 'Log pushover messages sent by the script', 'updated' => '设置已更新.', 'tab_email' => '邮件发送设置', 'tab_sms' => '短信发送设置', - 'tab_log' => '日志设置', + 'tab_pushover' => 'Pushover', 'settings_email' => '邮件发送设置', 'settings_sms' => '短信发送设置', + 'settings_pushover' => 'Pushover settings', 'settings_notification' => '提醒设置', 'settings_log' => '日志设置', 'auto_refresh' => 'Auto-refresh', @@ -193,15 +234,38 @@ $sm_lang = array( '单位为秒, 设置为0则不自动刷新.'. '', 'seconds' => 'seconds', + 'test' => 'Test', + 'test_email' => 'An email will be sent to the address specified in your user profile.', + 'test_sms' => 'An SMS will be sent to the phone number specified in your user profile.', + 'test_pushover' => 'A Pushover notification will be sent to the user key/device specified in your user profile.', + 'send' => 'Send', + 'test_subject' => 'Test', + 'test_message' => 'Test message', + 'email_sent' => 'Email sent', + 'email_error' => 'Error in email sending', + 'sms_sent' => 'Sms sent', + 'sms_error' => 'Error in sms sending', + 'sms_error_nomobile' => 'Unable to send test SMS: no valid phone number found in your profile.', + 'pushover_sent' => 'Pushover notification sent', + 'pushover_error' => 'An error has occurred while sending the Pushover notification: %s', + 'pushover_error_noapp' => 'Unable to send test notification: no Pushover App API token found in the global configuration.', + 'pushover_error_nokey' => 'Unable to send test notification: no Pushover key found in your profile.', + 'log_retention_period' => 'Log retention period', + 'log_retention_period_description' => 'Number of days to keep logs of notifications and archives of server uptime. Enter 0 to disable log cleanup.', + 'log_retention_days' => 'days', ), // for newlines in the email messages use
'notifications' => array( 'off_sms' => '服务器 \'%LABEL%\' 宕机: ip=%IP%, port=%PORT%. Error=%ERROR%', 'off_email_subject' => 'IMPORTANT: 服务器 \'%LABEL%\' 宕机', 'off_email_body' => "无法连接到以下服务器:

服务器: %LABEL%
IP: %IP%
Port: %PORT%
错误: %ERROR%
日期: %DATE%", + 'off_pushover_title' => '服务器 \'%LABEL%\' 宕机', + 'off_pushover_message' => "无法连接到以下服务器:

服务器: %LABEL%
IP: %IP%
Port: %PORT%
错误: %ERROR%
日期: %DATE%", 'on_sms' => '服务器 \'%LABEL%\' 运行中: ip=%IP%, port=%PORT%', 'on_email_subject' => 'IMPORTANT: 服务器 \'%LABEL%\' 运行中', 'on_email_body' => "服务器 '%LABEL%' 恢复运行:

服务器: %LABEL%
IP: %IP%
Port: %PORT%
日期: %DATE%", + 'on_pushover_title' => '服务器 \'%LABEL%\' 运行中', + 'on_pushover_message' => "服务器 '%LABEL%' 恢复运行:

服务器: %LABEL%
IP: %IP%
Port: %PORT%
日期: %DATE%", ), 'login' => array( 'welcome_usermenu' => '欢迎, %user_name%', @@ -226,4 +290,8 @@ $sm_lang = array( 'success_password_forgot' => '密码重设邮件已发送.', 'success_password_reset' => '密码重设成功.请登录.', ), + 'error' => array( + '401_unauthorized' => 'Unauthorized', + '401_unauthorized_description' => 'You do not have the privileges to view this page.', + ), ); diff --git a/src/psm/Module/AbstractController.class.php b/src/psm/Module/AbstractController.class.php index ac097687..d976e25b 100644 --- a/src/psm/Module/AbstractController.class.php +++ b/src/psm/Module/AbstractController.class.php @@ -27,7 +27,7 @@ namespace psm\Module; use psm\Service\Database; -use psm\Service\Template; +use Symfony\Component\HttpFoundation\Response; abstract class AbstractController implements ControllerInterface { @@ -79,6 +79,18 @@ abstract class AbstractController implements ControllerInterface { */ protected $sidebar; + /** + * array of Modal to add + * @var \psm\Util\Module\ModalInterface[] $modal + */ + protected $modal = array(); + + /** + * html code of header accessories + * @var string $header_accessories + */ + protected $header_accessories; + /** * Database object * @var \psm\Service\Database $db @@ -86,17 +98,10 @@ abstract class AbstractController implements ControllerInterface { protected $db; /** - * Template object - * @var \psm\Service\Template $tpl + * Twig object + * @var \Twig_Environment $twig */ - protected $tpl; - - /** - * Template Id that should be added to the main template - * @var string - * @see setTemplateId() getTemplateId() - */ - protected $tpl_id; + protected $twig; /** * User service @@ -118,22 +123,46 @@ abstract class AbstractController implements ControllerInterface { */ protected $user_level_required_actions = array(); - function __construct(Database $db, Template $tpl) { + /* + * Required using black background layout + * @var boolean $black_background + */ + protected $black_background = false; + + /** + * XHR mode? + * @var boolean $xhr + * @see isXHR() + */ + protected $xhr = false; + + function __construct(Database $db, \Twig_Environment $twig) { $this->db = $db; - $this->tpl = $tpl; + $this->twig = $twig; } /** - * Initialize the module + * Initialize the controller. + * + * @param string $action if NULL, the action will be retrieved from user input (GET/POST) + * @return \Symfony\Component\HttpFoundation\Response */ - public function initialize() { - $action = psm_GET('action', psm_POST('action', $this->action_default)); + public function initialize($action = null) { + if($action === null) { + $action = psm_GET('action', psm_POST('action', $this->action_default)); + } + $this->xhr = (bool) psm_GET('xhr', psm_POST('xhr', false)); - if(!in_array($action, $this->actions) || !$this->initializeAction($action)) { - $this->initializeAction($this->action_default); + if(!in_array($action, $this->actions) || !($result = $this->initializeAction($action))) { + $result = $this->initializeAction($this->action_default); } - $this->createHTML(); + if($result instanceof Response) { + return $result; + } + + // no response returned from execute, create regular HTML + return $this->createHTML($result); } /** @@ -141,7 +170,7 @@ abstract class AbstractController implements ControllerInterface { * * For it to run, the "execute$action" method must exist. * @param string $action - * @return boolean whether action has been initialized successfully + * @return mixed FALSE when action couldnt be initialized, response otherwise */ protected function initializeAction($action) { if(isset($this->user_level_required_actions[$action])) { @@ -155,57 +184,67 @@ abstract class AbstractController implements ControllerInterface { $method = 'execute' . ucfirst($action); if(method_exists($this, $method)) { $this->action = $action; - $this->$method(); - return true; + $result = $this->$method(); + // if result from execute is null, no return value given so return true to indicate a successful execute + return ($result === null) ? true : $result; } return false; } /** * Create the HTML code for the module. - * First the createHTMLLabels() will be called to add all labels to the template, - * Then the tpl_id set in $this->getTemplateId() will be added to the main template automatically + * + * If XHR is on, no main template will be added. + * + * @param string $html HTML code to add to the main body + * @return \Symfony\Component\HttpFoundation\Response */ - protected function createHTML() { - $tpl_data = array(); + protected function createHTML($html = null) { + if(!$this->xhr) { + // in XHR mode, we will not add the main template + $tpl_data = array( + 'title' => strtoupper(psm_get_lang('system', 'title')), + 'label_back_to_top' => psm_get_lang('system', 'back_to_top'), + 'add_footer' => $this->add_footer, + 'version' => 'v' . PSM_VERSION, + 'messages' => $this->getMessages(), + 'html_content' => $html, + ); - if(!empty($this->messages)) { - $this->tpl->addTemplateDataRepeat('main', 'messages', $this->messages); - } - // add menu to page? - if($this->add_menu) { - $tpl_data['html_menu'] = $this->createHTMLMenu(); - } - // add sidebar to page? - if($this->sidebar !== null) { - $tpl_data['html_sidebar'] = $this->sidebar->createHTML(); - $tpl_data['content_span'] = '10'; - } else { - $tpl_data['content_span'] = '12'; - } - // add footer to page? - if($this->add_footer) { - $this->tpl->newTemplate('main_footer', 'main.tpl.html'); - $tpl_data['html_footer'] = $this->tpl->getTemplate('main_footer'); - $tpl_data['version'] = 'v' . PSM_VERSION; + // add menu to page? + if($this->add_menu) { + $tpl_data['html_menu'] = $this->createHTMLMenu(); + } + // add header accessories to page ? + if($this->header_accessories) { + $tpl_data['header_accessories'] = $this->header_accessories; + } + // add modal dialog to page ? + if(sizeof($this->modal)) { + $html_modal = ''; + foreach($this->modal as $modal) { + $html_modal .= $modal->createHTML(); + } + $tpl_data['html_modal'] = $html_modal; + } + // add sidebar to page? + if($this->sidebar !== null) { + $tpl_data['html_sidebar'] = $this->sidebar->createHTML(); + } + + if(psm_update_available()) { + $tpl_data['update_available'] = str_replace('{version}', 'v'.psm_get_conf('version_update_check'), psm_get_lang('system', 'update_available')); + } + + if($this->black_background) { + $tpl_data['body_class'] = 'black_background'; + } + $html = $this->twig->render('main/body.tpl.html', $tpl_data); } - $tpl_id_content = $this->getTemplateId(); - if($tpl_id_content) { - $tpl_data['content'] = $this->tpl->getTemplate($tpl_id_content); - } + $response = new Response($html); - if(psm_update_available()) { - $tpl_data['update_available'] = str_replace('{version}', 'v'.psm_get_conf('version_update_check'), psm_get_lang('system', 'update_available')); - } - - // add the module's custom template to the main template to get some content - $this->setTemplateId('main'); - $this->tpl->addTemplatedata($this->getTemplateId(), $tpl_data); - $this->createHTMLLabels(); - - // display main template - echo $this->tpl->display($this->getTemplateId()); + return $response; } /** @@ -215,9 +254,6 @@ abstract class AbstractController implements ControllerInterface { protected function createHTMLMenu() { $ulvl = ($this->user) ? $this->user->getUserLevel() : PSM_USER_ANONYMOUS; - $tpl_id = 'main_menu'; - $this->tpl->newTemplate($tpl_id, 'main.tpl.html'); - $tpl_data = array( 'label_help' => psm_get_lang('menu', 'help'), 'label_profile' => psm_get_lang('users', 'profile'), @@ -237,17 +273,14 @@ abstract class AbstractController implements ControllerInterface { $items = array(); break; } - $menu = array(); + $tpl_data['menu'] = array(); foreach($items as $key) { - $menu[] = array( + $tpl_data['menu'][] = array( 'active' => ($key == psm_GET('mod')) ? 'active' : '', 'url' => psm_build_url(array('mod' => $key)), 'label' => psm_get_lang('menu', $key), ); } - if(!empty($menu)) { - $this->tpl->addTemplateDataRepeat($tpl_id, 'menu', $menu); - } if($ulvl != PSM_USER_ANONYMOUS) { $user = $this->user->getUser(); @@ -257,53 +290,7 @@ abstract class AbstractController implements ControllerInterface { psm_get_lang('login', 'welcome_usermenu') ); } - $this->tpl->addTemplateData($tpl_id, $tpl_data); - - return $this->tpl->getTemplate($tpl_id); - } - - /** - * Use this to add language specific labels to template - * - * @see createHTML() - */ - protected function createHTMLLabels() { - global $type; - - $this->tpl->addTemplateData( - 'main', - array( - 'title' => strtoupper(psm_get_lang('system', 'title')), - 'label_back_to_top' => psm_get_lang('system', 'back_to_top'), - ) - ); - } - - /** - * Set a template id that will be added to the main template automatically - * once you call the parent::createHTML() - * - * @param string $tpl_id - * @param string $tpl_file if given, the tpl_id will be created automatically from this file - * @see getTemplateId() createHTML() - */ - public function setTemplateId($tpl_id, $tpl_file = null) { - $this->tpl_id = $tpl_id; - - if($tpl_file != null) { - // tpl_file given, try to load the template.. - $this->tpl->newTemplate($tpl_id, $tpl_file); - } - } - - /** - * Get the mpalte id that will be added to the main template - * - * @return string - * @see setTemplateId() - */ - public function getTemplateId() { - return $this->tpl_id; + return $this->twig->render('main/menu.tpl.html', $tpl_data); } /** @@ -358,7 +345,8 @@ abstract class AbstractController implements ControllerInterface { * Add one or multiple message to the stack to be displayed to the user * @param string|array $msg * @param string $shortcode info/success/warning/error - * @return \psm\Module\AbstractModule + * @return \psm\Module\ControllerInterface + * @see getMessages() */ public function addMessage($msg, $shortcode = 'info') { if(!is_array($msg)) { @@ -389,6 +377,20 @@ abstract class AbstractController implements ControllerInterface { return $this; } + /** + * Get all messages (and optionally clear them) + * @param boolean $clear + * @return array + * @see addMessage() + */ + public function getMessages($clear = true) { + $msgs = $this->messages; + if($clear) { + $this->messages = array(); + } + return $msgs; + } + /** * Set user service * @param \psm\Service\User $user @@ -443,4 +445,30 @@ abstract class AbstractController implements ControllerInterface { $this->sidebar = $sidebar; return $this; } + + /** + * Add a modal dialog to the page + * @param \psm\Util\Module\ModalInterface $modal + * @return \psm\Module\ControllerInterface + */ + public function addModal(\psm\Util\Module\ModalInterface $modal) { + $this->modal[$modal->getModalID()] = $modal; + return $this; + } + + /** + * Set the html code of the header accessories + * @param string $html + */ + public function setHeaderAccessories($html) { + $this->header_accessories = $html; + } + + /** + * Check if XHR is on + * @return boolean + */ + public function isXHR() { + return $this->xhr; + } } diff --git a/src/psm/Module/Config/Controller/ConfigController.class.php b/src/psm/Module/Config/Controller/ConfigController.class.php index 34544cc2..5c7a2be1 100644 --- a/src/psm/Module/Config/Controller/ConfigController.class.php +++ b/src/psm/Module/Config/Controller/ConfigController.class.php @@ -28,7 +28,6 @@ namespace psm\Module\Config\Controller; use psm\Module\AbstractController; use psm\Service\Database; -use psm\Service\Template; class ConfigController extends AbstractController { @@ -40,9 +39,11 @@ class ConfigController extends AbstractController { 'email_status', 'email_smtp', 'sms_status', + 'pushover_status', 'log_status', 'log_email', 'log_sms', + 'log_pushover', 'show_update', ); @@ -60,10 +61,13 @@ class ConfigController extends AbstractController { 'sms_gateway_username', 'sms_gateway_password', 'sms_from', + 'pushover_api_token', ); - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + private $default_tab = 'general'; + + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->setMinUserLevelRequired(PSM_USER_ADMIN); @@ -74,9 +78,12 @@ class ConfigController extends AbstractController { /** * Populate all the config fields with values from the database + * + * @return string */ protected function executeIndex() { - $this->setTemplateId('config', 'config/config.tpl.html'); + $this->twig->addGlobal('subtitle', psm_get_lang('menu', 'config')); + $tpl_data = $this->getLabels(); $config_db = $this->db->select( PSM_DB_PREFIX . 'config', @@ -91,21 +98,21 @@ class ConfigController extends AbstractController { // generate language array $lang_keys = psm_get_langs(); - $languages = array(); + $tpl_data['language_current'] = (isset($config['language'])) + ? $config['language'] + : 'en_US'; + $tpl_data['languages'] = array(); foreach($lang_keys as $key => $label) { - $languages[] = array( + $tpl_data['languages'][] = array( 'value' => $key, 'label' => $label, - 'selected' => ($key == $config['language']) ? 'selected="selected"' : '', ); } - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'languages', $languages); - $tpl_data = array( - 'sms_selected_' . $config['sms_gateway'] => 'selected="selected"', - 'alert_type_selected_' . $config['alert_type'] => 'selected="selected"', - 'auto_refresh_servers' => (isset($config['auto_refresh_servers'])) ? $config['auto_refresh_servers'] : '0', - ); + $tpl_data['sms_selected_' . $config['sms_gateway']] = 'selected="selected"'; + $tpl_data['alert_type_selected_' . $config['alert_type']] = 'selected="selected"'; + $tpl_data['auto_refresh_servers'] = (isset($config['auto_refresh_servers'])) ? $config['auto_refresh_servers'] : '0'; + $tpl_data['log_retention_period'] = (isset($config['log_retention_period'])) ? $config['log_retention_period'] : '365'; foreach($this->checkboxes as $input_key) { $tpl_data[$input_key . '_checked'] = @@ -117,7 +124,18 @@ class ConfigController extends AbstractController { $tpl_data[$input_key] = (isset($config[$input_key])) ? $config[$input_key] : ''; } - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + $tpl_data[$this->default_tab . '_active'] = 'active'; + + $testmodals = array('email', 'sms', 'pushover'); + foreach($testmodals as $modal_id) { + $modal = new \psm\Util\Module\Modal($this->twig, 'test' . ucfirst($modal_id), \psm\Util\Module\Modal::MODAL_TYPE_OKCANCEL); + $this->addModal($modal); + $modal->setTitle(psm_get_lang('servers', 'send_' . $modal_id)); + $modal->setMessage(psm_get_lang('config', 'test_' . $modal_id)); + $modal->setOKButtonLabel(psm_get_lang('config', 'send')); + } + + return $this->twig->render('module/config/config.tpl.html', $tpl_data); } /** @@ -131,7 +149,8 @@ class ConfigController extends AbstractController { 'language' => $_POST['language'], 'sms_gateway' => $_POST['sms_gateway'], 'alert_type' => $_POST['alert_type'], - 'auto_refresh_servers' => (isset($_POST['auto_refresh_servers'])) ? intval($_POST['auto_refresh_servers']) : '0', + 'auto_refresh_servers' => intval(psm_POST('auto_refresh_servers', 0)), + 'log_retention_period' => intval(psm_POST('log_retention_period', 365)), ); foreach($this->checkboxes as $input_key) { $clean[$input_key] = (isset($_POST[$input_key])) ? '1': '0'; @@ -141,91 +160,181 @@ class ConfigController extends AbstractController { $clean[$input_key] = $_POST[$input_key]; } } - - // save all values to the database + $language_refresh = ($clean['language'] != psm_get_conf('language')); foreach($clean as $key => $value) { - // check if key already exists, otherwise add it - if(psm_get_conf($key) === null) { - // not yet set, add it - $this->db->save( - PSM_DB_PREFIX . 'config', - array( - 'key' => $key, - 'value' => $value, - ) - ); - } else { - // update - $this->db->save( - PSM_DB_PREFIX . 'config', - array('value' => $value), - array('key' => $key) - ); - } + psm_update_conf($key, $value); } - $this->addMessage(psm_get_lang('config', 'updated'), 'success'); - if($clean['language'] != psm_get_conf('language')) { - header('Location: ' . $_SERVER['REQUEST_URI']); + if(!empty($_POST['test_email'])) { + $this->testEmail(); + } elseif(!empty($_POST['test_sms'])) { + $this->testSMS(); + } elseif(!empty($_POST['test_pushover'])) { + $this->testPushover(); + } + + if($language_refresh) { + header('Location: ' . psm_build_url(array('mod' => 'config'), true, false)); die(); } + + if(isset($_POST['general_submit'])) { + $this->default_tab = 'general'; + } elseif(isset($_POST['email_submit']) || !empty($_POST['test_email'])) { + $this->default_tab = 'email'; + } elseif(isset($_POST['sms_submit']) || !empty($_POST['test_sms'])) { + $this->default_tab = 'sms'; + } elseif(isset($_POST['pushover_submit']) || !empty($_POST['test_pushover'])) { + $this->default_tab = 'pushover'; + } } - $this->initializeAction('index'); + return $this->initializeAction('index'); } - // override parent::createHTMLLabels() - protected function createHTMLLabels() { - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'subtitle' => psm_get_lang('menu', 'config'), - 'label_tab_email' => psm_get_lang('config', 'tab_email'), - 'label_tab_sms' => psm_get_lang('config', 'tab_sms'), - 'label_tab_log' => psm_get_lang('config', 'tab_log'), - 'label_settings_email' => psm_get_lang('config', 'settings_email'), - 'label_settings_sms' => psm_get_lang('config', 'settings_sms'), - 'label_settings_notification' => psm_get_lang('config', 'settings_notification'), - 'label_settings_log' => psm_get_lang('config', 'settings_log'), - 'label_general' => psm_get_lang('config', 'general'), - 'label_language' => psm_get_lang('config', 'language'), - 'label_show_update' => psm_get_lang('config', 'show_update'), - 'label_email_status' => psm_get_lang('config', 'email_status'), - 'label_email_from_email' => psm_get_lang('config', 'email_from_email'), - 'label_email_from_name' => psm_get_lang('config', 'email_from_name'), - 'label_email_smtp' => psm_get_lang('config', 'email_smtp'), - 'label_email_smtp_host' => psm_get_lang('config', 'email_smtp_host'), - 'label_email_smtp_port' => psm_get_lang('config', 'email_smtp_port'), - 'label_email_smtp_username' => psm_get_lang('config', 'email_smtp_username'), - 'label_email_smtp_password' => psm_get_lang('config', 'email_smtp_password'), - 'label_email_smtp_noauth' => psm_get_lang('config', 'email_smtp_noauth'), - 'label_sms_status' => psm_get_lang('config', 'sms_status'), - 'label_sms_gateway' => psm_get_lang('config', 'sms_gateway'), - 'label_sms_gateway_mosms' => psm_get_lang('config', 'sms_gateway_mosms'), - 'label_sms_gateway_mollie' => psm_get_lang('config', 'sms_gateway_mollie'), - 'label_sms_gateway_spryng' => psm_get_lang('config', 'sms_gateway_spryng'), - 'label_sms_gateway_inetworx' => psm_get_lang('config', 'sms_gateway_inetworx'), - 'label_sms_gateway_clickatell' => psm_get_lang('config', 'sms_gateway_clickatell'), - 'label_sms_gateway_textmarketer' => psm_get_lang('config', 'sms_gateway_textmarketer'), - 'label_sms_gateway_username' => psm_get_lang('config', 'sms_gateway_username'), - 'label_sms_gateway_password' => psm_get_lang('config', 'sms_gateway_password'), - 'label_sms_from' => psm_get_lang('config', 'sms_from'), - 'label_alert_type' => psm_get_lang('config', 'alert_type'), - 'label_alert_type_description' => psm_get_lang('config', 'alert_type_description'), - 'label_alert_type_status' => psm_get_lang('config', 'alert_type_status'), - 'label_alert_type_offline' => psm_get_lang('config', 'alert_type_offline'), - 'label_alert_type_always' => psm_get_lang('config', 'alert_type_always'), - 'label_log_status' => psm_get_lang('config', 'log_status'), - 'label_log_status_description' => psm_get_lang('config', 'log_status_description'), - 'label_log_email' => psm_get_lang('config', 'log_email'), - 'label_log_sms' => psm_get_lang('config', 'log_sms'), - 'label_auto_refresh' => psm_get_lang('config', 'auto_refresh'), - 'label_auto_refresh_servers' => psm_get_lang('config', 'auto_refresh_servers'), - 'label_seconds' => psm_get_lang('config', 'seconds'), - 'label_save' => psm_get_lang('system', 'save'), - ) - ); + /** + * Execute email test + * + * @todo move test to separate class + */ + protected function testEmail() { + $mail = psm_build_mail(); + $message = psm_get_lang('config', 'test_message'); + $mail->Subject = psm_get_lang('config', 'test_subject'); + $mail->Priority = 1; + $mail->Body = $message; + $mail->AltBody = str_replace('
', "\n", $message); + $user = $this->user->getUser(); + $mail->AddAddress($user->email, $user->name); + if($mail->Send()) { + $this->addMessage(psm_get_lang('config', 'email_sent'), 'success'); + } else { + $this->addMessage(psm_get_lang('config', 'email_error') . ': ' . $mail->ErrorInfo, 'error'); + } + } - return parent::createHTMLLabels(); + /** + * Execute SMS test + * + * @todo move test to separate class + */ + protected function testSMS() { + $sms = psm_build_sms(); + if($sms) { + $user = $this->user->getUser(); + if(empty($user->mobile)) { + $this->addMessage(psm_get_lang('config', 'sms_error_nomobile'), 'error'); + } else { + $sms->addRecipients($user->mobile); + if($sms->sendSMS(psm_get_lang('config', 'test_message'))) { + $this->addMessage(psm_get_lang('config', 'sms_sent'), 'success'); + } else { + $this->addMessage(psm_get_lang('config', 'sms_error'), 'error'); + } + } + } + } + + /** + * Execute pushover test + * + * @todo move test to separate class + */ + protected function testPushover() { + $pushover = psm_build_pushover(); + $pushover->setDebug(true); + $user = $this->user->getUser(); + $api_token = psm_get_conf('pushover_api_token'); + + if(empty($api_token)) { + $this->addMessage(psm_get_lang('config', 'pushover_error_noapp'), 'error'); + } elseif(empty($user->pushover_key)) { + $this->addMessage(psm_get_lang('config', 'pushover_error_nokey'), 'error'); + } else { + $pushover->setPriority(0); + $pushover->setTitle(psm_get_lang('config', 'test_subject')); + $pushover->setMessage(psm_get_lang('config', 'test_message')); + $pushover->setUser($user->pushover_key); + if($user->pushover_device != '') { + $pushover->setDevice($user->pushover_device); + } + $result = $pushover->send(); + + if(isset($result['output']->status) && $result['output']->status == 1) { + $this->addMessage(psm_get_lang('config', 'pushover_sent'), 'success'); + } else { + if(isset($result['output']->errors->error)) { + $error = $result['output']->errors->error; + } else { + $error = 'Unknown'; + } + $this->addMessage(sprintf(psm_get_lang('config', 'pushover_error'), $error), 'error'); + } + } + } + + protected function getLabels() { + return array( + 'label_tab_email' => psm_get_lang('config', 'tab_email'), + 'label_tab_sms' => psm_get_lang('config', 'tab_sms'), + 'label_tab_pushover' => psm_get_lang('config', 'tab_pushover'), + 'label_settings_email' => psm_get_lang('config', 'settings_email'), + 'label_settings_sms' => psm_get_lang('config', 'settings_sms'), + 'label_settings_pushover' => psm_get_lang('config', 'settings_pushover'), + 'label_settings_notification' => psm_get_lang('config', 'settings_notification'), + 'label_settings_log' => psm_get_lang('config', 'settings_log'), + 'label_general' => psm_get_lang('config', 'general'), + 'label_language' => psm_get_lang('config', 'language'), + 'label_show_update' => psm_get_lang('config', 'show_update'), + 'label_email_status' => psm_get_lang('config', 'email_status'), + 'label_email_from_email' => psm_get_lang('config', 'email_from_email'), + 'label_email_from_name' => psm_get_lang('config', 'email_from_name'), + 'label_email_smtp' => psm_get_lang('config', 'email_smtp'), + 'label_email_smtp_host' => psm_get_lang('config', 'email_smtp_host'), + 'label_email_smtp_port' => psm_get_lang('config', 'email_smtp_port'), + 'label_email_smtp_username' => psm_get_lang('config', 'email_smtp_username'), + 'label_email_smtp_password' => psm_get_lang('config', 'email_smtp_password'), + 'label_email_smtp_noauth' => psm_get_lang('config', 'email_smtp_noauth'), + 'label_sms_status' => psm_get_lang('config', 'sms_status'), + 'label_sms_gateway' => psm_get_lang('config', 'sms_gateway'), + 'label_sms_gateway_mosms' => psm_get_lang('config', 'sms_gateway_mosms'), + 'label_sms_gateway_mollie' => psm_get_lang('config', 'sms_gateway_mollie'), + 'label_sms_gateway_spryng' => psm_get_lang('config', 'sms_gateway_spryng'), + 'label_sms_gateway_inetworx' => psm_get_lang('config', 'sms_gateway_inetworx'), + 'label_sms_gateway_clickatell' => psm_get_lang('config', 'sms_gateway_clickatell'), + 'label_sms_gateway_textmarketer' => psm_get_lang('config', 'sms_gateway_textmarketer'), + 'label_sms_gateway_smsit' => psm_get_lang('config', 'sms_gateway_smsit'), + 'label_sms_gateway_smsglobal' => psm_get_lang('config', 'sms_gateway_smsglobal'), + 'label_sms_gateway_username' => psm_get_lang('config', 'sms_gateway_username'), + 'label_sms_gateway_password' => psm_get_lang('config', 'sms_gateway_password'), + 'label_sms_from' => psm_get_lang('config', 'sms_from'), + 'label_pushover_description' => psm_get_lang('config', 'pushover_description'), + 'label_pushover_status' => psm_get_lang('config', 'pushover_status'), + 'label_pushover_clone_app' => psm_get_lang('config', 'pushover_clone_app'), + 'pushover_clone_url' => PSM_PUSHOVER_CLONE_URL, + 'label_pushover_api_token' => psm_get_lang('config', 'pushover_api_token'), + 'label_pushover_api_token_description' => sprintf( + psm_get_lang('config', 'pushover_api_token_description'), + PSM_PUSHOVER_CLONE_URL + ), + 'label_alert_type' => psm_get_lang('config', 'alert_type'), + 'label_alert_type_description' => psm_get_lang('config', 'alert_type_description'), + 'label_alert_type_status' => psm_get_lang('config', 'alert_type_status'), + 'label_alert_type_offline' => psm_get_lang('config', 'alert_type_offline'), + 'label_alert_type_always' => psm_get_lang('config', 'alert_type_always'), + 'label_log_status' => psm_get_lang('config', 'log_status'), + 'label_log_status_description' => psm_get_lang('config', 'log_status_description'), + 'label_log_email' => psm_get_lang('config', 'log_email'), + 'label_log_sms' => psm_get_lang('config', 'log_sms'), + 'label_log_pushover' => psm_get_lang('config', 'log_pushover'), + 'label_auto_refresh' => psm_get_lang('config', 'auto_refresh'), + 'label_auto_refresh_servers' => psm_get_lang('config', 'auto_refresh_servers'), + 'label_seconds' => psm_get_lang('config', 'seconds'), + 'label_save' => psm_get_lang('system', 'save'), + 'label_test' => psm_get_lang('config', 'test'), + 'label_log_retention_period' => psm_get_lang('config', 'log_retention_period'), + 'label_log_retention_period_description' => psm_get_lang('config', 'log_retention_period_description'), + 'label_log_retention_days' => psm_get_lang('config', 'log_retention_days'), + ); } } diff --git a/src/psm/Module/ControllerInterface.class.php b/src/psm/Module/ControllerInterface.class.php index 14a63abf..8889d55c 100644 --- a/src/psm/Module/ControllerInterface.class.php +++ b/src/psm/Module/ControllerInterface.class.php @@ -28,11 +28,10 @@ namespace psm\Module; use psm\Service\Database; -use psm\Service\Template; interface ControllerInterface { - public function __construct(Database $db, Template $tpl); + public function __construct(Database $db, \Twig_Environment $twig); /** * Initialize the module diff --git a/src/psm/Module/Error/Controller/ErrorController.class.php b/src/psm/Module/Error/Controller/ErrorController.class.php new file mode 100644 index 00000000..f4c2c061 --- /dev/null +++ b/src/psm/Module/Error/Controller/ErrorController.class.php @@ -0,0 +1,56 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1 + **/ + +namespace psm\Module\Error\Controller; +use psm\Module\AbstractController; +use psm\Service\Database; + +class ErrorController extends AbstractController { + + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); + + $this->setMinUserLevelRequired(PSM_USER_ANONYMOUS); + + $this->setActions(array( + '401', + ), '401'); + } + + /** + * 401 error page + * + * @return string + */ + protected function execute401() { + return $this->twig->render('module/error/401.tpl.html', array( + 'label_title' => psm_get_lang('error', '401_unauthorized'), + 'label_description' => psm_get_lang('error', '401_unauthorized_description'), + )); + } +} diff --git a/src/psm/Module/Error/ErrorModule.class.php b/src/psm/Module/Error/ErrorModule.class.php new file mode 100644 index 00000000..03652230 --- /dev/null +++ b/src/psm/Module/Error/ErrorModule.class.php @@ -0,0 +1,39 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1 + **/ + +namespace psm\Module\Error; +use psm\Module\ModuleInterface; + +class ErrorModule implements ModuleInterface { + public function getControllers() { + return array( + 'error' => __NAMESPACE__ . '\Controller\ErrorController', + ); + + } +} diff --git a/src/psm/Module/Install/Controller/InstallController.class.php b/src/psm/Module/Install/Controller/InstallController.class.php index 7b4006f7..a5a8b3d5 100644 --- a/src/psm/Module/Install/Controller/InstallController.class.php +++ b/src/psm/Module/Install/Controller/InstallController.class.php @@ -29,7 +29,6 @@ namespace psm\Module\Install\Controller; use psm\Module\AbstractController; use psm\Service\Database; -use psm\Service\Template; class InstallController extends AbstractController { @@ -45,8 +44,8 @@ class InstallController extends AbstractController { */ protected $path_config_old; - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->setMinUserLevelRequired(PSM_USER_ANONYMOUS); $this->addMenu(false); @@ -57,33 +56,14 @@ class InstallController extends AbstractController { $this->setActions(array( 'index', 'config', 'install' ), 'index'); - } - protected function createHTML() { - $html_results = ''; - if(!empty($this->messages)) { - $this->tpl->newTemplate('install_results', 'install/install.tpl.html'); - $this->tpl->addTemplateDataRepeat('install_results', 'resultmsgs', $this->messages); - $html_results = $this->tpl->getTemplate('install_results'); - $this->messages = array(); - } - $tpl_id = $this->getTemplateId(); - $this->setTemplateId('install', 'install/install.tpl.html'); - - $this->tpl->addTemplateData($this->getTemplateId(), array( - 'html_install' => $this->tpl->getTemplate($tpl_id), - 'html_results' => $html_results, - )); - - return parent::createHTML(); + $this->twig->addGlobal('subtitle', psm_get_lang('system,', 'install')); } /** * Say hi to our new user */ protected function executeIndex() { - $this->setTemplateId('install_index', 'install/install.tpl.html'); - // build prerequisites $errors = 0; @@ -110,13 +90,17 @@ class InstallController extends AbstractController { if($errors > 0) { $this->addMessage($errors . ' error(s) have been encountered. Please fix them and refresh this page.', 'error'); } + + return $this->twig->render('module/install/index.tpl.html', array( + 'messages' => $this->getMessages() + )); } /** * Help the user create a new config file */ protected function executeConfig() { - $this->setTemplateId('install_config_new', 'install/install.tpl.html'); + $tpl_name = 'module/install/config_new.tpl.html'; $tpl_data = array(); if(!defined('PSM_DB_PREFIX')) { @@ -168,8 +152,7 @@ class InstallController extends AbstractController { $this->addMessage('Configuration file written successfully.', 'success'); } else { $this->addMessage('Config file is not writable, we cannot save it for you.', 'error'); - $this->tpl->newTemplate('install_config_new_copy', 'install/install.tpl.html'); - $tpl_data['html_config_copy'] = $this->tpl->getTemplate('install_config_new_copy'); + $tpl_data['include_config_new_copy'] = true; $tpl_data['php_config'] = $config_php; } } else { @@ -185,14 +168,14 @@ class InstallController extends AbstractController { if(version_compare($this->getPreviousVersion(), '3.0.0', '<')) { // upgrade from before 3.0, does not have passwords yet.. create new user first $this->addMessage('Your current version does not have an authentication system, but since v3.0 access to the monitor is restricted by user accounts. Please set up a new account to be able to login after the upgrade, and which you can use to change the passwords for your other accounts.', 'info'); - $this->setTemplateId('install_config_new_user', 'install/install.tpl.html'); + $tpl_name = 'module/install/config_new_user.tpl.html'; } else { - $this->setTemplateId('install_config_upgrade', 'install/install.tpl.html'); + $tpl_name = 'module/install/config_upgrade.tpl.html'; $tpl_data['version'] = PSM_VERSION; } } else { // fresh install ahead - $this->setTemplateId('install_config_new_user', 'install/install.tpl.html'); + $tpl_name = 'module/install/config_new_user.tpl.html'; $tpl_data['username'] = (isset($_POST['username'])) ? $_POST['username'] : ''; $tpl_data['email'] = (isset($_POST['email'])) ? $_POST['email'] : ''; @@ -201,7 +184,8 @@ class InstallController extends AbstractController { $this->addMessage('Configuration file found, but unable to connect to MySQL. Please check your information.', 'error'); } } - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + $tpl_data['messages'] = $this->getMessages(); + return $this->twig->render($tpl_name, $tpl_data); } /** @@ -211,6 +195,8 @@ class InstallController extends AbstractController { if(!defined('PSM_DB_PREFIX') || !$this->db->status()) { return $this->executeConfig(); } + $add_user = false; + // check if user submitted username + password in previous step // this would only be the case for new installs, and install from // before 3.0 @@ -220,7 +206,10 @@ class InstallController extends AbstractController { 'password' => psm_POST('password'), 'password_repeat' => psm_POST('password_repeat'), 'email' => psm_POST('email', ''), + 'mobile' => '', 'level' => PSM_USER_ADMIN, + 'pushover_key' => '', + 'pushover_device' => '', ); $validator = new \psm\Util\User\UserValidator($this->user); @@ -275,7 +264,9 @@ class InstallController extends AbstractController { } } - $this->setTemplateId('install_success', 'install/install.tpl.html'); + return $this->twig->render('module/install/success.tpl.html', array( + 'messages' => $this->getMessages() + )); } /** @@ -295,7 +286,6 @@ class InstallController extends AbstractController { ); $config .= $line; } - $config .= "?>".PHP_EOL; if(is_writeable($this->path_config)) { file_put_contents($this->path_config, $config); return true; @@ -358,15 +348,4 @@ class InstallController extends AbstractController { return $version_from; } } - - protected function createHTMLLabels() { - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'subtitle' => psm_get_lang('system', 'install'), - ) - ); - - return parent::createHTMLLabels(); - } } diff --git a/src/psm/Module/ModuleInterface.class.php b/src/psm/Module/ModuleInterface.class.php index 206f24fb..e26e9d2a 100644 --- a/src/psm/Module/ModuleInterface.class.php +++ b/src/psm/Module/ModuleInterface.class.php @@ -27,8 +27,6 @@ **/ namespace psm\Module; -use psm\Service\Database; -use psm\Service\Template; interface ModuleInterface { diff --git a/src/psm/Module/Server/Controller/AbstractServerController.class.php b/src/psm/Module/Server/Controller/AbstractServerController.class.php index 4a763b2f..d5c773b2 100644 --- a/src/psm/Module/Server/Controller/AbstractServerController.class.php +++ b/src/psm/Module/Server/Controller/AbstractServerController.class.php @@ -29,12 +29,11 @@ namespace psm\Module\Server\Controller; use psm\Module\AbstractController; use psm\Service\Database; -use psm\Service\Template; abstract class AbstractServerController extends AbstractController { - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); } /** @@ -73,8 +72,10 @@ abstract class AbstractServerController extends AbstractController { `s`.`active`, `s`.`email`, `s`.`sms`, + `s`.`pushover`, `s`.`warning_threshold`, - `s`.`warning_threshold_counter` + `s`.`warning_threshold_counter`, + `s`.`timeout` FROM `".PSM_DB_PREFIX."servers` AS `s` {$sql_join} {$sql_where} @@ -100,11 +101,7 @@ abstract class AbstractServerController extends AbstractController { $server['active'] = psm_get_lang('system', $server['active']); $server['email'] = psm_get_lang('system', $server['email']); $server['sms'] = psm_get_lang('system', $server['sms']); - $server['url_view'] = psm_build_url(array( - 'mod' => 'server', - 'action' => 'view', - 'id' => $server['server_id'], - )); + $server['pushover'] = psm_get_lang('system', $server['pushover']); if($server['status'] == 'on' && $server['warning_threshold_counter'] > 0) { $server['status'] = 'warning'; @@ -112,7 +109,17 @@ abstract class AbstractServerController extends AbstractController { $server['error'] = htmlentities($server['error']); $server['type'] = psm_get_lang('servers', 'type_' . $server['type']); + $server['timeout'] = ($server['timeout'] > 0) ? $server['timeout'] : PSM_CURL_TIMEOUT; + + $url_actions = array('delete', 'edit', 'view'); + foreach($url_actions as $action) { + $server['url_' . $action] = psm_build_url(array( + 'mod' => 'server', + 'action' => $action, + 'id' => $server['server_id'], + )); + } return $server; } -} \ No newline at end of file +} diff --git a/src/psm/Module/Server/Controller/LogController.class.php b/src/psm/Module/Server/Controller/LogController.class.php index 65e617bc..2b6e465f 100644 --- a/src/psm/Module/Server/Controller/LogController.class.php +++ b/src/psm/Module/Server/Controller/LogController.class.php @@ -27,15 +27,14 @@ namespace psm\Module\Server\Controller; use psm\Service\Database; -use psm\Service\Template; /** * Log module. Create the page to view previous log messages */ class LogController extends AbstractServerController { - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->setActions('index', 'index'); } @@ -44,12 +43,22 @@ class LogController extends AbstractServerController { * Prepare the template with a list of all log entries */ protected function executeIndex() { - $this->setTemplateId('server_log_list', 'server/log.tpl.html'); - - $entries = array(); - $entries['status'] = $this->getEntries('status'); - $entries['email'] = $this->getEntries('email'); - $entries['sms'] = $this->getEntries('sms'); + $this->twig->addGlobal('subtitle', psm_get_lang('menu', 'server_log')); + $tpl_data = array( + 'label_status' => psm_get_lang('log', 'status'), + 'label_email' => psm_get_lang('log', 'email'), + 'label_sms' => psm_get_lang('log', 'sms'), + 'label_pushover' => psm_get_lang('log', 'pushover'), + 'label_title' => psm_get_lang('log', 'title'), + 'label_server' => psm_get_lang('servers', 'server'), + 'label_type' => psm_get_lang('log', 'type'), + 'label_message' => psm_get_lang('system', 'message'), + 'label_date' => psm_get_lang('system', 'date'), + 'label_users' => ucfirst(psm_get_lang('menu', 'user')), + 'label_no_logs' => psm_get_lang('log', 'no_logs'), + 'tabs' => array(), + ); + $log_types = array('status', 'email', 'sms', 'pushover'); // get users $users = $this->db->select(PSM_DB_PREFIX.'users', null, array('user_id','name')); @@ -59,42 +68,50 @@ class LogController extends AbstractServerController { $users_labels[$user['user_id']] = $user['name']; } - foreach($entries as $key => $records) { + foreach($log_types as $key) { + $records = $this->getEntries($key); $log_count = count($records); + $tab_data = array( + 'id' => $key, + 'has_users' => ($key == 'status') ? false : true, + 'no_logs' => ($log_count == 0) ? true : false, + 'tab_active' => ($key == 'status') ? 'active' : '', + ); + for ($x = 0; $x < $log_count; $x++) { - $records[$x]['class'] = ($x & 1) ? 'odd' : 'even'; - $records[$x]['users'] = ''; - $records[$x]['server'] = $records[$x]['label'] . ' (' . $records[$x]['label_adv'] . ')'; - $records[$x]['datetime_format'] = psm_date($records[$x]['datetime']); + $record = &$records[$x]; + $record['class'] = ($x & 1) ? 'odd' : 'even'; + $record['users'] = ''; + $record['server'] = $record['label']; + $record['type_icon'] = ($record['server_type'] == 'website') ? 'icon-globe' : 'icon-cog'; + $record['type_title'] = psm_get_lang('servers', 'type_' . $record['server_type']); + $ip = '(' . $record['ip']; + if(!empty($record['port']) && (($record['server_type'] != 'website') || ($record['port'] != 80))) { + $ip .= ':' . $record['port']; + } + $ip .= ')'; + $record['ip'] = $ip; + $record['datetime_format'] = psm_date($record['datetime']); // fix up user list - if($records[$x]['user_id'] == '') continue; - - $users = explode(',', $records[$x]['user_id']); - foreach($users as $user_id) { - if((int) $user_id == 0 || !isset($users_labels[$user_id])) continue; - - $records[$x]['users'] .= '
'.$users_labels[$user_id]; + if(!empty($record['user_id'])) { + $names = array(); + $users = explode(',', $record['user_id']); + foreach($users as $user_id) { + if(isset($users_labels[$user_id])) { + $names[] = $users_labels[$user_id]; + } + } + sort($names); + $record['users'] = implode('
', $names); + $record['user_list'] = implode(' • ', $names); } } - - // add entries to template - $this->tpl->newTemplate('server_log_entries', 'server/log.tpl.html'); - $this->tpl->addTemplateDataRepeat('server_log_entries', 'entries', $records); - $this->tpl->addTemplateData( - 'server_log_entries', - array( - 'logtitle' => $key, - ) - ); - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'content_' . $key => $this->tpl->getTemplate('server_log_entries'), - ) - ); + $tab_data['entries'] = $records; + $tpl_data['tabs'][] = $tab_data; } + return $this->twig->render('module/server/log.tpl.html', $tpl_data); } /** @@ -115,11 +132,9 @@ class LogController extends AbstractServerController { $entries = $this->db->query( 'SELECT '. '`servers`.`label`, '. - 'CONCAT_WS('. - '\':\','. - '`servers`.`ip`, '. - '`servers`.`port`'. - ') AS `label_adv`, '. + '`servers`.`ip`, '. + '`servers`.`port`, '. + '`servers`.`type` AS server_type, '. '`log`.`type`, '. '`log`.`message`, '. '`log`.`datetime`, '. @@ -133,25 +148,4 @@ class LogController extends AbstractServerController { ); return $entries; } - - // override parent::createHTMLLabels() - protected function createHTMLLabels() { - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'subtitle' => psm_get_lang('menu', 'server_log'), - 'label_status' => psm_get_lang('log', 'status'), - 'label_email' => psm_get_lang('log', 'email'), - 'label_sms' => psm_get_lang('log', 'sms'), - 'label_title' => psm_get_lang('log', 'title'), - 'label_server' => psm_get_lang('servers', 'server'), - 'label_type' => psm_get_lang('log', 'type'), - 'label_message' => psm_get_lang('system', 'message'), - 'label_date' => psm_get_lang('system', 'date'), - 'label_users' => ucfirst(psm_get_lang('menu', 'user')), - ) - ); - - return parent::createHTMLLabels(); - } } diff --git a/src/psm/Module/Server/Controller/ServerController.class.php b/src/psm/Module/Server/Controller/ServerController.class.php index ead0cf84..7d635c47 100644 --- a/src/psm/Module/Server/Controller/ServerController.class.php +++ b/src/psm/Module/Server/Controller/ServerController.class.php @@ -27,7 +27,6 @@ namespace psm\Module\Server\Controller; use psm\Service\Database; -use psm\Service\Template; /** * Server module. Add/edit/delete servers, show a list of all servers etc. @@ -40,8 +39,8 @@ class ServerController extends AbstractServerController { */ protected $server_id; - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->server_id = isset($_GET['id']) ? intval($_GET['id']) : 0; @@ -53,29 +52,32 @@ class ServerController extends AbstractServerController { $this->setMinUserLevelRequiredForAction(PSM_USER_ADMIN, array( 'delete', 'edit', 'save' )); + $this->twig->addGlobal('subtitle', psm_get_lang('menu', 'server')); } /** * Prepare the template to show a list of all servers */ protected function executeIndex() { - $this->setTemplateId('server_list', 'server/server.tpl.html'); - $sidebar = new \psm\Util\Module\Sidebar($this->tpl); + $tpl_data = $this->getLabels(); + $tpl_data['user_level'] = $this->user->getUserLevel(); + $sidebar = new \psm\Util\Module\Sidebar($this->twig); $this->setSidebar($sidebar); // check if user is admin, in that case we add the buttons if($this->user->getUserLevel() == PSM_USER_ADMIN) { + $modal = new \psm\Util\Module\Modal($this->twig, 'delete', \psm\Util\Module\Modal::MODAL_TYPE_DANGER); + $this->addModal($modal); + $modal->setTitle(psm_get_lang('servers', 'delete_title')); + $modal->setMessage(psm_get_lang('servers', 'delete_message')); + $modal->setOKButtonLabel(psm_get_lang('system', 'delete')); + $sidebar->addButton( 'add_new', psm_get_lang('system', 'add_new'), psm_build_url(array('mod' => 'server', 'action' => 'edit')), 'plus icon-white', 'success' ); - // get the action buttons per server - $this->tpl->newTemplate('server_list_admin_actions', 'server/server.tpl.html'); - $html_actions = $this->tpl->getTemplate('server_list_admin_actions'); - } else { - $html_actions = ''; } $sidebar->addButton( @@ -85,45 +87,65 @@ class ServerController extends AbstractServerController { 'refresh' ); - // we need an array for our template magic (see below): - $html_actions = array('html_actions' => $html_actions); + $icons = array( + 'email' => 'icon-envelope', + 'sms' => 'icon-mobile', + 'pushover' => 'icon-pushover', + ); $servers = $this->getServers(); $server_count = count($servers); for ($x = 0; $x < $server_count; $x++) { - // template magic: push the actions html to the front of the server array - // so the template handler will add it first. that way the other server vars - // will also be replaced in the html_actions template itself - $servers[$x] = $html_actions + $servers[$x]; $servers[$x]['class'] = ($x & 1) ? 'odd' : 'even'; if($servers[$x]['type'] == 'website') { + $servers[$x]['type_icon'] = 'icon-globe'; // add link to label - $servers[$x]['ip'] = ''.$servers[$x]['ip'].''; + $ip = $servers[$x]['ip']; + if(!empty($servers[$x]['port']) && ($servers[$x]['port'] != 80)) { + $ip .= ' : ' . $servers[$x]['port']; + } + $servers[$x]['ip'] = ''.$ip.''; + $servers[$x]['ip_short'] = $ip; + } else { + $servers[$x]['type_icon'] = 'icon-cog'; + $servers[$x]['ip_short'] = $servers[$x]['ip'] . ' : ' . $servers[$x]['port']; } + if(($servers[$x]['active'] == 'yes')) { + $servers[$x]['active_icon'] = 'icon-eye-open'; + $servers[$x]['active_title'] = psm_get_lang('servers', 'monitoring'); + + foreach($icons as $i_id => $i_icon) { + if(psm_get_conf($i_id . '_status') && $servers[$x][$i_id] == 'yes') { + $servers[$x][$i_id . '_icon'] = $i_icon; + } + } + } else { + $servers[$x]['active_icon'] = 'icon-eye-close'; + $servers[$x]['active_title'] = psm_get_lang('servers', 'no_monitoring'); + } + $servers[$x] = $this->formatServer($servers[$x]); } - // add servers to template - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'servers', $servers); + $tpl_data['servers'] = $servers; + return $this->twig->render('module/server/server/list.tpl.html', $tpl_data); } /** * Prepare the template to show the update screen for a single server */ protected function executeEdit() { - $this->setTemplateId('server_update', 'server/server.tpl.html'); $back_to = isset($_GET['back_to']) ? $_GET['back_to'] : ''; - $tpl_data = array( - // form url: - 'url_save' => psm_build_url(array( - 'mod' => 'server', - 'action' => 'save', - 'id' => $this->server_id, - 'back_to' => $back_to, - )), - ); + $tpl_data = $this->getLabels(); + $tpl_data['edit_server_id'] = $this->server_id; + $tpl_data['url_save'] = psm_build_url(array( + 'mod' => 'server', + 'action' => 'save', + 'id' => $this->server_id, + 'back_to' => $back_to, + )); // depending on where the user came from, add the go back url: if($back_to == 'view' && $this->server_id > 0) { @@ -132,85 +154,153 @@ class ServerController extends AbstractServerController { $tpl_data['url_go_back'] = psm_build_url(array('mod' => 'server')); } + $tpl_data['users'] = $this->db->select(PSM_DB_PREFIX.'users', null, array('user_id', 'name'), '', 'name'); + switch($this->server_id) { case 0: // insert mode $tpl_data['titlemode'] = psm_get_lang('system', 'insert'); - $tpl_data['edit_server_id'] = '0'; $tpl_data['edit_value_warning_threshold'] = '1'; + + $edit_server = $_POST; break; default: // edit mode // get server entry $edit_server = $this->getServers($this->server_id); - if (empty($edit_server)) { - $this->addMessage('Invalid server', 'error'); + if(empty($edit_server)) { + $this->addMessage(psm_get_lang('servers', 'error_server_no_match'), 'error'); return $this->initializeAction('index'); } + $tpl_data['titlemode'] = psm_get_lang('system', 'edit') . ' ' . $edit_server['label']; - $tpl_data = array_merge($tpl_data, array( - 'titlemode' => psm_get_lang('system', 'edit') . ' ' . $edit_server['label'], - 'edit_server_id' => $edit_server['server_id'], - 'edit_value_label' => $edit_server['label'], - 'edit_value_ip' => $edit_server['ip'], - 'edit_value_port' => $edit_server['port'], - 'edit_value_pattern' => $edit_server['pattern'], - 'edit_value_warning_threshold' => $edit_server['warning_threshold'], - 'edit_type_selected_' . $edit_server['type'] => 'selected="selected"', - 'edit_active_selected_' . $edit_server['active'] => 'selected="selected"', - 'edit_email_selected_' . $edit_server['email'] => 'selected="selected"', - 'edit_sms_selected_' . $edit_server['sms'] => 'selected="selected"', - )); + $user_idc_selected = $this->getServerUsers($this->server_id); + foreach($tpl_data['users'] as &$user) { + if(in_array($user['user_id'], $user_idc_selected)) { + $user['edit_selected'] = 'selected="selected"'; + } + } break; } - $this->tpl->addTemplateData( - $this->getTemplateId(), - $tpl_data - ); + if(!empty($edit_server)) { + // attempt to prefill previously posted fields + foreach($edit_server as $key => $value) { + $edit_server[$key] = psm_POST($key, $value); + } + + $tpl_data = array_merge($tpl_data, array( + 'edit_value_label' => $edit_server['label'], + 'edit_value_ip' => $edit_server['ip'], + 'edit_value_port' => $edit_server['port'], + 'edit_value_timeout' => $edit_server['timeout'], + 'default_value_timeout' => PSM_CURL_TIMEOUT, + 'edit_value_pattern' => $edit_server['pattern'], + 'edit_value_warning_threshold' => $edit_server['warning_threshold'], + 'edit_type_selected_' . $edit_server['type'] => 'selected="selected"', + 'edit_active_selected_' . $edit_server['active'] => 'selected="selected"', + 'edit_email_selected_' . $edit_server['email'] => 'selected="selected"', + 'edit_sms_selected_' . $edit_server['sms'] => 'selected="selected"', + 'edit_pushover_selected_' . $edit_server['pushover'] => 'selected="selected"', + )); + } + + $notifications = array('email', 'sms', 'pushover'); + foreach($notifications as $notification) { + if(psm_get_conf($notification . '_status') == 0) { + $tpl_data['warning_' . $notification] = true; + $tpl_data['control_class_' . $notification] = 'warning'; + $tpl_data['label_warning_' . $notification] = psm_get_lang( + 'servers', 'warning_notifications_disabled_' . $notification + ); + } else { + $tpl_data['warning_' . $notification] = false; + } + } + + return $this->twig->render('module/server/server/update.tpl.html', $tpl_data); } /** * Executes the saving of one of the servers */ protected function executeSave() { - // check for add/edit mode - if(isset($_POST['label']) && isset($_POST['ip']) && isset($_POST['port'])) { - $clean = array( - 'label' => strip_tags($_POST['label']), - 'ip' => strip_tags($_POST['ip']), - 'port' => intval($_POST['port']), - 'type' => in_array($_POST['type'], array('website', 'service')) ? $_POST['type'] : 'website', - 'pattern' => $_POST['pattern'], - 'warning_threshold' => intval($_POST['warning_threshold']), - 'active' => in_array($_POST['active'], array('yes', 'no')) ? $_POST['active'] : 'no', - 'email' => in_array($_POST['email'], array('yes', 'no')) ? $_POST['email'] : 'no', - 'sms' => in_array($_POST['sms'], array('yes', 'no')) ? $_POST['sms'] : 'no', - ); + if(empty($_POST)) { + // dont process anything if no data has been posted + return $this->executeIndex(); + } + $clean = array( + 'label' => trim(strip_tags(psm_POST('label', ''))), + 'ip' => trim(strip_tags(psm_POST('ip', ''))), + 'timeout' => (isset($_POST['timeout']) && intval($_POST['timeout']) > 0) ? intval($_POST['timeout']) : null, + 'port' => intval(psm_POST('port', 0)), + 'type' => psm_POST('type', ''), + 'pattern' => psm_POST('pattern', ''), + 'warning_threshold' => intval(psm_POST('warning_threshold', 0)), + 'active' => in_array($_POST['active'], array('yes', 'no')) ? $_POST['active'] : 'no', + 'email' => in_array($_POST['email'], array('yes', 'no')) ? $_POST['email'] : 'no', + 'sms' => in_array($_POST['sms'], array('yes', 'no')) ? $_POST['sms'] : 'no', + 'pushover' => in_array($_POST['pushover'], array('yes', 'no')) ? $_POST['pushover'] : 'no', + ); + // make sure websites start with http:// + if($clean['type'] == 'website' && substr($clean['ip'], 0, 4) != 'http') { + $clean['ip'] = 'http://' . $clean['ip']; + } - // check for edit or add + // validate the lot + $server_validator = new \psm\Util\Server\ServerValidator($this->db); + + try { if($this->server_id > 0) { - // edit - $this->db->save( - PSM_DB_PREFIX.'servers', - $clean, - array('server_id' => $this->server_id) - ); - $this->addMessage(psm_get_lang('servers', 'updated'), 'success'); - } else { - // add - $clean['status'] = 'on'; - $this->server_id = $this->db->save(PSM_DB_PREFIX.'servers', $clean); - $this->addMessage(psm_get_lang('servers', 'inserted'), 'success'); + $server_validator->serverId($this->server_id); } + $server_validator->label($clean['label']); + $server_validator->type($clean['type']); + $server_validator->ip($clean['ip'], $clean['type']); + $server_validator->warningThreshold($clean['warning_threshold']); + } catch(\InvalidArgumentException $ex) { + $this->addMessage(psm_get_lang('servers', 'error_' . $ex->getMessage()), 'error'); + return $this->executeEdit(); + } + + // check for edit or add + if($this->server_id > 0) { + // edit + $this->db->save( + PSM_DB_PREFIX.'servers', + $clean, + array('server_id' => $this->server_id) + ); + $this->addMessage(psm_get_lang('servers', 'updated'), 'success'); + } else { + // add + $clean['status'] = 'on'; + $this->server_id = $this->db->save(PSM_DB_PREFIX.'servers', $clean); + $this->addMessage(psm_get_lang('servers', 'inserted'), 'success'); + } + + // update users + $user_idc = psm_POST('user_id', array()); + $user_idc_save = array(); + + foreach($user_idc as $user_id) { + $user_idc_save[] = array( + 'user_id' => intval($user_id), + 'server_id' => intval($this->server_id), + ); + } + $this->db->delete(PSM_DB_PREFIX.'users_servers', array('server_id' => $this->server_id)); + if(!empty($user_idc_save)) { + // add all new users + $this->db->insertMultiple(PSM_DB_PREFIX.'users_servers', $user_idc_save); } $back_to = isset($_GET['back_to']) ? $_GET['back_to'] : 'index'; if($back_to == 'view') { - $this->initializeAction('view'); + return $this->initializeAction('view'); } else { - $this->initializeAction('index'); + return $this->initializeAction('index'); } } @@ -229,9 +319,9 @@ class ServerController extends AbstractServerController { $this->db->delete(PSM_DB_PREFIX.'servers_uptime', array('server_id' => $id)); $this->db->delete(PSM_DB_PREFIX.'servers_history', array('server_id' => $id)); } - $this->addMessage(psm_get_lang('system', 'deleted'), 'success'); + $this->addMessage(psm_get_lang('servers', 'deleted'), 'success'); } - $this->initializeAction('index'); + return $this->initializeAction('index'); } /** @@ -247,35 +337,37 @@ class ServerController extends AbstractServerController { return $this->initializeAction('index'); } - $this->setTemplateId('server_view', 'server/view.tpl.html'); - - $tpl_data = $this->formatServer($server); + $tpl_data = $this->getLabels(); + $tpl_data = array_merge($tpl_data, $this->formatServer($server)); // create history HTML - $history = new \psm\Util\Server\HistoryGraph($this->db, $this->tpl); + $history = new \psm\Util\Server\HistoryGraph($this->db, $this->twig); $tpl_data['html_history'] = $history->createHTML($this->server_id); // add edit/delete buttons for admins if($this->user->getUserLevel() == PSM_USER_ADMIN) { - $tpl_id_actions = 'server_view_admin_actions'; - $this->tpl->newTemplate($tpl_id_actions, 'server/view.tpl.html'); - $tpl_data['html_actions'] = $this->tpl->getTemplate($tpl_id_actions); + $tpl_data['has_admin_actions'] = true; $tpl_data['url_edit'] = psm_build_url(array('mod' => 'server', 'action' => 'edit', 'id' => $this->server_id, 'back_to' => 'view')); + + $modal = new \psm\Util\Module\Modal($this->twig, 'delete', \psm\Util\Module\Modal::MODAL_TYPE_DANGER); + $this->addModal($modal); + $modal->setTitle(psm_get_lang('servers', 'delete_title')); + $modal->setMessage(psm_get_lang('servers', 'delete_message')); + $modal->setOKButtonLabel(psm_get_lang('system', 'delete')); } // add all available servers to the menu $servers = $this->getServers(); - $options = array(); + $tpl_data['options'] = array(); foreach($servers as $i => $server_available) { - $options[] = array( + $tpl_data['options'][] = array( 'class_active' => ($server_available['server_id'] == $this->server_id) ? 'active' : '', 'url' => psm_build_url(array('mod' => 'server', 'action' => 'view', 'id' => $server_available['server_id'])), 'label' => $server_available['label'], ); } - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'options', $options); - $sidebar = new \psm\Util\Module\Sidebar($this->tpl); + $sidebar = new \psm\Util\Module\Sidebar($this->twig); $this->setSidebar($sidebar); // check which module the user came from, and add a link accordingly @@ -287,44 +379,61 @@ class ServerController extends AbstractServerController { 'th-list' ); - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + return $this->twig->render('module/server/server/view.tpl.html', $tpl_data); } - // override parent::createHTMLLabels() - protected function createHTMLLabels() { - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'subtitle' => psm_get_lang('menu', 'server'), - 'label_label' => psm_get_lang('servers', 'label'), - 'label_status' => psm_get_lang('menu', 'server_status'), - 'label_domain' => psm_get_lang('servers', 'domain'), - 'label_port' => psm_get_lang('servers', 'port'), - 'label_type' => psm_get_lang('servers', 'type'), - 'label_website' => psm_get_lang('servers', 'type_website'), - 'label_service' => psm_get_lang('servers', 'type_service'), - 'label_type' => psm_get_lang('servers', 'type'), - 'label_pattern' => psm_get_lang('servers', 'pattern'), - 'label_pattern_description' => psm_get_lang('servers', 'pattern_description'), - 'label_last_check' => psm_get_lang('servers', 'last_check'), - 'label_rtime' => psm_get_lang('servers', 'latency'), - 'label_last_online' => psm_get_lang('servers', 'last_online'), - 'label_monitoring' => psm_get_lang('servers', 'monitoring'), - 'label_send_email' => psm_get_lang('servers', 'send_email'), - 'label_send_sms' => psm_get_lang('servers', 'send_sms'), - 'label_warning_threshold' => psm_get_lang('servers', 'warning_threshold'), - 'label_warning_threshold_description' => psm_get_lang('servers', 'warning_threshold_description'), - 'label_action' => psm_get_lang('system', 'action'), - 'label_save' => psm_get_lang('system', 'save'), - 'label_go_back' => psm_get_lang('system', 'go_back'), - 'label_edit' => psm_get_lang('system', 'edit'), - 'label_delete' => psm_get_lang('system', 'delete'), - 'label_yes' => psm_get_lang('system', 'yes'), - 'label_no' => psm_get_lang('system', 'no'), - 'label_add_new' => psm_get_lang('system', 'add_new'), - ) + protected function getLabels() { + return array( + 'label_label' => psm_get_lang('servers', 'label'), + 'label_status' => psm_get_lang('servers', 'status'), + 'label_domain' => psm_get_lang('servers', 'domain'), + 'label_timeout' => psm_get_lang('servers', 'timeout'), + 'label_timeout_description' => psm_get_lang('servers', 'timeout_description'), + 'label_port' => psm_get_lang('servers', 'port'), + 'label_type' => psm_get_lang('servers', 'type'), + 'label_website' => psm_get_lang('servers', 'type_website'), + 'label_service' => psm_get_lang('servers', 'type_service'), + 'label_type' => psm_get_lang('servers', 'type'), + 'label_pattern' => psm_get_lang('servers', 'pattern'), + 'label_pattern_description' => psm_get_lang('servers', 'pattern_description'), + 'label_last_check' => psm_get_lang('servers', 'last_check'), + 'label_rtime' => psm_get_lang('servers', 'latency'), + 'label_last_online' => psm_get_lang('servers', 'last_online'), + 'label_monitoring' => psm_get_lang('servers', 'monitoring'), + 'label_email' => psm_get_lang('servers', 'email'), + 'label_send_email' => psm_get_lang('servers', 'send_email'), + 'label_sms' => psm_get_lang('servers', 'sms'), + 'label_send_sms' => psm_get_lang('servers', 'send_sms'), + 'label_pushover' => psm_get_lang('servers', 'pushover'), + 'label_users' => psm_get_lang('servers', 'users'), + 'label_warning_threshold' => psm_get_lang('servers', 'warning_threshold'), + 'label_warning_threshold_description' => psm_get_lang('servers', 'warning_threshold_description'), + 'label_action' => psm_get_lang('system', 'action'), + 'label_save' => psm_get_lang('system', 'save'), + 'label_go_back' => psm_get_lang('system', 'go_back'), + 'label_edit' => psm_get_lang('system', 'edit'), + 'label_delete' => psm_get_lang('system', 'delete'), + 'label_yes' => psm_get_lang('system', 'yes'), + 'label_no' => psm_get_lang('system', 'no'), + 'label_add_new' => psm_get_lang('system', 'add_new'), ); + } - return parent::createHTMLLabels(); + /** + * Get all user ids for a server + * @param int $server_id + * @return array with ids only + */ + protected function getServerUsers($server_id) { + $users = $this->db->select( + PSM_DB_PREFIX.'users_servers', + array('server_id' => $server_id), + array('user_id') + ); + $result = array(); + foreach($users as $user) { + $result[] = $user['user_id']; + } + return $result; } } diff --git a/src/psm/Module/Server/Controller/StatusController.class.php b/src/psm/Module/Server/Controller/StatusController.class.php index 60673aaf..a98aef23 100644 --- a/src/psm/Module/Server/Controller/StatusController.class.php +++ b/src/psm/Module/Server/Controller/StatusController.class.php @@ -28,17 +28,16 @@ namespace psm\Module\Server\Controller; use psm\Service\Database; -use psm\Service\Template; /** * Status module */ class StatusController extends AbstractServerController { - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); - $this->setActions(array('index'), 'index'); + $this->setActions(array('index', 'saveLayout'), 'index'); } /** @@ -46,14 +45,28 @@ class StatusController extends AbstractServerController { * @todo move the background colurs to the config */ protected function executeIndex() { - $this->setTemplateId('server_status', 'server/status.tpl.html'); + // set background color to black + $this->black_background = true; + $this->twig->addGlobal('subtitle', psm_get_lang('menu', 'server_status')); + + // add header accessories + $layout = $this->user->getUserPref('status_layout', 0); + $layout_data = array( + 'label_last_check' => psm_get_lang('servers', 'last_check'), + 'label_last_online' => psm_get_lang('servers', 'last_online'), + 'label_rtime' => psm_get_lang('servers', 'latency'), + 'block_layout_active' => ($layout == 0) ? 'active' : '', + 'list_layout_active' => ($layout != 0) ? 'active' : '', + ); + $this->setHeaderAccessories($this->twig->render('module/server/status/header.tpl.html', $layout_data)); + $this->addFooter(false); // get the active servers from database $servers = $this->getServers(); - $offline = array(); - $online = array(); + $layout_data['servers_offline'] = array(); + $layout_data['servers_online'] = array(); foreach ($servers as $server) { if($server['active'] == 'no') { @@ -64,40 +77,33 @@ class StatusController extends AbstractServerController { $server['url_view'] = psm_build_url(array('mod' => 'server', 'action' => 'view', 'id' => $server['server_id'], 'back_to' => 'server_status')); if ($server['status'] == "off") { - $offline[$server['server_id']] = $server; + $layout_data['servers_offline'][] = $server; } elseif($server['warning_threshold_counter'] > 0) { $server['class_warning'] = 'warning'; - $offline[$server['server_id']] = $server; + $layout_data['servers_offline'][] = $server; } else { - $online[$server['server_id']] = $server; + $layout_data['servers_online'][] = $server; } } - // add servers to template - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'servers_offline', $offline); - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'servers_online', $online); - - // check if we need to add the auto refresh - $auto_refresh = psm_get_conf('auto_refresh_servers'); - if(intval($auto_refresh) > 0) { - // add it - $this->tpl->newTemplate('main_auto_refresh', 'main.tpl.html'); - $this->tpl->addTemplateData('main_auto_refresh', array('seconds' => $auto_refresh)); - $this->tpl->addTemplateData('main', array('auto_refresh' => $this->tpl->getTemplate('main_auto_refresh'))); + $auto_refresh_seconds = psm_get_conf('auto_refresh_servers'); + if(intval($auto_refresh_seconds) > 0) { + $this->twig->addGlobal('auto_refresh', true); + $this->twig->addGlobal('auto_refresh_seconds', $auto_refresh_seconds); } + return $this->twig->render('module/server/status/index.tpl.html', $layout_data); } - protected function createHTMLLabels() { - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'subtitle' => psm_get_lang('menu', 'server_status'), - 'label_last_check' => psm_get_lang('servers', 'last_check'), - 'label_last_online' => psm_get_lang('servers', 'last_online'), - 'label_rtime' => psm_get_lang('servers', 'latency'), - ) - ); + protected function executeSaveLayout() { + if($this->isXHR()) { + $layout = psm_POST('layout', 0); + $this->user->setUserPref('status_layout', $layout); - return parent::createHTMLLabels(); + $response = new \Symfony\Component\HttpFoundation\JsonResponse(); + $response->setData(array( + 'layout' => $layout, + )); + return $response; + } } } diff --git a/src/psm/Module/Server/Controller/UpdateController.class.php b/src/psm/Module/Server/Controller/UpdateController.class.php index f6f241ff..c3d0fa4f 100644 --- a/src/psm/Module/Server/Controller/UpdateController.class.php +++ b/src/psm/Module/Server/Controller/UpdateController.class.php @@ -30,18 +30,17 @@ namespace psm\Module\Server\Controller; use psm\Module\AbstractController; use psm\Service\Database; -use psm\Service\Template; class UpdateController extends AbstractController { - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->setActions('index', 'index'); } protected function executeIndex() { - $autorun = new \psm\Util\Updater\Autorun($this->db); + $autorun = new \psm\Util\Server\UpdateManager($this->db); $autorun->setUser($this->user); $autorun->run(); diff --git a/src/psm/Module/User/Controller/LoginController.class.php b/src/psm/Module/User/Controller/LoginController.class.php index a363fa2b..5f9a165f 100644 --- a/src/psm/Module/User/Controller/LoginController.class.php +++ b/src/psm/Module/User/Controller/LoginController.class.php @@ -29,12 +29,11 @@ namespace psm\Module\User\Controller; use psm\Module\AbstractController; use psm\Service\Database; -use psm\Service\Template; class LoginController extends AbstractController { - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->setMinUserLevelRequired(PSM_USER_ANONYMOUS); @@ -46,8 +45,6 @@ class LoginController extends AbstractController { } protected function executeLogin() { - $this->setTemplateId('user_login', 'user/login.tpl.html'); - if(isset($_POST['user_name']) && isset($_POST['user_password'])) { $rememberme = (isset($_POST['user_rememberme'])) ? true : false; $result = $this->user->loginWithPostData( @@ -58,7 +55,7 @@ class LoginController extends AbstractController { if($result) { // success login, redirect - header('Location: ' . $_SERVER['REQUEST_URI']); + header('Location: ' . psm_build_url($_SERVER['QUERY_STRING'])); die(); } else { $this->addMessage(psm_get_lang('login', 'error_login_incorrect'), 'error'); @@ -76,15 +73,15 @@ class LoginController extends AbstractController { 'value_rememberme' => (isset($rememberme) && $rememberme) ? 'checked="checked"' : '', ); - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + return $this->twig->render('module/user/login/login.tpl.html', $tpl_data); } /** * Show/process the password forgot form (before the mail) + * + * @return string */ protected function executeForgot() { - $this->setTemplateId('user_login_forgot', 'user/login.tpl.html'); - if(isset($_POST['user_name'])) { $user = $this->user->getUserByUsername($_POST['user_name']); @@ -110,16 +107,13 @@ class LoginController extends AbstractController { 'label_submit' => psm_get_lang('login', 'submit'), 'label_go_back' => psm_get_lang('system', 'go_back'), ); - - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + return $this->twig->render('module/user/login/forgot.tpl.html', $tpl_data); } /** * Show/process the password reset form (after the mail) */ protected function executeReset() { - $this->setTemplateId('user_login_reset', 'user/login.tpl.html'); - $user_id = (isset($_GET['user_id'])) ? intval($_GET['user_id']) : 0; $token = (isset($_GET['token'])) ? $_GET['token'] : ''; @@ -153,8 +147,7 @@ class LoginController extends AbstractController { 'label_go_back' => psm_get_lang('system', 'go_back'), 'value_user_name' => $user->user_name, ); - - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + return $this->twig->render('module/user/login/reset.tpl.html', $tpl_data); } /** diff --git a/src/psm/Module/User/Controller/ProfileController.class.php b/src/psm/Module/User/Controller/ProfileController.class.php index a9d11158..a51ca031 100644 --- a/src/psm/Module/User/Controller/ProfileController.class.php +++ b/src/psm/Module/User/Controller/ProfileController.class.php @@ -28,7 +28,6 @@ namespace psm\Module\User\Controller; use psm\Module\AbstractController; use psm\Service\Database; -use psm\Service\Template; class ProfileController extends AbstractController { @@ -36,10 +35,10 @@ class ProfileController extends AbstractController { * Editable fields for the profile * @var array $profile_fields */ - protected $profile_fields = array('name', 'user_name', 'mobile', 'email'); + protected $profile_fields = array('name', 'user_name', 'mobile', 'pushover_key', 'pushover_device', 'email'); - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->setActions(array( 'index', 'save', @@ -48,13 +47,26 @@ class ProfileController extends AbstractController { /** * Show the profile page + * @return string */ protected function executeIndex() { - $this->setTemplateId('user_profile', 'user/profile.tpl.html'); - + $this->twig->addGlobal('subtitle', psm_get_lang('users', 'profile')); $user = $this->user->getUser(null, true); $tpl_data = array( + 'label_name' => psm_get_lang('users', 'name'), + 'label_user_name' => psm_get_lang('users', 'user_name'), + 'label_password' => psm_get_lang('users', 'password'), + 'label_password_repeat' => psm_get_lang('users', 'password_repeat'), + 'label_level' => psm_get_lang('users', 'level'), + 'label_mobile' => psm_get_lang('users', 'mobile'), + 'label_pushover' => psm_get_lang('users', 'pushover'), + 'label_pushover_description' => psm_get_lang('users', 'pushover_description'), + 'label_pushover_key' => psm_get_lang('users', 'pushover_key'), + 'label_pushover_device' => psm_get_lang('users', 'pushover_device'), + 'label_pushover_device_description' => psm_get_lang('users', 'pushover_device_description'), + 'label_email' => psm_get_lang('users', 'email'), + 'label_save' => psm_get_lang('system', 'save'), 'form_action' => psm_build_url(array( 'mod' => 'user_profile', 'action' => 'save', @@ -65,7 +77,7 @@ class ProfileController extends AbstractController { foreach($this->profile_fields as $field) { $tpl_data[$field] = (isset($user->$field)) ? $user->$field : ''; } - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + return $this->twig->render('module/user/profile.tpl.html', $tpl_data); } /** @@ -119,24 +131,4 @@ class ProfileController extends AbstractController { return $this->executeIndex(); } - - // override parent::createHTMLLabels() - protected function createHTMLLabels() { - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'subtitle' => psm_get_lang('users', 'profile'), - 'label_name' => psm_get_lang('users', 'name'), - 'label_user_name' => psm_get_lang('users', 'user_name'), - 'label_password' => psm_get_lang('users', 'password'), - 'label_password_repeat' => psm_get_lang('users', 'password_repeat'), - 'label_level' => psm_get_lang('users', 'level'), - 'label_mobile' => psm_get_lang('users', 'mobile'), - 'label_email' => psm_get_lang('users', 'email'), - 'label_save' => psm_get_lang('system', 'save'), - ) - ); - - return parent::createHTMLLabels(); - } -} +} \ No newline at end of file diff --git a/src/psm/Module/User/Controller/UserController.class.php b/src/psm/Module/User/Controller/UserController.class.php index 0aca3587..f29fa6c9 100644 --- a/src/psm/Module/User/Controller/UserController.class.php +++ b/src/psm/Module/User/Controller/UserController.class.php @@ -28,7 +28,6 @@ namespace psm\Module\User\Controller; use psm\Module\AbstractController; use psm\Service\Database; -use psm\Service\Template; /** * User module. Add, edit and delete users, or assign @@ -43,14 +42,15 @@ class UserController extends AbstractController { */ protected $user_validator; - function __construct(Database $db, Template $tpl) { - parent::__construct($db, $tpl); + function __construct(Database $db, \Twig_Environment $twig) { + parent::__construct($db, $twig); $this->setMinUserLevelRequired(PSM_USER_ADMIN); $this->setActions(array( 'index', 'edit', 'delete', 'save', ), 'index'); + $this->twig->addGlobal('subtitle', psm_get_lang('menu', 'user')); } public function initialize() { @@ -65,11 +65,12 @@ class UserController extends AbstractController { } /** - * Prepare the template to show a list of all users + * Create HTML to show a list of all users + * + * @return string */ protected function executeIndex() { - $this->setTemplateId('user_list', 'user/user.tpl.html'); - $sidebar = new \psm\Util\Module\Sidebar($this->tpl); + $sidebar = new \psm\Util\Module\Sidebar($this->twig); $this->setSidebar($sidebar); $sidebar->addButton( @@ -79,6 +80,12 @@ class UserController extends AbstractController { 'plus icon-white', 'success' ); + $modal = new \psm\Util\Module\Modal($this->twig, 'delete', \psm\Util\Module\Modal::MODAL_TYPE_DANGER); + $this->addModal($modal); + $modal->setTitle(psm_get_lang('users', 'delete_title')); + $modal->setMessage(psm_get_lang('users', 'delete_message')); + $modal->setOKButtonLabel(psm_get_lang('system', 'delete')); + // build label array for the next loop $servers_labels = array(); foreach ($this->servers as $server) { @@ -88,7 +95,7 @@ class UserController extends AbstractController { $users = $this->db->select( PSM_DB_PREFIX.'users', null, - array('user_id', 'user_name', 'level', 'name', 'mobile', 'email'), + array('user_id', 'user_name', 'level', 'name', 'mobile', 'pushover_key', 'pushover_device', 'email'), null, array('name') ); @@ -96,26 +103,43 @@ class UserController extends AbstractController { foreach($users as $x => &$user) { $user_servers = $this->getUserServers($user['user_id']); $user['class'] = ($x & 1) ? 'odd' : 'even'; + $user['level_text'] = psm_get_lang('users', 'level_' . $user['level']); - $user['emp_servers'] = ''; + $user['emp_servers'] = array(); // fix server list foreach($user_servers as $server_id) { if (!isset($servers_labels[$server_id])) continue; - $user['emp_servers'] .= $servers_labels[$server_id] . '
'; + $user['emp_servers'][] = array( + 'label' => $servers_labels[$server_id] + ); } - $user['emp_servers'] = substr($user['emp_servers'], 0, -5); + + $user['url_delete'] = psm_build_url(array( + 'mod' => 'user', + 'action' => 'delete', + 'id' => $user['user_id'], + )); + $user['url_edit'] = psm_build_url(array( + 'mod' => 'user', + 'action' => 'edit', + 'id' => $user['user_id'], + )); } - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'users', $users); + $tpl_data = $this->getLabels(); + $tpl_data['users'] = $users; + + return $this->twig->render('module/user/user/list.tpl.html', $tpl_data); } /** - * Prepare the template to show the update screen for a user + * Crate HTML for the update screen for a user + * + * @return string */ protected function executeEdit() { - $this->setTemplateId('user_update', 'user/user.tpl.html'); $user_id = isset($_GET['id']) ? intval($_GET['id']) : 0; - $fields_prefill = array('name', 'user_name', 'mobile', 'email'); + $fields_prefill = array('name', 'user_name', 'mobile', 'pushover_key', 'pushover_device', 'email'); if($user_id == 0) { // insert mode @@ -151,7 +175,7 @@ class UserController extends AbstractController { foreach($this->servers as &$server) { if(in_array($server['server_id'], $user_servers)) { - $server['edit_checked'] = 'checked="checked"'; + $server['edit_selected'] = 'selected="selected"'; $server['class'] = 'active'; } } @@ -160,6 +184,13 @@ class UserController extends AbstractController { 'titlemode' => $title, 'placeholder_password' => $placeholder_password, 'edit_user_id' => $user_id, + 'url_save' => psm_build_url(array( + 'mod' => 'user', + 'action' => 'save', + 'id' => $user_id, + )), + 'servers' => $this->servers, + 'user_level' => $lvl_selected, ); foreach($fields_prefill as $field) { if(isset($edit_user->$field)) { @@ -167,17 +198,17 @@ class UserController extends AbstractController { } } - $ulvls_tpl = array(); + $tpl_data['levels'] = array(); foreach($this->user_validator->getUserLevels() as $lvl) { - $ulvls_tpl[] = array( + $tpl_data['levels'][] = array( 'value' => $lvl, 'label' => psm_get_lang('users', 'level_' . $lvl), - 'selected' => ($lvl == $lvl_selected) ? 'selected="selected"' : '', ); } - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'levels', $ulvls_tpl); - $this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'servers', $this->servers); - $this->tpl->addTemplateData($this->getTemplateId(), $tpl_data); + + $tpl_data = array_merge($this->getLabels(), $tpl_data); + + return $this->twig->render('module/user/user/update.tpl.html', $tpl_data); } /** @@ -190,7 +221,7 @@ class UserController extends AbstractController { } $user_id = (isset($_GET['id'])) ? intval($_GET['id']) : 0; - $fields = array('name', 'user_name', 'password', 'password_repeat', 'level', 'mobile', 'email'); + $fields = array('name', 'user_name', 'password', 'password_repeat', 'level', 'mobile', 'pushover_key', 'pushover_device', 'email'); $clean = array(); foreach($fields as $field) { if(isset($_POST[$field])) { @@ -221,15 +252,16 @@ class UserController extends AbstractController { if(!empty($clean['password'])) { $password = $clean['password']; } - unset($clean['password']); unset($clean['password_repeat']); if($user_id > 0) { // edit user + unset($clean['password']); // password update is executed separately $this->db->save(PSM_DB_PREFIX.'users', $clean, array('user_id' => $user_id)); $this->addMessage(psm_get_lang('users', 'updated'), 'success'); } else { // add user + $clean['password'] = ''; // password update is executed separately $user_id = $this->db->save(PSM_DB_PREFIX.'users', $clean); $this->addMessage(psm_get_lang('users', 'inserted'), 'success'); } @@ -268,7 +300,7 @@ class UserController extends AbstractController { $this->db->delete(PSM_DB_PREFIX . 'users', array('user_id' => $id,)); $this->db->delete(PSM_DB_PREFIX.'users_servers', array('user_id' => $id)); - $this->addMessage(psm_get_lang('system', 'deleted'), 'success'); + $this->addMessage(psm_get_lang('users', 'deleted'), 'success'); } catch(\InvalidArgumentException $e) { $this->addMessage(psm_get_lang('users', 'error_' . $e->getMessage()), 'error'); } @@ -276,34 +308,31 @@ class UserController extends AbstractController { return $this->executeIndex(); } - // override parent::createHTMLLabels() - protected function createHTMLLabels() { - $this->tpl->addTemplateData( - $this->getTemplateId(), - array( - 'subtitle' => psm_get_lang('menu', 'user'), - 'label_users' => psm_get_lang('menu', 'users'), - 'label_name' => psm_get_lang('users', 'name'), - 'label_user_name' => psm_get_lang('users', 'user_name'), - 'label_password' => psm_get_lang('users', 'password'), - 'label_password_repeat' => psm_get_lang('users', 'password_repeat'), - 'label_level' => psm_get_lang('users', 'level'), - 'label_level_10' => psm_get_lang('users', 'level_10'), - 'label_level_20' => psm_get_lang('users', 'level_20'), - 'label_level_description' => psm_get_lang('users', 'level_description'), - 'label_mobile' => psm_get_lang('users', 'mobile'), - 'label_email' => psm_get_lang('users', 'email'), - 'label_servers' => psm_get_lang('menu', 'server'), - 'label_action' => psm_get_lang('system', 'action'), - 'label_save' => psm_get_lang('system', 'save'), - 'label_go_back' => psm_get_lang('system', 'go_back'), - 'label_edit' => psm_get_lang('system', 'edit') . ' ' . psm_get_lang('users', 'user'), - 'label_delete' => psm_get_lang('system', 'delete') . ' ' . psm_get_lang('users', 'user'), - 'label_add_new' => psm_get_lang('system', 'add_new'), - ) + protected function getLabels() { + return array( + 'label_users' => psm_get_lang('menu', 'users'), + 'label_user' => psm_get_lang('users', 'user'), + 'label_name' => psm_get_lang('users', 'name'), + 'label_user_name' => psm_get_lang('users', 'user_name'), + 'label_password' => psm_get_lang('users', 'password'), + 'label_password_repeat' => psm_get_lang('users', 'password_repeat'), + 'label_level' => psm_get_lang('users', 'level'), + 'label_level_description' => psm_get_lang('users', 'level_description'), + 'label_mobile' => psm_get_lang('users', 'mobile'), + 'label_pushover' => psm_get_lang('users', 'pushover'), + 'label_pushover_description' => psm_get_lang('users', 'pushover_description'), + 'label_pushover_key' => psm_get_lang('users', 'pushover_key'), + 'label_pushover_device' => psm_get_lang('users', 'pushover_device'), + 'label_pushover_device_description' => psm_get_lang('users', 'pushover_device_description'), + 'label_email' => psm_get_lang('users', 'email'), + 'label_servers' => psm_get_lang('menu', 'server'), + 'label_action' => psm_get_lang('system', 'action'), + 'label_save' => psm_get_lang('system', 'save'), + 'label_go_back' => psm_get_lang('system', 'go_back'), + 'label_edit' => psm_get_lang('system', 'edit'), + 'label_delete' => psm_get_lang('system', 'delete'), + 'label_add_new' => psm_get_lang('system', 'add_new'), ); - - return parent::createHTMLLabels(); } /** diff --git a/src/psm/Router.class.php b/src/psm/Router.class.php index 378f302a..125b690e 100644 --- a/src/psm/Router.class.php +++ b/src/psm/Router.class.php @@ -27,6 +27,7 @@ **/ namespace psm; +use Symfony\Component\HttpFoundation\Response; /** * The router class opens the controller and initializes the module. @@ -63,9 +64,14 @@ class Router { public function __construct() { global $db; $this->services['db'] = $db; - $this->services['tpl'] = new \psm\Service\Template(); $this->services['user'] = new \psm\Service\User($db); + $loader = new \Twig_Loader_Filesystem(PSM_PATH_TPL . PSM_THEME); + $this->services['twig'] = new \Twig_Environment($loader); + if(PSM_DEBUG) { + $this->services['twig']->enableDebug(); + } + $modules = $this->getModules(); foreach($modules as $id => $module) { @@ -82,6 +88,7 @@ class Router { public function getModules() { return array( 'config' => new Module\Config\ConfigModule(), + 'error' => new Module\Error\ErrorModule(), 'server' => new Module\Server\ServerModule(), 'user' => new Module\User\UserModule(), 'install' => new Module\Install\InstallModule(), @@ -97,8 +104,16 @@ class Router { * If no mod is given it will attempt to load the default module. * @param string $mod if empty, the mod getvar will be used, or fallback to default * @throws \InvalidArgumentException + * @throws \LogicException */ public function run($mod = null) { + if(!psm_is_cli() && isset($_GET["logout"])) { + $this->services['user']->doLogout(); + // logged out, redirect to login + header('Location: ' . psm_build_url()); + die(); + } + if($mod === null) { $mod = psm_GET('mod', $this->default_module); } @@ -113,21 +128,25 @@ class Router { } // get min required level for this controller and make sure the user matches $min_lvl = $controller->getMinUserLevelRequired(); + $action = null; if($min_lvl < PSM_USER_ANONYMOUS) { // if user is not logged in, load login module if(!$this->services['user']->isUserLoggedIn()) { - // redirect to login $controller = $this->getController('user_login'); } elseif($this->services['user']->getUserLevel() > $min_lvl) { - // @todo perhaps show a nice permission denied page - die('You do not have the privileges to view this page.'); + $controller = $this->getController('error'); + $action = '401'; } } $controller->setUser($this->services['user']); - // let the module prepare it's HTML code - $controller->initialize(); + $response = $controller->initialize($action); + + if(!($response instanceof Response)) { + throw new \LogicException('Controller did not return a Response object.'); + } + $response->send(); } /** @@ -142,7 +161,7 @@ class Router { if($controller === false) { throw new \InvalidArgumentException('Controller is not registered'); } - $controller = new $controller($this->services['db'], $this->services['tpl']); + $controller = new $controller($this->services['db'], $this->services['twig']); if(!$controller instanceof \psm\Module\ControllerInterface) { throw new \Exception('Controller does not use ControllerInterface'); diff --git a/src/psm/Service/Database.class.php b/src/psm/Service/Database.class.php index 24649ebd..52a9bf01 100644 --- a/src/psm/Service/Database.class.php +++ b/src/psm/Service/Database.class.php @@ -77,9 +77,9 @@ class Database { * Constructor * * @param string $host - * @param string $db * @param string $user * @param string $pass + * @param string $db */ function __construct($host = null, $user = null, $pass = null, $db = null) { if($host != null && $user != null && $pass != null && $db != null) { @@ -151,6 +151,29 @@ class Database { return $this->last; } + /** + * Prepare and execute SQL statement with parameters + * @param string $query SQL statement + * @param Array $parameters An array of values with as many elements as there are bound parameters in the SQL statement + * @param boolean $fetch automatically fetch results, or return PDOStatement? + * @return @return array|\PDOStatement if $fetch = true, array, otherwise \PDOStatement + */ + public function execute($query, $parameters, $fetch = true) { + try { + $this->last = $this->pdo()->prepare($query); + $this->last->execute($parameters); + } catch (\PDOException $e) { + $this->error($e); + } + + if($fetch && $this->last != false) { + $result = $this->last->fetchAll(\PDO::FETCH_ASSOC); + } else { + $result = $this->last; + } + return $result; + } + /** * Performs a select on the given table and returns an multi dimensional associative array with results * @param string $table tablename diff --git a/src/psm/Service/Template.class.php b/src/psm/Service/Template.class.php deleted file mode 100644 index 8c639f1a..00000000 --- a/src/psm/Service/Template.class.php +++ /dev/null @@ -1,283 +0,0 @@ -. - * - * @package phpservermon - * @author Pepijn Over - * @copyright Copyright (c) 2008-2014 Pepijn Over - * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 - * @version Release: @package_version@ - * @link http://www.phpservermonitor.org/ - **/ - -namespace psm\Service; - -class Template { - - /** - * Loaded templates - * @var array $templates - */ - protected $templates = array(); - - /** - * Cache of parsed files - * @var array $files_parsed - * @see parseFile() - */ - protected $files_parsed = array(); - - function __construct() { - // add the main template - $this->newTemplate('main', 'main.tpl.html'); - } - - /** - * Add template - * - * @param string $id template id used in the tpl file (html) - * @param string $filename path to template file, or if file is located in the default template dir just the filename - * @return mixed false if template cannot be found, html code on success - */ - public function newTemplate($id, $filename) { - if (file_exists($filename)) { - $this->templates[$id] = $this->parseFile($filename); - } elseif (file_exists(PSM_PATH_TPL.$filename)) { - $this->templates[$id] = $this->parseFile(PSM_PATH_TPL.$filename); - } else { - // file does not exist - trigger_error('Template not found with id: '.$id.' and filename: '.$filename); - return false; - } - $use_tpl = null; - - // only get data from the file that's between the tpl id tags with this id - // find "tpl_{$row_id}" in the current template - //preg_match_all('{(.*?)}is', $this->templates[$id], $matches); - preg_match_all("{(.*?)}is", $this->templates[$id], $matches); - - // no repeat tpl found? skip to next one - if (empty($matches)) return false; - - // check if the row_id is in one of the matches (aka whether the supplied row id actually has a template in this file) - if (isset($matches[1][0])) { - $use_tpl = $matches[1][0]; - } - - // no template with the given id found.. - if ($use_tpl === null) return false; - - // remove tpl code tags from original template so it won't be in the source - $this->templates[$id] = preg_replace("{(.*?)}is", "", $use_tpl); - - return $this->templates[$id]; - } - - /** - * Add data to the template - * - * @param string $tpl_id template_id used by add_template() - * @param array $data - * @param boolean $use_html if true, $tpl_id is considered to be HTML code and used rather than a template - * @return string new template - */ - public function addTemplateData($tpl_id, $data, $use_html = false) { - if($use_html) { - // no template - $source = $tpl_id; - } else { - // does the template exist? - if (!isset($this->templates[$tpl_id])) { - // file does not exist - trigger_error("Template '{$tpl_id}' could not be found", E_USER_WARNING); - return false; - } - $source =& $this->templates[$tpl_id]; - } - - foreach($data as $key => $value) { - if(is_array($value)) { - $subdata = array(); - foreach($value as $k => $v) { - $subdata[$key.'_'.$k] = $v; - } - $source = $this->addTemplateData($source, $subdata, true); - } else { - $source = str_replace('{'.$key.'}', $value, $source); - } - } - return $source; - } - - /** - * Add repeat rows to template. - * It's possible to create a nested repeat tpl. All you need to do is to create a subarray - * For example: - * $data = array( - * 0 => array( - * 'name' => 'Test', - * 'subdata' => array( - * 0 => array( - * 'name' => 'Subtest 1', - * ), - * 1 => array(...) - * ), - * ), - * ); - * In your template you would literally put the nested repeat inside the first repeat. - * If you have more than 1 nested array, the first subtemplate will be used for all others. - * - * @param string $id template id used by add_template() or html code in case of repeat-repeat tpl - * @param string $repeat_id ID used in template file for the repeat template: html - * @param array $data - * @param boolean $use_html can only be used from within this function for recursive repeat templating. in this case the $id is the html code - * @param int $level starts off with 0. if level=2, current repeat template will be used again for subs, so you can go all the way - * @param int $level_reuse_prev from what level should we start repeating the current template for more subrecords, so we can go all the way? - * @return mixed false if repeat template cannot be found, html code on success - */ - public function addTemplateDataRepeat($tpl_id, $repeat_id, $data, $use_html = false, $level = 0, $level_reuse_prev = 2) { - if($use_html) { - $source = $tpl_id; - } else { - // does the template exist? - if (!isset($this->templates[$tpl_id])) { - // file does not exist - trigger_error("Template '{$tpl_id}' could not be found", E_USER_WARNING); - return false; - } - $source =& $this->templates[$tpl_id]; - } - - if($level < $level_reuse_prev) { - // find "tpl_repeat_{$repeat_id}_" in the current template - preg_match_all("{(.*?)}is", $source, $matches); - - // check if the repeat_id is in one of the matches - if (isset($matches[1][0])) { - $use_tpl = $matches[1][0]; - } else { - // if we didn't find a repeat template for the repeat_id supplied, skip the rest.. - return false; - } - - // remove repeat tpl code from original template so it won't be in the source - $source = preg_replace("{(.*?)}is", "", $source); - } else { - $use_tpl = $source; - } - // now lets go through all the records supplied and put them in the HTML repeat code we just found - $result = ''; - - foreach($data as $record) { - $tmp_string = $use_tpl; - - if(!is_array($record)) { - $record = array( - 'value' => $record, - ); - } - - // multi dim array - foreach($record as $k => $v) { - if(is_array($v)) { - // nested repeat - if(isset($v[0]) && is_array($v[0])) { - // repeat template in a repeat template - $repeat_html = $this->addTemplateDataRepeat($use_tpl, $k, $v, true, ($level + 1), $level_reuse_prev); - $tmp_string = str_replace('{'.$k.'}', $repeat_html, $tmp_string); - } else { - foreach($v as $vk => $vv) { - $tmp_string = str_replace('{'.$k.'_'.$vk.'}', $vv, $tmp_string); - } - } - } else { - $tmp_string = str_replace('{'.$k.'}', $v, $tmp_string); - } - } - - $result .= $tmp_string.PHP_EOL; - } - - if($use_html === false) { - // add to main template.. - return $this->addTemplateData($tpl_id, array($repeat_id => $result)); - } else { - return $result; - } - } - - public function display($id) { - // check if there are any unused tpl_repeat templates, and if there are remove them - $result = preg_replace('{(.*?)}is', '', $this->templates[$id]); - - // check for tpl variables that have not been replaced. ie: {name}. ignore literal stuff, though. ie: {{name}} is {name} and should not be removed - preg_match_all('~{?{(\w+?)}}?~', $result, $matches); - - foreach($matches[0] as $match) { - if (substr($match, 0, 2) == '{{') { - // literal! remove only first and last bracket! - $result = str_replace($match, substr($match, 1, -1), $result); - } else { - // unused variable, remove completely - $result = str_replace($match, '', $result); - } - } - return $result; - } - - /** - * Get html code for a template, or if no template id given get all templates - * - * @param string $template_id - * @return mixed string when ID given, else array - */ - public function getTemplate($template_id = null) { - if ($template_id === null) { - return $this->templates; - } elseif (isset($this->templates[$template_id])) { - return $this->templates[$template_id]; - } else { - return false; - } - } - - /** - * - * Get file content - * - * @param string $filename filename - * @return string file contents - */ - protected function parseFile($filename) { - if (!file_exists($filename)) return false; - - if(isset($this->files_parsed[$filename])) { - return $this->files_parsed[$filename]; - } - - ob_start(); - include($filename); - $file_content = ob_get_contents(); - ob_end_clean(); - - $this->files_parsed[$filename] = $file_content; - - return $file_content; - } -} - diff --git a/src/psm/Service/User.class.php b/src/psm/Service/User.class.php index ca2dd04b..77cf17dc 100644 --- a/src/psm/Service/User.class.php +++ b/src/psm/Service/User.class.php @@ -28,10 +28,14 @@ **/ namespace psm\Service; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * This is a heavily modified version of the php-login-advanced project by Panique. * + * It uses the Session classes from the Symfony HttpFoundation component. + * * @author Panique * @author Pepijn Over * @link http://www.php-login.net @@ -52,44 +56,55 @@ class User { */ protected $user_data = array(); + /** + * Session object + * @var \Symfony\Component\HttpFoundation\Session\Session $session + */ + protected $session; + /** * Current user id * @var int $user_id */ protected $user_id; - /** + /** + *Current user preferences + * @var array $user_preferences + */ + protected $user_preferences; + + /** * The user's login status * @var boolean $user_is_logged_in */ protected $user_is_logged_in = false; /** - * the function "__construct()" automatically starts whenever an object of this class is created, - * you know, when you do "$login = new Login();" + * Open a new user service + * + * @param \psm\Service\Database $db + * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session if NULL, one will be created */ - public function __construct(Database $db) { + public function __construct(Database $db, SessionInterface $session = null) { $this->db_connection = $db->pdo(); - if(php_sapi_name() != 'cli' && (!defined('PSM_INSTALL') || !PSM_INSTALL)) { - if(!$this->isSessionStarted()) { - session_start(); + if(!psm_is_cli()) { + if($session == null) { + $session = new Session(); + $session->start(); } - // check the possible login actions: - // 1. login via session data (happens each time user opens a page on your php project AFTER he has successfully logged in via the login form) - // 2. login via cookie - // 3. logout (happen when user clicks logout button) + $this->session = $session; - // if user has an active session on the server - if(!$this->loginWithSessionData()) { - $this->loginWithCookieData(); - } + if((!defined('PSM_INSTALL') || !PSM_INSTALL)) { + // check the possible login actions: + // 1. login via session data (happens each time user opens a page on your php project AFTER he has successfully logged in via the login form) + // 2. login via cookie - if(isset($_GET["logout"])) { - $this->doLogout(); - // logged out, redirect to login - header('Location: ' . psm_build_url()); - die(); + // if user has an active session on the server + if(!$this->loginWithSessionData()) { + $this->loginWithCookieData(); + } } } } @@ -133,13 +148,15 @@ class User { } /** - * Logs in with S_SESSION data. + * Logs in with SESSION data. + * + * @return boolean */ - private function loginWithSessionData() { - if(empty($_SESSION) || !isset($_SESSION['user_id'])) { + protected function loginWithSessionData() { + if(!$this->session->has('user_id')) { return false; } - $user = $this->getUser($_SESSION['user_id']); + $user = $this->getUser($this->session->get('user_id')); if(!empty($user)) { $this->setUserLoggedIn($user->user_id); @@ -161,7 +178,7 @@ class User { // extract data from the cookie list ($user_id, $token, $hash) = explode(':', $_COOKIE['rememberme']); // check cookie hash validity - if ($hash == hash('sha256', $user_id . ':' . $token . PSM_LOGIN_COOKIE_SECRET_KEY) && !empty($token)) { + if($hash == hash('sha256', $user_id . ':' . $token . PSM_LOGIN_COOKIE_SECRET_KEY) && !empty($token)) { // cookie looks good, try to select corresponding user // get real token from database (and all other data) $user = $this->getUser($user_id); @@ -200,14 +217,14 @@ class User { if(!isset($user->user_id)) { password_verify($user_password, 'dummy_call_against_timing'); return false; - } else if (! password_verify($user_password, $user->password)) { + } else if(!password_verify($user_password, $user->password)) { return false; } $this->setUserLoggedIn($user->user_id, true); // if user has check the "remember me" checkbox, then generate token and write cookie - if ($user_rememberme) { + if($user_rememberme) { $this->newRememberMeCookie(); } @@ -215,9 +232,9 @@ class User { // DELETE this if-block if you like, it only exists to recalculate users's hashes when you provide a cost factor, // by default the script will use a cost factor of 10 and never change it. // check if the have defined a cost factor in config/hashing.php - if (defined('PSM_LOGIN_HASH_COST_FACTOR')) { + if(defined('PSM_LOGIN_HASH_COST_FACTOR')) { // check if the hash needs to be rehashed - if (password_needs_rehash($user->password, PASSWORD_DEFAULT, array('cost' => PSM_LOGIN_HASH_COST_FACTOR))) { + if(password_needs_rehash($user->password, PASSWORD_DEFAULT, array('cost' => PSM_LOGIN_HASH_COST_FACTOR))) { $this->changePassword($user->user_id, $user_password); } } @@ -231,10 +248,10 @@ class User { */ protected function setUserLoggedIn($user_id, $regenerate = false) { if($regenerate) { - session_regenerate_id(); + $this->session->migrate(); } - $_SESSION['user_id'] = $user_id; - $_SESSION['user_logged_in'] = 1; + $this->session->set('user_id', $user_id); + $this->session->set('user_logged_in', 1); // declare user id, set the login status to true $this->user_id = $user_id; @@ -244,7 +261,7 @@ class User { /** * Create all data needed for remember me cookie connection on client and server side */ - private function newRememberMeCookie() { + protected function newRememberMeCookie() { // generate 64 char random string and store it in current user data $random_token_string = hash('sha256', mt_rand()); $sth = $this->db_connection->prepare('UPDATE '.PSM_DB_PREFIX.'users SET rememberme_token = :user_rememberme_token WHERE user_id = :user_id'); @@ -262,11 +279,11 @@ class User { /** * Delete all data needed for remember me cookie connection on client and server side */ - private function deleteRememberMeCookie() { + protected function deleteRememberMeCookie() { // Reset rememberme token - if(isset($_SESSION['user_id'])) { + if($this->session->has('user_id')) { $sth = $this->db_connection->prepare('UPDATE '.PSM_DB_PREFIX.'users SET rememberme_token = NULL WHERE user_id = :user_id'); - $sth->execute(array(':user_id' => $_SESSION['user_id'])); + $sth->execute(array(':user_id' => $this->session->get('user_id'))); } // set the rememberme-cookie to ten years ago (3600sec * 365 days * 10). @@ -281,10 +298,8 @@ class User { public function doLogout() { $this->deleteRememberMeCookie(); - $_SESSION = array(); - session_destroy(); - session_start(); - session_regenerate_id(); + $this->session->clear(); + $this->session->invalidate(); $this->user_is_logged_in = false; } @@ -427,17 +442,65 @@ class User { } /** - * Check if the session has already started - * @return boolean + * read current user preferences from the database + * @return boolean return false is user not connected */ - public function isSessionStarted() { - if(php_sapi_name() !== 'cli') { - if(version_compare(phpversion(), '5.4.0', '>=')) { - return session_status() === PHP_SESSION_ACTIVE ? true : false; - } else { - return session_id() === '' ? false : true; + protected function loadPreferences() { + if($this->user_preferences === null) { + if(!$this->getUser()) { + return false; + } + + $this->user_preferences = array(); + foreach($this->db_connection->query('SELECT `key`,`value` FROM `' . PSM_DB_PREFIX . 'users_preferences` WHERE `user_id` = ' . $this->user_id) as $row) { + $this->user_preferences[$row['key']] = $row['value']; } } - return false; + return true; + } + + /** + * Get a user preference value + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getUserPref($key, $default = '') { + if(!$this->loadPreferences() || !isset($this->user_preferences[$key])) { + return $default; + } + + $value = $this->user_preferences[$key]; + settype($value, gettype($default)); + return $value; + } + + /** + * Set a user preference value + * @param string $key + * @param mixed $value + */ + public function setUserPref($key, $value) { + if($this->loadPreferences()) { + if(isset($this->user_preferences[$key])) { + if($this->user_preferences[$key] == $value) { + return; // no change + } + $sql = 'UPDATE `' . PSM_DB_PREFIX . 'users_preferences` SET `key` = ?, `value` = ? WHERE `user_id` = ?'; + } else{ + $sql = 'INSERT INTO `' . PSM_DB_PREFIX . 'users_preferences` SET `key` = ?, `value` = ?, `user_id` = ?'; + } + $sth = $this->db_connection->prepare($sql); + $sth->execute(array($key, $value, $this->user_id)); + $this->user_preferences[$key] = $value; + } + } + + /** + * Get session object + * @return \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + public function getSession() { + return $this->session; } } diff --git a/src/psm/Txtmsg/Smsglobal.class.php b/src/psm/Txtmsg/Smsglobal.class.php new file mode 100644 index 00000000..762104e9 --- /dev/null +++ b/src/psm/Txtmsg/Smsglobal.class.php @@ -0,0 +1,80 @@ +. + * + * @package phpservermon + * @author Victor Macko + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1 + **/ + +namespace psm\Txtmsg; + +class Smsglobal extends Core { + // ========================================================================= + // [ Fields ] + // ========================================================================= + public $gateway = 1; + public $resultcode = null; + public $resultmessage = null; + public $success = false; + public $successcount = 0; + + /** + * Send the SMS message + * @param string $message + * @return boolean (true = message was sent successfully, false = there was a problem sending the message) + */ + public function sendSMS($message) { + $recipients = join(',', $this->recipients); + + if(count($recipients) == 0) { + return false; + } + /** + * Documentation is here: http://www.smsglobal.com/http-api/ + * Recipient numbers should be in the MSIDSN format (eg. 61400111222). The '+' sign should not be included before the country code. + */ + + $from = urlencode(substr($this->originator,0 , 11)); // Max 11 Char. + + $url = 'http://www.smsglobal.com/http-api.php' . + '?action=sendsms' . + '&user=' . $this->username . + '&password=' . $this->password . + '&from=' . $from . + '&to=' . rawurlencode($recipients) . + '&clientcharset=ISO-8859-1' . + '&text=' . substr(rawurlencode($message), 0, 153); + + $returnedData = file_get_contents($url); + $isOk = strpos($returnedData, 'OK: 0') !== false; + + $this->success = $isOk; + $this->resultmessage = $returnedData; + + if(!$isOk) { + error_log($this->resultmessage, E_USER_NOTICE); + } + + return $isOk; + } +} diff --git a/src/psm/Txtmsg/Smsit.class.php b/src/psm/Txtmsg/Smsit.class.php new file mode 100644 index 00000000..f9fb6999 --- /dev/null +++ b/src/psm/Txtmsg/Smsit.class.php @@ -0,0 +1,71 @@ +. + * + * @package phpservermon + * @author nerdalertdk + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1 + **/ + +namespace psm\Txtmsg; + +class Smsit extends Core { + // ========================================================================= + // [ Fields ] + // ========================================================================= + public $gateway = 1; + public $resultcode = null; + public $resultmessage = null; + public $success = false; + public $successcount = 0; + + public function sendSMS($message) { + // http://www.smsit.dk/api/sendSms.php?apiKey=[KEY]x&senderId=[SENDER]&mobile=[PHONENUMBER]&message=[MESSAGE] + // Use USERNAME as API KEY, password not needed + $apiurl = "http://www.smsit.dk/api/sendSms.php"; + $msg = urlencode( $message ); + $from = urlencode( substr($this->originator,0,11) ); // Max 11 Char. + + foreach( $this->recipients as $phone ){ + $URL = $apiurl."?apiKey=" . $this->username . "&mobile=" . $phone . "&message=" . $msg . "&senderId=" . $from; + $result = file_get_contents( $URL ); + + /* + 0 Everything went as it should + 1 Invalid API key + 2 Invalid sender name + 3 Invalid character set (charset) + 4 Invalid mobile number + 5 There is not filled out a message + 6 The message is too long (That was she said) + 7 API-key does not exist + */ + if((int)$result == 0) { + $success = true; + } + + } + + return $result; + } + +} diff --git a/src/psm/Util/Install/Installer.class.php b/src/psm/Util/Install/Installer.class.php index 424d66e5..30e3ad25 100644 --- a/src/psm/Util/Install/Installer.class.php +++ b/src/psm/Util/Install/Installer.class.php @@ -78,7 +78,7 @@ class Installer { // different DB version, check if the version requires any changes // @todo this is currently a manual check for each version, similar to upgrade().. not a clean way - if(version_compare($version_db, '3.0.0', '<')) { + if(version_compare($version_db, '3.1.0', '<')) { return true; } else { // change database version to current version so this check won't be required next time @@ -126,7 +126,7 @@ class Installer { $this->log('Populating database...'); $queries = array(); - $queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "servers` (`ip`, `port`, `label`, `type`, `status`, `error`, `rtime`, `last_online`, `last_check`, `active`, `email`, `sms`) VALUES ('http://sourceforge.net/index.php', 80, 'SourceForge', 'website', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes'), ('smtp.gmail.com', 465, 'Gmail SMTP', 'service', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes')"; + $queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "servers` (`ip`, `port`, `label`, `type`, `pattern`, `status`, `error`, `rtime`, `last_online`, `last_check`, `active`, `email`, `sms`, `pushover`) VALUES ('http://sourceforge.net/index.php', 80, 'SourceForge', 'website', '', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes', 'yes'), ('smtp.gmail.com', 465, 'Gmail SMTP', 'service', '', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes', 'yes')"; $queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "users_servers` (`user_id`,`server_id`) VALUES (1, 1), (1, 2);"; $queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "config` (`key`, `value`) VALUE ('language', 'en_US'), @@ -138,15 +138,19 @@ class Installer { ('email_smtp_port', ''), ('email_smtp_username', ''), ('email_smtp_password', ''), - ('sms_status', '1'), + ('sms_status', '0'), ('sms_gateway', 'mollie'), ('sms_gateway_username', 'username'), ('sms_gateway_password', 'password'), ('sms_from', '1234567890'), + ('pushover_status', '0'), + ('pushover_api_token', ''), ('alert_type', 'status'), ('log_status', '1'), ('log_email', '1'), ('log_sms', '1'), + ('log_pushover', '1'), + ('log_retention_period', '365'), ('version', '" . PSM_VERSION . "'), ('version_update_check', '" . PSM_VERSION . "'), ('auto_refresh_servers', '0'), @@ -177,10 +181,18 @@ class Installer { `level` tinyint(2) unsigned NOT NULL DEFAULT '20', `name` varchar(255) NOT NULL, `mobile` varchar(15) NOT NULL, + `pushover_key` varchar(255) NOT NULL, + `pushover_device` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, PRIMARY KEY (`user_id`), UNIQUE KEY `unique_username` (`user_name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;", + PSM_DB_PREFIX . 'users_preferences' => "CREATE TABLE IF NOT EXISTS `" . PSM_DB_PREFIX . "users_preferences` ( + `user_id` int(11) unsigned NOT NULL, + `key` varchar(255) NOT NULL, + `value` varchar(255) NOT NULL, + PRIMARY KEY (`user_id`, `key`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;", PSM_DB_PREFIX . 'users_servers' => "CREATE TABLE `" . PSM_DB_PREFIX . "users_servers` ( `user_id` INT( 11 ) UNSIGNED NOT NULL , `server_id` INT( 11 ) UNSIGNED NOT NULL , @@ -189,7 +201,7 @@ class Installer { PSM_DB_PREFIX . 'log' => "CREATE TABLE `" . PSM_DB_PREFIX . "log` ( `log_id` int(11) unsigned NOT NULL AUTO_INCREMENT, `server_id` int(11) unsigned NOT NULL, - `type` enum('status','email','sms') NOT NULL, + `type` enum('status','email','sms','pushover') NOT NULL, `message` varchar(255) NOT NULL, `datetime` timestamp NOT NULL default CURRENT_TIMESTAMP, `user_id` varchar(255) NOT NULL, @@ -210,8 +222,10 @@ class Installer { `active` enum('yes','no') NOT NULL default 'yes', `email` enum('yes','no') NOT NULL default 'yes', `sms` enum('yes','no') NOT NULL default 'no', + `pushover` enum('yes','no') NOT NULL default 'yes', `warning_threshold` mediumint(1) unsigned NOT NULL DEFAULT '1', `warning_threshold_counter` mediumint(1) unsigned NOT NULL DEFAULT '0', + `timeout` smallint(1) unsigned NULL DEFAULT NULL, PRIMARY KEY (`server_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;", PSM_DB_PREFIX . 'servers_uptime' => "CREATE TABLE IF NOT EXISTS `" . PSM_DB_PREFIX . "servers_uptime` ( @@ -264,6 +278,10 @@ class Installer { // upgrade to 3.0.0 $this->upgrade300(); } + if(version_compare($version_from, '3.1.0', '<')) { + // upgrade to 3.1.0 + $this->upgrade310(); + } psm_update_conf('version', $version_to); } @@ -378,4 +396,29 @@ class Installer { } $this->execSQL("ALTER TABLE `".PSM_DB_PREFIX."users` DROP `server_id`;"); } + + protected function upgrade310() { + $queries = array(); + psm_update_conf('log_retention_period', '365'); + + psm_update_conf('pushover_status', 0); + psm_update_conf('log_pushover', 1); + psm_update_conf('pushover_api_token', ''); + $queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "users` ADD `pushover_key` VARCHAR( 255 ) NOT NULL AFTER `mobile`;"; + $queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "users` ADD `pushover_device` VARCHAR( 255 ) NOT NULL AFTER `pushover_key`;"; + + $queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "servers` ADD `pushover` ENUM( 'yes','no' ) NOT NULL DEFAULT 'yes' AFTER `sms`;"; + $queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "log` CHANGE `type` `type` ENUM( 'status', 'email', 'sms', 'pushover' ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;"; + + $queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "servers` ADD `timeout` smallint(1) unsigned NULL DEFAULT NULL;"; + + $queries[] = "CREATE TABLE IF NOT EXISTS `" . PSM_DB_PREFIX . "users_preferences` ( + `user_id` int(11) unsigned NOT NULL, + `key` varchar(255) NOT NULL, + `value` varchar(255) NOT NULL, + PRIMARY KEY (`user_id`, `key`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;"; + + $this->execSQL($queries); + } } diff --git a/src/psm/Util/Module/Modal.class.php b/src/psm/Util/Module/Modal.class.php new file mode 100644 index 00000000..00857afd --- /dev/null +++ b/src/psm/Util/Module/Modal.class.php @@ -0,0 +1,149 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * @author Jérôme Cabanis + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + **/ + +namespace psm\Util\Module; + +class Modal implements ModalInterface { + + const MODAL_TYPE_OK = 0; + const MODAL_TYPE_OKCANCEL = 1; + const MODAL_TYPE_DANGER = 2; + + /** + * prefix used for modal dialog box elements + * @var string $modal_id + */ + protected $modal_id; + + /** + * @var int $type Type of modal dialog + */ + protected $type; + + /** + * Modal dialog title + * @var string $title + */ + protected $title; + + /** + * Modal dialog message + * @var string $body + */ + protected $message; + + /** + * label of the OK button + * @var string $ok_label + */ + protected $ok_label; + + /** + * Twig environment + * @var \Twig_Environment $twig + */ + protected $twig; + + public function __construct(\Twig_Environment $twig, $modal_id = 'main', $type = self::MODAL_TYPE_OK ) { + $this->modal_id = $modal_id; + $this->twig = $twig; + $this->type = $type; + } + + /** + * get the modal dialog box element prefix + * @return string + */ + public function getModalID() { + return $this->modal_id; + } + + /** + * Set the modal dialog type + * @param int $type + * @return \psm\Util\Module\Modal + */ + public function setType($type) { + if(in_array($type, array(self::MODAL_TYPE_OK, self::MODAL_TYPE_OKCANCEL, self::MODAL_TYPE_DANGER))) { + $this->type = $type; + } + return $this; + } + + /** + * Set the modal dialog title + * @param string $title + * @return \psm\Util\Module\Modal + */ + public function setTitle($title) { + $this->title = $title; + return $this; + } + + /** + * Set the modal dialog message + * @param string $message + * @return \psm\Util\Module\Modal + */ + public function setMessage($message) { + $this->message = $message; + return $this; + } + + public function setOKButtonLabel($label) { + $this->ok_label = $label; + return $this; + } + + public function createHTML() { + $has_cancel = ($this->type == self::MODAL_TYPE_OK) ? false : true; + $button_type = ($this->type == self::MODAL_TYPE_DANGER) ? 'danger' : 'primary'; + $button_label = empty($this->ok_label) ? psm_get_lang('system', 'ok') : $this->ok_label; + $message = !empty($this->message) ? $this->message : ''; + + $matches = array(); + if(preg_match_all('/%(\d)/', $message, $matches, PREG_SET_ORDER)) { + foreach($matches as $match) { + $message = str_replace($match[0], '', $message); + } + } + + $tpl = $this->twig->loadTemplate('util/module/modal.tpl.html'); + $html = $tpl->render(array( + 'modal_id' => $this->modal_id, + 'modal_title' => !empty($this->title) ? $this->title : psm_get_lang('system', 'title'), + 'modal_body' => $message, + 'has_cancel' => $has_cancel, + 'label_cancel' => psm_get_lang('system', 'cancel'), + 'modal_button_type' => $button_type, + 'modal_button_label'=> $button_label, + )); + + return $html; + } +} diff --git a/src/psm/Util/Module/ModalInterface.class.php b/src/psm/Util/Module/ModalInterface.class.php new file mode 100644 index 00000000..3f204823 --- /dev/null +++ b/src/psm/Util/Module/ModalInterface.class.php @@ -0,0 +1,37 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * @author Jérôme Cabanis + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + **/ + +namespace psm\Util\Module; + +interface ModalInterface { + + public function __construct(\Twig_Environment $twig); + + public function getModalID(); + public function createHTML(); +} \ No newline at end of file diff --git a/src/psm/Util/Module/Sidebar.class.php b/src/psm/Util/Module/Sidebar.class.php index 051e762d..7a52bac2 100644 --- a/src/psm/Util/Module/Sidebar.class.php +++ b/src/psm/Util/Module/Sidebar.class.php @@ -26,7 +26,6 @@ **/ namespace psm\Util\Module; -use psm\Service\Template; class Sidebar implements SidebarInterface { @@ -51,13 +50,13 @@ class Sidebar implements SidebarInterface { protected $subtitle; /** - * Template service - * @var \psm\Service\Template $tpl + * Twig environment + * @var \Twig_Environment $twig */ - protected $tpl; + protected $twig; - public function __construct(Template $tpl) { - $this->tpl = $tpl; + public function __construct(\Twig_Environment $twig) { + $this->twig = $twig; } /** @@ -73,7 +72,7 @@ class Sidebar implements SidebarInterface { /** * Set a custom subtitle (default is module subitle) * @param string $title - * @return \psm\Util\Moduke\Sidebar + * @return \psm\Util\Module\Sidebar */ public function setSubtitle($title) { $this->subtitle = $title; @@ -154,11 +153,11 @@ class Sidebar implements SidebarInterface { } public function createHTML() { - $tpl_id = 'main_sidebar_container'; - $this->tpl->newTemplate($tpl_id, 'main_sidebar.tpl.html'); - + $tpl_data = array( + 'subtitle' => $this->subtitle, + ); $types = array('dropdown', 'button', 'link'); - $items = array(); + $tpl_data['items'] = array(); // loop through all types and build their html foreach($types as $type) { @@ -166,37 +165,17 @@ class Sidebar implements SidebarInterface { // no items for this type continue; } - // retrieve template for this type once so we can use it in the loop - $tpl_id_type = 'main_sidebar_types_' . $type; - $this->tpl->newTemplate($tpl_id_type, 'main_sidebar.tpl.html'); - $html_type = $this->tpl->getTemplate($tpl_id_type); // build html for each individual item foreach($this->items[$type] as $id => $item) { - $html_item = $html_type; - - if(isset($item['options'])) { - $item['options'] = $this->tpl->addTemplateDataRepeat($html_item, 'options', $item['options'], true); - - } - $html_item = $this->tpl->addTemplateData($html_type, $item, true); - - $items[] = array( - 'html_item' => $html_item, - 'class_active' => ($id === $this->active_id) ? 'active' : '', - ); + $item['type'] = $type; + $item['class_active'] = ($id === $this->active_id) ? 'active' : ''; + $tpl_data['items'][] = $item; } } - if(!empty($items)) { - $this->tpl->addTemplateDataRepeat($tpl_id, 'items', $items); - } - if($this->subtitle !== null) { - $this->tpl->addTemplateData($tpl_id, array( - 'subtitle' => $this->subtitle, - )); - } - $html = $this->tpl->getTemplate($tpl_id); + $tpl = $this->twig->loadTemplate('util/module/sidebar.tpl.html'); + $html = $tpl->render($tpl_data); return $html; } diff --git a/src/psm/Util/Module/SidebarInterface.class.php b/src/psm/Util/Module/SidebarInterface.class.php index 24f791ea..e388eae0 100644 --- a/src/psm/Util/Module/SidebarInterface.class.php +++ b/src/psm/Util/Module/SidebarInterface.class.php @@ -29,7 +29,7 @@ namespace psm\Util\Module; interface SidebarInterface { - public function __construct(\psm\Service\Template $tpl); + public function __construct(\Twig_Environment $twig); public function createHTML(); diff --git a/src/psm/Util/Server/ArchiveManager.class.php b/src/psm/Util/Server/ArchiveManager.class.php new file mode 100644 index 00000000..42da59cd --- /dev/null +++ b/src/psm/Util/Server/ArchiveManager.class.php @@ -0,0 +1,119 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1 + **/ + +namespace psm\Util\Server; + +/** + * Makes sure all data of servers is being archived properly or removed if necessary. + */ +class ArchiveManager { + + /** + * Available archiver utils. + * @var array $archivers + */ + protected $archivers = array(); + + /** + * Database service + * @var \psm\Service\Database $db + */ + protected $db; + + /** + * Retention period + * @var \DateInterval $retention_period + * @see setRetentionPeriod() + */ + protected $retention_period; + + public function __construct(\psm\Service\Database $db) { + $this->db = $db; + + $this->setRetentionPeriod(psm_get_conf('log_retention_period', 365)); + + $this->archivers[] = new Archiver\UptimeArchiver($db); + $this->archivers[] = new Archiver\LogsArchiver($db); + } + + /** + * Archive one or more servers. + * @param int $server_id + * @return boolean + */ + public function archive($server_id = null) { + $result = true; + foreach($this->archivers as $archiver) { + if(!$archiver->archive($server_id)) { + $result = false; + } + } + return $result; + } + + /** + * Cleanup old records for one or more servers + * @param int $server_id + * @return boolean + */ + public function cleanup($server_id = null) { + $result = true; + if(!$this->retention_period) { + // cleanup is disabled + return $result; + } + $retdate = new \DateTime(); + $retdate->sub($this->retention_period); + + foreach($this->archivers as $archiver) { + if(!$archiver->cleanup($retdate, $server_id)) { + $result = false; + } + } + return $result; + } + + /** + * Set retention period for this archive run. + * + * Set period to 0 to disable cleanup altogether. + * @param \DateInterval|int $period \DateInterval object or number of days (int) + * @return \psm\Util\Server\ArchiveManager + */ + public function setRetentionPeriod($period) { + if(is_object($period) && $period instanceof \DateInterval) { + $this->retention_period = $period; + } elseif(intval($period) == 0) { + // cleanup disabled + $this->retention_period = false; + } else { + $this->retention_period = new \DateInterval('P' . intval($period) . 'D'); + } + return $this; + } +} \ No newline at end of file diff --git a/src/psm/Util/Mailer.class.php b/src/psm/Util/Server/Archiver/ArchiverInterface.class.php similarity index 66% rename from src/psm/Util/Mailer.class.php rename to src/psm/Util/Server/Archiver/ArchiverInterface.class.php index 18f369dd..084b3f6d 100644 --- a/src/psm/Util/Mailer.class.php +++ b/src/psm/Util/Server/Archiver/ArchiverInterface.class.php @@ -1,55 +1,47 @@ -. - * - * @package phpservermon - * @author Pepijn Over - * @copyright Copyright (c) 2008-2014 Pepijn Over - * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 - * @version Release: @package_version@ - * @link http://www.phpservermonitor.org/ - * @since phpservermon 3.0.0 - **/ - -namespace psm\Util; - -/** - * PHPMailer is not using namespaces so unable to load files in autoloader. - */ -require_once(PSM_PATH_VENDOR . '/PHPMailer/class.phpmailer.php'); -require_once(PSM_PATH_VENDOR . '/PHPMailer/class.smtp.php'); - -/** - * PSM Mailer utility - * - * The PHPMailer is an open source lib that can be found in vendor/PHPMailer. - * - * @see \PHPMailer - */ -class Mailer extends \PHPMailer { - - /** - * Open new PHPMailer - * - * @param boolean $exceptions - */ - function __construct($exceptions = false) { - parent::__construct($exceptions); - - } -} +. + * + * @package phpservermon + * @author Pepijn Over + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1 + **/ + +namespace psm\Util\Server\Archiver; + +interface ArchiverInterface { + + /** + * Archive for one or all servers. + * @param int $server_id + * @return boolean + */ + public function archive($server_id = null); + + /** + * Cleanup data older than the retention period given. + * @param \DateTime $retention_date + * @param int $server_id + * @return boolean + */ + public function cleanup(\DateTime $retention_date, $server_id = null); +} \ No newline at end of file diff --git a/src/psm/Util/Server/Archiver/LogsArchiver.class.php b/src/psm/Util/Server/Archiver/LogsArchiver.class.php new file mode 100644 index 00000000..c64073aa --- /dev/null +++ b/src/psm/Util/Server/Archiver/LogsArchiver.class.php @@ -0,0 +1,70 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1 + **/ + +/** + * Cleanup log table + */ +namespace psm\Util\Server\Archiver; +use psm\Service\Database; + +class LogsArchiver implements ArchiverInterface { + + /** + * Database service + * @var \psm\Service\Database $db + */ + protected $db; + + function __construct(Database $db) { + $this->db = $db; + } + + /** + * Currently there is not really a log archive. + * + * It stays in the log table until cleaned up. + * @param int $server_id + */ + public function archive($server_id = null) { + return true; + } + + public function cleanup(\DateTime $retention_date, $server_id = null) { + $sql_where_server = ($server_id !== null) + // this is obviously not the cleanest way to implement this when using paramter binding.. sorry. + ? ' `server_id` = ' . intval($server_id) . ' AND ' + : ''; + + $this->db->execute( + "DELETE FROM `".PSM_DB_PREFIX."log` WHERE {$sql_where_server} `datetime` < :latest_date", + array('latest_date' => $retention_date->format('Y-m-d 00:00:00')), + false + ); + return true; + } +} \ No newline at end of file diff --git a/src/psm/Util/Server/Archiver/UptimeArchiver.class.php b/src/psm/Util/Server/Archiver/UptimeArchiver.class.php new file mode 100644 index 00000000..7c0c0a16 --- /dev/null +++ b/src/psm/Util/Server/Archiver/UptimeArchiver.class.php @@ -0,0 +1,173 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * Jérôme Cabanis + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + **/ + +/** + * The archiver class moves active data from the uptime table to the history table. + * + * Because the uptime table has a record for every single run of the status-check, + * it will grow very large over time. For this reason, uptime records are only kept for a limited time + * to provide detailed statistics. After that, the archiver comes in and saves the averages per day + * in the history table. That way we can always show statistics regarding average latency and failed checks per day, + * but we only need 1 record per server per day. + * + * @see \psm\Util\Updater\Autorun + */ +namespace psm\Util\Server\Archiver; +use psm\Service\Database; + +class UptimeArchiver implements ArchiverInterface { + + /** + * Database service + * @var \psm\Service\Database $db + */ + protected $db; + + function __construct(Database $db) { + $this->db = $db; + } + + /** + * Archive all server status records older than 1 week. + * + * Archiving means calculating averages per day, and storing 1 single + * history row for each day for each server. + * + * @param int $server_id + */ + public function archive($server_id = null) { + $latest_date = new \DateTime('-1 week 0:0:0'); + + // Lock tables to prevent simultaneous archiving (by other sessions or the cron job) + try { + $this->db->pdo()->exec('LOCK TABLES ' . PSM_DB_PREFIX . 'servers_uptime WRITE, ' . PSM_DB_PREFIX . 'servers_history WRITE'); + $locked = true; + } catch (\PDOException $e) { + // user does not have lock rights, ignore + $locked = false; + } + + $latest_date_str = $latest_date->format('Y-m-d 00:00:00'); + + $sql_where_server = $this->createSQLWhereServer($server_id); + + $records = $this->db->execute( + "SELECT `server_id`,`date`,`status`,`latency` + FROM `" . PSM_DB_PREFIX."servers_uptime` + WHERE {$sql_where_server} `date` < :latest_date", + array('latest_date' => $latest_date_str)); + + if(!empty($records)) { + // first group all records by day and server_id + $data_by_day = array(); + foreach($records as $record) { + $server_id = (int)$record['server_id']; + $day = date('Y-m-d', strtotime($record['date'])); + if(!isset($data_by_day[$day][$server_id])) { + $data_by_day[$day][$server_id] = array(); + } + $data_by_day[$day][$server_id][] = $record; + } + + // now get history data day by day + $histories = array(); + foreach($data_by_day as $day => $day_records) { + foreach ($day_records as $server_id => $server_day_records) { + $histories[] = $this->getHistoryForDay($day, $server_id, $server_day_records); + } + } + + // Save all + $this->db->insertMultiple(PSM_DB_PREFIX.'servers_history', $histories); + + // now remove all records from the uptime table + $this->db->execute( + "DELETE FROM `".PSM_DB_PREFIX."servers_uptime` WHERE {$sql_where_server} `date` < :latest_date", + array('latest_date' => $latest_date_str), + false + ); + } + + if($locked) { + $this->db->exec('UNLOCK TABLES'); + } + + return true; + } + + public function cleanup(\DateTime $retention_date, $server_id = null) { + $sql_where_server = $this->createSQLWhereServer($server_id); + $this->db->execute( + "DELETE FROM `".PSM_DB_PREFIX."servers_history` WHERE {$sql_where_server} `date` < :latest_date", + array('latest_date' => $retention_date->format('Y-m-d 00:00:00')), + false + ); + return true; + } + + /** + * Build a history array for a day records + * @param string $day + * @param int $server_id + * @param array $day_records + * @return array + */ + protected function getHistoryForDay($day, $server_id, $day_records) { + $latencies = array(); + $checks_failed = 0; + + foreach($day_records as $day_record) { + $latencies[] = $day_record['latency']; + + if($day_record['status'] == 0) { + $checks_failed++; + } + } + sort($latencies, SORT_NUMERIC); + + $history = array( + 'date' => $day, + 'server_id' => $server_id, + 'latency_min' => min($latencies), + 'latency_avg' => array_sum($latencies) / count($latencies), + 'latency_max' => max($latencies), + 'checks_total' => count($day_records), + 'checks_failed' => $checks_failed, + ); + return $history; + } + + protected function createSQLWhereServer($server_id) { + $sql_where_server = ($server_id !== null) + // this is obviously not the cleanest way to implement this when using paramter binding.. sorry. + ? ' `server_id` = ' . intval($server_id) . ' AND ' + : ''; + + return $sql_where_server; + } +} \ No newline at end of file diff --git a/src/psm/Util/Server/HistoryGraph.class.php b/src/psm/Util/Server/HistoryGraph.class.php index 2700e97a..ddc16e41 100644 --- a/src/psm/Util/Server/HistoryGraph.class.php +++ b/src/psm/Util/Server/HistoryGraph.class.php @@ -28,7 +28,6 @@ namespace psm\Util\Server; use psm\Service\Database; -use psm\Service\Template; /** * History util, create HTML for server graphs @@ -42,14 +41,14 @@ class HistoryGraph { protected $db; /** - * Template service - * @var \psm\Service\Template $tpl + * Twig environment + * @var \Twig_Environment $twig */ - protected $tpl; + protected $twig; - function __construct(Database $db, Template $tpl) { + function __construct(Database $db, \Twig_Environment $twig) { $this->db = $db; - $this->tpl = $tpl; + $this->twig = $twig; } /** @@ -57,127 +56,184 @@ class HistoryGraph { * @return string */ public function createHTML($server_id) { - $tpl_id = 'server_history'; - $this->tpl->newTemplate($tpl_id, 'server/history.tpl.html'); + // Archive all records for this server to make sure we have up-to-date stats + $archive = new ArchiveManager($this->db); + $archive->archive($server_id); + + $now = new \DateTime(); + $last_week = new \DateTime('-1 week 0:0:0'); + $last_year = new \DateTime('-1 year -1 week 0:0:0'); $graphs = array( - 0 => $this->generateGraphUptime($server_id), - 1 => $this->generateGraphHistory($server_id), + 0 => $this->generateGraphUptime($server_id, $last_week, $now), + 1 => $this->generateGraphHistory($server_id, $last_year, $last_week), ); - $this->tpl->addTemplateDataRepeat($tpl_id, 'graphs', $graphs); - - $this->tpl->addTemplateData( - $tpl_id, - array( - 'label_server' => psm_get_lang('servers', 'server'), - 'label_latency_avg' => psm_get_lang('servers', 'latency_avg'), - 'day_format' => psm_get_lang('servers', 'chart_day_format'), - 'long_date_format' => psm_get_lang('servers', 'chart_long_date_format'), - 'short_date_format' => psm_get_lang('servers', 'chart_short_date_format'), - 'short_time_format' => psm_get_lang('servers', 'chart_short_time_format'), - ) + $info_fields = array( + 'latency_avg' => '%01.4f', + 'uptime' => '%01.3f%%', ); - return $this->tpl->getTemplate($tpl_id); + foreach($graphs as $i => &$graph) { + // add subarray for info fields + $graph['info'] = array(); + + foreach($info_fields as $field => $format) { + if(!isset($graph[$field])) { + continue; + } + $graph['info'][] = array( + 'label' => psm_get_lang('servers', $field), + 'value' => sprintf($format, $graph[$field]), + ); + } + } + + $tpl_data = array( + 'graphs' => $graphs, + 'label_server' => psm_get_lang('servers', 'server'), + 'day_format' => psm_get_lang('servers', 'chart_day_format'), + 'long_date_format' => psm_get_lang('servers', 'chart_long_date_format'), + 'short_date_format' => psm_get_lang('servers', 'chart_short_date_format'), + 'short_time_format' => psm_get_lang('servers', 'chart_short_time_format'), + ); + + return $this->twig->render('module/server/history.tpl.html', $tpl_data); } /** * Generate data for uptime graph * @param int $server_id + * @param \DateTime $start_time Lowest DateTime of the graph + * @param \DateTime $end_time Highest DateTime of the graph * @return array */ - protected function generateGraphUptime($server_id) { - $uptimes = $this->db->select(PSM_DB_PREFIX.'servers_uptime' , array('server_id' => $server_id), null, '', 'date'); - $last_date = 0; - $latency_avg = 0; - // Create the list of points and server down zones - $line = array(); - $lines = array(); - $down = array(); - foreach ($uptimes as $uptime) { - $latency_avg += (float) $uptime['latency']; - - $time = strtotime($uptime['date']) * 1000; - if($uptime['status']) { - // The server is up - $line[] = '[' . $time . ',' . round((float)$uptime['latency'], 4) . ']'; - if($last_date) { - // Was down before. - // Record the first and last date as a string in the down array - $down[] = '[' . $last_date . ',' . $time . ']'; - $last_date = 0; - } - } - else { - if(!$last_date) { - $last_date = $time; - } - } - } - if(!empty($line)) { - $lines[] = '[' . implode(',', $line) . ']'; - } - if($last_date) { - $down[] = '[' . $last_date . ',0]'; - } - $buttons = array(); - $buttons[] = array('mode' => 'hour', 'label' => psm_get_lang('servers', 'hour'), 'class_active' => 'btn-info'); - $buttons[] = array('mode' => 'day', 'label' => psm_get_lang('servers', 'day')); - $buttons[] = array('mode' => 'week', 'label' => psm_get_lang('servers', 'week')); - - $data = array( - 'title' => psm_get_lang('servers', 'chart_last_week'), - 'latency_avg' => count($uptimes) > 0 ? round(($latency_avg / count($uptimes)), 4) : 0, - 'server_lines' => sizeof($lines) ? '[' . implode(',', $lines) . ']' : '', - 'server_down' => sizeof($down) ? '[' . implode(',', $down) . ']' : '', - 'series' => "[{label: '".psm_get_lang('servers', 'latency')."'}]", - 'plotmode' => 'hour', - 'buttons' => $buttons, - 'chart_id' => $server_id . '_uptime', + public function generateGraphUptime($server_id, $start_time, $end_time) { + $lines = array( + 'latency' => array(), ); + $cb_if_up = function($uptime_record) { + return ($uptime_record['status'] == 1); + }; + $records = $this->getRecords('uptime', $server_id, $start_time, $end_time); + + $data = $this->generateGraphLines($records, $lines, $cb_if_up, 'latency', $start_time, $end_time, true); + + $data['title'] = psm_get_lang('servers', 'chart_last_week'); + $data['plotmode'] = 'hour'; + $data['buttons'] = array(); + $data['buttons'][] = array('mode' => 'hour', 'label' => psm_get_lang('servers', 'hour'), 'class_active' => 'btn-info'); + $data['buttons'][] = array('mode' => 'day', 'label' => psm_get_lang('servers', 'day')); + $data['buttons'][] = array('mode' => 'week', 'label' => psm_get_lang('servers', 'week')); + // make sure to add chart id after buttons so its added to those tmeplates as well + $data['chart_id'] = $server_id . '_uptime'; + return $data; } /** * Generate data for history graph * @param int $server_id + * @param \DateTime $start_time Lowest DateTime of the graph + * @param \DateTime $end_time Highest DateTime of the graph * @return array */ - protected function generateGraphHistory($server_id) { - $uptimes = $this->db->select(PSM_DB_PREFIX.'servers_history' , array('server_id' => $server_id), null, '', 'date'); - - $last_date = 0; - // Create the list of points and server down zones + public function generateGraphHistory($server_id, $start_time, $end_time) { $lines = array( 'latency_avg' => array(), 'latency_max' => array(), 'latency_min' => array(), ); + $server = $this->db->selectRow(PSM_DB_PREFIX.'servers', array('server_id' => $server_id), array('warning_threshold')); + + $cb_if_up = function($uptime_record) use($server) { + return ($uptime_record['checks_failed'] < $server['warning_threshold']); + }; + $records = $this->getRecords('history', $server_id, $start_time, $end_time); + + // dont add uptime for now because we have no way to calculate accurate uptimes for archived records + $data = $this->generateGraphLines($records, $lines, $cb_if_up, 'latency_avg', $start_time, $end_time, false); + + $data['title'] = psm_get_lang('servers', 'chart_history'); + $data['plotmode'] = 'month'; + $data['buttons'] = array(); + $data['buttons'][] = array('mode' => 'week2', 'label' => psm_get_lang('servers', 'week')); + $data['buttons'][] = array('mode' => 'month', 'label' => psm_get_lang('servers', 'month'), 'class_active' => 'btn-info'); + $data['buttons'][] = array('mode' => 'year', 'label' => psm_get_lang('servers', 'year')); + // make sure to add chart id after buttons so its added to those tmeplates as well + $data['chart_id'] = $server_id . '_history'; + + return $data; + } + + /** + * Get all uptime/history records for a server + * @param string $type + * @param int $server_id + * @param \DateTime $start_time Lowest DateTime of the graph + * @param \DateTime $end_time Highest DateTime of the graph + * @return array + */ + protected function getRecords($type, $server_id, $start_time, $end_time) { + if(!in_array($type, array('history', 'uptime'))) { + return array(); + } + + $records = $this->db->execute( + 'SELECT * + FROM `' . PSM_DB_PREFIX . "servers_$type` + WHERE `server_id` = :server_id AND `date` BETWEEN :start_time AND :end_time", + array( + 'server_id' => $server_id, + 'start_time' => $start_time->format('Y-m-d H:i:s'), + 'end_time' => $end_time->format('Y-m-d H:i:s'), + )); + return $records; + } + + /** + * Generate data arrays for graphs + * @param array $records all uptime records to parse + * @param array $lines array with keys as line ids to prepare (key must be available in uptime records) + * @param callable $cb_if_up function to check if the server is up or down + * @param string $latency_avg_key which key from uptime records to use for calculating averages + * @param \DateTime $start_time Lowest DateTime of the graph + * @param \DateTime $end_time Highest DateTime of the graph + * @param boolean $add_uptime add uptime calculation? + * @return array + */ + protected function generateGraphLines($records, $lines, $cb_if_up, $latency_avg_key, $start_time, $end_time, $add_uptime = false) { + $data = array(); + + // PLEASE NOTE: all times are in microseconds! because of javascript. + $last_date = 0; $latency_avg = 0; $series = array(); - $time_end = 0; + // number of microseconds of downtime + $time_down = 0; $down = array(); - foreach ($uptimes as $uptime) { - $time = strtotime($uptime['date']) * 1000; - // keep track of highest timestamp to use as end-date for graphs - if($time > $time_end) { - $time_end = $time; - } - $latency_avg += (float) $uptime['latency_avg']; - if($uptime['checks_failed'] == 0) { + // Create the list of points and server down zones + foreach ($records as $uptime) { + $time = strtotime($uptime['date']) * 1000; + + // use the first line to calculate average latency + $latency_avg += (float) $uptime[$latency_avg_key]; + + if($cb_if_up($uptime)) { // The server is up foreach($lines as $key => &$value) { // add the value for each of the different lines if(isset($uptime[$key])) { - $value[] = '[' . $time . ',' . round((float)$uptime[$key], 4) . ']'; + $value[] = '[' . $time . ',' . round((float) $uptime[$key], 4) . ']'; } } if($last_date) { // Was down before. // Record the first and last date as a string in the down array $down[] = '[' . $last_date . ',' . $time . ']'; + // add the number of microseconds of downtime to counter for % + $time_down += ($time - $last_date); $last_date = 0; } } else { @@ -198,24 +254,20 @@ class HistoryGraph { } if($last_date) { $down[] = '[' . $last_date . ',0]'; + $time = $end_time->getTimestamp() * 1000; + $time_down += ($time - $last_date); } - $buttons = array(); - $buttons[] = array('mode' => 'week2', 'label' => psm_get_lang('servers', 'week')); - $buttons[] = array('mode' => 'month', 'label' => psm_get_lang('servers', 'month'), 'class_active' => 'btn-info'); - $buttons[] = array('mode' => 'year', 'label' => psm_get_lang('servers', 'year')); - $data = array( - 'title' => psm_get_lang('servers', 'chart_history'), - 'latency_avg' => count($uptimes) > 0 ? round(($latency_avg / count($uptimes)), 4) : 0, - 'server_lines' => sizeof($lines_merged) ? '[' . implode(',', $lines_merged) . ']' : '', - 'server_down' => sizeof($down) ? '[' . implode(',', $down) . ']' : '', - 'series' => sizeof($series) ? '[' . implode(',', $series) . ']' : '', - 'plotmode' => 'month', - 'end_timestamp' => $time_end ? $time_end : '', - 'buttons' => $buttons, - // make sure to add chart id after buttons so its added to those tmeplates as well - 'chart_id' => $server_id . '_history', - ); + if($add_uptime) { + $data['uptime'] = 100 - (($time_down / ($end_time->getTimestamp() - $start_time->getTimestamp())) / 10); + } + + $data['latency_avg'] = count($records) > 0 ? ($latency_avg / count($records)) : 0; + $data['server_lines'] = sizeof($lines_merged) ? '[' . implode(',', $lines_merged) . ']' : ''; + $data['server_down'] = sizeof($down) ? '[' . implode(',', $down) . ']' : ''; + $data['series'] = sizeof($series) ? '[' . implode(',', $series) . ']' : ''; + $data['end_timestamp'] = $end_time->getTimestamp() * 1000; + return $data; } -} \ No newline at end of file +} diff --git a/src/psm/Util/Server/ServerValidator.class.php b/src/psm/Util/Server/ServerValidator.class.php new file mode 100644 index 00000000..5644114c --- /dev/null +++ b/src/psm/Util/Server/ServerValidator.class.php @@ -0,0 +1,134 @@ +. + * + * @package phpservermon + * @author Pepijn Over + * @copyright Copyright (c) 2008-2014 Pepijn Over + * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 + * @version Release: @package_version@ + * @link http://www.phpservermonitor.org/ + * @since phpservermon 3.1.0 + **/ + +namespace psm\Util\Server; + +/** + * The ServerValidator helps you to check input data for servers. + */ +class ServerValidator { + + /** + * Database service + * @var \psm\Service\Database $db + */ + protected $db; + + public function __construct(\psm\Service\Database $db) { + $this->db = $db; + } + + /** + * Check if the server id exists + * @param int $server_id + * @return boolean + * @throws \InvalidArgumentException + */ + public function serverId($server_id) { + $server = $this->db->selectRow(PSM_DB_PREFIX . 'servers', array('server_id' => $server_id), array('server_id')); + + if(empty($server)) { + throw new \InvalidArgumentException('server_no_match'); + } + return true; + } + + /** + * Check label + * @param string $label + * @return boolean + * @throws \InvalidArgumentException + */ + public function label($label) { + $label = trim($label); + if(empty($label) || strlen($label) > 255) { + throw new \InvalidArgumentException('server_label_bad_length'); + } + return true; + } + + /** + * Check server domain/ip + * @param string $value + * @param string $type if given, it can be checked for "website"/"ip" + * @return boolean + * @throws \InvalidArgumentException + */ + public function ip($value, $type = null) { + $value = trim($value); + + if(empty($value) || strlen($value) > 255) { + throw new \InvalidArgumentException('server_ip_bad_length'); + } + + switch($type) { + case 'website': + if(!filter_var($value, FILTER_VALIDATE_URL)) { + throw new \InvalidArgumentException('server_ip_bad_website'); + } + break; + case 'service': + if( + !filter_var($value, FILTER_VALIDATE_IP) + // domain regex as per http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address : + && !preg_match("/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])/", $value) + ) { + throw new \InvalidArgumentException('server_ip_bad_service'); + } + break; + } + + return true; + } + + /** + * Check server type + * @param string $type + * @return boolean + * @throws \InvalidArgumentException + */ + public function type($type) { + if(!in_array($type, array('service', 'website'))) { + throw new \InvalidArgumentException('server_type_invalid'); + } + return true; + } + + /** + * Check warning threshold + * @param int $value + * @return boolean + * @throws \InvalidArgumentException + */ + public function warningThreshold($value) { + if(!is_numeric($value) || intval($value) == 0) { + throw new \InvalidArgumentException('server_warning_threshold_invalid'); + } + return true; + } +} diff --git a/src/psm/Util/Updater/Autorun.class.php b/src/psm/Util/Server/UpdateManager.class.php similarity index 87% rename from src/psm/Util/Updater/Autorun.class.php rename to src/psm/Util/Server/UpdateManager.class.php index 681456e1..0a09a63a 100644 --- a/src/psm/Util/Updater/Autorun.class.php +++ b/src/psm/Util/Server/UpdateManager.class.php @@ -26,7 +26,7 @@ * @since phpservermon 3.0.0 **/ -namespace psm\Util\Updater; +namespace psm\Util\Server; use psm\Service\Database; use psm\Service\User; @@ -36,7 +36,7 @@ use psm\Service\User; * If you provide a User service instance it will be * restricted to that user only. */ -class Autorun { +class UpdateManager { /** * Database service @@ -69,29 +69,27 @@ class Autorun { )"; } - $sql = "SELECT `s`.`server_id`,`s`.`ip`,`s`.`port`,`s`.`label`,`s`.`type`,`s`.`pattern`,`s`.`status`,`s`.`active`,`s`.`email`,`s`.`sms` + $sql = "SELECT `s`.`server_id`,`s`.`ip`,`s`.`port`,`s`.`label`,`s`.`type`,`s`.`pattern`,`s`.`status`,`s`.`active`,`s`.`email`,`s`.`sms`,`s`.`pushover` FROM `".PSM_DB_PREFIX."servers` AS `s` {$sql_join} WHERE `active`='yes' "; $servers = $this->db->query($sql); - $updater = new StatusUpdater($this->db); - $notifier = new StatusNotifier($this->db); - - $archiver = new StatusArchiver($this->db); - $cleanup_date = new \DateTime(); - $cleanup_date->modify('-1 week'); + $updater = new Updater\StatusUpdater($this->db); + $notifier = new Updater\StatusNotifier($this->db); foreach($servers as $server) { $status_old = ($server['status'] == 'on') ? true : false; $status_new = $updater->update($server['server_id']); // notify the nerds if applicable $notifier->notify($server['server_id'], $status_old, $status_new); - - // clean-up time!! archive all records older than 1 week - $archiver->archive($server['server_id'], $cleanup_date); } + + // clean-up time!! archive all records + $archive = new ArchiveManager($this->db); + $archive->archive(); + $archive->cleanup(); } /** diff --git a/src/psm/Util/Updater/StatusNotifier.class.php b/src/psm/Util/Server/Updater/StatusNotifier.class.php similarity index 71% rename from src/psm/Util/Updater/StatusNotifier.class.php rename to src/psm/Util/Server/Updater/StatusNotifier.class.php index 0d07ad43..028889f0 100644 --- a/src/psm/Util/Updater/StatusNotifier.class.php +++ b/src/psm/Util/Server/Updater/StatusNotifier.class.php @@ -28,10 +28,10 @@ /** * The status updater is for sending notifications to the users. * - * @see \psm\Util\Updater\StatusUpdater - * @see \psm\Util\Updater\Autorun + * @see \psm\Util\Server\Updater\StatusUpdater + * @see \psm\Util\Server\Updater\Autorun */ -namespace psm\Util\Updater; +namespace psm\Util\Server\Updater; use psm\Service\Database; class StatusNotifier { @@ -54,6 +54,12 @@ class StatusNotifier { */ protected $send_sms = false; + /** + * Send sms? + * @var boolean $send_pushover + */ + protected $send_pushover = false; + /** * Save log records? * @var boolean $save_log @@ -89,6 +95,7 @@ class StatusNotifier { $this->send_emails = psm_get_conf('email_status'); $this->send_sms = psm_get_conf('sms_status'); + $this->send_pushover = psm_get_conf('pushover_status'); $this->save_logs = psm_get_conf('log_status'); } @@ -114,7 +121,7 @@ class StatusNotifier { $this->server = $this->db->selectRow(PSM_DB_PREFIX . 'servers', array( 'server_id' => $server_id, ), array( - 'server_id', 'ip', 'port', 'label', 'type', 'pattern', 'status', 'error', 'active', 'email', 'sms', + 'server_id', 'ip', 'port', 'label', 'type', 'pattern', 'status', 'error', 'active', 'email', 'sms', 'pushover', )); if(empty($this->server)) { return false; @@ -157,16 +164,28 @@ class StatusNotifier { ); } + $users = $this->getUsers($this->server_id); + + if(empty($users)) { + return $notify; + } + // check if email is enabled for this server if($this->send_emails && $this->server['email'] == 'yes') { // send email - $this->notifyByEmail(); + $this->notifyByEmail($users); } // check if sms is enabled for this server if($this->send_sms && $this->server['sms'] == 'yes') { // yay lets wake those nerds up! - $this->notifyByTxtMsg(); + $this->notifyByTxtMsg($users); + } + + // check if pushover is enabled for this server + if($this->send_pushover && $this->server['pushover'] == 'yes') { + // yay lets wake those nerds up! + $this->notifyByPushover($users); } return $notify; @@ -175,17 +194,12 @@ class StatusNotifier { /** * This functions performs the email notifications * + * @param array $users * @return boolean */ - protected function notifyByEmail() { + protected function notifyByEmail($users) { $userlist = array(); - $users = $this->getUsers($this->server_id); - - if (empty($users)) { - return false; - } - // build mail object with some default values $mail = psm_build_mail(); $mail->Subject = psm_parse_msg($this->status_new, 'email_subject', $this->server); @@ -210,50 +224,63 @@ class StatusNotifier { } } + /** + * This functions performs the pushover notifications + * + * @param array $users + * @return boolean + */ + protected function notifyByPushover($users) { + $userlist = array(); + $pushover = psm_build_pushover(); + + if($this->status_new === true) { + $pushover->setPriority(0); + } else { + $pushover->setPriority(2); + $pushover->setRetry(300); //Used with Priority = 2; Pushover will resend the notification every 60 seconds until the user accepts. + $pushover->setExpire(3600); //Used with Priority = 2; Pushover will resend the notification every 60 seconds for 3600 seconds. After that point, it stops sending notifications. + } + $message = psm_parse_msg($this->status_new, 'pushover_message', $this->server); + + $pushover->setTitle(psm_parse_msg($this->status_new, 'pushover_title', $this->server)); + $pushover->setMessage(str_replace('
', "\n", $message)); + // @todo fix url when script is executed via CLI +// $pushover->setUrl($url); +// $pushover->setUrlTitle(psm_get_lang('system', 'title')); + + foreach($users as $user) { + if(trim($user['pushover_key']) == '') { + continue; + } + $userlist[] = $user['user_id']; + $pushover->setUser($user['pushover_key']); + if($user['pushover_device'] != '') { + $pushover->setDevice($user['pushover_device']); + } + $pushover->send(); + } + + if(psm_get_conf('log_pushover')) { + psm_add_log($this->server_id, 'pushover', $message, implode(',', $userlist)); + } + } + /** * This functions performs the text message notifications * - * @return unknown + * @param array $users + * @return boolean */ - protected function notifyByTxtMsg() { - // send sms to all users for this server using defined gateway - $users = $this->getUsers($this->server_id); - - if (empty($users)) { + protected function notifyByTxtMsg($users) { + $sms = psm_build_sms(); + if(!$sms) { return false; } // we have to build an userlist for the log table.. $userlist = array(); - // open the right class - // not making this any more dynamic, because perhaps some gateways need custom settings (like Mollie) - switch(strtolower(psm_get_conf('sms_gateway'))) { - case 'mosms': - $sms = new \psm\Txtmsg\Mosms(); - break; - case 'inetworx': - $sms = new \psm\Txtmsg\Inetworx(); - break; - case 'mollie': - $sms = new \psm\Txtmsg\Mollie(); - $sms->setGateway(1); - break; - case 'spryng': - $sms = new \psm\Txtmsg\Spryng(); - break; - case 'clickatell': - $sms = new \psm\Txtmsg\Clickatell(); - break; - case 'textmarketer': - $sms = new \psm\Txtmsg\Textmarketer(); - break; - } - - // copy login information from the config file - $sms->setLogin(psm_get_conf('sms_gateway_username'), psm_get_conf('sms_gateway_password')); - $sms->setOriginator(psm_get_conf('sms_from')); - // add all users to the recipients list foreach ($users as $user) { $userlist[] = $user['user_id']; @@ -280,7 +307,7 @@ class StatusNotifier { public function getUsers($server_id) { // find all the users with this server listed $users = $this->db->query(" - SELECT `u`.`user_id`, `u`.`name`,`u`.`email`, `u`.`mobile` + SELECT `u`.`user_id`, `u`.`name`,`u`.`email`, `u`.`mobile`, `u`.`pushover_key`, `u`.`pushover_device` FROM `".PSM_DB_PREFIX."users` AS `u` JOIN `".PSM_DB_PREFIX."users_servers` AS `us` ON ( `us`.`user_id`=`u`.`user_id` diff --git a/src/psm/Util/Updater/StatusUpdater.class.php b/src/psm/Util/Server/Updater/StatusUpdater.class.php similarity index 95% rename from src/psm/Util/Updater/StatusUpdater.class.php rename to src/psm/Util/Server/Updater/StatusUpdater.class.php index dacd784a..acd7bd9e 100644 --- a/src/psm/Util/Updater/StatusUpdater.class.php +++ b/src/psm/Util/Server/Updater/StatusUpdater.class.php @@ -28,10 +28,10 @@ /** * The status class is for checking the status of a server. * - * @see \psm\Util\Updater\StatusNotifier - * @see \psm\Util\Updater\Autorun + * @see \psm\Util\Server\Updater\StatusNotifier + * @see \psm\Util\Server\Updater\Autorun */ -namespace psm\Util\Updater; +namespace psm\Util\Server\Updater; use psm\Service\Database; class StatusUpdater { @@ -82,7 +82,7 @@ class StatusUpdater { $this->server = $this->db->selectRow(PSM_DB_PREFIX . 'servers', array( 'server_id' => $server_id, ), array( - 'server_id', 'ip', 'port', 'label', 'type', 'pattern', 'status', 'active', 'warning_threshold', 'warning_threshold_counter', + 'server_id', 'ip', 'port', 'label', 'type', 'pattern', 'status', 'active', 'warning_threshold', 'warning_threshold_counter', 'timeout', )); if(empty($this->server)) { return false; @@ -149,7 +149,9 @@ class StatusUpdater { $status = ($fp === false) ? false : true; $this->rtime = (microtime(true) - $starttime); - fclose($fp); + if(is_resource($fp)) { + fclose($fp); + } // check if server is available and rerun if asked. if(!$status && $run < $max_runs) { @@ -173,7 +175,8 @@ class StatusUpdater { $curl_result = psm_curl_get( $this->server['ip'], true, - ($this->server['pattern'] == '' ? false : true) + ($this->server['pattern'] == '' ? false : true), + $this->server['timeout'] ); $this->rtime = (microtime(true) - $starttime); diff --git a/src/psm/Util/Updater/StatusArchiver.class.php b/src/psm/Util/Updater/StatusArchiver.class.php deleted file mode 100644 index 9ddb33f6..00000000 --- a/src/psm/Util/Updater/StatusArchiver.class.php +++ /dev/null @@ -1,140 +0,0 @@ -. - * - * @package phpservermon - * @author Pepijn Over - * @copyright Copyright (c) 2008-2014 Pepijn Over - * @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3 - * @version Release: @package_version@ - * @link http://www.phpservermonitor.org/ - **/ - -/** - * The archiver class moves active data from the uptime table to the history table. - * - * Because the uptime table has a record for every single run of the status-check, - * it will grow very large over time. For this reason, uptime records are only kept for a limited time - * to provide detailed statistics. After that, the archiver comes in and saves the averages per day - * in the history table. That way we can always show statistics regarding average latency and failed checks per day, - * but we only need 1 record per server per day. - * - * @see \psm\Util\Updater\Autorun - */ -namespace psm\Util\Updater; -use psm\Service\Database; - -class StatusArchiver { - - /** - * Database service - * @var \psm\Service\Database $db - */ - protected $db; - - function __construct(Database $db) { - $this->db = $db; - } - - /** - * Archive the active records of a server before a certain date. - * - * Archiving means calculating averages per day, and storing 1 single - * history row for each day for this server. - * - * @param int $server_id - * @param \DateTime $date_before archive all records before this date - */ - public function archive($server_id, \DateTime $date_before) { - // get all uptime records for this server - $q_records = $this->db->pdo()->prepare(" - SELECT `date`,`status`,`latency` - FROM `".PSM_DB_PREFIX."servers_uptime` - WHERE `server_id` = :server_id AND `date` < :latest_date - "); - $q_records->execute(array( - 'server_id' => $server_id, - 'latest_date' => $date_before->format('Y-m-d 00:00:00'), - )); - $records = $q_records->fetchAll(); - - if(empty($records)) { - return false; - } - - $data_by_day = array(); - - // first group all records by day - foreach($records as $record) { - $day = date('Y-m-d', strtotime($record['date'])); - if(!isset($data_by_day[$day])) { - $data_by_day[$day] = array(); - } - $data_by_day[$day][] = $record; - } - - // now lets sort out and save the history day by day - foreach($data_by_day as $day => $day_records) { - $history = $this->getHistoryForDay($day, $day_records); - $history['server_id'] = $server_id; - - // store the history for this day in the history table - $this->db->save(PSM_DB_PREFIX.'servers_history', $history); - } - - // now remove all records from the uptime table - $q_records_cleanup = $this->db->pdo()->prepare(" - DELETE FROM `".PSM_DB_PREFIX."servers_uptime` - WHERE `server_id` = :server_id AND `date` < :latest_date - "); - $q_records_cleanup->execute(array( - 'server_id' => $server_id, - 'latest_date' => $date_before->format('Y-m-d 00:00:00'), - )); - } - - /** - * Build a history array for a certain day and its records - * @param string $day - * @param array $day_records - * @return array - */ - protected function getHistoryForDay($day, $day_records) { - $latencies = array(); - $checks_failed = 0; - - foreach($day_records as $day_record) { - $latencies[] = $day_record['latency']; - - if($day_record['status'] == 0) { - $checks_failed++; - } - } - sort($latencies, SORT_NUMERIC); - - $history = array( - 'date' => $day, - 'latency_min' => min($latencies), - 'latency_avg' => array_sum($latencies) / count($latencies), - 'latency_max' => max($latencies), - 'checks_total' => count($day_records), - 'checks_failed' => $checks_failed, - ); - return $history; - } -} \ No newline at end of file diff --git a/src/templates/config/config.tpl.html b/src/templates/config/config.tpl.html deleted file mode 100755 index ff9fcf96..00000000 --- a/src/templates/config/config.tpl.html +++ /dev/null @@ -1,166 +0,0 @@ - -
- -
-
-
- {label_general} -
- -
- -
-
-
-
- -
-
-
- -
-  {label_seconds} -

{label_auto_refresh_servers}

-
-
- {label_settings_notification} -
- -
- -

{label_alert_type_description}

-
-
-
- -
-
-
-
-
- {label_settings_email} -
-
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
- -
- - -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
-
-
-
- {label_settings_sms} -
-
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
-
-
-
- {label_settings_log} -
- -

{label_log_status_description}

-
-
- -
-
- -
-
- -
-
-
-
-
- \ No newline at end of file diff --git a/src/templates/default/main/body.tpl.html b/src/templates/default/main/body.tpl.html new file mode 100644 index 00000000..95a14490 --- /dev/null +++ b/src/templates/default/main/body.tpl.html @@ -0,0 +1,79 @@ +{% block header %} + + + + + {{ title }} + + + + + + {% if auto_refresh %} + + {% endif %} + + + + + + + + + + + + +{% endblock %} + + + + + + {{ html_modal|raw }} + +
+ +
+ {{ html_sidebar|raw }} +
+
+ {% for msg in messages %} +
+

+

{{ msg.message }}

+
+ {% endfor %} +
+ {{ html_content|raw }} +
+
+ {% if add_footer %} + {% block footer %} + + {% endblock %} + {% endif %} +
+ + + \ No newline at end of file diff --git a/src/templates/default/main/menu.tpl.html b/src/templates/default/main/menu.tpl.html new file mode 100644 index 00000000..7ffeba0c --- /dev/null +++ b/src/templates/default/main/menu.tpl.html @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/src/templates/default/module/config/config.tpl.html b/src/templates/default/module/config/config.tpl.html new file mode 100644 index 00000000..88b223b8 --- /dev/null +++ b/src/templates/default/module/config/config.tpl.html @@ -0,0 +1,217 @@ +
+ +
+
+
+ {{ label_general }} +
+ +
+ +
+
+
+
+ +
+
+
+ +
+  {{ label_seconds }} +
+
+
+
+ {{ label_settings_notification }} +
+ +
+ +

{{ label_alert_type_description|raw }}

+
+
+
+
+ {{ label_settings_log }} +
+
+ +
+
+
+ +
+  {{ label_log_retention_days }} +
+
+
+ +
+
+
+ +
+
+ {{ label_settings_sms }} +
+
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+ +
+
+
+
+
+ {{ label_settings_pushover }} +
+
+ +

{{ label_pushover_description|raw }}

+
+
+
+
+ +
+
+
+ +
+

+ +

{{ label_pushover_api_token_description|raw }}

+
+
+
+
+ + +
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/templates/default/module/error/401.tpl.html b/src/templates/default/module/error/401.tpl.html new file mode 100644 index 00000000..0733878d --- /dev/null +++ b/src/templates/default/module/error/401.tpl.html @@ -0,0 +1,2 @@ +

{{ label_title }}

+

{{ label_description }}

\ No newline at end of file diff --git a/src/templates/default/module/install/config_new.tpl.html b/src/templates/default/module/install/config_new.tpl.html new file mode 100644 index 00000000..f7bc1712 --- /dev/null +++ b/src/templates/default/module/install/config_new.tpl.html @@ -0,0 +1,61 @@ +{% extends "module/install/main.tpl.html" %} +{% use "module/install/results.tpl.html" %} + +{% block install %} +
+
{{ block('results') }}
+
+
+
+
+

Please enter your database info:

+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ {% if include_config_new_copy %} +
+

Your config file:

+
Unable to save your configuration.
+

Your database information is valid, however we are unable to create the configuration file automatically. + Please create a new file in the project directory called "config.php" and copy the information below.

+

After you have copied the configuration, press the button to continue.

+

+

+
+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/src/templates/default/module/install/config_new_user.tpl.html b/src/templates/default/module/install/config_new_user.tpl.html new file mode 100644 index 00000000..28bddbb1 --- /dev/null +++ b/src/templates/default/module/install/config_new_user.tpl.html @@ -0,0 +1,44 @@ +{% extends "module/install/main.tpl.html" %} +{% use "module/install/results.tpl.html" %} + +{% block install %} +

Sweet, your database connection is up and running!

+

Next, please set up a new account to access your monitor:

+{{ block('results') }} +

 

+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/src/templates/default/module/install/config_upgrade.tpl.html b/src/templates/default/module/install/config_upgrade.tpl.html new file mode 100644 index 00000000..d233b515 --- /dev/null +++ b/src/templates/default/module/install/config_upgrade.tpl.html @@ -0,0 +1,9 @@ +{% extends "module/install/main.tpl.html" %} + +{% block install %} +

We have discovered a previous version.

+

In the next step we will upgrade your database to the latest version.

+{{ block('results') }} +

 

+

Upgrade to {{ version }}

+{% endblock %} \ No newline at end of file diff --git a/src/templates/default/module/install/index.tpl.html b/src/templates/default/module/install/index.tpl.html new file mode 100644 index 00000000..a3edb8e4 --- /dev/null +++ b/src/templates/default/module/install/index.tpl.html @@ -0,0 +1,15 @@ +{% extends "module/install/main.tpl.html" %} +{% use "module/install/results.tpl.html" %} + +{% block install %} +

Welcome to the installation of PHP Server Monitor. This page will guide you through the steps to install or upgrade your monitor.

+

Before we start, we need to verify your system meets the requirements. + If you see any errors in the list below, you may still continue, but PHP Server Monitor may not work correctly. + It is recommended you fix any errors before continuing. +

+{{ block('results') }} +

 

+

+ Let's go +

+{% endblock %} \ No newline at end of file diff --git a/src/templates/default/module/install/main.tpl.html b/src/templates/default/module/install/main.tpl.html new file mode 100644 index 00000000..2378f96d --- /dev/null +++ b/src/templates/default/module/install/main.tpl.html @@ -0,0 +1,8 @@ +
+
+

+

 PHP Server Monitor

+

+
+ {% block install %} {% endblock %} +
\ No newline at end of file diff --git a/src/templates/default/module/install/results.tpl.html b/src/templates/default/module/install/results.tpl.html new file mode 100644 index 00000000..6a4085b9 --- /dev/null +++ b/src/templates/default/module/install/results.tpl.html @@ -0,0 +1,10 @@ +{% block results %} +{% if messages %} + {% for msg in messages %} +
+

{{ msg.shortcode }}

+

{{ msg.message }}

+
+ {% endfor %} +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/src/templates/default/module/install/success.tpl.html b/src/templates/default/module/install/success.tpl.html new file mode 100644 index 00000000..0df73c09 --- /dev/null +++ b/src/templates/default/module/install/success.tpl.html @@ -0,0 +1,20 @@ +{% extends "module/install/main.tpl.html" %} +{% use "module/install/results.tpl.html" %} + +{% block install %} +
+
{{ block('results') }}
+
+
+
+

 

+

The installation is complete. Please check above if errors have occurred.
+If no errors have occurred, you are good to go.

+

+ Go to your monitor + PHP Server Monitor + Documentation +

+
+
+{% endblock %} \ No newline at end of file diff --git a/src/templates/server/history.tpl.html b/src/templates/default/module/server/history.tpl.html similarity index 58% rename from src/templates/server/history.tpl.html rename to src/templates/default/module/server/history.tpl.html index a36d00db..7a2aeaab 100644 --- a/src/templates/server/history.tpl.html +++ b/src/templates/default/module/server/history.tpl.html @@ -1,4 +1,3 @@ - @@ -9,19 +8,19 @@
- +{% for graph in graphs %}
-
+
@@ -29,25 +28,28 @@
- {label_latency_avg}: {latency_avg}
+
    + {% for record in graph.info %} +
  • {{ record.label }}: {{ record.value }}
  • + {% endfor %} +
- - - - {buttons} + {% for button in graph.buttons %} + + {% endfor %}
 
- -{graphs} - - \ No newline at end of file +{% endfor %} + \ No newline at end of file diff --git a/src/templates/default/module/server/log.tpl.html b/src/templates/default/module/server/log.tpl.html new file mode 100644 index 00000000..5ee80bb8 --- /dev/null +++ b/src/templates/default/module/server/log.tpl.html @@ -0,0 +1,60 @@ +
+ +
+ {% for tab in tabs %} +
+ + + + + + + {% if tab.has_users %} {% endif %} + + + + {% for entry in tab.entries %} + + + + + {% if tab.has_users %}{% endif %} + + {% endfor %} + {% if tab.no_logs %} + + + + {% endif %} + +
{{ label_server }}{{ label_message }}{{ label_date }}{{ label_users }}
+
+
+
+  {{ entry.datetime_format }} +
+ {{ entry.server }} {{ entry.ip }} +
+
+
+
+
+
{{ entry.message|raw }}
+
+
+ {% if tab.has_users %}
+
+
+
{{ label_users }}: {{ entry.user_list|raw }}
+
+
{% endif %} +
{{ entry.message|raw }}{{ entry.datetime_format }}{{ entry.users|raw }}
{{ label_no_logs }}
+
+ {% endfor %} +
+
\ No newline at end of file diff --git a/src/templates/default/module/server/server/list.tpl.html b/src/templates/default/module/server/server/list.tpl.html new file mode 100644 index 00000000..9427ad9f --- /dev/null +++ b/src/templates/default/module/server/server/list.tpl.html @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + {% for server in servers %} + + + + + + + + + + + + + + {% endfor %} + +
 {{ label_label }}{{ label_domain }}{{ label_port }}{{ label_type }}{{ label_rtime }}{{ label_last_online }}{{ label_monitoring }}{{ label_action }}
{{ server.ip_short|raw }}
{{ server.ip|raw }}
{{ server.port }}
{{ server.type }}
{{ server.rtime }} s
{{ server.last_online }}
+
+ + {% if user_level == 10 %} + + + {% endif %} +
+
+
+ +
+   + {% if user_level == 10 %} + + + {% endif %} +
+
+
+
+
{{ server.ip_short|raw }}
+
+ +
+
+
+
+
{{ label_rtime }}:  
{{ server.rtime }} s
+
{{ label_last_online }}:  
{{ server.last_online }}
+
+
\ No newline at end of file diff --git a/src/templates/default/module/server/server/update.tpl.html b/src/templates/default/module/server/server/update.tpl.html new file mode 100644 index 00000000..5dcd46d6 --- /dev/null +++ b/src/templates/default/module/server/server/update.tpl.html @@ -0,0 +1,106 @@ +
+
+ {{ titlemode }} +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ s +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + {{ label_go_back }} +
+
+
\ No newline at end of file diff --git a/src/templates/default/module/server/server/view.tpl.html b/src/templates/default/module/server/server/view.tpl.html new file mode 100644 index 00000000..8b95c7f5 --- /dev/null +++ b/src/templates/default/module/server/server/view.tpl.html @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% if has_admin_actions %} + + + + + {% endif %} + +
+
+ + +
+
{{ label_status }}: + + {{ status }} +  {{ error }} +
{{ label_type }}:{{ type }}
{{ label_domain }}:{{ ip|raw }}
{{ label_port }}:{{ port }}
{{ label_last_check }}:{{ last_check }}
{{ label_last_online }}:{{ last_online }}
{{ label_rtime }}:{{ rtime }} s
{{ label_monitoring }}:{{ active }}
{{ label_email }}:{{ email }}
{{ label_sms }}:{{ sms }}
{{ label_pushover }}:{{ pushover }}
{{ label_timeout }}:{{ timeout }} s
  + +  {{ label_edit }} + + +  {{ label_delete }} + +
+{{ html_history|raw }} \ No newline at end of file diff --git a/src/templates/default/module/server/status/header.tpl.html b/src/templates/default/module/server/status/header.tpl.html new file mode 100644 index 00000000..d6455f4a --- /dev/null +++ b/src/templates/default/module/server/status/header.tpl.html @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/src/templates/default/module/server/status/index.tpl.html b/src/templates/default/module/server/status/index.tpl.html new file mode 100644 index 00000000..4f579206 --- /dev/null +++ b/src/templates/default/module/server/status/index.tpl.html @@ -0,0 +1,60 @@ +
+
+
+ {% for server in servers_offline %} +
+
+

{{ server.label }}

+

{{ label_last_online }}: {{ server.last_online_nice }}

+

{{ label_last_check }}: {{ server.last_checked_nice }}

+
+
+ {% endfor %} + {% for server in servers_online %} +
+
+

{{ server.label }}

+

{{ label_last_online }}: {{ server.last_online_nice }}

+

{{ label_rtime }}: {{ server.rtime }}s

+
+
+ {% endfor %} +
+
+
+
+ + + {% for server in servers_offline %} + + + + + + {% endfor %} + {% for server in servers_online %} + + + + + + {% endfor %} + +
{{ server.label }}
+
+
+
{{ label_last_online }}:  
{{ server.last_online_nice }}
+
{{ label_last_check }}:  
{{ server.last_checked_nice }}
+
+
+
{{ label_last_online }}: {{ server.last_online_nice }}{{ label_last_check }}: {{ server.last_checked_nice }}
{{ server.label }}
+
+
+
{{ label_last_online }}:  
{{ server.last_online_nice }}
+
{{ label_rtime }}:  
{{ server.rtime }}
+
+
+
{{ label_last_online }}: {{ server.last_online_nice }}{{ label_rtime }}: {{ server.rtime }}s
+
+
+
\ No newline at end of file diff --git a/src/templates/default/module/user/login/forgot.tpl.html b/src/templates/default/module/user/login/forgot.tpl.html new file mode 100644 index 00000000..5d669239 --- /dev/null +++ b/src/templates/default/module/user/login/forgot.tpl.html @@ -0,0 +1,8 @@ +
+ +
\ No newline at end of file diff --git a/src/templates/default/module/user/login/login.tpl.html b/src/templates/default/module/user/login/login.tpl.html new file mode 100644 index 00000000..0bf89cd7 --- /dev/null +++ b/src/templates/default/module/user/login/login.tpl.html @@ -0,0 +1,13 @@ +
+ +
\ No newline at end of file diff --git a/src/templates/default/module/user/login/reset.tpl.html b/src/templates/default/module/user/login/reset.tpl.html new file mode 100644 index 00000000..24151e6e --- /dev/null +++ b/src/templates/default/module/user/login/reset.tpl.html @@ -0,0 +1,10 @@ +
+ +
\ No newline at end of file diff --git a/src/templates/default/module/user/profile.tpl.html b/src/templates/default/module/user/profile.tpl.html new file mode 100644 index 00000000..f6c78f99 --- /dev/null +++ b/src/templates/default/module/user/profile.tpl.html @@ -0,0 +1,71 @@ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
{{ label_pushover_description|raw }}
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/src/templates/default/module/user/user/list.tpl.html b/src/templates/default/module/user/user/list.tpl.html new file mode 100644 index 00000000..7043b358 --- /dev/null +++ b/src/templates/default/module/user/user/list.tpl.html @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + {% for user in users %} + + + + + + + + + + {% endfor %} + +
{{ label_user }}{{ label_name }}{{ label_level }}{{ label_email }}{{ label_mobile }}{{ label_servers }}{{ label_action }}
+
+
+ + + {% if user.level == '10' %} +   + {% elseif user.level == '20' %} +   + {% endif %} + + {{ user.user_name }} + + ({{ user.name }}) +
+
+ +
+
+
+
+
{{ label_email }}: 
{{ user.email }}
+
{{ label_mobile }}: 
{{ user.mobile }}
+
{{ label_servers }}: 
{% for server in user.emp_servers %} {{ server.label }}
{% endfor %}
+
+
+
{{ user.name }}
{{ user.level_text }}
{{ user.email }}
{{ user.mobile }}
{% for server in user.emp_servers %} {{ server.label }}
{% endfor %}
+ + + + + + +
\ No newline at end of file diff --git a/src/templates/default/module/user/user/update.tpl.html b/src/templates/default/module/user/user/update.tpl.html new file mode 100644 index 00000000..8c587117 --- /dev/null +++ b/src/templates/default/module/user/user/update.tpl.html @@ -0,0 +1,82 @@ +
+
+ {{ titlemode }} +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +

{{ label_level_description|raw }}

+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
{{ label_pushover_description|raw }}
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +
+
+
\ No newline at end of file diff --git a/src/templates/default/util/module/modal.tpl.html b/src/templates/default/util/module/modal.tpl.html new file mode 100644 index 00000000..56ee6eed --- /dev/null +++ b/src/templates/default/util/module/modal.tpl.html @@ -0,0 +1,14 @@ + diff --git a/src/templates/default/util/module/sidebar.tpl.html b/src/templates/default/util/module/sidebar.tpl.html new file mode 100644 index 00000000..0588d8c8 --- /dev/null +++ b/src/templates/default/util/module/sidebar.tpl.html @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/src/templates/install/install.tpl.html b/src/templates/install/install.tpl.html deleted file mode 100755 index c57e973b..00000000 --- a/src/templates/install/install.tpl.html +++ /dev/null @@ -1,160 +0,0 @@ - -
-

 PHP Server Monitor

-

 

- {html_install} -
- - - -

Welcome to the installation of PHP Server Monitor. This page will guide you through the steps to install or upgrade your monitor.

-

Before we start, we need to verify your system meets the requirements. - If you see any errors in the list below, you may still continue, but PHP Server Monitor may not work correctly. - It is recommended you fix any errors before continuing. -

-{html_results} -

 

-

- Let's go -

- - - -

We have discovered a previous version.

-

In the next step we will upgrade your database to the latest version.

-{html_results} -

 

-

Upgrade to {version}

- - - -

Sweet, your database connection is up and running!

-

Next, please set up a new account to access your monitor:

-{html_results} -

 

-
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
-
-{html_config_copy} -
- - - -
-
{html_results}
-
-
-
-
-

Please enter your database info:

-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
-
-{html_config_copy} -
- - - -
-

Your config file:

-
Unable to save your configuration.
-

Your database information is valid, however we are unable to create the configuration file automatically. - Please create a new file in the project directory called "config.php" and copy the information below.

-

After you have copied the configuration, press the button to continue.

-

-

-
- - - -
-
{html_results}
-
-
-
-

 

-

The installation is complete. Please check above if errors have occurred.
-If no errors have occurred, you are good to go.

-

- Go to your monitor - PHP Server Monitor - Twitter Bootstrap -

-
-
- - - - -
-

{shortcode}

-

{message}

-
- -{resultmsgs} - \ No newline at end of file diff --git a/src/templates/main.tpl.html b/src/templates/main.tpl.html deleted file mode 100755 index 85f556bf..00000000 --- a/src/templates/main.tpl.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - {title} - - - - {auto_refresh} - - - - - - - - - - - - - - - - - -
- -
- {html_sidebar} -
-
- -
-

-

{message}

-
- - {messages} -
- {content} -
-
- {html_footer} -
- - - - - - - - - - - - - - - - diff --git a/src/templates/main_sidebar.tpl.html b/src/templates/main_sidebar.tpl.html deleted file mode 100755 index c5defbc5..00000000 --- a/src/templates/main_sidebar.tpl.html +++ /dev/null @@ -1,37 +0,0 @@ - -
- -
- - - {label} - - - - - - -
- - -
- \ No newline at end of file diff --git a/src/templates/server/log.tpl.html b/src/templates/server/log.tpl.html deleted file mode 100755 index c58d8123..00000000 --- a/src/templates/server/log.tpl.html +++ /dev/null @@ -1,51 +0,0 @@ - -
- -
-
- {content_status} -
-
- {content_email} -
-
- {content_sms} -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - {entries} - -
{label_server}{label_message}{label_date}{label_users}
{server}{message}{datetime_format}{users}
- \ No newline at end of file diff --git a/src/templates/server/server.tpl.html b/src/templates/server/server.tpl.html deleted file mode 100755 index 1b738653..00000000 --- a/src/templates/server/server.tpl.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {servers} - -
 {label_label}{label_domain}{label_port}{label_type}{label_rtime}{label_last_online}{label_monitoring}{label_send_email}{label_send_sms}{label_action}
- - {status} - - {label}{ip}{port}{type}{rtime} s{last_online}{active}{email}{sms} - {html_actions}
- - - - - - - - - - - - -
-
- {titlemode} -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- - {label_go_back} -
-
-
- \ No newline at end of file diff --git a/src/templates/server/status.tpl.html b/src/templates/server/status.tpl.html deleted file mode 100755 index eb870da5..00000000 --- a/src/templates/server/status.tpl.html +++ /dev/null @@ -1,43 +0,0 @@ - - -
- -
-
-

{label}

-

{label_last_online}: {last_online_nice}

-

{label_last_check}: {last_checked_nice}

-
-
- - {servers_offline} - -
-
-

{label}

-

{label_last_online}: {last_checked_nice}

-

{label_rtime}: {rtime}s

-
-
- - {servers_online} -
- \ No newline at end of file diff --git a/src/templates/server/view.tpl.html b/src/templates/server/view.tpl.html deleted file mode 100644 index efae2383..00000000 --- a/src/templates/server/view.tpl.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {html_actions} - -
-
- - -
-
{label_status}: - - {status} -  {error} -
{label_type}:{type}
{label_domain}:{ip}
{label_port}:{port}
{label_last_check}:{last_check}
{label_last_online}:{last_online}
{label_rtime}:{rtime} s
{label_monitoring}:{active}
{label_send_email}:{email}
{label_send_sms}:{sms}
-{html_history} - - - - -   - - -  {label_edit} - - -  {label_delete} - - - - \ No newline at end of file diff --git a/src/templates/user/login.tpl.html b/src/templates/user/login.tpl.html deleted file mode 100755 index d43ee9e3..00000000 --- a/src/templates/user/login.tpl.html +++ /dev/null @@ -1,39 +0,0 @@ - -
- -
- - - -
- -
- - - -
- -
- \ No newline at end of file diff --git a/src/templates/user/profile.tpl.html b/src/templates/user/profile.tpl.html deleted file mode 100755 index 71e0b85e..00000000 --- a/src/templates/user/profile.tpl.html +++ /dev/null @@ -1,49 +0,0 @@ - -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
-
- \ No newline at end of file diff --git a/src/templates/user/user.tpl.html b/src/templates/user/user.tpl.html deleted file mode 100755 index a62a17db..00000000 --- a/src/templates/user/user.tpl.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - {users} - -
{label_name}{label_user_name}{label_level}{label_mobile}{label_email}{label_servers}{label_action}
{name}{user_name}{label_level_{level}}{mobile}{email}{emp_servers} - - - - - - -
- - - -
-
- {titlemode} -
- -
- -
-
-
- -
- -
-
-
- -
- -

- {label_level_description} -

-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- - - - {servers} -
-
-
- - -
-
-
- \ No newline at end of file diff --git a/static/css/history.css b/static/css/history.css index 51718506..30f01149 100644 --- a/static/css/history.css +++ b/static/css/history.css @@ -1,3 +1,7 @@ +#main-container { + overflow: hidden; +} + #history-panel { width: 100%; max-width: 1100px; @@ -39,6 +43,10 @@ padding-top: 40px; height: 200px; } +.server-info ul { + list-style: none; + margin: 0; +} .chart-selector { } @@ -47,11 +55,11 @@ .chart-container { margin-right: 0; } - + .chart-content { margin-right: 0; } - + .info-container { float: none; clear: both; @@ -62,11 +70,11 @@ display: block; float: left; } - + .server-info { display: none; } - + .chart-selector { float: right; } diff --git a/static/css/style.css b/static/css/style.css index bbcb3b6c..cd345552 100755 --- a/static/css/style.css +++ b/static/css/style.css @@ -10,6 +10,19 @@ body { -ms-user-select: none; user-select: none; } + +body.black_background { + background: black; +} + +body.black_background .page-header { + border-bottom: 0; +} + +body.black_background .page-header h1 { + color: #888; +} + .navbar-fixed-top { position: fixed; margin: 0; @@ -28,30 +41,138 @@ body { background-color: #444; } +#main-container { + padding-left: 20px; + padding-right: 20px; +} + +#main-content { + display: table; + width: 100%; +} + +#sidebar-container { + display: table-cell; + vertical-align: top; + width: 1px; + white-space: nowrap; +} + +#page-container { + display: table-cell; + vertical-align: top; +} + .powered { display: inline-block; white-space: nowrap; } +.page-header { + display: table; + width: 100%; +} +.header-label { + display: table-cell; +} +.header-accessories { + display: table-cell; + white-space: nowrap; + text-align: right; +} +.header-accessories .btn-group { + display: inline-block; + vertical-align: bottom; +} + +table { + background-color: white; +} .tab-content { display: block; width: auto; - padding-top: 10px; + padding-top: 0; padding-bottom: 0; } +.tab-content.well { + padding-top: 19px; +} .nav-tabs { + position: relative; + top: 1px; margin-left: 10px; margin-bottom: 0; border: 0; } .nav-tabs > .active > a, .nav-tabs > .active > a:hover { - background-color: whiteSmoke; + background-color: #f5f5f5; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .nav-tabs > li > a, .nav-tabs > li > a:hover { padding: 8px 8px; } +/* Tables */ +.align-middle { + vertical-align: middle; +} +.tight { + width: 10px; + white-space: nowrap; +} +.nowrap { + white-space: nowrap; +} + +.table-body { + display: table; + width: 100%; +} +.table-row { + display: table-row +} +.table-cell { + display: table-cell; + vertical-align: top; +} +.table-cell-title { + display: table-cell; + vertical-align: middle; + height: 32px; +} +.table-cell-title .label { + position: relative; + top: -2px; +} + +.table-separator { + margin: 4px 0; + border-top: 1px solid #EEE; + border-bottom: 1px solid #FFF; +} + +td.cell-center { + text-align: center; +} + +@media (max-width: 767px) { + .table-cell .title, .table-cell-title .title { + font-size: 1.1em; + font-weight: bold; + } +} +.table-cell-details { + display: table-cell; + font-size: 11px; + font-style: italic; + line-height: 13px; +} + +/* Form */ legend { + margin-top: 0px; margin-bottom: 0px; } .form-horizontal .control-label { @@ -72,6 +193,7 @@ legend { color: #666; } +/* Status page */ .offline, .online { display: inline-block; width: 100%; @@ -86,6 +208,13 @@ legend { margin-bottom: 20px; cursor: pointer; } +.entity h2 { + margin-bottom: 10px; +} +.entity p { + margin: 0; + padding: 0; +} .offline .entity { background: #a00000; color: #f7cece; @@ -101,15 +230,67 @@ legend { color: #d8f7ce; border: 2px solid #d8f7ce; } +.entity-container table td { + padding: 6px 8px; + vertical-align: middle; +} +.row-offline, .row-online { + cursor: pointer; +} +.row-offline td, .row-offline tr:hover td { + background: #a00000 !important; + color: #f7cece; +} +.row-offline td.warning, .row-offline tr:hover tr.warning { + background: #FAA732 !important; + color: #F3F3B1; +} +.row-online td, .row-online tr:hover td { + background: #53a000 !important; + color: #d8f7ce; +} +.entity-container .server-name { + display: inline-block; + font-size: 1.15em; + line-height: 18px; + font-weight: bold; + white-space: nowrap; +} +.visible-small { + display: none; +} + +td.visible-desktop, th.visible-desktop, +td.hidden-phone, th.hidden-phone, +td.hidden-tablet, th.hidden-tablet { + display: table-cell; +} + +span.visible-desktop, span.visible-desktop, +span.hidden-phone, span.hidden-phone, +span.hidden-tablet, span.hidden-tablet { + display: inline-block; +} + +span.visible-phone, span.visible-phone, +span.visible-tablet, span.visible-tablet, +span.hidden-desktop, span.hidden-desktop { + display: none; +} @media (min-width: 980px) { .hidden-desktop { display: none !important; } } - @media (max-width: 979px) { + #sidebar-container { + display: table-row; + } + #page-container { + display: table-row; + } .sidebar-nav .nav-list { padding: 0; } @@ -126,14 +307,49 @@ legend { .visible-desktop { display: none !important; } - .tab-content { + .tab-content.well { padding-left: 10px; padding-right: 10px; } } - +@media (max-width: 767px) { + td.visible-phone, th.visible-phone, + td.hidden-desktop, th.hidden-desktop { + display: table-cell; + } + span.visible-phone, span.visible-phone, + span.hidden-desktop, span.hidden-desktop { + display: inline-block; + } + td.hidden-phone, th.hidden-phone, + td.visible-desktop, th.visible-desktop { + display: none; + } + span.hidden-phone, span.hidden-phone, + span.visible-desktop, span.visible-desktop { + display: none; + } +} +@media (min-width: 768px) and (max-width: 979px) { + td.visible-tablet, th.visible-tablet, + td.hidden-desktop, th.hidden-desktop { + display: table-cell; + } + span.visible-tablet, span.visible-tablet, + span.hidden-desktop, span.hidden-desktop { + display: inline-block; + } + td.hidden-tablet, th.hidden-tablet, + td.visible-desktop, th.visible-desktop { + display: none; + } + span.hidden-tablet, span.hidden-tablet, + span.visible-desktop, span.visible-desktop { + display: none; + } +} @media (max-width: 479px) { - .container-fluid { + #main-container, .container-fluid { padding-left: 10px; padding-right: 10px; } @@ -151,18 +367,42 @@ legend { .entity { margin: 0 10px 20px 10px; } + .hidden-small { + display: none !important; + } + .action-small { + text-align: center !important; + background-color: white; + } + .visible-small { + display: block; + } + td.visible-small { + display: table-cell; + } } body.install{ padding-top:20px; } -.label-status-on{ +.install_header h2 { + line-height: 100px; +} +.install_header img { + height: 100px; +} + +.label-status-on, .label-status-off, .label-status-warning { + vertical-align: baseline; +} +.label-status-on { background-color: #468847; } -.label-status-off{ +.label-status-off, +.label-error { background-color: #B94A48; } -.label-status-warning{ +.label-status-warning { background-color: #FAA732; } .label a{ @@ -182,6 +422,7 @@ div.tabbable ul.nav-tabs li{ } legend{ border-color: -moz-use-text-color -moz-use-text-color #CCCCCC; + margin-bottom: 10px; } .form-actions { background-color: transparent; @@ -230,4 +471,102 @@ legend{ .oce-first{ background-color:#eee; +} + +/* Bootstrap 2.3.2 added icons */ +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + +/* personal added icons */ +.icon-chart { + background-position: 0px -168px; +} + +.icon-mobile { + background-position: -24px -168px; +} + +.icon-admin { + background-position: -48px -168px; +} + +.icon-pushover { + background-position: -72px -168px; } \ No newline at end of file diff --git a/static/js/history.js b/static/js/history.js index 27584e8a..bc020b7b 100644 --- a/static/js/history.js +++ b/static/js/history.js @@ -132,6 +132,9 @@ function create_plot($this, mode) tickOptions : { formatString: tickFormat }, min: time - timeStamp, max: time + }, + yaxis:{ + min: 0 } }, highlighter: { diff --git a/static/js/scripts.js b/static/js/scripts.js index 18829b16..f77c5e3f 100755 --- a/static/js/scripts.js +++ b/static/js/scripts.js @@ -1,9 +1,79 @@ -function sm_delete(id, mod) { - var del = confirm("Are you sure you want to delete this record?"); - if (del === true) { - var loc = 'index.php?action=delete&id=' + id + '&mod=' + mod; - window.location = loc; - } +$().ready(function() { + $('.show-modal').click(function (e) { + var $this = $(this); + if ($this.is('a')) { + e.preventDefault(); + } + var $modal_id = $this.attr('data-modal-id') || 'main'; + var $modal = $('#' + $modal_id + 'Modal'); + if($modal.length) { + $modal.find('.modalOKButton').data('modal-origin', $this); + + var param = $this.attr('data-modal-param'); + if(param) { + var ary = param.split(','); + for (var index = 0; index < ary.length && index < 9; ++index) { + var value = ary[index]; + $($modal).find('span.modalP' + (index+1)).text(value); + } + } + scroll(0, 0); + $modal.modal('show'); + } else { + // Just in case we forgot the dialog box + var conf = confirm("Are you sure?"); + if (conf === true) { + window.location = href; + } + } + return false; + }); + + $('.modalOKButton').click(function(e) { + var $this = $(this); + var $origin = $this.data('modal-origin'); + if ($origin.is('a')) { + window.location = $origin.attr('href'); + } else { + $origin.next('input[type=hidden]').attr('value', 1); + $origin.closest('form').submit(); + } + return false; + }); + $('select.multiselect').multiselect({ + includeSelectAllOption: true, + maxHeight: 400, + enableCaseInsensitiveFiltering: true + }); + + psm_flash_message(); + psm_tooltips(); +}); + +function psm_xhr(mod, params, method, on_complete, options) { + method = (typeof method == 'undefined') ? 'GET' : method; + + var xhr_options = { + data: params, + type: method, + success: on_complete, + error: function(jqjqXHR, textStatus, errorThrown) { + psm_flash_message(errorThrown); + } + }; + $.extend(xhr_options, options); + + var result = $.ajax('index.php?xhr=1&mod=' + mod, xhr_options); + + return result; +} + +function psm_saveLayout(layout) { + var params = { + action: 'saveLayout', + layout: layout + }; + psm_xhr('server_status', params, 'POST'); } function psm_tooltips() { @@ -12,6 +82,10 @@ function psm_tooltips() { 'placement': 'right', 'container': 'body' }); + $('i[data-toggle="tooltip"]').tooltip({ + 'trigger':'hover', + 'placement': 'bottom' + }); } function psm_goTo(url) { diff --git a/static/opensource.png b/static/opensource.png deleted file mode 100644 index 7dffde8b..00000000 Binary files a/static/opensource.png and /dev/null differ diff --git a/static/phpservermon.png b/static/phpservermon.png new file mode 100644 index 00000000..22cda643 Binary files /dev/null and b/static/phpservermon.png differ diff --git a/static/plugin/bootstrap-multiselect/bootstrap-multiselect.min.css b/static/plugin/bootstrap-multiselect/bootstrap-multiselect.min.css new file mode 100644 index 00000000..d982bd70 --- /dev/null +++ b/static/plugin/bootstrap-multiselect/bootstrap-multiselect.min.css @@ -0,0 +1 @@ +.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li>label.multiselect-group{margin:0;padding:3px 20px;height:100%;font-weight:700}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.radio,.multiselect-container>li>a>label.checkbox{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0} \ No newline at end of file diff --git a/static/plugin/bootstrap-multiselect/bootstrap-multiselect.min.js b/static/plugin/bootstrap-multiselect/bootstrap-multiselect.min.js new file mode 100644 index 00000000..a1108d2c --- /dev/null +++ b/static/plugin/bootstrap-multiselect/bootstrap-multiselect.min.js @@ -0,0 +1 @@ +!function($){if(Array.prototype.forEach===null||Array.prototype.forEach===undefined){Array.prototype.forEach=function(func){var index;for(index=0;index0){$(element).multiselect("select",addedArray)}if(deletedArray.length>0){$(element).multiselect("deselect",deletedArray)}},null,"arrayChange")}},update:function(element,valueAccessor,allBindingsAccessor,viewModel,bindingContext){var listOfItems=allBindingsAccessor().options,ms=$(element).data("multiselect"),config=ko.utils.unwrapObservable(valueAccessor());if(isObservableArray(listOfItems)){listOfItems.subscribe(function(theArray){$(element).multiselect("rebuild")})}if(!ms){$(element).multiselect(config)}else{ms.updateOriginalOptions()}}}}function isObservableArray(obj){return ko.isObservable(obj)&&!(obj.destroyAll===undefined)}function Multiselect(select,options){this.options=this.mergeOptions(options);this.$select=$(select);this.originalOptions=this.$select.clone()[0].options;this.query="";this.searchTimeout=null;this.options.multiple=this.$select.attr("multiple")==="multiple";this.options.onChange=$.proxy(this.options.onChange,this);this.options.onDropdownShow=$.proxy(this.options.onDropdownShow,this);this.options.onDropdownHide=$.proxy(this.options.onDropdownHide,this);this.buildContainer();this.buildButton();this.buildDropdown();this.buildSelectAll();this.buildDropdownOptions();this.buildFilter();this.updateButtonText();this.updateSelectAll();this.$select.hide().after(this.$container)}Multiselect.prototype={defaults:{buttonText:function(options,select){if(options.length===0){return this.nonSelectedText+' '}else{if(options.length>this.numberDisplayed){return options.length+" "+this.nSelectedText+' '}else{var selected="";options.each(function(){var label=($(this).attr("label")!==undefined)?$(this).attr("label"):$(this).html();selected+=label+", "});return selected.substr(0,selected.length-2)+' '}}},buttonTitle:function(options,select){if(options.length===0){return this.nonSelectedText}else{var selected="";options.each(function(){selected+=$(this).text()+", "});return selected.substr(0,selected.length-2)}},label:function(element){return $(element).attr("label")||$(element).html()},onChange:function(option,checked){},onDropdownShow:function(event){},onDropdownHide:function(event){},buttonClass:"btn btn-default",dropRight:false,selectedClass:"active",buttonWidth:"auto",buttonContainer:'
',maxHeight:false,checkboxName:"multiselect",includeSelectAllOption:false,includeSelectAllIfMoreThan:0,selectAllText:" Select all",selectAllValue:"multiselect-all",enableFiltering:false,enableCaseInsensitiveFiltering:false,filterPlaceholder:"Search",filterBehavior:"text",preventInputChangeEvent:false,nonSelectedText:"None selected",nSelectedText:"selected",numberDisplayed:3,templates:{button:'',ul:'',filter:'
  • ',li:'
  • ',divider:'
  • ',liGroup:'
  • '}},constructor:Multiselect,buildContainer:function(){this.$container=$(this.options.buttonContainer);this.$container.on("show.bs.dropdown",this.options.onDropdownShow);this.$container.on("hide.bs.dropdown",this.options.onDropdownHide)},buildButton:function(){this.$button=$(this.options.templates.button).addClass(this.options.buttonClass);if(this.$select.prop("disabled")){this.disable()}else{this.enable()}if(this.options.buttonWidth&&this.options.buttonWidth!=="auto"){this.$button.css({width:this.options.buttonWidth});this.$container.css({width:this.options.buttonWidth})}var tabindex=this.$select.attr("tabindex");if(tabindex){this.$button.attr("tabindex",tabindex)}this.$container.prepend(this.$button)},buildDropdown:function(){this.$ul=$(this.options.templates.ul);if(this.options.dropRight){this.$ul.addClass("pull-right")}if(this.options.maxHeight){this.$ul.css({"max-height":this.options.maxHeight+"px","overflow-y":"auto","overflow-x":"hidden"})}this.$container.append(this.$ul)},buildDropdownOptions:function(){this.$select.children().each($.proxy(function(index,element){var tag=$(element).prop("tagName").toLowerCase();if($(element).prop("value")==this.options.selectAllValue){return}if(tag==="optgroup"){this.createOptgroup(element)}else{if(tag==="option"){if($(element).data("role")==="divider"){this.createDivider()}else{this.createOptionValue(element)}}}},this));$("li input",this.$ul).on("change",$.proxy(function(event){var $target=$(event.target);var checked=$target.prop("checked")||false;var isSelectAllOption=$target.val()===this.options.selectAllValue;if(this.options.selectedClass){if(checked){$target.parents("li").addClass(this.options.selectedClass)}else{$target.parents("li").removeClass(this.options.selectedClass)}}var value=$target.val();var $option=this.getOptionByValue(value);var $optionsNotThis=$("option",this.$select).not($option);var $checkboxesNotThis=$("input",this.$container).not($target);if(isSelectAllOption){if(checked){this.selectall()}else{this.deselectall()}}if(!isSelectAllOption){if(checked){$option.prop("selected",true);if(this.options.multiple){$option.prop("selected",true)}else{if(this.options.selectedClass){$($checkboxesNotThis).parents("li").removeClass(this.options.selectedClass)}$($checkboxesNotThis).prop("checked",false);$optionsNotThis.prop("selected",false);this.$button.click()}if(this.options.selectedClass==="active"){$optionsNotThis.parents("a").css("outline","")}}else{$option.prop("selected",false)}}this.$select.change();this.updateButtonText();this.updateSelectAll();this.options.onChange($option,checked);if(this.options.preventInputChangeEvent){return false}},this));$("li a",this.$ul).on("touchstart click",function(event){event.stopPropagation();var $target=$(event.target);if(event.shiftKey){var checked=$target.prop("checked")||false;if(checked){var prev=$target.parents("li:last").siblings('li[class="active"]:first');var currentIdx=$target.parents("li").index();var prevIdx=prev.index();if(currentIdx>prevIdx){$target.parents("li:last").prevUntil(prev).each(function(){$(this).find("input:first").prop("checked",true).trigger("change")})}else{$target.parents("li:last").nextUntil(prev).each(function(){$(this).find("input:first").prop("checked",true).trigger("change")})}}}$target.blur()});this.$container.off("keydown.multiselect").on("keydown.multiselect",$.proxy(function(event){if($('input[type="text"]',this.$container).is(":focus")){return}if((event.keyCode===9||event.keyCode===27)&&this.$container.hasClass("open")){this.$button.click()}else{var $items=$(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible");if(!$items.length){return}var index=$items.index($items.filter(":focus"));if(event.keyCode===38&&index>0){index--}else{if(event.keyCode===40&&index<$items.length-1){index++}else{if(!~index){index=0}}}var $current=$items.eq(index);$current.focus();if(event.keyCode===32||event.keyCode===13){var $checkbox=$current.find("input");$checkbox.prop("checked",!$checkbox.prop("checked"));$checkbox.change()}event.stopPropagation();event.preventDefault()}},this))},createOptionValue:function(element){if($(element).is(":selected")){$(element).prop("selected",true)}var label=this.options.label(element);var value=$(element).val();var inputType=this.options.multiple?"checkbox":"radio";var $li=$(this.options.templates.li);$("label",$li).addClass(inputType);$("label",$li).append('');var selected=$(element).prop("selected")||false;var $checkbox=$("input",$li);$checkbox.val(value);if(value===this.options.selectAllValue){$li.addClass("multiselect-item multiselect-all");$checkbox.parent().parent().addClass("multiselect-all")}$("label",$li).append(" "+label);this.$ul.append($li);if($(element).is(":disabled")){$checkbox.attr("disabled","disabled").prop("disabled",true).parents("a").attr("tabindex","-1").parents("li").addClass("disabled")}$checkbox.prop("checked",selected);if(selected&&this.options.selectedClass){$checkbox.parents("li").addClass(this.options.selectedClass)}},createDivider:function(element){var $divider=$(this.options.templates.divider);this.$ul.append($divider)},createOptgroup:function(group){var groupName=$(group).prop("label");var $li=$(this.options.templates.liGroup);$("label",$li).text(groupName);this.$ul.append($li);if($(group).is(":disabled")){$li.addClass("disabled")}$("option",group).each($.proxy(function(index,element){this.createOptionValue(element)},this))},buildSelectAll:function(){var alreadyHasSelectAll=this.hasSelectAll();if(!alreadyHasSelectAll&&this.options.includeSelectAllOption&&this.options.multiple&&$("option",this.$select).length>this.options.includeSelectAllIfMoreThan){if(this.options.includeSelectAllDivider){this.$ul.prepend($(this.options.templates.divider))}var $li=$(this.options.templates.li);$("label",$li).addClass("checkbox");$("label",$li).append('');var $checkbox=$("input",$li);$checkbox.val(this.options.selectAllValue);$li.addClass("multiselect-item multiselect-all");$checkbox.parent().parent().addClass("multiselect-all");$("label",$li).append(" "+this.options.selectAllText);this.$ul.prepend($li);$checkbox.prop("checked",false)}},buildFilter:function(){if(this.options.enableFiltering||this.options.enableCaseInsensitiveFiltering){var enableFilterLength=Math.max(this.options.enableFiltering,this.options.enableCaseInsensitiveFiltering);if(this.$select.find("option").length>=enableFilterLength){this.$filter=$(this.options.templates.filter);$("input",this.$filter).attr("placeholder",this.options.filterPlaceholder);this.$ul.prepend(this.$filter);this.$filter.val(this.query).on("click",function(event){event.stopPropagation()}).on("input keydown",$.proxy(function(event){clearTimeout(this.searchTimeout);this.searchTimeout=this.asyncFunction($.proxy(function(){if(this.query!==event.target.value){this.query=event.target.value;$.each($("li",this.$ul),$.proxy(function(index,element){var value=$("input",element).val();var text=$("label",element).text();var filterCandidate="";if((this.options.filterBehavior==="text")){filterCandidate=text}else{if((this.options.filterBehavior==="value")){filterCandidate=value}else{if(this.options.filterBehavior==="both"){filterCandidate=text+"\n"+value}}}if(value!==this.options.selectAllValue&&text){var showElement=false;if(this.options.enableCaseInsensitiveFiltering&&filterCandidate.toLowerCase().indexOf(this.query.toLowerCase())>-1){showElement=true}else{if(filterCandidate.indexOf(this.query)>-1){showElement=true}}if(showElement){$(element).show().removeClass("filter-hidden")}else{$(element).hide().addClass("filter-hidden")}}},this))}this.updateSelectAll()},this),300,this)},this))}}},destroy:function(){this.$container.remove();this.$select.show();this.$select.data("multiselect",null)},refresh:function(){$("option",this.$select).each($.proxy(function(index,element){var $input=$("li input",this.$ul).filter(function(){return $(this).val()===$(element).val()});if($(element).is(":selected")){$input.prop("checked",true);if(this.options.selectedClass){$input.parents("li").addClass(this.options.selectedClass)}}else{$input.prop("checked",false);if(this.options.selectedClass){$input.parents("li").removeClass(this.options.selectedClass)}}if($(element).is(":disabled")){$input.attr("disabled","disabled").prop("disabled",true).parents("li").addClass("disabled")}else{$input.prop("disabled",false).parents("li").removeClass("disabled")}},this));this.updateButtonText();this.updateSelectAll()},select:function(selectValues){if(!$.isArray(selectValues)){selectValues=[selectValues]}for(var i=0;i';option.children.forEach(function(subOption){optionDOM+='"});optionDOM+=""}else{optionDOM+='"}});this.$select.html(optionDOM);this.rebuild()},enable:function(){this.$select.prop("disabled",false);this.$button.prop("disabled",false).removeClass("disabled")},disable:function(){this.$select.prop("disabled",true);this.$button.prop("disabled",true).addClass("disabled")},setOptions:function(options){this.options=this.mergeOptions(options)},mergeOptions:function(options){return $.extend(true,{},this.defaults,options)},hasSelectAll:function(){return $("li."+this.options.selectAllValue,this.$ul).length>0},updateSelectAll:function(){if(this.hasSelectAll()){var allBoxes=$("li:not(.multiselect-item):not(.filter-hidden) input:enabled",this.$ul),allBoxesLength=allBoxes.length,checkedBoxesLength=allBoxes.filter(":checked").length,selectAllLi=$("li."+this.options.selectAllValue,this.$ul),selectAllInput=selectAllLi.find("input");if(checkedBoxesLength>0&&checkedBoxesLength===allBoxesLength){selectAllInput.prop("checked",true);selectAllLi.addClass(this.options.selectedClass)}else{selectAllInput.prop("checked",false);selectAllLi.removeClass(this.options.selectedClass)}}},updateButtonText:function(){var options=this.getSelected();$("button",this.$container).html(this.options.buttonText(options,this.$select));$("button",this.$container).attr("title",this.options.buttonTitle(options,this.$select))},getSelected:function(){return $("option",this.$select).filter(":selected")},getOptionByValue:function(value){var options=$("option",this.$select);var valueToCompare=value.toString();for(var i=0;iUseSendmailOptions) ) { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header); - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header, $params); - } - return $rt; - } - - /** - * Outputs debugging info via user-defined method - * @param string $str - */ - protected function edebug($str) { - switch ($this->Debugoutput) { - case 'error_log': - error_log($str); - break; - case 'html': - //Cleans up output a bit for a better looking display that's HTML-safe - echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet)."
    \n"; - break; - case 'echo': - default: - //Just echoes exactly what was received - echo $str; - } - } - - /** - * Constructor - * @param boolean $exceptions Should we throw external exceptions? - */ - public function __construct($exceptions = false) { - $this->exceptions = ($exceptions == true); - } - - /** - * Destructor - */ - public function __destruct() { - if ($this->Mailer == 'smtp') { //Close any open SMTP connection nicely - $this->SmtpClose(); - } - } - - /** - * Sets message type to HTML. - * @param bool $ishtml - * @return void - */ - public function IsHTML($ishtml = true) { - if ($ishtml) { - $this->ContentType = 'text/html'; - } else { - $this->ContentType = 'text/plain'; - } - } - - /** - * Sets Mailer to send message using SMTP. - * @return void - */ - public function IsSMTP() { - $this->Mailer = 'smtp'; - } - - /** - * Sets Mailer to send message using PHP mail() function. - * @return void - */ - public function IsMail() { - $this->Mailer = 'mail'; - } - - /** - * Sets Mailer to send message using the $Sendmail program. - * @return void - */ - public function IsSendmail() { - if (!stristr(ini_get('sendmail_path'), 'sendmail')) { - $this->Sendmail = '/var/qmail/bin/sendmail'; - } - $this->Mailer = 'sendmail'; - } - - /** - * Sets Mailer to send message using the qmail MTA. - * @return void - */ - public function IsQmail() { - if (stristr(ini_get('sendmail_path'), 'qmail')) { - $this->Sendmail = '/var/qmail/bin/sendmail'; - } - $this->Mailer = 'sendmail'; - } - - ///////////////////////////////////////////////// - // METHODS, RECIPIENTS - ///////////////////////////////////////////////// - - /** - * Adds a "To" address. - * @param string $address - * @param string $name - * @return boolean true on success, false if address already used - */ - public function AddAddress($address, $name = '') { - return $this->AddAnAddress('to', $address, $name); - } - - /** - * Adds a "Cc" address. - * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address - * @param string $name - * @return boolean true on success, false if address already used - */ - public function AddCC($address, $name = '') { - return $this->AddAnAddress('cc', $address, $name); - } - - /** - * Adds a "Bcc" address. - * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address - * @param string $name - * @return boolean true on success, false if address already used - */ - public function AddBCC($address, $name = '') { - return $this->AddAnAddress('bcc', $address, $name); - } - - /** - * Adds a "Reply-to" address. - * @param string $address - * @param string $name - * @return boolean - */ - public function AddReplyTo($address, $name = '') { - return $this->AddAnAddress('Reply-To', $address, $name); - } - - /** - * Adds an address to one of the recipient arrays - * Addresses that have been added already return false, but do not throw exceptions - * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' - * @param string $address The email address to send to - * @param string $name - * @throws phpmailerException - * @return boolean true on success, false if address already used or invalid in some way - * @access protected - */ - protected function AddAnAddress($kind, $address, $name = '') { - if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { - $this->SetError($this->Lang('Invalid recipient array').': '.$kind); - if ($this->exceptions) { - throw new phpmailerException('Invalid recipient array: ' . $kind); - } - if ($this->SMTPDebug) { - $this->edebug($this->Lang('Invalid recipient array').': '.$kind); - } - return false; - } - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!$this->ValidateAddress($address)) { - $this->SetError($this->Lang('invalid_address').': '. $address); - if ($this->exceptions) { - throw new phpmailerException($this->Lang('invalid_address').': '.$address); - } - if ($this->SMTPDebug) { - $this->edebug($this->Lang('invalid_address').': '.$address); - } - return false; - } - if ($kind != 'Reply-To') { - if (!isset($this->all_recipients[strtolower($address)])) { - array_push($this->$kind, array($address, $name)); - $this->all_recipients[strtolower($address)] = true; - return true; - } - } else { - if (!array_key_exists(strtolower($address), $this->ReplyTo)) { - $this->ReplyTo[strtolower($address)] = array($address, $name); - return true; - } - } - return false; -} - - /** - * Set the From and FromName properties - * @param string $address - * @param string $name - * @param boolean $auto Whether to also set the Sender address, defaults to true - * @throws phpmailerException - * @return boolean - */ - public function SetFrom($address, $name = '', $auto = true) { - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!$this->ValidateAddress($address)) { - $this->SetError($this->Lang('invalid_address').': '. $address); - if ($this->exceptions) { - throw new phpmailerException($this->Lang('invalid_address').': '.$address); - } - if ($this->SMTPDebug) { - $this->edebug($this->Lang('invalid_address').': '.$address); - } - return false; - } - $this->From = $address; - $this->FromName = $name; - if ($auto) { - if (empty($this->Sender)) { - $this->Sender = $address; - } - } - return true; - } - - /** - * Check that a string looks roughly like an email address should - * Static so it can be used without instantiation, public so people can overload - * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is - * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to - * not allow a@b type valid addresses :( - * @link http://squiloople.com/2009/12/20/email-address-validation/ - * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice. - * @param string $address The email address to check - * @return boolean - * @static - * @access public - */ - public static function ValidateAddress($address) { - if (defined('PCRE_VERSION')) { //Check this instead of extension_loaded so it works when that function is disabled - if (version_compare(PCRE_VERSION, '8.0') >= 0) { - return (boolean)preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); - } else { - //Fall back to an older regex that doesn't need a recent PCRE - return (boolean)preg_match('/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address); - } - } else { - //No PCRE! Do something _very_ approximate! - //Check the address is 3 chars or longer and contains an @ that's not the first or last char - return (strlen($address) >= 3 and strpos($address, '@') >= 1 and strpos($address, '@') != strlen($address) - 1); - } - } - - ///////////////////////////////////////////////// - // METHODS, MAIL SENDING - ///////////////////////////////////////////////// - - /** - * Creates message and assigns Mailer. If the message is - * not sent successfully then it returns false. Use the ErrorInfo - * variable to view description of the error. - * @throws phpmailerException - * @return bool - */ - public function Send() { - try { - if(!$this->PreSend()) return false; - return $this->PostSend(); - } catch (phpmailerException $e) { - $this->mailHeader = ''; - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - return false; - } - } - - /** - * Prep mail by constructing all message entities - * @throws phpmailerException - * @return bool - */ - public function PreSend() { - try { - $this->mailHeader = ""; - if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { - throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); - } - - // Set whether the message is multipart/alternative - if(!empty($this->AltBody)) { - $this->ContentType = 'multipart/alternative'; - } - - $this->error_count = 0; // reset errors - $this->SetMessageType(); - //Refuse to send an empty message unless we are specifically allowing it - if (!$this->AllowEmpty and empty($this->Body)) { - throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); - } - - $this->MIMEHeader = $this->CreateHeader(); - $this->MIMEBody = $this->CreateBody(); - - // To capture the complete message when using mail(), create - // an extra header list which CreateHeader() doesn't fold in - if ($this->Mailer == 'mail') { - if (count($this->to) > 0) { - $this->mailHeader .= $this->AddrAppend("To", $this->to); - } else { - $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;"); - } - $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject)))); - } - - // digitally sign with DKIM if enabled - if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) { - $header_dkim = $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); - $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; - } - - return true; - - } catch (phpmailerException $e) { - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - return false; - } - } - - /** - * Actual Email transport function - * Send the email via the selected mechanism - * @throws phpmailerException - * @return bool - */ - public function PostSend() { - try { - // Choose the mailer and send through it - switch($this->Mailer) { - case 'sendmail': - return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); - case 'smtp': - return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); - case 'mail': - return $this->MailSend($this->MIMEHeader, $this->MIMEBody); - default: - return $this->MailSend($this->MIMEHeader, $this->MIMEBody); - } - } catch (phpmailerException $e) { - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - if ($this->SMTPDebug) { - $this->edebug($e->getMessage()."\n"); - } - } - return false; - } - - /** - * Sends mail using the $Sendmail program. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @access protected - * @return bool - */ - protected function SendmailSend($header, $body) { - if ($this->Sender != '') { - $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); - } else { - $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); - } - if ($this->SingleTo === true) { - foreach ($this->SingleToArray as $val) { - if(!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, "To: " . $val . "\n"); - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - // implement call back function if it exists - $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); - if($result != 0) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - } - } else { - if(!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - // implement call back function if it exists - $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body); - if($result != 0) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - } - return true; - } - - /** - * Sends mail using the PHP mail() function. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @access protected - * @return bool - */ - protected function MailSend($header, $body) { - $toArr = array(); - foreach($this->to as $t) { - $toArr[] = $this->AddrFormat($t); - } - $to = implode(', ', $toArr); - - if (empty($this->Sender)) { - $params = " "; - } else { - $params = sprintf("-f%s", $this->Sender); - } - if ($this->Sender != '' and !ini_get('safe_mode')) { - $old_from = ini_get('sendmail_from'); - ini_set('sendmail_from', $this->Sender); - } - $rt = false; - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $val) { - $rt = $this->mail_passthru($val, $this->Subject, $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); - } - } else { - $rt = $this->mail_passthru($to, $this->Subject, $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); - } - if (isset($old_from)) { - ini_set('sendmail_from', $old_from); - } - if(!$rt) { - throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); - } - return true; - } - - /** - * Sends mail via SMTP using PhpSMTP - * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @uses SMTP - * @access protected - * @return bool - */ - protected function SmtpSend($header, $body) { - require_once $this->PluginDir . 'class.smtp.php'; - $bad_rcpt = array(); - - if(!$this->SmtpConnect()) { - throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL); - } - $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; - if(!$this->smtp->Mail($smtp_from)) { - $this->SetError($this->Lang('from_failed') . $smtp_from . ' : ' .implode(',', $this->smtp->getError())); - throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); - } - - // Attempt to send attach all recipients - foreach($this->to as $to) { - if (!$this->smtp->Recipient($to[0])) { - $bad_rcpt[] = $to[0]; - // implement call back function if it exists - $isSent = 0; - $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); - } else { - // implement call back function if it exists - $isSent = 1; - $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); - } - } - foreach($this->cc as $cc) { - if (!$this->smtp->Recipient($cc[0])) { - $bad_rcpt[] = $cc[0]; - // implement call back function if it exists - $isSent = 0; - $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); - } else { - // implement call back function if it exists - $isSent = 1; - $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); - } - } - foreach($this->bcc as $bcc) { - if (!$this->smtp->Recipient($bcc[0])) { - $bad_rcpt[] = $bcc[0]; - // implement call back function if it exists - $isSent = 0; - $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); - } else { - // implement call back function if it exists - $isSent = 1; - $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); - } - } - - - if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses - $badaddresses = implode(', ', $bad_rcpt); - throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); - } - if(!$this->smtp->Data($header . $body)) { - throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); - } - if($this->SMTPKeepAlive == true) { - $this->smtp->Reset(); - } else { - $this->smtp->Quit(); - $this->smtp->Close(); - } - return true; - } - - /** - * Initiates a connection to an SMTP server. - * Returns false if the operation failed. - * @param array $options An array of options compatible with stream_context_create() - * @uses SMTP - * @access public - * @throws phpmailerException - * @return bool - */ - public function SmtpConnect($options = array()) { - if(is_null($this->smtp)) { - $this->smtp = new SMTP; - } - - //Already connected? - if ($this->smtp->Connected()) { - return true; - } - - $this->smtp->Timeout = $this->Timeout; - $this->smtp->do_debug = $this->SMTPDebug; - $this->smtp->Debugoutput = $this->Debugoutput; - $this->smtp->do_verp = $this->do_verp; - $index = 0; - $tls = ($this->SMTPSecure == 'tls'); - $ssl = ($this->SMTPSecure == 'ssl'); - $hosts = explode(';', $this->Host); - $lastexception = null; - - foreach ($hosts as $hostentry) { - $hostinfo = array(); - $host = $hostentry; - $port = $this->Port; - if (preg_match('/^(.+):([0-9]+)$/', $hostentry, $hostinfo)) { //If $hostentry contains 'address:port', override default - $host = $hostinfo[1]; - $port = $hostinfo[2]; - } - if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout, $options)) { - try { - if ($this->Helo) { - $hello = $this->Helo; - } else { - $hello = $this->ServerHostname(); - } - $this->smtp->Hello($hello); - - if ($tls) { - if (!$this->smtp->StartTLS()) { - throw new phpmailerException($this->Lang('connect_host')); - } - //We must resend HELO after tls negotiation - $this->smtp->Hello($hello); - } - if ($this->SMTPAuth) { - if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation)) { - throw new phpmailerException($this->Lang('authenticate')); - } - } - return true; - } catch (phpmailerException $e) { - $lastexception = $e; - //We must have connected, but then failed TLS or Auth, so close connection nicely - $this->smtp->Quit(); - } - } - } - //If we get here, all connection attempts have failed, so close connection hard - $this->smtp->Close(); - //As we've caught all exceptions, just report whatever the last one was - if ($this->exceptions and !is_null($lastexception)) { - throw $lastexception; - } - return false; - } - - /** - * Closes the active SMTP session if one exists. - * @return void - */ - public function SmtpClose() { - if ($this->smtp !== null) { - if($this->smtp->Connected()) { - $this->smtp->Quit(); - $this->smtp->Close(); - } - } - } - - /** - * Sets the language for all class error messages. - * Returns false if it cannot load the language file. The default language is English. - * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") - * @param string $lang_path Path to the language file directory - * @return bool - * @access public - */ - function SetLanguage($langcode = 'en', $lang_path = 'language/') { - //Define full set of translatable strings - $PHPMAILER_LANG = array( - 'authenticate' => 'SMTP Error: Could not authenticate.', - 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', - 'data_not_accepted' => 'SMTP Error: Data not accepted.', - 'empty_message' => 'Message body empty', - 'encoding' => 'Unknown encoding: ', - 'execute' => 'Could not execute: ', - 'file_access' => 'Could not access file: ', - 'file_open' => 'File Error: Could not open file: ', - 'from_failed' => 'The following From address failed: ', - 'instantiate' => 'Could not instantiate mail function.', - 'invalid_address' => 'Invalid address', - 'mailer_not_supported' => ' mailer is not supported.', - 'provide_address' => 'You must provide at least one recipient email address.', - 'recipients_failed' => 'SMTP Error: The following recipients failed: ', - 'signing' => 'Signing Error: ', - 'smtp_connect_failed' => 'SMTP Connect() failed.', - 'smtp_error' => 'SMTP server error: ', - 'variable_set' => 'Cannot set or reset variable: ' - ); - //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! - $l = true; - if ($langcode != 'en') { //There is no English translation file - $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; - } - $this->language = $PHPMAILER_LANG; - return ($l == true); //Returns false if language not found - } - - /** - * Return the current array of language strings - * @return array - */ - public function GetTranslations() { - return $this->language; - } - - ///////////////////////////////////////////////// - // METHODS, MESSAGE CREATION - ///////////////////////////////////////////////// - - /** - * Creates recipient headers. - * @access public - * @param string $type - * @param array $addr - * @return string - */ - public function AddrAppend($type, $addr) { - $addr_str = $type . ': '; - $addresses = array(); - foreach ($addr as $a) { - $addresses[] = $this->AddrFormat($a); - } - $addr_str .= implode(', ', $addresses); - $addr_str .= $this->LE; - - return $addr_str; - } - - /** - * Formats an address correctly. - * @access public - * @param string $addr - * @return string - */ - public function AddrFormat($addr) { - if (empty($addr[1])) { - return $this->SecureHeader($addr[0]); - } else { - return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; - } - } - - /** - * Wraps message for use with mailers that do not - * automatically perform wrapping and for quoted-printable. - * Original written by philippe. - * @param string $message The message to wrap - * @param integer $length The line length to wrap to - * @param boolean $qp_mode Whether to run in Quoted-Printable mode - * @access public - * @return string - */ - public function WrapText($message, $length, $qp_mode = false) { - $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; - // If utf-8 encoding is used, we will need to make sure we don't - // split multibyte characters when we wrap - $is_utf8 = (strtolower($this->CharSet) == "utf-8"); - $lelen = strlen($this->LE); - $crlflen = strlen(self::CRLF); - - $message = $this->FixEOL($message); - if (substr($message, -$lelen) == $this->LE) { - $message = substr($message, 0, -$lelen); - } - - $line = explode($this->LE, $message); // Magic. We know FixEOL uses $LE - $message = ''; - for ($i = 0 ;$i < count($line); $i++) { - $line_part = explode(' ', $line[$i]); - $buf = ''; - for ($e = 0; $e $length)) { - $space_left = $length - strlen($buf) - $crlflen; - if ($e != 0) { - if ($space_left > 20) { - $len = $space_left; - if ($is_utf8) { - $len = $this->UTF8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == "=") { - $len--; - } elseif (substr($word, $len - 2, 1) == "=") { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - $buf .= ' ' . $part; - $message .= $buf . sprintf("=%s", self::CRLF); - } else { - $message .= $buf . $soft_break; - } - $buf = ''; - } - while (strlen($word) > 0) { - if ($length <= 0) { - break; - } - $len = $length; - if ($is_utf8) { - $len = $this->UTF8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == "=") { - $len--; - } elseif (substr($word, $len - 2, 1) == "=") { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - - if (strlen($word) > 0) { - $message .= $part . sprintf("=%s", self::CRLF); - } else { - $buf = $part; - } - } - } else { - $buf_o = $buf; - $buf .= ($e == 0) ? $word : (' ' . $word); - - if (strlen($buf) > $length and $buf_o != '') { - $message .= $buf_o . $soft_break; - $buf = $word; - } - } - } - $message .= $buf . self::CRLF; - } - - return $message; - } - - /** - * Finds last character boundary prior to maxLength in a utf-8 - * quoted (printable) encoded string. - * Original written by Colin Brown. - * @access public - * @param string $encodedText utf-8 QP text - * @param int $maxLength find last character boundary prior to this length - * @return int - */ - public function UTF8CharBoundary($encodedText, $maxLength) { - $foundSplitPos = false; - $lookBack = 3; - while (!$foundSplitPos) { - $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); - $encodedCharPos = strpos($lastChunk, "="); - if ($encodedCharPos !== false) { - // Found start of encoded character byte within $lookBack block. - // Check the encoded byte value (the 2 chars after the '=') - $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); - $dec = hexdec($hex); - if ($dec < 128) { // Single byte character. - // If the encoded char was found at pos 0, it will fit - // otherwise reduce maxLength to start of the encoded char - $maxLength = ($encodedCharPos == 0) ? $maxLength : - $maxLength - ($lookBack - $encodedCharPos); - $foundSplitPos = true; - } elseif ($dec >= 192) { // First byte of a multi byte character - // Reduce maxLength to split at start of character - $maxLength = $maxLength - ($lookBack - $encodedCharPos); - $foundSplitPos = true; - } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back - $lookBack += 3; - } - } else { - // No encoded character found - $foundSplitPos = true; - } - } - return $maxLength; - } - - - /** - * Set the body wrapping. - * @access public - * @return void - */ - public function SetWordWrap() { - if($this->WordWrap < 1) { - return; - } - - switch($this->message_type) { - case 'alt': - case 'alt_inline': - case 'alt_attach': - case 'alt_inline_attach': - $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); - break; - default: - $this->Body = $this->WrapText($this->Body, $this->WordWrap); - break; - } - } - - /** - * Assembles message header. - * @access public - * @return string The assembled header - */ - public function CreateHeader() { - $result = ''; - - // Set the boundaries - $uniq_id = md5(uniqid(time())); - $this->boundary[1] = 'b1_' . $uniq_id; - $this->boundary[2] = 'b2_' . $uniq_id; - $this->boundary[3] = 'b3_' . $uniq_id; - - if ($this->MessageDate == '') { - $result .= $this->HeaderLine('Date', self::RFCDate()); - } else { - $result .= $this->HeaderLine('Date', $this->MessageDate); - } - - if ($this->ReturnPath) { - $result .= $this->HeaderLine('Return-Path', '<'.trim($this->ReturnPath).'>'); - } elseif ($this->Sender == '') { - $result .= $this->HeaderLine('Return-Path', '<'.trim($this->From).'>'); - } else { - $result .= $this->HeaderLine('Return-Path', '<'.trim($this->Sender).'>'); - } - - // To be created automatically by mail() - if($this->Mailer != 'mail') { - if ($this->SingleTo === true) { - foreach($this->to as $t) { - $this->SingleToArray[] = $this->AddrFormat($t); - } - } else { - if(count($this->to) > 0) { - $result .= $this->AddrAppend('To', $this->to); - } elseif (count($this->cc) == 0) { - $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); - } - } - } - - $from = array(); - $from[0][0] = trim($this->From); - $from[0][1] = $this->FromName; - $result .= $this->AddrAppend('From', $from); - - // sendmail and mail() extract Cc from the header before sending - if(count($this->cc) > 0) { - $result .= $this->AddrAppend('Cc', $this->cc); - } - - // sendmail and mail() extract Bcc from the header before sending - if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { - $result .= $this->AddrAppend('Bcc', $this->bcc); - } - - if(count($this->ReplyTo) > 0) { - $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); - } - - // mail() sets the subject itself - if($this->Mailer != 'mail') { - $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); - } - - if($this->MessageID != '') { - $result .= $this->HeaderLine('Message-ID', $this->MessageID); - } else { - $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); - } - $result .= $this->HeaderLine('X-Priority', $this->Priority); - if ($this->XMailer == '') { - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (https://github.com/PHPMailer/PHPMailer/)'); - } else { - $myXmailer = trim($this->XMailer); - if ($myXmailer) { - $result .= $this->HeaderLine('X-Mailer', $myXmailer); - } - } - - if($this->ConfirmReadingTo != '') { - $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); - } - - // Add custom headers - for($index = 0; $index < count($this->CustomHeader); $index++) { - $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); - } - if (!$this->sign_key_file) { - $result .= $this->HeaderLine('MIME-Version', '1.0'); - $result .= $this->GetMailMIME(); - } - - return $result; - } - - /** - * Returns the message MIME. - * @access public - * @return string - */ - public function GetMailMIME() { - $result = ''; - switch($this->message_type) { - case 'inline': - $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); - break; - case 'attach': - case 'inline_attach': - case 'alt_attach': - case 'alt_inline_attach': - $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); - break; - case 'alt': - case 'alt_inline': - $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); - break; - default: - // Catches case 'plain': and case '': - $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet); - break; - } - //RFC1341 part 5 says 7bit is assumed if not specified - if ($this->Encoding != '7bit') { - $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); - } - - if($this->Mailer != 'mail') { - $result .= $this->LE; - } - - return $result; - } - - /** - * Returns the MIME message (headers and body). Only really valid post PreSend(). - * @access public - * @return string - */ - public function GetSentMIMEMessage() { - return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; - } - - - /** - * Assembles the message body. Returns an empty string on failure. - * @access public - * @throws phpmailerException - * @return string The assembled message body - */ - public function CreateBody() { - $body = ''; - - if ($this->sign_key_file) { - $body .= $this->GetMailMIME().$this->LE; - } - - $this->SetWordWrap(); - - switch($this->message_type) { - case 'inline': - $body .= $this->GetBoundary($this->boundary[1], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll('inline', $this->boundary[1]); - break; - case 'attach': - $body .= $this->GetBoundary($this->boundary[1], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll('attachment', $this->boundary[1]); - break; - case 'inline_attach': - $body .= $this->TextLine('--' . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll('inline', $this->boundary[2]); - $body .= $this->LE; - $body .= $this->AttachAll('attachment', $this->boundary[1]); - break; - case 'alt': - $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - if(!empty($this->Ical)) { - $body .= $this->GetBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); - $body .= $this->EncodeString($this->Ical, $this->Encoding); - $body .= $this->LE.$this->LE; - } - $body .= $this->EndBoundary($this->boundary[1]); - break; - case 'alt_inline': - $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->TextLine('--' . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll('inline', $this->boundary[2]); - $body .= $this->LE; - $body .= $this->EndBoundary($this->boundary[1]); - break; - case 'alt_attach': - $body .= $this->TextLine('--' . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->EndBoundary($this->boundary[2]); - $body .= $this->LE; - $body .= $this->AttachAll('attachment', $this->boundary[1]); - break; - case 'alt_inline_attach': - $body .= $this->TextLine('--' . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->TextLine('--' . $this->boundary[2]); - $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3].'"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll('inline', $this->boundary[3]); - $body .= $this->LE; - $body .= $this->EndBoundary($this->boundary[2]); - $body .= $this->LE; - $body .= $this->AttachAll('attachment', $this->boundary[1]); - break; - default: - // catch case 'plain' and case '' - $body .= $this->EncodeString($this->Body, $this->Encoding); - break; - } - - if ($this->IsError()) { - $body = ''; - } elseif ($this->sign_key_file) { - try { - if (!defined('PKCS7_TEXT')) { - throw new phpmailerException($this->Lang('signing').' OpenSSL extension missing.'); - } - $file = tempnam(sys_get_temp_dir(), 'mail'); - file_put_contents($file, $body); //TODO check this worked - $signed = tempnam(sys_get_temp_dir(), 'signed'); - if (@openssl_pkcs7_sign($file, $signed, 'file://'.realpath($this->sign_cert_file), array('file://'.realpath($this->sign_key_file), $this->sign_key_pass), null)) { - @unlink($file); - $body = file_get_contents($signed); - @unlink($signed); - } else { - @unlink($file); - @unlink($signed); - throw new phpmailerException($this->Lang('signing').openssl_error_string()); - } - } catch (phpmailerException $e) { - $body = ''; - if ($this->exceptions) { - throw $e; - } - } - } - return $body; - } - - /** - * Returns the start of a message boundary. - * @access protected - * @param string $boundary - * @param string $charSet - * @param string $contentType - * @param string $encoding - * @return string - */ - protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { - $result = ''; - if($charSet == '') { - $charSet = $this->CharSet; - } - if($contentType == '') { - $contentType = $this->ContentType; - } - if($encoding == '') { - $encoding = $this->Encoding; - } - $result .= $this->TextLine('--' . $boundary); - $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet); - $result .= $this->LE; - $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); - $result .= $this->LE; - - return $result; - } - - /** - * Returns the end of a message boundary. - * @access protected - * @param string $boundary - * @return string - */ - protected function EndBoundary($boundary) { - return $this->LE . '--' . $boundary . '--' . $this->LE; - } - - /** - * Sets the message type. - * @access protected - * @return void - */ - protected function SetMessageType() { - $this->message_type = array(); - if($this->AlternativeExists()) $this->message_type[] = "alt"; - if($this->InlineImageExists()) $this->message_type[] = "inline"; - if($this->AttachmentExists()) $this->message_type[] = "attach"; - $this->message_type = implode("_", $this->message_type); - if($this->message_type == "") $this->message_type = "plain"; - } - - /** - * Returns a formatted header line. - * @access public - * @param string $name - * @param string $value - * @return string - */ - public function HeaderLine($name, $value) { - return $name . ': ' . $value . $this->LE; - } - - /** - * Returns a formatted mail line. - * @access public - * @param string $value - * @return string - */ - public function TextLine($value) { - return $value . $this->LE; - } - - ///////////////////////////////////////////////// - // CLASS METHODS, ATTACHMENTS - ///////////////////////////////////////////////// - - /** - * Adds an attachment from a path on the filesystem. - * Returns false if the file could not be found - * or accessed. - * @param string $path Path to the attachment. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @throws phpmailerException - * @return bool - */ - public function AddAttachment($path, $name = '', $encoding = 'base64', $type = '') { - try { - if ( !@is_file($path) ) { - throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); - } - - //If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($path); - } - - $filename = basename($path); - if ( $name == '' ) { - $name = $filename; - } - - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => 'attachment', - 7 => 0 - ); - - } catch (phpmailerException $e) { - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - if ($this->SMTPDebug) { - $this->edebug($e->getMessage()."\n"); - } - return false; - } - return true; - } - - /** - * Return the current array of attachments - * @return array - */ - public function GetAttachments() { - return $this->attachment; - } - - /** - * Attaches all fs, string, and binary attachments to the message. - * Returns an empty string on failure. - * @access protected - * @param string $disposition_type - * @param string $boundary - * @return string - */ - protected function AttachAll($disposition_type, $boundary) { - // Return text of body - $mime = array(); - $cidUniq = array(); - $incl = array(); - - // Add all attachments - foreach ($this->attachment as $attachment) { - // CHECK IF IT IS A VALID DISPOSITION_FILTER - if($attachment[6] == $disposition_type) { - // Check for string attachment - $string = ''; - $path = ''; - $bString = $attachment[5]; - if ($bString) { - $string = $attachment[0]; - } else { - $path = $attachment[0]; - } - - $inclhash = md5(serialize($attachment)); - if (in_array($inclhash, $incl)) { continue; } - $incl[] = $inclhash; - $filename = $attachment[1]; - $name = $attachment[2]; - $encoding = $attachment[3]; - $type = $attachment[4]; - $disposition = $attachment[6]; - $cid = $attachment[7]; - if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } - $cidUniq[$cid] = true; - - $mime[] = sprintf("--%s%s", $boundary, $this->LE); - $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); - $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); - - if($disposition == 'inline') { - $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); - } - - //If a filename contains any of these chars, it should be quoted, but not otherwise: RFC2183 & RFC2045 5.1 - //Fixes a warning in IETF's msglint MIME checker - if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); - } else { - $mime[] = sprintf("Content-Disposition: %s; filename=%s%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); - } - - // Encode as string attachment - if($bString) { - $mime[] = $this->EncodeString($string, $encoding); - if($this->IsError()) { - return ''; - } - $mime[] = $this->LE.$this->LE; - } else { - $mime[] = $this->EncodeFile($path, $encoding); - if($this->IsError()) { - return ''; - } - $mime[] = $this->LE.$this->LE; - } - } - } - - $mime[] = sprintf("--%s--%s", $boundary, $this->LE); - - return implode("", $mime); - } - - /** - * Encodes attachment in requested format. - * Returns an empty string on failure. - * @param string $path The full path to the file - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' - * @throws phpmailerException - * @see EncodeFile() - * @access protected - * @return string - */ - protected function EncodeFile($path, $encoding = 'base64') { - try { - if (!is_readable($path)) { - throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); - } - $magic_quotes = get_magic_quotes_runtime(); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime(0); - } else { - ini_set('magic_quotes_runtime', 0); - } - } - $file_buffer = file_get_contents($path); - $file_buffer = $this->EncodeString($file_buffer, $encoding); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime($magic_quotes); - } else { - ini_set('magic_quotes_runtime', $magic_quotes); - } - } - return $file_buffer; - } catch (Exception $e) { - $this->SetError($e->getMessage()); - return ''; - } - } - - /** - * Encodes string to requested format. - * Returns an empty string on failure. - * @param string $str The text to encode - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' - * @access public - * @return string - */ - public function EncodeString($str, $encoding = 'base64') { - $encoded = ''; - switch(strtolower($encoding)) { - case 'base64': - $encoded = chunk_split(base64_encode($str), 76, $this->LE); - break; - case '7bit': - case '8bit': - $encoded = $this->FixEOL($str); - //Make sure it ends with a line break - if (substr($encoded, -(strlen($this->LE))) != $this->LE) - $encoded .= $this->LE; - break; - case 'binary': - $encoded = $str; - break; - case 'quoted-printable': - $encoded = $this->EncodeQP($str); - break; - default: - $this->SetError($this->Lang('encoding') . $encoding); - break; - } - return $encoded; - } - - /** - * Encode a header string to best (shortest) of Q, B, quoted or none. - * @access public - * @param string $str - * @param string $position - * @return string - */ - public function EncodeHeader($str, $position = 'text') { - $x = 0; - - switch (strtolower($position)) { - case 'phrase': - if (!preg_match('/[\200-\377]/', $str)) { - // Can't use addslashes as we don't know what value has magic_quotes_sybase - $encoded = addcslashes($str, "\0..\37\177\\\""); - if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { - return ($encoded); - } else { - return ("\"$encoded\""); - } - } - $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); - break; - case 'comment': - $x = preg_match_all('/[()"]/', $str, $matches); - // Fall-through - case 'text': - default: - $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); - break; - } - - if ($x == 0) { //There are no chars that need encoding - return ($str); - } - - $maxlen = 75 - 7 - strlen($this->CharSet); - // Try to select the encoding which should produce the shortest output - if ($x > strlen($str)/3) { //More than a third of the content will need encoding, so B encoding will be most efficient - $encoding = 'B'; - if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { - // Use a custom function which correctly encodes and wraps long - // multibyte strings without breaking lines within a character - $encoded = $this->Base64EncodeWrapMB($str, "\n"); - } else { - $encoded = base64_encode($str); - $maxlen -= $maxlen % 4; - $encoded = trim(chunk_split($encoded, $maxlen, "\n")); - } - } else { - $encoding = 'Q'; - $encoded = $this->EncodeQ($str, $position); - $encoded = $this->WrapText($encoded, $maxlen, true); - $encoded = str_replace('='.self::CRLF, "\n", trim($encoded)); - } - - $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); - $encoded = trim(str_replace("\n", $this->LE, $encoded)); - - return $encoded; - } - - /** - * Checks if a string contains multibyte characters. - * @access public - * @param string $str multi-byte text to wrap encode - * @return bool - */ - public function HasMultiBytes($str) { - if (function_exists('mb_strlen')) { - return (strlen($str) > mb_strlen($str, $this->CharSet)); - } else { // Assume no multibytes (we can't handle without mbstring functions anyway) - return false; - } - } - - /** - * Correctly encodes and wraps long multibyte strings for mail headers - * without breaking lines within a character. - * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php - * @access public - * @param string $str multi-byte text to wrap encode - * @param string $lf string to use as linefeed/end-of-line - * @return string - */ - public function Base64EncodeWrapMB($str, $lf=null) { - $start = "=?".$this->CharSet."?B?"; - $end = "?="; - $encoded = ""; - if ($lf === null) { - $lf = $this->LE; - } - - $mb_length = mb_strlen($str, $this->CharSet); - // Each line must have length <= 75, including $start and $end - $length = 75 - strlen($start) - strlen($end); - // Average multi-byte ratio - $ratio = $mb_length / strlen($str); - // Base64 has a 4:3 ratio - $offset = $avgLength = floor($length * $ratio * .75); - - for ($i = 0; $i < $mb_length; $i += $offset) { - $lookBack = 0; - - do { - $offset = $avgLength - $lookBack; - $chunk = mb_substr($str, $i, $offset, $this->CharSet); - $chunk = base64_encode($chunk); - $lookBack++; - } - while (strlen($chunk) > $length); - - $encoded .= $chunk . $lf; - } - - // Chomp the last linefeed - $encoded = substr($encoded, 0, -strlen($lf)); - return $encoded; - } - - /** - * Encode string to RFC2045 (6.7) quoted-printable format - * @access public - * @param string $string The text to encode - * @param integer $line_max Number of chars allowed on a line before wrapping - * @return string - * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 - */ - public function EncodeQP($string, $line_max = 76) { - if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) - return quoted_printable_encode($string); - } - //Fall back to a pure PHP implementation - $string = str_replace(array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string)); - $string = preg_replace('/[^\r\n]{'.($line_max - 3).'}[^=\r\n]{2}/', "$0=\r\n", $string); - return $string; - } - - /** - * Wrapper to preserve BC for old QP encoding function that was removed - * @see EncodeQP() - * @access public - * @param string $string - * @param integer $line_max - * @param bool $space_conv - * @return string - */ - public function EncodeQPphp($string, $line_max = 76, $space_conv = false) { - return $this->EncodeQP($string, $line_max); - } - - /** - * Encode string to q encoding. - * @link http://tools.ietf.org/html/rfc2047 - * @param string $str the text to encode - * @param string $position Where the text is going to be used, see the RFC for what that means - * @access public - * @return string - */ - public function EncodeQ($str, $position = 'text') { - //There should not be any EOL in the string - $pattern = ''; - $encoded = str_replace(array("\r", "\n"), '', $str); - switch (strtolower($position)) { - case 'phrase': - $pattern = '^A-Za-z0-9!*+\/ -'; - break; - - case 'comment': - $pattern = '\(\)"'; - //note that we don't break here! - //for this reason we build the $pattern without including delimiters and [] - - case 'text': - default: - //Replace every high ascii, control =, ? and _ characters - //We put \075 (=) as first value to make sure it's the first one in being converted, preventing double encode - $pattern = '\075\000-\011\013\014\016-\037\077\137\177-\377' . $pattern; - break; - } - - if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { - foreach (array_unique($matches[0]) as $char) { - $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); - } - } - - //Replace every spaces to _ (more readable than =20) - return str_replace(' ', '_', $encoded); -} - - - /** - * Adds a string or binary attachment (non-filesystem) to the list. - * This method can be used to attach ascii or binary data, - * such as a BLOB record from a database. - * @param string $string String attachment data. - * @param string $filename Name of the attachment. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return void - */ - public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = '') { - //If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($filename); - } - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $filename, - 2 => basename($filename), - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => 'attachment', - 7 => 0 - ); - } - - /** - * Add an embedded attachment from a file. - * This can include images, sounds, and just about any other document type. - * @param string $path Path to the attachment. - * @param string $cid Content ID of the attachment; Use this to reference - * the content when using an embedded image in HTML. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File MIME type. - * @return bool True on successfully adding an attachment - */ - public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '') { - if ( !@is_file($path) ) { - $this->SetError($this->Lang('file_access') . $path); - return false; - } - - //If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($path); - } - - $filename = basename($path); - if ( $name == '' ) { - $name = $filename; - } - - // Append to $attachment array - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => 'inline', - 7 => $cid - ); - return true; - } - - - /** - * Add an embedded stringified attachment. - * This can include images, sounds, and just about any other document type. - * Be sure to set the $type to an image type for images: - * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. - * @param string $string The attachment binary data. - * @param string $cid Content ID of the attachment; Use this to reference - * the content when using an embedded image in HTML. - * @param string $name - * @param string $encoding File encoding (see $Encoding). - * @param string $type MIME type. - * @return bool True on successfully adding an attachment - */ - public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = '') { - //If a MIME type is not specified, try to work it out from the name - if ($type == '') { - $type = self::filenameToType($name); - } - - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $name, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => 'inline', - 7 => $cid - ); - return true; - } - - /** - * Returns true if an inline attachment is present. - * @access public - * @return bool - */ - public function InlineImageExists() { - foreach($this->attachment as $attachment) { - if ($attachment[6] == 'inline') { - return true; - } - } - return false; - } - - /** - * Returns true if an attachment (non-inline) is present. - * @return bool - */ - public function AttachmentExists() { - foreach($this->attachment as $attachment) { - if ($attachment[6] == 'attachment') { - return true; - } - } - return false; - } - - /** - * Does this message have an alternative body set? - * @return bool - */ - public function AlternativeExists() { - return !empty($this->AltBody); - } - - ///////////////////////////////////////////////// - // CLASS METHODS, MESSAGE RESET - ///////////////////////////////////////////////// - - /** - * Clears all recipients assigned in the TO array. Returns void. - * @return void - */ - public function ClearAddresses() { - foreach($this->to as $to) { - unset($this->all_recipients[strtolower($to[0])]); - } - $this->to = array(); - } - - /** - * Clears all recipients assigned in the CC array. Returns void. - * @return void - */ - public function ClearCCs() { - foreach($this->cc as $cc) { - unset($this->all_recipients[strtolower($cc[0])]); - } - $this->cc = array(); - } - - /** - * Clears all recipients assigned in the BCC array. Returns void. - * @return void - */ - public function ClearBCCs() { - foreach($this->bcc as $bcc) { - unset($this->all_recipients[strtolower($bcc[0])]); - } - $this->bcc = array(); - } - - /** - * Clears all recipients assigned in the ReplyTo array. Returns void. - * @return void - */ - public function ClearReplyTos() { - $this->ReplyTo = array(); - } - - /** - * Clears all recipients assigned in the TO, CC and BCC - * array. Returns void. - * @return void - */ - public function ClearAllRecipients() { - $this->to = array(); - $this->cc = array(); - $this->bcc = array(); - $this->all_recipients = array(); - } - - /** - * Clears all previously set filesystem, string, and binary - * attachments. Returns void. - * @return void - */ - public function ClearAttachments() { - $this->attachment = array(); - } - - /** - * Clears all custom headers. Returns void. - * @return void - */ - public function ClearCustomHeaders() { - $this->CustomHeader = array(); - } - - ///////////////////////////////////////////////// - // CLASS METHODS, MISCELLANEOUS - ///////////////////////////////////////////////// - - /** - * Adds the error message to the error container. - * @access protected - * @param string $msg - * @return void - */ - protected function SetError($msg) { - $this->error_count++; - if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { - $lasterror = $this->smtp->getError(); - if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { - $msg .= '

    ' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "

    \n"; - } - } - $this->ErrorInfo = $msg; - } - - /** - * Returns the proper RFC 822 formatted date. - * @access public - * @return string - * @static - */ - public static function RFCDate() { - //Set the time zone to whatever the default is to avoid 500 errors - //Will default to UTC if it's not set properly in php.ini - date_default_timezone_set(@date_default_timezone_get()); - return date('D, j M Y H:i:s O'); - } - - /** - * Returns the server hostname or 'localhost.localdomain' if unknown. - * @access protected - * @return string - */ - protected function ServerHostname() { - if (!empty($this->Hostname)) { - $result = $this->Hostname; - } elseif (isset($_SERVER['SERVER_NAME'])) { - $result = $_SERVER['SERVER_NAME']; - } else { - $result = 'localhost.localdomain'; - } - - return $result; - } - - /** - * Returns a message in the appropriate language. - * @access protected - * @param string $key - * @return string - */ - protected function Lang($key) { - if(count($this->language) < 1) { - $this->SetLanguage('en'); // set the default language - } - - if(isset($this->language[$key])) { - return $this->language[$key]; - } else { - return 'Language string failed to load: ' . $key; - } - } - - /** - * Returns true if an error occurred. - * @access public - * @return bool - */ - public function IsError() { - return ($this->error_count > 0); - } - - /** - * Changes every end of line from CRLF, CR or LF to $this->LE. - * @access public - * @param string $str String to FixEOL - * @return string - */ - public function FixEOL($str) { - // condense down to \n - $nstr = str_replace(array("\r\n", "\r"), "\n", $str); - // Now convert LE as needed - if ($this->LE !== "\n") { - $nstr = str_replace("\n", $this->LE, $nstr); - } - return $nstr; - } - - /** - * Adds a custom header. $name value can be overloaded to contain - * both header name and value (name:value) - * @access public - * @param string $name custom header name - * @param string $value header value - * @return void - */ - public function AddCustomHeader($name, $value=null) { - if ($value === null) { - // Value passed in as name:value - $this->CustomHeader[] = explode(':', $name, 2); - } else { - $this->CustomHeader[] = array($name, $value); - } - } - - /** - * Creates a message from an HTML string, making modifications for inline images and backgrounds - * and creates a plain-text version by converting the HTML - * Overwrites any existing values in $this->Body and $this->AltBody - * @access public - * @param string $message HTML message string - * @param string $basedir baseline directory for path - * @param bool $advanced Whether to use the advanced HTML to text converter - * @return string $message - */ - public function MsgHTML($message, $basedir = '', $advanced = false) { - preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); - if (isset($images[2])) { - foreach ($images[2] as $i => $url) { - // do not change urls for absolute images (thanks to corvuscorax) - if (!preg_match('#^[A-z]+://#', $url)) { - $filename = basename($url); - $directory = dirname($url); - if ($directory == '.') { - $directory = ''; - } - $cid = md5($url).'@phpmailer.0'; //RFC2392 S 2 - if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { - $basedir .= '/'; - } - if (strlen($directory) > 1 && substr($directory, -1) != '/') { - $directory .= '/'; - } - if ($this->AddEmbeddedImage($basedir.$directory.$filename, $cid, $filename, 'base64', self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)))) { - $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"cid:".$cid."\"", $message); - } - } - } - } - $this->IsHTML(true); - if (empty($this->AltBody)) { - $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; - } - //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better - $this->Body = $this->NormalizeBreaks($message); - $this->AltBody = $this->NormalizeBreaks($this->html2text($message, $advanced)); - return $this->Body; - } - - /** - * Convert an HTML string into a plain text version - * @param string $html The HTML text to convert - * @param bool $advanced Should this use the more complex html2text converter or just a simple one? - * @return string - */ - public function html2text($html, $advanced = false) { - if ($advanced) { - require_once 'extras/class.html2text.php'; - $h = new html2text($html); - return $h->get_text(); - } - return html_entity_decode(trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), ENT_QUOTES, $this->CharSet); - } - - /** - * Gets the MIME type of the embedded or inline image - * @param string $ext File extension - * @access public - * @return string MIME type of ext - * @static - */ - public static function _mime_types($ext = '') { - $mimes = array( - 'xl' => 'application/excel', - 'hqx' => 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'bin' => 'application/macbinary', - 'doc' => 'application/msword', - 'word' => 'application/msword', - 'class' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'psd' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'so' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => 'application/pdf', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'php3' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'php' => 'application/x-httpd-php', - 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'js' => 'application/x-javascript', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => 'application/x-tar', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'zip' => 'application/zip', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mpga' => 'audio/mpeg', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'wav' => 'audio/x-wav', - 'bmp' => 'image/bmp', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'png' => 'image/png', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'eml' => 'message/rfc822', - 'css' => 'text/css', - 'html' => 'text/html', - 'htm' => 'text/html', - 'shtml' => 'text/html', - 'log' => 'text/plain', - 'text' => 'text/plain', - 'txt' => 'text/plain', - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'mpeg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mov' => 'video/quicktime', - 'qt' => 'video/quicktime', - 'rv' => 'video/vnd.rn-realvideo', - 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie' - ); - return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; - } - - /** - * Try to map a file name to a MIME type, default to application/octet-stream - * @param string $filename A file name or full path, does not need to exist as a file - * @return string - * @static - */ - public static function filenameToType($filename) { - //In case the path is a URL, strip any query string before getting extension - $qpos = strpos($filename, '?'); - if ($qpos !== false) { - $filename = substr($filename, 0, $qpos); - } - $pathinfo = self::mb_pathinfo($filename); - return self::_mime_types($pathinfo['extension']); - } - - /** - * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. - * Works similarly to the one in PHP >= 5.2.0 - * @link http://www.php.net/manual/en/function.pathinfo.php#107461 - * @param string $path A filename or path, does not need to exist as a file - * @param integer|string $options Either a PATHINFO_* constant, or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 - * @return string|array - * @static - */ - public static function mb_pathinfo($path, $options = null) { - $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); - $m = array(); - preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); - if(array_key_exists(1, $m)) { - $ret['dirname'] = $m[1]; - } - if(array_key_exists(2, $m)) { - $ret['basename'] = $m[2]; - } - if(array_key_exists(5, $m)) { - $ret['extension'] = $m[5]; - } - if(array_key_exists(3, $m)) { - $ret['filename'] = $m[3]; - } - switch($options) { - case PATHINFO_DIRNAME: - case 'dirname': - return $ret['dirname']; - break; - case PATHINFO_BASENAME: - case 'basename': - return $ret['basename']; - break; - case PATHINFO_EXTENSION: - case 'extension': - return $ret['extension']; - break; - case PATHINFO_FILENAME: - case 'filename': - return $ret['filename']; - break; - default: - return $ret; - } - } - - /** - * Set (or reset) Class Objects (variables) - * - * Usage Example: - * $page->set('X-Priority', '3'); - * - * @access public - * @param string $name - * @param mixed $value - * NOTE: will not work with arrays, there are no arrays to set/reset - * @throws phpmailerException - * @return bool - * @todo Should this not be using __set() magic function? - */ - public function set($name, $value = '') { - try { - if (isset($this->$name) ) { - $this->$name = $value; - } else { - throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL); - } - } catch (Exception $e) { - $this->SetError($e->getMessage()); - if ($e->getCode() == self::STOP_CRITICAL) { - return false; - } - } - return true; - } - - /** - * Strips newlines to prevent header injection. - * @access public - * @param string $str - * @return string - */ - public function SecureHeader($str) { - return trim(str_replace(array("\r", "\n"), '', $str)); - } - - /** - * Normalize UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format - * Defaults to CRLF (for message bodies) and preserves consecutive breaks - * @param string $text - * @param string $breaktype What kind of line break to use, defaults to CRLF - * @return string - * @access public - * @static - */ - public static function NormalizeBreaks($text, $breaktype = "\r\n") { - return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); - } - - - /** - * Set the private key file and password to sign the message. - * - * @access public - * @param string $cert_filename - * @param string $key_filename - * @param string $key_pass Password for private key - */ - public function Sign($cert_filename, $key_filename, $key_pass) { - $this->sign_cert_file = $cert_filename; - $this->sign_key_file = $key_filename; - $this->sign_key_pass = $key_pass; - } - - /** - * Set the private key file and password to sign the message. - * - * @access public - * @param string $txt - * @return string - */ - public function DKIM_QP($txt) { - $line = ''; - for ($i = 0; $i < strlen($txt); $i++) { - $ord = ord($txt[$i]); - if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) { - $line .= $txt[$i]; - } else { - $line .= "=".sprintf("%02X", $ord); - } - } - return $line; - } - - /** - * Generate DKIM signature - * - * @access public - * @param string $s Header - * @throws phpmailerException - * @return string - */ - public function DKIM_Sign($s) { - if (!defined('PKCS7_TEXT')) { - if ($this->exceptions) { - throw new phpmailerException($this->Lang("signing").' OpenSSL extension missing.'); - } - return ''; - } - $privKeyStr = file_get_contents($this->DKIM_private); - if ($this->DKIM_passphrase != '') { - $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); - } else { - $privKey = $privKeyStr; - } - if (openssl_sign($s, $signature, $privKey)) { - return base64_encode($signature); - } - return ''; - } - - /** - * Generate DKIM Canonicalization Header - * - * @access public - * @param string $s Header - * @return string - */ - public function DKIM_HeaderC($s) { - $s = preg_replace("/\r\n\s+/", " ", $s); - $lines = explode("\r\n", $s); - foreach ($lines as $key => $line) { - list($heading, $value) = explode(":", $line, 2); - $heading = strtolower($heading); - $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces - $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value - } - $s = implode("\r\n", $lines); - return $s; - } - - /** - * Generate DKIM Canonicalization Body - * - * @access public - * @param string $body Message Body - * @return string - */ - public function DKIM_BodyC($body) { - if ($body == '') return "\r\n"; - // stabilize line endings - $body = str_replace("\r\n", "\n", $body); - $body = str_replace("\n", "\r\n", $body); - // END stabilize line endings - while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { - $body = substr($body, 0, strlen($body) - 2); - } - return $body; - } - - /** - * Create the DKIM header, body, as new header - * - * @access public - * @param string $headers_line Header lines - * @param string $subject Subject - * @param string $body Body - * @return string - */ - public function DKIM_Add($headers_line, $subject, $body) { - $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms - $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body - $DKIMquery = 'dns/txt'; // Query method - $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) - $subject_header = "Subject: $subject"; - $headers = explode($this->LE, $headers_line); - $from_header = ''; - $to_header = ''; - $current = ''; - foreach($headers as $header) { - if (strpos($header, 'From:') === 0) { - $from_header = $header; - $current = 'from_header'; - } elseif (strpos($header, 'To:') === 0) { - $to_header = $header; - $current = 'to_header'; - } else { - if($current && strpos($header, ' =?') === 0){ - $current .= $header; - } else { - $current = ''; - } - } - } - $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); - $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); - $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable - $body = $this->DKIM_BodyC($body); - $DKIMlen = strlen($body) ; // Length of body - $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body - $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";"; - $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n". - "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n". - "\th=From:To:Subject;\r\n". - "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n". - "\tz=$from\r\n". - "\t|$to\r\n". - "\t|$subject;\r\n". - "\tbh=" . $DKIMb64 . ";\r\n". - "\tb="; - $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); - $signed = $this->DKIM_Sign($toSign); - return $dkimhdrs.$signed."\r\n"; - } - - /** - * Perform callback - * @param boolean $isSent - * @param string $to - * @param string $cc - * @param string $bcc - * @param string $subject - * @param string $body - * @param string $from - */ - protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) { - if (!empty($this->action_function) && is_callable($this->action_function)) { - $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); - call_user_func_array($this->action_function, $params); - } - } -} - -/** - * Exception handler for PHPMailer - * @package PHPMailer - */ -class phpmailerException extends Exception { - /** - * Prettify error message output - * @return string - */ - public function errorMessage() { - $errorMsg = '' . $this->getMessage() . "
    \n"; - return $errorMsg; - } -} diff --git a/vendor/PHPMailer/class.smtp.php b/vendor/PHPMailer/class.smtp.php deleted file mode 100644 index 4b02f999..00000000 --- a/vendor/PHPMailer/class.smtp.php +++ /dev/null @@ -1,1092 +0,0 @@ -Debugoutput) { - case 'error_log': - error_log($str); - break; - case 'html': - //Cleans up output a bit for a better looking display that's HTML-safe - echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8')."
    \n"; - break; - case 'echo': - default: - //Just echoes exactly what was received - echo $str; - } - } - - /** - * Initialize the class so that the data is in a known state. - * @access public - * @return SMTP - */ - public function __construct() { - $this->smtp_conn = 0; - $this->error = null; - $this->helo_rply = null; - - $this->do_debug = 0; - } - - ///////////////////////////////////////////////// - // CONNECTION FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Connect to an SMTP server - * - * SMTP CODE SUCCESS: 220 - * SMTP CODE FAILURE: 421 - * @access public - * @param string $host SMTP server IP or host name - * @param int $port The port number to connect to, or use the default port if not specified - * @param int $timeout How long to wait for the connection to open - * @param array $options An array of options compatible with stream_context_create() - * @return bool - */ - public function Connect($host, $port = 0, $timeout = 30, $options = array()) { - // Clear errors to avoid confusion - $this->error = null; - - // Make sure we are __not__ connected - if($this->connected()) { - // Already connected, generate error - $this->error = array('error' => 'Already connected to a server'); - return false; - } - - if(empty($port)) { - $port = $this->SMTP_PORT; - } - - // Connect to the SMTP server - $errno = 0; - $errstr = ''; - $socket_context = stream_context_create($options); - //Need to suppress errors here as connection failures can be handled at a higher level - $this->smtp_conn = @stream_socket_client($host.":".$port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context); - - // Verify we connected properly - if(empty($this->smtp_conn)) { - $this->error = array('error' => 'Failed to connect to server', - 'errno' => $errno, - 'errstr' => $errstr); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ": $errstr ($errno)"); - } - return false; - } - - // SMTP server can take longer to respond, give longer timeout for first read - // Windows does not have support for this timeout function - if(substr(PHP_OS, 0, 3) != 'WIN') { - $max = ini_get('max_execution_time'); - if ($max != 0 && $timeout > $max) { // Don't bother if unlimited - @set_time_limit($timeout); - } - stream_set_timeout($this->smtp_conn, $timeout, 0); - } - - // get any announcement - $announce = $this->get_lines(); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $announce); - } - - return true; - } - - /** - * Initiate a TLS communication with the server. - * - * SMTP CODE 220 Ready to start TLS - * SMTP CODE 501 Syntax error (no parameters allowed) - * SMTP CODE 454 TLS not available due to temporary reason - * @access public - * @return bool success - */ - public function StartTLS() { - $this->error = null; # to avoid confusion - - if(!$this->connected()) { - $this->error = array('error' => 'Called StartTLS() without being connected'); - return false; - } - - $this->client_send('STARTTLS' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $rply); - } - - if($code != 220) { - $this->error = - array('error' => 'STARTTLS not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - // Begin encrypted connection - if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { - return false; - } - - return true; - } - - /** - * Performs SMTP authentication. Must be run after running the - * Hello() method. Returns true if successfully authenticated. - * @access public - * @param string $username - * @param string $password - * @param string $authtype - * @param string $realm - * @param string $workstation - * @return bool - */ - public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') { - if (empty($authtype)) { - $authtype = 'LOGIN'; - } - - switch ($authtype) { - case 'PLAIN': - // Start authentication - $this->client_send('AUTH PLAIN' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 334) { - $this->error = - array('error' => 'AUTH not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - // Send encoded username and password - $this->client_send(base64_encode("\0".$username."\0".$password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 235) { - $this->error = - array('error' => 'Authentication not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - break; - case 'LOGIN': - // Start authentication - $this->client_send('AUTH LOGIN' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 334) { - $this->error = - array('error' => 'AUTH not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - // Send encoded username - $this->client_send(base64_encode($username) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 334) { - $this->error = - array('error' => 'Username not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - // Send encoded password - $this->client_send(base64_encode($password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 235) { - $this->error = - array('error' => 'Password not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - break; - case 'NTLM': - /* - * ntlm_sasl_client.php - ** Bundled with Permission - ** - ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx - ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication - */ - require_once 'extras/ntlm_sasl_client.php'; - $temp = new stdClass(); - $ntlm_client = new ntlm_sasl_client_class; - if(! $ntlm_client->Initialize($temp)){//let's test if every function its available - $this->error = array('error' => $temp->error); - if($this->do_debug >= 1) { - $this->edebug('You need to enable some modules in your php.ini file: ' . $this->error['error']); - } - return false; - } - $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1 - - $this->client_send('AUTH NTLM ' . base64_encode($msg1) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 334) { - $this->error = - array('error' => 'AUTH not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - $challenge = substr($rply, 3);//though 0 based, there is a white space after the 3 digit number....//msg2 - $challenge = base64_decode($challenge); - $ntlm_res = $ntlm_client->NTLMResponse(substr($challenge, 24, 8), $password); - $msg3 = $ntlm_client->TypeMsg3($ntlm_res, $username, $realm, $workstation);//msg3 - // Send encoded username - $this->client_send(base64_encode($msg3) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 235) { - $this->error = - array('error' => 'Could not authenticate', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - break; - case 'CRAM-MD5': - // Start authentication - $this->client_send('AUTH CRAM-MD5' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 334) { - $this->error = - array('error' => 'AUTH not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - // Get the challenge - $challenge = base64_decode(substr($rply, 4)); - - // Build the response - $response = $username . ' ' . $this->hmac($challenge, $password); - - // Send encoded credentials - $this->client_send(base64_encode($response) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($code != 235) { - $this->error = - array('error' => 'Credentials not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - break; - } - return true; - } - - /** - * Works like hash_hmac('md5', $data, $key) in case that function is not available - * @access protected - * @param string $data - * @param string $key - * @return string - */ - protected function hmac($data, $key) { - if (function_exists('hash_hmac')) { - return hash_hmac('md5', $data, $key); - } - - // The following borrowed from http://php.net/manual/en/function.mhash.php#27225 - - // RFC 2104 HMAC implementation for php. - // Creates an md5 HMAC. - // Eliminates the need to install mhash to compute a HMAC - // Hacked by Lance Rushing - - $b = 64; // byte length for md5 - if (strlen($key) > $b) { - $key = pack('H*', md5($key)); - } - $key = str_pad($key, $b, chr(0x00)); - $ipad = str_pad('', $b, chr(0x36)); - $opad = str_pad('', $b, chr(0x5c)); - $k_ipad = $key ^ $ipad ; - $k_opad = $key ^ $opad; - - return md5($k_opad . pack('H*', md5($k_ipad . $data))); - } - - /** - * Returns true if connected to a server otherwise false - * @access public - * @return bool - */ - public function Connected() { - if(!empty($this->smtp_conn)) { - $sock_status = stream_get_meta_data($this->smtp_conn); - if($sock_status['eof']) { - // the socket is valid but we are not connected - if($this->do_debug >= 1) { - $this->edebug('SMTP -> NOTICE: EOF caught while checking if connected'); - } - $this->Close(); - return false; - } - return true; // everything looks good - } - return false; - } - - /** - * Closes the socket and cleans up the state of the class. - * It is not considered good to use this function without - * first trying to use QUIT. - * @access public - * @return void - */ - public function Close() { - $this->error = null; // so there is no confusion - $this->helo_rply = null; - if(!empty($this->smtp_conn)) { - // close the connection and cleanup - fclose($this->smtp_conn); - $this->smtp_conn = 0; - } - } - - ///////////////////////////////////////////////// - // SMTP COMMANDS - ///////////////////////////////////////////////// - - /** - * Issues a data command and sends the msg_data to the server - * finializing the mail transaction. $msg_data is the message - * that is to be send with the headers. Each header needs to be - * on a single line followed by a with the message headers - * and the message body being separated by and additional . - * - * Implements rfc 821: DATA - * - * SMTP CODE INTERMEDIATE: 354 - * [data] - * . - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 552, 554, 451, 452 - * SMTP CODE FAILURE: 451, 554 - * SMTP CODE ERROR : 500, 501, 503, 421 - * @access public - * @param string $msg_data - * @return bool - */ - public function Data($msg_data) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - 'error' => 'Called Data() without being connected'); - return false; - } - - $this->client_send('DATA' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $rply); - } - - if($code != 354) { - $this->error = - array('error' => 'DATA command not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - /* the server is ready to accept data! - * according to rfc 821 we should not send more than 1000 - * including the CRLF - * characters on a single line so we will break the data up - * into lines by \r and/or \n then if needed we will break - * each of those into smaller lines to fit within the limit. - * in addition we will be looking for lines that start with - * a period '.' and append and additional period '.' to that - * line. NOTE: this does not count towards limit. - */ - - // normalize the line breaks so we know the explode works - $msg_data = str_replace("\r\n", "\n", $msg_data); - $msg_data = str_replace("\r", "\n", $msg_data); - $lines = explode("\n", $msg_data); - - /* we need to find a good way to determine is headers are - * in the msg_data or if it is a straight msg body - * currently I am assuming rfc 822 definitions of msg headers - * and if the first field of the first line (':' sperated) - * does not contain a space then it _should_ be a header - * and we can process all lines before a blank "" line as - * headers. - */ - - $field = substr($lines[0], 0, strpos($lines[0], ':')); - $in_headers = false; - if(!empty($field) && !strstr($field, ' ')) { - $in_headers = true; - } - - $max_line_length = 998; // used below; set here for ease in change - - while(list(, $line) = @each($lines)) { - $lines_out = null; - if($line == '' && $in_headers) { - $in_headers = false; - } - // ok we need to break this line up into several smaller lines - while(strlen($line) > $max_line_length) { - $pos = strrpos(substr($line, 0, $max_line_length), ' '); - - // Patch to fix DOS attack - if(!$pos) { - $pos = $max_line_length - 1; - $lines_out[] = substr($line, 0, $pos); - $line = substr($line, $pos); - } else { - $lines_out[] = substr($line, 0, $pos); - $line = substr($line, $pos + 1); - } - - /* if processing headers add a LWSP-char to the front of new line - * rfc 822 on long msg headers - */ - if($in_headers) { - $line = "\t" . $line; - } - } - $lines_out[] = $line; - - // send the lines to the server - while(list(, $line_out) = @each($lines_out)) { - if(strlen($line_out) > 0) - { - if(substr($line_out, 0, 1) == '.') { - $line_out = '.' . $line_out; - } - } - $this->client_send($line_out . $this->CRLF); - } - } - - // message data has been sent - $this->client_send($this->CRLF . '.' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $rply); - } - - if($code != 250) { - $this->error = - array('error' => 'DATA not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - return true; - } - - /** - * Sends the HELO command to the smtp server. - * This makes sure that we and the server are in - * the same known state. - * - * Implements from rfc 821: HELO - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 501, 504, 421 - * @access public - * @param string $host - * @return bool - */ - public function Hello($host = '') { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - 'error' => 'Called Hello() without being connected'); - return false; - } - - // if hostname for HELO was not specified send default - if(empty($host)) { - // determine appropriate default to send to server - $host = 'localhost'; - } - - // Send extended hello first (RFC 2821) - if(!$this->SendHello('EHLO', $host)) { - if(!$this->SendHello('HELO', $host)) { - return false; - } - } - - return true; - } - - /** - * Sends a HELO/EHLO command. - * @access protected - * @param string $hello - * @param string $host - * @return bool - */ - protected function SendHello($hello, $host) { - $this->client_send($hello . ' ' . $host . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER: ' . $rply); - } - - if($code != 250) { - $this->error = - array('error' => $hello . ' not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - $this->helo_rply = $rply; - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. - * - * Implements rfc 821: MAIL FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552, 451, 452 - * SMTP CODE SUCCESS: 500, 501, 421 - * @access public - * @param string $from - * @return bool - */ - public function Mail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - 'error' => 'Called Mail() without being connected'); - return false; - } - - $useVerp = ($this->do_verp ? ' XVERP' : ''); - $this->client_send('MAIL FROM:<' . $from . '>' . $useVerp . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $rply); - } - - if($code != 250) { - $this->error = - array('error' => 'MAIL not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - return true; - } - - /** - * Sends the quit command to the server and then closes the socket - * if there is no error or the $close_on_error argument is true. - * - * Implements from rfc 821: QUIT - * - * SMTP CODE SUCCESS: 221 - * SMTP CODE ERROR : 500 - * @access public - * @param bool $close_on_error - * @return bool - */ - public function Quit($close_on_error = true) { - $this->error = null; // so there is no confusion - - if(!$this->connected()) { - $this->error = array( - 'error' => 'Called Quit() without being connected'); - return false; - } - - // send the quit command to the server - $this->client_send('quit' . $this->CRLF); - - // get any good-bye messages - $byemsg = $this->get_lines(); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $byemsg); - } - - $rval = true; - $e = null; - - $code = substr($byemsg, 0, 3); - if($code != 221) { - // use e as a tmp var cause Close will overwrite $this->error - $e = array('error' => 'SMTP server rejected quit command', - 'smtp_code' => $code, - 'smtp_rply' => substr($byemsg, 4)); - $rval = false; - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $e['error'] . ': ' . $byemsg); - } - } - - if(empty($e) || $close_on_error) { - $this->Close(); - } - - return $rval; - } - - /** - * Sends the command RCPT to the SMTP server with the TO: argument of $to. - * Returns true if the recipient was accepted false if it was rejected. - * - * Implements from rfc 821: RCPT TO: - * - * SMTP CODE SUCCESS: 250, 251 - * SMTP CODE FAILURE: 550, 551, 552, 553, 450, 451, 452 - * SMTP CODE ERROR : 500, 501, 503, 421 - * @access public - * @param string $to - * @return bool - */ - public function Recipient($to) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - 'error' => 'Called Recipient() without being connected'); - return false; - } - - $this->client_send('RCPT TO:<' . $to . '>' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $rply); - } - - if($code != 250 && $code != 251) { - $this->error = - array('error' => 'RCPT not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - return true; - } - - /** - * Sends the RSET command to abort and transaction that is - * currently in progress. Returns true if successful false - * otherwise. - * - * Implements rfc 821: RSET - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 501, 504, 421 - * @access public - * @return bool - */ - public function Reset() { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array('error' => 'Called Reset() without being connected'); - return false; - } - - $this->client_send('RSET' . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $rply); - } - - if($code != 250) { - $this->error = - array('error' => 'RSET failed', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in and send them an email. - * - * Implements rfc 821: SAML FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552, 451, 452 - * SMTP CODE SUCCESS: 500, 501, 502, 421 - * @access public - * @param string $from - * @return bool - */ - public function SendAndMail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - 'error' => 'Called SendAndMail() without being connected'); - return false; - } - - $this->client_send('SAML FROM:' . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply, 0, 3); - - if($this->do_debug >= 2) { - $this->edebug('SMTP -> FROM SERVER:' . $rply); - } - - if($code != 250) { - $this->error = - array('error' => 'SAML not accepted from server', - 'smtp_code' => $code, - 'smtp_msg' => substr($rply, 4)); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); - } - return false; - } - return true; - } - - /** - * This is an optional command for SMTP that this class does not - * support. This method is here to make the RFC821 Definition - * complete for this class and __may__ be implimented in the future - * - * Implements from rfc 821: TURN - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 502 - * SMTP CODE ERROR : 500, 503 - * @access public - * @return bool - */ - public function Turn() { - $this->error = array('error' => 'This method, TURN, of the SMTP '. - 'is not implemented'); - if($this->do_debug >= 1) { - $this->edebug('SMTP -> NOTICE: ' . $this->error['error']); - } - return false; - } - - /** - * Sends data to the server - * @param string $data - * @access public - * @return Integer number of bytes sent to the server or FALSE on error - */ - public function client_send($data) { - if ($this->do_debug >= 1) { - $this->edebug("CLIENT -> SMTP: $data"); - } - return fwrite($this->smtp_conn, $data); - } - - /** - * Get the current error - * @access public - * @return array - */ - public function getError() { - return $this->error; - } - - ///////////////////////////////////////////////// - // INTERNAL FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Read in as many lines as possible - * either before eof or socket timeout occurs on the operation. - * With SMTP we can tell if we have more lines to read if the - * 4th character is '-' symbol. If it is a space then we don't - * need to read anything else. - * @access protected - * @return string - */ - protected function get_lines() { - $data = ''; - $endtime = 0; - /* If for some reason the fp is bad, don't inf loop */ - if (!is_resource($this->smtp_conn)) { - return $data; - } - stream_set_timeout($this->smtp_conn, $this->Timeout); - if ($this->Timelimit > 0) { - $endtime = time() + $this->Timelimit; - } - while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { - $str = @fgets($this->smtp_conn, 515); - if($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); - $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); - } - $data .= $str; - if($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); - } - // if 4th character is a space, we are done reading, break the loop - if(substr($str, 3, 1) == ' ') { break; } - // Timed-out? Log and break - $info = stream_get_meta_data($this->smtp_conn); - if ($info['timed_out']) { - if($this->do_debug >= 4) { - $this->edebug('SMTP -> get_lines(): timed-out (' . $this->Timeout . ' seconds)'); - } - break; - } - // Now check if reads took too long - if ($endtime) { - if (time() > $endtime) { - if($this->do_debug >= 4) { - $this->edebug('SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' seconds)'); - } - break; - } - } - } - return $data; - } - -}