Compare commits

...

25 Commits

Author SHA1 Message Date
slawkens
5ca10f5439 Release v0.8.24 2025-06-03 17:44:49 +02:00
slawkens
75bfc2cdb0 Update CHANGELOG.md 2025-06-03 17:44:40 +02:00
slawkens
e542e8a7ce Two new hooks for pages loaded from database (custom pages)
HOOK_BEFORE_PAGE_CUSTOM,
HOOK_AFTER_PAGE_CUSTOM
2025-05-31 22:08:28 +02:00
slawkens
fcfe5b0dbd Do not allow access to tools/ folder after install 2025-05-29 11:27:32 +02:00
slawkens
149e10261b Add code to insert guild_ranks, incase guild trigger is missing 2025-05-28 23:09:24 +02:00
slawkens
5726c9fa94 Load hooks before twig, add new hooks: HOOK_TWIG + HOOK_INIT 2025-05-16 07:42:50 +02:00
slawkens
26b2c472cc Fix HOOK_LAST 2025-05-15 22:40:09 +02:00
slawkens
76440a37d0 New hooks for account characters change-comment page 2025-05-15 20:22:36 +02:00
slawkens
335b7b3112 Do not return -1 in case of freePremium, makes things harder 2025-05-14 09:22:22 +02:00
slawkens
ddc13b110e Start v0.8.24-dev 2025-04-22 15:56:53 +02:00
slawkens
02918684fd Merge branch '0.8' of https://github.com/slawkens/myaac into 0.8 2025-04-22 14:20:45 +02:00
slawkens
5bc70beae6 Release v.0.8.23 2025-04-22 14:20:34 +02:00
slawkens
6418b782a3 Update buttons.base.html.twig 2025-04-22 14:15:37 +02:00
slawkens
687ccc4559 README.md corrections
* Add links to docs & faq
* add info about 2.x dev version
* fix some grammar typos
2025-04-22 14:13:12 +02:00
slawkens
29a198bfce Display more info when error parsing config.lua value 2025-04-22 14:07:46 +02:00
slawkens
06a235237d Plugin name is required, version is optional 2025-04-22 14:05:10 +02:00
slawkens
9c15c2fa68 Add $db->hasTableAndColumns($table, $columns), credits to @opentibiabr Team 2025-04-22 14:03:25 +02:00
slawkens
1708a48186 Change logout button color 2025-04-22 13:46:28 +02:00
slawkens
68170e42cd Fix headline.php: change image format to .png cause of black background 2025-04-22 13:45:35 +02:00
slawkens
4b9a7eaf85 Add noSubmit option to buttons.base 2025-04-22 13:44:45 +02:00
slawkens
01dda11a2f Merge branch '0.8' of https://github.com/slawkens/myaac into 0.8 2025-03-30 07:16:23 +02:00
slawkens
1d33750e85 Update .gitignore 2025-03-30 07:16:15 +02:00
slawkens
89f537c8c2 getPremDays: returns -1 if freePremium 2025-03-15 23:00:01 +01:00
slawkens
727da8b0cc Fix long title on headline.php 2025-02-12 16:19:07 +01:00
slawkens
5b6f29741d Update README.md 2025-01-27 22:59:02 +01:00
25 changed files with 146 additions and 67 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ Thumbs.db
#
/.htaccess
lua
# composer
composer.lock

View File

@@ -1,5 +1,34 @@
# Changelog
## [0.8.24 - 03.06.2025]
### Added
* Add code to insert guild_ranks on guild create, in case guild trigger is missing (https://github.com/slawkens/myaac/commit/149e10261befab22a38246bd792e2e4d1c42ef1e)
* Two new hooks for pages loaded from database (custom pages): HOOK_BEFORE_PAGE_CUSTOM + HOOK_AFTER_PAGE_CUSTOM (https://github.com/slawkens/myaac/commit/e542e8a7cebad2d4bd6984c62cd6f385363ba9eb)
* Load hooks before twig, add new hooks: HOOK_TWIG + HOOK_INIT (https://github.com/slawkens/myaac/commit/5726c9fa94e6f5a198917f6bda9014c0cbb141fb)
* New hooks for account characters change-comment page (https://github.com/slawkens/myaac/commit/76440a37d009b845db9157312f2807774e15de14)
### Fixed
* Do not allow access to tools/ folder after install (https://github.com/slawkens/myaac/commit/fcfe5b0dbd33fd628031fe60ae3f2acc5164eed8)
### Changed
* Do not return -1 in case of freePremium, makes things harder (https://github.com/slawkens/myaac/commit/335b7b3112b3f2ed87b46c5dbc5db70a33bbd953)
## [0.8.23 - 22.04.2025]
### Added
* Add db->hasTableAndColumns(table, columns), credits to @opentibiabr Team (https://github.com/slawkens/myaac/commit/9c15c2fa6848d966457972fce0d04347ecbd4f2c)
* Add noSubmit option to buttons.base (https://github.com/slawkens/myaac/commit/4b9a7eaf859f9d3dbc90deb03564fcddde2f90d3)
### Changed
* Display more info when error parsing config.lua value (https://github.com/slawkens/myaac/commit/29a198bfcedeb5d22e0d34c5c53098142acdf477)
* Change logout button color to red in tibiacom template (https://github.com/slawkens/myaac/commit/1708a48186294e3eea0a79344fdf56ba93327c6d)
### Fixed
* Fix headline.php: change image format to .png cause of black background (https://github.com/slawkens/myaac/commit/68170e42cd8fac43c55654ba595f09425618f26e)
* Fix long title on headline.php (https://github.com/slawkens/myaac/commit/727da8b0cc968d0cafc502dd9c8cebf17e46bf76)
* getPremDays: returns -1 if freePremium (https://github.com/slawkens/myaac/commit/89f537c8c2646c748a14ee46539a6103fda88ec1)
## [0.8.22 - 27.01.2025]
### Changed

View File

@@ -12,11 +12,18 @@ Official website: https://my-aac.org
| Version | Status | Branch | Requirements |
|:--------|:-----------------------|:--------|:---------------|
| **1.x** | **Active development** | develop | **PHP >= 8.1** |
| 2.x | Experimental features | develop | PHP >= 8.1 |
| **1.x** | **Active development** | main | **PHP >= 8.1** |
| 0.9.x | Not developed anymore | 0.9 | PHP >= 7.2.5 |
| 0.8.x | Active support | master | PHP >= 7.2.5 |
| 0.8.x | Active support | 0.8 | PHP >= 7.2.5 |
| 0.7.x | End Of Life | 0.7 | PHP >= 5.3.3 |
The recommended version to install is 1.x, which can be found at releases page - [https://github.com/slawkens/myaac/releases](https://github.com/slawkens/myaac/releases).
### Documentation
* [docs.my-aac.org](https://docs.my-aac.org)
* [my-aac.org - FAQ](https://my-aac.org/faqs/)
### Requirements
- MySQL database
@@ -47,23 +54,23 @@ Official website: https://my-aac.org
### Configuration
Check *config.php* to get more informations. (Notice: MyAAC 1.0+ doesn't use config.php anymore, it has been moved to Admin Panel - Settings page).
Check *config.php* to get more information. (Notice: MyAAC 1.0+ doesn't use config.php anymore, it has been moved to Admin Panel - Settings page).
Use *config.local.php* for your local configuration changes.
### Branches
This repository follows the Git Flow Workflow.
Cheatsheet: [Git-Flow-Cheetsheet](https://danielkummer.github.io/git-flow-cheatsheet)
Cheatsheet: [Git-Flow-Cheatsheet](https://danielkummer.github.io/git-flow-cheatsheet)
That means, we use:
* master branch, for current stable release
* main branch, for current stable release
* develop branch, for development version (next release)
* feature branches, for features etc.
### Known Problems
- Some compatibility issues with some exotical distibutions.
- Some compatibility issues with some exotic distributions.
### Contributing
@@ -77,7 +84,7 @@ Look: [Contributing](https://github.com/otsoft/myaac/wiki/Contributing) in our w
### Other Notes
If you have a great idea or want contribute to the project - visit our website at https://www.my-aac.org
If you have a great idea or want to contribute to the project - visit our website at https://www.my-aac.org
## Project supported by JetBrains

View File

@@ -34,11 +34,6 @@ if(config('env') === 'dev') {
error_reporting(E_ALL);
}
// event system
require_once SYSTEM . 'hooks.php';
$hooks = new Hooks();
$hooks->load();
require SYSTEM . 'status.php';
require SYSTEM . 'login.php';
require SYSTEM . 'migrate.php';

View File

@@ -26,7 +26,7 @@
if (version_compare(phpversion(), '7.2.5', '<')) die('PHP version 7.2.5 or higher is required.');
define('MYAAC', true);
define('MYAAC_VERSION', '0.8.22');
define('MYAAC_VERSION', '0.8.24');
define('DATABASE_VERSION', 33);
define('TABLE_PREFIX', 'myaac_');
define('START_TIME', microtime(true));
@@ -100,7 +100,7 @@ for($i = 1; $i < $size; $i++)
$basedir = str_replace(array('/admin', '/install', '/tools'), '', $basedir);
define('BASE_DIR', $basedir);
if (file_exists(BASE . 'config.local.php') && !defined('MYAAC_INSTALL')) {
if (file_exists(BASE . 'config.local.php')) {
require BASE . 'config.local.php';
}

View File

@@ -193,10 +193,6 @@ define('PAGE', $page);
$template_place_holders = array();
// event system
require_once SYSTEM . 'hooks.php';
$hooks = new Hooks();
$hooks->load();
require_once SYSTEM . 'login.php';
require_once SYSTEM . 'status.php';
@@ -338,7 +334,7 @@ if($load_it)
$success = false;
$tmp_content = getCustomPage($page, $success);
if($success) {
if($success && $hooks->trigger(HOOK_BEFORE_PAGE_CUSTOM)) {
$content .= $tmp_content;
if(hasFlag(FLAG_CONTENT_PAGES) || superAdmin()) {
$pageInfo = getCustomPageInfo($page);
@@ -346,6 +342,8 @@ if($load_it)
'page' => array('id' => $pageInfo !== null ? $pageInfo['id'] : 0, 'hidden' => $pageInfo !== null ? $pageInfo['hidden'] : '0')
)) . $content;
}
$hooks->trigger(HOOK_AFTER_PAGE_CUSTOM);
} else {
$file = TEMPLATES . "$template_name/pages/$page.php";
if(!@file_exists($file) || preg_match('/[^A-z0-9_\-]/', $page)) {

View File

@@ -13,9 +13,6 @@ require BASE . 'install/includes/functions.php';
require BASE . 'install/includes/locale.php';
require SYSTEM . 'clients.conf.php';
if(file_exists(BASE . 'config.local.php'))
require BASE . 'config.local.php';
// ignore undefined index from Twig autoloader
$config['env'] = 'prod';

View File

@@ -139,14 +139,5 @@ else {
}
$_SESSION['installed'] = true;
}
foreach($_SESSION as $key => $value) {
if(strpos($key, 'var_') !== false)
unset($_SESSION[$key]);
}
unset($_SESSION['saved']);
if(file_exists(CACHE . 'install.txt')) {
unlink(CACHE . 'install.txt');
}
}
}

View File

@@ -7,6 +7,11 @@ require SYSTEM . 'functions.php';
require BASE . 'install/includes/functions.php';
require BASE . 'install/includes/locale.php';
if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) {
warning($locale['already_installed']);
return;
}
$error = false;
require BASE . 'install/includes/config.php';

View File

@@ -97,8 +97,18 @@ require_once SYSTEM . 'migrations/22.php';
require_once SYSTEM . 'migrations/27.php';
require_once SYSTEM . 'migrations/30.php';
// cleanup
foreach($_SESSION as $key => $value) {
if(strpos($key, 'var_') !== false)
unset($_SESSION[$key]);
}
unset($_SESSION['saved']);
if(file_exists(CACHE . 'install.txt')) {
unlink(CACHE . 'install.txt');
}
$locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']);
$locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']);
$locale['step_finish_desc'] = str_replace('$LINK$', generateLink('https://my-aac.org', 'https://my-aac.org', true), $locale['step_finish_desc']);
success($locale['step_finish_desc']);
success($locale['step_finish_desc']);

View File

@@ -8,7 +8,6 @@ if(PHP_SAPI !== 'cli') {
require_once __DIR__ . '/../../common.php';
require_once SYSTEM . 'functions.php';
require_once SYSTEM . 'init.php';
require_once SYSTEM . 'hooks.php';
if($argc !== 2) {
echo 'This command expects one parameter: zip file name (plugin)' . PHP_EOL;

View File

@@ -972,12 +972,19 @@ function load_config_lua($filename)
}
else
{
foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
foreach($result as $tmp_key => $tmp_value) { // load values defined by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
$value = str_replace($tmp_key, $tmp_value, $value);
$ret = @eval("return $value;");
if((string) $ret == '' && trim($value) !== '""') // = parser error
{
throw new RuntimeException('ERROR: Loading config.lua file. Line <b>' . ($ln + 1) . '</b> of LUA config file is not valid [key: <b>' . $key . '</b>]');
}
try {
$ret = eval("return $value;");
}
catch (Throwable $e) {
throw new RuntimeException('ERROR: Loading config.lua file. Line: ' . ($ln + 1) . ' - Unable to parse value "' . $value . '" - ' . $e->getMessage());
}
if((string) $ret == '' && trim($value) !== '""') {
throw new RuntimeException('ERROR: Loading config.lua file. Line ' . ($ln + 1) . ' is not valid [key: ' . $key . ']');
}
$result[$key] = $ret;
}

View File

@@ -10,9 +10,12 @@
defined('MYAAC') or die('Direct access not allowed!');
$i = 0;
define('HOOK_INIT', ++$i);
define('HOOK_STARTUP', ++$i);
define('HOOK_BEFORE_PAGE', ++$i);
define('HOOK_BEFORE_PAGE_CUSTOM', ++$i);
define('HOOK_AFTER_PAGE', ++$i);
define('HOOK_AFTER_PAGE_CUSTOM', ++$i);
define('HOOK_FINISH', ++$i);
define('HOOK_TIBIACOM_ARTICLE', ++$i);
define('HOOK_TIBIACOM_BORDER_3', ++$i);
@@ -30,6 +33,10 @@ define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i);
define('HOOK_LOGIN', ++$i);
define('HOOK_LOGIN_ATTEMPT', ++$i);
define('HOOK_LOGOUT', ++$i);
define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_SUCCESS', ++$i);
define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_NAME', ++$i);
define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_HIDE_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_COMMENT', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', ++$i);
@@ -57,8 +64,9 @@ define('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS', ++$i);
define('HOOK_ACCOUNT_MANAGE_BEFORE_CHARACTERS', ++$i);
define('HOOK_EMAIL_CONFIRMED', ++$i);
define('HOOK_GUILDS_AFTER_INVITED_CHARACTERS', ++$i);
define('HOOK_TWIG', ++$i);
define('HOOK_FIRST', HOOK_STARTUP);
define('HOOK_LAST', HOOK_EMAIL_CONFIRMED);
define('HOOK_LAST', $i);
require_once LIBS . 'plugins.php';
require_once LIBS . 'src/Plugins.php';

View File

@@ -124,6 +124,12 @@ require_once SYSTEM . 'libs/pot/OTS.php';
$ots = POT::getInstance();
require_once SYSTEM . 'database.php';
// event system
require_once SYSTEM . 'hooks.php';
$hooks = new Hooks();
$hooks->load();
$hooks->trigger(HOOK_INIT);
// twig
require_once SYSTEM . 'twig.php';

View File

@@ -144,20 +144,13 @@ class Plugins {
$continue = true;
if(!isset($plugin_json['name']) || empty(trim($plugin_json['name']))) {
self::$warnings[] = 'Plugin "name" tag is not set.';
}
if(!isset($plugin_json['description']) || empty(trim($plugin_json['description']))) {
self::$warnings[] = 'Plugin "description" tag is not set.';
self::$error = 'Plugin "name" tag is not set.';
return false;
}
if(!isset($plugin_json['version']) || empty(trim($plugin_json['version']))) {
self::$warnings[] = 'Plugin "version" tag is not set.';
}
if(!isset($plugin_json['author']) || empty(trim($plugin_json['author']))) {
self::$warnings[] = 'Plugin "author" tag is not set.';
}
if(!isset($plugin_json['contact']) || empty(trim($plugin_json['contact']))) {
self::$warnings[] = 'Plugin "contact" tag is not set.';
}
if(isset($plugin_json['require'])) {
$require = $plugin_json['require'];

View File

@@ -374,16 +374,13 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
if(isset($this->data['premium_ends_at']) || isset($this->data['premend'])) {
$col = isset($this->data['premium_ends_at']) ? 'premium_ends_at' : 'premend';
$ret = ceil(($this->data[$col] - time()) / (24 * 60 * 60));
return $ret > 0 ? $ret : 0;
return max($ret, 0);
}
if($this->data['premdays'] == 0) {
return 0;
}
global $config;
if(isset($config['lua']['freePremium']) && getBoolean($config['lua']['freePremium'])) return -1;
if($this->data['premdays'] == self::GRATIS_PREMIUM_DAYS){
return self::GRATIS_PREMIUM_DAYS;
}

View File

@@ -220,6 +220,19 @@ class OTS_DB_MySQL extends OTS_Base_DB
return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE '" . $column . "'")->fetchAll()) > 0);
}
public function hasTableAndColumns(string $table, array $columns = []): bool
{
if (!$this->hasTable($table)) return false;
foreach ($columns as $column) {
if (!$this->hasColumn($table, $column)) {
return false;
}
}
return true;
}
public function revalidateCache() {
foreach($this->has_table_cache as $key => $value) {
$this->hasTableInternal($key);

View File

@@ -36,6 +36,8 @@ if($player_name != null) {
'description' => 'The character information has been changed.'
));
$show_form = false;
$hooks->trigger(HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_SUCCESS, ['player' => $player]);
}
}
} else {

View File

@@ -111,7 +111,26 @@ if(isset($todo) && $todo == 'save')
$new_guild->setOwner($player);
$new_guild->save();
$new_guild->setCustomField('description', 'New guild. Leader must edit this text :)');
//$new_guild->setCustomField('creationdata', time());
if ($db->hasTable('guild_ranks')) {
$query = $db->query("SELECT * FROM guild_ranks WHERE guild_id = " . $new_guild->getId());
if($query->rowCount() == 0) {
$ranks = [
['level' => 3, 'name' => 'the Leader'],
['level' => 2, 'name' => 'a Vice-Leader'],
['level' => 1, 'name' => 'a Member'],
];
foreach ($ranks as $rank) {
$db->insert('guild_ranks', [
'guild_id' => $new_guild->getId(),
'name' => $rank['name'],
'level' => $rank['level'],
]);
}
}
}
$ranks = $new_guild->getGuildRanksList();
$ranks->orderBy('level', POT::ORDER_DESC);
foreach($ranks as $rank) {
@@ -119,14 +138,11 @@ if(isset($todo) && $todo == 'save')
$player->setRank($rank);
}
}
$twig->display('guilds.create.success.html.twig', array(
'guild_name' => $guild_name,
'leader_name' => $player->getName()
));
/*$db->exec('INSERT INTO `guild_ranks` (`id`, `guild_id`, `name`, `level`) VALUES (null, '.$new_guild->getId().', "the Leader", 3)');
$db->exec('INSERT INTO `guild_ranks` (`id`, `guild_id`, `name`, `level`) VALUES (null, '.$new_guild->getId().', "a Vice-Leader", 2)');
$db->exec('INSERT INTO `guild_ranks` (`id`, `guild_id`, `name`, `level`) VALUES (null, '.$new_guild->getId().', "a Member", 1)');*/
}
else {
sort($array_of_player_nig);

View File

@@ -32,6 +32,7 @@ If you do not want to specify a certain field, just leave it blank.<br/><br/>
<td class="LabelV">Name:</td>
<td style="width:80%;" >{{ player.getName() }}</td>
</tr>
{{ hook('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_NAME') }}
<tr>
<td class="LabelV" >Hide Account:</td>
<td>
@@ -41,6 +42,7 @@ If you do not want to specify a certain field, just leave it blank.<br/><br/>
{% if player.getCustomField('group_id') > 1 %} (you will be also hidden on the Team page!){% endif %}
</td>
</tr>
{{ hook('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_HIDE_ACCOUNT') }}
</table>
</div>
</div>
@@ -64,6 +66,7 @@ If you do not want to specify a certain field, just leave it blank.<br/><br/>
<td class="LabelV" ><span>Comment:</span></td>
<td style="width:80%;"><textarea name="comment" rows="10" cols="50" wrap="virtual">{{ player.getCustomField('comment')|raw }}</textarea><br>[max. length: 2000 chars, 50 lines (ENTERs)]</td>
</tr>
{{ hook('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_COMMENT') }}
</table>
</div>
</div>
@@ -108,4 +111,4 @@ If you do not want to specify a certain field, just leave it blank.<br/><br/>
</table>
</td>
</tr>
</table>
</table>

View File

@@ -1 +1 @@
<input type="submit" name="{{ button_name }}" value="{{ button_name }}" />
<input {% if noSubmit is not defined %}type="submit"{% endif %} name="{{ button_name }}" value="{{ button_name }}" />

View File

@@ -83,3 +83,5 @@ $filter = new TwigFilter('urlencode', function ($s) {
$twig->addFilter($filter);
unset($function, $filter);
$hooks->trigger(HOOK_TWIG, ['twig' => $twig, 'twig_loader' => $twig_loader]);

View File

@@ -13,7 +13,7 @@
<div class="BigButton" style="background-image:url({{ template_path }}/images/global/buttons/{{ tmp_image }}.gif)">
<div onMouseOver="MouseOverBigButton(this);" onMouseOut="MouseOutBigButton(this);">
<div class="BigButtonOver" style="background-image:url({{ template_path }}/images/global/buttons/{{ tmp_image }}_over.gif);" ></div>
<input class="BigButtonText" type="submit" value="{{ button_name }}">
<input class="BigButtonText" {% if noSubmit is not defined %}type="submit"{% endif %} value="{{ button_name }}">
</div>
</div>
{% endapply %}

View File

@@ -1,3 +1,3 @@
{% set button_name = 'Logout' %}
{% set button_image = '_sbutton_logout' %}
{% include('buttons.base.html.twig') %}
{% set button_color = 'red' %}
{% include('buttons.base.html.twig') %}

View File

@@ -21,7 +21,7 @@ if(!@file_exists($page_file))
putenv('GDFONTPATH=' . __DIR__);
// create image
$image = imagecreatetruecolor(250, 28);
$image = imagecreatetruecolor(600, 28);
// make the background transparent
imagecolortransparent($image, imagecolorallocate($image, 0, 0, 0));
@@ -31,12 +31,12 @@ if(!@file_exists($page_file))
imagettftext($image, 18, 0, 4, 20, imagecolorallocate($image, 240, 209, 164), $font, $_GET['t']);
// header mime type
header('Content-type: image/gif');
header('Content-type: image/png');
// save image
imagegif($image/*, $file*/);
imagepng($image/*, $file*/);
//}
// output image
//header('Content-type: image/gif');
//header('Content-type: image/png');
//readfile($file);