Finished Highscore API
This commit is contained in:
parent
49b7368f44
commit
e4a08ce32d
@ -24,6 +24,12 @@ updates -> Removed col 'Log'
|
||||
|
||||
ms4_log -> Added
|
||||
|
||||
entrys -> ms4_highscoreentries
|
||||
games -> ms4_highscoregames
|
||||
ms4_highscoreentries.IP -> length = 41
|
||||
|
||||
|
||||
|
||||
updateslog -> new table
|
||||
<?php
|
||||
$rows=Yii::app()->db->createCommand('SELECT * FROM {{updates}}')->queryAll();
|
||||
|
@ -91,6 +91,12 @@ return ArrayX::merge(
|
||||
'blog/<id>' => 'blogPost/view/id/<id>',
|
||||
'blog/<id>/<name>' => 'blogPost/view/id/<id>',
|
||||
|
||||
'Highscores/list.php' => 'Highscores/list', // Compatibility
|
||||
'Highscores/insert.php' => 'Highscores/insert', // Compatibility
|
||||
'Highscores/update.php' => 'Highscores/update', // Compatibility
|
||||
'Highscores/list_top50.php' => 'Highscores/list_top50', // Compatibility
|
||||
'Highscores/getNewID.php' => 'Highscores/newID', // Compatibility
|
||||
|
||||
'downloads/details.php' => 'programs/index', // Compatibility
|
||||
'downloads/downloads.php' => 'programs/index', // Compatibility
|
||||
'downloads/<id>' => 'programs/view', // Compatibility
|
||||
|
@ -51,4 +51,9 @@ class APIController extends MSController
|
||||
|
||||
$this->render('update', ['data' => $data]);
|
||||
}
|
||||
|
||||
public function actionTest()
|
||||
{
|
||||
$this->render('test', []);
|
||||
}
|
||||
}
|
222
www/protected/controllers/HighscoresController.php
Normal file
222
www/protected/controllers/HighscoresController.php
Normal file
@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
class HighscoresController extends MSController //TODO-MS Test online if it all works
|
||||
{
|
||||
const ENTRYLIST_PAGESIZE = 20;
|
||||
|
||||
public $layout = false;
|
||||
|
||||
/**
|
||||
* @return array action filters
|
||||
*/
|
||||
public function filters()
|
||||
{
|
||||
return array(
|
||||
'accessControl',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the access control rules.
|
||||
* This method is used by the 'accessControl' filter.
|
||||
* @return array access control rules
|
||||
*/
|
||||
public function accessRules()
|
||||
{
|
||||
return array(
|
||||
array('allow',
|
||||
'users'=>array('*'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function actionInsert($gameid, $check, $name, $rand, $points)
|
||||
{
|
||||
if (! is_numeric($gameid))
|
||||
throw new CHttpException(400, 'Invalid Request');
|
||||
if (! is_numeric($points))
|
||||
throw new CHttpException(400, 'Invalid Request');
|
||||
|
||||
$entry = new HighscoreEntries();
|
||||
$entry->GAME_ID = $gameid;
|
||||
$entry->POINTS = $points;
|
||||
$entry->PLAYER = $name;
|
||||
$entry->PLAYERID = -1;
|
||||
$entry->CHECKSUM = $check;
|
||||
$entry->TIMESTAMP = time();
|
||||
$entry->IP = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if ($entry->checkChecksum($rand))
|
||||
{
|
||||
if ($entry->save())
|
||||
{
|
||||
$this->actionListEntries($gameid);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo 'Error while inserting';
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo 'Nice try !';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function actionList()
|
||||
{
|
||||
if (!isset($_GET["gameid"]))
|
||||
{
|
||||
$this->actionListGames();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->actionListEntries(intval(mysql_real_escape_string($_GET["gameid"])));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function actionListEntries($gameid)
|
||||
{
|
||||
if (! is_numeric($gameid))
|
||||
throw new CHttpException(400, 'Invalid Request - [gameid] must be an integer');
|
||||
|
||||
if (!isset($_GET["start"]))
|
||||
{
|
||||
$start = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$start = intval(mysql_real_escape_string($_GET["start"])) - 1;
|
||||
if ($start < 0)
|
||||
{
|
||||
$start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET["highlight"]))
|
||||
{
|
||||
$highlight= intval(mysql_real_escape_string($_GET["highlight"]));
|
||||
}
|
||||
else
|
||||
$highlight = 0;
|
||||
|
||||
$game = HighscoreGames::model()->findByPk($gameid);
|
||||
|
||||
$this->render('listentries',
|
||||
[
|
||||
'game' => $game,
|
||||
'start' => $start,
|
||||
'highlight' => $highlight,
|
||||
'pagesize' => self::ENTRYLIST_PAGESIZE,
|
||||
]);
|
||||
}
|
||||
|
||||
public function actionListGames()
|
||||
{
|
||||
$criteria = new CDbCriteria;
|
||||
$games = HighscoreGames::model()->findAll($criteria);
|
||||
|
||||
$this->render('listgames',
|
||||
[
|
||||
'games' => $games,
|
||||
]);
|
||||
}
|
||||
|
||||
public function actionUpdate($gameid, $check, $name, $nameid, $rand, $points)
|
||||
{
|
||||
if (! is_numeric($gameid))
|
||||
throw new CHttpException(400, 'Invalid Request');
|
||||
if (! is_numeric($nameid))
|
||||
throw new CHttpException(400, 'Invalid Request');
|
||||
if (! is_numeric($points))
|
||||
throw new CHttpException(400, 'Invalid Request');
|
||||
|
||||
$criteria = new CDbCriteria;
|
||||
$criteria->addCondition('GAME_ID = ' . $gameid);
|
||||
$criteria->addCondition('PLAYERID = ' . $nameid);
|
||||
|
||||
/* @var HighscoreEntries $entry */
|
||||
$entry = HighscoreEntries::model()->find($criteria);
|
||||
|
||||
if (is_null($entry))
|
||||
{
|
||||
$entry = new HighscoreEntries();
|
||||
$entry->GAME_ID = $gameid;
|
||||
$entry->POINTS = $points;
|
||||
$entry->PLAYER = $name;
|
||||
$entry->PLAYERID = -1;
|
||||
$entry->CHECKSUM = $check;
|
||||
$entry->TIMESTAMP = time();
|
||||
$entry->IP = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if ($entry->checkChecksum($rand))
|
||||
{
|
||||
if ($entry->save())
|
||||
{
|
||||
$this->actionListEntries($gameid);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo 'Error while inserting';
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo 'Nice try !';
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$entry->POINTS = $points;
|
||||
$entry->PLAYER = $name;
|
||||
$entry->CHECKSUM = $check;
|
||||
$entry->IP = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if ($entry->checkChecksum($rand))
|
||||
{
|
||||
$entry->update();
|
||||
$this->actionListEntries($gameid);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo 'Nice try !';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function actionList_Top50($gameid)
|
||||
{
|
||||
if (! is_numeric($gameid))
|
||||
throw new CHttpException(400, 'Invalid Request - [gameid] must be an integer');
|
||||
|
||||
$game = HighscoreGames::model()->findByPk($gameid);
|
||||
|
||||
$this->render('list_top50',
|
||||
[
|
||||
'game' => $game,
|
||||
]);
|
||||
}
|
||||
|
||||
public function actionNewID($gameid)
|
||||
{
|
||||
$connection=Yii::app()->db;
|
||||
$command=$connection->createCommand("SELECT MAX(PLAYERID)+1 AS NID FROM {{highscoreentries}} WHERE GAME_ID = $gameid");
|
||||
|
||||
$newid = $command->queryScalar();
|
||||
if ($newid < 1024) {
|
||||
$newid = 1024;
|
||||
}
|
||||
|
||||
print $newid;
|
||||
}
|
||||
}
|
149
www/protected/models/HighscoreEntries.php
Normal file
149
www/protected/models/HighscoreEntries.php
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This is the model class for table "{{highscoreentries}}".
|
||||
*
|
||||
* The followings are the available columns in table '{{highscoreentries}}':
|
||||
* @property integer $GAME_ID
|
||||
* @property string $POINTS
|
||||
* @property string $PLAYER
|
||||
* @property integer $PLAYERID
|
||||
* @property string $CHECKSUM
|
||||
* @property string $TIMESTAMP
|
||||
* @property string $IP
|
||||
* @property HighscoreGames $GAME
|
||||
*/
|
||||
class HighscoreEntries extends CActiveRecord
|
||||
{
|
||||
/**
|
||||
* @return string the associated database table name
|
||||
*/
|
||||
public function tableName()
|
||||
{
|
||||
return '{{highscoreentries}}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array validation rules for model attributes.
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
// NOTE: you should only define rules for those attributes that
|
||||
// will receive user inputs.
|
||||
return array(
|
||||
array('GAME_ID, PLAYER, CHECKSUM, TIMESTAMP, IP', 'required'),
|
||||
array('GAME_ID, PLAYERID', 'numerical', 'integerOnly'=>true),
|
||||
array('POINTS', 'length', 'max'=>20),
|
||||
array('PLAYER, IP', 'length', 'max'=>41),
|
||||
array('CHECKSUM', 'length', 'max'=>32),
|
||||
// The following rule is used by search().
|
||||
// @todo Please remove those attributes that should not be searched.
|
||||
array('GAME_ID, POINTS, PLAYER, PLAYERID, CHECKSUM, TIMESTAMP, IP', 'safe', 'on'=>'search'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array relational rules.
|
||||
*/
|
||||
public function relations()
|
||||
{
|
||||
// NOTE: you may need to adjust the relation name and the related
|
||||
// class name for the relations automatically generated below.
|
||||
return array(
|
||||
'GAME' =>
|
||||
[
|
||||
self::HAS_ONE,
|
||||
'HighscoreGames',
|
||||
[
|
||||
'ID' => 'GAME_ID'
|
||||
]
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array customized attribute labels (name=>label)
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
return array(
|
||||
'GAME_ID' => 'Game',
|
||||
'POINTS' => 'Points',
|
||||
'PLAYER' => 'Player',
|
||||
'PLAYERID' => 'Playerid',
|
||||
'CHECKSUM' => 'Checksum',
|
||||
'TIMESTAMP' => 'Timestamp',
|
||||
'IP' => 'IP',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of models based on the current search/filter conditions.
|
||||
*
|
||||
* Typical usecase:
|
||||
* - Initialize the model fields with values from filter form.
|
||||
* - Execute this method to get CActiveDataProvider instance which will filter
|
||||
* models according to data in model fields.
|
||||
* - Pass data provider to CGridView, CListView or any similar widget.
|
||||
*
|
||||
* @return CActiveDataProvider the data provider that can return the models
|
||||
* based on the search/filter conditions.
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
// @todo Please modify the following code to remove attributes that should not be searched.
|
||||
|
||||
$criteria=new CDbCriteria;
|
||||
|
||||
$criteria->compare('GAME_ID',$this->GAME_ID);
|
||||
$criteria->compare('POINTS',$this->POINTS,true);
|
||||
$criteria->compare('PLAYER',$this->PLAYER,true);
|
||||
$criteria->compare('PLAYERID',$this->PLAYERID);
|
||||
$criteria->compare('CHECKSUM',$this->CHECKSUM,true);
|
||||
$criteria->compare('TIMESTAMP',$this->TIMESTAMP,true);
|
||||
$criteria->compare('IP',$this->IP,true);
|
||||
|
||||
return new CActiveDataProvider($this, array(
|
||||
'criteria'=>$criteria,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the static model of the specified AR class.
|
||||
* Please note that you should have this exact method in all your CActiveRecord descendants!
|
||||
* @param string $className active record class name.
|
||||
* @return HighscoreEntries the static model class
|
||||
*/
|
||||
public static function model($className=__CLASS__)
|
||||
{
|
||||
return parent::model($className);
|
||||
}
|
||||
|
||||
//####################################
|
||||
//########### MY FUNCTIONS ###########
|
||||
//####################################
|
||||
|
||||
/**
|
||||
* @param $rand
|
||||
* @return string
|
||||
*/
|
||||
public function generateChecksum($rand)
|
||||
{
|
||||
$game = HighscoreGames::model()->findByPk($this->GAME_ID);
|
||||
/* @var $game HighscoreGames */
|
||||
|
||||
if ($this->PLAYERID >= 0)
|
||||
return md5($rand . $this->PLAYER . $this->PLAYERID . $this->POINTS . $game->ID);
|
||||
else
|
||||
return md5($rand . $this->PLAYER . $this->POINTS . $game->ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $rand
|
||||
* @return bool
|
||||
*/
|
||||
public function checkChecksum($rand)
|
||||
{
|
||||
return $this->generateChecksum($rand) == $this->CHECKSUM;
|
||||
}
|
||||
}
|
118
www/protected/models/HighscoreGames.php
Normal file
118
www/protected/models/HighscoreGames.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This is the model class for table "{{highscoregames}}".
|
||||
*
|
||||
* The followings are the available columns in table '{{highscoregames}}':
|
||||
* @property integer $ID
|
||||
* @property string $NAME
|
||||
* @property string $SALT
|
||||
* @property HighscoreEntries[] $ENTRIES
|
||||
*/
|
||||
class HighscoreGames extends CActiveRecord
|
||||
{
|
||||
/**
|
||||
* @return string the associated database table name
|
||||
*/
|
||||
public function tableName()
|
||||
{
|
||||
return '{{highscoregames}}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array validation rules for model attributes.
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
// NOTE: you should only define rules for those attributes that
|
||||
// will receive user inputs.
|
||||
return array(
|
||||
array('NAME, SALT', 'required'),
|
||||
array('NAME', 'length', 'max'=>63),
|
||||
array('SALT', 'length', 'max'=>6),
|
||||
// The following rule is used by search().
|
||||
array('ID, NAME', 'safe', 'on'=>'search'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array relational rules.
|
||||
*/
|
||||
public function relations()
|
||||
{
|
||||
// NOTE: you may need to adjust the relation name and the related
|
||||
// class name for the relations automatically generated below.
|
||||
return array(
|
||||
'ENTRIES' =>
|
||||
[
|
||||
self::HAS_MANY,
|
||||
'HighscoreEntries',
|
||||
[
|
||||
'GAME_ID' => 'ID'
|
||||
],
|
||||
'order'=>'POINTS DESC'
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array customized attribute labels (name=>label)
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
return array(
|
||||
'ID' => 'ID',
|
||||
'NAME' => 'Name',
|
||||
'SALT' => 'Salt',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of models based on the current search/filter conditions.
|
||||
*
|
||||
* Typical usecase:
|
||||
* - Initialize the model fields with values from filter form.
|
||||
* - Execute this method to get CActiveDataProvider instance which will filter
|
||||
* models according to data in model fields.
|
||||
* - Pass data provider to CGridView, CListView or any similar widget.
|
||||
*
|
||||
* @return CActiveDataProvider the data provider that can return the models
|
||||
* based on the search/filter conditions.
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
// @todo Please modify the following code to remove attributes that should not be searched.
|
||||
|
||||
$criteria=new CDbCriteria;
|
||||
|
||||
$criteria->compare('ID',$this->ID);
|
||||
$criteria->compare('NAME',$this->NAME,true);
|
||||
|
||||
return new CActiveDataProvider($this, array(
|
||||
'criteria'=>$criteria,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the static model of the specified AR class.
|
||||
* Please note that you should have this exact method in all your CActiveRecord descendants!
|
||||
* @param string $className active record class name.
|
||||
* @return HighscoreGames the static model class
|
||||
*/
|
||||
public static function model($className=__CLASS__)
|
||||
{
|
||||
return parent::model($className);
|
||||
}
|
||||
|
||||
//####################################
|
||||
//########### MY FUNCTIONS ###########
|
||||
//####################################
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getListLink()
|
||||
{
|
||||
return '/Highscores/list?gameid=' . $this->ID;
|
||||
}
|
||||
}
|
@ -49,7 +49,8 @@ class ProgramUpdates extends CActiveRecord
|
||||
'ProgramUpdatesLog',
|
||||
[
|
||||
'programname' => 'Name'
|
||||
]],
|
||||
]
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
8
www/protected/views/Highscores/list_top50.php
Normal file
8
www/protected/views/Highscores/list_top50.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/* @var $this HighscoresController */
|
||||
/* @var $game HighscoreGames */
|
||||
|
||||
for ($i = 0; $i < 50; $i++)
|
||||
{
|
||||
print($game->ENTRIES[$i]->POINTS . '||' . htmlentities($game->ENTRIES[$i]->PLAYER) . "\r\n");
|
||||
}
|
102
www/protected/views/Highscores/listentries.php
Normal file
102
www/protected/views/Highscores/listentries.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/* @var $this HighscoresController */
|
||||
/* @var $game HighscoreGames */
|
||||
/* @var $start int */
|
||||
/* @var $highlight int */
|
||||
/* @var $pagesize int */
|
||||
?>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>highscores</title>
|
||||
<style type="text/css">
|
||||
<!--
|
||||
body {
|
||||
background-color: #DDF;
|
||||
padding: 1em 1em 0em;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 2px 0px;
|
||||
}
|
||||
|
||||
caption {
|
||||
font-weight: bolder;
|
||||
text-decoration: underline;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #008;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover { text-decoration: none; }
|
||||
|
||||
#headline > td { text-decoration: underline; }
|
||||
#highlight {
|
||||
font-weight: bolder;
|
||||
background-color: #CCF;
|
||||
}
|
||||
-->
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<caption><?php echo $game->NAME; ?></caption>
|
||||
<tr id="headline" >
|
||||
<td>rank</td>
|
||||
<td>points</td>
|
||||
<td>name</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
$current = 0;
|
||||
foreach ($game->ENTRIES as $entry)
|
||||
{
|
||||
$current++;
|
||||
|
||||
if ($current >= $start && $current - $start <= $pagesize)
|
||||
{
|
||||
if ($current == $highlight)
|
||||
echo '<tr id="highlight">';
|
||||
else
|
||||
echo "<tr>";
|
||||
echo "<td>$current</td>";
|
||||
echo "<td>$entry->POINTS</td>";
|
||||
echo "<td>$entry->PLAYER</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
|
||||
$more = max(0, $start - $pagesize);
|
||||
$less = $start + $pagesize;
|
||||
|
||||
echo '<tr>';
|
||||
if ($start > 0)
|
||||
echo '<td><a href="' . "/Highscores/list?gameid=$game->ID&start=$more&highlight=$highlight" . '">[more points]</a></td>';
|
||||
else
|
||||
echo '<td></td>';
|
||||
echo '<td></td>';
|
||||
if ($start + $pagesize < count($game->ENTRIES))
|
||||
echo '<td><a href="' . "/Highscores/list?gameid=$game->ID&start=$less&highlight=$highlight" . '">[less points]</a></td>';
|
||||
else
|
||||
echo '<td></td>';
|
||||
echo '</tr>';
|
||||
|
||||
?>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
36
www/protected/views/Highscores/listgames.php
Normal file
36
www/protected/views/Highscores/listgames.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/* @var $this HighscoresController */
|
||||
/* @var $games HighscoreGames[] */
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>highscores</title>
|
||||
<style type="text/css">
|
||||
<!--
|
||||
body {
|
||||
background-color: #DDF;
|
||||
padding: 1em 1em 0em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #008;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover { text-decoration: none; }
|
||||
-->
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
foreach ($games as $game)
|
||||
{
|
||||
echo '<a href="' . $game->getListLink() . '">' . $game->NAME . '</a><br>' . "\r\n";
|
||||
}
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
7
www/protected/views/api/test.php
Normal file
7
www/protected/views/api/test.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
/* @var $this APIController */
|
||||
|
||||
/* @var $gm HighscoreGames */
|
||||
$gm = HighscoreGames::model()->findByPk(1);
|
||||
|
||||
echo nl2br(print_r( $gm->ENTRIES, true));
|
Loading…
Reference in New Issue
Block a user