mirror of
https://github.com/brunofontes/shareit.git
synced 2025-12-14 11:32:08 -03:00
Compare commits
23 Commits
v0.4
...
54abd983aa
| Author | SHA1 | Date | |
|---|---|---|---|
|
54abd983aa
|
|||
|
5d7aee7778
|
|||
|
b767265200
|
|||
|
2ed6065901
|
|||
|
0b3f6d4837
|
|||
|
6339a23b02
|
|||
|
dc0c4e679b
|
|||
|
bb849dba4b
|
|||
|
437e847cdd
|
|||
|
|
f31228843f | ||
|
28520edee9
|
|||
|
74eb254297
|
|||
|
2f16d4dc60
|
|||
| 130e47b198 | |||
|
6be295e25b
|
|||
|
00c382e1cc
|
|||
|
2bc2792a24
|
|||
|
3d6b0dbeca
|
|||
|
52223676dc
|
|||
|
7a8d5ccaff
|
|||
|
8c4fe0a489
|
|||
|
69f0722c79
|
|||
|
5667fd0a86
|
@@ -19,6 +19,8 @@ class ReturnItem
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Item $item The returned item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Item $item)
|
||||
|
||||
@@ -3,22 +3,43 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use Mail;
|
||||
use \App\User;
|
||||
use App\FlashMessage;
|
||||
use \App\Mail\UserWaiting;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AlertController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store the waiting_user_id on db
|
||||
* so the user can be alerted when
|
||||
* the item is free
|
||||
*
|
||||
* @param Request $request Form data
|
||||
*
|
||||
* @return redirect to home
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$item = User::loggedIn()->items()->find(request('item'));
|
||||
$item->waiting_user_id = Auth::id();
|
||||
$item->timestamps = false;
|
||||
$item->save();
|
||||
if (!$item->used_by) {
|
||||
session()->flash(
|
||||
FlashMessage::PRIMARY,
|
||||
__('Oh! This item has just being returned. Take it before anyone else!')
|
||||
);
|
||||
return redirect('home');
|
||||
}
|
||||
|
||||
if ($item->used_by == Auth::id()) {
|
||||
return redirect('home');
|
||||
}
|
||||
|
||||
$item->storeAlert();
|
||||
|
||||
$loggedUser = Auth::user()->name;
|
||||
$userWithItem = User::find($item->used_by);
|
||||
\Mail::to($userWithItem)
|
||||
Mail::to($userWithItem)
|
||||
->locale($userWithItem->language)
|
||||
->send(new UserWaiting($loggedUser, $userWithItem->name, $item));
|
||||
|
||||
@@ -28,10 +49,12 @@ class AlertController extends Controller
|
||||
public function delete(Request $request)
|
||||
{
|
||||
$item = User::loggedIn()->items()->find(request('item'));
|
||||
$item->waiting_user_id = null;
|
||||
$item->timestamps = false;
|
||||
$item->save();
|
||||
|
||||
if ($item->waiting_user_id != Auth::id()) {
|
||||
return redirect('home');
|
||||
}
|
||||
|
||||
$item->removeAlert();
|
||||
return redirect('home');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use App\FlashMessage;
|
||||
use Mail;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
@@ -71,7 +72,13 @@ class RegisterController extends Controller
|
||||
'password' => Hash::make($data['password']),
|
||||
]);
|
||||
|
||||
\Mail::to($user)->send(new Welcome($user));
|
||||
Mail::to($user)->send(new Welcome($user));
|
||||
|
||||
$text = "Share it! New user: {$user->name} ($user->email)";
|
||||
Mail::raw($text, function ($message) use ($text) {
|
||||
$message->to('brunofontes.shareit@mailnull.com');
|
||||
$message->subject($text);
|
||||
});
|
||||
|
||||
session()->flash(FlashMessage::PRIMARY, __('Thanks for registering. Please, do not forget to validate your e-mail address.'));
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ use \App\User;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
protected $activeUsers = [];
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
@@ -16,6 +18,50 @@ class HomeController extends Controller
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the application dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$items = User::loggedIn()->items()->with('users')->get();
|
||||
|
||||
$numberOfUsedItems = 0;
|
||||
foreach ($items as $item) {
|
||||
if (isset($item->used_by)) {
|
||||
$numberOfUsedItems++;
|
||||
}
|
||||
$this->getUsername($item->users, $item->used_by);
|
||||
$this->getUsername($item->users, $item->waiting_user_id);
|
||||
}
|
||||
|
||||
$products = $items
|
||||
->sortBy('product.name', SORT_NATURAL | SORT_FLAG_CASE)
|
||||
->groupBy('product.name');
|
||||
|
||||
return view(
|
||||
'home',
|
||||
['products' => $products, 'users' => $this->activeUsers, 'usedItems' => $numberOfUsedItems]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username from an specified user id.
|
||||
*
|
||||
* @param object $itemUsers Array with IDs and usernames
|
||||
* @param int $id The user id to search for
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function getUsername(\Illuminate\Database\Eloquent\Collection $itemUsers, ?int $id)
|
||||
{
|
||||
if ($id && !isset($this->activeUsers[$id])) {
|
||||
$this->activeUsers[$id] = $this->findName($itemUsers, $id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a name from a specific id in a array
|
||||
*
|
||||
@@ -32,41 +78,4 @@ class HomeController extends Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user_id alreay exists on $users array.
|
||||
*
|
||||
* @param array $users The array with users
|
||||
* @param int $user_id The user_id to try to find on array
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isNewUser($users, $user_id)
|
||||
{
|
||||
return ($user_id && !isset($users[$user_id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$users = [];
|
||||
$items = User::loggedIn()->items()->with('users')->get();
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($this->isNewUser($users, $item->used_by)) {
|
||||
$users[$item->used_by] = $this->findName($item->users, $item->used_by);
|
||||
}
|
||||
|
||||
if ($this->isNewUser($users, $item->waiting_user_id)) {
|
||||
$users[$item->waiting_user_id] = $this->findName($item->users, $item->waiting_user_id);
|
||||
}
|
||||
}
|
||||
|
||||
$products = $items->sortBy('product.name', SORT_NATURAL | SORT_FLAG_CASE)->groupBy('product.name');
|
||||
return view('home', compact('products', 'users'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,63 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use \App\Item;
|
||||
use \App\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Auth;
|
||||
use Lang;
|
||||
use App\Item;
|
||||
use App\User;
|
||||
use App\Events\ReturnItem;
|
||||
use Illuminate\Http\Request;
|
||||
use PhpParser\Node\Stmt\TryCatch;
|
||||
|
||||
/**
|
||||
* Responsible to Take and Return an Item.
|
||||
*/
|
||||
class TakeController extends Controller
|
||||
{
|
||||
/**
|
||||
* The user take an item
|
||||
*
|
||||
* @param Request $request The form data
|
||||
*
|
||||
* @return home view
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$item = User::loggedIn()->items()->find(request('item'));
|
||||
if ($item->used_by) {
|
||||
|
||||
try {
|
||||
$item->takeItem();
|
||||
} catch (\Exception $e) {
|
||||
return back()->withErrors(
|
||||
\Lang::getFromJson(
|
||||
"This item is already taken"
|
||||
)
|
||||
Lang::getFromJson('This item is already taken')
|
||||
);
|
||||
}
|
||||
$item->used_by = \Auth::id();
|
||||
$item->save();
|
||||
|
||||
return redirect('home');
|
||||
}
|
||||
|
||||
/**
|
||||
* User return an item
|
||||
* Trigger an event: ReturnItem
|
||||
*
|
||||
* @param Request $request Form data
|
||||
*
|
||||
* @return View home
|
||||
*/
|
||||
public function delete(Request $request)
|
||||
{
|
||||
$item = User::loggedIn()->items()->find(request('item'));
|
||||
event(new ReturnItem($item));
|
||||
|
||||
try {
|
||||
$item->returnItem();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()->withErrors(
|
||||
Lang::getFromJson("You cannot return an item that is not with you")
|
||||
);
|
||||
}
|
||||
|
||||
event(new ReturnItem($item));
|
||||
return redirect('home');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use \Lang;
|
||||
use \App\User;
|
||||
use \App\Item;
|
||||
use \App\Product;
|
||||
@@ -43,7 +44,7 @@ class UserController extends Controller
|
||||
|
||||
if (count($userArray) == 0) {
|
||||
return back()->withErrors(
|
||||
\Lang::getFromJson("The e-mail address is not registered yet.")
|
||||
Lang::getFromJson("The e-mail address is not registered yet.")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ class UserController extends Controller
|
||||
->syncWithoutDetaching([request('item_id')]);
|
||||
} else {
|
||||
return back()->withErrors(
|
||||
\Lang::getFromJson(
|
||||
Lang::getFromJson(
|
||||
"You cannot add a user to a product that is not yourse."
|
||||
)
|
||||
);
|
||||
@@ -85,7 +86,7 @@ class UserController extends Controller
|
||||
->detach([request('item_id')]);
|
||||
} else {
|
||||
return back()->withErrors(
|
||||
\Lang::getFromJson(
|
||||
Lang::getFromJson(
|
||||
"You cannot remove a user from a product that is not yourse."
|
||||
)
|
||||
);
|
||||
|
||||
48
app/Item.php
48
app/Item.php
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App;
|
||||
|
||||
use Auth;
|
||||
use Lang;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Exception;
|
||||
|
||||
class Item extends Model
|
||||
{
|
||||
@@ -36,7 +39,23 @@ class Item extends Model
|
||||
*/
|
||||
public static function fromAuthUser()
|
||||
{
|
||||
return (new static)->where('user_id', \Auth::id());
|
||||
return (new static)->where('user_id', Auth::id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a specified item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function takeItem()
|
||||
{
|
||||
if (isset($this->used_by)) {
|
||||
throw new Exception("Trying to take an Item that is in use", 1);
|
||||
}
|
||||
|
||||
$this->used_by = Auth::id();
|
||||
$this->waiting_user_id = null;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,8 +65,35 @@ class Item extends Model
|
||||
*/
|
||||
public function returnItem()
|
||||
{
|
||||
if ($this->used_by != Auth::id()) {
|
||||
throw new Exception("Trying to return an empty Item or from other user", 1);
|
||||
}
|
||||
|
||||
$this->used_by = null;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a waiting user to the item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function storeAlert()
|
||||
{
|
||||
$this->waiting_user_id = Auth::id();
|
||||
$this->timestamps = false;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a waiting user to the item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeAlert()
|
||||
{
|
||||
$this->waiting_user_id = null;
|
||||
$this->timestamps = false;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ class AlertReturnedItem
|
||||
* Send an email to the user that
|
||||
* is waiting for the item
|
||||
*
|
||||
* @param ReturnItem $item
|
||||
* @param ReturnItem $event The return event that contains an item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(ReturnItem $event)
|
||||
|
||||
2978
composer.lock
generated
2978
composer.lock
generated
File diff suppressed because it is too large
Load Diff
9869
package-lock.json
generated
Normal file
9869
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@@ -10,13 +10,17 @@
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.18",
|
||||
"bootstrap": "^4.0.0",
|
||||
"cross-env": "^5.1",
|
||||
"jquery": "^3.2",
|
||||
"laravel-mix": "^2.0",
|
||||
"lodash": "^4.17.5",
|
||||
"popper.js": "^1.12",
|
||||
"vue": "^2.5.7"
|
||||
"axios": "^0.21",
|
||||
"bootstrap": "^4.6.0",
|
||||
"cross-env": "^5.2.1",
|
||||
"jquery": "^3.5.1",
|
||||
"laravel-mix": "^5.0.9",
|
||||
"lodash": "^4.17.20",
|
||||
"popper.js": "^1.16.1",
|
||||
"resolve-url-loader": "^3.1.2",
|
||||
"sass": "^1.32.7",
|
||||
"sass-loader": "^8.0.2",
|
||||
"vue": "^2.6.12",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
}
|
||||
}
|
||||
|
||||
14
public/css/app.css
vendored
14
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
3
public/js/app.js
vendored
3
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
77
public/js/app.js.LICENSE.txt
Normal file
77
public/js/app.js.LICENSE.txt
Normal file
@@ -0,0 +1,77 @@
|
||||
/*!
|
||||
* Bootstrap v4.5.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Determine if an object is a Buffer
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Sizzle CSS Selector Engine v2.3.5
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright JS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://js.foundation/
|
||||
*
|
||||
* Date: 2020-03-14
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Vue.js v2.6.12
|
||||
* (c) 2014-2020 Evan You
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.5.1
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright JS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2020-05-04T22:49Z
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Lodash <https://lodash.com/>
|
||||
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
|
||||
* Released under MIT license <https://lodash.com/license>
|
||||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
||||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
*/
|
||||
|
||||
/**!
|
||||
* @fileOverview Kickass library to create and place poppers near their reference elements.
|
||||
* @version 1.16.1
|
||||
* @license
|
||||
* Copyright (c) 2016 Federico Zivolo and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
4
public/mix-manifest.json
Normal file
4
public/mix-manifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js",
|
||||
"/css/app.css": "/css/app.css"
|
||||
}
|
||||
15
resources/lang/en/test.php
Normal file
15
resources/lang/en/test.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used during authentication for various
|
||||
| messages that we need to display to the user. You are free to modify
|
||||
| these language lines according to your application's requirements.
|
||||
|
|
||||
*/
|
||||
'failed' => 'These credentials do not match our records.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||
];
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
":itemname is available!": ":itemname está disponível!",
|
||||
"Hi, :username,": "Olá, :username,",
|
||||
"Good news: :itemname is available!": "Uma boa notícia: :itemname está disponível!",
|
||||
"Good news: :itemname is available!": "Boa notícia: :itemname está disponível!",
|
||||
"The item <em>:itemname (:productname)</em> is now available on **Share It**.": "O item <em>:itemname (:productname)</em> já está disponível no **Share It**.",
|
||||
"**Take It** before anyone else at the website:": "Entre no nosso site para usar o item antes de todo mundo.",
|
||||
|
||||
@@ -46,5 +46,7 @@
|
||||
|
||||
"The item doesn't exist.": "O item não existe.",
|
||||
"The product doesn't exist or doesn't belongs to you.": "O produto não existe ou não é seu.",
|
||||
"This item is already taken": "Esse item já está sendo usado"
|
||||
"This item is already taken": "Esse item já está sendo usado.",
|
||||
"You cannot return an item that is not with you": "Você não pode devolver um item que não está com você.",
|
||||
"Oh! This item has just being returned. Take it before anyone else!": "Opa! Esse item acabou de ser devolvido. Aproveite!"
|
||||
}
|
||||
@@ -1,6 +1,17 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
<script type="text/javascript">
|
||||
setInterval(
|
||||
function() {
|
||||
if (!document.hasFocus() ) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
},
|
||||
2*60000); //NOTE: period is passed in milliseconds
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'Laravel') }}</title>
|
||||
<title>{{ config('app.name', 'Laravel') }} {{ isset($usedItems) && $usedItems > 0 ? "(${usedItems})" : '' }}</title>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="{{ asset('js/app.js') }}" defer></script>
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
<!-- Copyright -->
|
||||
</div>
|
||||
</footer>
|
||||
@include('layouts.tracker')
|
||||
5
resources/views/layouts/tracker.blade.php
Normal file
5
resources/views/layouts/tracker.blade.php
Normal file
@@ -0,0 +1,5 @@
|
||||
@production
|
||||
<script type="text/javascript">
|
||||
var owa_baseUrl='https://brunofontes.net/owa/';var owa_cmds=owa_cmds||[];owa_cmds.push(['setSiteId','15a38975230dfe7528d647a1419be7f7']);owa_cmds.push(['trackPageView']);owa_cmds.push(['trackClicks']);owa_cmds.push(['trackDomStream']);(function(){var _owa=document.createElement('script');_owa.type='text/javascript';_owa.async=true;owa_baseUrl=('https:'==document.location.protocol?window.owa_baseSecUrl||owa_baseUrl.replace(/http:/,'https:'):owa_baseUrl);_owa.src=owa_baseUrl+'modules/base/js/owa.tracker-combined-min.js';var _owa_s=document.getElementsByTagName('script')[0];_owa_s.parentNode.insertBefore(_owa,_owa_s)}());
|
||||
</script>
|
||||
@endproduction
|
||||
@@ -114,5 +114,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include('layouts.tracker')
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user