obrazec
Deploy to Development and Production / deploy (push) Successful in 4s Details

This commit is contained in:
Mark Poljanšek 2025-09-09 20:49:19 +02:00
parent f73ef2eb2b
commit b9c87390d5
12 changed files with 19417 additions and 133 deletions

8
api/config.php Normal file
View File

@ -0,0 +1,8 @@
<?php
// Zamenjajte z vašimi dejanskimi podatki!
define('SMTP_HOST', 'mail.herminamerc.si');
define('SMTP_USERNAME', 'info@herminamerc.si');
define('SMTP_PASSWORD', 'HERmina112358:'); // Bodite previdni s posebnimi znaki
define('SMTP_PORT', 465);
define('SMTP_SECURE', 'ssl'); // 'ssl' za port 465, 'tls' za 587
?>

View File

@ -0,0 +1,245 @@
<?php
/**
* PHPMailer - PHP email creation and transport class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2023 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace PHPMailer\PHPMailer;
/**
* Configure PHPMailer with DSN string.
*
* @see https://en.wikipedia.org/wiki/Data_source_name
*
* @author Oleg Voronkovich <oleg-voronkovich@yandex.ru>
*/
class DSNConfigurator
{
/**
* Create new PHPMailer instance configured by DSN.
*
* @param string $dsn DSN
* @param bool $exceptions Should we throw external exceptions?
*
* @return PHPMailer
*/
public static function mailer($dsn, $exceptions = null)
{
static $configurator = null;
if (null === $configurator) {
$configurator = new DSNConfigurator();
}
return $configurator->configure(new PHPMailer($exceptions), $dsn);
}
/**
* Configure PHPMailer instance with DSN string.
*
* @param PHPMailer $mailer PHPMailer instance
* @param string $dsn DSN
*
* @return PHPMailer
*/
public function configure(PHPMailer $mailer, $dsn)
{
$config = $this->parseDSN($dsn);
$this->applyConfig($mailer, $config);
return $mailer;
}
/**
* Parse DSN string.
*
* @param string $dsn DSN
*
* @throws Exception If DSN is malformed
*
* @return array Configuration
*/
private function parseDSN($dsn)
{
$config = $this->parseUrl($dsn);
if (false === $config || !isset($config['scheme']) || !isset($config['host'])) {
throw new Exception('Malformed DSN');
}
if (isset($config['query'])) {
parse_str($config['query'], $config['query']);
}
return $config;
}
/**
* Apply configuration to mailer.
*
* @param PHPMailer $mailer PHPMailer instance
* @param array $config Configuration
*
* @throws Exception If scheme is invalid
*/
private function applyConfig(PHPMailer $mailer, $config)
{
switch ($config['scheme']) {
case 'mail':
$mailer->isMail();
break;
case 'sendmail':
$mailer->isSendmail();
break;
case 'qmail':
$mailer->isQmail();
break;
case 'smtp':
case 'smtps':
$mailer->isSMTP();
$this->configureSMTP($mailer, $config);
break;
default:
throw new Exception(
sprintf(
'Invalid scheme: "%s". Allowed values: "mail", "sendmail", "qmail", "smtp", "smtps".',
$config['scheme']
)
);
}
if (isset($config['query'])) {
$this->configureOptions($mailer, $config['query']);
}
}
/**
* Configure SMTP.
*
* @param PHPMailer $mailer PHPMailer instance
* @param array $config Configuration
*/
private function configureSMTP($mailer, $config)
{
$isSMTPS = 'smtps' === $config['scheme'];
if ($isSMTPS) {
$mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
}
$mailer->Host = $config['host'];
if (isset($config['port'])) {
$mailer->Port = $config['port'];
} elseif ($isSMTPS) {
$mailer->Port = SMTP::DEFAULT_SECURE_PORT;
}
$mailer->SMTPAuth = isset($config['user']) || isset($config['pass']);
if (isset($config['user'])) {
$mailer->Username = $config['user'];
}
if (isset($config['pass'])) {
$mailer->Password = $config['pass'];
}
}
/**
* Configure options.
*
* @param PHPMailer $mailer PHPMailer instance
* @param array $options Options
*
* @throws Exception If option is unknown
*/
private function configureOptions(PHPMailer $mailer, $options)
{
$allowedOptions = get_object_vars($mailer);
unset($allowedOptions['Mailer']);
unset($allowedOptions['SMTPAuth']);
unset($allowedOptions['Username']);
unset($allowedOptions['Password']);
unset($allowedOptions['Hostname']);
unset($allowedOptions['Port']);
unset($allowedOptions['ErrorInfo']);
$allowedOptions = \array_keys($allowedOptions);
foreach ($options as $key => $value) {
if (!in_array($key, $allowedOptions)) {
throw new Exception(
sprintf(
'Unknown option: "%s". Allowed values: "%s"',
$key,
implode('", "', $allowedOptions)
)
);
}
switch ($key) {
case 'AllowEmpty':
case 'SMTPAutoTLS':
case 'SMTPKeepAlive':
case 'SingleTo':
case 'UseSendmailOptions':
case 'do_verp':
case 'DKIM_copyHeaderFields':
$mailer->$key = (bool) $value;
break;
case 'Priority':
case 'SMTPDebug':
case 'WordWrap':
$mailer->$key = (int) $value;
break;
default:
$mailer->$key = $value;
break;
}
}
}
/**
* Parse a URL.
* Wrapper for the built-in parse_url function to work around a bug in PHP 5.5.
*
* @param string $url URL
*
* @return array|false
*/
protected function parseUrl($url)
{
if (\PHP_VERSION_ID >= 50600 || false === strpos($url, '?')) {
return parse_url($url);
}
$chunks = explode('?', $url);
if (is_array($chunks)) {
$result = parse_url($chunks[0]);
if (is_array($result)) {
$result['query'] = $chunks[1];
}
return $result;
}
return false;
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* PHPMailer Exception class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace PHPMailer\PHPMailer;
/**
* PHPMailer exception handler.
*
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
*/
class Exception extends \Exception
{
/**
* Prettify error message output.
*
* @return string
*/
public function errorMessage()
{
return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n";
}
}

139
api/phpmailer/src/OAuth.php Normal file
View File

@ -0,0 +1,139 @@
<?php
/**
* PHPMailer - PHP email creation and transport class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace PHPMailer\PHPMailer;
use League\OAuth2\Client\Grant\RefreshToken;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Token\AccessToken;
/**
* OAuth - OAuth2 authentication wrapper class.
* Uses the oauth2-client package from the League of Extraordinary Packages.
*
* @see https://oauth2-client.thephpleague.com
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
*/
class OAuth implements OAuthTokenProvider
{
/**
* An instance of the League OAuth Client Provider.
*
* @var AbstractProvider
*/
protected $provider;
/**
* The current OAuth access token.
*
* @var AccessToken
*/
protected $oauthToken;
/**
* The user's email address, usually used as the login ID
* and also the from address when sending email.
*
* @var string
*/
protected $oauthUserEmail = '';
/**
* The client secret, generated in the app definition of the service you're connecting to.
*
* @var string
*/
protected $oauthClientSecret = '';
/**
* The client ID, generated in the app definition of the service you're connecting to.
*
* @var string
*/
protected $oauthClientId = '';
/**
* The refresh token, used to obtain new AccessTokens.
*
* @var string
*/
protected $oauthRefreshToken = '';
/**
* OAuth constructor.
*
* @param array $options Associative array containing
* `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements
*/
public function __construct($options)
{
$this->provider = $options['provider'];
$this->oauthUserEmail = $options['userName'];
$this->oauthClientSecret = $options['clientSecret'];
$this->oauthClientId = $options['clientId'];
$this->oauthRefreshToken = $options['refreshToken'];
}
/**
* Get a new RefreshToken.
*
* @return RefreshToken
*/
protected function getGrant()
{
return new RefreshToken();
}
/**
* Get a new AccessToken.
*
* @return AccessToken
*/
protected function getToken()
{
return $this->provider->getAccessToken(
$this->getGrant(),
['refresh_token' => $this->oauthRefreshToken]
);
}
/**
* Generate a base64-encoded OAuth token.
*
* @return string
*/
public function getOauth64()
{
//Get a new token if it's not available or has expired
if (null === $this->oauthToken || $this->oauthToken->hasExpired()) {
$this->oauthToken = $this->getToken();
}
return base64_encode(
'user=' .
$this->oauthUserEmail .
"\001auth=Bearer " .
$this->oauthToken .
"\001\001"
);
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* PHPMailer - PHP email creation and transport class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace PHPMailer\PHPMailer;
/**
* OAuthTokenProvider - OAuth2 token provider interface.
* Provides base64 encoded OAuth2 auth strings for SMTP authentication.
*
* @see OAuth
* @see SMTP::authenticate()
*
* @author Peter Scopes (pdscopes)
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
*/
interface OAuthTokenProvider
{
/**
* Generate a base64-encoded OAuth token ensuring that the access token has not expired.
* The string to be base 64 encoded should be in the form:
* "user=<user_email_address>\001auth=Bearer <access_token>\001\001"
*
* @return string
*/
public function getOauth64();
}

File diff suppressed because it is too large Load Diff

469
api/phpmailer/src/POP3.php Normal file
View File

@ -0,0 +1,469 @@
<?php
/**
* PHPMailer POP-Before-SMTP Authentication Class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace PHPMailer\PHPMailer;
/**
* PHPMailer POP-Before-SMTP Authentication Class.
* Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
* 1) This class does not support APOP authentication.
* 2) Opening and closing lots of POP3 connections can be quite slow. If you need
* to send a batch of emails then just perform the authentication once at the start,
* and then loop through your mail sending script. Providing this process doesn't
* take longer than the verification period lasts on your POP3 server, you should be fine.
* 3) This is really ancient technology; you should only need to use it to talk to very old systems.
* 4) This POP3 class is deliberately lightweight and incomplete, implementing just
* enough to do authentication.
* If you want a more complete class there are other POP3 classes for PHP available.
*
* @author Richard Davey (original author) <rich@corephp.co.uk>
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
*/
class POP3
{
/**
* The POP3 PHPMailer Version number.
*
* @var string
*/
const VERSION = '6.10.0';
/**
* Default POP3 port number.
*
* @var int
*/
const DEFAULT_PORT = 110;
/**
* Default timeout in seconds.
*
* @var int
*/
const DEFAULT_TIMEOUT = 30;
/**
* POP3 class debug output mode.
* Debug output level.
* Options:
* @see POP3::DEBUG_OFF: No output
* @see POP3::DEBUG_SERVER: Server messages, connection/server errors
* @see POP3::DEBUG_CLIENT: Client and Server messages, connection/server errors
*
* @var int
*/
public $do_debug = self::DEBUG_OFF;
/**
* POP3 mail server hostname.
*
* @var string
*/
public $host;
/**
* POP3 port number.
*
* @var int
*/
public $port;
/**
* POP3 Timeout Value in seconds.
*
* @var int
*/
public $tval;
/**
* POP3 username.
*
* @var string
*/
public $username;
/**
* POP3 password.
*
* @var string
*/
public $password;
/**
* Resource handle for the POP3 connection socket.
*
* @var resource
*/
protected $pop_conn;
/**
* Are we connected?
*
* @var bool
*/
protected $connected = false;
/**
* Error container.
*
* @var array
*/
protected $errors = [];
/**
* Line break constant.
*/
const LE = "\r\n";
/**
* Debug level for no output.
*
* @var int
*/
const DEBUG_OFF = 0;
/**
* Debug level to show server -> client messages
* also shows clients connection errors or errors from server
*
* @var int
*/
const DEBUG_SERVER = 1;
/**
* Debug level to show client -> server and server -> client messages.
*
* @var int
*/
const DEBUG_CLIENT = 2;
/**
* Simple static wrapper for all-in-one POP before SMTP.
*
* @param string $host The hostname to connect to
* @param int|bool $port The port number to connect to
* @param int|bool $timeout The timeout value
* @param string $username
* @param string $password
* @param int $debug_level
*
* @return bool
*/
public static function popBeforeSmtp(
$host,
$port = false,
$timeout = false,
$username = '',
$password = '',
$debug_level = 0
) {
$pop = new self();
return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
}
/**
* Authenticate with a POP3 server.
* A connect, login, disconnect sequence
* appropriate for POP-before SMTP authorisation.
*
* @param string $host The hostname to connect to
* @param int|bool $port The port number to connect to
* @param int|bool $timeout The timeout value
* @param string $username
* @param string $password
* @param int $debug_level
*
* @return bool
*/
public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
{
$this->host = $host;
//If no port value provided, use default
if (false === $port) {
$this->port = static::DEFAULT_PORT;
} else {
$this->port = (int) $port;
}
//If no timeout value provided, use default
if (false === $timeout) {
$this->tval = static::DEFAULT_TIMEOUT;
} else {
$this->tval = (int) $timeout;
}
$this->do_debug = $debug_level;
$this->username = $username;
$this->password = $password;
//Reset the error log
$this->errors = [];
//Connect
$result = $this->connect($this->host, $this->port, $this->tval);
if ($result) {
$login_result = $this->login($this->username, $this->password);
if ($login_result) {
$this->disconnect();
return true;
}
}
//We need to disconnect regardless of whether the login succeeded
$this->disconnect();
return false;
}
/**
* Connect to a POP3 server.
*
* @param string $host
* @param int|bool $port
* @param int $tval
*
* @return bool
*/
public function connect($host, $port = false, $tval = 30)
{
//Are we already connected?
if ($this->connected) {
return true;
}
//On Windows this will raise a PHP Warning error if the hostname doesn't exist.
//Rather than suppress it with @fsockopen, capture it cleanly instead
set_error_handler(function () {
call_user_func_array([$this, 'catchWarning'], func_get_args());
});
if (false === $port) {
$port = static::DEFAULT_PORT;
}
//Connect to the POP3 server
$errno = 0;
$errstr = '';
$this->pop_conn = fsockopen(
$host, //POP3 Host
$port, //Port #
$errno, //Error Number
$errstr, //Error Message
$tval
); //Timeout (seconds)
//Restore the error handler
restore_error_handler();
//Did we connect?
if (false === $this->pop_conn) {
//It would appear not...
$this->setError(
"Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
);
return false;
}
//Increase the stream time-out
stream_set_timeout($this->pop_conn, $tval, 0);
//Get the POP3 server response
$pop3_response = $this->getResponse();
//Check for the +OK
if ($this->checkResponse($pop3_response)) {
//The connection is established and the POP3 server is talking
$this->connected = true;
return true;
}
return false;
}
/**
* Log in to the POP3 server.
* Does not support APOP (RFC 2828, 4949).
*
* @param string $username
* @param string $password
*
* @return bool
*/
public function login($username = '', $password = '')
{
if (!$this->connected) {
$this->setError('Not connected to POP3 server');
return false;
}
if (empty($username)) {
$username = $this->username;
}
if (empty($password)) {
$password = $this->password;
}
//Send the Username
$this->sendString("USER $username" . static::LE);
$pop3_response = $this->getResponse();
if ($this->checkResponse($pop3_response)) {
//Send the Password
$this->sendString("PASS $password" . static::LE);
$pop3_response = $this->getResponse();
if ($this->checkResponse($pop3_response)) {
return true;
}
}
return false;
}
/**
* Disconnect from the POP3 server.
*/
public function disconnect()
{
// If could not connect at all, no need to disconnect
if ($this->pop_conn === false) {
return;
}
$this->sendString('QUIT' . static::LE);
// RFC 1939 shows POP3 server sending a +OK response to the QUIT command.
// Try to get it. Ignore any failures here.
try {
$this->getResponse();
} catch (Exception $e) {
//Do nothing
}
//The QUIT command may cause the daemon to exit, which will kill our connection
//So ignore errors here
try {
@fclose($this->pop_conn);
} catch (Exception $e) {
//Do nothing
}
// Clean up attributes.
$this->connected = false;
$this->pop_conn = false;
}
/**
* Get a response from the POP3 server.
*
* @param int $size The maximum number of bytes to retrieve
*
* @return string
*/
protected function getResponse($size = 128)
{
$response = fgets($this->pop_conn, $size);
if ($this->do_debug >= self::DEBUG_SERVER) {
echo 'Server -> Client: ', $response;
}
return $response;
}
/**
* Send raw data to the POP3 server.
*
* @param string $string
*
* @return int
*/
protected function sendString($string)
{
if ($this->pop_conn) {
if ($this->do_debug >= self::DEBUG_CLIENT) { //Show client messages when debug >= 2
echo 'Client -> Server: ', $string;
}
return fwrite($this->pop_conn, $string, strlen($string));
}
return 0;
}
/**
* Checks the POP3 server response.
* Looks for for +OK or -ERR.
*
* @param string $string
*
* @return bool
*/
protected function checkResponse($string)
{
if (strpos($string, '+OK') !== 0) {
$this->setError("Server reported an error: $string");
return false;
}
return true;
}
/**
* Add an error to the internal error store.
* Also display debug output if it's enabled.
*
* @param string $error
*/
protected function setError($error)
{
$this->errors[] = $error;
if ($this->do_debug >= self::DEBUG_SERVER) {
echo '<pre>';
foreach ($this->errors as $e) {
print_r($e);
}
echo '</pre>';
}
}
/**
* Get an array of error messages, if any.
*
* @return array
*/
public function getErrors()
{
return $this->errors;
}
/**
* POP3 connection error handler.
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
*/
protected function catchWarning($errno, $errstr, $errfile, $errline)
{
$this->setError(
'Connecting to the POP3 server raised a PHP warning:' .
"errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline"
);
}
}

1547
api/phpmailer/src/SMTP.php Normal file

File diff suppressed because it is too large Load Diff

141
api/send_mail.php Normal file
View File

@ -0,0 +1,141 @@
<?php
// Omogočimo prikazovanje napak za lažje razhroščevanje med razvojem.
// V produkciji to zakomentirajte ali nastavite na 0.
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Uvozimo potrebne PHPMailer razrede v globalni imenski prostor.
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
// Vključimo datoteke knjižnice PHPMailer.
// Pot je relativna na lokacijo te skripte (api/send_mail.php).
require 'phpmailer/Exception.php';
require 'phpmailer/PHPMailer.php';
require 'phpmailer/SMTP.php';
// Vključimo našo varno konfiguracijsko datoteko z gesli.
require 'config.php';
// Nastavimo glavo (header) odgovora, da bo brskalnik vedel, da pričakuje JSON.
header('Content-Type: application/json');
// Pripravimo privzet odgovor. Če pride do napake pred pošiljanjem, bo to poslano nazaj.
$response = ['success' => false, 'message' => 'Neznana napaka. Prosimo, kontaktirajte administratorja.'];
// 1. KORAK: Preverimo, ali je bila zahteva poslana z metodo POST.
// S tem preprečimo, da bi kdo do skripte dostopal neposredno preko URL-ja.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$response['message'] = 'Nedovoljen dostop.';
echo json_encode($response);
exit;
}
// 2. KORAK: Preberemo JSON podatke, ki jih je poslal JavaScript (fetch).
// Standardna PHP spremenljivka $_POST ne deluje za JSON vsebino.
$json_data = file_get_contents('php://input');
$data = json_decode($json_data, true);
// Preverimo, če so podatki pravilno dekodirani in če vsebujejo vse potrebne ključe.
if (!$data || !isset($data['name']) || !isset($data['email']) || !isset($data['message'])) {
$response['message'] = 'Manjkajoči podatki. Prosimo, izpolnite vsa polja.';
echo json_encode($response);
exit;
}
// 3. KORAK: Očistimo in validiramo prejete podatke.
$name = trim($data['name']);
$email = trim($data['email']);
$message = trim($data['message']);
if (empty($name)) {
$response['message'] = 'Prosimo, vnesite vaše ime in priimek.';
echo json_encode($response);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$response['message'] = 'Prosimo, vnesite veljaven e-poštni naslov.';
echo json_encode($response);
exit;
}
if (empty($message)) {
$response['message'] = 'Prosimo, vnesite vaše sporočilo.';
echo json_encode($response);
exit;
}
// 4. KORAK: Ustvarimo novo instanco PHPMailer.
// `true` v konstruktorju omogoči izjeme (exceptions), kar je boljši način za obravnavo napak.
$mail = new PHPMailer(true);
try {
// === Nastavitve strežnika (uporabimo konstante iz config.php) ===
// Za razhroščevanje - prikaže podroben izpis dogajanja. V produkciji nastavite na SMTP::DEBUG_OFF.
// $mail->SMTPDebug = SMTP::DEBUG_SERVER;
$mail->isSMTP();
$mail->Host = SMTP_HOST;
$mail->SMTPAuth = true;
$mail->Username = SMTP_USERNAME;
$mail->Password = SMTP_PASSWORD;
$mail->SMTPSecure = SMTP_SECURE;
$mail->Port = SMTP_PORT;
// Nastavimo kodiranje znakov na UTF-8 za pravilno podporo šumnikom.
$mail->CharSet = 'UTF-8';
// === Prejemniki ===
// Pošiljatelj - to mora biti naslov, s katerim se prijavljate (info@herminamerc.si).
// Ime pošiljatelja pa je ime, ki ga je vpisala oseba v obrazec.
$mail->setFrom(SMTP_USERNAME, $name);
// Prejemnik - to ste vi (lastnik spletne strani).
$mail->addAddress(SMTP_USERNAME, 'Hermina Merc - Spletna Stran');
// Naslov za odgovor (Reply-To) - KLJUČEN DEL!
// To nastavimo na e-mail osebe, ki je izpolnila obrazec.
// Ko boste v vašem e-mail klientu kliknili "Odgovori", boste odgovorili neposredno tej osebi.
$mail->addReplyTo($email, $name);
// === Vsebina e-sporočila ===
$mail->isHTML(false); // Pošljemo kot navadno besedilo, ne HTML.
$mail->Subject = 'Novo sporočilo s spletne strani herminamerc.si';
// Sestavimo telo sporočila.
$mail->Body = "Prejeli ste novo sporočilo preko kontaktnega obrazca.\n\n" .
"==================================================\n" .
"Ime in priimek: " . $name . "\n" .
"E-mail naslov: " . $email . "\n" .
"==================================================\n\n" .
"Sporočilo:\n" .
"--------------------------------------------------\n" .
$message . "\n" .
"--------------------------------------------------\n";
// 5. KORAK: Pošljemo e-sporočilo.
$mail->send();
// Če je pošiljanje uspelo, pripravimo uspešen odgovor.
$response['success'] = true;
$response['message'] = 'Hvala za vaše sporočilo! Odgovorili vam bomo v najkrajšem možnem času.';
} catch (Exception $e) {
// Če je prišlo do napake med pošiljanjem, jo ujamemo.
$response['success'] = false;
$response['message'] = 'Sporočila ni bilo mogoče poslati. Prosimo, poskusite znova kasneje.';
// Za razhroščevanje: zabeležimo dejansko napako v strežniški log (ne prikažemo je uporabniku).
// Na strežniku omogočite pisanje v error log.
error_log("PHPMailer napaka: " . $mail->ErrorInfo);
}
// 6. KORAK: Pošljemo JSON odgovor nazaj JavaScriptu.
echo json_encode($response);
?>

View File

@ -2,6 +2,8 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Funkcija za nalaganje HTML komponent (glava, noga). * Funkcija za nalaganje HTML komponent (glava, noga).
* Ta funkcija je bila v originalni kodi, a se na koncu ne uporablja,
* ker so glave in noge vgrajene neposredno v HTML datoteke. Pustimo jo za morebitno kasnejšo rabo.
*/ */
const loadComponent = (componentPath, placeholderId) => { const loadComponent = (componentPath, placeholderId) => {
return fetch(componentPath) return fetch(componentPath)
@ -38,8 +40,14 @@ document.addEventListener('DOMContentLoaded', () => {
const currentPagePath = window.location.pathname; const currentPagePath = window.location.pathname;
navLinks.forEach(link => { navLinks.forEach(link => {
const linkPath = new URL(link.href).pathname; // Normaliziramo poti, da se izognemo težavam z zadnjimi poševnicami
if (linkPath === currentPagePath || (linkPath !== '/' && currentPagePath.startsWith(linkPath))) { const linkPath = new URL(link.href).pathname.replace(/\/$/, "");
const currentPath = currentPagePath.replace(/\/$/, "");
// Korenska stran (/) se mora ujemati samo z linkom, ki kaže na "/"
if (linkPath === "" && currentPath === "") {
link.classList.add('active');
} else if (linkPath !== "" && currentPath.startsWith(linkPath)) {
link.classList.add('active'); link.classList.add('active');
} }
}); });
@ -51,7 +59,7 @@ document.addEventListener('DOMContentLoaded', () => {
const initializeApp = () => { const initializeApp = () => {
// ================================================================= // =================================================================
// === LOGIKA ZA DRSNIK NA DOMAČI STRANI (PRILAGOJENO ZA MOBILNE NAPRAVE) === // === LOGIKA ZA DRSNIK NA DOMAČI STRANI (NESPREMENJENO) ===
// ================================================================= // =================================================================
const sliderElement = document.getElementById("slider"); const sliderElement = document.getElementById("slider");
@ -64,49 +72,40 @@ document.addEventListener('DOMContentLoaded', () => {
let slideInterval; let slideInterval;
const autoPlayDelay = 5000; // 5 sekund const autoPlayDelay = 5000; // 5 sekund
// Funkcija za prikaz prosojnice
const showSlide = (index) => { const showSlide = (index) => {
if (slides.length === 0) return; if (slides.length === 0) return;
slides[current].classList.remove("active"); slides[current].classList.remove("active");
current = (index + total) % total; // Modulo za ciklično ponavljanje current = (index + total) % total;
slides[current].classList.add("active"); slides[current].classList.add("active");
}; };
// Funkcije za premikanje in pavzo
const nextSlide = () => showSlide(current + 1); const nextSlide = () => showSlide(current + 1);
const prevSlide = () => showSlide(current - 1); const prevSlide = () => showSlide(current - 1);
const pauseSlider = () => clearInterval(slideInterval); const pauseSlider = () => clearInterval(slideInterval);
// Funkcija za zagon (ali ponovni zagon) drsnika
const startSlider = () => { const startSlider = () => {
pauseSlider(); // Najprej ustavimo morebitni obstoječi interval pauseSlider();
if (slides.length > 1) { // Zaženemo samo, če je več kot en slide if (slides.length > 1) {
slideInterval = setInterval(nextSlide, autoPlayDelay); slideInterval = setInterval(nextSlide, autoPlayDelay);
} }
}; };
// Dogodki za ročno navigacijo if (prevBtn && nextBtn) {
prevBtn.addEventListener("click", () => { prevBtn.addEventListener("click", () => {
prevSlide(); prevSlide();
startSlider(); // Ponovno zaženi časovnik ob ročni interakciji startSlider();
}); });
nextBtn.addEventListener("click", () => { nextBtn.addEventListener("click", () => {
nextSlide(); nextSlide();
startSlider(); // Ponovno zaženi časovnik ob ročni interakciji startSlider();
}); });
}
// --- DOGODKI ZA PAVZO ---
// 1. Pavza ob preletu z miško (za namizne računalnike)
sliderElement.addEventListener('mouseenter', pauseSlider); sliderElement.addEventListener('mouseenter', pauseSlider);
// 2. Nadaljevanje ob umiku miške (za namizne računalnike)
sliderElement.addEventListener('mouseleave', startSlider); sliderElement.addEventListener('mouseleave', startSlider);
// 3. Pavza ob dotiku (za mobilne naprave)
sliderElement.addEventListener('touchstart', pauseSlider, { passive: true }); sliderElement.addEventListener('touchstart', pauseSlider, { passive: true });
// Zagon drsnika ob nalaganju strani
startSlider(); startSlider();
} }
@ -138,7 +137,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
// ================================================================= // =================================================================
// === OBSTOJEČA KODA ZA PODSTRANI (NESPREMENJENO) === // === OSTALA OBSTOJEČA KODA (NESPREMENJENO) ===
// ================================================================= // =================================================================
// Animacije ob pomikanju (Intersection Observer) // Animacije ob pomikanju (Intersection Observer)
@ -166,43 +165,91 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
// Koda za kontaktni obrazec // =================================================================
// === KODA ZA KONTAKTNI OBRAZEC (NADGRAJENO IN SPREMENJENO) ===
// =================================================================
const contactForm = document.getElementById('contact-form'); const contactForm = document.getElementById('contact-form');
if (contactForm) { if (contactForm) {
// Poiščemo element za sporočila. Če ne obstaja, ga ustvarimo in dodamo.
let formMessageElement = contactForm.querySelector('.form-message'); let formMessageElement = contactForm.querySelector('.form-message');
if (!formMessageElement) { if (!formMessageElement) {
formMessageElement = document.createElement('div'); formMessageElement = document.createElement('div');
formMessageElement.className = 'form-message'; formMessageElement.className = 'form-message';
formMessageElement.setAttribute('aria-live', 'polite'); formMessageElement.setAttribute('aria-live', 'polite');
contactForm.appendChild(formMessageElement); // Vstavimo ga pred gumbom za pošiljanje
const submitButton = contactForm.querySelector('button[type="submit"]');
if (submitButton) {
submitButton.parentElement.insertAdjacentElement('beforebegin', formMessageElement);
} else {
contactForm.appendChild(formMessageElement);
}
} }
contactForm.addEventListener('submit', function(e) { contactForm.addEventListener('submit', function(e) {
e.preventDefault(); e.preventDefault(); // Preprečimo privzeto pošiljanje obrazca
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const message = document.getElementById('message').value;
formMessageElement.textContent = '';
formMessageElement.className = 'form-message';
if (name.trim() === '' || email.trim() === '' || message.trim() === '') { const submitButton = contactForm.querySelector('button[type="submit"]');
const originalButtonText = submitButton.textContent;
// Zberemo podatke iz obrazca
const formData = {
name: document.getElementById('name').value,
email: document.getElementById('email').value,
message: document.getElementById('message').value
};
// Osnovna validacija na strani klienta (dopolnitev strežniške)
if (formData.name.trim() === '' || formData.email.trim() === '' || formData.message.trim() === '') {
formMessageElement.textContent = 'Prosimo, izpolnite vsa polja.'; formMessageElement.textContent = 'Prosimo, izpolnite vsa polja.';
formMessageElement.classList.add('error', 'visible'); formMessageElement.className = 'form-message error visible';
return;
}
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(email)) {
formMessageElement.textContent = 'Prosimo, vnesite veljaven e-poštni naslov.';
formMessageElement.classList.add('error', 'visible');
return; return;
} }
formMessageElement.textContent = 'Hvala za vaše sporočilo! Odgovoril/a vam bom v najkrajšem možnem času.'; // Vizualna povratna informacija, da se je pošiljanje začelo
formMessageElement.classList.add('success', 'visible'); submitButton.disabled = true;
contactForm.reset(); submitButton.textContent = 'Pošiljam...';
setTimeout(() => formMessageElement.classList.remove('visible'), 5000); formMessageElement.className = 'form-message'; // Skrijemo prejšnja sporočila
// Pošljemo podatke na PHP skripto z uporabo Fetch API
fetch('/api/send_mail.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
})
.then(response => response.json())
.then(data => {
// Ko prejmemo odgovor od strežnika, ga prikažemo
formMessageElement.textContent = data.message;
if (data.success) {
formMessageElement.classList.add('success', 'visible');
contactForm.reset(); // Počistimo obrazec ob uspehu
// Resetiramo števec znakov
const charCounter = document.getElementById('char-counter');
if(charCounter) {
const maxLength = document.getElementById('message').getAttribute('maxlength');
charCounter.textContent = `${maxLength} znakov na voljo`;
charCounter.className = 'char-counter';
}
} else {
formMessageElement.classList.add('error', 'visible');
}
})
.catch((error) => {
// V primeru mrežne napake ali če strežnik ni dosegljiv
console.error('Napaka pri pošiljanju:', error);
formMessageElement.textContent = 'Prišlo je do napake pri povezavi. Prosimo, poskusite kasneje.';
formMessageElement.className = 'form-message error visible';
})
.finally(() => {
// Ne glede na rezultat, gumbu povrnemo prvotno stanje
submitButton.disabled = false;
submitButton.textContent = originalButtonText;
});
}); });
// Logika za števec znakov (ohranjeno in nespremenjeno)
const messageTextarea = document.getElementById('message'); const messageTextarea = document.getElementById('message');
const charCounter = document.getElementById('char-counter'); const charCounter = document.getElementById('char-counter');
@ -215,21 +262,19 @@ document.addEventListener('DOMContentLoaded', () => {
charCounter.textContent = `${remaining} znakov na voljo`; charCounter.textContent = `${remaining} znakov na voljo`;
charCounter.classList.remove('warning', 'limit');
if (remaining < 0) { if (remaining < 0) {
charCounter.classList.add('limit'); charCounter.classList.add('limit');
charCounter.classList.remove('warning');
} else if (remaining <= 20) { } else if (remaining <= 20) {
charCounter.classList.add('warning'); charCounter.classList.add('warning');
charCounter.classList.remove('limit');
} else {
charCounter.classList.remove('warning');
charCounter.classList.remove('limit');
} }
}); });
} }
} }
// Gumb za na vrh // =================================================================
// === GUMB ZA NA VRH (NESPREMENJENO) ===
// =================================================================
const backToTopButton = document.getElementById('back-to-top-btn'); const backToTopButton = document.getElementById('back-to-top-btn');
if (backToTopButton) { if (backToTopButton) {
window.addEventListener('scroll', () => { window.addEventListener('scroll', () => {
@ -251,18 +296,8 @@ document.addEventListener('DOMContentLoaded', () => {
}; };
// --- Glavni Zagon Aplikacije --- // --- Glavni Zagon Aplikacije ---
if (document.getElementById('header-placeholder') && document.getElementById('footer-placeholder')) { // Poenostavljen zagon, saj nalaganje komponent ni potrebno.
Promise.all([ setCurrentYear();
loadComponent('/layouts/header.html', 'header-placeholder'), setActiveNavLink();
loadComponent('/layouts/footer.html', 'footer-placeholder') initializeApp();
]).then(() => {
setCurrentYear();
setActiveNavLink();
initializeApp();
});
} else {
setCurrentYear();
setActiveNavLink();
initializeApp();
}
}); });

File diff suppressed because it is too large Load Diff

View File

@ -13,15 +13,15 @@
<!-- CSS Stylesheet --> <!-- CSS Stylesheet -->
<link rel="stylesheet" href="/assets/css/main.css"> <link rel="stylesheet" href="/assets/css/main.css">
<!-- === SPREMEMBA: Posodobljeni stili za portret na kontaktni strani === --> <!-- Stili za portret na kontaktni strani (ostanejo nespremenjeni) -->
<style> <style>
.contact-portrait { .contact-portrait {
display: block; display: block;
width: 100%; /* Slika naj zavzame celotno širino vsebnika */ width: 100%;
max-width: 250px; /* Omejitev maksimalne širine */ max-width: 250px;
height: auto; /* Višina se prilagodi samodejno, da ohrani razmerje */ height: auto;
margin: 0 auto var(--spacing-l) auto; /* Centriranje in odmik spodaj */ margin: 0 auto var(--spacing-l) auto;
border-radius: 10px; /* Rahlo zaobljeni robovi */ border-radius: 10px;
box-shadow: var(--shadow-medium); box-shadow: var(--shadow-medium);
} }
</style> </style>
@ -29,30 +29,25 @@
<body class="subpage"> <body class="subpage">
<header class="main-header"> <header class="main-header">
<div class="header-container"> <div class="header-container">
<div class="logo"> <div class="logo">
<a href="/">Psihoterapevt, vrhunski strokovnjak</a> <a href="/">Psihoterapevt, vrhunski strokovnjak</a>
</div> </div>
<nav class="main-nav" id="main-navigation"> <nav class="main-nav" id="main-navigation">
<ul> <ul>
<li><a href="/o-meni/">O meni</a></li> <li><a href="/o-meni/">O meni</a></li>
<li><a href="/kaj-je-psihoterapija/">Kaj je psihoterapija</a></li> <li><a href="/kaj-je-psihoterapija/">Kaj je psihoterapija</a></li>
<li><a href="/storitve/">Predstavitev ponudbe</a></li> <li><a href="/storitve/">Predstavitev ponudbe</a></li>
<li><a href="/cenik/">Cenik storitev</a></li> <li><a href="/cenik/">Cenik storitev</a></li>
<li><a href="/blog/">Blog</a></li> <li><a href="/blog/">Blog</a></li>
<li><a href="/kontakt/" class="nav-cta">Kontakt in naročanje</a></li> <li><a href="/kontakt/" class="nav-cta">Kontakt in naročanje</a></li>
</ul> </ul>
</nav> </nav>
<button <button class="mobile-nav-toggle" aria-controls="main-navigation" aria-expanded="false" aria-label="Toggle navigation">
class="mobile-nav-toggle" <span class="hamburger"></span>
aria-controls="main-navigation" </button>
aria-expanded="false" </div>
aria-label="Toggle navigation" </header>
>
<span class="hamburger"></span>
</button>
</div>
</header>
<main class="page-main-content"> <main class="page-main-content">
<section class="page-hero"> <section class="page-hero">
@ -82,7 +77,7 @@
<div class="contact-info-item"> <div class="contact-info-item">
<span class="info-label">Elektronski naslov:</span> <span class="info-label">Elektronski naslov:</span>
<p class="info-data"><a href="mailto:herminamerc8888@gmail.com">herminamerc8888@gmail.com</a></p> <p class="info-data"><a href="mailto:info@herminamerc.si">info@herminamerc.si</a></p>
</div> </div>
<div class="map-placeholder"> <div class="map-placeholder">
@ -93,7 +88,7 @@
<!-- Desna stran: Kontaktni obrazec --> <!-- Desna stran: Kontaktni obrazec -->
<div class="contact-form-wrapper animate-on-scroll"> <div class="contact-form-wrapper animate-on-scroll">
<h2 class="section-title">Pošljite sporočilo</h2> <h2 class="section-title">Pošljite sporočilo</h2>
<form id="contact-form" class="contact-form" action="#" method="POST"> <form id="contact-form" class="contact-form" novalidate>
<div class="form-group"> <div class="form-group">
<label for="name">Ime in priimek</label> <label for="name">Ime in priimek</label>
<input type="text" id="name" name="name" required> <input type="text" id="name" name="name" required>
@ -104,14 +99,16 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="message">Kratko sporočilo</label> <label for="message">Kratko sporočilo</label>
<textarea id="message" name="message" rows="8" required maxlength="200"></textarea> <textarea id="message" name="message" rows="8" required maxlength="500"></textarea>
<!-- === DODAN ELEMENT ZA ŠTEVEC ZNAKOV === --> <div id="char-counter" class="char-counter">500 znakov na voljo</div>
<div id="char-counter" class="char-counter">200 znakov na voljo</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="cta-button">Pošlji sporočilo</button> <!-- POPRAVEK: Gumbu je dodan razred "cta" za pravilen stil -->
<button type="submit" class="cta cta-button">Pošlji sporočilo</button>
</div> </div>
</form> </form>
<!-- Element za prikaz sporočil o uspehu/napaki, ki ga bo upravljal JavaScript -->
<div class="form-message" aria-live="polite"></div>
<p style="text-align: center; margin-top: 1rem; font-style: italic; font-size: 0.9rem;">Pri naročanju je potrebno termin predhodno dogovoriti po mailu.</p> <p style="text-align: center; margin-top: 1rem; font-style: italic; font-size: 0.9rem;">Pri naročanju je potrebno termin predhodno dogovoriti po mailu.</p>
</div> </div>
@ -120,19 +117,19 @@
</main> </main>
<footer class="main-footer-bar"> <footer class="main-footer-bar">
<p> <p>
© <span id="current-year"></span> Mag. Hermina Merc. Vse pravice © <span id="current-year"></span> Mag. Hermina Merc. Vse pravice
pridržane. pridržane.
</p> </p>
<p style="font-size: 0.8rem; margin-top: 0.5rem; margin-bottom: 0.25rem;"> <p style="font-size: 0.8rem; margin-top: 0.5rem; margin-bottom: 0.25rem;">
Copy right za vse fotografije in ves tekst na tej internetni strani: Copy right za vse fotografije in ves tekst na tej internetni strani:
Hermina Merc Hermina Merc
</p> </p>
<p style="font-size: 0.8rem; margin-bottom: 0;"> <p style="font-size: 0.8rem; margin-bottom: 0;">
Nobena fotografija in noben del teksta ni dovoljeno uporabljati brez Nobena fotografija in noben del teksta ni dovoljeno uporabljati brez
pisnega dovoljenja avtorice te spletne strani. pisnega dovoljenja avtorice te spletne strani.
</p> </p>
</footer> </footer>
<!-- JavaScript --> <!-- JavaScript -->
<script src="/assets/js/main.js"></script> <script src="/assets/js/main.js"></script>