add chat components

This commit is contained in:
flash 2016-08-10 18:10:57 +02:00
parent 0493e3dcbb
commit 65740718fb
12 changed files with 620 additions and 9 deletions

View file

@ -0,0 +1,17 @@
<?php
/**
* Holds the authentication interface.
* @package Sakura
*/
namespace Sakura\Chat;
/**
* Interface for authentication methods.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
interface AuthInterface
{
public function attempt();
}

81
app/Chat/LinkInfo.php Normal file
View file

@ -0,0 +1,81 @@
<?php
/**
* Holds the link info object.
* @package Sakura
*/
namespace Sakura\Chat;
/**
* Object to serve back to the chat.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class LinkInfo
{
/**
* Types for $Type.
*/
const TYPES = [
'PLAIN' => 0,
'META' => 1,
'VIDEO' => 2,
'AUDIO' => 3,
'IMAGE' => 4,
'EMBED' => 5,
];
/**
* Modifiable url.
* @var string
*/
public $URL;
/**
* Original url.
* @var string
*/
public $OriginalURL;
/**
* Type (from const TYPES).
* @var int
*/
public $Type;
/**
* Full image or thumbnail, depends on Type.
* @var string
*/
public $Image;
/**
* Title/header text.
* @var string
*/
public $Title;
/**
* Description text.
* @var string
*/
public $Description;
/**
* The content type to assign if applicable.
* @var string
*/
public $ContentType;
/**
* The width of an image if applicable.
* @var int
*/
public $Width;
/**
* The height of an image if applicable.
* @var int
*/
public $Height;
}

289
app/Chat/Settings.php Normal file
View file

@ -0,0 +1,289 @@
<?php
/**
* Hold the Sakurako settings object.
* @package Sakura
*/
namespace Sakura\Chat;
/**
* Chat settings. Keep this up-to-date with settings.json for Sakurako.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Settings
{
/**
* Protocol the chat will use.
* @var string
*/
public $protocol = 'TestRepeater';
/**
* Server address the chat will connect to.
* @var string
*/
public $server = null;
/**
* Title to display on the window/tab.
* @var string
*/
public $title = 'Sakurako';
/**
* Location to redirect to when the authentication failed.
* @var string
*/
public $authRedir = null;
/**
* Cookies to send to the server for authentication (in proper order).
* @var array
*/
public $authCookies = [];
/**
* URL format for avatars, {0} gets replaced with the user's id and set to null to disable.
* @var string
*/
public $avatarUrl = null;
/**
* URL format for profile links, works the same as avatars.
* @var string
*/
public $profileUrl = null;
/**
* Enabling compact (classic) by default.
* @var bool
*/
public $compactView = false;
/**
* Strobe the tab title on new message.
* @var bool
*/
public $flashTitle = true;
/**
* Enabling browser notifications.
* @var bool
*/
public $enableNotifications = true;
/**
* Words that trigger a notification separated with spaces.
* @var string
*/
public $notificationTriggers = '';
/**
* Show the contents of the message in the notification.
* @var bool
*/
public $notificationShowMessage = false;
/**
* Enabling development mode (e.g. loading eruda).
* @var bool
*/
public $development = false;
/**
* Default style.
* @var string
*/
public $style = 'dark';
/**
* Path to language files relative to the chat client's index.
* @var string
*/
public $languagePath = './languages/';
/**
* Default language file to use.
* @var string
*/
public $language = 'en-gb';
/**
* Available languages.
* @var array
*/
public $languages = [
'en-gb' => 'English',
];
/**
* Formatting string to the timestamp, uses the PHP syntax.
* @var string
*/
public $dateTimeFormat = 'H:i:s';
/**
* Markup parser to use.
* @var string
*/
public $parser = 'WaterDown';
/**
* Enabling the markup parser.
* @var bool
*/
public $enableParser = true;
/**
* Enabling emoticon parsing.
* @var bool
*/
public $enableEmoticons = true;
/**
* Whether urls should be automatically detected in message.
* @var bool
*/
public $autoParseUrls = true;
/**
* Whether the chat should embed url macros like image embedding.
* @var bool
*/
public $autoEmbed = true;
/**
* Enabling automatically scrolling down when a new message is received.
* @var bool
*/
public $autoScroll = true;
/**
* Enabling notification sounds.
* @var bool
*/
public $soundEnable = true;
/**
* The volume percentage for sounds.
* @var int
*/
public $soundVolume = 80;
/**
* The default sound pack.
* @var string
*/
public $soundPack = 'default';
/**
* Available sound packs.
* @var array
*/
public $soundPacks = [
'default' => 'Default',
];
/**
* Enabling the user join sound.
* @var bool
*/
public $soundEnableJoin = true;
/**
* Enabling the user leave sound.
* @var bool
*/
public $soundEnableLeave = true;
/**
* Enabling the error sound.
* @var bool
*/
public $soundEnableError = true;
/**
* Enabling the server broadcast sound.
* @var bool
*/
public $soundEnableServer = true;
/**
* Enabling the incoming message sound.
* @var bool
*/
public $soundEnableIncoming = true;
/**
* Enabling the outgoing message sound.
* @var bool
*/
public $soundEnableOutgoing = true;
/**
* Enabling the private message sound.
* @var bool
*/
public $soundEnablePrivate = true;
/**
* Enabling the forceful leave (kick/ban/etc) sound.
* @var bool
*/
public $soundEnableForceLeave = true;
/**
* Whether to let the user confirm before closing the tab.
* @var bool
*/
public $closeTabConfirm = false;
/**
* Emoticons to be loaded.
* @var array
*/
public $emoticons = [];
/**
* Applies settings based on Sakura's configuration.
*/
public function loadStandard()
{
$this->protocol = config('chat.protocol');
$this->server = config('chat.server');
$this->title = config('chat.title');
$this->authRedir = route('auth.login', null, true);
$cpfx = config('cookie.prefix');
$this->authCookies = [
"{$cpfx}id",
"{$cpfx}session",
];
$this->avatarUrl = route('file.avatar', '{0}', true);
$this->profileUrl = route('user.profile', '{0}', true);
$this->development = config('dev.show_errors');
$this->languagePath = config('chat.language_path');
$this->language = config('chat.language');
$this->languages = config('chat.languages');
$this->dateTimeFormat = config('chat.date_format');
$this->parser = config('chat.parser');
$this->soundPack = config('chat.sound_pack');
$this->soundPacks = config('chat.sound_packs');
}
/**
* Adding an emoticon to the list.
* @param array $triggers
* @param string $image
* @param int $hierarchy
* @param bool $relativePath
*/
public function addEmoticon($triggers, $image, $hierarchy = 0, $relativePath = false)
{
$this->emoticons[] = [
'Text' => $triggers,
'Image' => ($relativePath ? full_domain() : '') . $image,
'Hierarchy' => $hierarchy,
];
}
}

36
app/Chat/URLResolver.php Normal file
View file

@ -0,0 +1,36 @@
<?php
/**
* Holds the url resolver.
* @package Sakura
*/
namespace Sakura\Chat;
/**
* Resolves URL data.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class URLResolver
{
/**
* Resolves a url.
* @param string $protocol
* @param string $slashes
* @param string $authority
* @param string $host
* @param string $port
* @param string $path
* @param string $query
* @param string $hash
* @return LinkInfo
*/
public static function resolve($protocol, $slashes, $authority, $host, $port, $path, $query, $hash)
{
$url = "{$protocol}:{$slashes}{$authority}{$host}{$port}{$path}{$query}{$hash}";
$info = new LinkInfo;
$info->URL = $info->OriginalURL = $url;
$info->Type = LinkInfo::TYPES['PLAIN'];
return $info;
}
}

View file

@ -6,6 +6,16 @@
namespace Sakura\Controllers; namespace Sakura\Controllers;
use Sakura\Chat\LinkInfo;
use Sakura\Chat\Settings;
use Sakura\Chat\URLResolver;
use Sakura\DB;
use Sakura\Perms;
use Sakura\Perms\Manage;
use Sakura\Perms\Site;
use Sakura\Session;
use Sakura\User;
/** /**
* Chat related controller. * Chat related controller.
* @package Sakura * @package Sakura
@ -13,12 +23,20 @@ namespace Sakura\Controllers;
*/ */
class ChatController extends Controller class ChatController extends Controller
{ {
/**
* Middlewares!
* @var array
*/
protected $middleware = [
'EnableCORS',
];
/** /**
* Redirects the user to the chat client. * Redirects the user to the chat client.
*/ */
public function redirect() public function redirect()
{ {
return; header('Location: ' . config('chat.webclient'));
} }
/** /**
@ -26,7 +44,78 @@ class ChatController extends Controller
* @return string * @return string
*/ */
public function settings() public function settings()
{
$settings = new Settings;
$settings->loadStandard();
$emotes = DB::table('emoticons')
->get();
foreach ($emotes as $emote) {
$settings->addEmoticon([$emote->emote_string], $emote->emote_path, 1, true);
}
return $this->json($settings);
}
/**
* Resolves urls.
* @return string
*/
public function resolve()
{
$data = json_decode(file_get_contents('php://input'));
$info = new LinkInfo;
if (json_last_error() === JSON_ERROR_NONE) {
$info = URLResolver::resolve(
$data->Protocol ?? null,
$data->Slashes ?? null,
$data->Authority ?? null,
$data->Host ?? null,
$data->Port ?? null,
$data->Path ?? null,
$data->Query ?? null,
$data->Hash ?? null
);
}
return $info;
}
/**
* Handles the authentication for a chat server.
* @return string
*/
public function auth()
{ {
return; return;
} }
/**
* Legacy auth, for SockLegacy. Remove when the old chat server finally dies.
* @return string
*/
public function authLegacy()
{
$user = User::construct($_GET['arg1'] ?? null);
$session = new Session($_GET['arg2'] ?? null);
if ($session->validate($user->id)
&& !$user->permission(Site::DEACTIVATED)
&& !$user->permission(Site::RESTRICTED)) {
$hierarchy = $user->hierarchy();
$moderator = $user->permission(Manage::USE_MANAGE, Perms::MANAGE) ? 1 : 0;
$changeName = $user->permission(Site::CHANGE_USERNAME) ? 1 : 0;
$createChans = $user->permission(Site::MULTIPLE_GROUPS) ? 2 : (
$user->permission(Site::CREATE_GROUP) ? 1 : 0
);
// The single 0 in here is used to determine log access, which isn't supported by sakurako anymore since it
// required direct database access and the chat is databaseless now.
return "yes{$user->id}\n{$user->username}\n{$user->colour}\n{$hierarchy}\f{$moderator}\f0\f{$changeName}\f{$createChans}\f";
}
return "no";
}
} }

View file

@ -0,0 +1,31 @@
<?php
/**
* Enables CORS globally.
* @package Sakura
*/
namespace Sakura\Middleware;
/**
* Enables CORS.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class EnableCORS implements MiddlewareInterface
{
/**
* Enables CORS.
*/
public function run()
{
if (isset($_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
header("Access-Control-Allow-Methods: GET, POST");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
}
}
}
}

View file

@ -1079,4 +1079,16 @@ class User
{ {
return Template::exists($this->design) ? $this->design : config('general.design'); return Template::exists($this->design) ? $this->design : config('general.design');
} }
/**
* Gets the user's proper (highest) hierarchy.
* @return int
*/
public function hierarchy()
{
return DB::table('ranks')
->join('user_ranks', 'ranks.rank_id', '=', 'user_ranks.rank_id')
->where('user_id', $this->id)
->max('ranks.rank_hierarchy');
}
} }

View file

@ -93,7 +93,7 @@ report_host =
; Mailing settings ; Mailing settings
[mail] [mail]
contact_address = sakura@localhost contact_address = sakura@localhost
signature = Sakura | http://localhost/ signature = "Sakura | http://localhost/"
; SMTP settings ; SMTP settings
[mail.smtp] [mail.smtp]
@ -208,3 +208,39 @@ mail['Administrator'] = sakura@localghost
twit['smugwave'] = "Sakura's main developer" twit['smugwave'] = "Sakura's main developer"
repo['Sakura'] = https://github.com/flashwave/sakura repo['Sakura'] = https://github.com/flashwave/sakura
; Chat specific settings
[chat]
; Path to the webclient
webclient = http://localhost/chat/
; Protocol to use
protocol = Sock
; Server address
server = ws://localhost
; Window/tab title
title = Sakurako
; Path to language files directory for the chat (relative to the chat's client)
language_path = ./languages/
; Available languages
languages[en-gb] = English
languages[nl-nl] = Nederlands
; Default language
language = en-gb
; Date formatting used for chat message (standard PHP format)
date_format = H:i:s
; Markup parser to use
parser = WaterDown
; Soundpacks
sound_packs[default] = Default
; Default soundpack to use
sound_pack = default

View file

@ -17,6 +17,13 @@ namespace Sakura
Notifications.Start(); Notifications.Start();
} }
public static Delete(id: number): void
{
var deleter: AJAX = new AJAX;
deleter.SetUrl("/notifications/" + id + "/mark");
deleter.Start(HTTPMethod.GET);
}
public static Poll(): void public static Poll(): void
{ {
this.Client.Start(HTTPMethod.GET); this.Client.Start(HTTPMethod.GET);

View file

@ -49,7 +49,7 @@ namespace Yuuno
Sakura.DOM.Append(inner, text); Sakura.DOM.Append(inner, text);
Sakura.DOM.Append(container, inner); Sakura.DOM.Append(container, inner);
close.setAttribute('onclick', 'Yuuno.Notifications.CloseAlert(this.parentNode.id);'); close.setAttribute('onclick', (alert.id ? 'Sakura.Notifications.Delete(' + alert.id + ');' : '') + 'Yuuno.Notifications.CloseAlert(this.parentNode.id)');
Sakura.DOM.Append(close, closeIcon); Sakura.DOM.Append(close, closeIcon);
Sakura.DOM.Append(container, close); Sakura.DOM.Append(container, close);
@ -58,7 +58,12 @@ namespace Yuuno
if (alert.timeout > 0) { if (alert.timeout > 0) {
setTimeout(() => { setTimeout(() => {
Notifications.CloseAlert(id); if (Sakura.DOM.ID(id)) {
if (alert.id) {
Sakura.Notifications.Delete(alert.id);
}
Notifications.CloseAlert(id);
}
}, alert.timeout); }, alert.timeout);
} }
} }

View file

@ -79,9 +79,6 @@ Router::group(['before' => 'maintenance'], function () {
'mcptest' => 'manage.index', 'mcptest' => 'manage.index',
//'report' => 'report.something', //'report' => 'report.something',
//'osu' => 'eventual link to flashii team', //'osu' => 'eventual link to flashii team',
//'filehost' => '???',
//'fhscript' => '???',
//'fhmanager' => '???',
'everlastingness' => 'https://i.flash.moe/18661469927746.txt', 'everlastingness' => 'https://i.flash.moe/18661469927746.txt',
'fuckingdone' => 'https://i.flash.moe/18671469927761.txt', 'fuckingdone' => 'https://i.flash.moe/18671469927761.txt',
]; ];
@ -119,8 +116,13 @@ Router::group(['before' => 'maintenance'], function () {
Router::group(['prefix' => 'chat'], function () { Router::group(['prefix' => 'chat'], function () {
Router::get('/redirect', 'ChatController@redirect', 'chat.redirect'); Router::get('/redirect', 'ChatController@redirect', 'chat.redirect');
Router::get('/settings', 'ChatController@settings', 'chat.settings'); Router::get('/settings', 'ChatController@settings', 'chat.settings');
Router::get('/auth', 'ChatController@auth', 'chat.auth');
Router::get('/resolve', 'Chatcontroller@resolve', 'chat.resolve');
}); });
// Authentication for the "old" chat
Router::get('/web/sock-auth.php', 'ChatController@authLegacy');
// Forum // Forum
Router::group(['prefix' => 'forum'], function () { Router::group(['prefix' => 'forum'], function () {
// Post // Post

View file

@ -23,9 +23,15 @@ function config($value)
} }
// Alias for Router::route // Alias for Router::route
function route($name, $args = null) function route($name, $args = null, $full = false)
{ {
return Router::route($name, $args); return ($full ? full_domain() : '') . Router::route($name, $args);
}
// Getting the full domain (+protocol) of the current host, only works for http
function full_domain()
{
return 'http' . ($_SERVER['HTTPS'] ?? false ? 's' : '') . '://' . $_SERVER['HTTP_HOST'];
} }
// Checking if a parameter is equal to session_id() // Checking if a parameter is equal to session_id()