rudimentary admin interface

This commit is contained in:
Tomáš 2017-03-07 09:38:47 +01:00
parent 6f59b479f3
commit 0c1fc8431a
16 changed files with 577 additions and 76 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
pile.db pile.db
vendor vendor
files

View file

@ -0,0 +1,26 @@
<div class="text document edit-form">
<form method="post" id="form" action="admin.php?action=edit_item<?= empty($doc) ? "" : "&item=" . $doc["ID"] ?>" enctype="multipart/form-data">
<strong>Title:</strong> <input type="text" name="Title" value="<?= empty($doc) ? "" : $doc["Title"] ?>"><br>
<strong>Author:</strong> <input type="text" name="Author" value="<?= empty($doc) ? "" : $doc["Author"] ?>"><br>
<strong>Date published:</strong> <input type="text" name="Published" value="<?= empty($doc) ? "" : $doc["Published"] ?>"><br>
<strong>Description:</strong><br>
<textarea name="Description" cols="120" rows="20">
<?= empty($doc) ? "" : $doc["Description"] ?>
</textarea><br>
<strong>File:</strong> <input type="file" name="upfile"><br>
<strong>URL:</strong> <input type="text" name="URL" value="<?= empty($doc) ? "" : $doc["URL"] ?>"><br>
<strong>Tags:</strong> <input type="text" name="Tags" value="<?
if ( !empty($doc) ){
$tags = [];
foreach ($doc["tags"] as $tag){
array_push($tags, $tag["Name"]);
}
echo implode(", ", $tags);
} else if ( !empty($_GET["tag"]) ) {
echo $_GET["tag"];
}
?>"><br>
<input type="hidden" name="ID" value="<?= empty($doc) ? "" : $doc["ID"] ?>" >
<input type="submit">
</form>
</div>

View file

@ -0,0 +1,30 @@
<?php if (isset($tag)): ?>
<div class="text tag-text">
<h1><?= $tag["Name"] ?></h1>
<p class="tag-desc"><?= $tag["Description"] ?></p>
<a class="tag-edit-link" href="?action=edit&tag=<?= $tag["ID"] ?>">[edit tag]</a>
</div>
<? endif; ?>
<?php if ($_GET["tag"] != "*" &&
$_GET["tag"] != "_"): ?>
<div class="text doc-item doc-new-item">
<a href="?action=new_item&tag=<?= $tag["Name"] ?>">
<div class="doc-item-text">
<h2>Upload a new document</h2>
</div>
</a>
</div>
<? endif; ?>
<?php foreach($docs as $doc): ?>
<div class="text doc-item">
<a class="doc-item-link" href="?action=remove&item=<?= $doc["ID"] ?>">[X]</a>
<a href="?action=edit_item&item=<?= $doc["ID"]?>">
<div class="doc-item-text">
<h2><?= $doc["Title"]?></h2>
<h3><?= $doc["Author"] . " " . $doc['date']?></h3>
</div>
</a>
</div>
<?php endforeach; ?>

View file

@ -0,0 +1,4 @@
<div class="text">
<p>Confirm deletion of <strong>"<?= $doc["Title"] ?>"</strong>:</p>
<a href="admin.php?action=remove&confirm=yes&item=<?= $doc["ID"] ?>&ret=<?= $_SERVER['HTTP_REFERER']; ?>" class="button">Remove from database (and drive)</a>
</div>

View file

@ -0,0 +1,3 @@
<div class="text">
<p class="intro">Handle with care.</p>
</div>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>pile ADMIN INTERFACE</title>
<link rel="stylesheet" type="text/css" href="assets/admin.css">
</head>
<body>
<div id="sidebar">
<div id="sidebar-head">
<h1><a href="admin.php">pile admin</a></h1>
</div>
<div id="sidebar-taglist">
<ul id="sidebar-taglist-overview">
<li id="sidebar-taglist-top"><a href="?tag=*">ALL (<?= $all_count ?>)</a></li>
<li id="sidebar-taglist-top"><a href="?tag=_">UNTAGGED (<?= $none_count ?>)</a></li>
</ul>
<ul>
<?
foreach($tags as $tag){
echo '<li><a href="?tag=' . $tag['name'] . "\">" . $tag['name'] . " (" . $tag['count'] . ")</a></li>";
}
?>
</ul>
</div>
</div>
<div id="content">
<?php echo $content ?>
</div>
<div id="login">
<form method="get">
<input type="hidden" name="action" value="logout">
<button type="submit" id="login-button">log out</button>
</form>
</div>
</body>
</html>

View file

@ -1,7 +1,9 @@
<?php if (isset($tag)): ?>
<div class="text tag-text"> <div class="text tag-text">
<h1><?= $tag["Name"] ?></h1> <h1><?= $tag["Name"] ?></h1>
<p class="tag-desc"><?= $tag["Description"] ?></p> <p class="tag-desc"><?= $tag["Description"] ?></p>
</div> </div>
<?php endif; ?>
<?php foreach($docs as $doc): ?> <?php foreach($docs as $doc): ?>
<div class="text doc-item"> <div class="text doc-item">

View file

@ -1,11 +1,13 @@
<div class="text document"> <div class="text document">
<h1><?= $doc["Title"] ?></h1> <h1><?= $doc["Title"] ?></h1>
<h2><?= $doc["Author"] ?></h2> <h2><?= $doc["Author"] ?></h2>
<?php if (!empty($doc["Published"])): ?>
<h3>Published: <?= $doc["Published"] ?></h3> <h3>Published: <?= $doc["Published"] ?></h3>
<?php endif; ?>
<h3 class="doc-taglist">Tags: <h3 class="doc-taglist">Tags:
<? <?
foreach($tags as $tag){ foreach($doc["tags"] as $tag){
echo '<li><a href="?tag=' . $tag["ID"] . "\">" . $tag["Name"] . "</a></li>"; echo '<li><a href="?tag=' . $tag["Name"] . "\">" . $tag["Name"] . "</a></li>";
} }
?> ?>
</h3> </h3>

View file

@ -33,10 +33,10 @@
</div> </div>
<div id="sidebar-taglist"> <div id="sidebar-taglist">
<ul> <ul>
<li id="sidebar-taglist-top"><a href="?tag=*">ALL (<?= $tag_count ?>)</a></li> <li id="sidebar-taglist-top"><a href="?tag=*">ALL (<?= $doc_count ?>)</a></li>
<? <?
foreach($tags as $tag){ foreach($tags as $tag){
echo '<li><a href="?tag=' . $tag['href'] . "\">" . $tag['name'] . " (" . $tag['count'] . ")</a></li>"; echo '<li><a href="?tag=' . $tag['name'] . "\">" . $tag['name'] . " (" . $tag['count'] . ")</a></li>";
} }
?> ?>
</ul> </ul>
@ -48,11 +48,17 @@
</div> </div>
<div id="login"> <div id="login">
<form method="post"> <?php if ($logged): ?>
<form method="get" action="admin.php">
<button type="submit" id="login-button">></button>
</form>
<? else: ?>
<form method="post" action="admin.php">
<input type="text" name="username" id="login-user"></input> <input type="text" name="username" id="login-user"></input>
<input type="password" name="password" id="login-pass"></input> <input type="password" name="password" id="login-pass"></input>
<button type="submit" id="login-button">></button> <button type="submit" id="login-button">></button>
</form> </form>
</div> <? endif; ?>
</div>
</body> </body>
</html> </html>

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<?php if (isset($redirect)): ?>
<meta http-equiv="refresh" content="1;URL=<?= $redirect ?>" />
<?php endif;?>
<title>The /-\ pile</title>
<link rel="stylesheet" type="text/css" href="assets/main.css">
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
display: table
}
body p {
font-size: 3rem;
text-align: center;
}
div {
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
</head>
<body>
<div>
<p>
<?= $text ?>
</p>
</div>
</body>
</html>

5
www/_templates/intro.php Normal file
View file

@ -0,0 +1,5 @@
<div class="text">
<p class="intro"> This site is the sdbs pile, where we upload the stuff we consider important to the larger conceptual and thematic landscape of what we do: "confronting apathy", inter-subjectivity, the human right to self-determination, counter-culture and such...</p>
<p class="intro czech">Tohle je hromádka zajímavýho materiálu co něco znamená v kontextu sdbs - budeme sem postupně dávat ty nejdůležitější nebo nejzajímavější věci, zatim se o tom ale nikde moc nešiřte.</p>
<p class="intro sign">/-\</p>
</div>

1
www/_util/.htaccess Normal file
View file

@ -0,0 +1 @@
Deny from all

199
www/_util/PileDB.php Normal file
View file

@ -0,0 +1,199 @@
<?php
class PileDB {
private $db;
function __construct() {
$this->db = new SQLite3("pile.db");
}
function prepare($statement){
return $this->db->prepare($statement);
}
function query($statement){
return $this->db->query($statement);
}
public function getDocCount(){
$ret_count = $this->db->query("SELECT count(ID) FROM Documents")->fetchArray(SQLITE3_NUM);
return $ret_count[0];
}
public function getTags(){
$tag_query = "SELECT
ID, Name, count(Document)
FROM
Tags t
LEFT OUTER JOIN
DocumentstoTags d ON t.ID = d.Tag
GROUP BY Name
ORDER BY count(Document) DESC, Name";
$tags_ret = $this->db->query($tag_query);
$tags = [];
while ($row = $tags_ret->fetchArray(SQLITE3_NUM)) {
array_push($tags, array(
'href' => $row[0],
'name' => $row[1],
'count' => $row[2]
));
}
return $tags;
}
public function fetchDoc($id){
$stmt_doc = $this->db->prepare("SELECT * FROM Documents WHERE ID = :id");
$stmt_doc->bindValue(":id", $id, SQLITE3_INTEGER);
$doc = $stmt_doc->execute()->fetchArray(SQLITE3_ASSOC);
$stmt_tags = $this->db->prepare("SELECT t.ID, t.Name FROM Tags t
JOIN DocumentsToTags dt ON t.ID = dt.Tag
JOIN Documents d on d.ID = dt.Document
WHERE d.ID = :id");
$stmt_tags->bindValue(":id", $id, SQLITE3_INTEGER);
$ret = $stmt_tags->execute();
$doc["tags"] = [];
while ($tag = $ret->fetchArray(SQLITE3_ASSOC)) {
array_push($doc["tags"], $tag);
}
return $doc;
}
public function listDocs(){
if (func_num_args() > 0){
$tag = func_get_arg(0);
if ($tag > 0 ) {
$stmt = $this->db->prepare("SELECT
ID, Title, Author, Published, URL
FROM
Documents d
LEFT OUTER JOIN
DocumentsToTags dt ON d.ID = dt.Document
WHERE Tag == :tag");
$stmt->bindValue(":tag", $tag, SQLITE3_INTEGER);
} else {
$stmt = $this->db->prepare("SELECT
ID, Title, Author, Published, URL
FROM
Documents d
LEFT OUTER JOIN
DocumentsToTags dt ON d.ID = dt.Document
WHERE dt.Document IS NULL");
}
$doc_ret = $stmt->execute();
} else {
$query = "SELECT ID, Title, Author, Published, URL FROM Documents";
$doc_ret = $this->db->query($query);
}
$docs = [];
while ($doc = $doc_ret->fetchArray(SQLITE3_ASSOC)) {
$doc['date'] = empty($doc["Published"]) ? "" : "(" . $doc["Published"] . ")";
array_push($docs, $doc);
}
return $docs;
}
public function updateDoc($id, $title, $author, $description, $published, $url, $tag_ids){
if ( empty($id) ){
$stmt = $this->db->prepare("INSERT INTO Documents
(ID, Title, Author, Description, Published, URL)
VALUES
(NULL, :title, :author, :description, :published, :url)");
} else {
$stmt = $this->db->prepare("UPDATE Documents SET
Title=:title,
Author=:author,
Description=:description,
Published=:published,
URL=:url
WHERE ID = :id");
$stmt->bindValue(":id", $id, SQLITE3_INTEGER);
}
$stmt->bindValue(":title", $title, SQLITE3_TEXT);
$stmt->bindValue(":author", $author, SQLITE3_TEXT);
$stmt->bindValue(":description", $description, SQLITE3_TEXT);
$stmt->bindValue(":published", $published, SQLITE3_TEXT);
$stmt->bindValue(":url", $url, SQLITE3_TEXT);
$stmt->execute();
if ( empty($id) ){
$id = $this->db->lastInsertRowid();
}
if ( ! empty($id) ){
$delete_stmt = $this->db->prepare("DELETE FROM DocumentsToTags
WHERE Document = :id");
$delete_stmt->bindValue(":id", $id, SQLITE3_INTEGER);
$delete_stmt->execute();
}
foreach ($tag_ids as $tag){
$tag_stmt = $this->db->prepare("INSERT INTO DocumentsToTags ('Document', 'Tag')
VALUES (:doc, :tag)");
$tag_stmt->bindValue("doc", $id, SQLITE3_INTEGER);
$tag_stmt->bindValue("tag", $tag, SQLITE3_INTEGER);
$tag_stmt->execute();
}
}
public function removeDoc($id){
$doc_stmt = $this->db->prepare("DELETE FROM Documents
WHERE ID = :id");
$doc_stmt->bindValue("id", $id, SQLITE3_INTEGER);
$doc_stmt->execute();
$tag_stmt = $this->db->prepare("DELETE FROM DocumentsToTags
WHERE Document = :id");
$tag_stmt->bindValue("id", $id, SQLITE3_INTEGER);
$tag_stmt->execute();
}
public function findTag($name){
$stmt = $this->db->prepare("SELECT * FROM Tags WHERE Name == :name");
$stmt->bindValue(":name", $name, SQLITE3_TEXT);
return $stmt->execute()->fetchArray(SQLITE3_ASSOC);
}
public function fetchTag($tag){
$stmt = $this->db->prepare("SELECT * FROM Tags WHERE ID == :tag");
$stmt->bindValue(":tag", $tag, SQLITE3_INTEGER);
return $stmt->execute()->fetchArray(SQLITE3_ASSOC);
}
public function updateTag($id, $name, $description, $parent){
if (empty($id)){
$stmt = $this->db->prepare("INSERT INTO Tags
(ID, Name, Description, Parent)
VALUES
(NULL, :name, :description, :parent");
} else {
$stmt = $this->db->prepare("UPDATE Tags SET
Name=:name,
Description=:description,
Parent=:Parent
WHERE ID = :id");
$stmt->bindValue(":id", $id, SQLITE3_INTEGER);
}
$stmt->bindValue(":name", $name, SQLITE3_TEXT);
$stmt->bindValue(":description", $description, SQLITE3_TEXT);
$stmt->bindValue(":parent", $parent, SQLITE3_INTEGER);
return $stmt->execute();
}
public function authenticate($username, $password){
$stmt = $this->db->prepare("SELECT
*
FROM
Users
WHERE
Username = :username");
$stmt->bindValue(":username", $username, SQLITE3_TEXT);
$auth_ret = $stmt->execute();
$auth = $auth_ret->fetchArray(SQLITE3_ASSOC);
if (password_verify($password, $auth["Password"])){
return $auth["ID"];
} else {
return -1;
}
}
}
?>

49
www/_util/Uploader.php Normal file
View file

@ -0,0 +1,49 @@
<?
class Uploader {
public function handle($files, $dir){
if ( is_array($files['upfile']['error']) ) {
throw new RuntimeException('Invalid parameters.');
}
switch ($files['upfile']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('No file sent.');
case UPLOAD_ERR_INI_SIZE:
throw new RuntimeException('Exceeded INI filesize limit.');
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Exceeded form filesize limit.');
default:
throw new RuntimeException('Unknown errors.');
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$finfo->file($files['upfile']['tmp_name']),
array(
'pdf' => 'application/pdf',
'zip' => 'application/zip',
'rar' => 'application/rar'
),
true
)) {
throw new RuntimeException('Invalid file format.');
}
$name = basename($files['upfile']['name']);
$name = preg_replace('/[^\x20-\x7E]/','', $name);
if ($name != ".htaccess"){
if (!move_uploaded_file(
$files['upfile']['tmp_name'],
$dir . $name)) {
throw new RuntimeException('Failed to move uploaded file.');
}
} else {
throw new RuntimeException('Invalid filename.');
}
return $name;
}
}
?>

139
www/admin.php Normal file
View file

@ -0,0 +1,139 @@
<?php
require '_templates/Template.php';
require '_util/PileDB.php';
require '_util/Uploader.php';
$db = new PileDB();
$uploader = new Uploader();
session_start();
if (isset($_SESSION['ID'])){
$page = new Template();
if (isset($_GET["action"])){
switch ($_GET["action"]){
case "new_tag":
$content = $page->render("admin_doc_edit.php");
break;
case "edit_tag":
$content = $page->render("admin_doc_edit.php");
break;
case "new_item":
$content = $page->render("admin_doc_edit.php");
break;
case "edit_item":
if (isset($_POST["Title"])){
if ( !empty($_FILES['upfile']['name']) ){
try {
$url = "http://pile.sdbs.cz/files/" . rawurlencode($uploader->handle($_FILES, "files/"));
} catch (RuntimeException $ex){
$page->text = $ex->getMessage();
echo $page->render('full_text.php');
return;
}
} else {
$url = $_POST["URL"];
}
$doc_tags = [];
foreach (explode(",", $_POST["Tags"]) as $tagName){
$tagName = trim($tagName);
$tag = $db->findTag($tagName);
if (!in_array($tag["ID"], $doc_tags)){
array_push($doc_tags, $tag["ID"]);
}
}
$db->updateDoc(
$_POST["ID"],
$_POST["Title"],
$_POST["Author"],
$_POST["Description"],
$_POST["Published"],
$url,
$doc_tags
);
}
if ( !empty($_GET["item"]) ) {
$page->doc = $db->fetchDoc($_GET["item"]);
$content = $page->render("admin_doc_edit.php");
}
break;
case "remove":
if ( ! empty($_GET["confirm"]) && $_GET["confirm"] == "yes"){
$db->removeDoc($_GET["item"]);
$page->text = "Document deleted.";
$page->redirect = $_GET["ret"];
echo $page->render("full_text.php");
return;
} else {
$page->doc = $db->fetchDoc($_GET["item"]);
$content = $page->render("admin_doc_remove.php");
}
break;
case "logout":
unset($_SESSION["ID"]);
$page->text = "See you.";
$page->redirect = "/";
echo $page->render("full_text.php");
return;
}
} elseif (isset($_GET["tag"])) {
$doc_list_template = new Template();
if ($_GET["tag"] == "*"){
$docs = $db->listDocs();
} elseif ($_GET["tag"] == "_") {
$docs = $db->listDocs(-1);
} else {
$tag = $db->findTag($_GET["tag"]);
$docs = $db->listDocs($tag["ID"]);
$doc_list_template->tag = $db->fetchTag($tag["ID"]);
}
$doc_list_template->docs = $docs;
$content = $doc_list_template->render('admin_doc_listing.php');
} else {
$intro_template = new Template();
$content = $intro_template->render('admin_intro.php');
}
$all_count = $db->getDocCount();
$ret_count = $db->query("SELECT
count(ID)
FROM
Documents d
LEFT OUTER JOIN
DocumentstoTags dt ON dt.Document = d.ID
WHERE dt.Document IS NULL")->fetchArray(SQLITE3_NUM);
$none_count = $ret_count[0];
$tags = $db->getTags();
$page->all_count = $all_count;
$page->none_count = $none_count;
$page->tags = $tags;
$page->content = $content;
echo $page->render('admin_wrap.php');
} else {
$page = new Template();
if (isset($_POST['username']) && isset($_POST['password'])){
$ret_id = $db->authenticate($_POST["username"], $_POST["password"]);
if ($ret_id > 0){
$_SESSION['ID'] = $ret_id;
$page->text = "You have logged in successfully.";
$page->redirect = "admin.php";
} else {
$page->text = "Username and/or password incorrect.";
$page->redirect = "/";
}
} else {
$page->text = "Please log in before accessing this page.";
$page->redirect = "/";
}
echo $page->render('full_text.php');
}
?>

View file

@ -1,76 +1,25 @@
<?php <?php
require '_templates/Template.php'; require '_templates/Template.php';
require '_util/PileDB.php';
$db = new PileDB();
$db = new SQLite3("pile.db"); session_start();
$ret_count = $db->query("SELECT count(ID) FROM Documents")->fetchArray(SQLITE3_NUM);
$count = $ret_count[0];
$tag_query = "SELECT
ID, Name, count(Document)
FROM
Tags t
LEFT OUTER JOIN
DocumentstoTags d ON t.ID = d.Tag
GROUP BY Name
ORDER BY count(Document) DESC, Name";
$tags_ret = $db->query($tag_query);
$tags = [];
while ($row = $tags_ret->fetchArray(SQLITE3_NUM)) {
array_push($tags, array(
'href' => $row[0],
'name' => $row[1],
'count' => $row[2]
));
}
if (isset($_GET["item"])) { if (isset($_GET["item"])) {
$stmt_doc = $db->prepare("SELECT * FROM Documents WHERE ID = :id"); $doc = $db->fetchDoc($_GET["item"]);
$stmt_doc->bindValue(":id", $_GET["item"], SQLITE3_INTEGER);
$doc = $stmt_doc->execute()->fetchArray(SQLITE3_ASSOC);
$stmt_tags = $db->prepare("SELECT t.ID, t.Name FROM Tags t
JOIN DocumentsToTags dt ON t.ID = dt.Tag
JOIN Documents d on t.ID = dt.Document
WHERE d.ID = :id");
$stmt_tags->bindValue(":id", $_GET["item"], SQLITE3_INTEGER);
$ret = $stmt_tags->execute();
$doc_tags = [];
while ($tag = $ret->fetchArray(SQLITE3_ASSOC)) {
array_push($doc_tags, $tag);
}
$doc_template = new Template(); $doc_template = new Template();
$doc_template->doc = $doc; $doc_template->doc = $doc;
$doc_template->tags = $doc_tags;
$content = $doc_template->render('front_doc_overview.php'); $content = $doc_template->render('front_doc_overview.php');
} elseif (isset($_GET["tag"])) { } elseif (isset($_GET["tag"])) {
if ($_GET["tag"] == "*"){
$query = "SELECT ID, Title, Author, Published, URL FROM Documents";
$doc_ret = $db->query($query);
} else {
$stmt = $db->prepare("SELECT
ID, Title, Author, Published, URL
FROM
Documents d
LEFT OUTER JOIN
DocumentsToTags t ON d.ID = t.Document
WHERE Tag == :tag");
$stmt->bindValue(":tag", $_GET["tag"], SQLITE3_INTEGER);
$doc_ret = $stmt->execute();
}
$docs = [];
while ($doc = $doc_ret->fetchArray(SQLITE3_ASSOC)) {
$doc['date'] = empty($doc["Published"]) ? "" : "(" . $doc["Published"] . ")";
array_push($docs, $doc);
}
$stmt = $db->prepare("SELECT Name, Description FROM Tags WHERE ID == :tag");
$stmt->bindValue(":tag", $_GET["tag"], SQLITE3_INTEGER);
$doc_list_template = new Template(); $doc_list_template = new Template();
$doc_list_template->tag = $stmt->execute()->fetchArray(SQLITE3_ASSOC); if ($_GET["tag"] == "*"){
$docs = $db->listDocs();
} else {
$tag = $db->findTag($_GET["tag"]);
$docs = $db->listDocs($tag["ID"]);
$doc_list_template->tag = $db->fetchTag($tag["ID"]);
}
$doc_list_template->docs = $docs; $doc_list_template->docs = $docs;
$content = $doc_list_template->render('front_doc_listing.php'); $content = $doc_list_template->render('front_doc_listing.php');
} else { } else {
@ -79,8 +28,9 @@ if (isset($_GET["item"])) {
} }
$page = new Template(); $page = new Template();
$page->tag_count = $count; $page->doc_count = $db->getDocCount();
$page->tags = $tags; $page->tags = $db->getTags();
$page->content = $content; $page->content = $content;
$page->logged = isset($_SESSION["ID"]);
echo $page->render('front_wrap.php'); echo $page->render('front_wrap.php');
?> ?>