ImgHost first commit

This commit is contained in:
Taylor Courage 2023-12-16 09:37:58 -05:00
parent b1664f0af3
commit 1c071d8215
11 changed files with 337 additions and 369 deletions

View File

@ -1,21 +1,46 @@
# Meme Machine
# ImgHost
## Current Version: v0.3.6-beta
## Current Version: v0.4.0-beta
## Overview
The Meme Machine is a semi-private repository of memes that can be easily searched, indexed, and accessed anywhere at any time. Content is not limited to memes, either. Reaction images and gifs, cancerous stuff, friends/members-related stuff, fails, replies, anything, really. It's mostly here for organization and access. This is the source code.
ImgHost is a self-hosted image hosting software. Upload your images and share them with the world!
## Features
Meme Machine is able to take in images up to 75MiB in size, and stores some basic metadata in a SQL database.
ImgHost is designed to be fully configurable for the ultimate photo sharing experience. User-configurable options range from changing the allowed file size (dependant on your web server), to how files are named and stored, layouts and fonts, to mundane things like table names in your databases.
When a user uploads a meme, they are able to specify a name/description of the meme, as well as a category to help easily organize the memes.
Many features are planned, so stay tuned!
## Installation
ImgHost is designed to be as plug-and-play as possible. You will need a:
- Webserver, that supports
- PHP, coupled with
- MySQL or MariaDB
You will also want to configure your server to allow for larger file uploads.
First you will want to either clone this repo, or download the zip (and unzip it to) a directory that your web server software has full read/write access to.
Next you need to edit `config/db_config.php` and add your SQL credentials.
If you plan on using a custom domain, you can also edit `config/configuation.php` to include that.
Then you will need to navigate to `http://imghost.local/config/` or your custom domain to finish database and folder setup. Simply change the available options based on your preferences and click `Setup databases`, once complete you will be redirected to the home page where your server should be ready to use!
Users are then able to browse the memes using the categories, and in future versions will be able to search memes by their name/description.
## Changelog
### v0.4.0-beta - Dec 16, 2023
- Full re-factor to convert the old "Meme Machine" to a general-purpose, plug-and-play image hosting software.
- Removed categories, removed names. These will come back as configurable options.
- Added user-configurable CSS for some pizzazz.
- Added private toggle, to prevent images from being shown in the public gallery.
- Photos are given a completely random 6-byte ID instead of storing full file names.
### v0.3.6-beta - April 7, 2016
- Fixed critical issue where searches wouldn't work

View File

@ -1,80 +0,0 @@
<?php
include '../credentials.php';
//Setting up the webpage
echo '<html>';
echo '<head>';
echo '<title>Browse - Meme Machine</title>';
echo '<style>img {width:100%;max-width:300px;;padding:7px;}</style>';
echo '</head>';
echo '<body>';
echo '<div align="center">';
echo '<h1>Meme Machine</h1>';
$dir = "../uploads"; // Directory for file uploads
$fileType = array( // Types of files that are thumbnail'd
'jpg',
'jpeg',
'png',
'gif'
);
// MySQL server connection info
$count = 0;
//Connect to MySQL
$conn = mysqli_connect($servername, $username, $password, $dbname);
if (!$conn) {
die ("CONNECTION FAIL " .mysqli_connect_error());
} else {
}
if(isset($_POST["browse"]) && $_POST["memeCategory"]) { // If "Browse" is pressed
echo '<h2>Category: ' . $_POST["memeCategory"] . '</h2>';
$memeCategory = mysqli_real_escape_string($conn, $_POST["memeCategory"]);
$browse = "SELECT * FROM memes WHERE category = \"$memeCategory\"";
$result = mysqli_query($conn, $browse);
if (mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_assoc($result)) {
$count++;
echo '<img src="' . $dir . '/' . $row["fileName"] . '" alt="' . $row["name"] . '" />';
if ($count == 3) {
echo '<br />';
}
}
} else {
echo "This category is empty. Please choose another or start uploading";
?>
<p>&nbsp;</p>
<a href="./">Back</a>
<?php
}
} else { // If 'Browse All' is pressed
echo '<h2>Category: all</h2>';
$browse = "SELECT * FROM memes";
$result = mysqli_query($conn, $browse);
//browse all code goes here
if (mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_assoc($result)) {
$count++;
echo '<img src="' . $dir . '/' . $row["fileName"] . '" alt="' . $row["name"] . '" />';
if ($count == 3) { // Change this number based on how many pictures across you want on the page
echo '<br />';
}
}
} else {
echo "Shit, something broke. Try again! Or if the problem persists contact the system administrator";
?>
<p>&nbsp;</p>
<a href="./">Back</a>
<?php
}
}
echo '</body>';
echo '</html>';
?>

View File

@ -1,105 +1,53 @@
<html>
<head>
<title>Meme Machine Gallery</title>
</head>
<?php
include '../config/db_config.php';
include '../config/configuration.php';
<body>
<div align="center">
<h1>Meme Machine</h1>
<h2>Search by name</h2>
<form action="query.php" method="post">
<input type="text" name="term" />
<input type="submit" name="search" value="Search" />
</form>
</div>
<div align="center">
<h2>- OR -</h2>
</div>
<div align="center">
<form action="browse.php" method="post">
<h4>Browse by category:
<select name="memeCategory">
<option value="" default>-- Choose A Category --</option>
<option value="alcohol">Alcohol</option>
<option value="animals">Animals</option>
<option value="beashame">Be A Shame If...</option>
<option value="canada">Canada</option>
<option value="cats">Cats</option>
<option value="coolstory">Cool Story (Bro)</option>
<option value="cringe">Cringe</option>
<option value="dealwithit">Deal With It</option>
<option value="didntread">Didn't Read</option>
<option value="disgust">Disgust</option>
<option value="downvote">Downvote</option>
<option value="boners">Erections/Fapping</option>
<option value="fail">Fail</option>
<option value="frank">Filthy Frank</option>
<option value="fiteme">Fite Me</option>
<option value="fuck">Fuck!</option>
<option value="fuckthis">Fuck This</option>
<option value="fuckyeah">Fuck Yeah</option>
<option value="fuckyou">Fuck You</option>
<option value="games">Games/Gaming</option>
<option value="guns">Guns</option>
<option value="haters">Haters Gonna Hate</option>
<option value="history">History</option>
<option value="hitler">Hitler/Nazi's/Holocaust</option>
<option value="impressed">Impressed</option>
<option value="infograph">Infograph</option>
<option value="insults">Insults</option>
<option value="joancornella">Joan Cornella</option>
<option value="killyourself">Kill Yourself</option>
<option value="laugh">Laugh/Laughing</option>
<option value="lolwut">lolwut</option>
<option value="memberspecific">Member-Specific</option>
<option value="meta">Meta</option>
<option value="military">Military</option>
<option value="mindblown">Mind Blown</option>
<option value="misc">Misc</option>
<option value="morbid">Morbid</option>
<option value="murica">'Murica</option>
<option value="muslims">Muslims/Terrorists</option>
<option value="nope">Nope</option>
<option value="nsfw">NSFW</option>
<option value="partyhard">Party Hard</option>
<option value="pepe">Pepe</option>
<option value="pokemon">Pokemon</option>
<option value="polandball">Poland Ball</option>
<option value="political">Political</option>
<option value="racist">Racist</option>
<option value="ricky">Ricky Comic</option>
<option value="rip">RIP in piece</option>
<option value="sanic">Sanic</option>
<option value="scary">Scary</option>
<option value="shocked">Shocked</option>
<option value="spongebob">Spongebob</option>
<option value="stfu">STFU/Shut Up</option>
<option value="thinking">Thinking</option>
<option value="thissucks">This Sucks</option>
<option value="umad">UMAD BRO?</option>
<option value="upvote">Upvote</option>
<option value="visser">Visser</option>
<option value="wtf">WTF</option>
</select>
</h4>
<br />
<input type="submit" name="browse" value="Browse" />
<input type="submit" name="browseAll" value="Browse All" />
</form>
<?
</div>
</body>
//Setting up the webpage
echo '<html>';
echo '<head>';
echo '<link rel="stylesheet" href="../config/' . $stylesheet . '">';
echo '<meta charset="UTF-8">';
echo '<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">';
echo '<title>Browse - ' . $serverName . '</title>';
echo '</head>';
echo '<body class="background">';
echo '<div class="layout" align="center">';
echo '<h1>' . $serverName . '</h1>';
</html>
$count = 0;
//Connect to MySQL
$conn = mysqli_connect($sqlServer, $sqlUsername, $sqlPassword, $dbname);
if (!$conn) {
die ("CONNECTION FAIL " . mysqli_connect_error());
} else {
}
echo '<h3>All public photos</h3>';
$browse = "SELECT * FROM $tablename WHERE isPrivate = 0";
$result = mysqli_query($conn, $browse);
//browse all code goes here
if (mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_assoc($result)) {
$file = $row["imgID"] . "." . $row["fileType"];
$count++;
echo '<a href="../' . $targetDir . $file . '"><img src="../' . $targetDir . $file . '" alt="' . $row["imgID"] . '" /></a>';
if ($count == 3) { // Change this number based on how many pictures across you want on the page
echo '<br />';
}
}
} else {
echo "Shit, something broke. Try again! Or if the problem persists contact the system administrator";
?>
<p>&nbsp;</p>
<a href="./">Home</a>
<?php
}
echo '</body>';
echo '</html>';
?>

View File

@ -1,68 +0,0 @@
<?php
include '../credentials.php';
$dir = "../uploads"; // Directory for file uploads
//Setting up the webpage
echo '<html>';
echo '<head>';
echo '<title>Browse - Meme Machine</title>';
echo '<style>img {width:100%;max-width:300px;;padding:7px;}</style>';
echo '</head>';
echo '<body>';
echo '<div align="center">';
echo '<h1>Meme Machine</h1>';
$count = 0;
//Connect to MySQL
$conn = mysqli_connect($servername, $username, $password, $dbname);
if (!$conn) {
die ("CONNECTION FAIL " .mysqli_connect_error());
} else {
}
$words = $_POST['term'];
$parts=explode(" ",trim($words));
$clauses=array();
foreach ($parts as $part){
$clauses[]="'%" . $part . "%'";
}
$clause .= implode(' OR ' ,$clauses);
$sql = "SELECT * FROM memes WHERE name LIKE $clause OR category LIKE $clause";
if(isset($_POST['search']) && $words){
echo "You searched for: " . $words . "<br />";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_assoc($result)) {
$count++;
echo '<img src="' . $dir . '/' . $row["fileName"] . '" alt="' . $row["name"] . '" />';
if ($count == 3) {
echo '<br />';
}
}
} else {
echo "Your search returned no results. Please go back and try again";
?>
<p>&nbsp;</p>
<a href="./">Back</a>
<?php
}
} else {
?>
<h1>Please go back and enter a search query</h1>
<br />
<br />
<a href="./">Back</a>
<?php
}
echo '<br>';
echo '<a href="./">Back</a>';
echo '</body>';
echo '</html>';
?>

35
config/configuration.php Normal file
View File

@ -0,0 +1,35 @@
<?php
/************ CONFIGURATION FILE ***********/
/** This file is used for general server **/
/** configuration. These values may be **/
/** changed as necessary to suit your **/
/** requirements. **/
// The name that will appear on the pages of the server
$serverName = "ImgHost";
$serverHostname = "imghost.local";
// Directory where the images will be stored
$targetDir = "uploads/";
// Filename of the style sheet (found in config foler)
$stylesheet = "style.css";
// Supported file types
$supportedFileTypes = array (
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/avif",
"image/apng",
"image/svg"
);
?>

23
config/db_config.php Normal file
View File

@ -0,0 +1,23 @@
<?php
/*********** SQL CONFIGURATION ***********/
/** Ensure you have the correct username **/
/** and password for your SQL server. **/
/** **/
/** If you plan on running multiple **/
/** instances of this program, you will **/
/** need to select a different 'dbname' **/
/** and/or 'tablename' for each instance. **/
// Connection Settings
$sqlServer = "127.0.0.1";
$sqlUsername = "username";
$sqlPassword = "password";
// Database Settings
$dbname = "imghost_data";
$tablename = "images";
?>

36
config/index.php Normal file
View File

@ -0,0 +1,36 @@
<?php
include './configuration.php';
include './db_config.php';
?>
<html>
<head>
<link rel="stylesheet" href="./<?php echo $stylesheet; ?>">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title><?php echo $serverName; ?> Config</title>
</head>
<body class="background">
<div class="layout" align="center">
<h1><?php echo $serverName; ?></h1>
<br />
<form action="initial_setup.php" method="post" enctype="multipart/form-data">
<br />
<p><input type="submit" value="Setup databases" name="submit" /></p>
</form>
</div>
<div align="center">
<br />
<br />
<hr>
<br />
</div>
<div align="center">
<br />
<br />
<br />
<h6>ImgHost (v0.4.0-beta)</h6>
</div>
</html>

67
config/initial_setup.php Normal file
View File

@ -0,0 +1,67 @@
<?php
include './db_config.php';
include './configuration.php';
echo "Configuration started...<br>";
echo "$sqlServer<br>";
echo "$sqlUsername<br>";
echo "$sqlPassword<br>";
$conn = mysqli_connect($sqlServer, $sqlUsername, $sqlPassword);
echo "Sql connection attempt<br>";
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "Sql connection successful!<br>";
// Create the database, if it doesn't exist
$dbSetup = "CREATE DATABASE IF NOT EXISTS $dbname";
echo "Preparing database with command - " . $dbSetup . "<br>";
if (mysqli_query($conn, $dbSetup)) {
echo "Database successfully prepared!<br>";
} else {
echo "Database Error " . $sql . "<br>" . mysqli_error($conn);
}
echo "Reconnecting to new database...<br>";
$conn = mysqli_connect($sqlServer, $sqlUsername, $sqlPassword, $dbname);
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "Sql connection successful!<br>";
// Create the table, if doesn't exit
$tableSetup = "CREATE TABLE IF NOT EXISTS $tablename (
imgID CHAR(12),
fileType VARCHAR(4),
mimeType VARCHAR(20),
isPrivate BOOLEAN,
uploadDate DATETIME DEFAULT CURRENT_TIMESTAMP,
editDate DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)";
echo "Preparing table with command - " . $tableSetup . "<br>";
if (mysqli_query($conn, $tableSetup)) {
echo "Table successfully prepared!<br>";
} else {
echo "Database Error " . $sql . "<br>" . mysqli_error($conn);
}
echo "Preparing images folder...<br>";
$directory = "../" . $targetDir;
echo $directory. "<br>";
mkdir ($directory);
echo "Initial setup complete! Enjoy!<br>"
?>
<a href="../">Homepage</a>

40
config/style.css Normal file
View File

@ -0,0 +1,40 @@
.background {
background-attachment: fixed;
background: linear-gradient(237deg, #0000ffd2, #1eff009a, #7d00a7);
background-size: 500% 500%;
-webkit-animation: AnimationName 45s ease infinite;
-moz-animation: AnimationName 45s ease infinite;
animation: AnimationName 45s ease infinite;
}
@-webkit-keyframes AnimationName {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
@-moz-keyframes AnimationName {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
@keyframes AnimationName {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
.layout {
max-width: 900px;
margin: auto;
font-family: 'Calibri';
}
h1,h2,h3,h4,h5,h6,p {
text-align: center;
}
img {
width:100%;
max-width:300px;
padding:7px;
}

107
index.php
View File

@ -1,108 +1,41 @@
<!--
The Meme Machine - An open source image databasing system
ImgHost - An open source image databasing and hosting system
A project by Taylor Courage (http://taylorcourage.net)
Latest Release: v0.3.6-beta (April 7, 2016)
Latest Release: v0.4.0-beta (Dec 16, 2023)
-->
<?php
include './config/configuration.php';
?>
<html>
<head>
<title>Meme Machine</title>
<link rel="stylesheet" href="./config/<?php echo $stylesheet; ?>">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title><?php echo $serverName; ?></title>
<style>
layout {
width:950px;
}
</style>
</head>
<body>
<div id="layout" align="center">
<h1>The Meme Machine</h1>
<body class="background">
<div class="layout" align="center">
<h1><?php echo $serverName; ?></h1>
<br />
<form action="upload.php" method="post" enctype="multipart/form-data">
<p>Name/Description of meme: <input type="input" name="memeName" id="memeName" /></p>
<h4>Category:
<select name="memeCategory">
<option value="" default>-- Choose A Category --</option>
<option value="alcohol">Alcohol</option>
<option value="animals">Animals</option>
<option value="beashame">Be A Shame If...</option>
<option value="canada">Canada</option>
<option value="cats">Cats</option>
<option value="coolstory">Cool Story (Bro)</option>
<option value="cringe">Cringe</option>
<option value="dealwithit">Deal With It</option>
<option value="didntread">Didn't Read</option>
<option value="disgust">Disgust</option>
<option value="downvote">Downvote</option>
<option value="boners">Erections/Fapping</option>
<option value="fail">Fail</option>
<option value="frank">Filthy Frank</option>
<option value="fiteme">Fite Me</option>
<option value="fuck">Fuck!</option>
<option value="fuckthis">Fuck This</option>
<option value="fuckyeah">Fuck Yeah</option>
<option value="fuckyou">Fuck You</option>
<option value="games">Games/Gaming</option>
<option value="guns">Guns</option>
<option value="haters">Haters Gonna Hate</option>
<option value="history">History</option>
<option value="hitler">Hitler/Nazi's/Holocaust</option>
<option value="impressed">Impressed</option>
<option value="infograph">Infograph</option>
<option value="insults">Insults</option>
<option value="joancornella">Joan Cornella</option>
<option value="killyourself">Kill Yourself</option>
<option value="laugh">Laugh/Laughing</option>
<option value="lolwut">lolwut</option>
<option value="memberspecific">Member-Specific</option>
<option value="meta">Meta</option>
<option value="military">Military</option>
<option value="mindblown">Mind Blown</option>
<option value="misc">Misc</option>
<option value="morbid">Morbid</option>
<option value="murica">'Murica</option>
<option value="muslims">Muslims/Terrorists</option>
<option value="nope">Nope</option>
<option value="nsfw">NSFW</option>
<option value="partyhard">Party Hard</option>
<option value="pepe">Pepe</option>
<option value="pokemon">Pokemon</option>
<option value="polandball">Poland Ball</option>
<option value="political">Political</option>
<option value="racist">Racist</option>
<option value="ricky">Ricky Comic</option>
<option value="rip">RIP in piece</option>
<option value="sanic">Sanic</option>
<option value="scary">Scary</option>
<option value="shocked">Shocked</option>
<option value="spongebob">Spongebob</option>
<option value="stfu">STFU/Shut Up</option>
<option value="thinking">Thinking</option>
<option value="thissucks">This Sucks</option>
<option value="umad">UMAD BRO?</option>
<option value="upvote">Upvote</option>
<option value="visser">Visser</option>
<option value="wtf">WTF</option>
</select>
</h4>
<br />
<p><input type="file" name="fileToUpload" id="fileToUpload" /></p>
<p><input type="submit" value="Upload Your Meme" name="submit" /></p>
<p><input type="checkbox" name="isPrivate" id="isPrivate" value="1" /><label for="isPrivate"> Private</label></p>
<p>Private images will not be shown in the public gallery, but will still be accessible by visiting the provided link.</p>
<p>All links are generated at random, and are virtually impossible to guess. "Security" through obscurity!</p>
<p><input type="submit" value="Upload Your Image" name="submit" /></p>
</form>
</div>
<div align="center">
<br />
<p>Accepted file formats: .gif, .png, .jpg, .jpeg</p>
<p>Accepted file formats: .gif, .png, .jpg/jpeg, .webp</p>
<br />
<hr>
<br />
<input type="submit" value="Browse Memes" onclick="window.location='./browse';" />
</div>
<div align="center">
<input type="submit" value="Browse Public Images" onclick="window.location='./browse';" />
<br />
<br />
<br />
<h6>Meme Machine v0.3.6-beta</h6>
<h6>ImgHost (v0.4.0-beta)</h6>
</div>
</html>

View File

@ -1,37 +1,48 @@
<?php
include './credentials.php';
$target_dir = "uploads/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
include './config/db_config.php';
include './config/configuration.php';
// Create a random 12-character hex ID number for the pictures
$randBytes = random_bytes(6);
$imageID = bin2hex($randBytes);
$uppedFile = basename($_FILES["fileToUpload"]["name"]);
$isPrivate = 0;
$imageFileType = pathinfo($uppedFile,PATHINFO_EXTENSION);
$fileName = $imageID . "." . $imageFileType;
$target_file = $targetDir . $fileName;
$mimeType = $_FILES['fileToUpload']['type'];
$uploadOk = 1;
$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
$conn = mysqli_connect($servername, $username, $password, $dbname);
$memeDir = mysqli_real_escape_string($conn, basename($_FILES["fileToUpload"]["name"]));
$memeName = mysqli_real_escape_string($conn, $_POST["memeName"]);
$memeCategory = mysqli_real_escape_string($conn, $_POST["memeCategory"]);
$conn = mysqli_connect($sqlServer, $sqlUsername, $sqlPassword, $dbname);
$imageDir = mysqli_real_escape_string($conn, basename($_FILES["fileToUpload"]["name"]));
if (!$conn) {
die ("CONNECTION FAIL " .mysqli_connect_error());
} else {
}
// Check if private flag is active
if (isset($_POST["isPrivate"])) {
$isPrivate = 1;
}
$insert = "INSERT INTO memes (name, category, fileName) VALUES ('$memeName', '$memeCategory', '$memeDir')";
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if($check !== false) {
$insert = "INSERT INTO $tablename (imgID, fileType, mimeType, isPrivate) VALUES ('$imageID', '$imageFileType', '$mimeType', '$isPrivate')";
// Check file mimetype to ensure it's actually an image
if (isset($_POST["submit"])) {
if (in_array($mimeType, $supportedFileTypes)){
$uploadOk = 1;
} else {
echo "File is not an image.";
echo "File is not a valid image.";
$uploadOk = 0;
}
}
if ($memeCategory == '') {
$uploadOk = 0;
echo "Choose a category";
}
// Check if file already exists
if (file_exists($target_file)) {
@ -40,13 +51,7 @@ if (file_exists($target_file)) {
}
// Check file size
if ($_FILES["fileToUpload"]["size"] > 75000000) {
echo "Sorry, your file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
echo "Only JPG, JPEG, PNG & GIF files are allowed.";
echo "Sorry, your image is too large (75 MB limit).";
$uploadOk = 0;
}
@ -61,7 +66,11 @@ if ($uploadOk == 0) {
// if everything is ok, try to upload file
} else {
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
echo "The meme ". basename( $_FILES["fileToUpload"]["name"]). " has been uploaded.";
echo "<p>The image ". $fileName . " has been uploaded.<p><br>";
if ($isPrivate != 0) {
echo "<p>You have chosen to make this image private - it will not appear in the public gallery.</p>";
}
echo "<p><a href=" . $targetDir . $fileName .">View image</a></p>";
?>
<p>&nbsp;</p>
<a href="./">Back</a>