diff --git a/.serena/project.yml b/.serena/project.yml index 83717c5..4eb1cd9 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -115,3 +115,10 @@ initial_prompt: "" # override of the corresponding setting in serena_config.yml, see the documentation there. # If null or missing, the value from the global config is used. symbol_info_budget: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ad33427 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,16 @@ +# Workflow + +## KONIEC PRACY + +Gdy użytkownik napisze `KONIEC PRACY`, wykonaj kolejno: + +1. Przeprowadzenie testów. +2. Aktualizacja dokumentacji technicznej, jeśli zmiany tego wymagają: + - `docs/PROJECT_STRUCTURE.md` + - `docs/FORM_EDIT_SYSTEM.md` +3. Migracje SQL (jeśli były zmiany w bazie danych): + - Plik: `migrations/{version}.sql` (np. `migrations/0.304.sql`) + - **NIE** w `updates/` — build script sam wczyta z `migrations/` + - Sprawdź czy plik istnieje i jest poprawnie nazwany przed commitem +4. Commit. +5. Push. \ No newline at end of file diff --git a/admin/ajax.php b/admin/ajax.php index 61fff4e..a115fab 100644 --- a/admin/ajax.php +++ b/admin/ajax.php @@ -4,14 +4,20 @@ function __autoload_my_classes( $classname ) { $q = explode( '\\' , $classname ); $c = array_pop( $q ); - $f = '../autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php'; + if ( $c == 'Savant3' ) { require_once( '../autoload/Savant3.php' ); return true; } - if ( file_exists( $f ) ) - require_once( $f ); + + // 1. Legacy: class.ClassName.php + $f = '../autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php'; + if ( file_exists( $f ) ) { require_once( $f ); return; } + + // 2. PSR-4: ClassName.php + $f = '../autoload/' . implode( '/' , $q ) . '/' . $c . '.php'; + if ( file_exists( $f ) ) require_once( $f ); } spl_autoload_register( '__autoload_my_classes' ); diff --git a/admin/index.php b/admin/index.php index a29ddc4..4568517 100644 --- a/admin/index.php +++ b/admin/index.php @@ -16,9 +16,14 @@ function __autoload_my_classes( $classname ) { $q = explode( '\\' , $classname ); $c = array_pop( $q ); + + // 1. Legacy: class.ClassName.php $f = '../autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php'; - if ( file_exists( $f ) ) - require_once( $f ); + if ( file_exists( $f ) ) { require_once( $f ); return; } + + // 2. PSR-4: ClassName.php + $f = '../autoload/' . implode( '/' , $q ) . '/' . $c . '.php'; + if ( file_exists( $f ) ) require_once( $f ); } spl_autoload_register( '__autoload_my_classes' ); diff --git a/ajax.php b/ajax.php index 78caebb..b37fbd8 100644 --- a/ajax.php +++ b/ajax.php @@ -4,10 +4,14 @@ function __autoload_my_classes( $classname ) { $q = explode( '\\' , $classname ); $c = array_pop( $q ); - $f = 'autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php'; - if ( file_exists( $f ) ) - require_once( $f ); + // 1. Legacy: class.ClassName.php + $f = 'autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php'; + if ( file_exists( $f ) ) { require_once( $f ); return; } + + // 2. PSR-4: ClassName.php + $f = 'autoload/' . implode( '/' , $q ) . '/' . $c . '.php'; + if ( file_exists( $f ) ) require_once( $f ); } spl_autoload_register( '__autoload_my_classes' ); date_default_timezone_set( 'Europe/Warsaw' ); diff --git a/api.php b/api.php index 5fdb974..722fe2b 100644 --- a/api.php +++ b/api.php @@ -4,10 +4,14 @@ function __autoload_my_classes($classname) { $q = explode('\\', $classname); $c = array_pop($q); - $f = 'autoload/' . implode('/', $q) . '/class.' . $c . '.php'; - if (file_exists($f)) - require_once($f); + // 1. Legacy: class.ClassName.php + $f = 'autoload/' . implode('/', $q) . '/class.' . $c . '.php'; + if (file_exists($f)) { require_once($f); return; } + + // 2. PSR-4: ClassName.php + $f = 'autoload/' . implode('/', $q) . '/' . $c . '.php'; + if (file_exists($f)) require_once($f); } spl_autoload_register('__autoload_my_classes'); date_default_timezone_set('Europe/Warsaw'); diff --git a/autoload/Domain/Languages/LanguagesRepository.php b/autoload/Domain/Languages/LanguagesRepository.php new file mode 100644 index 0000000..9f6fe89 --- /dev/null +++ b/autoload/Domain/Languages/LanguagesRepository.php @@ -0,0 +1,213 @@ +db = $db; + } + + // ------------------------------------------------------------------------- + // Odczyt + // ------------------------------------------------------------------------- + + public function languagesList(): array + { + return $this->db->select( 'pp_langs', '*', [ 'ORDER' => [ 'o' => 'ASC' ] ] ) ?: []; + } + + public function languageDetails( string $languageId ): ?array + { + return $this->db->get( 'pp_langs', '*', [ 'id' => $languageId ] ) ?: null; + } + + public function availableDomains(): array + { + return $this->db->query( + 'SELECT domain FROM pp_langs WHERE status = 1 AND domain IS NOT NULL GROUP BY domain' + )->fetchAll( \PDO::FETCH_ASSOC ) ?: []; + } + + public function defaultDomain(): ?string + { + $results = $this->db->query( + 'SELECT domain FROM pp_langs WHERE status = 1 AND domain IS NOT NULL AND main_domain = 1' + )->fetchAll(); + return $results[0][0] ?? null; + } + + public function defaultLanguage( string $domain = '' ): ?string + { + if ( !$default = \Shared\Cache\CacheHandler::fetch( "default_language:$domain" ) ) + { + if ( $domain ) + $results = $this->db->query( + 'SELECT id FROM pp_langs WHERE status = 1 AND domain = \'' . $domain . '\' ORDER BY start DESC, o ASC LIMIT 1' + )->fetchAll(); + + if ( !$domain || !$this->defaultDomain() ) + $results = $this->db->query( + 'SELECT id FROM pp_langs WHERE status = 1 AND domain IS NULL ORDER BY start DESC, o ASC LIMIT 1' + )->fetchAll(); + + $default = $results[0][0] ?? null; + \Shared\Cache\CacheHandler::store( "default_language:$domain", $default ); + } + return $default; + } + + public function activeLanguages(): array + { + if ( !$active = \Shared\Cache\CacheHandler::fetch( 'active_languages' ) ) + { + $active = $this->db->select( 'pp_langs', [ 'id', 'name', 'domain' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] ) ?: []; + \Shared\Cache\CacheHandler::store( 'active_languages', $active ); + } + return $active; + } + + public function langTranslations( string $language = 'pl' ): array + { + if ( !$translations = \Shared\Cache\CacheHandler::fetch( "lang_translations:$language" ) ) + { + $translations = [ '0' => $language ]; + + $results = $this->db->select( 'pp_langs_translations', [ 'text', $language ] ); + if ( is_array( $results ) ) + foreach ( $results as $row ) + $translations[ $row['text'] ] = $row[ $language ]; + + \Shared\Cache\CacheHandler::store( "lang_translations:$language", $translations ); + } + return $translations; + } + + public function translationDetails( int $translationId ): ?array + { + return $this->db->get( 'pp_langs_translations', '*', [ 'id' => $translationId ] ) ?: null; + } + + public function maxOrder(): int + { + return (int) $this->db->max( 'pp_langs', 'o' ); + } + + // ------------------------------------------------------------------------- + // Zapis / usuwanie + // ------------------------------------------------------------------------- + + public function languageSave( string $languageId, string $name, $status, $start, $o, $domain, $main_domain ): string + { + if ( $start == 'on' && $status == 'on' && !\S::get_domain( $domain ) ) + $this->db->update( 'pp_langs', [ 'start' => 0 ], [ 'id[!]' => $languageId ] ); + + if ( $start == 'on' && $status == 'on' && \S::get_domain( $domain ) ) + $this->db->update( 'pp_langs', [ 'start' => 0 ], [ + 'AND' => [ 'id[!]' => $languageId, 'domain' => \S::get_domain( $domain ) ] + ] ); + + if ( $main_domain == 'on' && $domain && $status == 'on' ) + $this->db->update( 'pp_langs', [ 'main_domain' => 0 ], [ ' id[!]' => $languageId ] ); + + if ( $this->db->count( 'pp_langs', [ 'id' => $languageId ] ) ) + { + $this->db->update( 'pp_langs', [ + 'status' => $status == 'on' ? 1 : 0, + 'start' => $start == 'on' ? 1 : 0, + 'name' => $name, + 'o' => $o, + 'domain' => \S::get_domain( $domain ) ?: null, + 'main_domain' => $main_domain == 'on' && \S::get_domain( $domain ) ? 1 : 0, + ], [ 'id' => $languageId ] ); + } + else + { + if ( $this->db->query( 'ALTER TABLE pp_langs_translations ADD ' . strtolower( $languageId ) . ' TEXT NULL DEFAULT NULL' ) ) + { + $this->db->insert( 'pp_langs', [ + 'id' => strtolower( $languageId ), + 'name' => $name, + 'status' => $status == 'on' ? 1 : 0, + 'start' => $start == 'on' ? 1 : 0, + 'o' => $o, + 'domain' => \S::get_domain( $domain ) ?: null, + 'main_domain' => $main_domain == 'on' && \S::get_domain( $domain ) ? 1 : 0, + ] ); + } + } + + // Upewnij się, że każda domena ma język startowy + if ( !$this->db->count( 'pp_langs', [ 'AND' => [ 'status' => 1, 'domain[!]' => null ] ] ) ) + { + if ( !$this->db->count( 'pp_langs', [ 'AND' => [ 'status' => 1, 'start' => 1, 'domain' => null ] ] ) ) + { + if ( $idTmp = $this->db->get( 'pp_langs', 'id', [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] ) ) + $this->db->update( 'pp_langs', [ 'start' => 1 ], [ 'id' => $idTmp ] ); + } + } + + $domains = $this->db->select( 'pp_langs', 'domain', [ 'domain[!]' => null, 'GROUP' => 'domain' ] ); + if ( is_array( $domains ) && !empty( $domains ) ) + { + $this->db->update( 'pp_langs', [ 'start' => 0 ], [ 'domain' => null ] ); + foreach ( $domains as $dom ) + { + if ( !$this->db->count( 'pp_langs', [ 'AND' => [ 'status' => 1, 'start' => 1, 'domain' => $dom ] ] ) ) + { + if ( $idTmp = $this->db->get( 'pp_langs', 'id', [ 'AND' => [ 'status' => 1, 'domain' => $dom ], 'ORDER' => [ 'o' => 'ASC' ] ] ) ) + $this->db->update( 'pp_langs', [ 'start' => 1 ], [ 'id' => $idTmp ] ); + } + } + } + + if ( !$this->db->count( 'pp_langs', [ 'AND' => [ 'status' => 1, 'main_domain' => 1 ] ] ) ) + { + if ( $idTmp = $this->db->get( 'pp_langs', 'id', [ 'AND' => [ 'status' => 1, 'domain[!]' => null ], 'ORDER' => [ 'o' => 'ASC' ] ] ) ) + $this->db->update( 'pp_langs', [ 'main_domain' => 1 ], [ 'id' => $idTmp ] ); + } + + \S::htacces(); + \S::delete_cache(); + return $languageId; + } + + public function languageDelete( string $languageId ): bool + { + if ( $this->db->count( 'pp_langs' ) > 1 ) + { + if ( $this->db->query( 'ALTER TABLE pp_langs_translations DROP ' . $languageId ) + && $this->db->delete( 'pp_langs', [ 'id' => $languageId ] ) ) + return true; + } + return false; + } + + public function translationSave( $translationId, string $text, array $languages = [] ): int + { + if ( $translationId ) + { + $this->db->update( 'pp_langs_translations', [ 'text' => $text ], [ 'id' => $translationId ] ); + foreach ( $languages as $key => $val ) + $this->db->update( 'pp_langs_translations', [ $key => $val ], [ 'id' => $translationId ] ); + } + else + { + $this->db->insert( 'pp_langs_translations', [ 'text' => $text ] ); + $translationId = $this->db->id(); + foreach ( $languages as $key => $val ) + $this->db->update( 'pp_langs_translations', [ $key => $val ], [ 'id' => $translationId ] ); + } + + \S::htacces(); + \S::delete_cache(); + return (int) $translationId; + } + + public function translationDelete( int $translationId ): bool + { + return (bool) $this->db->delete( 'pp_langs_translations', [ 'id' => $translationId ] ); + } +} diff --git a/autoload/Domain/Settings/SettingsRepository.php b/autoload/Domain/Settings/SettingsRepository.php new file mode 100644 index 0000000..6105c2d --- /dev/null +++ b/autoload/Domain/Settings/SettingsRepository.php @@ -0,0 +1,72 @@ +db = $db; + } + + /** + * Zwraca wszystkie ustawienia jako tablicę asocjacyjną param => value. + * Wynik jest cache'owany (TTL 24h). + */ + public function allSettings(): array + { + if ( !$settings = \Shared\Cache\CacheHandler::fetch( 'settings_details' ) ) + { + $results = $this->db->select( 'pp_settings', '*' ); + if ( is_array( $results ) ) + foreach ( $results as $row ) + $settings[ $row['param'] ] = $row['value']; + + \Shared\Cache\CacheHandler::store( 'settings_details', $settings ?? [] ); + } + + return $settings ?? []; + } + + /** + * Upsert jednego parametru. + */ + public function update( string $param, $value ): bool + { + if ( $this->db->count( 'pp_settings', [ 'param' => $param ] ) ) + return (bool) $this->db->update( 'pp_settings', [ 'value' => $value ], [ 'param' => $param ] ); + else + return (bool) $this->db->insert( 'pp_settings', [ 'param' => $param, 'value' => $value ] ); + } + + /** + * Zapisuje zbiorczo ustawienia (TRUNCATE + INSERT). + * Czyści cache i regeneruje .htaccess. + * + * @param array $data Tablica asocjacyjna [ 'param' => value, ... ] + */ + public function save( array $data ): bool + { + $this->db->query( 'TRUNCATE pp_settings' ); + + $rows = []; + foreach ( $data as $param => $value ) + $rows[] = [ 'param' => $param, 'value' => $value ]; + + $this->db->insert( 'pp_settings', $rows ); + + \S::delete_cache(); + \S::htacces(); + + return true; + } + + /** + * Zwraca bieżącą wartość licznika odwiedzin. + */ + public function visitCounter(): ?string + { + return $this->db->get( 'pp_settings', 'value', [ 'param' => 'visits' ] ) ?: null; + } +} diff --git a/autoload/Domain/User/UserRepository.php b/autoload/Domain/User/UserRepository.php new file mode 100644 index 0000000..46d7556 --- /dev/null +++ b/autoload/Domain/User/UserRepository.php @@ -0,0 +1,235 @@ +db = $db; + } + + // ------------------------------------------------------------------------- + // Odczyt + // ------------------------------------------------------------------------- + + public function find( int $userId ): ?array + { + return $this->db->get( 'pp_users', '*', [ 'id' => $userId ] ) ?: null; + } + + public function findByLogin( string $login ): ?array + { + return $this->db->get( 'pp_users', '*', [ 'login' => $login ] ) ?: null; + } + + public function all(): array + { + return $this->db->select( 'pp_users', '*' ) ?: []; + } + + public function privileges( int $userId ): array + { + return $this->db->select( 'pp_users_privileges', '*', [ 'id_user' => $userId ] ) ?: []; + } + + public function hasPrivilege( string $name, int $userId ): bool + { + if ( $userId === 1 ) + return true; + + if ( !$result = \Shared\Cache\CacheHandler::fetch( "check_privileges:$userId:$name-tmp" ) ) + { + $result = $this->db->count( 'pp_users_privileges', [ 'AND' => [ 'name' => $name, 'id_user' => $userId ] ] ); + \Shared\Cache\CacheHandler::store( "check_privileges:$userId:$name", $result ); + } + return (bool) $result; + } + + // ------------------------------------------------------------------------- + // Logowanie + // ------------------------------------------------------------------------- + + /** + * Weryfikuje login i hasło. + * @return int 1 = OK, 0 = złe dane, -1 = konto zablokowane + */ + public function logon( string $login, string $password ): int + { + if ( !$this->db->get( 'pp_users', '*', [ 'login' => $login ] ) ) + return 0; + + if ( !$this->db->get( 'pp_users', '*', [ 'AND' => [ 'login' => $login, 'status' => 1, 'error_logged_count[<]' => 5 ] ] ) ) + return -1; + + if ( $this->db->get( 'pp_users', '*', [ + 'AND' => [ + 'login' => $login, + 'status' => 1, + 'password' => md5( $password ), + 'OR' => [ 'active_to[>=]' => date( 'Y-m-d' ), 'active_to' => null ] + ] + ] ) ) { + $this->db->update( 'pp_users', [ 'last_logged' => date( 'Y-m-d H:i:s' ), 'error_logged_count' => 0 ], [ 'login' => $login ] ); + return 1; + } + + $this->db->update( 'pp_users', [ 'last_error_logged' => date( 'Y-m-d H:i:s' ), 'error_logged_count[+]' => 1 ], [ 'login' => $login ] ); + if ( $this->db->get( 'pp_users', 'error_logged_count', [ 'login' => $login ] ) >= 5 ) + { + $this->db->update( 'pp_users', [ 'status' => 0 ], [ 'login' => $login ] ); + return -1; + } + return 0; + } + + public function isLoginTaken( string $login, int $excludeId = 0 ): bool + { + return (bool) $this->db->get( 'pp_users', 'login', [ 'AND' => [ 'login' => $login, 'id[!]' => $excludeId ] ] ); + } + + // ------------------------------------------------------------------------- + // 2FA + // ------------------------------------------------------------------------- + + public function update( int $userId, array $data ): bool + { + return (bool) $this->db->update( 'pp_users', $data, [ 'id' => $userId ] ); + } + + public function sendTwofaCode( int $userId, bool $resend = false ): bool + { + $user = $this->find( $userId ); + if ( !$user ) return false; + + if ( (int)$user['twofa_enabled'] !== 1 ) return false; + + $to = $user['twofa_email'] ?: $user['login']; + if ( !filter_var( $to, FILTER_VALIDATE_EMAIL ) ) return false; + + if ( $resend && !empty( $user['twofa_sent_at'] ) ) + { + $last = strtotime( $user['twofa_sent_at'] ); + if ( $last && ( time() - $last ) < 30 ) return false; + } + + $code = random_int( 100000, 999999 ); + $hash = password_hash( (string)$code, PASSWORD_DEFAULT ); + + $this->update( $userId, [ + 'twofa_code_hash' => $hash, + 'twofa_expires_at' => date( 'Y-m-d H:i:s', time() + 10 * 60 ), + 'twofa_sent_at' => date( 'Y-m-d H:i:s' ), + 'twofa_failed_attempts' => 0, + ] ); + + $subject = 'Twój kod logowania 2FA'; + $body = "Twój kod logowania do panelu administratora: {$code}. Kod jest ważny przez 10 minut. Jeśli to nie Ty inicjowałeś logowanie – zignoruj tę wiadomość i poinformuj administratora."; + + $sent = \S::send_email( $to, $subject, $body ); + if ( !$sent ) + { + $headers = "MIME-Version: 1.0\r\n"; + $headers .= "Content-type: text/plain; charset=UTF-8\r\n"; + $headers .= "From: no-reply@" . ( $_SERVER['HTTP_HOST'] ?? 'localhost' ) . "\r\n"; + $sent = mail( $to, mb_encode_mimeheader( $subject, 'UTF-8' ), $body, $headers ); + } + return (bool) $sent; + } + + public function verifyTwofaCode( int $userId, string $code ): bool + { + $user = $this->find( $userId ); + if ( !$user ) return false; + + if ( (int)$user['twofa_failed_attempts'] >= 5 ) return false; + + if ( empty( $user['twofa_expires_at'] ) || time() > strtotime( $user['twofa_expires_at'] ) ) + { + $this->update( $userId, [ 'twofa_code_hash' => null, 'twofa_expires_at' => null ] ); + return false; + } + + $ok = !empty( $user['twofa_code_hash'] ) && password_verify( $code, $user['twofa_code_hash'] ); + if ( $ok ) + { + $this->update( $userId, [ + 'twofa_code_hash' => null, + 'twofa_expires_at' => null, + 'twofa_sent_at' => null, + 'twofa_failed_attempts' => 0, + 'last_logged' => date( 'Y-m-d H:i:s' ), + ] ); + return true; + } + + $this->update( $userId, [ + 'twofa_failed_attempts' => (int)$user['twofa_failed_attempts'] + 1, + 'last_error_logged' => date( 'Y-m-d H:i:s' ), + ] ); + return false; + } + + // ------------------------------------------------------------------------- + // Zapis / usuwanie + // ------------------------------------------------------------------------- + + public function save( + $userId, string $login, $status, $activeTo, string $password, string $passwordRe, + $admin, $privileges, $twofaEnabled = 0, string $twofaEmail = '' + ): array { + $this->db->delete( 'pp_users_privileges', [ 'id_user' => (int)$userId ] ); + + if ( !$userId ) + { + if ( strlen( $password ) < 5 ) + return [ 'status' => 'error', 'msg' => 'Podane hasło jest zbyt krótkie.' ]; + if ( $password !== $passwordRe ) + return [ 'status' => 'error', 'msg' => 'Podane hasła są różne.' ]; + + $this->db->insert( 'pp_users', [ + 'login' => $login, + 'status' => $status == 'on' ? 1 : 0, + 'active_to' => $activeTo === '' ? null : $activeTo, + 'admin' => $admin, + 'password' => md5( $password ), + 'twofa_enabled' => $twofaEnabled == 'on' ? 1 : 0, + 'twofa_email' => $twofaEmail, + ] ); + $userId = $this->db->get( 'pp_users', 'id', [ 'ORDER' => [ 'id' => 'DESC' ] ] ); + } + else + { + if ( $password && strlen( $password ) < 5 ) + return [ 'status' => 'error', 'msg' => 'Podane hasło jest zbyt krótkie.' ]; + if ( $password && $password !== $passwordRe ) + return [ 'status' => 'error', 'msg' => 'Podane hasła są różne.' ]; + + if ( $password ) + $this->db->update( 'pp_users', [ 'password' => md5( $password ) ], [ 'id' => (int)$userId ] ); + + $this->db->update( 'pp_users', [ + 'login' => $login, + 'admin' => $admin, + 'status' => $status == 'on' ? 1 : 0, + 'active_to' => $activeTo === '' ? null : $activeTo, + 'error_logged_count' => 0, + 'twofa_enabled' => $twofaEnabled == 'on' ? 1 : 0, + 'twofa_email' => $twofaEmail, + ], [ 'id' => (int)$userId ] ); + } + + $privileges = (array)$privileges; + foreach ( $privileges as $pri ) + $this->db->insert( 'pp_users_privileges', [ 'name' => $pri, 'id_user' => $userId ] ); + + \S::delete_cache(); + return [ 'status' => 'ok', 'msg' => 'Użytkownik został zapisany.' ]; + } + + public function delete( int $userId ): bool + { + return (bool) $this->db->delete( 'pp_users', [ 'id' => $userId ] ); + } +} diff --git a/autoload/Shared/Cache/CacheHandler.php b/autoload/Shared/Cache/CacheHandler.php new file mode 100644 index 0000000..d58ab33 --- /dev/null +++ b/autoload/Shared/Cache/CacheHandler.php @@ -0,0 +1,47 @@ + $data[0] ) + { + if ( file_exists( $filename ) ) + unlink( $filename ); + return false; + } + + return $data[1]; + } +} diff --git a/autoload/Shared/Helpers/Helpers.php b/autoload/Shared/Helpers/Helpers.php new file mode 100644 index 0000000..e4a4399 --- /dev/null +++ b/autoload/Shared/Helpers/Helpers.php @@ -0,0 +1,1281 @@ +readImage($file); + + if ($file_type === 'png') + { + $image->setImageFormat('webp'); + $image->setImageCompressionQuality($compression_quality); + $image->setOption('webp:lossless', 'true'); + } + + $image->writeImage($output_file); + return $output_file; + } + + return false; + } + + static public function is_array_fix($value) + { + if (is_array($value) and count($value)) + return true; + return false; + } + + public static function suAppendHtmlById(&$s, $sId, $sHtml, &$oDoc = null) + { + return self::suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, false); + } + + public static function suInsertHtmlById(&$s, $sId, $sHtml, &$oDoc = null) + { + return self::suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, true); + } + + public static function suAddHtmlBeforeById(&$s, $sId, $sHtml, &$oDoc = null) + { + return self::suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, true, true); + } + + public static function suAddHtmlAfterById(&$s, $sId, $sHtml, &$oDoc = null) + { + return self::suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, false, true); + } + + public static function suSetHtmlById(&$s, $sId, $sHtml, &$oDoc = null) + { + return self::suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, true); + } + + public static function suReplaceHtmlElementById(&$s, $sId, $sHtml, &$oDoc = null) + { + return self::suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, false); + } + + public static function suRemoveHtmlElementById(&$s, $sId, &$oDoc = null) + { + return self::suSetHtmlElementById($oDoc, $s, $sId, null, false, false); + } + + public static function suSetHtmlElementById(&$oDoc, &$s, $sId, $sHtml, $bAppend = false, $bInsert = false, $bAddToOuter = false) + { + if (self::suIsValidString($s) && self::suIsValidString($sId)) + { + $bCreate = true; + if (is_object($oDoc)) + { + if (!($oDoc instanceof \DOMDocument)) + { + return false; + } + $bCreate = false; + } + + if ($bCreate) + { + $oDoc = new \DOMDocument(); + } + + libxml_use_internal_errors(true); + $oDoc->loadHTML($s, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); + libxml_use_internal_errors(false); + $oNode = $oDoc->getElementById($sId); + + if (is_object($oNode)) + { + $bReplaceOuter = (!$bAppend && !$bInsert); + + $sId = uniqid('SHEBI-'); + $aId = array("", ""); + + if ($bReplaceOuter) + { + if (self::suIsValidString($sHtml)) + { + $oNode->parentNode->replaceChild($oDoc->createComment($sId), $oNode); + $s = $oDoc->saveHtml(); + $s = str_replace($aId, $sHtml, $oDoc->saveHtml()); + } + else + { + $oNode->parentNode->removeChild($oNode); + $s = $oDoc->saveHtml(); + } + return true; + } + + $bReplaceInner = ($bAppend && $bInsert); + $sThis = null; + + if (!$bReplaceInner) + { + $sThis = $oDoc->saveHTML($oNode); + $sThis = ($bInsert ? $sHtml : '') . ($bAddToOuter ? $sThis : (substr($sThis, strpos($sThis, '>') + 1, - (strlen($oNode->nodeName) + 3)))) . ($bAppend ? $sHtml : ''); + } + + if (!$bReplaceInner && $bAddToOuter) + { + $oNode->parentNode->replaceChild($oDoc->createComment($sId), $oNode); + $sId = &$aId; + } + else + { + $oNode->nodeValue = $sId; + } + + $s = str_replace($sId, $bReplaceInner ? $sHtml : $sThis, $oDoc->saveHtml()); + return true; + } + } + return false; + } + + public static function suIsValidString(&$s, &$iLen = null, $minLen = null, $maxLen = null) + { + if (!is_string($s) || !isset($s[0])) + { + return false; + } + + if ($iLen !== null) + { + $iLen = strlen($s); + } + + return (($minLen === null ? true : ($minLen > 0 && isset($s[$minLen - 1]))) && + $maxLen === null ? true : ($maxLen >= $minLen && !isset($s[$maxLen]))); + } + + public static function log_db_error($db_tmp, $debug) + { + global $settings; + + if ($settings['mysql_debug']) + { + $out .= date('Y-m-d H:i:s') . PHP_EOL; + $out .= $db_tmp->error()[2] . PHP_EOL; + $out .= $db_tmp->last() . PHP_EOL; + $out .= $debug[0]['file'] . ' | ' . $debug[0]['line'] . PHP_EOL; + $out .= '--------------------------------------------------------------------------------------------' . PHP_EOL; + + if (!is_dir('logs')) + mkdir('logs', 0755, true); + + $content = file_get_contents('logs/' . date('Y-m-d') . '.txt'); + $content = $out . $content; + file_put_contents('logs/' . date('Y-m-d') . '.txt', $content); + die('Blad bazy danych.'); + } + } + + public static function lang($text) + { + global $lang; + return $lang[$text] ? $lang[$text] : 'LANG-' . $text; + } + + public static function cache_read($path) + { + $f = fopen($path, 'r'); + $buffer = ''; + while (!feof($f)) + { + $buffer .= fread($f, 2048); + } + fclose($f); + return $buffer; + } + + public static function cache_write($cache_url, $cache_file, $html) + { + $dir = md5($cache_url); + $dir = 'cache/' . $dir[0] . '/' . $dir[1] . '/'; + + if (!is_dir($dir)) + mkdir($dir, 0755, true); + + $f = fopen($cache_file, 'w'); + fwrite($f, $html, strlen($html)); + fclose($f); + return true; + } + + public static function cache_file_url($cache_url) + { + $cache = md5($cache_url); + return 'cache/' . $cache[1] . '/' . $cache[2] . '/' . $cache; + } + + public static function date_diff($data1, $data2, $rodz = '60') + { + $data1 = date('Y-m-d H:i:s', strtotime($data1)); + $data2 = date('Y-m-d H:i:s', strtotime($data2)); + + $d1_t = explode(' ', $data1); + $d1_tt = explode('-', $d1_t[0]); + $rok1 = $d1_tt[0]; + $mc1 = $d1_tt[1]; + $d1 = $d1_tt[2]; + $d1_tt = explode(':', $d1_t[1]); + $g1 = $d1_tt[0]; + $m1 = $d1_tt[1]; + $s1 = $d1_tt[2]; + + $d2_t = explode(' ', $data2); + $d2_tt = explode('-', $d2_t[0]); + $rok2 = $d2_tt[0]; + $mc2 = $d2_tt[1]; + $d2 = $d2_tt[2]; + $d2_tt = explode(':', $d2_t[1]); + $g2 = $d2_tt[0]; + $m2 = $d2_tt[1]; + $s2 = $d2_tt[2]; + + $lt = mktime($g2, $m2, $s2, $mc2, $d2, $rok2); + $st = mktime($g1, $m1, $s1, $mc1, $d1, $rok1); + + return round(($lt - $st) / $rodz); + } + + public static function is_token_valid($token) + { + if (!empty($_SESSION['tokens'][$token])) + { + unset($_SESSION['tokens'][$token]); + return true; + } + return false; + } + + public static function get_token() + { + $token = sha1(mt_rand()); + if (!isset($_SESSION['tokens'])) + $_SESSION['tokens'] = [$token => 1]; + else + $_SESSION['tokens'][$token] = 1; + return $token; + } + + public static function get_domain($url) + { + $parseUrl = parse_url(trim($url)); + return trim($parseUrl[host] ? str_replace('www.', '', $parseUrl[host]) : str_replace('www.', '', array_shift(explode('/', $parseUrl[path], 2)))); + } + + public static function get_domain_url($url) + { + global $settings; + + $settings['link_version'] ? $www = 'www.' : $www = ''; + $settings['ssl'] == true ? $domain_prefix = 'https' : $domain_prefix = 'http'; + return $domain_prefix . '://' . $www . self::get_domain($url); + } + + public static function max_db_value($table, $column) + { + global $mdb; + $results = $mdb->query('SELECT MAX(' . $column . ') FROM ' . $table)->fetchAll(); + return $results[0][0]; + } + + public static function shuffle_assoc($list) + { + if (!is_array($list)) + return $list; + + $keys = array_keys($list); + shuffle($keys); + $random = array(); + foreach ($keys as $key) + $random[$key] = $list[$key]; + return $random; + } + + public static function escape($value) + { + $return = ''; + for ($i = 0; $i < strlen($value); ++$i) + { + $char = $value[$i]; + $ord = ord($char); + if ($char !== "'" && $char !== "\"" && $char !== '\\' && $ord >= 32 && $ord <= 126) + $return .= $char; + else + $return .= '\\x' . dechex($ord); + } + return $return; + } + + public static function is_bot() + { + $bots = ["Slurp", "Scooter", "URL_Spider_SQL", "Googlebot", "Firefly", "WebBug", "WebFindBot", "crawler", "appie", "msnbot", "InfoSeek", "FAST", "Spade", "NationalDirectory"]; + $agent = strtolower($_SERVER['HTTP_USER_AGENT']); + foreach ($bots as $bot) + if (stripos($agent, $bot) !== false) + return true; + return false; + } + + public static function months() + { + return array(1 => 'Styczeń', 2 => 'Luty', 3 => 'Marzec', 4 => 'Kwiecień', 5 => 'Maj', 6 => 'Czerwiec', 7 => 'Lipiec', 8 => 'Sierpień', 9 => 'Wrzesień', 10 => 'Październik', 11 => 'Listopad', 12 => 'Grudzień'); + } + + public static function months_short() + { + return [1 => 'sty', 2 => 'lut', 3 => 'mar', 4 => 'kwi', 5 => 'maj', 6 => 'cze', 7 => 'lip', 8 => 'sie', 9 => 'wrz', 10 => 'paź', 11 => 'lis', 12 => 'gru']; + } + + public static function chmod_r($path, $chmod = 0755) + { + $dir = new \DirectoryIterator($path); + foreach ($dir as $item) + { + chmod($item->getPathname(), $chmod); + if ($item->isDir() && !$item->isDot()) + self::chmod_r($item->getPathname()); + } + } + + public static function rrmdir($dir) + { + if (is_dir($dir)) + { + $files = scandir($dir); + foreach ($files as $file) + if ($file != "." && $file != "..") + self::rrmdir("$dir/$file"); + rmdir($dir); + } + else if (file_exists($dir)) + unlink($dir); + } + + public static function rcopy($src, $dst) + { + if (is_dir($src)) + { + mkdir($dst, 0755); + $files = scandir($src); + foreach ($files as $file) + if ($file != "." && $file != "..") + self::rcopy("$src/$file", "$dst/$file"); + } + else if (file_exists($src)) + copy($src, $dst); + + self::rrmdir($src); + } + + public static function is_mobile() + { + $detect = new \Mobile_Detect; + return $detect->isMobile(); + } + + public static function get_new_version() + { + global $settings; + + if ($version = self::get_session('new-version')) + return $version; + + $versions = file_get_contents('http://www.cmspro.project-dc.pl/updates/versions.php?key=' . $settings['update_key']); + $versions = explode(PHP_EOL, $versions); + $version = str_replace(',', '.', max($versions)); + + self::set_session('new-version', $version); + + return $version; + } + + public static function get_version() + { + return str_replace(',', '.', @file_get_contents('../libraries/version.ini')); + } + + public static function pre($data, $type = '') + { + $data = str_replace('Array + (', '', $data); + $data = str_replace(')', '', $data); + + echo '' . print_r($data, true) . ''; + } + + public static function json_to_array($json) + { + $values_tmp = json_decode($json, true); + + if (is_array($values_tmp)) + foreach ($values_tmp as $val) + { + if (isset($values[$val['name']])) + { + if (is_array($values[$val['name']])) + $values[$val['name']][] = $val['value']; + else + $values[$val['name']] = array($values[$val['name']], $val['value']); + } + else + $values[$val['name']] = $val['value']; + } + return $values; + } + + public static function set_session($var, $val) + { + $_SESSION[$var] = $val; + } + + public static function get_session($var) + { + return $_SESSION[$var]; + } + + public static function delete_session($var) + { + unset($_SESSION[$var]); + } + + public static function get($var, $strip_tags = false) + { + if (isset($_POST[$var])) + { + if (is_string($_POST[$var])) + { + if ($strip_tags) + return trim(strip_tags($_POST[$var])); + else + return trim($_POST[$var]); + } + else + return $_POST[$var]; + } + else + { + if (isset($_GET[$var])) + { + if (is_string($_GET[$var])) + { + if ($strip_tags) + return trim(strip_tags($_GET[$var])); + else + return trim($_GET[$var]); + } + else + return $_GET[$var]; + } + } + } + + public static function set_message($text) + { + self::set_session('message', $text); + } + + public static function alert($text, $class = 'alert-success') + { + self::set_session('alert', $text); + self::set_session('alert-class', $class); + } + + static public function get_language_domain($lang_id) + { + global $mdb; + + $settings = \front\factory\Settings::settings_details(); + $default_domain = \admin\factory\Languages::default_domain(); + $settings['link_version'] ? $www = 'www.' : $www = ''; + + $domain = $mdb->get('pp_langs', 'domain', ['id' => $lang_id]); + if (!$domain) + { + if ($default_domain) + return $www . $default_domain; + else + return $www . preg_replace('#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME']); + } + else + { + return $www . $domain; + } + } + + public static function htacces($dir = '../') + { + global $mdb; + + $settings = \front\factory\Settings::settings_details(); + $default_domain = \admin\factory\Languages::default_domain(); + $available_domains = \admin\factory\Languages::available_domains(); + + $settings['link_version'] ? $www = 'www.' : $www = ''; + + $settings['ssl'] == true ? $domain_prefix = 'https' : $domain_prefix = 'http'; + + $default_domain ? $url = $default_domain : $url = preg_replace('#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME']); + + $robots = 'User-agent: *' . PHP_EOL; + $robots .= 'Allow: /' . PHP_EOL; + + unlink('../sitemap.xml'); + if (is_array($available_domains) and !empty($available_domains)) + { + foreach ($available_domains as $domain) + { + $site_map[$domain['domain']] = '' . PHP_EOL; + $site_map[$domain['domain']] .= '' . PHP_EOL; + $site_map[$domain['domain']] .= '' . PHP_EOL; + $site_map[$domain['domain']] .= '' . $domain_prefix . '://' . $www . $domain['domain'] . '' . PHP_EOL; + $site_map[$domain['domain']] .= '' . date('Y-m-d') . '' . PHP_EOL; + $site_map[$domain['domain']] .= 'daily' . PHP_EOL; + $site_map[$domain['domain']] .= '1' . PHP_EOL; + $site_map[$domain['domain']] .= '' . PHP_EOL; + } + } + else + { + $site_map[$url] = '' . PHP_EOL; + $site_map[$url] .= '' . PHP_EOL; + $site_map[$url] .= '' . PHP_EOL; + $site_map[$url] .= '' . $domain_prefix . '://' . $www . $url . '' . PHP_EOL; + $site_map[$url] .= '' . date('Y-m-d') . '' . PHP_EOL; + $site_map[$url] .= 'daily' . PHP_EOL; + $site_map[$url] .= '1' . PHP_EOL; + $site_map[$url] .= '' . PHP_EOL; + } + + $htaccess_data = file_get_contents($dir . 'libraries/htaccess.conf'); + + /* cache */ + if ($settings['htaccess_cache']) + { + $htaccess_data = str_replace( + '{HTACCESS_CACHE}', + '' . PHP_EOL + . 'AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/css text/javascript application/javascript application/x-javascript' . PHP_EOL + . '' . PHP_EOL + . '' . PHP_EOL + . 'ExpiresActive on' . PHP_EOL + . 'ExpiresDefault "access plus 1 year"' . PHP_EOL + . 'ExpiresByType text/css "access plus 1 year"' . PHP_EOL + . 'ExpiresByType application/json "access plus 0 seconds"' . PHP_EOL + . 'ExpiresByType application/xml "access plus 0 seconds"' . PHP_EOL + . 'ExpiresByType text/xml "access plus 0 seconds"' . PHP_EOL + . 'ExpiresByType image/x-icon "access plus 1 week"' . PHP_EOL + . 'ExpiresByType text/x-component "access plus 1 year"' . PHP_EOL + . 'ExpiresByType text/html "access plus 0 seconds"' . PHP_EOL + . 'ExpiresByType application/javascript "access plus 1 year"' . PHP_EOL + . 'ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"' . PHP_EOL + . 'ExpiresByType text/cache-manifest "access plus 0 seconds"' . PHP_EOL + . 'ExpiresByType audio/ogg "access plus 1 year"' . PHP_EOL + . 'ExpiresByType image/gif "access plus 1 year"' . PHP_EOL + . 'ExpiresByType image/jpeg "access plus 1 year"' . PHP_EOL + . 'ExpiresByType image/webp "access plus 1 year"' . PHP_EOL + . 'ExpiresByType image/png "access plus 1 year"' . PHP_EOL + . 'ExpiresByType video/mp4 "access plus 1 year"' . PHP_EOL + . 'ExpiresByType video/ogg "access plus 1 year"' . PHP_EOL + . 'ExpiresByType video/webm "access plus 1 year"' . PHP_EOL + . 'ExpiresByType application/atom+xml "access plus 1 hour"' . PHP_EOL + . 'ExpiresByType application/rss+xml "access plus 1 hour"' . PHP_EOL + . 'ExpiresByType application/font-woff "access plus 1 year"' . PHP_EOL + . 'ExpiresByType application/vnd.ms-fontobject "access plus 1 year"' . PHP_EOL + . 'ExpiresByType application/x-font-ttf "access plus 1 year"' . PHP_EOL + . 'ExpiresByType font/opentype "access plus 1 year"' . PHP_EOL + . 'ExpiresByType image/svg+xml "access plus 1 year"' . PHP_EOL + . '', + $htaccess_data + ); + } + else + { + $htaccess_data = str_replace( + '{HTACCESS_CACHE}', + '' . PHP_EOL + . 'Header set Cache-Control "no-cache, no-store, must-revalidate"' . PHP_EOL + . 'Header set Pragma "no-cache"' . PHP_EOL + . 'Header set Expires 0' . PHP_EOL + . '', + $htaccess_data + ); + } + + /* języki w domenie głównej */ + $results = $mdb->select('pp_langs', ['id'], ['AND' => ['status' => 1, 'domain' => null], 'ORDER' => ['o' => 'ASC']]); + if (is_array($results)) foreach ($results as $row) + { + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $row['id'] . '/$ index.php?a=change_language&id=' . $row['id'] . ' [L]'; + } + + $htaccess_data .= PHP_EOL; + + $results = $mdb->select('pp_langs', ['id', 'start', 'domain', 'main_domain'], ['status' => 1, 'ORDER' => ['o' => 'ASC']]); + if (is_array($results)) foreach ($results as $row) + { + $row['domain'] ? $url_tmp = $row['domain'] : $url_tmp = $url; + + !$row['start'] ? $language_link = $row['id'] . '/' : $language_link = ''; + + $results2 = $mdb->select( + 'pp_pages_langs', + ['[><]pp_pages' => ['page_id' => 'id']], + ['seo_link', 'title', 'page_id', 'noindex', 'start', 'page_type'], + ['AND' => ['status' => 1, 'lang_id' => $row['id'], 'block_direct_access' => 0], 'ORDER' => ['start' => 'DESC', 'o' => 'ASC']] + ); + if (is_array($results2)) foreach ($results2 as $row2) + { + if ($row2['title']) + { + /* sitemap.xml */ + if ($row2['page_type'] != 3 and !$row2['noindex']) + { + $site_map[$url_tmp] .= '' . PHP_EOL; + + if ($row2['seo_link']) + { + if ($settings['links_structure']) + $seo = \admin\factory\Pages::google_url_preview($row2['page_id'], $row2['title'], $row[id], 0, 0, $row2['seo_link'], $language_link); + else + $seo = $language_link . self::seo($row2['seo_link']); + + $site_map[$url_tmp] .= '' . $domain_prefix . '://' . $www . $url_tmp . '/' . $seo . '' . PHP_EOL; + } + else + { + if ($settings['links_structure']) + $seo = \admin\factory\Pages::google_url_preview($row2['page_id'], $row2['title'], $row['id'], 0, 0, $row2['seo_link'], $language_link); + else + $seo = $language_link . 's-' . $row2['page_id'] . '-' . self::seo($row2['title']); + + $site_map[$url_tmp] .= '' . $domain_prefix . '://' . $www . $url_tmp . '/' . $seo . '' . PHP_EOL; + } + + $site_map[$url_tmp] .= '' . date('Y-m-d') . '' . PHP_EOL; + $site_map[$url_tmp] .= 'daily' . PHP_EOL; + + $row['start'] ? $priority = 1 : $priority = 0.8; + + $site_map[$url_tmp] .= '' . $priority . '' . PHP_EOL; + $site_map[$url_tmp] .= '' . PHP_EOL; + } + + /* robotx.txt */ + if ($row2['noindex'] and $row2['page_type'] != 3) + { + $robots .= 'User-agent: GoogleBot' . PHP_EOL; + + if ($row2['seo_link']) + { + if ($settings['links_structure']) + $seo = \admin\factory\Pages::google_url_preview($row2['page_id'], $row2['title'], $row[id], 0, 0, $row2['seo_link'], $language_link); + else + $seo = $language_link . self::seo($row2['seo_link']); + + $robots .= 'Disallow: /' . $seo . '$' . PHP_EOL; + $robots .= 'Disallow: /' . $seo . '/s/*' . PHP_EOL; + } + else + { + if ($settings['links_structure']) + $seo = \admin\factory\Pages::google_url_preview($row2['page_id'], $row2['title'], $row['id'], 0, 0, $row2['seo_link'], $language_link); + else + $seo = $language_link . 's-' . $row2['page_id'] . '-' . self::seo($row2['title']); + + $robots .= 'Disallow: /' . $seo . '$' . PHP_EOL; + $robots .= 'Disallow: /' . $seo . '/s/*$' . PHP_EOL; + } + } + + /* htaccess */ + if ($row2['page_type'] != 3) + { + if ( $row['start'] and $row2['start'] ) + { + $htaccess_data .= PHP_EOL . 'RewriteRule ^$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]' . PHP_EOL; + + if ( $row2['seo_link'] ) + { + $htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/' . self::seo( $row2['seo_link'] ) . '(|/)$'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ ' . $domain_prefix . '://' . $www . $url_tmp . '/' . $language_link . ' [R=301,L]'; + + $htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/' . self::seo($row2['seo_link']) . '/s/1$'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ ' . $domain_prefix . '://' . $www . $url_tmp . '/' . $language_link . ' [R=301,L]'; + } + else + { + $htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo($row2['title']) . '$'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ ' . $domain_prefix . '://' . $www . $url_tmp . '/' . $language_link . ' [R=301,L]'; + + $htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo($row2['title']) . '-s-1$'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ ' . $domain_prefix . '://' . $www . $url_tmp . '/' . $language_link . ' [R=301,L]'; + } + + $htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} "^/$"'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . ' [L]'; + + $htaccess_data .= PHP_EOL; + } + + if ($row2['seo_link']) + { + if ($settings['links_structure']) + $seo = \admin\factory\Pages::google_url_preview($row2['page_id'], $row2['title'], $row[id], 0, 0, $row2['seo_link'], $language_link); + else + $seo = $language_link . self::seo($row2['seo_link']); + + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $seo . '(|/)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $seo . '/s/1(|/)$ ' . $seo . ' [R=301,L]'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $seo . '/s/([0-9]+)(|/)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]'; + } + else + { + if ($settings['links_structure']) + $seo = \admin\factory\Pages::google_url_preview($row2['page_id'], $row2['title'], $row['id'], 0, 0, $row2['seo_link'], $language_link); + else + $seo = $language_link . 's-' . $row2['page_id'] . '-' . self::seo($row2['title']); + + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $seo . '(|/)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $seo . '/s/1(|/)$ ' . $seo . ' [R=301,L]'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $seo . '/s/([0-9]+)(|/)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]'; + } + $htaccess_data .= PHP_EOL; + } + } + } + + $results2 = $mdb->select( + 'pp_articles_langs', + ['[><]pp_articles' => ['article_id' => 'id']], + ['seo_link', 'title', 'article_id', 'noindex', 'copy_from', 'block_direct_access'], + ['AND' => ['status' => 1, 'lang_id' => $row['id']]] + ); + if (is_array($results2)) foreach ($results2 as $row2) + { + $domain = self::get_language_domain($row['id']); + + if ($row2['copy_from'] != null) + { + $results_tmp = $mdb->get( + 'pp_articles_langs', + [ + 'seo_link', + 'title' + ], + [ + 'AND' => [ + 'article_id' => $row2['article_id'], + 'lang_id' => $row2['copy_from'] + ] + ] + ); + $row2['seo_link'] = $results_tmp['seo_link']; + $row2['title'] = $results_tmp['title']; + } + + /* sitemap */ + if (!$row2['block_direct_access'] and $row2['title']) + { + $site_map[$url_tmp] .= '' . PHP_EOL; + if ($row2['seo_link']) + $site_map[$url_tmp] .= '' . $domain_prefix . '://' . $www . $url_tmp . '/' . $language_link . self::seo($row2['seo_link']) . '' . PHP_EOL; + else + $site_map[$url_tmp] .= '' . $domain_prefix . '://' . $www . $url_tmp . '/' . $language_link . 'a-' . $row2['article_id'] . '-' . self::seo($row2['title']) . '' . PHP_EOL; + $site_map[$url_tmp] .= '' . date('Y-m-d') . '' . PHP_EOL; + $site_map[$url_tmp] .= 'daily' . PHP_EOL; + $site_map[$url_tmp] .= '0.6' . PHP_EOL; + $site_map[$url_tmp] .= '' . PHP_EOL; + } + + /* robots.txt */ + if ($row2['noindex']) + { + $robots .= 'User-agent: GoogleBot' . PHP_EOL; + if ($row2['seo_link']) + $robots .= 'Disallow: /' . $row2['seo_link'] . '$' . PHP_EOL; + else + $robots .= 'Disallow: /a-' . $row2['article_id'] . '-' . self::seo($row2['title']) . '$' . PHP_EOL; + } + + if (!$row2['block_direct_access']) + { + if ($row2['seo_link']) + { + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo($row2['seo_link']) . '(|/)$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]'; + } + else if ($row2['title'] != null) + { + $htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'a-' . $row2['article_id'] . '-' . self::seo($row2['title']) . '(|/)$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]'; + } + $htaccess_data .= PHP_EOL; + } + } + } + + $results = $mdb->query('SELECT ' + . 'name, tag_id ' + . 'FROM ' + . 'pp_tags AS pt ' + . 'INNER JOIN ' + . 'pp_articles_tags AS pat ON pat.tag_id = pt.id ' + . 'GROUP BY ' + . 'tag_id')->fetchAll(); + if (is_array($results) and !empty($results)) foreach ($results as $row) + { + $htaccess_data .= PHP_EOL . 'RewriteCond %{QUERY_STRING} !=""'; + $htaccess_data .= PHP_EOL . 'RewriteRule tag/' . self::seo( $row['name'] ) . '(|/) %{REQUEST_URI}? [R=301,L]'; + $htaccess_data .= PHP_EOL . 'RewriteRule ^tag/' . self::seo( $row['name'] ) . '(|/)$ index.php?tag=' . $row['tag_id'] . ' [L]'; + } + + $results = $mdb->get('pp_settings', 'value', ['param' => 'htaccess']); + if ($results) + $htaccess_data .= PHP_EOL . $results; + + if (file_exists('../libraries/htaccess.ini')) + $htaccess_data .= PHP_EOL . file_get_contents('../libraries/htaccess.ini'); + + $results = $mdb->get('pp_settings', 'value', ['param' => 'robots']); + if ($results) + $robots .= PHP_EOL . $results; + + if (is_array($available_domains) and !empty($available_domains)) + { + foreach ($available_domains as $domain) + $site_map[$domain['domain']] .= ''; + } + else + $site_map[$url] .= ''; + + $redirect = 'RewriteCond %{REQUEST_METHOD} ^(GET|HEAD)$'. PHP_EOL; + if ( $settings['ssl'] ) + { + $redirect .= 'RewriteCond %{HTTPS} off' . PHP_EOL + . 'RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]'; + } + else + { + $redirect .= 'RewriteCond %{HTTPS} on' . PHP_EOL + . 'RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]'; + } + + $redirect .= 'RewriteCond %{REQUEST_METHOD} ^(GET|HEAD)$'. PHP_EOL; + if ( $settings['link_version'] ) + { + $redirect .= 'RewriteCond %{HTTP_HOST} !^www\. [NC]' . PHP_EOL + . 'RewriteRule ^ %{REQUEST_SCHEME}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]' . PHP_EOL; + } + else + { + $redirect .= 'RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]' . PHP_EOL + . 'RewriteRule ^ %{REQUEST_SCHEME}://%1%{REQUEST_URI} [L,R=301]' . PHP_EOL; + } + + $redirect .= 'RewriteCond %{REQUEST_METHOD} ^(GET|HEAD)$'. PHP_EOL; + if ( $settings['url_version'] ) + { + $redirect .= 'RewriteCond %{REQUEST_URI} !^/admin(?:/.*)?$ [NC]' . PHP_EOL + . 'RewriteRule ^(.+)/$ %{REQUEST_SCHEME}://%{HTTP_HOST}/$1 [L,R=301]' . PHP_EOL; + } + else + { + $redirect .= 'RewriteCond %{REQUEST_URI} !^/admin(/|$) [NC]' . PHP_EOL + . 'RewriteCond %{REQUEST_FILENAME} !-f' . PHP_EOL + . 'RewriteCond %{REQUEST_FILENAME} !-d' . PHP_EOL + . 'RewriteCond %{REQUEST_URI} !/$' . PHP_EOL + . 'RewriteRule ^(.+)$ %{REQUEST_SCHEME}://%{HTTP_HOST}/$1/ [L,R=301]' . PHP_EOL; + } + + $htaccess_data = str_replace( '{REDIRECT}', $redirect, $htaccess_data ); + + $additional_classes = file_get_contents('../libraries/additional-classes.ini'); + $additional_classes = explode(PHP_EOL, $additional_classes); + $additional_classes = array_filter($additional_classes); + if (is_array($additional_classes) and !empty($additional_classes)) + { + foreach ($additional_classes as $class) + { + $classes .= 'RewriteCond %{REQUEST_URI} ^/' . trim($class) . '/(.*) [NC]' . PHP_EOL; + $classes .= 'RewriteRule ^([^/]*)/([^/]*)(|/([^/]*))$ index.php?module=$1&action=$2&$4 [L]' . PHP_EOL; + } + } + $htaccess_data = str_replace('{ADDITIONAL_CLASSES}', $classes, $htaccess_data); + + /* pozostałe linki */ + $htaccess_data .= PHP_EOL; + $htaccess_data .= 'RewriteRule ^newsletter/signin$ index.php?module=newsletter&action=signin [L]' . PHP_EOL; + $htaccess_data .= 'RewriteRule ^newsletter/confirm/hash=(.*)$ index.php?module=newsletter&action=confirm&hash=$1 [L]' . PHP_EOL; + $htaccess_data .= 'RewriteRule ^newsletter/unsubscribe/hash=(.*)$ index.php?module=newsletter&action=unsubscribe&hash=$1 [L]' . PHP_EOL; + + /* pixieset */ + $results = $mdb->select('pp_articles', 'id', ['pixieset' => 1]); + if (is_array($results) and count($results)) + { + $pixieset = 'RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?' . $url_tmp . ' [NC]' . PHP_EOL; + $pixieset .= 'RewriteCond %{REQUEST_URI} ^('; + foreach ($results as $row) + { + $pixieset .= '/upload/article_images/article_' . $row . '/'; + if ($row != end($results)) + $pixieset .= '|'; + } + $pixieset .= ') [NC]' . PHP_EOL . 'RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]' . PHP_EOL; + + $htaccess_data = str_replace('{PIXIESET]', $pixieset, $htaccess_data); + } + else + { + $htaccess_data = str_replace('{PIXIESET]', '', $htaccess_data); + } + + $fp = fopen($dir . '.htaccess', 'w'); + fwrite($fp, $htaccess_data); + fclose($fp); + + $class = '\admin\factory\Sitemap'; + $action = 'sitemap'; + + if (class_exists($class) and method_exists(new $class, $action)) + $site_map = call_user_func_array(array($class, $action), array($site_map, $available_domains, $domain_prefix, $www, $url)); + + if (is_array($available_domains) and !empty($available_domains)) + { + foreach ($available_domains as $domain) + { + $fp = fopen($dir . 'sitemap_' . self::seo($domain['domain']) . '.xml', 'w'); + fwrite($fp, $site_map[$domain['domain']]); + fclose($fp); + } + } + else + { + $fp = fopen($dir . 'sitemap.xml', 'w'); + fwrite($fp, $site_map[$url]); + fclose($fp); + } + + $fp = fopen($dir . 'robots.txt', 'w'); + fwrite($fp, $robots); + fclose($fp); + } + + public static function seo( $val, $delete_rhombs = false ) + { + $array_rep1 = array('*', '_', ' ', '+', '"', "'", '?', '-', ',', '!', '~', '<', '>', '@', '#', '$', '%', '^', '&', '*' . '(', ')' . '-', '=', '\\', '|', '[', ']', ':', '(', ')'); + $array_rep2 = array('-', '-', '-', '-', '', '', '', '-', '-', '', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '', '-', '-', '-', '-', '-', '-', '-', '-'); + $val = self::noPl($val); + $val = str_replace($array_rep1, $array_rep2, $val); + if ($delete_rhombs) + $val = str_replace('/', '', $val); + + $val = strtolower($val); + $val = preg_replace('/(-){2,}/', '-', $val); + $val = ltrim($val, '-'); + $val = rtrim($val, '-'); + return $val; + } + + public static function noPL($string) + { + $chars = array( // Decompositions for Latin-1 Supplement chr(195).chr(128)=> 'A', chr(195).chr(129) => 'A', + chr(195) . chr(130) => 'A', chr(195) . chr(131) => 'A', + chr(195) . chr(132) => 'A', chr(195) . chr(133) => 'A', + chr(195) . chr(135) => 'C', chr(195) . chr(136) => 'E', + chr(195) . chr(137) => 'E', chr(195) . chr(138) => 'E', + chr(195) . chr(139) => 'E', chr(195) . chr(140) => 'I', + chr(195) . chr(141) => 'I', chr(195) . chr(142) => 'I', + chr(195) . chr(143) => 'I', chr(195) . chr(145) => 'N', + chr(195) . chr(146) => 'O', chr(195) . chr(147) => 'O', + chr(195) . chr(148) => 'O', chr(195) . chr(149) => 'O', + chr(195) . chr(150) => 'O', chr(195) . chr(153) => 'U', + chr(195) . chr(154) => 'U', chr(195) . chr(155) => 'U', + chr(195) . chr(156) => 'U', chr(195) . chr(157) => 'Y', + chr(195) . chr(159) => 's', chr(195) . chr(160) => 'a', + chr(195) . chr(161) => 'a', chr(195) . chr(162) => 'a', + chr(195) . chr(163) => 'a', chr(195) . chr(164) => 'a', + chr(195) . chr(165) => 'a', chr(195) . chr(167) => 'c', + chr(195) . chr(168) => 'e', chr(195) . chr(169) => 'e', + chr(195) . chr(170) => 'e', chr(195) . chr(171) => 'e', + chr(195) . chr(172) => 'i', chr(195) . chr(173) => 'i', + chr(195) . chr(174) => 'i', chr(195) . chr(175) => 'i', + chr(195) . chr(177) => 'n', chr(195) . chr(178) => 'o', + chr(195) . chr(179) => 'o', chr(195) . chr(180) => 'o', + chr(195) . chr(181) => 'o', chr(195) . chr(182) => 'o', + chr(195) . chr(182) => 'o', chr(195) . chr(185) => 'u', + chr(195) . chr(186) => 'u', chr(195) . chr(187) => 'u', + chr(195) . chr(188) => 'u', chr(195) . chr(189) => 'y', + chr(195) . chr(191) => 'y', + // Decompositions for Latin Extended-A + chr(196) . chr(128) => 'A', chr(196) . chr(129) => 'a', + chr(196) . chr(130) => 'A', chr(196) . chr(131) => 'a', + chr(196) . chr(132) => 'A', chr(196) . chr(133) => 'a', + chr(196) . chr(134) => 'C', chr(196) . chr(135) => 'c', + chr(196) . chr(136) => 'C', chr(196) . chr(137) => 'c', + chr(196) . chr(138) => 'C', chr(196) . chr(139) => 'c', + chr(196) . chr(140) => 'C', chr(196) . chr(141) => 'c', + chr(196) . chr(142) => 'D', chr(196) . chr(143) => 'd', + chr(196) . chr(144) => 'D', chr(196) . chr(145) => 'd', + chr(196) . chr(146) => 'E', chr(196) . chr(147) => 'e', + chr(196) . chr(148) => 'E', chr(196) . chr(149) => 'e', + chr(196) . chr(150) => 'E', chr(196) . chr(151) => 'e', + chr(196) . chr(152) => 'E', chr(196) . chr(153) => 'e', + chr(196) . chr(154) => 'E', chr(196) . chr(155) => 'e', + chr(196) . chr(156) => 'G', chr(196) . chr(157) => 'g', + chr(196) . chr(158) => 'G', chr(196) . chr(159) => 'g', + chr(196) . chr(160) => 'G', chr(196) . chr(161) => 'g', + chr(196) . chr(162) => 'G', chr(196) . chr(163) => 'g', + chr(196) . chr(164) => 'H', chr(196) . chr(165) => 'h', + chr(196) . chr(166) => 'H', chr(196) . chr(167) => 'h', + chr(196) . chr(168) => 'I', chr(196) . chr(169) => 'i', + chr(196) . chr(170) => 'I', chr(196) . chr(171) => 'i', + chr(196) . chr(172) => 'I', chr(196) . chr(173) => 'i', + chr(196) . chr(174) => 'I', chr(196) . chr(175) => 'i', + chr(196) . chr(176) => 'I', chr(196) . chr(177) => 'i', + chr(196) . chr(178) => 'IJ', chr(196) . chr(179) => 'ij', + chr(196) . chr(180) => 'J', chr(196) . chr(181) => 'j', + chr(196) . chr(182) => 'K', chr(196) . chr(183) => 'k', + chr(196) . chr(184) => 'k', chr(196) . chr(185) => 'L', + chr(196) . chr(186) => 'l', chr(196) . chr(187) => 'L', + chr(196) . chr(188) => 'l', chr(196) . chr(189) => 'L', + chr(196) . chr(190) => 'l', chr(196) . chr(191) => 'L', + chr(197) . chr(128) => 'l', chr(197) . chr(129) => 'L', + chr(197) . chr(130) => 'l', chr(197) . chr(131) => 'N', + chr(197) . chr(132) => 'n', chr(197) . chr(133) => 'N', + chr(197) . chr(134) => 'n', chr(197) . chr(135) => 'N', + chr(197) . chr(136) => 'n', chr(197) . chr(137) => 'N', + chr(197) . chr(138) => 'n', chr(197) . chr(139) => 'N', + chr(197) . chr(140) => 'O', chr(197) . chr(141) => 'o', + chr(197) . chr(142) => 'O', chr(197) . chr(143) => 'o', + chr(197) . chr(144) => 'O', chr(197) . chr(145) => 'o', + chr(197) . chr(146) => 'OE', chr(197) . chr(147) => 'oe', + chr(197) . chr(148) => 'R', chr(197) . chr(149) => 'r', + chr(197) . chr(150) => 'R', chr(197) . chr(151) => 'r', + chr(197) . chr(152) => 'R', chr(197) . chr(153) => 'r', + chr(197) . chr(154) => 'S', chr(197) . chr(155) => 's', + chr(197) . chr(156) => 'S', chr(197) . chr(157) => 's', + chr(197) . chr(158) => 'S', chr(197) . chr(159) => 's', + chr(197) . chr(160) => 'S', chr(197) . chr(161) => 's', + chr(197) . chr(162) => 'T', chr(197) . chr(163) => 't', + chr(197) . chr(164) => 'T', chr(197) . chr(165) => 't', + chr(197) . chr(166) => 'T', chr(197) . chr(167) => 't', + chr(197) . chr(168) => 'U', chr(197) . chr(169) => 'u', + chr(197) . chr(170) => 'U', chr(197) . chr(171) => 'u', + chr(197) . chr(172) => 'U', chr(197) . chr(173) => 'u', + chr(197) . chr(174) => 'U', chr(197) . chr(175) => 'u', + chr(197) . chr(176) => 'U', chr(197) . chr(177) => 'u', + chr(197) . chr(178) => 'U', chr(197) . chr(179) => 'u', + chr(197) . chr(180) => 'W', chr(197) . chr(181) => 'w', + chr(197) . chr(182) => 'Y', chr(197) . chr(183) => 'y', + chr(197) . chr(184) => 'Y', chr(197) . chr(185) => 'Z', + chr(197) . chr(186) => 'z', chr(197) . chr(187) => 'Z', + chr(197) . chr(188) => 'z', chr(197) . chr(189) => 'Z', + chr(197) . chr(190) => 'z', chr(197) . chr(191) => 's' + ); + + $string = strtr($string, $chars); + + $table = array( + "А" => "a", "Б" => "b", "В" => "v", "Г" => "g", "Д" => "d", + "Е" => "e", "Ё" => "yo", "Ж" => "zh", "З" => "z", "И" => "i", + "Й" => "j", "К" => "k", "Л" => "l", "М" => "m", "Н" => "n", + "О" => "o", "П" => "p", "Р" => "r", "С" => "s", "Т" => "t", + "У" => "u", "Ф" => "f", "Х" => "kh", "Ц" => "ts", "Ч" => "ch", + "Ш" => "sh", "Щ" => "sch", "Ъ" => "", "Ы" => "y", "Ь" => "", + "Э" => "e", "Ю" => "yu", "Я" => "ya", "а" => "a", "б" => "b", + "в" => "v", "г" => "g", "д" => "d", "е" => "e", "ё" => "yo", + "ж" => "zh", "з" => "z", "и" => "i", "й" => "j", "к" => "k", + "л" => "l", "м" => "m", "н" => "n", "о" => "o", "п" => "p", + "р" => "r", "с" => "s", "т" => "t", "у" => "u", "ф" => "f", + "х" => "kh", "ц" => "ts", "ч" => "ch", "ш" => "sh", "щ" => "sch", + "ъ" => "", "ы" => "y", "ь" => "", "э" => "e", "ю" => "yu", + "я" => "ya", " " => "-", "." => "", "," => "", + ":" => "", ";" => "", "—" => "", "–" => "-" + ); + + $string = strtr($string, $table); + + return $string; + } + + public static function delete_cache() + { + self::delete_dir('../cache/'); + self::delete_dir('../temp/'); + self::delete_dir('temp/'); + } + + public static function delete_dir($dir) + { + if (is_file($dir)) + return @unlink($dir); + + else if (is_dir($dir)) + { + $scan = glob(rtrim($dir, '/') . '/*'); + + if (is_array($scan)) + foreach ($scan as $index => $path) + self::delete_dir($path); + + if (is_dir($dir) && self::is_empty_dir($dir) and $dir != '../temp/') + return @rmdir($dir); + } + } + + public static function is_empty_dir($dir) + { + return (($files = @scandir($dir)) && count($files) <= 2); + } + + public static function email_check($email) + { + return filter_var($email, FILTER_VALIDATE_EMAIL); + } + + public static function send_email( $email, $subject, $text, $replay = '', $file = '' ) + { + global $settings; + + if ( file_exists('libraries/phpmailer/class.phpmailer.php') ) require_once 'libraries/phpmailer/class.phpmailer.php'; + if ( file_exists('libraries/phpmailer/class.smtp.php') ) require_once 'libraries/phpmailer/class.smtp.php'; + if ( file_exists('../libraries/phpmailer/class.phpmailer.php') ) require_once '../libraries/phpmailer/class.phpmailer.php'; + if ( file_exists('../libraries/phpmailer/class.smtp.php') ) require_once '../libraries/phpmailer/class.smtp.php'; + if ( $email and $subject ) + { + $mail = new \PHPMailer(); + $mail->IsSMTP(); + $mail->SMTPAuth = true; + $mail->Host = $settings['email_host']; + $mail->Port = $settings['email_port']; + $mail->Username = $settings['email_login']; + $mail->Password = $settings['email_password']; + $mail->CharSet = "UTF-8"; + $mail->SMTPOptions = array( + 'ssl' => array( + 'verify_peer' => false, + 'verify_peer_name' => false, + 'allow_self_signed' => true + ) + ); + + if (self::email_check($replay)) + { + $mail->AddReplyTo($replay, $replay); + $mail->SetFrom($settings['contact_email'], $settings['contact_email']); + } + else + { + $mail->AddReplyTo($settings['contact_email'], $settings['firm_name']); + $mail->SetFrom($settings['contact_email'], $settings['firm_name']); + } + + $mail->AddAddress($email, ''); + $mail->Subject = $subject; + $mail->Body = $text; + if (is_array($file)) + { + foreach ($file as $file_tmp) + { + if (file_exists($file_tmp)) + $mail->AddAttachment($file_tmp); + } + } + else + { + if (file_exists($file)) + $mail->AddAttachment($file); + } + $mail->IsHTML(true); + return $mail->Send(); + } + return true; + } +} diff --git a/autoload/Shared/Html/Html.php b/autoload/Shared/Html/Html.php new file mode 100644 index 0000000..965c75a --- /dev/null +++ b/autoload/Shared/Html/Html.php @@ -0,0 +1,93 @@ +params = $params; + return $tpl->render( 'html/form-text' ); + } + + public static function input_switch( array $params = array() ) + { + $tpl = new \Shared\Tpl\Tpl; + $tpl->params = $params; + return $tpl->render( 'html/input-switch' ); + } + + public static function select( array $params = array() ) + { + $tpl = new \Shared\Tpl\Tpl; + $tpl->params = $params; + return $tpl->render( 'html/select' ); + } + + public static function textarea( array $params = array() ) + { + $defaults = array( + 'rows' => 4, + ); + + $params = array_merge( $defaults, $params ); + + $tpl = new \Shared\Tpl\Tpl; + $tpl->params = $params; + return $tpl->render( 'html/textarea' ); + } + + public static function input_icon( array $params = array() ) + { + $defaults = array( + 'type' => 'text', + ); + + $params = array_merge( $defaults, $params ); + + $tpl = new \Shared\Tpl\Tpl; + $tpl->params = $params; + return $tpl->render( 'html/input-icon' ); + } + + public static function input( array $params = array() ) + { + $defaults = array( + 'type' => 'text', + ); + + $params = array_merge( $defaults, $params ); + + $tpl = new \Shared\Tpl\Tpl; + $tpl->params = $params; + return $tpl->render( 'html/input' ); + } + + public static function button( array $params = array() ) + { + $defaults = array( + 'class' => 'btn-sm btn-info', + ); + + $params = array_merge( $defaults, $params ); + + $tpl = new \Shared\Tpl\Tpl; + $tpl->params = $params; + return $tpl->render( 'html/button' ); + } + + public static function panel( array $params = array() ) + { + $defaults = array( + 'title' => 'panel-title', + 'class' => 'panel-primary', + 'content' => 'panel-content' + ); + + $params = array_merge( $defaults, $params ); + + $tpl = new \Shared\Tpl\Tpl; + $tpl->params = $params; + return $tpl->render( 'html/panel' ); + } +} diff --git a/autoload/Shared/Image/ImageManipulator.php b/autoload/Shared/Image/ImageManipulator.php new file mode 100644 index 0000000..2894af8 --- /dev/null +++ b/autoload/Shared/Image/ImageManipulator.php @@ -0,0 +1,314 @@ +img_src = $file; + $this->setImageFile($file); + } else { + echo 'a'; exit; + $this->setImageString($file); + } + } + } + + /** + * Set image resource from file + * + * @param string $file Path to image file + * @return ImageManipulator for a fluent interface + * @throws \InvalidArgumentException + */ + public function setImageFile($file) + { + if (!(is_readable($file) && is_file($file))) { + throw new \InvalidArgumentException("Image file $file is not readable"); + } + + if (is_resource($this->image)) { + imagedestroy($this->image); + } + + list ($this->width, $this->height, $type) = getimagesize($file); + + switch ($type) { + case IMAGETYPE_GIF : + $this->image = imagecreatefromgif($file); + break; + case IMAGETYPE_JPEG : + $this->image = imagecreatefromjpeg($file); + break; + case IMAGETYPE_PNG : + $this->image = imagecreatefrompng($file); + break; + default : + throw new \InvalidArgumentException("Image type $type not supported"); + } + + return $this; + } + + /** + * Set image resource from string data + * + * @param string $data + * @return ImageManipulator for a fluent interface + * @throws \RuntimeException + */ + public function setImageString($data) + { + if (is_resource($this->image)) { + imagedestroy($this->image); + } + + if (!$this->image = imagecreatefromstring($data)) { + throw new \RuntimeException('Cannot create image from data string'); + } + $this->width = imagesx($this->image); + $this->height = imagesy($this->image); + return $this; + } + + /** + * Resamples the current image + * + * @param int $width New width + * @param int $height New height + * @param bool $constrainProportions Constrain current image proportions when resizing + * @return ImageManipulator for a fluent interface + * @throws \RuntimeException + */ + public function resample( $width, $height, $constrainProportions = true ) + { + if (!is_resource($this->image)) { + throw new \RuntimeException('No image set'); + } + if ($constrainProportions) { + if ($this->height >= $this->width) { + $width = round($height / $this->height * $this->width); + } else { + $height = round($width / $this->width * $this->height); + } + } + + $temp = imagecreatetruecolor($width, $height); + + imagecopyresampled($temp, $this->image, 0, 0, 0, 0, $width, $height, $this->width, $this->height); + + if ( function_exists('exif_read_data') ) + { + $exif = exif_read_data( $this->img_src ); + if ( $exif && isset($exif['Orientation']) ) + { + $orientation = $exif['Orientation']; + if ( $orientation != 1 ) + { + $deg = 0; + switch ($orientation) + { + case 3: + $deg = 180; + break; + case 6: + $deg = 270; + break; + case 8: + $deg = 90; + break; + } + + if ( $deg ) + $temp = imagerotate( $temp, $deg, 0 ); + } + } + } + return $this->_replace($temp); + } + + /** + * Enlarge canvas + * + * @param int $width Canvas width + * @param int $height Canvas height + * @param array $rgb RGB colour values + * @param int $xpos X-Position of image in new canvas, null for centre + * @param int $ypos Y-Position of image in new canvas, null for centre + * @return ImageManipulator for a fluent interface + * @throws \RuntimeException + */ + public function enlargeCanvas($width, $height, array $rgb = array(), $xpos = null, $ypos = null) + { + if (!is_resource($this->image)) { + throw new \RuntimeException('No image set'); + } + + $width = max($width, $this->width); + $height = max($height, $this->height); + + $temp = imagecreatetruecolor($width, $height); + if (count($rgb) == 3) { + $bg = imagecolorallocate($temp, $rgb[0], $rgb[1], $rgb[2]); + imagefill($temp, 0, 0, $bg); + } + + if (null === $xpos) { + $xpos = round(($width - $this->width) / 2); + } + if (null === $ypos) { + $ypos = round(($height - $this->height) / 2); + } + + imagecopy($temp, $this->image, (int) $xpos, (int) $ypos, 0, 0, $this->width, $this->height); + return $this->_replace($temp); + } + + /** + * Crop image + * + * @param int|array $x1 Top left x-coordinate of crop box or array of coordinates + * @param int $y1 Top left y-coordinate of crop box + * @param int $x2 Bottom right x-coordinate of crop box + * @param int $y2 Bottom right y-coordinate of crop box + * @return ImageManipulator for a fluent interface + * @throws \RuntimeException + */ + public function crop($x1, $y1 = 0, $x2 = 0, $y2 = 0) + { + if (!is_resource($this->image)) { + throw new \RuntimeException('No image set'); + } + if (is_array($x1) && 4 == count($x1)) { + list($x1, $y1, $x2, $y2) = $x1; + } + + $x1 = max($x1, 0); + $y1 = max($y1, 0); + + $x2 = min($x2, $this->width); + $y2 = min($y2, $this->height); + + $width = $x2 - $x1; + $height = $y2 - $y1; + + $temp = imagecreatetruecolor($width, $height); + imagecopy($temp, $this->image, 0, 0, $x1, $y1, $width, $height); + + return $this->_replace($temp); + } + + /** + * Replace current image resource with a new one + * + * @param resource $res New image resource + * @return ImageManipulator for a fluent interface + * @throws \UnexpectedValueException + */ + protected function _replace($res) + { + if (!is_resource($res)) { + throw new \UnexpectedValueException('Invalid resource'); + } + if (is_resource($this->image)) { + imagedestroy($this->image); + } + $this->image = $res; + $this->width = imagesx($res); + $this->height = imagesy($res); + return $this; + } + + /** + * Save current image to file + * + * @param string $fileName + * @return void + * @throws \RuntimeException + */ + public function save($fileName, $type = IMAGETYPE_JPEG) + { + $dir = dirname($fileName); + if (!is_dir($dir)) { + if (!mkdir($dir, 0755, true)) { + throw new \RuntimeException('Error creating directory ' . $dir); + } + } + + try { + switch ($type) { + case IMAGETYPE_GIF : + if (!imagegif($this->image, $fileName)) { + throw new \RuntimeException; + } + break; + case IMAGETYPE_PNG : + if (!imagepng($this->image, $fileName)) { + throw new \RuntimeException; + } + break; + case IMAGETYPE_JPEG : + default : + if (!imagejpeg($this->image, $fileName, 95)) { + throw new \RuntimeException; + } + } + } catch (\Exception $ex) { + throw new \RuntimeException('Error saving image file to ' . $fileName); + } + } + + /** + * Returns the GD image resource + * + * @return resource + */ + public function getResource() + { + return $this->image; + } + + /** + * Get current image resource width + * + * @return int + */ + public function getWidth() + { + return $this->width; + } + + /** + * Get current image height + * + * @return int + */ + public function getHeight() + { + return $this->height; + } +} diff --git a/autoload/Shared/Tpl/Tpl.php b/autoload/Shared/Tpl/Tpl.php new file mode 100644 index 0000000..9d5109d --- /dev/null +++ b/autoload/Shared/Tpl/Tpl.php @@ -0,0 +1,75 @@ +dir = $dir; + } + + public static function view( $file, $values = '' ) + { + $tpl = new self; + if ( is_array( $values ) ) foreach ( $values as $key => $val ) + $tpl->$key = $val; + return $tpl->render( $file ); + } + + public function secureHTML( $val ) + { + $out = stripslashes( $val ); + $out = str_replace( "'", "'", $out ); + $out = str_replace( '"', """, $out ); + $out = str_replace( "<", "<", $out ); + $out = str_replace( ">", ">", $out ); + return $out; + } + + public function render( $file ) + { + if ( file_exists( 'templates_user/' . $file . '.php' ) ) + { + ob_start(); + include 'templates_user/' . $file . '.php'; + $out = ob_get_contents(); + ob_end_clean(); + + return $out; + } + else if ( file_exists( 'templates/' . $file . '.php' ) ) + { + ob_start(); + include 'templates/' . $file . '.php'; + $out = ob_get_contents(); + ob_end_clean(); + + return $out; + } + else if ( file_exists( $file . '.php' ) ) + { + ob_start(); + include $file . '.php'; + $out = ob_get_contents(); + ob_end_clean(); + + return $out; + } + else + return '