Language updates. New upload form. new classes.
This commit is contained in:
parent
4c2857b445
commit
8f3061ab99
62 changed files with 3107 additions and 1883 deletions
75
classes/CDN.php
Normal file
75
classes/CDN.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace DJMixHosting;
|
||||
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\Credentials\Credentials;
|
||||
|
||||
class CDN
|
||||
{
|
||||
protected $s3Client;
|
||||
protected $bucket;
|
||||
protected $config;
|
||||
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->bucket = $config['aws']['s3']['bucket'];
|
||||
|
||||
$credentials = new Credentials(
|
||||
$config['aws']['s3']['access_key'],
|
||||
$config['aws']['s3']['secret_key']
|
||||
);
|
||||
|
||||
$this->s3Client = new S3Client([
|
||||
'version' => 'latest',
|
||||
'region' => $config['aws']['s3']['region'],
|
||||
'endpoint' => $config['aws']['s3']['host'],
|
||||
'credentials' => $credentials,
|
||||
'use_path_style_endpoint' => true,
|
||||
'profile' => null,
|
||||
'use_aws_shared_config_files' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function uploadFile(string $localPath, string $remotePath, string $mimeType, string $acl = 'private')
|
||||
{
|
||||
try {
|
||||
$result = $this->s3Client->putObject([
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $remotePath,
|
||||
'SourceFile' => $localPath,
|
||||
'ACL' => $acl,
|
||||
'ContentType' => $mimeType,
|
||||
]);
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception("Error uploading file to CDN: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function renameFile(string $oldRemotePath, string $newRemotePath)
|
||||
{
|
||||
// S3 does not support renaming directly. Copy then delete.
|
||||
try {
|
||||
$this->s3Client->copyObject([
|
||||
'Bucket' => $this->bucket,
|
||||
'CopySource' => "{$this->bucket}/{$oldRemotePath}",
|
||||
'Key' => $newRemotePath,
|
||||
]);
|
||||
|
||||
$this->s3Client->deleteObject([
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $oldRemotePath,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception("Error renaming file on CDN: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function clearCache(string $remotePath)
|
||||
{
|
||||
// If using CloudFront or another CDN in front of S3, implement invalidation logic here.
|
||||
// This might involve calling the CloudFront API to invalidate the specific path.
|
||||
}
|
||||
}
|
73
classes/Email.php
Normal file
73
classes/Email.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace DJMixHosting;
|
||||
|
||||
use Aws\Ses\SesClient;
|
||||
use Aws\Exception\AwsException;
|
||||
use Exception;
|
||||
|
||||
class Email {
|
||||
|
||||
private $config;
|
||||
private $sesClient;
|
||||
|
||||
public function __construct(array $config) {
|
||||
$this->config = $config;
|
||||
$this->sesClient = new SesClient([
|
||||
'version' => 'latest',
|
||||
'region' => $config['aws']['ses']['region'],
|
||||
'credentials' => [
|
||||
'key' => $config['aws']['ses']['access_key'],
|
||||
'secret' => $config['aws']['ses']['secret_key']
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic method to send an email.
|
||||
*
|
||||
* @param string $recipientEmail
|
||||
* @param string $subject
|
||||
* @param string $bodyText
|
||||
* @throws Exception if email sending fails.
|
||||
*/
|
||||
public function sendEmail(string $recipientEmail, string $subject, string $bodyText): void {
|
||||
$senderEmail = $this->config['aws']['ses']['sender_email'];
|
||||
try {
|
||||
$this->sesClient->sendEmail([
|
||||
'Destination' => ['ToAddresses' => [$recipientEmail]],
|
||||
'ReplyToAddresses' => [$senderEmail],
|
||||
'Source' => $senderEmail,
|
||||
'Message' => [
|
||||
'Body' => [
|
||||
'Text' => [
|
||||
'Charset' => 'UTF-8',
|
||||
'Data' => $bodyText,
|
||||
],
|
||||
],
|
||||
'Subject' => [
|
||||
'Charset' => 'UTF-8',
|
||||
'Data' => $subject,
|
||||
],
|
||||
],
|
||||
]);
|
||||
} catch (AwsException $e) {
|
||||
throw new Exception("Failed to send email: " . $e->getAwsErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to send a verification email.
|
||||
*
|
||||
* @param string $recipientEmail
|
||||
* @param string $verificationCode
|
||||
* @throws Exception if email sending fails.
|
||||
*/
|
||||
public function sendVerificationEmail(string $recipientEmail, string $verificationCode): void {
|
||||
$subject = "Verify Your Email Address";
|
||||
$verificationLink = $this->config['app']['url'] . "/verify_email.php?code={$verificationCode}";
|
||||
$bodyText = "Please verify your email address by clicking the link below or by entering the code in your profile:\n\n";
|
||||
$bodyText .= "{$verificationLink}\n\nYour verification code is: {$verificationCode}\nThis code will expire in 15 minutes.";
|
||||
$this->sendEmail($recipientEmail, $subject, $bodyText);
|
||||
}
|
||||
}
|
|
@ -26,29 +26,29 @@ class HeaderMeta
|
|||
$this->config = $config;
|
||||
// detect if it is DJMixHosting\Mix, DJMixHosting\Genre, DJMixHosting\Mixshow, or DJMixHosting\DJ in $object
|
||||
if ($object instanceof Mix) {
|
||||
$djs = $object->get_djs();
|
||||
$djs = $object->getDJs();
|
||||
$djList = "";
|
||||
foreach ($djs as $dj) {
|
||||
$djList .= $dj->getName() . ", ";
|
||||
}
|
||||
$djList = rtrim($djList, ", ");
|
||||
|
||||
$genres = $object->get_genres();
|
||||
$genres = $object->getGenres();
|
||||
$genreNames = "";
|
||||
|
||||
$genreList = rtrim($genreNames, ", ");
|
||||
|
||||
|
||||
$this->set_ogTitle($object->get_name());
|
||||
$this->set_ogDescription($object->get_description());
|
||||
$this->set_ogUrl($object->get_page_url());
|
||||
$this->set_ogImage($object->get_cover());
|
||||
$this->set_ogTitle($object->getName());
|
||||
$this->set_ogDescription($object->getDescription());
|
||||
$this->set_ogUrl($object->getPageUrl());
|
||||
$this->set_ogImage($object->getCover());
|
||||
$this->set_ogType("music.song");
|
||||
$this->set_ogDurationSeconds($object->get_seconds());
|
||||
$this->set_ogDurationSeconds($object->getSeconds());
|
||||
$this->set_ogMusician($djList);
|
||||
$this->set_ogAudio($object->get_download_url());
|
||||
$this->set_ogAudio($object->getDownloadUrl());
|
||||
$this->set_ogKeywords($genreList);
|
||||
$this->set_ogCanonical($object->get_page_url());
|
||||
$this->set_ogCanonical($object->getPageUrl());
|
||||
$this->set_ogRobot("index, follow");
|
||||
$this->set_ogNoindex("");
|
||||
}
|
||||
|
|
454
classes/Mix.php
454
classes/Mix.php
|
@ -2,343 +2,277 @@
|
|||
|
||||
namespace DJMixHosting;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
|
||||
class Mix
|
||||
{
|
||||
|
||||
private $id = -1;
|
||||
private $enabled = false;
|
||||
private $name = "";
|
||||
private $slug = "";
|
||||
private $db = null;
|
||||
private $description = "";
|
||||
private $cover = "";
|
||||
private $url = "";
|
||||
private $seconds = 0;
|
||||
private $download_only = true;
|
||||
private $djs = [];
|
||||
private $genres = [];
|
||||
private $recorded;
|
||||
private $downloads = 0;
|
||||
private $created;
|
||||
private $updated;
|
||||
private $playcount = 0;
|
||||
private int $id = -1;
|
||||
private bool $enabled = false;
|
||||
private string $name = "";
|
||||
private string $slug = "";
|
||||
private $db;
|
||||
private string $description = "";
|
||||
private string $cover = "";
|
||||
private string $url = "";
|
||||
private int $seconds = 0;
|
||||
private bool $download_only = true;
|
||||
private array $djs = [];
|
||||
private array $genres = [];
|
||||
private ?string $recorded = null;
|
||||
private int $downloads = 0;
|
||||
private ?string $created = null;
|
||||
private ?string $updated = null;
|
||||
private int $playcount = 0;
|
||||
private $tracklist = [];
|
||||
private $loadDJs = true;
|
||||
private $related_mixes = [];
|
||||
private $duration = [];
|
||||
private $mixshow = [];
|
||||
private bool $loadDJs = true;
|
||||
private array $related_mixes = [];
|
||||
private array $duration = [];
|
||||
private array $mixshow = [];
|
||||
|
||||
|
||||
public function __construct($value, $db, $loadDJs = true)
|
||||
/**
|
||||
* Construct a Mix object using either an ID or slug.
|
||||
*
|
||||
* @param mixed $value The mix identifier (ID or slug).
|
||||
* @param mixed $db Database connection.
|
||||
* @param bool $loadDJs Whether to load DJ objects.
|
||||
*/
|
||||
public function __construct($value, $db, bool $loadDJs = true)
|
||||
{
|
||||
$this->db = $db;
|
||||
|
||||
// echo the type of value
|
||||
$this->loadDJs = $loadDJs;
|
||||
|
||||
if (ctype_digit((string)$value)) {
|
||||
$this->id = (int)$value;
|
||||
return $this->load_by_id();
|
||||
|
||||
$loaded = $this->loadById();
|
||||
} else {
|
||||
$this->slug = $value;
|
||||
return $this->load_by_slug();
|
||||
$loaded = $this->loadBySlug();
|
||||
}
|
||||
|
||||
if (!$loaded) {
|
||||
throw new Exception("Mix not found.");
|
||||
}
|
||||
}
|
||||
|
||||
private function load_by_id(): bool
|
||||
/**
|
||||
* Loads mix data by its ID.
|
||||
*/
|
||||
private function loadById(): bool
|
||||
{
|
||||
$mix = $this->get_mix_by_id();
|
||||
if ($mix && $mix['title'] != "") {
|
||||
return $this->build_mix($mix);
|
||||
} else {
|
||||
$mix = $this->getMixById();
|
||||
if ($mix && !empty($mix['title'])) {
|
||||
return $this->buildMix($mix);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function get_mix_by_id()
|
||||
/**
|
||||
* Loads mix data by its slug.
|
||||
*/
|
||||
private function loadBySlug(): bool
|
||||
{
|
||||
// Check if current user is admin
|
||||
$isAdmin = (isset($_SESSION['user']) && isset($_SESSION['user']['role']) && $_SESSION['user']['role'] === 'admin');
|
||||
|
||||
if ($isAdmin) {
|
||||
$stmt = $this->db->prepare("SELECT * FROM mix WHERE id = ?");
|
||||
} else {
|
||||
// Only return approved mixes (pending = 0) for non-admins
|
||||
$stmt = $this->db->prepare("SELECT * FROM mix WHERE id = ? AND pending = 0");
|
||||
$mix = $this->getMixBySlug();
|
||||
if ($mix && !empty($mix['title'])) {
|
||||
return $this->buildMix($mix);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve mix data by ID.
|
||||
*/
|
||||
private function getMixById(): ?array
|
||||
{
|
||||
// If the current user is admin, show all mixes;
|
||||
// Otherwise, only return mixes with pending = 0.
|
||||
$isAdmin = isset($_SESSION['user']) &&
|
||||
isset($_SESSION['user']['role']) &&
|
||||
$_SESSION['user']['role'] === 'admin';
|
||||
$query = $isAdmin
|
||||
? "SELECT * FROM mix WHERE id = ?"
|
||||
: "SELECT * FROM mix WHERE id = ? AND pending = 0";
|
||||
|
||||
$stmt = $this->db->prepare($query);
|
||||
$stmt->bind_param("i", $this->id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$mix = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
return $mix;
|
||||
return $mix ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $mix
|
||||
* @return true
|
||||
* Retrieve mix data by slug.
|
||||
*/
|
||||
private function build_mix($mix): bool
|
||||
private function getMixBySlug(): ?array
|
||||
{
|
||||
$this->id = $mix['id'];
|
||||
$this->name = $mix['title'];
|
||||
$this->slug = $mix['slug'];
|
||||
$this->description = $mix['description'];
|
||||
$this->cover = $this->legacyFix($mix['cover']);
|
||||
$this->url = $this->legacyFix($mix['url']);
|
||||
$this->seconds = $mix['seconds'];
|
||||
$this->duration = $this->configure_duration();
|
||||
$this->download_only = $mix['mediaplayer'];
|
||||
$this->recorded = $mix['recorded'];
|
||||
$this->created = $mix['created'];
|
||||
$this->updated = $mix['lastupdated'];
|
||||
$this->enabled = $mix['pending'];
|
||||
$isAdmin = isset($_SESSION['user']) &&
|
||||
isset($_SESSION['user']['role']) &&
|
||||
$_SESSION['user']['role'] === 'admin';
|
||||
$query = $isAdmin
|
||||
? "SELECT * FROM mix WHERE slug = ?"
|
||||
: "SELECT * FROM mix WHERE slug = ? AND pending = 0";
|
||||
|
||||
$stmt = $this->db->prepare($query);
|
||||
$stmt->bind_param("s", $this->slug);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$mix = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
return $mix ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the mix object from database data.
|
||||
*/
|
||||
private function buildMix(array $mix): bool
|
||||
{
|
||||
$this->id = (int)$mix['id'];
|
||||
$this->name = $mix['title'] ?? "";
|
||||
$this->slug = $mix['slug'] ?? "";
|
||||
$this->description = $mix['description'] ?? "";
|
||||
$this->cover = $this->legacyFix($mix['cover'] ?? "");
|
||||
$this->url = $this->legacyFix($mix['url'] ?? "");
|
||||
$this->seconds = (int)($mix['seconds'] ?? 0);
|
||||
$this->duration = $this->configureDuration();
|
||||
$this->download_only = (bool)($mix['mediaplayer'] ?? true);
|
||||
$this->recorded = $mix['recorded'] ?? null;
|
||||
$this->created = $mix['created'] ?? null;
|
||||
$this->updated = $mix['lastupdated'] ?? null;
|
||||
$this->enabled = (bool)($mix['pending'] ?? false);
|
||||
|
||||
if ($this->loadDJs) {
|
||||
require_once 'DJ.php';
|
||||
$this->djs[] = new DJ($mix['dj1'], $this->db);
|
||||
if ($mix['dj2'] != null) {
|
||||
if (!empty($mix['dj2'])) {
|
||||
$this->djs[] = new DJ($mix['dj2'], $this->db);
|
||||
}
|
||||
if ($mix['dj3'] != null) {
|
||||
if (!empty($mix['dj3'])) {
|
||||
$this->djs[] = new DJ($mix['dj3'], $this->db);
|
||||
}
|
||||
|
||||
$this->djs = array_filter($this->djs);
|
||||
}
|
||||
|
||||
$this->load_mix_meta();
|
||||
$this->tracklist = $this->evaluate_tracklist();
|
||||
$this->loadMixMeta();
|
||||
$this->tracklist = $this->evaluateTracklist();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function legacyFix(mixed $item)
|
||||
/**
|
||||
* Fix legacy URL paths.
|
||||
*/
|
||||
private function legacyFix(string $item): string
|
||||
{
|
||||
if (str_starts_with($item, "/djs/")) {
|
||||
return "https://cdn.utahsdjs.com" . substr($item, 4);
|
||||
} else {
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
private function configure_duration(): array
|
||||
/**
|
||||
* Configure a formatted duration based on seconds.
|
||||
*/
|
||||
private function configureDuration(): array
|
||||
{
|
||||
$seconds = $this->seconds;
|
||||
$hours = floor($seconds / 3600);
|
||||
$minutes = floor(($seconds / 60) % 60);
|
||||
$seconds = $seconds % 60;
|
||||
|
||||
// for 't', we need to show it as 01:02:03
|
||||
if ($hours < 10) {
|
||||
$hours0 = "0" . $hours;
|
||||
} else {
|
||||
$hours0 = $hours;
|
||||
}
|
||||
if ($minutes < 10) {
|
||||
$minutes0 = "0" . $minutes;
|
||||
} else {
|
||||
$minutes0 = $minutes;
|
||||
}
|
||||
if ($seconds < 10) {
|
||||
$seconds0 = "0" . $seconds;
|
||||
} else {
|
||||
$seconds0 = $seconds;
|
||||
$secs = $seconds % 60;
|
||||
$time = ($hours > 0)
|
||||
? sprintf("%02d:%02d:%02d", $hours, $minutes, $secs)
|
||||
: sprintf("%02d:%02d", $minutes, $secs);
|
||||
return [
|
||||
'h' => $hours,
|
||||
'm' => $minutes,
|
||||
's' => $secs,
|
||||
't' => $time,
|
||||
'S' => $this->seconds
|
||||
];
|
||||
}
|
||||
|
||||
// if hours is 0, we don't need to show it
|
||||
$time = $hours > 0 ? $hours0 . ":" . $minutes0 . ":" . $seconds0 : $minutes0 . ":" . $seconds0;
|
||||
|
||||
return ['h' => $hours, 'm' => $minutes, 's' => $seconds, 't' => $time, 'S' => $this->seconds];
|
||||
}
|
||||
|
||||
private function load_mix_meta(): void
|
||||
/**
|
||||
* Load mix meta data.
|
||||
*/
|
||||
private function loadMixMeta(): void
|
||||
{
|
||||
$stmt = $this->db->prepare("SELECT attribute,value FROM mix_meta WHERE mix_id = ?");
|
||||
$stmt = $this->db->prepare("SELECT attribute, value FROM mix_meta WHERE mix_id = ?");
|
||||
$stmt->bind_param("i", $this->id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$meta = $result->fetch_all(MYSQLI_ASSOC);
|
||||
$stmt->close();
|
||||
|
||||
foreach ($meta as $key => $value) {
|
||||
if ($value['attribute'] == "genre") {
|
||||
$this->genres[] = $value['value'];
|
||||
unset($meta[$key]);
|
||||
foreach ($meta as $entry) {
|
||||
switch ($entry['attribute']) {
|
||||
case "genre":
|
||||
$this->genres[] = $entry['value'];
|
||||
break;
|
||||
case "related":
|
||||
$this->related_mixes[] = $entry['value'];
|
||||
break;
|
||||
case "playcount":
|
||||
$this->playcount = (int)$entry['value'];
|
||||
break;
|
||||
case "downloads":
|
||||
$this->downloads = (int)$entry['value'];
|
||||
break;
|
||||
case "tracklist":
|
||||
$this->tracklist = $entry['value'];
|
||||
break;
|
||||
case "mixshow":
|
||||
$this->mixshow[] = $entry['value'];
|
||||
break;
|
||||
default:
|
||||
// Handle additional meta attributes if needed.
|
||||
break;
|
||||
}
|
||||
if ($value['attribute'] == "related") {
|
||||
$this->related_mixes[] = $value['value'];
|
||||
unset($meta[$key]);
|
||||
}
|
||||
if ($value['attribute'] == "playcount") {
|
||||
$this->playcount = $value['value'];
|
||||
unset($meta[$key]);
|
||||
}
|
||||
if ($value['attribute'] == "downloads") {
|
||||
$this->downloads = $value['value'];
|
||||
unset($meta[$key]);
|
||||
}
|
||||
if ($value['attribute'] == "tracklist") {
|
||||
$this->tracklist = $value['value'];
|
||||
unset($meta[$key]);
|
||||
}
|
||||
if ($value['attribute'] == "mixshow") {
|
||||
$this->mixshow[] = $value['value'];
|
||||
unset($meta[$key]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function evaluate_tracklist()
|
||||
/**
|
||||
* Evaluate the tracklist data.
|
||||
*/
|
||||
private function evaluateTracklist()
|
||||
{
|
||||
if (empty($this->tracklist)) {
|
||||
return [];
|
||||
} else {
|
||||
// if the first item in the array is also an array, then return it
|
||||
}
|
||||
if (is_array($this->tracklist)) {
|
||||
return $this->tracklist;
|
||||
} else {
|
||||
}
|
||||
return explode("\n", (string)$this->tracklist);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Getter methods for mix properties.
|
||||
|
||||
private function load_by_slug(): bool
|
||||
public function getId(): int { return $this->id; }
|
||||
public function getName(): string { return $this->name; }
|
||||
public function getSlug(): string { return $this->slug; }
|
||||
public function getDescription(): string { return $this->description; }
|
||||
public function getCover(): string { return $this->cover; }
|
||||
public function getUrl(): string { return $this->url; }
|
||||
public function getSeconds(): int { return $this->seconds; }
|
||||
public function isDownloadOnly(): bool { return $this->download_only; }
|
||||
public function getDJs(): array { return $this->djs; }
|
||||
public function getGenres(): array { return $this->genres; }
|
||||
public function getRecorded(): ?string { return $this->recorded; }
|
||||
public function getDownloads(): int { return $this->downloads; }
|
||||
public function getCreated(): ?string { return $this->created; }
|
||||
public function getUpdated(): ?string { return $this->updated; }
|
||||
public function getPlaycount(): int { return $this->playcount; }
|
||||
public function getTracklist(): array { return $this->evaluateTracklist(); }
|
||||
public function getRelatedMixes(): array { return $this->related_mixes; }
|
||||
public function getDuration(): array { return $this->duration; }
|
||||
public function getMixshow(): array { return $this->mixshow; }
|
||||
|
||||
public function getDownloadUrl(): string
|
||||
{
|
||||
$mix = $this->get_mix_by_slug();
|
||||
if ($mix) {
|
||||
if ($mix['title'] != "") {
|
||||
return $this->build_mix($mix);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return "https://beta.utahsdjs.com/mix/{$this->slug}/download";
|
||||
}
|
||||
|
||||
|
||||
private function get_mix_by_slug()
|
||||
public function getPageUrl(): string
|
||||
{
|
||||
// Check if current user is admin
|
||||
$isAdmin = (isset($_SESSION['user']) && isset($_SESSION['user']['role']) && $_SESSION['user']['role'] === 'admin');
|
||||
|
||||
if ($isAdmin) {
|
||||
$stmt = $this->db->prepare("SELECT * FROM mix WHERE slug = ?");
|
||||
} else {
|
||||
$stmt = $this->db->prepare("SELECT * FROM mix WHERE slug = ? AND pending = 0");
|
||||
return "https://beta.utahsdjs.com/mix/{$this->slug}";
|
||||
}
|
||||
$stmt->bind_param("s", $this->slug);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$mix = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
return $mix;
|
||||
}
|
||||
|
||||
public function get_recorded()
|
||||
{
|
||||
return $this->recorded;
|
||||
}
|
||||
|
||||
public function get_created()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function get_updated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function get_img()
|
||||
{
|
||||
return $this->cover;
|
||||
}
|
||||
|
||||
public function get_djs()
|
||||
{
|
||||
return $this->djs;
|
||||
}
|
||||
|
||||
public function get_description()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function get_tracklist(): array
|
||||
{
|
||||
return $this->tracklist;
|
||||
}
|
||||
|
||||
public function get_genres()
|
||||
{
|
||||
return $this->genres;
|
||||
}
|
||||
|
||||
public function get_seconds(): int
|
||||
{
|
||||
return $this->seconds;
|
||||
}
|
||||
|
||||
public function is_download_only(): bool
|
||||
{
|
||||
return $this->download_only;
|
||||
}
|
||||
|
||||
public function get_url(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function get_cover()
|
||||
{
|
||||
return $this->cover;
|
||||
}
|
||||
|
||||
public function get_downloads(): int
|
||||
{
|
||||
return $this->downloads;
|
||||
}
|
||||
|
||||
public function get_plays(): int
|
||||
{
|
||||
return $this->playcount;
|
||||
}
|
||||
|
||||
public function get_id(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function get_name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function get_slug(): string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function get_duration(): array
|
||||
{
|
||||
return $this->duration;
|
||||
}
|
||||
|
||||
public function get_mixshow(): array
|
||||
{
|
||||
return $this->mixshow;
|
||||
}
|
||||
|
||||
public function get_download_url()
|
||||
{
|
||||
return "https://beta.utahsdjs.com/mix/" . "$this->slug" . "/download";
|
||||
}
|
||||
|
||||
public function get_page_url()
|
||||
{
|
||||
return "https://beta.utahsdjs.com/mix/" . "$this->slug";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
39
classes/SessionManager.php
Normal file
39
classes/SessionManager.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace DJMixHosting;
|
||||
|
||||
class SessionManager {
|
||||
|
||||
/**
|
||||
* Ensure the session is started.
|
||||
*/
|
||||
public static function start() {
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user data in session.
|
||||
*/
|
||||
public static function setUser(array $userData) {
|
||||
$_SESSION['user'] = $userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current user data from session.
|
||||
*/
|
||||
public static function getUser() {
|
||||
return $_SESSION['user'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the session.
|
||||
*/
|
||||
public static function destroy() {
|
||||
if (session_status() !== PHP_SESSION_NONE) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,87 +4,125 @@ namespace DJMixHosting;
|
|||
|
||||
class Upload
|
||||
{
|
||||
private $file;
|
||||
private $file_name;
|
||||
private $file_size;
|
||||
private $file_tmp;
|
||||
private $file_type;
|
||||
private $file_ext;
|
||||
private $file_path;
|
||||
private $extensions = array("mp3", "zip");
|
||||
private $upload_dir = "uploads/";
|
||||
private $errors = array();
|
||||
private $ok = false;
|
||||
private $config;
|
||||
private $uuid = "";
|
||||
protected $file;
|
||||
protected $config;
|
||||
protected $errors = [];
|
||||
protected $uploadDir;
|
||||
protected $uuid;
|
||||
protected $fileExt;
|
||||
protected $localPath;
|
||||
|
||||
|
||||
public function __construct($file, $config)
|
||||
public function __construct(array $file, array $config)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->file_name = $file['name'];
|
||||
$this->file_size = $file['size'];
|
||||
$this->file_tmp = $file['tmp_name'];
|
||||
$this->file_type = $file['type'];
|
||||
$this->config = $config;
|
||||
$ext = explode('.', $file['name']);
|
||||
$this->file_ext = strtolower(end($ext));
|
||||
$this->uploadDir = rtrim($config['uploads']['tmp_path'], '/') . '/';
|
||||
$this->uuid = uniqid();
|
||||
|
||||
$this->fileExt = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
}
|
||||
|
||||
private function check_file_size(): bool
|
||||
public function validate(): bool
|
||||
{
|
||||
if ($this->file_size > $this->config['uploads']['max_file_size']){
|
||||
$this->errors[] = "File size is too large";
|
||||
return false;
|
||||
if ($this->file['error'] !== UPLOAD_ERR_OK) {
|
||||
$this->errors[] = "File upload error.";
|
||||
}
|
||||
if ($this->file['size'] > $this->config['uploads']['max_file_size']) {
|
||||
$this->errors[] = "File is too large.";
|
||||
}
|
||||
if (!in_array($this->fileExt, $this->config['uploads']['allowed_file_types'])) {
|
||||
$this->errors[] = "File type not allowed.";
|
||||
}
|
||||
if (!in_array($this->file['type'], $this->config['uploads']['allowed_mime_type'])) {
|
||||
$this->errors[] = "MIME type not allowed.";
|
||||
}
|
||||
return empty($this->errors);
|
||||
}
|
||||
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function moveFile(): bool
|
||||
{
|
||||
$destination = $this->uploadDir . $this->uuid . "." . $this->fileExt;
|
||||
if (move_uploaded_file($this->file['tmp_name'], $destination)) {
|
||||
$this->localPath = $destination;
|
||||
return true;
|
||||
}
|
||||
|
||||
private function check_file_extension(): bool
|
||||
{
|
||||
if (!in_array($this->file_ext, $this->extensions)){
|
||||
$this->errors[] = "Invalid file extension";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function check_file_exists(): bool
|
||||
{
|
||||
if (file_exists($this->upload_dir . $this->file_name)){
|
||||
$this->errors[] = "File already exists";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function move_file(): bool
|
||||
{
|
||||
if (move_uploaded_file($this->file_tmp, $this->upload_dir . $this->uuid)){
|
||||
$this->file_path = $this->upload_dir . $this->uuid;
|
||||
return true;
|
||||
}
|
||||
$this->errors[] = "Failed to move uploaded file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
public function dump_all()
|
||||
public function process(): array
|
||||
{
|
||||
$array = array(
|
||||
"file" => $this->file,
|
||||
"file_name" => $this->file_name,
|
||||
"file_size" => $this->file_size,
|
||||
"file_tmp" => $this->file_tmp,
|
||||
"file_type" => $this->file_type,
|
||||
"file_ext" => $this->file_ext,
|
||||
"file_path" => $this->file_path,
|
||||
"extensions" => $this->extensions,
|
||||
"upload_dir" => $this->upload_dir,
|
||||
"errors" => $this->errors,
|
||||
"ok" => $this->ok,
|
||||
"uuid" => $this->uuid
|
||||
);
|
||||
// Assuming moveFile() has been called
|
||||
$uploadTask = [
|
||||
'original_name' => $this->file['name'],
|
||||
'local_path' => $this->localPath,
|
||||
'size' => $this->file['size'],
|
||||
'ext' => $this->fileExt,
|
||||
];
|
||||
|
||||
if ($this->fileExt === "mp3") {
|
||||
$escapedFile = escapeshellarg($this->localPath);
|
||||
$artist = trim(shell_exec("eyed3 --query='%a%' $escapedFile"));
|
||||
$title = trim(shell_exec("eyed3 --query='%t%' $escapedFile"));
|
||||
$duration = trim(shell_exec("mp3info -p \"%S\" $escapedFile"));
|
||||
|
||||
$uploadTask['file_type'] = 'mp3';
|
||||
$uploadTask['metadata'] = [
|
||||
'artist' => $artist,
|
||||
'title' => $title,
|
||||
'duration' => $duration,
|
||||
];
|
||||
$uploadTask['mediaplayer'] = 0;
|
||||
} elseif ($this->fileExt === "zip") {
|
||||
$zip = new \ZipArchive;
|
||||
if ($zip->open($this->localPath) === true) {
|
||||
$totalDuration = 0;
|
||||
$tracklist = [];
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$entryName = $zip->getNameIndex($i);
|
||||
$entryExt = strtolower(pathinfo($entryName, PATHINFO_EXTENSION));
|
||||
if ($entryExt === "mp3") {
|
||||
$tempDir = $this->uploadDir . uniqid('zip_');
|
||||
mkdir($tempDir);
|
||||
$tempFilePath = $tempDir . '/' . basename($entryName);
|
||||
$zip->extractTo($tempDir, $entryName);
|
||||
$escapedFile = escapeshellarg($tempFilePath);
|
||||
$title = trim(shell_exec("eyed3 --query='%t%' $escapedFile"));
|
||||
$duration = trim(shell_exec("mp3info -p \"%S\" $escapedFile"));
|
||||
$tracklist[] = $title ?: basename($entryName);
|
||||
$totalDuration += (int)$duration;
|
||||
unlink($tempFilePath);
|
||||
rmdir($tempDir);
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
$uploadTask['file_type'] = 'zip';
|
||||
$uploadTask['metadata'] = [
|
||||
'tracklist' => $tracklist,
|
||||
'total_duration' => $totalDuration,
|
||||
];
|
||||
$uploadTask['mediaplayer'] = 1;
|
||||
} else {
|
||||
$this->errors[] = "Failed to open ZIP file.";
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = "Unsupported file type.";
|
||||
}
|
||||
|
||||
return $uploadTask;
|
||||
}
|
||||
|
||||
public function uploadToCDN(CDN $cdn): string
|
||||
{
|
||||
// Create a remote path – e.g., under a temp folder with a unique name
|
||||
$remotePath = "temp/mixes/" . uniqid() . "_" . basename($this->localPath);
|
||||
$mimeType = ($this->fileExt === 'mp3') ? 'audio/mpeg' : 'application/zip';
|
||||
|
||||
$cdn->uploadFile($this->localPath, $remotePath, $mimeType);
|
||||
return $remotePath;
|
||||
}
|
||||
}
|
||||
|
|
277
classes/User.php
277
classes/User.php
|
@ -2,13 +2,19 @@
|
|||
|
||||
namespace DJMixHosting;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Random\RandomException;
|
||||
use Aws\Ses\SesClient;
|
||||
use Aws\Exception\AwsException;
|
||||
|
||||
Class User{
|
||||
class User {
|
||||
|
||||
private $db;
|
||||
private $id;
|
||||
private $username;
|
||||
private $firstName;
|
||||
private $lastName;
|
||||
private $email;
|
||||
private $location;
|
||||
private $bio;
|
||||
|
@ -16,25 +22,62 @@ Class User{
|
|||
private $updated;
|
||||
private $verified;
|
||||
private $role;
|
||||
private $img;
|
||||
|
||||
private $img = "";
|
||||
private $api_key;
|
||||
|
||||
public function __construct($db){
|
||||
public function __construct($db, $id = null) {
|
||||
$this->db = $db;
|
||||
if ($id) {
|
||||
$this->loadUserById($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RandomException
|
||||
* Load user data from the database by id.
|
||||
*/
|
||||
public function newUser($username, $password, $email){
|
||||
if ($this->check_existing_user($username, $email)){
|
||||
throw new RandomException("User already exists");
|
||||
private function loadUserById($id) {
|
||||
$stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $id);
|
||||
$stmt->execute();
|
||||
$user_data = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
if ($user_data) {
|
||||
$this->id = $user_data['id'];
|
||||
$this->username = $user_data['username'];
|
||||
$this->firstName = $user_data['firstName'];
|
||||
$this->lastName = $user_data['lastName'];
|
||||
$this->email = $user_data['email'];
|
||||
$this->verified = $user_data['emailVerified'];
|
||||
$this->img = $user_data['img'];
|
||||
$this->api_key = $user_data['apiKey'];
|
||||
$this->created = $user_data['created'];
|
||||
$this->updated = $user_data['lastupdated'];
|
||||
$this->role = $user_data['isAdmin'] ? 'admin' : 'user';
|
||||
|
||||
// These fields are not in your table; assign defaults or remove them.
|
||||
$this->location = "";
|
||||
$this->bio = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new user.
|
||||
*
|
||||
* @throws RandomException if the user already exists.
|
||||
*/
|
||||
public function newUser(string $username, string $password, string $email, string $firstName, string $lastName): int {
|
||||
if ($this->check_existing_user($username, $email)) {
|
||||
throw new \Random\RandomException("User already exists");
|
||||
}
|
||||
$this->username = $username;
|
||||
$this->email = $email;
|
||||
$password2 = password_hash($password, PASSWORD_DEFAULT);
|
||||
$this->password = $password2;
|
||||
$this->firstName = $firstName;
|
||||
$this->lastName = $lastName;
|
||||
$password_hashed = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Set default values for optional fields.
|
||||
$this->location = "";
|
||||
$this->bio = "";
|
||||
$this->created = date('Y-m-d H:i:s');
|
||||
|
@ -44,14 +87,18 @@ Class User{
|
|||
$this->img = "";
|
||||
$this->api_key = bin2hex(random_bytes(32));
|
||||
|
||||
$stmt = $this->db->prepare("INSERT INTO users (username, password, email, img) VALUES (?, ?, ?, ?)");
|
||||
$stmt->bind_param("ssss", $this->username, $this->password, $this->email, $this->img);
|
||||
$stmt = $this->db->prepare("INSERT INTO users (username, password, email, firstName, lastName, img, emailVerified) VALUES (?, ?, ?, ?, ?, '', 0)");
|
||||
$stmt->bind_param("sssss", $this->username, $password_hashed, $this->email, $this->firstName, $this->lastName);
|
||||
$stmt->execute();
|
||||
$userId = $stmt->insert_id;
|
||||
$stmt->close();
|
||||
|
||||
$this->id = $userId;
|
||||
return $userId;
|
||||
}
|
||||
|
||||
private function check_existing_user($username, $email){
|
||||
|
||||
private function check_existing_user($username, $email) {
|
||||
$stmt = $this->db->prepare("SELECT * FROM users WHERE username = ? OR email = ?");
|
||||
$stmt->bind_param("ss", $username, $email);
|
||||
$stmt->execute();
|
||||
|
@ -61,8 +108,13 @@ Class User{
|
|||
return $user;
|
||||
}
|
||||
|
||||
public function login($email, $password)
|
||||
{
|
||||
/**
|
||||
* Login a user by email and password.
|
||||
*
|
||||
* Returns the user data array if successful. In case of failure,
|
||||
* a string error message is returned.
|
||||
*/
|
||||
public function login($email, $password) {
|
||||
// Retrieve user record by email
|
||||
$stmt = $this->db->prepare("SELECT * FROM users WHERE email = ?");
|
||||
$stmt->bind_param("s", $email);
|
||||
|
@ -78,10 +130,10 @@ Class User{
|
|||
$attempt_data = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
$current_time = new \DateTime();
|
||||
$current_time = new DateTime();
|
||||
|
||||
if ($attempt_data && !empty($attempt_data['lockout_until'])) {
|
||||
$lockout_until = new \DateTime($attempt_data['lockout_until']);
|
||||
$lockout_until = new DateTime($attempt_data['lockout_until']);
|
||||
if ($current_time < $lockout_until) {
|
||||
return "Account locked until " . $lockout_until->format('Y-m-d H:i:s') . ". Please try again later.";
|
||||
}
|
||||
|
@ -95,15 +147,10 @@ Class User{
|
|||
|
||||
// Verify the password using password_verify
|
||||
if (password_verify($password, $user_data['password'])) {
|
||||
// Successful login – clear login attempts and set session variables
|
||||
// Successful login – clear login attempts.
|
||||
$this->resetLoginAttempts($email);
|
||||
$_SESSION['user'] = [
|
||||
'id' => $user_data['id'],
|
||||
'email' => $user_data['email'],
|
||||
'username' => $user_data['username'],
|
||||
'role' => $user_data['isAdmin'] ? 'admin' : 'user'
|
||||
];
|
||||
return true;
|
||||
// Return the user data for further session handling
|
||||
return $user_data;
|
||||
} else {
|
||||
$attempts = $this->updateFailedAttempt($email);
|
||||
return "Invalid email or password. Attempt $attempts of 3.";
|
||||
|
@ -115,8 +162,7 @@ Class User{
|
|||
* If attempts reach 3, set a lockout that doubles each time.
|
||||
* Returns the current number of attempts.
|
||||
*/
|
||||
private function updateFailedAttempt($email)
|
||||
{
|
||||
private function updateFailedAttempt($email) {
|
||||
// Check for an existing record
|
||||
$stmt = $this->db->prepare("SELECT * FROM login_attempts WHERE email = ?");
|
||||
$stmt->bind_param("s", $email);
|
||||
|
@ -124,7 +170,7 @@ Class User{
|
|||
$record = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
$current_time = new \DateTime();
|
||||
$current_time = new DateTime();
|
||||
|
||||
if ($record) {
|
||||
$attempts = $record['attempts'] + 1;
|
||||
|
@ -165,13 +211,186 @@ Class User{
|
|||
/**
|
||||
* Reset the login_attempts record for the given email.
|
||||
*/
|
||||
private function resetLoginAttempts($email)
|
||||
{
|
||||
private function resetLoginAttempts($email) {
|
||||
$stmt = $this->db->prepare("DELETE FROM login_attempts WHERE email = ?");
|
||||
$stmt->bind_param("s", $email);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's email address.
|
||||
*
|
||||
* @param string $newEmail
|
||||
* @param array $config Configuration array for AWS SES and app settings.
|
||||
* @return string Success message.
|
||||
* @throws \Exception on validation or email-sending failure.
|
||||
*/
|
||||
public function updateEmail(string $newEmail, array $config): string {
|
||||
$newEmail = filter_var($newEmail, FILTER_VALIDATE_EMAIL);
|
||||
if (!$newEmail) {
|
||||
throw new \Exception("Invalid email format.");
|
||||
}
|
||||
|
||||
// Update email and mark as unverified.
|
||||
$stmt = $this->db->prepare("UPDATE users SET email = ?, emailVerified = 0 WHERE id = ?");
|
||||
$stmt->bind_param("si", $newEmail, $this->id);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Generate verification code and expiry (15 minutes from now)
|
||||
$verification_code = bin2hex(random_bytes(16));
|
||||
$expires_at = date("Y-m-d H:i:s", strtotime("+15 minutes"));
|
||||
|
||||
// Store the verification record.
|
||||
$stmt = $this->db->prepare("REPLACE INTO email_verifications (user_id, email, verification_code, expires_at) VALUES (?, ?, ?, ?)");
|
||||
$stmt->bind_param("isss", $this->id, $newEmail, $verification_code, $expires_at);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Use the new Email class to send the verification email.
|
||||
$emailObj = new Email($config);
|
||||
$emailObj->sendVerificationEmail($newEmail, $verification_code);
|
||||
|
||||
// Update the class properties.
|
||||
$this->email = $newEmail;
|
||||
$this->verified = 0;
|
||||
return "Email updated. A verification email has been sent to your new address.";
|
||||
}
|
||||
|
||||
public function updateName($firstName, $lastName) {
|
||||
// Update the user's name.
|
||||
$stmt = $this->db->prepare("UPDATE users SET firstName = ?, lastName = ? WHERE id = ?");
|
||||
$stmt->bind_param("ssi", $firstName, $lastName, $this->id);
|
||||
if (!$stmt->execute()) {
|
||||
$stmt->close();
|
||||
throw new Exception("Failed to update name. Please try again.");
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
// Optionally update class properties.
|
||||
$this->firstName = $firstName;
|
||||
$this->lastName = $lastName;
|
||||
return "Name updated successfully.";
|
||||
}
|
||||
|
||||
public function updatePassword($currentPassword, $newPassword, $confirmPassword) {
|
||||
// Retrieve the current password hash.
|
||||
$stmt = $this->db->prepare("SELECT password FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $this->id);
|
||||
$stmt->execute();
|
||||
$userData = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
if (!$userData || !password_verify($currentPassword, $userData['password'])) {
|
||||
throw new Exception("Current password is incorrect.");
|
||||
}
|
||||
|
||||
if ($newPassword !== $confirmPassword) {
|
||||
throw new Exception("New password and confirmation do not match.");
|
||||
}
|
||||
|
||||
// Validate the new password.
|
||||
$pattern = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,32}$/';
|
||||
if (!preg_match($pattern, $newPassword)) {
|
||||
throw new Exception("New password must be 8-32 characters and include at least one uppercase letter, one lowercase letter, one number, and one symbol.");
|
||||
}
|
||||
|
||||
$hashed_new_password = password_hash($newPassword, PASSWORD_DEFAULT);
|
||||
$stmt = $this->db->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
$stmt->bind_param("si", $hashed_new_password, $this->id);
|
||||
if (!$stmt->execute()) {
|
||||
$stmt->close();
|
||||
throw new Exception("Failed to update password. Please try again.");
|
||||
}
|
||||
$stmt->close();
|
||||
return "Password updated successfully.";
|
||||
}
|
||||
|
||||
public function updateUsername($newUsername) {
|
||||
// Validate username format.
|
||||
if (!preg_match('/^[a-zA-Z0-9_]{3,25}$/', $newUsername)) {
|
||||
throw new Exception("Invalid username format.");
|
||||
}
|
||||
|
||||
// Check if the new username already exists for another user.
|
||||
$stmt = $this->db->prepare("SELECT id FROM users WHERE username = ? AND id != ?");
|
||||
$stmt->bind_param("si", $newUsername, $this->id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0) {
|
||||
$stmt->close();
|
||||
throw new Exception("Username already taken.");
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
// Update the username.
|
||||
$stmt = $this->db->prepare("UPDATE users SET username = ? WHERE id = ?");
|
||||
$stmt->bind_param("si", $newUsername, $this->id);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
$this->username = $newUsername;
|
||||
return "Username updated successfully.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the user's email using the provided verification code.
|
||||
*
|
||||
* @param string $verification_code The code submitted by the user.
|
||||
* @return string Success message.
|
||||
* @throws \Exception If the code is invalid or expired.
|
||||
*/
|
||||
public function verifyEmail(string $verification_code): string {
|
||||
// Look up the verification record for this user and code
|
||||
$stmt = $this->db->prepare("SELECT * FROM email_verifications WHERE user_id = ? AND verification_code = ?");
|
||||
$stmt->bind_param("is", $this->id, $verification_code);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$record = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
if (!$record) {
|
||||
throw new \Exception("Invalid verification code.");
|
||||
}
|
||||
|
||||
// Check if the verification code has expired
|
||||
$current_time = new \DateTime();
|
||||
$expires_at = new \DateTime($record['expires_at']);
|
||||
if ($current_time > $expires_at) {
|
||||
throw new \Exception("Verification code has expired. Please request a new one.");
|
||||
}
|
||||
|
||||
// Update the user's record to mark the email as verified
|
||||
$stmt = $this->db->prepare("UPDATE users SET emailVerified = 1 WHERE id = ?");
|
||||
$stmt->bind_param("i", $this->id);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Remove the verification record to clean up
|
||||
$stmt = $this->db->prepare("DELETE FROM email_verifications WHERE user_id = ?");
|
||||
$stmt->bind_param("i", $this->id);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Update the object property
|
||||
$this->verified = 1;
|
||||
|
||||
return "Email verified successfully.";
|
||||
}
|
||||
|
||||
// Getter methods
|
||||
public function getId() { return $this->id; }
|
||||
public function getUsername() { return $this->username; }
|
||||
public function getFirstName() { return $this->firstName; }
|
||||
public function getLastName() { return $this->lastName; }
|
||||
public function getEmail() { return $this->email; }
|
||||
public function getLocation() { return $this->location; }
|
||||
public function getBio() { return $this->bio; }
|
||||
public function getCreated() { return $this->created; }
|
||||
public function getUpdated() { return $this->updated; }
|
||||
public function getVerified() { return $this->verified; }
|
||||
public function getRole() { return $this->role; }
|
||||
public function getImg() { return $this->img; }
|
||||
public function getApiKey() { return $this->api_key; }
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
"name": "utahsdjs/utahsdjs2025",
|
||||
"description": "description",
|
||||
"minimum-stability": "dev",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.16",
|
||||
"license": "proprietary",
|
||||
"authors": [
|
||||
{
|
||||
|
@ -11,12 +11,13 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.2.0",
|
||||
"yosymfony/toml": "*",
|
||||
"ext-mysqli": "*",
|
||||
"aws/aws-sdk-php": ">=3.339.15",
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": ">8.5.1.0",
|
||||
"aws/aws-sdk-php": "*"
|
||||
"ext-mysqli": "*",
|
||||
"php": ">=8.2.0",
|
||||
"phpunit/phpunit": ">=11",
|
||||
"yosymfony/toml": "*",
|
||||
"ext-zip": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
1358
composer.lock
generated
1358
composer.lock
generated
File diff suppressed because it is too large
Load diff
BIN
composer.phar
BIN
composer.phar
Binary file not shown.
135
contact.php
135
contact.php
|
@ -1,69 +1,128 @@
|
|||
<?php
|
||||
require_once 'includes/globals.php';
|
||||
|
||||
// Initialize auto-fill variables
|
||||
$userName = '';
|
||||
$userEmail = '';
|
||||
|
||||
// Check if user details are available in the session
|
||||
if (isset($_SESSION['user'])) {
|
||||
$userName = $_SESSION['user']['firstName'] . ' ' . $_SESSION['user']['lastName'];
|
||||
$userEmail = $_SESSION['user']['email'];
|
||||
}
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Telegram;
|
||||
|
||||
if ($_POST) {
|
||||
$name = $_POST['name'];
|
||||
$email = $_POST['email'];
|
||||
$formName = $_POST['name'];
|
||||
if ($formName != $userName) {
|
||||
$name = $userName . " (Orig: " . $formName . ")";
|
||||
} else {
|
||||
$name = $formName;
|
||||
}
|
||||
$formEmail = $_POST['email'];
|
||||
if ($formEmail != $userEmail) {
|
||||
$email = $userEmail . " (Orig: " . $formEmail . ")";
|
||||
} else {
|
||||
$email = $userEmail;
|
||||
}
|
||||
$message = $_POST['message'];
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
require_once 'classes/Telegram.php';
|
||||
$telegram = new Telegram($config['notifications']['telegram']['token'], $config['notifications']['telegram']['chat_id']);
|
||||
$result = $telegram->send_message("Name: $name\nEmail: $email\nMessage: $message");
|
||||
$result = $telegram->send_message("Name: $name\nEmail: $email\nIP Address: $ipAddress\nMessage: $message");
|
||||
}
|
||||
$title = $locale['contactus'];
|
||||
require_once 'includes/header.php';
|
||||
if ($_POST) {
|
||||
if ($result['ok']) {
|
||||
echo '<div class="alert alert-success" role="alert">Message sent successfully</div>';
|
||||
} else {
|
||||
echo '<div class="alert alert-danger" role="alert">An error occurred while sending the message</div>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<section>
|
||||
<div class="container py-5">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<nav aria-label="breadcrumb" class="bg-body-tertiary rounded-3 p-3 mb-4">
|
||||
<ol class="breadcrumb mb-0">
|
||||
<li class="breadcrumb-item"><a href="/"><?php echo $locale['home']; ?></a></li>
|
||||
<li class="breadcrumb-item active"><a
|
||||
href="/contact.php"><?php echo $locale['contactus']; ?></a></li>
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<style>
|
||||
|
||||
.contact-form {
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 0.25rem rgba(13,110,253,.25);
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
color: #6c757d;
|
||||
margin: 0 10px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.social-links a:hover {
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<?php if ($_POST): ?>
|
||||
<div class="position-fixed top-0 start-50 translate-middle-x p-3" style="z-index: 1000">
|
||||
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header <?php echo $result['ok'] ? 'bg-success' : 'bg-danger'; ?> text-white">
|
||||
<strong class="me-auto">Notification</strong>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
<?php echo $result['ok'] ? 'Message sent successfully!' : 'An error occurred while sending the message.'; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<section>
|
||||
<div class="container py-3">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb mb-0">
|
||||
<li class="breadcrumb-item"><a href="/" class="text-decoration-none"><?php echo $locale['home']; ?></a></li>
|
||||
<li class="breadcrumb-item active"><?php echo $locale['contactus']; ?></li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Contact Section -->
|
||||
<div class="container py-5">
|
||||
<!-- Header -->
|
||||
<div class="row mb-5 text-center">
|
||||
<div class="col-lg-8 mx-auto">
|
||||
<h1 class="display-4 mb-3"><?php echo $locale['contactus']; ?></h1>
|
||||
<p class="lead text-muted">We'd love to hear from you. Send us a message and we'll respond as soon as possible.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="container py-5">
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1><?php echo $locale['contactus']; ?></h1>
|
||||
<p>For any inquiries, please complete the form below.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form action="contact.php" method="post">
|
||||
<div class="mb-3">
|
||||
<!-- Contact Form -->
|
||||
<div class="col-lg-7 mb-4 mb-lg-0">
|
||||
<div class="contact-form">
|
||||
<h3 class="mb-4">Send us a Message</h3>
|
||||
<form action="/contact" method="post">
|
||||
<div class="mb-4">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
<input type="text" class="form-control form-control-lg" id="name" name="name" value="<?php echo htmlspecialchars($userName); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="mb-4">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
<input type="email" class="form-control form-control-lg" id="email" name="email" value="<?php echo htmlspecialchars($userEmail); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="mb-4">
|
||||
<label for="message" class="form-label">Message</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="3" required></textarea>
|
||||
<textarea class="form-control form-control-lg" id="message" name="message" rows="5" required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<button type="submit" class="btn btn-primary btn-lg px-5">Send Message</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
require_once 'includes/footer.php';
|
||||
</div>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
|
|
12
dj.php
12
dj.php
|
@ -151,7 +151,7 @@ require_once 'includes/header.php';
|
|||
|
||||
foreach ($mixes as $mix) {
|
||||
$output = new Mix($mix['slug'], $db);
|
||||
$genres = $output->get_genres();
|
||||
$genres = $output->getGenres();
|
||||
$genrelist = [];
|
||||
|
||||
foreach ($genres as $genre) {
|
||||
|
@ -164,8 +164,8 @@ require_once 'includes/header.php';
|
|||
// Column for mix name and link
|
||||
echo '<div class="col-md text-truncate" >';
|
||||
echo '<p class="mb-0 " >';
|
||||
echo '<a title="' . $output->get_name() . '" href="/mix/' . $output->get_slug() . '">';
|
||||
echo $output->get_name();
|
||||
echo '<a title="' . $output->getName() . '" href="/mix/' . $output->getSlug() . '">';
|
||||
echo $output->getName();
|
||||
echo '</a>';
|
||||
echo '</p>';
|
||||
echo '</div>'; // End column
|
||||
|
@ -185,7 +185,7 @@ require_once 'includes/header.php';
|
|||
// Column for duration
|
||||
echo '<div class="col-md">';
|
||||
echo '<p class="mb-0">';
|
||||
$duration = $output->get_duration();
|
||||
$duration = $output->getDuration();
|
||||
echo $duration['t'];
|
||||
echo '</p>';
|
||||
echo '</div>'; // End column
|
||||
|
@ -194,9 +194,9 @@ require_once 'includes/header.php';
|
|||
echo '<div class="col-md">';
|
||||
echo '<p class="mb-0">';
|
||||
// date format should just be year
|
||||
$date = $output->get_recorded();
|
||||
$date = $output->getRecorded();
|
||||
if (is_null($date) || empty(trim($date))) {
|
||||
$date = $output->get_created();
|
||||
$date = $output->getCreated();
|
||||
}
|
||||
echo date('Y', strtotime($date));
|
||||
|
||||
|
|
|
@ -1,19 +1,32 @@
|
|||
<section>
|
||||
<footer>
|
||||
<div class="container py-5">
|
||||
<!-- Navigation Links -->
|
||||
<div class="row mb-3">
|
||||
<div class="col text-center">
|
||||
<p><?php
|
||||
echo "© 2008 - ";
|
||||
echo date('Y');
|
||||
echo " " . $locale['allrightsreserved'];
|
||||
?></p>
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item"><a href="/contact">Contact Us</a></li>
|
||||
<li class="list-inline-item"><a href="/privacy">Privacy Policy</a></li>
|
||||
<!-- Uncomment the following line once you have a Terms & Conditions page -->
|
||||
<!-- <li class="list-inline-item"><a href="/terms">Terms & Conditions</a></li> -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Copyright Text -->
|
||||
<div class="row mb-2">
|
||||
<div class="col text-center">
|
||||
<p><?php echo "© 2008 - " . date('Y') . " " . $locale['allrightsreserved']; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Software Credit -->
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
Created using <a href="https://gitea.threefifteen.info/cody/dj_mix_hosting_software">DJ Mix Hosting Software</a>
|
||||
<p>Created using <a href="https://gitea.threefifteen.info/cody/dj_mix_hosting_software">DJ Mix Hosting Software</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js" integrity="sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
</body>
|
||||
|
|
|
@ -4,24 +4,21 @@
|
|||
require_once 'vendor/autoload.php';
|
||||
|
||||
use Yosymfony\Toml\Toml;
|
||||
use DJMixHosting\SessionManager;
|
||||
|
||||
$config = Toml::ParseFile('includes/config.toml');
|
||||
require_once 'functions/i18n.php';
|
||||
require_once 'includes/sessions.php';
|
||||
// Instead of including sessions.php, start the session via SessionManager:
|
||||
SessionManager::start();
|
||||
|
||||
require_once 'includes/lang_loader.php';
|
||||
|
||||
|
||||
$mixshowsPages = ["/mixshows", "/mixshows/", "/mixshows.php"];
|
||||
|
||||
$djsPages = ["/djs", "/djs/", "/djs.php"];
|
||||
|
||||
$genrePages = ["/genres", "/genres/", "/genres.php"];
|
||||
|
||||
$mixPages = ["/mix", "/mix/", "/mix.php"];
|
||||
|
||||
$specialStyle = array_merge($mixshowsPages, $djsPages, $genrePages);
|
||||
|
||||
|
||||
/**
|
||||
* @param int $count
|
||||
* @param mixed $dj
|
||||
|
@ -51,7 +48,6 @@ function social_line($social, $value): string
|
|||
$icon = "";
|
||||
$url = "";
|
||||
$color = "#000000";
|
||||
|
||||
switch ($social) {
|
||||
case "facebook":
|
||||
$icon = "fa-brands fa-facebook";
|
||||
|
@ -107,18 +103,15 @@ function social_line($social, $value): string
|
|||
$name = "Website";
|
||||
break;
|
||||
}
|
||||
|
||||
return "
|
||||
<li class='list-group-item d-flex justify-content-between bg-body-secondary align-items-center p-3' title='$name'>
|
||||
<i class='fa $icon fa-lg' style='color: $color;' title='$name'></i>
|
||||
<p class='mb-0'><a href='$url' title='$name' >$value</a>
|
||||
</p>
|
||||
<p class='mb-0'><a href='$url' title='$name'>$value</a></p>
|
||||
</li>";
|
||||
}
|
||||
|
||||
function box_line($title, $value): string
|
||||
{
|
||||
|
||||
return "<hr>
|
||||
<div class='row'>
|
||||
<div class='col-sm-3'>
|
||||
|
@ -128,5 +121,4 @@ function box_line($title, $value): string
|
|||
<p class='text-muted mb-0'>$value</p>
|
||||
</div>
|
||||
</div>";
|
||||
|
||||
}
|
|
@ -18,4 +18,3 @@ if (isset($mix)) {
|
|||
echo $meta;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<?php require_once 'includes/header-security.php'; ?>
|
||||
<!doctype html >
|
||||
<html lang="<?php echo $lang ?>" <?php
|
||||
// dark mode checker
|
||||
if (isset($_SESSION['darkmode']) && $_SESSION['darkmode'] == 'true') {
|
||||
echo 'data-bs-theme="dark"';
|
||||
} ?>
|
||||
<!doctype html>
|
||||
<html lang="<?php echo $lang ?>">
|
||||
<head>
|
||||
<script>
|
||||
(function() {
|
||||
if (localStorage.getItem('darkmode') === 'true') {
|
||||
document.documentElement.setAttribute('data-bs-theme', 'dark');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?php if (isset($config['seo']['google']) && $config['seo']['google']) {
|
||||
require_once 'includes/google_tag_manager.php';
|
||||
echo get_google_tag_manager_header($config['seo']['gtm']['key']);
|
||||
|
|
|
@ -11,7 +11,6 @@ if (isset($_GET['lang']) && array_key_exists($_GET['lang'], $languages)) {
|
|||
}
|
||||
$current_lang = $_SESSION['lang'] ?? $config['app']['locale'];
|
||||
?>
|
||||
|
||||
<header class="navbar navbar-expand-md bg-body sticky-top shadow">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand pe-3" href="/"><?php echo htmlspecialchars($config['app']['name']); ?></a>
|
||||
|
@ -22,55 +21,52 @@ $current_lang = $_SESSION['lang'] ?? $config['app']['locale'];
|
|||
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link<?php
|
||||
// if the page is genres.php, set active and aria-current="page"
|
||||
if (basename($_SERVER['SCRIPT_NAME']) == 'index.php') {
|
||||
echo current_list();
|
||||
}
|
||||
?>" href="/"><?php echo $locale['home']; ?></a>
|
||||
<a class="nav-link<?php if (basename($_SERVER['SCRIPT_NAME']) == 'index.php') { echo current_list(); } ?>" href="/"><?php echo $locale['home']; ?></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link<?php
|
||||
|
||||
if (in_array($_SERVER['SCRIPT_NAME'], $genrePages)) {
|
||||
echo current_list();
|
||||
} ?>" href="/genres"><?php echo $locale['genres']; ?></a>
|
||||
<a class="nav-link<?php if (in_array($_SERVER['SCRIPT_NAME'], $genrePages)) { echo current_list(); } ?>" href="/genres"><?php echo $locale['genres']; ?></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link<?php
|
||||
|
||||
if (in_array($_SERVER['SCRIPT_NAME'], $djsPages)) {
|
||||
echo current_list();
|
||||
} ?>" href="/djs"><?php echo $locale['djs']; ?></a>
|
||||
<a class="nav-link<?php if (in_array($_SERVER['SCRIPT_NAME'], $djsPages)) { echo current_list(); } ?>" href="/djs"><?php echo $locale['djs']; ?></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link<?php
|
||||
|
||||
if (in_array($_SERVER['SCRIPT_NAME'], $mixshowsPages)) {
|
||||
echo current_list();
|
||||
} ?>" href="/mixshows"><?php echo $locale['mixshows']; ?></a>
|
||||
<a class="nav-link<?php if (in_array($_SERVER['SCRIPT_NAME'], $mixshowsPages)) { echo current_list(); } ?>" href="/mixshows"><?php echo $locale['mixshows']; ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<?php
|
||||
if (isset($_SESSION['darkmode']) && $_SESSION['darkmode'] == 'true') {
|
||||
echo '<button class="btn btn-outline-dark" id="darkModeToggle" onclick="toggleDarkMode()">🌙</button>';
|
||||
} else {
|
||||
echo '<button class="btn btn-outline-light" id="darkModeToggle" onclick="toggleDarkMode()">🌞</button>';
|
||||
}
|
||||
?>
|
||||
<div class="d-flex align-items-center">
|
||||
<!-- Search form remains outside the dropdown -->
|
||||
<form class="d-flex me-3" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="<?php echo $locale['search']; ?>"
|
||||
aria-label="<?php echo $locale['search']; ?>">
|
||||
<button class="btn btn-outline-success" type="submit"><?php echo $locale['search']; ?></button>
|
||||
</form>
|
||||
|
||||
<!-- Profile Widget Dropdown including language switcher -->
|
||||
<div class="dropdown">
|
||||
<label for="languageSelect" style="display: none">Language Selector</label>
|
||||
<select class="form-select" id="languageSelect"
|
||||
onchange="location = this.value;">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="profileDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa fa-user"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="profileDropdown">
|
||||
<?php if (isset($_SESSION['user'])): ?>
|
||||
<li><a class="dropdown-item" href="/profile"><?php echo $locale['userProfile']; ?></a></li>
|
||||
<li><a class="dropdown-item" href="/upload"><?php echo $locale['upload']; ?></a></li>
|
||||
<li><a class="dropdown-item" href="/logout"><?php echo $locale['logout']; ?></a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<?php else: ?>
|
||||
<li><a class="dropdown-item" href="/login"><?php echo $locale['login']; ?></a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<?php endif; ?>
|
||||
<!-- Language Switcher -->
|
||||
<li class="px-3 py-2">
|
||||
<label for="languageSelect" class="visually-hidden">Language Selector</label>
|
||||
<select class="form-select" id="languageSelect" onchange="location = this.value;">
|
||||
<?php
|
||||
if (isset($_SERVER["REQUIEST_URI"])) {
|
||||
$currentUrl = strtok($_SERVER["REQUEST_URI"], '?') ?? '/';
|
||||
} else {
|
||||
$currentUrl = '/';
|
||||
}
|
||||
|
||||
$queryParams = $_GET;
|
||||
foreach ($languages as $key => $value) {
|
||||
$queryParams['lang'] = $key;
|
||||
|
@ -81,62 +77,49 @@ $current_lang = $_SESSION['lang'] ?? $config['app']['locale'];
|
|||
}
|
||||
?>
|
||||
</select>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<button class="dropdown-item" id="darkModeToggle" onclick="toggleDarkMode()">
|
||||
<?php echo (isset($_SESSION['darkmode']) && $_SESSION['darkmode'] == 'true') ? '🌙' : '🌞'; ?>
|
||||
Toggle Dark Mode
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
if (isset($_SESSION['user'])) {
|
||||
echo '<a class="nav-link" href="/profile">' . $locale['userProfile'] . '</a>';
|
||||
} else {
|
||||
echo '<a class="nav-link" href="/login">' . $locale['login'] . '</a>';
|
||||
}
|
||||
?>
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="<?php echo $locale['search']; ?>"
|
||||
aria-label="<?php echo $locale['search']; ?>">
|
||||
<button class="btn btn-outline-success" type="submit"><?php echo $locale['search']; ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('darkModeToggle').addEventListener('click', function() {
|
||||
// Dark mode toggle script
|
||||
function toggleDarkMode() {
|
||||
var html = document.documentElement;
|
||||
|
||||
// Check the current theme
|
||||
if (html.getAttribute('data-bs-theme') === 'dark') {
|
||||
html.removeAttribute('data-bs-theme'); // Switch to light mode
|
||||
this.innerHTML = '🌞'; // Update the button to reflect light mode
|
||||
localStorage.setItem('darkmode', 'false');
|
||||
updateDarkModeOnServer('false');
|
||||
} else {
|
||||
html.setAttribute('data-bs-theme', 'dark'); // Switch to dark mode
|
||||
this.innerHTML = '🌙'; // Update the button to reflect dark mode
|
||||
localStorage.setItem('darkmode', 'true');
|
||||
updateDarkModeOnServer('true');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateDarkModeOnServer(value) {
|
||||
// Create a new AJAX request
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/settings.php', true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
|
||||
// Define what happens on successful data submission
|
||||
xhr.onload = function() {
|
||||
if (xhr.status >= 200 && xhr.status < 400) {
|
||||
console.log('Dark mode setting updated on server');
|
||||
}
|
||||
};
|
||||
|
||||
// Define what happens in case of an error
|
||||
xhr.onerror = function() {
|
||||
console.error('An error occurred during the request.');
|
||||
};
|
||||
|
||||
// Set the action and send the request
|
||||
xhr.send('action=darkmode&value=' + value);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</header>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
ob_start();
|
||||
|
||||
if (session_id() == '') {
|
||||
session_set_cookie_params(0, '/', '', true, true);
|
||||
session_start();
|
||||
}
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'مرحبا بكم في موقعنا على الإنترنت!',
|
||||
'description' => 'هذا وصف بالإنكليزية.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'الوصف',
|
||||
'userProfile' => "الملف الشخصي للمستخدم",
|
||||
'user' => 'المستخدم',
|
||||
'home' => 'المنزل',
|
||||
|
@ -14,17 +14,17 @@ return [
|
|||
'login' => 'تسجيل الدخول',
|
||||
'message' => 'رسالة',
|
||||
'follow' => 'اتبع',
|
||||
'djs' => 'DJs',
|
||||
'djs' => 'دي جي',
|
||||
"djNotFound" => "تعذر تحميل DJ; إما لم يتم العثور على DJ أو هذا DJ خاص.",
|
||||
"notfound" => "لم يتم العثور على الصفحة",
|
||||
"genre" => "النوع",
|
||||
"genres" => "Genres",
|
||||
"genres" => "أنواع الموسيقى",
|
||||
"genreNotFound" => "لا يمكن تحميل النوع الجيد؛ إما لم يتم العثور على هذا النوع، أو كان فارغا، أو هذا النوع خاص.",
|
||||
"mix-count" => "عدد المزيج",
|
||||
"mixes" => "خلط",
|
||||
"mix" => "خليط",
|
||||
"mixNotFound" => "تعذر تحميل المزيج ؛ إما لم يتم العثور على المزيج ، أو كان فارغا، أو هذا المزيج خاص.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "تعذر تحميل الخليط؛ إما لم يتم العثور على الخليط، أو كان فارغاً، أو هذا الخليط خاص.",
|
||||
"mixName" => "اسم المزيج",
|
||||
"mixDescription" => "وصف المزيج",
|
||||
"mixLength" => "مزيج الطول",
|
||||
|
@ -54,18 +54,23 @@ return [
|
|||
"plays" => "مشغلات",
|
||||
"play" => "تشغيل",
|
||||
"contactus" => "اتصل بنا",
|
||||
"allrightsreserved" => "جميع الحقوق محفوظة.format@@0",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"allrightsreserved" => "جميع الحقوق محفوظة.",
|
||||
"mixshows" => "المزج",
|
||||
"mixshow" => "عرض ميكس",
|
||||
"mixshowName" => "اسم المزج",
|
||||
"share" => "مشاركة",
|
||||
"sahrethismix" => "شارك هذا المزيج",
|
||||
"sharethismixshow" => "شارك هذا المزيج",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "السنة",
|
||||
"sharetotwitter" => "شارك إلى X (تويتر سابقاً)",
|
||||
"sharetofb" => "مشاركة على Facebook",
|
||||
"sharetoig" => "مشاركة على Instagram",
|
||||
"copyurl" => "نسخ الرابط",
|
||||
"urlcopiedtoclipboard" => "تم نسخ عنوان URL إلى الحافظة",
|
||||
"failedtocopyurl" => "فشل في نسخ الرابط",
|
||||
"name" => "الاسم",
|
||||
"username" => "اسم المستخدم",
|
||||
"logout" => "تسجيل الخروج",
|
||||
"upload" => "تحميل",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Vítejte na našich webových stránkách!',
|
||||
'description' => 'To je popis v angličtině.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'L 343, 22.12.2009, s. 1).',
|
||||
'userProfile' => "Profil uživatele",
|
||||
'user' => 'Uživatel',
|
||||
'home' => 'Domů',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Směsi",
|
||||
"mix" => "Míchat",
|
||||
"mixNotFound" => "Nelze načíst mix; buď směs nebyla nalezena, byla prázdná, nebo je tato směs soukromá.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Nelze načíst mixshow; buď mixshow nebyl nalezen, byl prázdný, nebo je tento mixshow soukromý.",
|
||||
"mixName" => "Směsný název",
|
||||
"mixDescription" => "Smíchat popis",
|
||||
"mixLength" => "Míchat délku",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Hrát",
|
||||
"contactus" => "Kontaktujte nás",
|
||||
"allrightsreserved" => "Všechna práva vyhrazena.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Směšovače",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Název Mixshow",
|
||||
"share" => "Sdílet",
|
||||
"sahrethismix" => "Sdílet tuto skladbu",
|
||||
"sharethismixshow" => "Sdílet tento mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "Rok",
|
||||
"sharetotwitter" => "Sdílet na X (dříve Twitter)",
|
||||
"sharetofb" => "Sdílet na Facebook",
|
||||
"sharetoig" => "Sdílet na Instagramu",
|
||||
"copyurl" => "Kopírovat URL",
|
||||
"urlcopiedtoclipboard" => "URL zkopírováno do schránky",
|
||||
"failedtocopyurl" => "Kopírování URL se nezdařilo",
|
||||
"name" => "Název",
|
||||
"username" => "Uživatelské jméno",
|
||||
"logout" => "Odhlásit se",
|
||||
"upload" => "Nahrát",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Velkommen til vores hjemmeside!',
|
||||
'description' => 'Dette er en beskrivelse på engelsk.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Varebeskrivelse',
|
||||
'userProfile' => "Bruger Profil",
|
||||
'user' => 'Bruger',
|
||||
'home' => 'Hjem',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Blandinger",
|
||||
"mix" => "Bland",
|
||||
"mixNotFound" => "Kunne ikke indlæse mix. Enten blev blandingen ikke fundet, var tom, eller også er dette miks privat.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Kunne ikke indlæse mixshow; enten blev mixshowet ikke fundet, var tomt, eller dette mixshow er privat.",
|
||||
"mixName" => "Mix Navn",
|
||||
"mixDescription" => "Bland Beskrivelse",
|
||||
"mixLength" => "Bland Længde",
|
||||
|
@ -57,15 +57,20 @@ return [
|
|||
"allrightsreserved" => "Alle rettigheder forbeholdt.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Mixshow Navn",
|
||||
"share" => "Del",
|
||||
"sahrethismix" => "Del dette mix",
|
||||
"sharethismixshow" => "Del dette mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "År",
|
||||
"sharetotwitter" => "Del til X (tidligere Twitter)",
|
||||
"sharetofb" => "Del på Facebook",
|
||||
"sharetoig" => "Del på Instagram",
|
||||
"copyurl" => "Kopiér URL",
|
||||
"urlcopiedtoclipboard" => "URL kopieret til udklipsholder",
|
||||
"failedtocopyurl" => "Kopiering af URL mislykkedes",
|
||||
"name" => "Navn",
|
||||
"username" => "Brugernavn",
|
||||
"logout" => "Log Ud",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Willkommen auf unserer Website!',
|
||||
'description' => 'Dies ist eine Beschreibung auf Englisch.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Beschreibung',
|
||||
'userProfile' => "Benutzerprofil",
|
||||
'user' => 'Benutzer',
|
||||
'home' => 'Zuhause',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Mischungen",
|
||||
"mix" => "Mix",
|
||||
"mixNotFound" => "Mix konnte nicht geladen werden; entweder wurde der Mix nicht gefunden, war leer oder dieser Mix ist privat.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Mixshow konnte nicht geladen werden; entweder wurde die Mixshow nicht gefunden, war leer oder diese Mixshow ist privat.",
|
||||
"mixName" => "Mix-Name",
|
||||
"mixDescription" => "Mix-Beschreibung",
|
||||
"mixLength" => "Mix-Länge",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Abspielen",
|
||||
"contactus" => "Kontaktiere uns",
|
||||
"allrightsreserved" => "Alle Rechte vorbehalten.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Mix-Shows",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Mix-Show-Name",
|
||||
"share" => "Teilen",
|
||||
"sahrethismix" => "Diesen Mix teilen",
|
||||
"sharethismixshow" => "Diese Mixshow teilen",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "Jahr",
|
||||
"sharetotwitter" => "Auf X teilen (ehemals Twitter)",
|
||||
"sharetofb" => "Auf Facebook teilen",
|
||||
"sharetoig" => "Auf Instagram teilen",
|
||||
"copyurl" => "URL kopieren",
|
||||
"urlcopiedtoclipboard" => "URL in Zwischenablage kopiert",
|
||||
"failedtocopyurl" => "Fehler beim Kopieren der URL",
|
||||
"name" => "Name",
|
||||
"username" => "Benutzername",
|
||||
"logout" => "Abmelden",
|
||||
"upload" => "Hochladen",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Καλώς ήλθατε στην ιστοσελίδα μας!',
|
||||
'description' => 'Αυτή είναι μια περιγραφή στα αγγλικά.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Περιγραφή',
|
||||
'userProfile' => "Προφίλ Χρήστη",
|
||||
'user' => 'Χρήστης',
|
||||
'home' => 'Αρχική',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Μείγματα",
|
||||
"mix" => "Μείγμα",
|
||||
"mixNotFound" => "Δεν ήταν δυνατή η φόρτωση του μείγματος. Είτε το μείγμα δεν βρέθηκε, ήταν άδειο, είτε αυτό το μίγμα είναι ιδιωτικό.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Δεν ήταν δυνατή η φόρτωση του mixshow. Είτε το mixshow δεν βρέθηκε, ήταν άδειο, είτε αυτό το mixshow είναι ιδιωτικό.",
|
||||
"mixName" => "Όνομα Μεικτού",
|
||||
"mixDescription" => "Περιγραφή Μεικτού",
|
||||
"mixLength" => "Μήκος Μείγματος",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Αναπαραγωγή",
|
||||
"contactus" => "Επικοινωνήστε Μαζί Μας",
|
||||
"allrightsreserved" => "Με επιφύλαξη παντός δικαιώματος.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Μείγματα",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Όνομα Mixshow",
|
||||
"share" => "Κοινοποίηση",
|
||||
"sahrethismix" => "Μοιραστείτε αυτό το μίγμα",
|
||||
"sharethismixshow" => "Μοιραστείτε αυτό το μίγμα",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "Έτος",
|
||||
"sharetotwitter" => "Μοιραστείτε στο X (πρώην Twitter)",
|
||||
"sharetofb" => "Κοινοποίηση στο Facebook",
|
||||
"sharetoig" => "Κοινοποίηση στο Instagram",
|
||||
"copyurl" => "Αντιγραφή URL",
|
||||
"urlcopiedtoclipboard" => "Το URL αντιγράφηκε στο πρόχειρο",
|
||||
"failedtocopyurl" => "Αποτυχία αντιγραφής URL",
|
||||
"name" => "Όνομα",
|
||||
"username" => "Όνομα Χρήστη",
|
||||
"logout" => "Αποσύνδεση",
|
||||
"upload" => "Ανέβασμα",
|
||||
];
|
|
@ -71,4 +71,17 @@ return [
|
|||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
"loginToVerifyEmail" => "You must be logged in to verify your email.",
|
||||
"loginToUploadMix" => "You must be logged in to upload a mix.",
|
||||
"verificationCodeRequired" => "Verification code is required.",
|
||||
"recordedDate" => "Recorded Date",
|
||||
"noUploadedFileFound" => "No uploaded file found. Please upload a mix file.",
|
||||
"mixTitleRequired" => "Mix title is required.",
|
||||
"errorUploadCDN" => "Error uploading file to CDN: ",
|
||||
"errorSavingMixDB" => "Error saving mix to database.",
|
||||
"uploadedPendingApproval" => "Mix uploaded successfully and is pending approval.",
|
||||
"uploadHeader1" => "Upload your mix to Utah's DJs"
|
||||
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => '¡Bienvenido a nuestro sitio web!',
|
||||
'description' => 'This is a description in Spanish.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Descripción',
|
||||
'userProfile' => "Perfil de usuario",
|
||||
'user' => 'Usuario',
|
||||
'home' => 'Inicio',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Mezclas",
|
||||
"mix" => "Mezcla",
|
||||
"mixNotFound" => "No se pudo cargar la mezcla; o bien la mezcla no fue encontrada, estaba vacía, o esta mezcla es privada.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "No se pudo cargar mixshow; o bien el mixshow no fue encontrado, estaba vacío, o este mixshow es privado.",
|
||||
"mixName" => "Mezclar nombre",
|
||||
"mixDescription" => "Mezclar descripción",
|
||||
"mixLength" => "Mezclar longitud",
|
||||
|
@ -57,15 +57,20 @@ return [
|
|||
"allrightsreserved" => "Todos los derechos reservados.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Nombre de Mixshow",
|
||||
"share" => "Compartir",
|
||||
"sahrethismix" => "Compartir esta mezcla",
|
||||
"sharethismixshow" => "Compartir este mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "Año",
|
||||
"sharetotwitter" => "Compartir en X (anteriormente Twitter)",
|
||||
"sharetofb" => "Compartir en Facebook",
|
||||
"sharetoig" => "Compartir en Instagram",
|
||||
"copyurl" => "Copiar URL",
|
||||
"urlcopiedtoclipboard" => "URL copiada al portapapeles",
|
||||
"failedtocopyurl" => "Error al copiar la URL",
|
||||
"name" => "Nombre",
|
||||
"username" => "Usuario",
|
||||
"logout" => "Cerrar sesión",
|
||||
"upload" => "Subir",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Tervetuloa sivustollemme!',
|
||||
'description' => 'Tämä on englanninkielinen kuvaus.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Kuvaus',
|
||||
'userProfile' => "Käyttäjän Profiili",
|
||||
'user' => 'Käyttäjä',
|
||||
'home' => 'Koti',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Sekoitukset",
|
||||
"mix" => "Sekoita",
|
||||
"mixNotFound" => "Sekoitusta ei voitu ladata. Sekoitus ei löytynyt, se oli tyhjä, tai tämä sekoitus on yksityinen.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Sekoitusnäyttöä ei voitu ladata. Sekoitusnäyttöä ei löytynyt, se oli tyhjä, tai tämä mixshow on yksityinen.",
|
||||
"mixName" => "Sekoita Nimi",
|
||||
"mixDescription" => "Sekoita Kuvaus",
|
||||
"mixLength" => "Sekoita Pituus",
|
||||
|
@ -57,15 +57,20 @@ return [
|
|||
"allrightsreserved" => "Kaikki oikeudet pidätetään.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Mixshow Nimi",
|
||||
"share" => "Jaa",
|
||||
"sahrethismix" => "Jaa tämä sekoitus",
|
||||
"sharethismixshow" => "Jaa tämä mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "Vuosi",
|
||||
"sharetotwitter" => "Jaa kohtaan X (entinen Twitter)",
|
||||
"sharetofb" => "Jaa Facebookiin",
|
||||
"sharetoig" => "Jaa Instagramiin",
|
||||
"copyurl" => "Kopioi URL",
|
||||
"urlcopiedtoclipboard" => "URL kopioitu leikepöydälle",
|
||||
"failedtocopyurl" => "URL-osoitteen kopiointi epäonnistui",
|
||||
"name" => "Nimi",
|
||||
"username" => "Käyttäjätunnus",
|
||||
"logout" => "Kirjaudu Ulos",
|
||||
"upload" => "Lähetä",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Bienvenue sur notre site Web!',
|
||||
'description' => 'Ceci est une description en français.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Libellé',
|
||||
'userProfile' => "Profil de l'utilisateur",
|
||||
'user' => 'Utilisateur',
|
||||
'home' => 'Domicile',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Mixes",
|
||||
"mix" => "Mélanger",
|
||||
"mixNotFound" => "Impossible de charger le mixage; soit le mixage n'a pas été trouvé, soit le mixage est vide, soit ce mixage est privé.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Impossible de charger le mixshow ; soit le mixshow n'a pas été trouvé, soit le mixshow est vide, soit ce mixshow est privé.",
|
||||
"mixName" => "Nom du mixage",
|
||||
"mixDescription" => "Description du mixage",
|
||||
"mixLength" => "Longueur du mixage",
|
||||
|
@ -57,15 +57,20 @@ return [
|
|||
"allrightsreserved" => "Tous droits réservés.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"mixshowName" => "Nom du Mixshow",
|
||||
"share" => "Partager",
|
||||
"sahrethismix" => "Partager ce mix",
|
||||
"sharethismixshow" => "Partager ce mixshow",
|
||||
"rss" => "Flux RSS",
|
||||
"year" => "Année",
|
||||
"sharetotwitter" => "Partager vers X (anciennement Twitter)",
|
||||
"sharetofb" => "Partager sur Facebook",
|
||||
"sharetoig" => "Partager sur Instagram",
|
||||
"copyurl" => "Copier l'URL",
|
||||
"urlcopiedtoclipboard" => "URL copiée dans le presse-papiers",
|
||||
"failedtocopyurl" => "Échec de la copie de l'URL",
|
||||
"name" => "Nom",
|
||||
"username" => "Nom d'utilisateur",
|
||||
"logout" => "Déconnexion",
|
||||
"upload" => "Charger",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Benvenuto nel nostro sito Web!',
|
||||
'description' => 'Questa è una descrizione in inglese.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Descrizione',
|
||||
'userProfile' => "Profilo Utente",
|
||||
'user' => 'Utente',
|
||||
'home' => 'Home',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Miscele",
|
||||
"mix" => "Miscela",
|
||||
"mixNotFound" => "Impossibile caricare il mix; o il mix non è stato trovato, è vuoto, o questo mix è privato.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Impossibile caricare mixshow; o il mixshow non è stato trovato, è stato vuoto, o questo mixshow è privato.",
|
||||
"mixName" => "Nome Mix",
|
||||
"mixDescription" => "Descrizione Mix",
|
||||
"mixLength" => "Miscela Lunghezza",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Gioca",
|
||||
"contactus" => "Contattaci",
|
||||
"allrightsreserved" => "Tutti i diritti riservati.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Mixshow",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Nome Mixshow",
|
||||
"share" => "Condividi",
|
||||
"sahrethismix" => "Condividi questo mix",
|
||||
"sharethismixshow" => "Condividi questo mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "Anno",
|
||||
"sharetotwitter" => "Condividi su X (ex Twitter)",
|
||||
"sharetofb" => "Condividi su Facebook",
|
||||
"sharetoig" => "Condividi su Instagram",
|
||||
"copyurl" => "Copia URL",
|
||||
"urlcopiedtoclipboard" => "URL copiato negli appunti",
|
||||
"failedtocopyurl" => "Impossibile copiare l'URL",
|
||||
"name" => "Nome",
|
||||
"username" => "Username",
|
||||
"logout" => "Esci",
|
||||
"upload" => "Carica",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => '私たちのウェブサイトへようこそ!',
|
||||
'description' => 'これは英語の説明です。',
|
||||
'desc' => 'Description',
|
||||
'desc' => '説明',
|
||||
'userProfile' => "ユーザープロフィール",
|
||||
'user' => 'ユーザー',
|
||||
'home' => 'ホーム',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "ミックス",
|
||||
"mix" => "ミックス",
|
||||
"mixNotFound" => "ミックスをロードできませんでした。ミックスが見つかりませんでした。空であるか、このミックスがプライベートです。",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "mixshowをロードできませんでした。mixshowが見つかりませんでした。空か、このmixshowはプライベートです。",
|
||||
"mixName" => "ミックス名",
|
||||
"mixDescription" => "ミックスの説明",
|
||||
"mixLength" => "ミックス長さ",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "再生",
|
||||
"contactus" => "お問い合わせ",
|
||||
"allrightsreserved" => "All rights reserved.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "ミックスショー",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"share" => "共有",
|
||||
"sahrethismix" => "このミックスを共有",
|
||||
"sharethismixshow" => "このミックスショーを共有",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "年",
|
||||
"sharetotwitter" => "X(旧 Twitter)で共有",
|
||||
"sharetofb" => "Facebookで共有",
|
||||
"sharetoig" => "Instagramで共有",
|
||||
"copyurl" => "URLをコピー",
|
||||
"urlcopiedtoclipboard" => "URL をクリップボードにコピーしました",
|
||||
"failedtocopyurl" => "URLのコピーに失敗しました",
|
||||
"name" => "名前",
|
||||
"username" => "ユーザー名",
|
||||
"logout" => "ログアウト",
|
||||
"upload" => "アップロード",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Welkom op onze Website!',
|
||||
'description' => 'Dit is een beschrijving in het Engels.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Beschrijving',
|
||||
'userProfile' => "Gebruikers Profiel",
|
||||
'user' => 'Gebruiker',
|
||||
'home' => 'Startpagina',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Mixen",
|
||||
"mix" => "Mengen",
|
||||
"mixNotFound" => "Kon mixen niet laden; of de mix is niet gevonden, was leeg, of deze mix is privé.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Kon mixshow niet laden; of de mixshow is niet gevonden, was leeg, of deze mixshow is privé.",
|
||||
"mixName" => "Mix Naam",
|
||||
"mixDescription" => "Beschrijving mixen",
|
||||
"mixLength" => "Mix Lengte",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Afspelen",
|
||||
"contactus" => "Contacteer ons",
|
||||
"allrightsreserved" => "Alle rechten voorbehouden.format@@0",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Mixseries",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Mixshow Naam",
|
||||
"share" => "Delen",
|
||||
"sahrethismix" => "Deel deze mix",
|
||||
"sharethismixshow" => "Deel deze mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "jaar",
|
||||
"sharetotwitter" => "Deel met X (voorheen Twitter)",
|
||||
"sharetofb" => "Delen op Facebook",
|
||||
"sharetoig" => "Delen op Instagram",
|
||||
"copyurl" => "URL kopiëren",
|
||||
"urlcopiedtoclipboard" => "URL gekopieerd naar klembord",
|
||||
"failedtocopyurl" => "Kopiëren van URL mislukt",
|
||||
"name" => "naam",
|
||||
"username" => "Gebruikersnaam",
|
||||
"logout" => "Afmelden",
|
||||
"upload" => "Uploaden",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Velkommen til vår hjemmeside!',
|
||||
'description' => 'Dette er en beskrivelse på engelsk.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Beskrivelse',
|
||||
'userProfile' => "Bruker profil",
|
||||
'user' => 'Bruker',
|
||||
'home' => 'Hjem',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Mixes",
|
||||
"mix" => "Bland",
|
||||
"mixNotFound" => "Kunne ikke laste blanding, verken var blandingen tom, eller så var denne blandingen privat.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Kan ikke laste mixshow; verken ble ikke funnet, var tom, eller denne blandingen er privat.",
|
||||
"mixName" => "Blandet navn",
|
||||
"mixDescription" => "Bland beskrivelse",
|
||||
"mixLength" => "Blandet lengde",
|
||||
|
@ -57,15 +57,20 @@ return [
|
|||
"allrightsreserved" => "Med enerett.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Mixshow navn",
|
||||
"share" => "Del",
|
||||
"sahrethismix" => "Del denne blandingen",
|
||||
"sharethismixshow" => "Del denne blandingen",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "År",
|
||||
"sharetotwitter" => "Del til X (tidligere Twitter)",
|
||||
"sharetofb" => "Del på Facebook",
|
||||
"sharetoig" => "Del på Instagram",
|
||||
"copyurl" => "Kopier URL",
|
||||
"urlcopiedtoclipboard" => "URL kopiert til utklippstavlen",
|
||||
"failedtocopyurl" => "Klarte ikke å kopiere URL",
|
||||
"name" => "Navn",
|
||||
"username" => "Brukernavn",
|
||||
"logout" => "Logg",
|
||||
"upload" => "Last opp",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Witamy na naszej stronie internetowej!',
|
||||
'description' => 'To jest opis w języku angielskim.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Opis',
|
||||
'userProfile' => "Profil użytkownika",
|
||||
'user' => 'Użytkownik',
|
||||
'home' => 'Strona główna',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Mixy",
|
||||
"mix" => "Mieszanina",
|
||||
"mixNotFound" => "Nie można załadować miksy; albo mieszanina nie została znaleziona, była pusta lub ta miksy jest prywatna.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Nie można załadować mixshow; albo mixshow nie został znaleziony, był pusty, albo mixshow jest prywatny.",
|
||||
"mixName" => "Nazwa mixu",
|
||||
"mixDescription" => "Opis mieszaniny",
|
||||
"mixLength" => "Długość mieszania",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Odtwórz",
|
||||
"contactus" => "Skontaktuj się z nami",
|
||||
"allrightsreserved" => "Wszystkie prawa zastrzeżone.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Mieszanki",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Nazwa Mixshow",
|
||||
"share" => "Udostępnij",
|
||||
"sahrethismix" => "Udostępnij ten koszyk",
|
||||
"sharethismixshow" => "Udostępnij ten mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "W związku z tym Komisja stwierdza, że środek 1 stanowi pomoc państwa w rozumieniu art. 107 ust. 1 Traktatu.",
|
||||
"sharetotwitter" => "Udostępnij do X (dawniej Twitter)",
|
||||
"sharetofb" => "Udostępnij na Facebooku",
|
||||
"sharetoig" => "Udostępnij na Instagramie",
|
||||
"copyurl" => "Kopiuj adres URL",
|
||||
"urlcopiedtoclipboard" => "Adres URL skopiowany do schowka",
|
||||
"failedtocopyurl" => "Nie udało się skopiować adresu URL",
|
||||
"name" => "Nazwisko",
|
||||
"username" => "Nazwa użytkownika",
|
||||
"logout" => "Wyloguj się",
|
||||
"upload" => "Prześlij",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Bem-vindo ao nosso site!',
|
||||
'description' => 'Esta é uma descrição em inglês.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Descrição:',
|
||||
'userProfile' => "Informações do Perfil",
|
||||
'user' => 'Usuário',
|
||||
'home' => 'Residencial',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Misturar",
|
||||
"mix" => "Mistura",
|
||||
"mixNotFound" => "Não foi possível carregar o mix; ou a mistura não foi encontrada, estava vazia, ou esta mistura é privada.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Não foi possível carregar o mixshow; ou o mixshow não foi encontrado, estava vazio ou este mixshow é privado.",
|
||||
"mixName" => "Nome do mix",
|
||||
"mixDescription" => "Descrição Misto",
|
||||
"mixLength" => "Comprimento Misturado",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Reproduzir",
|
||||
"contactus" => "Entre em contato",
|
||||
"allrightsreserved" => "Todos os direitos reservados.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Misturas",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"mixshowName" => "Nome do Mixshow",
|
||||
"share" => "Compartilhar",
|
||||
"sahrethismix" => "Compartilhe esta mistura",
|
||||
"sharethismixshow" => "Compartilhar este mixshow",
|
||||
"rss" => "Resposta",
|
||||
"year" => "ano",
|
||||
"sharetotwitter" => "Compartilhar para X (antigo Twitter)",
|
||||
"sharetofb" => "Compartilhar no Facebook",
|
||||
"sharetoig" => "Compartilhar com o Instagram",
|
||||
"copyurl" => "Copiar URL",
|
||||
"urlcopiedtoclipboard" => "URL copiado para área de transferência",
|
||||
"failedtocopyurl" => "Falha ao copiar URL",
|
||||
"name" => "Nome:",
|
||||
"username" => "Usuário:",
|
||||
"logout" => "Desconectar",
|
||||
"upload" => "Transferir",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Bem-vindo ao nosso site!',
|
||||
'description' => 'Esta é uma descrição em inglês.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Descrição:',
|
||||
'userProfile' => "Informações do Perfil",
|
||||
'user' => 'Usuário',
|
||||
'home' => 'Residencial',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Misturar",
|
||||
"mix" => "Mistura",
|
||||
"mixNotFound" => "Não foi possível carregar o mix; ou a mistura não foi encontrada, estava vazia, ou esta mistura é privada.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Não foi possível carregar o mixshow; ou o mixshow não foi encontrado, estava vazio ou este mixshow é privado.",
|
||||
"mixName" => "Nome do mix",
|
||||
"mixDescription" => "Descrição Misto",
|
||||
"mixLength" => "Comprimento Misturado",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Reproduzir",
|
||||
"contactus" => "Entre em contato",
|
||||
"allrightsreserved" => "Todos os direitos reservados.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Misturas",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"mixshowName" => "Nome do Mixshow",
|
||||
"share" => "Compartilhar",
|
||||
"sahrethismix" => "Compartilhe esta mistura",
|
||||
"sharethismixshow" => "Compartilhar este mixshow",
|
||||
"rss" => "Resposta",
|
||||
"year" => "ano",
|
||||
"sharetotwitter" => "Compartilhar para X (antigo Twitter)",
|
||||
"sharetofb" => "Compartilhar no Facebook",
|
||||
"sharetoig" => "Compartilhar com o Instagram",
|
||||
"copyurl" => "Copiar URL",
|
||||
"urlcopiedtoclipboard" => "URL copiado para área de transferência",
|
||||
"failedtocopyurl" => "Falha ao copiar URL",
|
||||
"name" => "Nome:",
|
||||
"username" => "Usuário:",
|
||||
"logout" => "Desconectar",
|
||||
"upload" => "Transferir",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Bun venit pe site-ul nostru!',
|
||||
'description' => 'Aceasta este o descriere în engleză.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Descriere',
|
||||
'userProfile' => "Profil utilizator",
|
||||
'user' => 'Utilizator',
|
||||
'home' => 'Acasă',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Mixuri",
|
||||
"mix" => "Amestecă",
|
||||
"mixNotFound" => "Nu s-a putut încărca mixul; fie mixul nu a fost găsit, a fost gol, fie acest mix este privat.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Nu s-a putut încărca mixshow-ul; fie emisiunea mixtă nu a fost găsită, a fost goală, fie acest spectacol mixt este privat.",
|
||||
"mixName" => "Nume mix",
|
||||
"mixDescription" => "Descriere mixtă",
|
||||
"mixLength" => "Amestecă lungimea",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Redare",
|
||||
"contactus" => "Contactează-ne",
|
||||
"allrightsreserved" => "Toate drepturile rezervate.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Amestec",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Nume Mixshow",
|
||||
"share" => "Distribuie",
|
||||
"sahrethismix" => "Distribuie acest mix",
|
||||
"sharethismixshow" => "Distribuie acest spectacol mixt",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "An",
|
||||
"sharetotwitter" => "Distribuie pe X (anterior Twitter)",
|
||||
"sharetofb" => "Distribuie pe Facebook",
|
||||
"sharetoig" => "Distribuie pe Instagram",
|
||||
"copyurl" => "Copiază URL-ul",
|
||||
"urlcopiedtoclipboard" => "URL copiat în clipboard",
|
||||
"failedtocopyurl" => "Copierea adresei URL a eșuat",
|
||||
"name" => "Nume",
|
||||
"username" => "Nume",
|
||||
"logout" => "Deconectare",
|
||||
"upload" => "Incarca",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Добро пожаловать на наш сайт!',
|
||||
'description' => 'Это описание на английском языке.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Описание',
|
||||
'userProfile' => "Профиль пользователя",
|
||||
'user' => 'Пользователь',
|
||||
'home' => 'Домашний',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Миксы",
|
||||
"mix" => "Микс",
|
||||
"mixNotFound" => "Не удалось загрузить смесь; либо смесь не найдена, либо эта смесь является приватной.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Не удалось загрузить mixshow; либо mixshow не найден, либо эта mixshow является приватной.",
|
||||
"mixName" => "Название микса",
|
||||
"mixDescription" => "Описание смеси",
|
||||
"mixLength" => "Длина микса",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Играть",
|
||||
"contactus" => "Свяжитесь с нами",
|
||||
"allrightsreserved" => "Все права защищены.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Смешанные шоу",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"mixshowName" => "Название Mixshow",
|
||||
"share" => "Поделиться",
|
||||
"sahrethismix" => "Поделиться этой смесью",
|
||||
"sharethismixshow" => "Поделиться этой смесительной шоу",
|
||||
"rss" => "RSS-лента",
|
||||
"year" => "Год",
|
||||
"sharetotwitter" => "Поделиться с X (ранее Twitter)",
|
||||
"sharetofb" => "Поделиться на Facebook",
|
||||
"sharetoig" => "Поделиться в Instagram",
|
||||
"copyurl" => "Копировать URL",
|
||||
"urlcopiedtoclipboard" => "URL скопирован в буфер обмена",
|
||||
"failedtocopyurl" => "Не удалось скопировать URL",
|
||||
"name" => "Наименование",
|
||||
"username" => "Имя пользователя",
|
||||
"logout" => "Выйти",
|
||||
"upload" => "Выгрузить",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Välkommen till vår webbplats!',
|
||||
'description' => 'Detta är en beskrivning på engelska.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Beskrivning',
|
||||
'userProfile' => "Användarprofil",
|
||||
'user' => 'Användare',
|
||||
'home' => 'Hem',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Blandningar",
|
||||
"mix" => "Blanda",
|
||||
"mixNotFound" => "Kunde inte ladda mix; antingen hittades blandningen inte, var tom, eller denna blandning är privat.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Kunde inte ladda mixshow; antingen hittades inte mixshowen, var tom, eller så är denna mixshow privat.",
|
||||
"mixName" => "Blanda namn",
|
||||
"mixDescription" => "Blanda beskrivning",
|
||||
"mixLength" => "Blanda längd",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Spela",
|
||||
"contactus" => "Kontakta oss",
|
||||
"allrightsreserved" => "Alla rättigheter reserverade.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Blandningsserier",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Mixseriens namn",
|
||||
"share" => "Dela",
|
||||
"sahrethismix" => "Dela denna mix",
|
||||
"sharethismixshow" => "Dela denna mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "År",
|
||||
"sharetotwitter" => "Dela till X (tidigare Twitter)",
|
||||
"sharetofb" => "Dela på Facebook",
|
||||
"sharetoig" => "Dela på Instagram",
|
||||
"copyurl" => "Kopiera URL",
|
||||
"urlcopiedtoclipboard" => "URL kopierad till urklipp",
|
||||
"failedtocopyurl" => "Kunde inte kopiera URL",
|
||||
"name" => "Namn",
|
||||
"username" => "Användarnamn",
|
||||
"logout" => "Utloggning",
|
||||
"upload" => "Ladda upp",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => 'Ласкаво просимо на наш сайт!',
|
||||
'description' => 'Це опис англійською.',
|
||||
'desc' => 'Description',
|
||||
'desc' => 'Опис',
|
||||
'userProfile' => "Профіль користувача",
|
||||
'user' => 'Користувач',
|
||||
'home' => 'Домашній екран',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "Змішати",
|
||||
"mix" => "Змішати",
|
||||
"mixNotFound" => "Не вдалось завантажити мікс; або суміш не знайдена, була порожня, або ця суміш приватна.",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "Не вдалося завантажити шоу, або не знайдене змішування, було порожнім, або це змішання є приватним.",
|
||||
"mixName" => "Змішане ім'я",
|
||||
"mixDescription" => "Змішати опис",
|
||||
"mixLength" => "Довжина мікса",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "Відтворити",
|
||||
"contactus" => "Зв’язатись з нами",
|
||||
"allrightsreserved" => "Усі права захищені.",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "Змішані серіали",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "Змішане ім'я",
|
||||
"share" => "Поділитись",
|
||||
"sahrethismix" => "Поділитися цим мікшуванням",
|
||||
"sharethismixshow" => "Поділитися цим мікшалом",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "Рік",
|
||||
"sharetotwitter" => "Поділитись до X (раніше Twitter)",
|
||||
"sharetofb" => "Поділитись у Facebook",
|
||||
"sharetoig" => "Поділитися в Instagram",
|
||||
"copyurl" => "Копіювати посилання",
|
||||
"urlcopiedtoclipboard" => "URL скопійовано до буферу обміну",
|
||||
"failedtocopyurl" => "Не вдалося скопіювати URL-адресу",
|
||||
"name" => "Ім'я",
|
||||
"username" => "Ім'я користувача",
|
||||
"logout" => "Вихід із системи",
|
||||
"upload" => "Вивантажити",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
return [
|
||||
'welcome' => '欢迎来到我们的网站!',
|
||||
'description' => '这是英文描述。',
|
||||
'desc' => 'Description',
|
||||
'desc' => '描述',
|
||||
'userProfile' => "用户资料",
|
||||
'user' => '用户',
|
||||
'home' => '首页',
|
||||
|
@ -24,7 +24,7 @@ return [
|
|||
"mixes" => "混音器",
|
||||
"mix" => "混音器",
|
||||
"mixNotFound" => "无法加载混合物;混合物未找到, 为空, 或者这种混合是私有的。",
|
||||
"mixshowNotFound" => "Could not load mixshow; either the mixshow wasn't found, was empty, or this mixshow is private.",
|
||||
"mixshowNotFound" => "无法加载mixshow;要么找不到mixshow,要么是空的,要么这个mixshow是私有的。",
|
||||
"mixName" => "混合名称",
|
||||
"mixDescription" => "混合描述",
|
||||
"mixLength" => "混合长度",
|
||||
|
@ -55,17 +55,22 @@ return [
|
|||
"play" => "播放",
|
||||
"contactus" => "联系我们",
|
||||
"allrightsreserved" => "版权所有。",
|
||||
"mixshows" => "Mixshows",
|
||||
"mixshows" => "混合显示",
|
||||
"mixshow" => "Mixshow",
|
||||
"mixshowName" => "Mixshow Name",
|
||||
"share" => "Share",
|
||||
"sahrethismix" => "Share this mix",
|
||||
"sharethismixshow" => "Share this mixshow",
|
||||
"mixshowName" => "混合节目名称",
|
||||
"share" => "分享",
|
||||
"sahrethismix" => "分享这个组合",
|
||||
"sharethismixshow" => "分享此 mixshow",
|
||||
"rss" => "RSS",
|
||||
"year" => "Year",
|
||||
"sharetotwitter" => "Share to X (formerly Twitter)",
|
||||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"year" => "年份",
|
||||
"sharetotwitter" => "分享到 X (旧的 Twitter)",
|
||||
"sharetofb" => "分享到 Facebook",
|
||||
"sharetoig" => "分享到 Instagram",
|
||||
"copyurl" => "复制 URL",
|
||||
"urlcopiedtoclipboard" => "URL 已复制到剪贴板",
|
||||
"failedtocopyurl" => "无法复制 URL",
|
||||
"name" => "名称",
|
||||
"username" => "用户名",
|
||||
"logout" => "注销",
|
||||
"upload" => "上传",
|
||||
];
|
|
@ -67,5 +67,10 @@ return [
|
|||
"sharetofb" => "Share to Facebook",
|
||||
"sharetoig" => "Share to Instagram",
|
||||
"copyurl" => "Copy URL",
|
||||
|
||||
"urlcopiedtoclipboard" => "URL copied to clipboard",
|
||||
"failedtocopyurl" => "Failed to copy URL",
|
||||
"name" => "Name",
|
||||
"username" => "Username",
|
||||
"logout" => "Logout",
|
||||
"upload" => "Upload",
|
||||
];
|
58
login.php
58
login.php
|
@ -1,15 +1,16 @@
|
|||
<?php
|
||||
require_once 'includes/globals.php';
|
||||
|
||||
if (isset($_SESSION['user'])) {
|
||||
header("Location: /profile");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
use DJMixHosting\User;
|
||||
use DJMixHosting\SessionManager;
|
||||
|
||||
// If a user is already logged in, redirect to profile.
|
||||
if (SessionManager::getUser()) {
|
||||
header("Location: /profile");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Generate a CSRF token if one is not set
|
||||
if (!isset($_SESSION['csrf_token'])) {
|
||||
|
@ -19,9 +20,7 @@ if (!isset($_SESSION['csrf_token'])) {
|
|||
$title = $locale['home'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
|
||||
if (isset($_POST['email'], $_POST['password'], $_POST['csrf_token'])) {
|
||||
|
||||
// Check the CSRF token
|
||||
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
|
||||
$_SESSION['error'] = $locale['message'] . ": Invalid form submission. Please try again.";
|
||||
|
@ -32,32 +31,42 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
$db = new Database($config);
|
||||
$user = new User($db);
|
||||
$result = $user->login($email, $password);
|
||||
if ($result === true) {
|
||||
// Successful login, redirect to profile page
|
||||
|
||||
// If login() returns an array, the login was successful.
|
||||
if (is_array($result)) {
|
||||
SessionManager::setUser([
|
||||
'id' => $result['id'],
|
||||
'email' => $result['email'],
|
||||
'username' => $result['username'],
|
||||
'firstName' => $result['firstName'],
|
||||
'lastName' => $result['lastName'],
|
||||
'role' => $result['isAdmin'] ? 'admin' : 'user'
|
||||
]);
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
} else {
|
||||
// Set error message from login method (includes lockout messages)
|
||||
// Login failed; $result contains an error message.
|
||||
$_SESSION['error'] = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require_once 'includes/header.php';
|
||||
?>
|
||||
<section class="login-section py-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-5">
|
||||
<?php
|
||||
if (isset($_SESSION['error'])) {
|
||||
if (isset($_SESSION['error'])) {
|
||||
echo '<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
|
||||
' . htmlspecialchars($_SESSION['error']) . '
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>';
|
||||
unset($_SESSION['error']);
|
||||
}
|
||||
?>
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<section class="login-section py-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-5">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-4">
|
||||
<h3 class="text-center mb-4">Login</h3>
|
||||
|
@ -97,30 +106,21 @@ require_once 'includes/header.php';
|
|||
</section>
|
||||
|
||||
<style>
|
||||
.login-section {
|
||||
min-height: calc(100vh - 200px); /* Adjust based on your header/footer height */
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.15);
|
||||
}
|
||||
|
||||
#togglePassword:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
#togglePassword:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.input-group .form-control:focus {
|
||||
border-right: 1px solid #86b7fe;
|
||||
}
|
||||
|
||||
.input-group .btn {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
|
14
logout.php
Normal file
14
logout.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
require_once 'includes/globals.php';
|
||||
|
||||
use DJMixHosting\SessionManager;
|
||||
|
||||
// Start the session (if not already started)
|
||||
SessionManager::start();
|
||||
|
||||
// Destroy the session using the SessionManager
|
||||
SessionManager::destroy();
|
||||
|
||||
// Redirect to the homepage
|
||||
header("Location: /");
|
||||
exit();
|
102
mix.php
102
mix.php
|
@ -13,9 +13,9 @@ $db = new Database($config);
|
|||
$mixFound = false;
|
||||
if (isset($_GET['mix']) && $_GET['mix'] != "") {
|
||||
$mix = new Mix($_GET['mix'], $db);
|
||||
if ($mix->get_name() != "") {
|
||||
if ($mix->getName() != "") {
|
||||
$mixFound = true;
|
||||
$title = $mix->get_name();
|
||||
$title = $mix->getName();
|
||||
} else {
|
||||
$title = $locale['notfound'];
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ require_once 'includes/header.php'; ?>
|
|||
<li class="breadcrumb-item"><?php echo $locale['mixes']; ?></li>
|
||||
<li class="breadcrumb-item active"
|
||||
aria-current="page"><?php
|
||||
if (isset($mix) && $mix->get_name() != "") {
|
||||
echo $mix->get_name();
|
||||
if (isset($mix) && $mix->getName() != "") {
|
||||
echo $mix->getName();
|
||||
} else {
|
||||
echo $locale['notfound'];
|
||||
}
|
||||
|
@ -59,14 +59,14 @@ require_once 'includes/header.php'; ?>
|
|||
<div class="card mb-4">
|
||||
<div class="card-body bg-body-secondary text-center">
|
||||
<?php
|
||||
if ($mix->get_img() != "") {
|
||||
echo "<img src='" . $mix->get_img() . "' alt='avatar' class='img-fluid' style='width: 150px;'>";
|
||||
if ($mix->getCover() != "") {
|
||||
echo "<img src='" . $mix->getCover() . "' alt='avatar' class='img-fluid' style='width: 150px;'>";
|
||||
} ?>
|
||||
<h1 class="my-3 fs-4"><?php echo $mix->get_name();
|
||||
<h1 class="my-3 fs-4"><?php echo $mix->getName();
|
||||
?></h1>
|
||||
<?php
|
||||
if ($mix->get_description() != "") {
|
||||
echo "<h2 class='text-muted mb-4 fs-6'>" . $mix->get_description() . "</h2>";
|
||||
if ($mix->getDescription() != "") {
|
||||
echo "<h2 class='text-muted mb-4 fs-6'>" . $mix->getDescription() . "</h2>";
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
|
@ -75,12 +75,12 @@ require_once 'includes/header.php'; ?>
|
|||
<div class="card mb-4">
|
||||
<div class="card-body bg-body-secondary text-center">
|
||||
<?php
|
||||
if ($mix->is_download_only()) {
|
||||
echo "<a href='/mix/" . $mix->get_slug() . "/download" . "' class='btn btn-primary w-100 mb-2'>" . $locale['download'] . "</a>";
|
||||
if ($mix->isDownloadOnly()) {
|
||||
echo "<a href='/mix/" . $mix->getSlug() . "/download" . "' class='btn btn-primary w-100 mb-2'>" . $locale['download'] . "</a>";
|
||||
} else {
|
||||
?>
|
||||
<div id="audio-player">
|
||||
<audio id="audio" src="<?php echo $mix->get_url(); ?>"></audio>
|
||||
<audio id="audio" src="<?php echo $mix->getUrl(); ?>"></audio>
|
||||
<div class="player-controls">
|
||||
<button id="play-pause-btn">
|
||||
<i class="fas fa-play" style="font-size: 12px;"></i>
|
||||
|
@ -101,10 +101,10 @@ require_once 'includes/header.php'; ?>
|
|||
<?php echo $locale['share']; ?>
|
||||
</button>
|
||||
|
||||
<?php if (!$mix->is_download_only()) : ?>
|
||||
<?php if (!$mix->isDownloadOnly()) : ?>
|
||||
<a href="<?php
|
||||
|
||||
echo "/mix/" . $mix->get_slug() . "/download";
|
||||
echo "/mix/" . $mix->getSlug() . "/download";
|
||||
|
||||
?>"
|
||||
class="btn btn-primary w-100 mb-2"><?php echo $locale['download']; ?></a>
|
||||
|
@ -121,7 +121,7 @@ require_once 'includes/header.php'; ?>
|
|||
<p class="mb-0"><?php echo $locale['mixname'] ?></p>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<p class="text-muted mb-0"><?php echo $mix->get_name(); ?></p>
|
||||
<p class="text-muted mb-0"><?php echo $mix->getName(); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
@ -134,7 +134,7 @@ require_once 'includes/header.php'; ?>
|
|||
<?php
|
||||
|
||||
// loop through the $mix['djs'] array and output them in comma separated format
|
||||
$djs = $mix->get_djs();
|
||||
$djs = $mix->getDJs();
|
||||
$djCount = count($djs);
|
||||
$i = 0;
|
||||
foreach ($djs as $dj) {
|
||||
|
@ -153,7 +153,7 @@ require_once 'includes/header.php'; ?>
|
|||
</div>
|
||||
</div>
|
||||
<?php
|
||||
$genres = $mix->get_genres();
|
||||
$genres = $mix->getGenres();
|
||||
$genreCount = count($genres);
|
||||
if ($genreCount > 0) {
|
||||
?>
|
||||
|
@ -181,7 +181,7 @@ require_once 'includes/header.php'; ?>
|
|||
</div><?php } ?>
|
||||
|
||||
<?php
|
||||
$mixshows = $mix->get_mixshow();
|
||||
$mixshows = $mix->getMixshow();
|
||||
$mixshowsCount = count($mixshows);
|
||||
if ($mixshowsCount > 0) {
|
||||
?>
|
||||
|
@ -215,7 +215,7 @@ require_once 'includes/header.php'; ?>
|
|||
<div class="col-sm-9">
|
||||
<p class="text-muted mb-0">
|
||||
<?php
|
||||
$time = $mix->get_duration();
|
||||
$time = $mix->getDuration();
|
||||
// Decide the correct singular or plural term
|
||||
$hour_text = $time['h'] == 1 ? $locale['hour'] : $locale['hours'];
|
||||
$minute_text = $time['m'] == 1 ? $locale['minute'] : $locale['minutes'];
|
||||
|
@ -245,7 +245,7 @@ require_once 'includes/header.php'; ?>
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($mix->get_recorded() != ""): ?>
|
||||
<?php if ($mix->getRecorded() != ""): ?>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
|
@ -253,11 +253,11 @@ require_once 'includes/header.php'; ?>
|
|||
</div>
|
||||
<div class="col-sm-9">
|
||||
<p class="text-muted mb-0">
|
||||
<?php echo $mix->get_recorded(); ?>
|
||||
<?php echo $mix->getRecorded(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($mix->get_created() != ""): ?>
|
||||
<?php if ($mix->getCreated() != ""): ?>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
|
@ -265,7 +265,7 @@ require_once 'includes/header.php'; ?>
|
|||
</div>
|
||||
<div class="col-sm-9">
|
||||
<p class="text-muted mb-0">
|
||||
<?php echo $mix->get_created(); ?>
|
||||
<?php echo $mix->getCreated(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
@ -276,7 +276,7 @@ require_once 'includes/header.php'; ?>
|
|||
</div>
|
||||
<div class="col-sm-9">
|
||||
<p class="text-muted mb-0">
|
||||
<?php echo $mix->get_downloads(); ?>
|
||||
<?php echo $mix->getDownloads(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
@ -286,10 +286,10 @@ require_once 'includes/header.php'; ?>
|
|||
</div>
|
||||
<div class="col-sm-9">
|
||||
<p class="text-muted mb-0">
|
||||
<?php echo $mix->get_plays(); ?>
|
||||
<?php echo $mix->getPlaycount(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($mix->get_updated() != ""): ?>
|
||||
<?php if ($mix->getUpdated() != ""): ?>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
|
@ -297,7 +297,7 @@ require_once 'includes/header.php'; ?>
|
|||
</div>
|
||||
<div class="col-sm-9">
|
||||
<p class="text-muted mb-0">
|
||||
<?php echo $mix->get_updated(); ?>
|
||||
<?php echo $mix->getUpdated(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
@ -307,9 +307,9 @@ require_once 'includes/header.php'; ?>
|
|||
|
||||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: '<?php echo addslashes($mix->get_name()); ?>',
|
||||
title: '<?php echo addslashes($mix->getName()); ?>',
|
||||
artist: '<?php
|
||||
$djs = $mix->get_djs();
|
||||
$djs = $mix->getDJs();
|
||||
$djCount = count($djs);
|
||||
$i = 0;
|
||||
$djnamelist = [];
|
||||
|
@ -319,15 +319,15 @@ require_once 'includes/header.php'; ?>
|
|||
}
|
||||
|
||||
echo addslashes(implode(", ", $djnamelist));?>',
|
||||
album: '<?php echo addslashes($mix->get_name()); ?>',
|
||||
album: '<?php echo addslashes($mix->getName()); ?>',
|
||||
artwork: [
|
||||
{
|
||||
src: '<?php echo $mix->get_cover('small'); ?>',
|
||||
src: '<?php echo $mix->getCover('small'); ?>',
|
||||
sizes: '96x96',
|
||||
type: 'image/jpeg'
|
||||
},
|
||||
{
|
||||
src: '<?php echo $mix->get_cover('large'); ?>',
|
||||
src: '<?php echo $mix->getCover('large'); ?>',
|
||||
sizes: '128x128',
|
||||
type: 'image/jpeg'
|
||||
}
|
||||
|
@ -359,12 +359,12 @@ require_once 'includes/header.php'; ?>
|
|||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if ($mix->get_tracklist() != []) {
|
||||
if ($mix->getTracklist() != []) {
|
||||
echo "<div class='card mb-4 bg-body-secondary'>";
|
||||
echo "<div class='card-body '>";
|
||||
echo "<p class='mb-4'><span class='text-primary font-italic me-1'>" . $locale['tracklist'] . "</span></p>";
|
||||
echo "<ul class='list-group list-group-flush rounded-3 bg-body-secondary'>";
|
||||
$tracklist = $mix->get_tracklist();
|
||||
$tracklist = $mix->getTracklist();
|
||||
foreach ($tracklist as $track) {
|
||||
echo "<li class='list-group-item bg-body-secondary d-flex justify-content-between align-items-center'>";
|
||||
echo $track;
|
||||
|
@ -443,7 +443,7 @@ require_once 'includes/header.php'; ?>
|
|||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({mix: '<?php echo $mix->get_id(); ?>'}),
|
||||
body: JSON.stringify({mix: '<?php echo $mix->getId(); ?>'}),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
|
@ -461,52 +461,52 @@ require_once 'includes/header.php'; ?>
|
|||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "MusicRecording",
|
||||
"name": "<?php echo $mix->get_name(); ?>",
|
||||
"name": "<?php echo $mix->getName(); ?>",
|
||||
"byArtist": {
|
||||
"@type": "MusicGroup",
|
||||
"name": "<?php
|
||||
$djs = $mix->get_djs();
|
||||
$djs = $mix->getDJs();
|
||||
echo $djs[0]->getName();
|
||||
?>",
|
||||
"image": "<?php echo $djs[0]->getImg(); ?>"
|
||||
},
|
||||
"inAlbum": {
|
||||
"@type": "MusicAlbum",
|
||||
"name": "<?php echo $mix->get_name(); ?>"
|
||||
"name": "<?php echo $mix->getName(); ?>"
|
||||
},
|
||||
"genre": "<?php
|
||||
$genre = new Genre($mix->get_genres()[0], $db);
|
||||
$genre = new Genre($mix->getGenres()[0], $db);
|
||||
echo $genre->get_name();
|
||||
?>",
|
||||
"url": "<?php echo "https://utahsdjs.com/mix/" . $mix->get_slug(); ?>",
|
||||
"image": "<?php echo $mix->get_cover(); ?>",
|
||||
"url": "<?php echo "https://utahsdjs.com/mix/" . $mix->getSlug(); ?>",
|
||||
"image": "<?php echo $mix->getCover(); ?>",
|
||||
|
||||
"duration": "<?php echo $mix->get_duration()['S']; ?>",
|
||||
"duration": "<?php echo $mix->getDuration()['S']; ?>",
|
||||
<?php
|
||||
// if recorded is empty, use created; if created is empty, use 2008-01-01;
|
||||
if (empty($mix->get_recorded())) {
|
||||
if (empty($mix->get_created())) {
|
||||
if (empty($mix->getRecorded())) {
|
||||
if (empty($mix->getCreated())) {
|
||||
$recorded = '2008-01-01 00:00:00';
|
||||
} else {
|
||||
$recorded = $mix->get_created();
|
||||
$recorded = $mix->getCreated();
|
||||
}
|
||||
} else {
|
||||
$recorded = $mix->get_recorded();
|
||||
$recorded = $mix->getRecorded();
|
||||
|
||||
} ?>
|
||||
|
||||
"datePublished": "<?php echo $recorded; ?>",
|
||||
"description": "<?php
|
||||
|
||||
if (empty($mix->get_description())) {
|
||||
$description = 'Listen to ' . $mix->get_name() . ' on Utah\'s DJs.';
|
||||
if (empty($mix->getDescription())) {
|
||||
$description = 'Listen to ' . $mix->getName() . ' on Utah\'s DJs.';
|
||||
}
|
||||
echo $mix->get_description(); ?>",
|
||||
echo $mix->getDescription(); ?>",
|
||||
"interactionStatistic": {
|
||||
"@type": "InteractionCounter",
|
||||
"interactionType": "https://schema.org/ListenAction",
|
||||
"userInteractionCount": "<?php echo $mix->get_plays() + $mix->get_downloads() ?>",
|
||||
"url": "<?php echo "https://utahsdjs.com/mix/" . $mix->get_slug() . "/download"; ?>"
|
||||
"userInteractionCount": "<?php echo $mix->getPlaycount() + $mix->getDownloads() ?>",
|
||||
"url": "<?php echo "https://utahsdjs.com/mix/" . $mix->getSlug() . "/download"; ?>"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
BIN
phing-latest.phar
Normal file
BIN
phing-latest.phar
Normal file
Binary file not shown.
158
profile.php
158
profile.php
|
@ -1,43 +1,116 @@
|
|||
<?php
|
||||
// profile.php
|
||||
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
// Make sure the user is authenticated; otherwise redirect to login.
|
||||
if (!isset($_SESSION['user'])) {
|
||||
use DJMixHosting\Database;
|
||||
use DJMixHosting\User;
|
||||
use DJMixHosting\SessionManager;
|
||||
|
||||
// Ensure the session is started and the user is authenticated.
|
||||
SessionManager::start();
|
||||
if (!SessionManager::getUser()) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
use DJMixHosting\Database;
|
||||
$db = new Database($config);
|
||||
$userId = SessionManager::getUser()['id'];
|
||||
// Instantiate the User object; this loads user details internally.
|
||||
$user = new User($db, $userId);
|
||||
|
||||
// Retrieve the full user record from the database
|
||||
$userId = $_SESSION['user']['id'];
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$userData = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
// Determine if editing should be disabled (if email not verified)
|
||||
$editingDisabled = (int)$userData['emailVerified'] !== 1; // Assuming 1 means verified
|
||||
|
||||
// Optionally, set a flag for showing an alert to verify email.
|
||||
$alertMessage = "";
|
||||
if ($editingDisabled) {
|
||||
$alertMessage = "Please verify your email to enable profile editing.";
|
||||
// Handle POST requests for profile updates.
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
if (!isset($_POST['action'])) {
|
||||
throw new Exception("No action specified.");
|
||||
}
|
||||
$action = $_POST['action'];
|
||||
switch ($action) {
|
||||
case 'updateUsername':
|
||||
$newUsername = trim($_POST['new_username'] ?? '');
|
||||
$message = $user->updateUsername($newUsername);
|
||||
$_SESSION['success'] = $message;
|
||||
break;
|
||||
case 'updateEmail':
|
||||
$newEmail = trim($_POST['new_email'] ?? '');
|
||||
$message = $user->updateEmail($newEmail, $config);
|
||||
$_SESSION['success'] = $message;
|
||||
break;
|
||||
case 'updateName':
|
||||
$firstName = trim($_POST['first_name'] ?? '');
|
||||
$lastName = trim($_POST['last_name'] ?? '');
|
||||
$message = $user->updateName($firstName, $lastName);
|
||||
$_SESSION['success'] = $message;
|
||||
break;
|
||||
case 'updatePassword':
|
||||
$currentPassword = $_POST['current_password'] ?? '';
|
||||
$newPassword = $_POST['new_password'] ?? '';
|
||||
$confirmPassword = $_POST['confirm_password'] ?? '';
|
||||
$message = $user->updatePassword($currentPassword, $newPassword, $confirmPassword);
|
||||
$_SESSION['success'] = $message;
|
||||
break;
|
||||
// Optionally, handle other actions such as profile picture update.
|
||||
default:
|
||||
throw new Exception("Invalid action.");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$_SESSION['error'] = $e->getMessage();
|
||||
}
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Retrieve user data using getter methods.
|
||||
$userData = [
|
||||
'id' => $user->getId(),
|
||||
'username' => $user->getUsername(),
|
||||
'firstName' => $user->getFirstName(),
|
||||
'lastName' => $user->getLastName(),
|
||||
'email' => $user->getEmail(),
|
||||
'img' => $user->getImg(),
|
||||
'emailVerified' => $user->getVerified(),
|
||||
];
|
||||
|
||||
// Determine if editing should be disabled (e.g., if email not verified)
|
||||
$editingDisabled = ((int)$userData['emailVerified'] !== 1);
|
||||
$alertMessage = $editingDisabled ? "Please verify your email to enable profile editing." : "";
|
||||
|
||||
require_once 'includes/header.php';
|
||||
?>
|
||||
<section>
|
||||
<div class="container py-5">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<nav aria-label="breadcrumb" class="bg-body-tertiary rounded-3 p-3 mb-4">
|
||||
<ol class="breadcrumb mb-0">
|
||||
<li class="breadcrumb-item"><a href="/"><?php echo $locale['home']; ?></a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Profile</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Display Flash Alerts -->
|
||||
<?php if (isset($_SESSION['error'])): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?php echo htmlspecialchars($_SESSION['error']); ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['error']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['success'])): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?php echo htmlspecialchars($_SESSION['success']); ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['success']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="container py-5">
|
||||
<?php if (!empty($alertMessage)): ?>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<?php echo $alertMessage; ?>
|
||||
<?php echo htmlspecialchars($alertMessage); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
|
@ -49,7 +122,7 @@ require_once 'includes/header.php';
|
|||
<img src="<?php echo htmlspecialchars($userData['img'] ?: 'default_profile.png'); ?>"
|
||||
alt="avatar"
|
||||
class="rounded-circle img-fluid" style="width: 150px;">
|
||||
<!-- Remove username from here -->
|
||||
<br/>
|
||||
<button type="button" class="btn btn-sm btn-secondary mb-2"
|
||||
<?php echo ($editingDisabled) ? 'disabled' : ''; ?>
|
||||
data-bs-toggle="modal" data-bs-target="#profilePictureModal">
|
||||
|
@ -74,8 +147,7 @@ require_once 'includes/header.php';
|
|||
<?php echo ($editingDisabled) ? 'disabled' : ''; ?>
|
||||
data-bs-toggle="modal" data-bs-target="#emailModal">
|
||||
Change
|
||||
</button>
|
||||
<?php if (!$userData['emailVerified']): ?>
|
||||
</button><?php if (!$userData['emailVerified']): ?>
|
||||
<button type="button" class="btn btn-sm btn-primary"
|
||||
data-bs-toggle="modal" data-bs-target="#verifyEmailModal">
|
||||
Verify
|
||||
|
@ -88,7 +160,7 @@ require_once 'includes/header.php';
|
|||
<button type="button" class="btn btn-sm btn-secondary"
|
||||
<?php echo ($editingDisabled) ? 'disabled' : ''; ?>
|
||||
data-bs-toggle="modal" data-bs-target="#nameModal">
|
||||
Change Name
|
||||
Change
|
||||
</button>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
|
@ -110,22 +182,36 @@ require_once 'includes/header.php';
|
|||
<p>Followed DJs and recent ratings will appear here once implemented.</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// If the user is an admin, display a new box with session output.
|
||||
$currentUser = SessionManager::getUser();
|
||||
if (isset($currentUser['role']) && $currentUser['role'] === 'admin'):
|
||||
?>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body bg-body-secondary">
|
||||
<h5>Admin Session Output</h5>
|
||||
<pre id="adminSessionOutput"><?php echo htmlspecialchars(print_r($_SESSION, true)); ?></pre>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Modals -->
|
||||
|
||||
<!-- 1. Profile Picture Modal -->
|
||||
<div class="modal fade" id="profilePictureModal" tabindex="-1" aria-labelledby="profilePictureModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form action="update_profile_picture.php" method="post" enctype="multipart/form-data">
|
||||
<form action="profile.php" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action" value="updateProfilePicture">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="profilePictureModalLabel">Change Profile Picture</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- CSRF token can be added here as hidden input if needed -->
|
||||
<!-- CSRF token can be added here as needed -->
|
||||
<div class="mb-3">
|
||||
<label for="profilePicture" class="form-label">Select new profile picture</label>
|
||||
<input type="file" class="form-control" id="profilePicture" name="profile_picture" accept="image/*" required>
|
||||
|
@ -143,7 +229,8 @@ require_once 'includes/header.php';
|
|||
<!-- 2. Username Modal -->
|
||||
<div class="modal fade" id="usernameModal" tabindex="-1" aria-labelledby="usernameModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form action="update_username.php" method="post">
|
||||
<form action="profile.php" method="post">
|
||||
<input type="hidden" name="action" value="updateUsername">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="usernameModalLabel">Change Username</h5>
|
||||
|
@ -167,7 +254,8 @@ require_once 'includes/header.php';
|
|||
<!-- 3. Email Modal -->
|
||||
<div class="modal fade" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form action="update_email.php" method="post">
|
||||
<form action="profile.php" method="post">
|
||||
<input type="hidden" name="action" value="updateEmail">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="emailModalLabel">Change Email</h5>
|
||||
|
@ -215,10 +303,11 @@ require_once 'includes/header.php';
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. Name Modal (First & Last Name) -->
|
||||
<!-- 5. Name Modal -->
|
||||
<div class="modal fade" id="nameModal" tabindex="-1" aria-labelledby="nameModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form action="update_name.php" method="post">
|
||||
<form action="profile.php" method="post">
|
||||
<input type="hidden" name="action" value="updateName">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="nameModalLabel">Change Your Name</h5>
|
||||
|
@ -246,7 +335,8 @@ require_once 'includes/header.php';
|
|||
<!-- 6. Password Modal -->
|
||||
<div class="modal fade" id="passwordModal" tabindex="-1" aria-labelledby="passwordModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form action="update_password.php" method="post">
|
||||
<form action="profile.php" method="post">
|
||||
<input type="hidden" name="action" value="updatePassword">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="passwordModalLabel">Change Password</h5>
|
||||
|
|
66
register.php
66
register.php
|
@ -1,19 +1,20 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
use DJMixHosting\User;
|
||||
use DJMixHosting\SessionManager;
|
||||
use Aws\Ses\SesClient;
|
||||
use Aws\Exception\AwsException;
|
||||
|
||||
// If the user is already logged in, redirect them
|
||||
if(isset($_SESSION['user'])) {
|
||||
if (SessionManager::getUser()) {
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
// Gather form fields
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
|
@ -24,68 +25,55 @@ if($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
|
||||
// Basic validation
|
||||
$errors = [];
|
||||
if(empty($username) || empty($email) || empty($password) || empty($confirm_password) || empty($first_name) || empty($last_name)) {
|
||||
if (empty($username) || empty($email) || empty($password) || empty($confirm_password) || empty($first_name) || empty($last_name)) {
|
||||
$errors[] = "All fields are required.";
|
||||
}
|
||||
if($password !== $confirm_password) {
|
||||
if ($password !== $confirm_password) {
|
||||
$errors[] = "Passwords do not match.";
|
||||
}
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = "Invalid email format.";
|
||||
}
|
||||
if(!preg_match('/^[a-zA-Z0-9_]{3,25}$/', $username)) {
|
||||
if (!preg_match('/^[a-zA-Z0-9_]{3,25}$/', $username)) {
|
||||
$errors[] = "Invalid username format.";
|
||||
}
|
||||
|
||||
if(!empty($errors)) {
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['error'] = implode(" ", $errors);
|
||||
header("Location: register.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = new Database($config);
|
||||
$user = new User($db);
|
||||
|
||||
// Check if username or email already exists
|
||||
$stmt = $db->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
|
||||
$stmt->bind_param("ss", $username, $email);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if($result->num_rows > 0) {
|
||||
$_SESSION['error'] = "Username or email already exists.";
|
||||
try {
|
||||
// Delegate registration to the User class
|
||||
$user_id = $user->newUser($username, $password, $email, $first_name, $last_name);
|
||||
} catch (\Exception $e) {
|
||||
$_SESSION['error'] = $e->getMessage();
|
||||
header("Location: register.php");
|
||||
exit;
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
// Insert the new user record. (Assuming columns firstName and lastName exist.)
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("INSERT INTO users (username, password, email, firstName, lastName, img, emailVerified) VALUES (?, ?, ?, ?, ?, '', 0)");
|
||||
$stmt->bind_param("sssss", $username, $hashed_password, $email, $first_name, $last_name);
|
||||
if(!$stmt->execute()){
|
||||
$_SESSION['error'] = "Registration failed. Please try again.";
|
||||
header("Location: register.php");
|
||||
exit;
|
||||
}
|
||||
$user_id = $stmt->insert_id;
|
||||
$stmt->close();
|
||||
|
||||
// Log the user in
|
||||
$_SESSION['user'] = [
|
||||
// Log the user in using SessionManager
|
||||
SessionManager::setUser([
|
||||
'id' => $user_id,
|
||||
'username' => $username,
|
||||
'email' => $email
|
||||
];
|
||||
]);
|
||||
|
||||
// Trigger email verification: generate a verification code valid for 15 minutes
|
||||
$verification_code = bin2hex(random_bytes(16));
|
||||
$expires_at = date("Y-m-d H:i:s", strtotime("+15 minutes"));
|
||||
// Insert record with purpose 'email_verification'
|
||||
|
||||
// Insert verification record (with purpose 'email_verification')
|
||||
$stmt = $db->prepare("REPLACE INTO email_verifications (user_id, email, verification_code, expires_at, purpose) VALUES (?, ?, ?, ?, 'email_verification')");
|
||||
$stmt->bind_param("isss", $user_id, $email, $verification_code, $expires_at);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Send verification email via AWS SES using config settings
|
||||
// Send verification email via AWS SES
|
||||
$sesClient = new SesClient([
|
||||
'version' => 'latest',
|
||||
'region' => $config['aws']['ses']['region'],
|
||||
|
@ -103,10 +91,8 @@ if($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
$body_text .= "{$verification_link}\n\nYour verification code is: {$verification_code}\nThis code will expire in 15 minutes.";
|
||||
|
||||
try {
|
||||
$result = $sesClient->sendEmail([
|
||||
'Destination' => [
|
||||
'ToAddresses' => [$recipient_email],
|
||||
],
|
||||
$sesClient->sendEmail([
|
||||
'Destination' => ['ToAddresses' => [$recipient_email]],
|
||||
'ReplyToAddresses' => [$sender_email],
|
||||
'Source' => $sender_email,
|
||||
'Message' => [
|
||||
|
@ -139,8 +125,10 @@ require_once 'includes/header.php';
|
|||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6">
|
||||
<?php
|
||||
if(isset($_SESSION['error'])) {
|
||||
echo '<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">' . htmlspecialchars($_SESSION['error']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
if (isset($_SESSION['error'])) {
|
||||
echo '<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">'
|
||||
. htmlspecialchars($_SESSION['error'])
|
||||
. '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['error']);
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
use Aws\Ses\SesClient;
|
||||
use Aws\Exception\AwsException;
|
||||
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($_POST['new_email']) || empty($_POST['new_email'])) {
|
||||
$_SESSION['error'] = "New email address is required.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$new_email = filter_var($_POST['new_email'], FILTER_VALIDATE_EMAIL);
|
||||
if (!$new_email) {
|
||||
$_SESSION['error'] = "Invalid email format.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = new Database($config);
|
||||
$userId = $_SESSION['user']['id'];
|
||||
|
||||
// Update the user's email and mark it as unverified
|
||||
$stmt = $db->prepare("UPDATE users SET email = ?, emailVerified = 0 WHERE id = ?");
|
||||
$stmt->bind_param("si", $new_email, $userId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Generate verification code and expiry (15 minutes from now)
|
||||
$verification_code = bin2hex(random_bytes(16));
|
||||
$expires_at = date("Y-m-d H:i:s", strtotime("+15 minutes"));
|
||||
|
||||
// Store the verification record (using REPLACE to update any existing record for this user and email)
|
||||
$stmt = $db->prepare("REPLACE INTO email_verifications (user_id, email, verification_code, expires_at) VALUES (?, ?, ?, ?)");
|
||||
$stmt->bind_param("isss", $userId, $new_email, $verification_code, $expires_at);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Send email using AWS SES with config settings
|
||||
$sesClient = new SesClient([
|
||||
'version' => 'latest',
|
||||
'region' => $config['aws']['ses']['region'],
|
||||
'credentials' => [
|
||||
'key' => $config['aws']['ses']['access_key'],
|
||||
'secret' => $config['aws']['ses']['secret_key'],
|
||||
]
|
||||
]);
|
||||
|
||||
$sender_email = $config['aws']['ses']['sender_email'];
|
||||
$recipient_email = $new_email;
|
||||
$subject = "Verify Your Email Address";
|
||||
|
||||
// Construct a verification link. Users can click this link to auto-submit the code.
|
||||
$verification_link = $config['app']['url'] . "/verify_email.php?code={$verification_code}";
|
||||
$body_text = "Please verify your email address by clicking the link below or by entering the code in your profile:\n\n";
|
||||
$body_text .= "{$verification_link}\n\nYour verification code is: {$verification_code}\nThis code will expire in 15 minutes.";
|
||||
|
||||
try {
|
||||
$result = $sesClient->sendEmail([
|
||||
'Destination' => [
|
||||
'ToAddresses' => [$recipient_email],
|
||||
],
|
||||
'ReplyToAddresses' => [$sender_email],
|
||||
'Source' => $sender_email,
|
||||
'Message' => [
|
||||
'Body' => [
|
||||
'Text' => [
|
||||
'Charset' => 'UTF-8',
|
||||
'Data' => $body_text,
|
||||
],
|
||||
],
|
||||
'Subject' => [
|
||||
'Charset' => 'UTF-8',
|
||||
'Data' => $subject,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$_SESSION['success'] = "Email updated. A verification email has been sent to your new address.";
|
||||
} catch (AwsException $e) {
|
||||
$_SESSION['error'] = "Failed to send verification email: " . $e->getAwsErrorMessage();
|
||||
}
|
||||
|
||||
header("Location: profile.php");
|
||||
exit;
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
|
||||
// Ensure the user is logged in
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check that both first and last names were provided
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_POST['first_name']) || empty($_POST['last_name'])) {
|
||||
$_SESSION['error'] = "Both first name and last name are required.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$firstName = trim($_POST['first_name']);
|
||||
$lastName = trim($_POST['last_name']);
|
||||
|
||||
$db = new Database($config);
|
||||
$userId = $_SESSION['user']['id'];
|
||||
|
||||
// Update the user's first and last name in the database
|
||||
$stmt = $db->prepare("UPDATE users SET firstName = ?, lastName = ? WHERE id = ?");
|
||||
$stmt->bind_param("ssi", $firstName, $lastName, $userId);
|
||||
if (!$stmt->execute()) {
|
||||
$_SESSION['error'] = "Failed to update name. Please try again.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
// Optionally update session data if you store these fields there
|
||||
$_SESSION['user']['firstName'] = $firstName;
|
||||
$_SESSION['user']['lastName'] = $lastName;
|
||||
|
||||
$_SESSION['success'] = "Name updated successfully.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
|
||||
// Ensure the user is authenticated.
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check for required POST fields.
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_POST['current_password']) || empty($_POST['new_password']) || empty($_POST['confirm_password'])) {
|
||||
$_SESSION['error'] = "All password fields are required.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$current_password = $_POST['current_password'];
|
||||
$new_password = $_POST['new_password'];
|
||||
$confirm_password = $_POST['confirm_password'];
|
||||
|
||||
// Validate that the new password meets the requirements:
|
||||
// - 8 to 32 characters
|
||||
// - at least one uppercase letter
|
||||
// - at least one lowercase letter
|
||||
// - at least one number
|
||||
// - at least one symbol
|
||||
$pattern = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,32}$/';
|
||||
if (!preg_match($pattern, $new_password)) {
|
||||
$_SESSION['error'] = "New password must be 8-32 characters and include at least one uppercase letter, one lowercase letter, one number, and one symbol.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verify that new password and confirmation match.
|
||||
if ($new_password !== $confirm_password) {
|
||||
$_SESSION['error'] = "New password and confirmation do not match.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = new Database($config);
|
||||
$userId = $_SESSION['user']['id'];
|
||||
|
||||
// Retrieve the current password hash from the database.
|
||||
$stmt = $db->prepare("SELECT password FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$userData = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
if (!$userData) {
|
||||
$_SESSION['error'] = "User not found.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verify that the current password is correct.
|
||||
if (!password_verify($current_password, $userData['password'])) {
|
||||
$_SESSION['error'] = "Current password is incorrect.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Hash the new password.
|
||||
$hashed_new_password = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
|
||||
// Update the user's password in the database.
|
||||
$stmt = $db->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
$stmt->bind_param("si", $hashed_new_password, $userId);
|
||||
if (!$stmt->execute()) {
|
||||
$_SESSION['error'] = "Failed to update password. Please try again.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
$_SESSION['success'] = "Password updated successfully.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
|
@ -1,53 +0,0 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['new_username']) || empty($_POST['new_username'])) {
|
||||
$_SESSION['error'] = "New username is required.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$new_username = trim($_POST['new_username']);
|
||||
|
||||
// Validate username (for example, only alphanumeric and underscores, 3-25 characters)
|
||||
if (!preg_match('/^[a-zA-Z0-9_]{3,25}$/', $new_username)) {
|
||||
$_SESSION['error'] = "Invalid username format.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = new Database($config);
|
||||
$userId = $_SESSION['user']['id'];
|
||||
|
||||
// Check if the new username already exists (excluding the current user)
|
||||
$stmt = $db->prepare("SELECT id FROM users WHERE username = ? AND id != ?");
|
||||
$stmt->bind_param("si", $new_username, $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0) {
|
||||
$_SESSION['error'] = "Username already taken.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
// Update the username in the database
|
||||
$stmt = $db->prepare("UPDATE users SET username = ? WHERE id = ?");
|
||||
$stmt->bind_param("si", $new_username, $userId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Update session data
|
||||
$_SESSION['user']['username'] = $new_username;
|
||||
$_SESSION['success'] = "Username updated successfully.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
382
upload.php
382
upload.php
|
@ -1,149 +1,305 @@
|
|||
<?php
|
||||
// upload.php - Step 1: File upload and immediate processing
|
||||
// upload.php - One Page Upload and Details Submission
|
||||
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Upload;
|
||||
use DJMixHosting\CDN;
|
||||
use DJMixHosting\Database;
|
||||
use DJMixHosting\Genres;
|
||||
use DJMixHosting\DJs;
|
||||
|
||||
// Ensure user is authenticated
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header("Location: login.php");
|
||||
$_SESSION['error'] = $locale['loginToUploadMix'];
|
||||
header("Location: /login");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Process the form submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['mix_file'])) {
|
||||
// Get upload config from $config
|
||||
$tmpPath = rtrim($config['uploads']['tmp_path'], '/') . '/';
|
||||
$maxFileSize = $config['uploads']['max_file_size'];
|
||||
$allowedMimeTypes = $config['uploads']['allowed_mime_type'];
|
||||
$allowedFileTypes = $config['uploads']['allowed_file_types'];
|
||||
// If the form was submitted, check which step we're processing via an "action" field.
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Step 1: Handle file upload
|
||||
if (isset($_POST['action']) && $_POST['action'] === 'upload_file') {
|
||||
if (isset($_FILES['mix_file'])) {
|
||||
$upload = new Upload($_FILES['mix_file'], $config);
|
||||
|
||||
$file = $_FILES['mix_file'];
|
||||
|
||||
// Basic file validations
|
||||
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||||
$_SESSION['error'] = "File upload error.";
|
||||
header("Location: upload.php");
|
||||
// Validate the file
|
||||
if (!$upload->validate()) {
|
||||
$_SESSION['error'] = implode(", ", $upload->getErrors());
|
||||
header("Location: /upload");
|
||||
exit;
|
||||
}
|
||||
if ($file['size'] > $maxFileSize) {
|
||||
$_SESSION['error'] = "File is too large.";
|
||||
header("Location: upload.php");
|
||||
// Move the file to the temporary directory
|
||||
if (!$upload->moveFile()) {
|
||||
$_SESSION['error'] = implode(", ", $upload->getErrors());
|
||||
header("Location: /upload");
|
||||
exit;
|
||||
}
|
||||
// Get file extension and mime type
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if (!in_array($ext, $allowedFileTypes)) {
|
||||
$_SESSION['error'] = "File type not allowed.";
|
||||
header("Location: upload.php");
|
||||
exit;
|
||||
}
|
||||
if (!in_array($file['type'], $allowedMimeTypes)) {
|
||||
$_SESSION['error'] = "MIME type not allowed.";
|
||||
header("Location: upload.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Create a unique temporary filename
|
||||
$uniqueName = uniqid() . "." . $ext;
|
||||
$destination = $tmpPath . $uniqueName;
|
||||
|
||||
if (!move_uploaded_file($file['tmp_name'], $destination)) {
|
||||
$_SESSION['error'] = "Failed to save uploaded file.";
|
||||
header("Location: upload.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Process the file: if mp3, extract metadata; if zip, extract each mp3's metadata.
|
||||
$uploadTask = [];
|
||||
$uploadTask['original_name'] = $file['name'];
|
||||
$uploadTask['local_path'] = $destination;
|
||||
$uploadTask['size'] = $file['size'];
|
||||
$uploadTask['ext'] = $ext;
|
||||
|
||||
if ($ext === "mp3") {
|
||||
// Process MP3 file using shell_exec calls (ensure you sanitize arguments)
|
||||
$escapedFile = escapeshellarg($destination);
|
||||
$artist = trim(shell_exec("eyed3 --query='%a%' $escapedFile"));
|
||||
$title = trim(shell_exec("eyed3 --query='%t%' $escapedFile"));
|
||||
$duration = trim(shell_exec("mp3info -p \"%S\" $escapedFile"));
|
||||
// You can extract additional info as needed
|
||||
$uploadTask['file_type'] = 'mp3';
|
||||
$uploadTask['metadata'] = [
|
||||
'artist' => $artist,
|
||||
'title' => $title,
|
||||
'duration' => $duration, // in seconds
|
||||
];
|
||||
$uploadTask['mediaplayer'] = 1;
|
||||
} elseif ($ext === "zip") {
|
||||
// Process ZIP file using ZipArchive
|
||||
$zip = new ZipArchive;
|
||||
if ($zip->open($destination) === true) {
|
||||
$totalDuration = 0;
|
||||
$tracklist = [];
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$entryName = $zip->getNameIndex($i);
|
||||
$entryExt = strtolower(pathinfo($entryName, PATHINFO_EXTENSION));
|
||||
if ($entryExt === "mp3") {
|
||||
// Extract the MP3 temporarily to process metadata
|
||||
$tempDir = $tmpPath . uniqid('zip_');
|
||||
mkdir($tempDir);
|
||||
$tempFilePath = $tempDir . '/' . basename($entryName);
|
||||
$zip->extractTo($tempDir, $entryName);
|
||||
$escapedFile = escapeshellarg($tempFilePath);
|
||||
$title = trim(shell_exec("eyed3 --query='%t%' $escapedFile"));
|
||||
$duration = trim(shell_exec("mp3info -p \"%S\" $escapedFile"));
|
||||
$tracklist[] = $title ?: basename($entryName);
|
||||
$totalDuration += (int)$duration;
|
||||
unlink($tempFilePath);
|
||||
rmdir($tempDir);
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
$uploadTask['file_type'] = 'zip';
|
||||
$uploadTask['metadata'] = [
|
||||
'tracklist' => $tracklist,
|
||||
'total_duration' => $totalDuration,
|
||||
];
|
||||
// Mark ZIPs as download only (no mediaplayer)
|
||||
$uploadTask['mediaplayer'] = 0;
|
||||
} else {
|
||||
$_SESSION['error'] = "Failed to open ZIP file.";
|
||||
header("Location: upload.php");
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error'] = "Unsupported file type.";
|
||||
header("Location: upload.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Save the upload task details in session so step 2 can use them.
|
||||
// Process the file (extract metadata for MP3/ZIP)
|
||||
$uploadTask = $upload->process();
|
||||
$_SESSION['upload_task'] = $uploadTask;
|
||||
header("Location: upload_details.php");
|
||||
// Reload the page so the details form can be shown
|
||||
header("Location: /upload");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step 2: Handle mix details submission
|
||||
elseif (isset($_POST['action']) && $_POST['action'] === 'submit_details') {
|
||||
if (!isset($_SESSION['upload_task'])) {
|
||||
$_SESSION['error'] = $locale['noUploadedFileFound'];
|
||||
header("Location: /upload");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'includes/header.php';
|
||||
$uploadTask = $_SESSION['upload_task'];
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$recorded = trim($_POST['recorded'] ?? '');
|
||||
$selectedGenres = $_POST['genres'] ?? []; // array of genre IDs
|
||||
$dj1 = $_POST['dj1'] ?? 0;
|
||||
$dj2 = $_POST['dj2'] ?? 0;
|
||||
$dj3 = $_POST['dj3'] ?? 0;
|
||||
|
||||
// Basic validation: title is required
|
||||
if (empty($title)) {
|
||||
$_SESSION['error'] = $locale['mixTitleRequired'];
|
||||
header("Location: /upload");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Simple slugify function
|
||||
function slugify($text) {
|
||||
$text = preg_replace('~[^\pL\d]+~u', '-', $text);
|
||||
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
|
||||
$text = preg_replace('~[^-\w]+~', '', $text);
|
||||
$text = trim($text, '-');
|
||||
$text = preg_replace('~-+~', '-', $text);
|
||||
$text = strtolower($text);
|
||||
return empty($text) ? 'n-a' : $text;
|
||||
}
|
||||
$slug = slugify($title);
|
||||
|
||||
// Upload the file to the CDN
|
||||
$cdn = new CDN($config);
|
||||
$remotePath = "temp/mixes/" . uniqid() . "_" . basename($uploadTask['local_path']);
|
||||
$mimeType = ($uploadTask['ext'] === 'mp3') ? 'audio/mpeg' : 'application/zip';
|
||||
|
||||
try {
|
||||
$cdn->uploadFile($uploadTask['local_path'], $remotePath, $mimeType, 'private');
|
||||
} catch (Exception $e) {
|
||||
$_SESSION['error'] = $locale['errorUploadCDN'] . $e->getMessage();
|
||||
header("Location: /upload");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Insert the mix record into the database
|
||||
$db = new Database($config);
|
||||
$stmt = $db->prepare("INSERT INTO mix (title, slug, description, cover, url, seconds, mediaplayer, dj1, dj2, dj3, pending, recorded) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)");
|
||||
|
||||
if ($uploadTask['file_type'] === 'mp3') {
|
||||
$seconds = (int)$uploadTask['metadata']['duration'];
|
||||
$mediaplayer = 0;
|
||||
} elseif ($uploadTask['file_type'] === 'zip') {
|
||||
$seconds = (int)$uploadTask['metadata']['total_duration'];
|
||||
$mediaplayer = 1;
|
||||
} else {
|
||||
$seconds = 0;
|
||||
$mediaplayer = 1;
|
||||
}
|
||||
$url = $remotePath;
|
||||
$dj1 = (int)$dj1;
|
||||
$dj2 = (int)$dj2;
|
||||
$dj3 = (int)$dj3;
|
||||
$cover = "";
|
||||
$stmt->bind_param("sssssiiiiss", $title, $slug, $description, $cover, $url, $seconds, $mediaplayer, $dj1, $dj2, $dj3, $recorded);
|
||||
if (!$stmt->execute()) {
|
||||
$_SESSION['error'] = $locale['errorSavingMixDB'];
|
||||
header("Location: /upload");
|
||||
exit;
|
||||
}
|
||||
$mixId = $stmt->insert_id;
|
||||
$stmt->close();
|
||||
|
||||
// Insert mix_meta entries for genres and tracklist
|
||||
foreach ($selectedGenres as $genreId) {
|
||||
$stmt = $db->prepare("INSERT INTO mix_meta (mix_id, attribute, value) VALUES (?, 'genre', ?)");
|
||||
$stmt->bind_param("is", $mixId, $genreId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
if ($uploadTask['file_type'] === 'mp3' && !empty($uploadTask['metadata']['title'])) {
|
||||
$tracklist = $uploadTask['metadata']['title'];
|
||||
$stmt = $db->prepare("INSERT INTO mix_meta (mix_id, attribute, value) VALUES (?, 'tracklist', ?)");
|
||||
$stmt->bind_param("is", $mixId, $tracklist);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
} elseif ($uploadTask['file_type'] === 'zip' && !empty($uploadTask['metadata']['tracklist'])) {
|
||||
$tracklist = implode("\n", $uploadTask['metadata']['tracklist']);
|
||||
$stmt = $db->prepare("INSERT INTO mix_meta (mix_id, attribute, value) VALUES (?, 'tracklist', ?)");
|
||||
$stmt->bind_param("is", $mixId, $tracklist);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
// Cleanup: delete the local temporary file and clear session upload task
|
||||
unlink($uploadTask['local_path']);
|
||||
unset($_SESSION['upload_task']);
|
||||
|
||||
$_SESSION['success'] = $locale['uploadedPendingApproval'];
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php require_once 'includes/header.php'; ?>
|
||||
|
||||
<section class="upload-section py-5">
|
||||
<div class="container">
|
||||
<h2 class="mb-4">Upload a New Mix</h2>
|
||||
<?php
|
||||
// Display any error messages
|
||||
if (isset($_SESSION['error'])) {
|
||||
echo '<div class="alert alert-danger">' . htmlspecialchars($_SESSION['error']) . '</div>';
|
||||
unset($_SESSION['error']);
|
||||
}
|
||||
// If no file has been uploaded, show the file upload form.
|
||||
if (!isset($_SESSION['upload_task'])):
|
||||
?>
|
||||
<form action="upload.php" method="post" enctype="multipart/form-data">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title mb-4"><?php echo $locale['uploadHeader1'];?></h3>
|
||||
<!-- Info Alert -->
|
||||
<div class="alert alert-info">
|
||||
<h4 class="alert-heading">Important Upload Information</h4>
|
||||
<p>Utah's DJs is primarily an archival project dedicated to preserving the history and culture of EDM DJs in Utah. Your uploads contribute to this historical record.</p>
|
||||
</div>
|
||||
<!-- Requirements -->
|
||||
<h4 class="mb-3">Before You Upload</h4>
|
||||
<ul class="list-group list-group-flush mb-4">
|
||||
<li class="list-group-item">
|
||||
<i class="fas fa-check-circle text-success me-2"></i>
|
||||
Verify that all DJs involved are listed in our database. If a DJ is not listed, they must be added and approved before uploading.
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<i class="fas fa-check-circle text-success me-2"></i>
|
||||
Check that appropriate genres are available for your mix. New genres require approval before they can be used.
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<i class="fas fa-info-circle text-primary me-2"></i>
|
||||
You can submit new DJ or genre requests through your profile settings.
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Process Steps -->
|
||||
<h4 class="mb-3">Upload Process</h4>
|
||||
<ol class="list-group list-group-numbered mb-4">
|
||||
<li class="list-group-item">Upload your mix file (MP3 or ZIP format)</li>
|
||||
<li class="list-group-item">Enter mix details, including title, description, and recording date</li>
|
||||
<li class="list-group-item">Select relevant genres and DJs</li>
|
||||
<li class="list-group-item">Submit for review</li>
|
||||
</ol>
|
||||
<!-- Upload Form -->
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title mb-4">Upload Your Mix</h4>
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action" value="upload_file">
|
||||
<div class="mb-3">
|
||||
<label for="mix_file" class="form-label">Select Mix File (MP3 or ZIP)</label>
|
||||
<input type="file" class="form-control" id="mix_file" name="mix_file" accept=".mp3,.zip" required>
|
||||
<div class="form-text">Maximum file size: 500MB</div>
|
||||
</div>
|
||||
<!-- Optionally, add album art upload here later -->
|
||||
<button type="submit" class="btn btn-primary">Upload File</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-upload me-2"></i>Upload File
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// If an upload task exists, show the mix details form.
|
||||
else:
|
||||
$uploadTask = $_SESSION['upload_task'];
|
||||
// Load available genres and DJs for the form
|
||||
$db = new Database($config);
|
||||
$genresObj = new Genres($db);
|
||||
$allGenres = $genresObj->get_all_genres();
|
||||
$djsObj = new DJs($db);
|
||||
$allDJs = $djsObj->get_all_djs();
|
||||
?>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title mb-4">Enter Mix Details</h3>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5>File Summary</h5>
|
||||
<p><strong>Original Name:</strong> <?php echo htmlspecialchars($uploadTask['original_name']); ?></p>
|
||||
<p><strong>File Type:</strong> <?php echo htmlspecialchars(strtoupper($uploadTask['file_type'])); ?></p>
|
||||
<p><strong>Size:</strong> <?php echo round($uploadTask['size'] / 1024, 2); ?> KB</p>
|
||||
<?php if ($uploadTask['file_type'] === 'mp3'): ?>
|
||||
<p><strong>Duration:</strong> <?php echo htmlspecialchars($uploadTask['metadata']['duration']); ?> seconds</p>
|
||||
<?php elseif ($uploadTask['file_type'] === 'zip'): ?>
|
||||
<p><strong>Total Duration:</strong> <?php echo htmlspecialchars($uploadTask['metadata']['total_duration']); ?> seconds</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<form action="/upload" method="post" class="needs-validation" novalidate>
|
||||
<input type="hidden" name="action" value="submit_details">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Mix Title</label>
|
||||
<input type="text" class="form-control" id="title" name="title" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Mix Description</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="recorded" class="form-label">Recorded Date</label>
|
||||
<input type="date" class="form-control" id="recorded" name="recorded" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="genres" class="form-label">Select Genres (type to search)</label>
|
||||
<select class="form-select" id="genres" name="genres[]" multiple required>
|
||||
<?php foreach ($allGenres as $genre): ?>
|
||||
<option value="<?php echo htmlspecialchars($genre['id']); ?>"><?php echo htmlspecialchars($genre['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Select DJs (Maximum 3)</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="form-select" name="dj1" required>
|
||||
<option value="">Select DJ 1</option>
|
||||
<?php foreach ($allDJs as $dj): ?>
|
||||
<option value="<?php echo htmlspecialchars($dj['id']); ?>"><?php echo htmlspecialchars($dj['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="form-select" name="dj2">
|
||||
<option value="">Select DJ 2 (Optional)</option>
|
||||
<?php foreach ($allDJs as $dj): ?>
|
||||
<option value="<?php echo htmlspecialchars($dj['id']); ?>"><?php echo htmlspecialchars($dj['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="form-select" name="dj3">
|
||||
<option value="">Select DJ 3 (Optional)</option>
|
||||
<?php foreach ($allDJs as $dj): ?>
|
||||
<option value="<?php echo htmlspecialchars($dj['id']); ?>"><?php echo htmlspecialchars($dj['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit Mix</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
<?php
|
||||
// upload_details.php - Step 2: Enter mix details and finalize upload
|
||||
|
||||
require_once 'includes/globals.php';
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
use DJMixHosting\Genres;
|
||||
use DJMixHosting\DJs;
|
||||
use Aws\Credentials\Credentials;
|
||||
|
||||
// Ensure user is authenticated and an upload task exists
|
||||
if (!isset($_SESSION['user']) || !isset($_SESSION['upload_task'])) {
|
||||
header("Location: upload.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$uploadTask = $_SESSION['upload_task'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Get form fields
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$recorded = trim($_POST['recorded'] ?? '');
|
||||
$selectedGenres = $_POST['genres'] ?? []; // an array of genre IDs or names
|
||||
$dj1 = $_POST['dj1'] ?? 0;
|
||||
$dj2 = $_POST['dj2'] ?? 0;
|
||||
$dj3 = $_POST['dj3'] ?? 0;
|
||||
|
||||
// Basic validation
|
||||
if (empty($title)) {
|
||||
$_SESSION['error'] = "Mix title is required.";
|
||||
header("Location: upload_details.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Generate a slug from the title
|
||||
function slugify($text) {
|
||||
// Replace non-letter or digits by -
|
||||
$text = preg_replace('~[^\pL\d]+~u', '-', $text);
|
||||
// Transliterate
|
||||
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
|
||||
// Remove unwanted characters
|
||||
$text = preg_replace('~[^-\w]+~', '', $text);
|
||||
// Trim
|
||||
$text = trim($text, '-');
|
||||
// Remove duplicate -
|
||||
$text = preg_replace('~-+~', '-', $text);
|
||||
// Lowercase
|
||||
$text = strtolower($text);
|
||||
return empty($text) ? 'n-a' : $text;
|
||||
}
|
||||
$slug = slugify($title);
|
||||
|
||||
|
||||
|
||||
$credentials = new Credentials(
|
||||
$config['aws']['s3']['access_key'],
|
||||
$config['aws']['s3']['secret_key']
|
||||
);
|
||||
|
||||
$s3Client = new Aws\S3\S3Client([
|
||||
'version' => 'latest',
|
||||
'region' => $config['aws']['s3']['region'],
|
||||
'endpoint' => $config['aws']['s3']['host'],
|
||||
'credentials' => $credentials,
|
||||
'use_path_style_endpoint' => true,
|
||||
'profile' => null, // disable reading ~/.aws/config
|
||||
'use_aws_shared_config_files' => false,
|
||||
]);
|
||||
|
||||
// Determine remote file path – for now, store in a temporary folder on Spaces
|
||||
$remotePath = "temp/mixes/" . uniqid() . "_" . basename($uploadTask['local_path']);
|
||||
|
||||
// Determine MIME type from file extension
|
||||
$mimeType = ($uploadTask['ext'] === 'mp3') ? 'audio/mpeg' : 'application/zip';
|
||||
|
||||
try {
|
||||
$s3Client->putObject([
|
||||
'Bucket' => $config['aws']['s3']['bucket'],
|
||||
'Key' => $remotePath,
|
||||
'SourceFile' => $uploadTask['local_path'],
|
||||
'ACL' => 'private', // file remains hidden until approved
|
||||
'ContentType' => $mimeType,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$_SESSION['error'] = "Error uploading file to CDN: " . $e->getMessage();
|
||||
header("Location: upload_details.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Now insert a new mix record into the database with pending status.
|
||||
$db = new Database($config);
|
||||
$stmt = $db->prepare("INSERT INTO mix (title, slug, description, cover, url, seconds, mediaplayer, dj1, dj2, dj3, pending, recorded) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)");
|
||||
|
||||
// Determine the duration and mediaplayer flag from the upload task:
|
||||
// For an MP3 file, set mediaplayer to 0; for a ZIP file, set it to 1.
|
||||
$seconds = 0;
|
||||
if ($uploadTask['file_type'] === 'mp3') {
|
||||
$seconds = (int)$uploadTask['metadata']['duration'];
|
||||
$mediaplayer = 0; // MP3 gets a player
|
||||
} elseif ($uploadTask['file_type'] === 'zip') {
|
||||
$seconds = (int)$uploadTask['metadata']['total_duration'];
|
||||
$mediaplayer = 1; // ZIPs are download only, so flag them as 1
|
||||
}
|
||||
// The URL can be constructed as per your CDN structure; for now, we store the remote path.
|
||||
$url = $remotePath;
|
||||
|
||||
// For DJs, cast to integer (0 if none selected)
|
||||
$dj1 = (int)$dj1;
|
||||
$dj2 = (int)$dj2;
|
||||
$dj3 = (int)$dj3;
|
||||
$cover = "";
|
||||
$stmt->bind_param("sssssiiiiss", $title, $slug, $description, $cover, $url, $seconds, $mediaplayer, $dj1, $dj2, $dj3, $recorded);
|
||||
if (!$stmt->execute()) {
|
||||
$_SESSION['error'] = "Error saving mix to database.";
|
||||
header("Location: upload_details.php");
|
||||
exit;
|
||||
}
|
||||
$mixId = $stmt->insert_id;
|
||||
$stmt->close();
|
||||
|
||||
// Insert mix_meta entries for genres and tracklist
|
||||
// For genres, assume $selectedGenres is an array of genre IDs (or names, if new genres need to be added)
|
||||
foreach ($selectedGenres as $genreId) {
|
||||
$stmt = $db->prepare("INSERT INTO mix_meta (mix_id, attribute, value) VALUES (?, 'genre', ?)");
|
||||
$stmt->bind_param("is", $mixId, $genreId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
// If the file was mp3 and metadata includes a track title or tracklist, insert that as well:
|
||||
if ($uploadTask['file_type'] === 'mp3' && !empty($uploadTask['metadata']['title'])) {
|
||||
$tracklist = $uploadTask['metadata']['title'];
|
||||
$stmt = $db->prepare("INSERT INTO mix_meta (mix_id, attribute, value) VALUES (?, 'tracklist', ?)");
|
||||
$stmt->bind_param("is", $mixId, $tracklist);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
} elseif ($uploadTask['file_type'] === 'zip' && !empty($uploadTask['metadata']['tracklist'])) {
|
||||
// If multiple tracks, you might store them as a newline-separated string
|
||||
$tracklist = implode("\n", $uploadTask['metadata']['tracklist']);
|
||||
$stmt = $db->prepare("INSERT INTO mix_meta (mix_id, attribute, value) VALUES (?, 'tracklist', ?)");
|
||||
$stmt->bind_param("is", $mixId, $tracklist);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
// Cleanup: delete the local temporary file and clear session task
|
||||
unlink($uploadTask['local_path']);
|
||||
unset($_SESSION['upload_task']);
|
||||
|
||||
$_SESSION['success'] = "Mix uploaded successfully and is pending approval.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'includes/header.php';
|
||||
|
||||
// Load available genres and DJs for the form
|
||||
$db = new Database($config);
|
||||
$genresObj = new DJMixHosting\Genres($db);
|
||||
$allGenres = $genresObj->get_all_genres();
|
||||
$djsObj = new DJMixHosting\DJs($db);
|
||||
$allDJs = $djsObj->get_all_djs();
|
||||
|
||||
?>
|
||||
|
||||
<section class="upload-details-section py-5">
|
||||
<div class="container">
|
||||
<h2 class="mb-4">Enter Mix Details</h2>
|
||||
<?php
|
||||
if(isset($_SESSION['error'])) {
|
||||
echo '<div class="alert alert-danger">' . htmlspecialchars($_SESSION['error']) . '</div>';
|
||||
unset($_SESSION['error']);
|
||||
}
|
||||
?>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5>File Summary</h5>
|
||||
<p><strong>Original Name:</strong> <?php echo htmlspecialchars($uploadTask['original_name']); ?></p>
|
||||
<p><strong>File Type:</strong> <?php echo htmlspecialchars(strtoupper($uploadTask['file_type'])); ?></p>
|
||||
<p><strong>Size:</strong> <?php echo round($uploadTask['size'] / 1024, 2); ?> KB</p>
|
||||
<?php if ($uploadTask['file_type'] === 'mp3'): ?>
|
||||
<p><strong>Duration:</strong> <?php echo htmlspecialchars($uploadTask['metadata']['duration']); ?> seconds</p>
|
||||
<?php elseif ($uploadTask['file_type'] === 'zip'): ?>
|
||||
<p><strong>Total Duration:</strong> <?php echo htmlspecialchars($uploadTask['metadata']['total_duration']); ?> seconds</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="upload_details.php" method="post" class="needs-validation" novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Mix Title</label>
|
||||
<input type="text" class="form-control" id="title" name="title" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Mix Description</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="recorded" class="form-label">Recorded Date</label>
|
||||
<input type="date" class="form-control" id="recorded" name="recorded" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="genres" class="form-label">Select Genres (type to search)</label>
|
||||
<select class="form-select" id="genres" name="genres[]" multiple required>
|
||||
<?php foreach ($allGenres as $genre): ?>
|
||||
<option value="<?php echo htmlspecialchars($genre['id']); ?>"><?php echo htmlspecialchars($genre['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Select DJs (Maximum 3)</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="form-select" name="dj1" required>
|
||||
<option value="">Select DJ 1</option>
|
||||
<?php foreach ($allDJs as $dj): ?>
|
||||
<option value="<?php echo htmlspecialchars($dj['id']); ?>"><?php echo htmlspecialchars($dj['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="form-select" name="dj2">
|
||||
<option value="">Select DJ 2 (Optional)</option>
|
||||
<?php foreach ($allDJs as $dj): ?>
|
||||
<option value="<?php echo htmlspecialchars($dj['id']); ?>"><?php echo htmlspecialchars($dj['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="form-select" name="dj3">
|
||||
<option value="">Select DJ 3 (Optional)</option>
|
||||
<?php foreach ($allDJs as $dj): ?>
|
||||
<option value="<?php echo htmlspecialchars($dj['id']); ?>"><?php echo htmlspecialchars($dj['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit Mix</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
|
@ -4,15 +4,20 @@ require_once 'includes/globals.php';
|
|||
require_once 'vendor/autoload.php';
|
||||
|
||||
use DJMixHosting\Database;
|
||||
use DJMixHosting\User;
|
||||
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header("Location: login.php");
|
||||
$_SESSION['error'] = $locale['loginToVerifyEmail'];
|
||||
header("Location: /login");
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = new Database($config);
|
||||
$userId = $_SESSION['user']['id'];
|
||||
|
||||
// Create a User instance
|
||||
$user = new User($db, $userId);
|
||||
|
||||
// Retrieve the verification code from GET or POST
|
||||
$verification_code = "";
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['verification_code'])) {
|
||||
|
@ -20,46 +25,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['verification_code']))
|
|||
} elseif (isset($_GET['code'])) {
|
||||
$verification_code = trim($_GET['code']);
|
||||
} else {
|
||||
$_SESSION['error'] = "Verification code is required.";
|
||||
header("Location: profile.php");
|
||||
$_SESSION['error'] = $locale['verificationCodeRequired'];
|
||||
header("Location: /profile");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Look up the email verification record for this user and code
|
||||
$stmt = $db->prepare("SELECT * FROM email_verifications WHERE user_id = ? AND verification_code = ?");
|
||||
$stmt->bind_param("is", $userId, $verification_code);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$record = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
if (!$record) {
|
||||
$_SESSION['error'] = "Invalid verification code.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
// Attempt to verify the email
|
||||
try {
|
||||
$message = $user->verifyEmail($verification_code);
|
||||
$_SESSION['success'] = $message;
|
||||
} catch (\Exception $e) {
|
||||
$_SESSION['error'] = $e->getMessage();
|
||||
}
|
||||
|
||||
// Check if the verification code has expired
|
||||
$current_time = new DateTime();
|
||||
$expires_at = new DateTime($record['expires_at']);
|
||||
if ($current_time > $expires_at) {
|
||||
$_SESSION['error'] = "Verification code has expired. Please request a new one.";
|
||||
header("Location: profile.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verification successful: update the user's record
|
||||
$stmt = $db->prepare("UPDATE users SET emailVerified = 1 WHERE id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Remove the verification record for cleanup
|
||||
$stmt = $db->prepare("DELETE FROM email_verifications WHERE user_id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
$_SESSION['success'] = "Email verified successfully.";
|
||||
header("Location: profile.php");
|
||||
header("Location: /profile");
|
||||
exit;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue