Laravel 9

Update to Laravel 9

Commit for the upcoming from-end update.
This commit is contained in:
Julian Prieber 2022-11-08 16:11:59 +01:00
parent b5a5c7db49
commit 951df23c91
75 changed files with 14235 additions and 10974 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ Homestead.json
Homestead.yaml Homestead.yaml
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
_ide_*

View File

@ -0,0 +1,130 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\LinkType;
use Illuminate\Http\Request;
use \App\Http\Requests\LinkTypeRequest;
class LinkTypeController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
// get all the sharks
$LinkTypes = LinkType::all();
// load the view and pass the link types
return View('admin.linktype.index')
->with('linktype', $LinkTypes);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return View('admin.linktype.create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(LinkTypeRequest $request)
{
// validate
// read more on validation at http://laravel.com/docs/validation
$validated = $request->validated();
// store
$LinkType = new LinkType;
$LinkType->typename = $request->typename;
$LinkType->title = $request->title;
$LinkType->description = $request->description;
$LinkType->icon = $request->icon;
$LinkType->params = $request->params;
$LinkType->save();
// redirect
return Redirect('admin/linktype')
->with('success', 'New link type has been added.');
}
/**
* Display the specified resource.
*
* @param \App\Models\LinkType $linkType
* @return \Illuminate\Http\Response
*/
public function show(LinkType $linkType)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\LinkType $linkType
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$lt = LinkType::find($id);
// show the edit form and pass the shark
return View('admin.linktype.edit', ['linktype' => $lt]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\LinkType $linkType
* @return \Illuminate\Http\Response
*/
public function update(LinkTypeRequest $request, $id)
{
$linktype = LinkType::find($id);
$validated = $request->validated();
// store
$linktype->title = $request->title;
$linktype->description = $request->description;
$linktype->icon = $request->icon;
$linktype->params = $request->params;
$linktype->save();
// redirect
return Redirect('admin/linktype')
->with('success', 'Link type updated.');
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\LinkType $linkType
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
// delete
$linktype = LinkType::find($id);
$linktype->delete();
// redirect
return Redirect('admin/linktype')
->with('success', 'Link type deleted');
}
}

View File

@ -20,14 +20,14 @@ use App\Models\Page;
class AdminController extends Controller class AdminController extends Controller
{ {
//Statistics of the number of clicks and links //Statistics of the number of clicks and links
public function index() public function index()
{ {
$userId = Auth::user()->id; $userId = Auth::user()->id;
$littlelink_name = Auth::user()->littlelink_name; $littlelink_name = Auth::user()->littlelink_name;
$links = Link::where('user_id', $userId)->select('link')->count(); $links = Link::where('user_id', $userId)->select('link')->count();
$clicks = Link::where('user_id', $userId)->sum('click_number'); $clicks = Link::where('user_id', $userId)->sum('click_number');
$userNumber = User::count(); $userNumber = User::count();
$siteLinks = Link::count(); $siteLinks = Link::count();
$siteClicks = Link::sum('click_number'); $siteClicks = Link::sum('click_number');
@ -40,7 +40,7 @@ class AdminController extends Controller
{ {
$usersType = $request->type; $usersType = $request->type;
switch($usersType){ switch ($usersType) {
case 'all': case 'all':
$data['users'] = User::select('id', 'name', 'email', 'littlelink_name', 'role', 'block', 'email_verified_at')->get(); $data['users'] = User::select('id', 'name', 'email', 'littlelink_name', 'role', 'block', 'email_verified_at')->get();
return view('panel/users', $data); return view('panel/users', $data);
@ -52,12 +52,12 @@ class AdminController extends Controller
case 'vip': case 'vip':
$data['users'] = User::where('role', 'email', 'vip')->select('id', 'name', 'littlelink_name', 'role', 'block', 'email_verified_at')->get(); $data['users'] = User::where('role', 'email', 'vip')->select('id', 'name', 'littlelink_name', 'role', 'block', 'email_verified_at')->get();
return view('panel/users', $data); return view('panel/users', $data);
break; break;
case 'admin': case 'admin':
$data['users'] = User::where('role', 'email', 'admin')->select('id', 'name', 'littlelink_name', 'role', 'block', 'email_verified_at')->get(); $data['users'] = User::where('role', 'email', 'admin')->select('id', 'name', 'littlelink_name', 'role', 'block', 'email_verified_at')->get();
return view('panel/users', $data); return view('panel/users', $data);
break; break;
} }
} }
//Search user by name //Search user by name
@ -74,9 +74,9 @@ class AdminController extends Controller
$id = $request->id; $id = $request->id;
$status = $request->block; $status = $request->block;
if($status == 'yes'){ if ($status == 'yes') {
$block = 'no'; $block = 'no';
}elseif($status == 'no'){ } elseif ($status == 'no') {
$block = 'yes'; $block = 'yes';
} }
@ -91,9 +91,9 @@ class AdminController extends Controller
$id = $request->id; $id = $request->id;
$status = $request->verify; $status = $request->verify;
if($status == '-'){ if ($status == '-') {
$verify = '0000-00-00 00:00:00'; $verify = '0000-00-00 00:00:00';
}else{ } else {
$verify = NULL; $verify = NULL;
} }
@ -101,7 +101,7 @@ class AdminController extends Controller
return redirect('panel/users/all'); return redirect('panel/users/all');
} }
//Create new user from the Admin Panel //Create new user from the Admin Panel
public function createNewUser() public function createNewUser()
{ {
@ -116,7 +116,7 @@ class AdminController extends Controller
$pieces = []; $pieces = [];
$max = mb_strlen($keyspace, '8bit') - 1; $max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) { for ($i = 0; $i < $length; ++$i) {
$pieces []= $keyspace[random_int(0, $max)]; $pieces[] = $keyspace[random_int(0, $max)];
} }
return implode('', $pieces); return implode('', $pieces);
} }
@ -129,7 +129,7 @@ class AdminController extends Controller
'block' => 'no', 'block' => 'no',
]); ]);
return redirect('panel/edit-user/'. $user->id); return redirect('panel/edit-user/' . $user->id);
} }
//Delete existing user //Delete existing user
@ -137,7 +137,7 @@ class AdminController extends Controller
{ {
$id = $request->id; $id = $request->id;
$user = User::find($id); $user = User::find($id);
Schema::disableForeignKeyConstraints(); Schema::disableForeignKeyConstraints();
$user->forceDelete(); $user->forceDelete();
@ -152,16 +152,15 @@ class AdminController extends Controller
$id = $request->id; $id = $request->id;
$data['user'] = User::where('id', $id)->get(); $data['user'] = User::where('id', $id)->get();
return view('panel/edit-user', $data);
return view('panel/edit-user', $data);
} }
//Show link, click number, up link in links page //Show link, click number, up link in links page
public function showLinksUser(request $request) public function showLinksUser(request $request)
{ {
$id = $request->id; $id = $request->id;
$data['user'] = User::where('id', $id)->get(); $data['user'] = User::where('id', $id)->get();
$data['links'] = Link::select('id', 'link', 'title', 'order', 'click_number', 'up_link', 'links.button_id')->where('user_id', $id)->orderBy('up_link', 'asc')->orderBy('order', 'asc')->paginate(10); $data['links'] = Link::select('id', 'link', 'title', 'order', 'click_number', 'up_link', 'links.button_id')->where('user_id', $id)->orderBy('up_link', 'asc')->orderBy('order', 'asc')->paginate(10);
@ -174,20 +173,20 @@ class AdminController extends Controller
$linkId = $request->id; $linkId = $request->id;
Link::where('id', $linkId)->delete(); Link::where('id', $linkId)->delete();
return back(); return back();
} }
//Save user edit //Save user edit
public function editUser(request $request) public function editUser(request $request)
{ {
$request->validate([ $request->validate([
'name' => '', 'name' => '',
'email' => '', 'email' => '',
'password' => '', 'password' => '',
'littlelink_name' => '', 'littlelink_name' => '',
]); ]);
$id = $request->id; $id = $request->id;
$name = $request->name; $name = $request->name;
$email = $request->email; $email = $request->email;
@ -196,14 +195,14 @@ class AdminController extends Controller
$littlelink_name = $request->littlelink_name; $littlelink_name = $request->littlelink_name;
$littlelink_description = $request->littlelink_description; $littlelink_description = $request->littlelink_description;
$role = $request->role; $role = $request->role;
if($request->password == '' ) { if ($request->password == '') {
User::where('id', $id)->update(['name' => $name, 'email' => $email, 'littlelink_name' => $littlelink_name, 'littlelink_description' => $littlelink_description, 'role' => $role]); User::where('id', $id)->update(['name' => $name, 'email' => $email, 'littlelink_name' => $littlelink_name, 'littlelink_description' => $littlelink_description, 'role' => $role]);
} else { } else {
User::where('id', $id)->update(['name' => $name, 'email' => $email, 'password' => $password, 'littlelink_name' => $littlelink_name, 'littlelink_description' => $littlelink_description, 'role' => $role]); User::where('id', $id)->update(['name' => $name, 'email' => $email, 'password' => $password, 'littlelink_name' => $littlelink_name, 'littlelink_description' => $littlelink_description, 'role' => $role]);
} }
if(!empty($profilePhoto)){ if (!empty($profilePhoto)) {
$profilePhoto->move(base_path('/img'), $littlelink_name . ".png"); $profilePhoto->move(base_path('/img'), $littlelink_name . ".png");
} }
return redirect('panel/users/all'); return redirect('panel/users/all');
@ -244,9 +243,9 @@ class AdminController extends Controller
Page::first()->update(['home_message' => $message]); Page::first()->update(['home_message' => $message]);
if(!empty($logo)){ if (!empty($logo)) {
$logo->move(base_path('/littlelink/images/'), "avatar.png"); $logo->move(base_path('/littlelink/images/'), "avatar.png");
} }
return back(); return back();
} }
@ -265,7 +264,7 @@ class AdminController extends Controller
return view('pages', ['data' => $data, 'name' => $name]); return view('pages', ['data' => $data, 'name' => $name]);
} }
//Statistics of the number of clicks and links //Statistics of the number of clicks and links
public function phpinfo() public function phpinfo()
{ {
return view('panel/phpinfo'); return view('panel/phpinfo');
@ -310,59 +309,57 @@ class AdminController extends Controller
$del = $request->deltheme; $del = $request->deltheme;
if (empty($del)) { if (empty($del)) {
echo '<script type="text/javascript">'; echo '<script type="text/javascript">';
echo 'alert("No themes to delete!");'; echo 'alert("No themes to delete!");';
echo 'window.location.href = "../studio/theme";'; echo 'window.location.href = "../studio/theme";';
echo '</script>'; echo '</script>';
} else { } else {
$folderName = base_path() . '/themes/' . $del; $folderName = base_path() . '/themes/' . $del;
function removeFolder($folderName) { function removeFolder($folderName)
{
if (is_dir($folderName))
if (is_dir($folderName))
$folderHandle = opendir($folderName);
$folderHandle = opendir($folderName);
if (!$folderHandle)
if (!$folderHandle)
return false;
return false;
while($file = readdir($folderHandle)) {
while ($file = readdir($folderHandle)) {
if ($file != "." && $file != "..") {
if ($file != "." && $file != "..") {
if (!is_dir($folderName."/".$file))
if (!is_dir($folderName . "/" . $file))
unlink($folderName."/".$file);
unlink($folderName . "/" . $file);
else
else
removeFolder($folderName.'/'.$file);
removeFolder($folderName . '/' . $file);
} }
}
}
closedir($folderHandle);
closedir($folderHandle);
rmdir($folderName);
rmdir($folderName); }
removeFolder($folderName);
return Redirect('/panel/theme');
} }
}
removeFolder($folderName);
return Redirect('/panel/theme');
}}
// Update themes // Update themes
public function updateThemes() public function updateThemes()
@ -372,82 +369,89 @@ class AdminController extends Controller
if ($handle = opendir('themes')) { if ($handle = opendir('themes')) {
while (false !== ($entry = readdir($handle))) { while (false !== ($entry = readdir($handle))) {
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){ if (file_exists(base_path('themes') . '/' . $entry . '/readme.md')) {
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md'); $text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
$pattern = '/Theme Version:.*/'; $pattern = '/Theme Version:.*/';
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE); preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
$verNr = substr($matches[0][0],15);} if (!count($matches)) continue;
$verNr = substr($matches[0][0], 15);
$themeVe = NULL; }
if ($entry != "." && $entry != "..") {
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){
if(!strpos(file_get_contents(base_path('themes') . '/' . $entry . '/readme.md'), 'Source code:')){$hasSource = false;}else{
$hasSource = true;
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
$pattern = '/Source code:.*/';
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
$sourceURL = substr($matches[0][0],13);
$replaced = str_replace("https://github.com/", "https://raw.githubusercontent.com/", trim($sourceURL));
$replaced = $replaced . "/main/readme.md";
if (strpos($sourceURL, 'github.com')){
ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 6.0)');
try{
$textGit = file_get_contents($replaced);
$patternGit = '/Theme Version:.*/';
preg_match($patternGit, $textGit, $matches, PREG_OFFSET_CAPTURE);
$sourceURLGit = substr($matches[0][0],15);
$Vgitt = 'v' . $sourceURLGit;
$verNrv = 'v' . $verNr;
}catch(Exception $ex){
$themeVe = "error";
$Vgitt = NULL;
$verNrv = NULL;
}
if(trim($Vgitt) > trim($verNrv)){
$fileUrl = trim($sourceURL) . '/archive/refs/tags/' . trim($Vgitt) . '.zip'; $themeVe = NULL;
if ($entry != "." && $entry != "..") {
file_put_contents(base_path('themes/theme.zip'), fopen($fileUrl, 'r')); if (file_exists(base_path('themes') . '/' . $entry . '/readme.md')) {
if (!strpos(file_get_contents(base_path('themes') . '/' . $entry . '/readme.md'), 'Source code:')) {
$hasSource = false;
$zip = new ZipArchive; } else {
$zip->open(base_path() . '/themes/theme.zip'); $hasSource = true;
$zip->extractTo(base_path('themes'));
$zip->close();
unlink(base_path() . '/themes/theme.zip');
$folder = base_path('themes');
$regex = '/[0-9.-]/';
$files = scandir($folder);
foreach($files as $file) { $text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
if($file !== '.' && $file !== '..') { $pattern = '/Source code:.*/';
if(preg_match($regex, $file)) { preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
$new_file = preg_replace($regex, '', $file); $sourceURL = substr($matches[0][0], 13);
File::copyDirectory($folder . '/' . $file, $folder . '/' . $new_file);
$dirname = $folder . '/' . $file; $replaced = str_replace("https://github.com/", "https://raw.githubusercontent.com/", trim($sourceURL));
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $replaced = $replaced . "/main/readme.md";
system('rmdir '.escapeshellarg($dirname).' /s /q');
} else { if (strpos($sourceURL, 'github.com')) {
system("rm -rf ".escapeshellarg($dirname));
} ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 6.0)');
} try {
$textGit = file_get_contents($replaced);
$patternGit = '/Theme Version:.*/';
preg_match($patternGit, $textGit, $matches, PREG_OFFSET_CAPTURE);
$sourceURLGit = substr($matches[0][0], 15);
$Vgitt = 'v' . $sourceURLGit;
$verNrv = 'v' . $verNr;
} catch (Exception $ex) {
$themeVe = "error";
$Vgitt = NULL;
$verNrv = NULL;
}
if (trim($Vgitt) > trim($verNrv)) {
$fileUrl = trim($sourceURL) . '/archive/refs/tags/' . trim($Vgitt) . '.zip';
file_put_contents(base_path('themes/theme.zip'), fopen($fileUrl, 'r'));
$zip = new ZipArchive;
$zip->open(base_path() . '/themes/theme.zip');
$zip->extractTo(base_path('themes'));
$zip->close();
unlink(base_path() . '/themes/theme.zip');
$folder = base_path('themes');
$regex = '/[0-9.-]/';
$files = scandir($folder);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
if (preg_match($regex, $file)) {
$new_file = preg_replace($regex, '', $file);
File::copyDirectory($folder . '/' . $file, $folder . '/' . $new_file);
$dirname = $folder . '/' . $file;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
system('rmdir ' . escapeshellarg($dirname) . ' /s /q');
} else {
system("rm -rf " . escapeshellarg($dirname));
}
}
}
}
}
}
} }
} }
}
} }
} }
}
}
}}}
return Redirect('/studio/theme'); return Redirect('/studio/theme');
@ -458,5 +462,4 @@ class AdminController extends Controller
{ {
return view('/panel/theme'); return view('/panel/theme');
} }
} }

View File

@ -9,6 +9,7 @@ use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
class RegisteredUserController extends Controller class RegisteredUserController extends Controller
{ {
@ -38,14 +39,29 @@ class RegisteredUserController extends Controller
'password' => 'required|string|confirmed|min:8', 'password' => 'required|string|confirmed|min:8',
]); ]);
Auth::login($user = User::create([ $name = $request->input('name');
'name' => $request->name,
'email' => $request->email, if(DB::table('users')->where('littlelink_name', $request->name)->exists())
'password' => Hash::make($request->password), {
'role' => 'user', Auth::login($user = User::create([
'block' => 'no', 'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'role' => 'user',
'block' => 'no',
]));
} else {
Auth::login($user = User::create([
'name' => $request->name,
'email' => $request->email,
'littlelink_name' => $request->name,
'password' => Hash::make($request->password),
'role' => 'user',
'block' => 'no',
]));
}
]));
event(new Registered($user)); event(new Registered($user));

View File

@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\SocialAccount;
class SocialLoginController extends Controller
{
public function redirectToProvider(String $provider)
{
return \Socialite::driver($provider)->redirect();
}
public function providerCallback(String $provider)
{
try {
$social_user = \Socialite::driver($provider)->user();
// First Find Social Account
$account = SocialAccount::where([
'provider_name' => $provider,
'provider_id' => $social_user->getId()
])->first();
// If Social Account Exist then Find User and Login
if ($account) {
auth()->login($account->user);
return redirect('/studio/index');
}
// Find User
$user = User::where([
'email' => $social_user->getEmail()
])->first();
// If User not found, then create new user
if (!$user) {
$user = User::create([
'email' => $social_user->getEmail(),
'name' => $social_user->getName(),
'image' => $social_user->getAvatar(),
'littlelink_name' => $social_user->getNickname(),
'email_verified_at' => now(),
]);
}
// Create Social Accounts
$user->socialAccounts()->create([
'provider_id' => $social_user->getId(),
'provider_name' => $provider
]);
// Login
auth()->login($user);
return redirect('/studio/index');
} catch (\Exception $e) {
return redirect()->route('login')->withErrors($e->getMessage());
}
}
}

View File

@ -16,10 +16,10 @@ class HomeController extends Controller
{ {
$message = Page::select('home_message')->first(); $message = Page::select('home_message')->first();
$countButton = Button::count(); $countButton = Button::count();
$updatedPages = DB::table('links')->join('users', 'users.id', '=', 'links.user_id')->select('users.littlelink_name', DB::raw('max(links.created_at) as created_at'))->groupBy('links.user_id')->orderBy('created_at', 'desc')->take(4)->get(); $updatedPages = DB::table('links')->join('users', 'users.id', '=', 'links.user_id')->select('users.littlelink_name', 'users.image', DB::raw('max(links.created_at) as created_at'))->groupBy('links.user_id')->orderBy('created_at', 'desc')->take(4)->get();
return view('home', ['message' => $message, 'countButton' => $countButton, 'updatedPages' => $updatedPages]); return view('home', ['message' => $message, 'countButton' => $countButton, 'updatedPages' => $updatedPages]);
} }

View File

@ -0,0 +1,165 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\LinkType;
use App\Models\Link;
use App\Models\Button;
use micro\FormFactory;
use DB;
class LinkTypeViewController extends Controller
{
public function getParamForm($typeid, $linkId = 0)
{
$linkType = LinkType::select('params', 'typename')->where('id', $typeid)->First();
$data['params'] = '';
$data['link_title'] = '';
$data['link_url'] = '';
if ($linkId) {
$link = Link::find($linkId);
$data['params'] = json_decode($link['type_params']);
$data['link_title'] = $link->title;
$data['link_url'] = $link->link;
}
if (!empty($linkType) && $linkType->typename === 'predefined') {
// get buttons list if showing predefined form
$buttons = Button::select('name')->orderBy('name', 'asc')->get();
foreach ($buttons as $btn) {
$data['buttons'][] = [
'name' => $btn->name,
'title' => ucwords($btn->name),
'selected' => (is_object($data['params']) && $data['params']->button === $btn->name)
];
}
//echo "<pre>"; print_r($data['params']); exit;
}
return view('components.pageitems.'. $linkType->typename.'-form', $data);
$jsonForm = FormFactory::jsonForm();
try {
$json = $linkType->params;
} catch (\Throwable $th) {
//throw $th;
}
// dynamiclly create params for predefined website to fill a select list with available buttons
if (!empty($linkType) && $linkType->typename === 'predefined') {
$buttons = Button::select('name')->orderBy('name', 'asc')->get();
$pdParams[] = ['tag' => 'select', 'name' => 'button', 'id'=> 'button'];
foreach ($buttons as $btn) {
$pdParams[0]['value'][] = [
'tag'=>'option',
'label' => ucwords($btn->name),
'value' => $btn->name
];
}
$pdParams[] = ['tag' => 'input', 'name' => 'link_title', 'id' => 'link_title', 'name' => 'link_title', 'tip' => 'Leave blank for default title'];
$pdParams[] = ['tag' => 'input', 'name' => 'link_url', 'id' => 'link_url', 'name' => 'link_url', 'tip' => 'Enter the url address for this site.'];
$json = json_encode($pdParams);
}
if (empty($json)) {
$json =
<<<EOS
[{
"tag": "input",
"id": "link_title",
"for": "link_title",
"label": "Link Title *",
"type": "text",
"name": "link_title",
"class": "form-control",
"tip": "Enter a title for this link",
"required": "required"
},
{
"tag": "input",
"id": "link",
"for": "link",
"label": "Link Address *",
"type": "text",
"name": "link_title",
"class": "form-control",
"tip": "Enter the website address",
"required": "required"
}
]
EOS;
}
if ($linkId) {
$link = Link::find($linkId);
}
// cleanup json
$params = json_decode($json, true);
$idx = 0;
foreach ($params as $p) {
if (!array_key_exists('for', $p))
$params[$idx]['for'] = $p['name'];
if (!array_key_exists('label', $p))
$params[$idx]['label'] = ucwords(preg_replace('/[^a-zA-Z0-9-]/', ' ', $p['name']));
if (!array_key_exists('label', $p) || !str_contains($p['class'], 'form-control')) {
$params[$idx]['class'] = " form-control";
}
// get existing values if any
if ($link) {
$typeParams = json_decode($link['type_params']);
//echo "<pre>";
// print_r($typeParams);
//print_r($params[$idx]);
//echo "</pre>";
if ($typeParams && property_exists($typeParams, $params[$idx]['name'])) {
if (key_exists('value', $params[$idx]) && is_array($params[$idx]['value'])) {
$optIdx = 0;
foreach ($params[$idx]['value'] as $option) {
//echo $option['value']."<br />";
//echo $typeParams->{$params[$idx]['name']};
if ($option['value'] == $typeParams->{$params[$idx]['name']}) {
$params[$idx]['value'][$optIdx]['selected'] = true;
break;
}
//echo $key ." = ".$value;
$optIdx++;
}
} else {
$params[$idx]['value'] = $typeParams->{$params[$idx]['name']};
}
}
}
$idx++;
}
$json = json_encode($params);
echo $jsonForm->render($json);
}
}

View File

@ -4,6 +4,8 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Cohensive\OEmbed\Facades\OEmbed;
use Illuminate\Support\Facades\Schema;
use Auth; use Auth;
use DB; use DB;
@ -13,20 +15,23 @@ use File;
use App\Models\User; use App\Models\User;
use App\Models\Button; use App\Models\Button;
use App\Models\Link; use App\Models\Link;
use App\Models\LinkType;
//Function tests if string starts with certain string (used to test for illegal strings) //Function tests if string starts with certain string (used to test for illegal strings)
function stringStartsWith($haystack,$needle,$case=true) { function stringStartsWith($haystack, $needle, $case = true)
if ($case){ {
if ($case) {
return strpos($haystack, $needle, 0) === 0; return strpos($haystack, $needle, 0) === 0;
} }
return stripos($haystack, $needle, 0) === 0; return stripos($haystack, $needle, 0) === 0;
} }
//Function tests if string ends with certain string (used to test for illegal strings) //Function tests if string ends with certain string (used to test for illegal strings)
function stringEndsWith($haystack,$needle,$case=true) { function stringEndsWith($haystack, $needle, $case = true)
{
$expectedPosition = strlen($haystack) - strlen($needle); $expectedPosition = strlen($haystack) - strlen($needle);
if ($case){ if ($case) {
return strrpos($haystack, $needle, 0) === $expectedPosition; return strrpos($haystack, $needle, 0) === $expectedPosition;
} }
return strripos($haystack, $needle, 0) === $expectedPosition; return strripos($haystack, $needle, 0) === $expectedPosition;
@ -35,18 +40,36 @@ function stringEndsWith($haystack,$needle,$case=true) {
class UserController extends Controller class UserController extends Controller
{ {
//Statistics of the number of clicks and links //Statistics of the number of clicks and links
public function index() public function index()
{ {
$userId = Auth::user()->id; $userId = Auth::user()->id;
$littlelink_name = Auth::user()->littlelink_name; $littlelink_name = Auth::user()->littlelink_name;
$userinfo = User::find($userId);
$links = Link::where('user_id', $userId)->select('link')->count(); $links = Link::where('user_id', $userId)->select('link')->count();
$clicks = Link::where('user_id', $userId)->sum('click_number'); $clicks = Link::where('user_id', $userId)->sum('click_number');
$topLinks = Link::where('user_id', $userId)->orderby('click_number', 'desc')
->whereNotNull('link')->where('link', '<>', '')
->take(5)->get();
return view('studio/index', ['littlelink_name' => $littlelink_name, 'links' => $links, 'clicks' => $clicks]); $pageStats = [
'visitors' => [
'all' => visits('App\Models\User', $littlelink_name)->count(),
'day' => visits('App\Models\User', $littlelink_name)->period('day')->count(),
'week' => visits('App\Models\User', $littlelink_name)->period('week')->count(),
'month' => visits('App\Models\User', $littlelink_name)->period('month')->count(),
'year' => visits('App\Models\User', $littlelink_name)->period('year')->count(),
],
'os' => visits('App\Models\User', $littlelink_name)->operatingSystems(),
'referers' => visits('App\Models\User', $littlelink_name)->refs(),
'countries' => visits('App\Models\User', $littlelink_name)->countries(),
];
return view('studio/index', ['greeting' => $userinfo->name, 'toplinks' => $topLinks, 'links' => $links, 'clicks' => $clicks, 'pageStats' => $pageStats]);
} }
//Show littlelink page. example => http://127.0.0.1:8000/+admin //Show littlelink page. example => http://127.0.0.1:8000/+admin
@ -70,75 +93,156 @@ class UserController extends Controller
//Show littlelink page as home page if set in config //Show littlelink page as home page if set in config
public function littlelinkhome(request $request) public function littlelinkhome(request $request)
{ {
$littlelink_name = env('HOME_URL'); $littlelink_name = env('HOME_URL');
$id = User::select('id')->where('littlelink_name', $littlelink_name)->value('id'); $id = User::select('id')->where('littlelink_name', $littlelink_name)->value('id');
if (empty($id)) { if (empty($id)) {
return abort(404); return abort(404);
} }
$userinfo = User::select('name', 'littlelink_name', 'littlelink_description', 'theme')->where('id', $id)->first(); $userinfo = User::select('name', 'littlelink_name', 'littlelink_description', 'image', 'theme')->where('id', $id)->first();
$information = User::select('name', 'littlelink_name', 'littlelink_description', 'theme')->where('id', $id)->get(); $information = User::select('name', 'littlelink_name', 'littlelink_description', 'image', 'theme')->where('id', $id)->get();
$links = DB::table('links')->join('buttons', 'buttons.id', '=', 'links.button_id')->select('links.link', 'links.id', 'links.button_id', 'links.title', 'links.custom_css', 'links.custom_icon', 'buttons.name')->where('user_id', $id)->orderBy('up_link', 'asc')->orderBy('order', 'asc')->get(); $links = DB::table('links')
->where('user_id', $id)
->orderBy('up_link', 'asc')->orderBy('order', 'asc')
->get();
return view('littlelink', ['userinfo' => $userinfo, 'information' => $information, 'links' => $links, 'littlelink_name' => $littlelink_name]); return view('littlelink', ['userinfo' => $userinfo, 'information' => $information, 'links' => $links, 'littlelink_name' => $littlelink_name]);
} }
//Show buttons for add link //Show add/update form
public function showButtons() public function AddUpdateLink($id = 0)
{ {
$data['buttons'] = Button::select('name')->orderBy('name', 'asc')->get();
return view('studio/add-link', $data); if ($id !== 0) {
$linkData = Link::find($id);
} else {
$linkData = new Link(['typename' => 'link', 'id'=>'0']);
}
$data['LinkTypes'] = LinkType::get();
$data['LinkData'] = $linkData;
$data['LinkID'] = $id;
$data['title'] = "link";
foreach ($data['LinkTypes']->toArray() as $key => $val) {
if ($val['typename'] === $linkData['typename']) {
$data['SelectedLinkType'] = $val;
break;
}
}
return view('studio/edit-link', $data);
} }
//Save add link //Save add link
public function addLink(request $request) public function saveLink(request $request)
{ {
if ($request->button == 'heading' or $request->button == 'space') // if ($request->button == 'heading' or $request->button == 'space')
$request->validate([ // $request->validate([
'link' => '', // 'link' => '',
'title' => '', // 'title' => '',
'button' => 'required' // 'button' => 'required'
]); // ]);
else // else
$request->validate([ // $request->validate([
'link' => 'required', // 'link' => 'required',
'title' => '', // 'title' => '',
'button' => 'required' // 'button' => 'required'
]); // ]);
if ($request->button == 'phone') $linkType = LinkType::find($request->linktype_id);
$link1 = 'tel:' . $request->link; $LinkTitle = ($request->link_text ?? $request->link_title) ?? $request->title;
elseif ($request->button == 'default email' or $request->button == 'default email_alt') $LinkURL = $request->link_url ?? $request->link;
$link1 = 'mailto:' . $request->link;
elseif (stringStartsWith($request->link,'http://') == 'true' or stringStartsWith($request->link,'https://') == 'true')
$link1 = $request->link; $OrigLink = Link::find($request->linkid);
else
$link1 = 'https://' . $request->link;
if (stringEndsWith($request->link,'/') == 'true') // if (stringStartsWith($LinkURL, 'http://') == 'true' or stringStartsWith($LinkURL, 'https://') == 'true')
$link = rtrim($link1, "/ "); // $link1 = $LinkURL;
else // elseif (!empty($LinkURL))
$link = $link1; // $link1 = 'https://' . $LinkURL;
if ($request->title == '')
$title = $request->button; // if (stringEndsWith($LinkURL, '/') == 'true')
else // $link = rtrim($link1, "/ ");
$title = $request->title; // else
$button = $request->button; // $link = $link1;
$customParams = [];
foreach ($request->all() as $key => $param) {
//echo $key . " = " . $param . "<br />";
if (str_starts_with($key, "_") || in_array($key, ['linktype_id', 'linktype_title', 'link_text', 'link_url']))
continue;
$customParams[$key] = $param;
}
$userId = Auth::user()->id; $userId = Auth::user()->id;
$buttonId = Button::select('id')->where('name' , $button)->value('id'); $button = Button::where('name', $request->button)->first();
$links = new Link; if ($button && empty($LinkTitle))
$links->link = $link; $LinkTitle = ucwords($button->name);
$links->user_id = $userId;
$links->title = $title; if ($linkType->typename == 'video' && empty($LinkTitle)) {
$links->button_id = $buttonId; $embed = OEmbed::get($LinkURL);
$links->save(); if ($embed) {
$links->order = ($links->id - 1); $LinkTitle = $embed->data()['title'];
$links->save(); }
}
$message = (ucwords($button?->name) ?? ucwords($linkType->typename)). " has been ";
if ($OrigLink) {
//EDITING EXISTING
$OrigLink->update([
'link' => $LinkURL,
'title' => $LinkTitle,
'button_id' => $button?->id,
]);
$message .="updated";
} else {
// ADDING NEW
$isCustomWebsite = $customParams['GetSiteIcon'] ?? null;
$SpacerHeight = $customParams['height'] ?? null;
$links = new Link;
$links->link = $LinkURL;
$links->user_id = $userId;
if($linkType->typename == "spacer"){
$links->title = $SpacerHeight;
}else{
$links->title = $LinkTitle;
}
if($linkType->typename == "link" and $isCustomWebsite == "1"){
$links->button_id = "2";
}elseif($linkType->typename == "link"){
$links->button_id = "1";
}elseif($linkType->typename == "spacer"){
$links->button_id = "43";
}elseif($linkType->typename == "heading"){
$links->button_id = "42";
}else{
$links->button_id = $button?->id;
}
// $links->type_params = json_encode($customParams);
// $links->typename = $linkType->typename;
$links->save();
$links->order = ($links->id - 1);
$links->save();
$message .= "added";
}
return Redirect('studio/links')
->with('success', $message);
// echo $customParams['GetSiteIcon'];
return back();
} }
public function sortLinks(Request $request) public function sortLinks(Request $request)
@ -179,23 +283,31 @@ class UserController extends Controller
]); ]);
} }
//Count the number of clicks and redirect to link //Count the number of clicks and redirect to link
public function clickNumber(request $request) public function clickNumber(request $request)
{ {
$link = $request->link; $link = $request->link;
$query = $request->query();
$linkId = $request->id; $linkId = $request->id;
if(empty($link && $linkId)) if (empty($link && $linkId)) {
{
return abort(404); return abort(404);
} }
if (!empty($query)) {
$qs = [];
foreach ($query as $qk => $qv) {
$qs[] = $qk . '=' . $qv;
}
$link = $link . '?' . implode('&', $qs);
}
Link::where('id', $linkId)->increment('click_number', 1); Link::where('id', $linkId)->increment('click_number', 1);
$link = Link::select('link')->where('id', $linkId)->get()[0]['link'];
return redirect()->away($link); return redirect()->away($link);
} }
//Show link, click number, up link in links page //Show link, click number, up link in links page
public function showLinks() public function showLinks()
{ {
@ -242,8 +354,8 @@ class UserController extends Controller
$linkId = $request->id; $linkId = $request->id;
Link::where('id', $linkId)->delete(); Link::where('id', $linkId)->delete();
return back(); return back()->with('success', 'Link Deleted');
} }
//Raise link on the littlelink page //Raise link on the littlelink page
@ -252,9 +364,9 @@ class UserController extends Controller
$linkId = $request->id; $linkId = $request->id;
$upLink = $request->up; $upLink = $request->up;
if($upLink == 'yes'){ if ($upLink == 'yes') {
$up = 'no'; $up = 'no';
}elseif($upLink == 'no'){ } elseif ($upLink == 'no') {
$up = 'yes'; $up = 'yes';
} }
@ -274,11 +386,10 @@ class UserController extends Controller
$custom_css = Link::where('id', $linkId)->value('custom_css'); $custom_css = Link::where('id', $linkId)->value('custom_css');
$buttonId = Link::where('id', $linkId)->value('button_id'); $buttonId = Link::where('id', $linkId)->value('button_id');
$buttonName = Button::where('id', $buttonId)->value('name'); $buttonName = Button::where('id', $buttonId)->value('name');
$buttons = Button::select('id', 'name')->orderBy('name', 'asc')->get();
return view('studio/edit-link', ['custom_css' => $custom_css, 'buttonId' => $buttonId, 'buttons' => $buttons, 'link' => $link, 'title' => $title, 'order' => $order, 'id' => $linkId , 'buttonName' => $buttonName]);
$buttons = Button::select('id', 'name')->orderBy('name', 'asc')->get();
return view('studio/edit-link', ['custom_css' => $custom_css, 'buttonId' => $buttonId, 'buttons' => $buttons, 'link' => $link, 'title' => $title, 'order' => $order, 'id' => $linkId, 'buttonName' => $buttonName]);
} }
//Show custom CSS + custom icon //Show custom CSS + custom icon
@ -294,9 +405,8 @@ class UserController extends Controller
$buttonId = Link::where('id', $linkId)->value('button_id'); $buttonId = Link::where('id', $linkId)->value('button_id');
$buttons = Button::select('id', 'name')->get(); $buttons = Button::select('id', 'name')->get();
return view('studio/button-editor', ['custom_icon' => $custom_icon, 'custom_css' => $custom_css, 'buttonId' => $buttonId, 'buttons' => $buttons, 'link' => $link, 'title' => $title, 'order' => $order, 'id' => $linkId]);
return view('studio/button-editor', ['custom_icon' => $custom_icon, 'custom_css' => $custom_css, 'buttonId' => $buttonId, 'buttons' => $buttons, 'link' => $link, 'title' => $title, 'order' => $order, 'id' => $linkId]);
} }
//Save edit link //Save edit link
@ -308,20 +418,20 @@ class UserController extends Controller
'button' => 'required', 'button' => 'required',
]); ]);
if (stringStartsWith($request->link,'http://') == 'true' or stringStartsWith($request->link,'https://') == 'true' or stringStartsWith($request->link,'mailto:') == 'true') if (stringStartsWith($request->link, 'http://') == 'true' or stringStartsWith($request->link, 'https://') == 'true' or stringStartsWith($request->link, 'mailto:') == 'true')
$link1 = $request->link; $link1 = $request->link;
else else
$link1 = 'https://' . $request->link; $link1 = 'https://' . $request->link;
if (stringEndsWith($request->link,'/') == 'true') if (stringEndsWith($request->link, '/') == 'true')
$link = rtrim($link1, "/ "); $link = rtrim($link1, "/ ");
else else
$link = $link1; $link = $link1;
$title = $request->title; $title = $request->title;
$order = $request->order; $order = $request->order;
$button = $request->button; $button = $request->button;
$linkId = $request->id; $linkId = $request->id;
$buttonId = Button::select('id')->where('name' , $button)->value('id'); $buttonId = Button::select('id')->where('name', $button)->value('id');
Link::where('id', $linkId)->update(['link' => $link, 'title' => $title, 'order' => $order, 'button_id' => $buttonId]); Link::where('id', $linkId)->update(['link' => $link, 'title' => $title, 'order' => $order, 'button_id' => $buttonId]);
@ -335,13 +445,13 @@ class UserController extends Controller
$custom_icon = $request->custom_icon; $custom_icon = $request->custom_icon;
$custom_css = $request->custom_css; $custom_css = $request->custom_css;
if ($request->custom_css == "" and $request->custom_icon =! "") { if ($request->custom_css == "" and $request->custom_icon = !"") {
Link::where('id', $linkId)->update(['custom_icon' => $custom_icon]); Link::where('id', $linkId)->update(['custom_icon' => $custom_icon]);
} elseif ($request->custom_icon == "" and $request->custom_css =! "") { } elseif ($request->custom_icon == "" and $request->custom_css = !"") {
Link::where('id', $linkId)->update(['custom_css' => $custom_css]); Link::where('id', $linkId)->update(['custom_css' => $custom_css]);
} else { } else {
Link::where('id', $linkId)->update([]); Link::where('id', $linkId)->update([]);
} }
return Redirect('#result'); return Redirect('#result');
} }
@ -350,7 +460,7 @@ class UserController extends Controller
{ {
$userId = Auth::user()->id; $userId = Auth::user()->id;
$data['pages'] = User::where('id', $userId)->select('littlelink_name', 'littlelink_description')->get(); $data['pages'] = User::where('id', $userId)->select('littlelink_name', 'littlelink_description', 'image', 'name')->get();
return view('/studio/page', $data); return view('/studio/page', $data);
} }
@ -358,17 +468,23 @@ class UserController extends Controller
//Save littlelink page (name, description, logo) //Save littlelink page (name, description, logo)
public function editPage(request $request) public function editPage(request $request)
{ {
$request->validate([
'littlelink_name' => 'required|string|max:255|unique:users',
'name' => 'required|string|max:255|unique:users',
]);
$userId = Auth::user()->id; $userId = Auth::user()->id;
$littlelink_name = Auth::user()->littlelink_name; $littlelink_name = Auth::user()->littlelink_name;
$profilePhoto = $request->file('image'); $profilePhoto = $request->file('image');
$pageName = $request->pageName; $pageName = $request->pageName;
$pageDescription = $request->pageDescription; $pageDescription = $request->pageDescription;
$name = $request->Name;
User::where('id', $userId)->update(['littlelink_name' => $pageName, 'littlelink_description' => $pageDescription]);
if(!empty($profilePhoto)){ User::where('id', $userId)->update(['littlelink_name' => $pageName, 'littlelink_description' => $pageDescription, 'name' => $name]);
$profilePhoto->move(base_path('/img'), $littlelink_name . ".png");
if (!empty($profilePhoto)) {
$profilePhoto->move(base_path('/img'), $littlelink_name . ".png");
} }
return Redirect('/studio/page'); return Redirect('/studio/page');
@ -396,44 +512,46 @@ class UserController extends Controller
$zipfile = $request->file('zip'); $zipfile = $request->file('zip');
$theme = $request->theme; $theme = $request->theme;
$message = "";
User::where('id', $userId)->update(['theme' => $theme]); User::where('id', $userId)->update(['theme' => $theme]);
if(!empty($zipfile)){
$zipfile->move(base_path('/themes'), "temp.zip");
$zip = new ZipArchive; if (!empty($zipfile)) {
$zip->open(base_path() . '/themes/temp.zip');
$zip->extractTo(base_path() . '/themes');
$zip->close();
unlink(base_path() . '/themes/temp.zip');
// Removes version numbers from folder. $zipfile->move(base_path('/themes'), "temp.zip");
$folder = base_path('themes');
$regex = '/[0-9.-]/';
$files = scandir($folder);
foreach($files as $file) { $zip = new ZipArchive;
if($file !== '.' && $file !== '..') { $zip->open(base_path() . '/themes/temp.zip');
if(preg_match($regex, $file)) { $zip->extractTo(base_path() . '/themes');
$new_file = preg_replace($regex, '', $file); $zip->close();
File::copyDirectory($folder . '/' . $file, $folder . '/' . $new_file); unlink(base_path() . '/themes/temp.zip');
$dirname = $folder . '/' . $file;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { // Removes version numbers from folder.
system('rmdir '.escapeshellarg($dirname).' /s /q');
} else { $folder = base_path('themes');
system("rm -rf ".escapeshellarg($dirname)); $regex = '/[0-9.-]/';
$files = scandir($folder);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
if (preg_match($regex, $file)) {
$new_file = preg_replace($regex, '', $file);
File::copyDirectory($folder . '/' . $file, $folder . '/' . $new_file);
$dirname = $folder . '/' . $file;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
system('rmdir ' . escapeshellarg($dirname) . ' /s /q');
} else {
system("rm -rf " . escapeshellarg($dirname));
}
}
}
} }
}
}
} }
}
return Redirect('/studio/theme')->with("success", $message);
return Redirect('/studio/theme');
} }
//Show user (name, email, password) //Show user (name, email, password)
@ -461,12 +579,12 @@ class UserController extends Controller
$email = $request->email; $email = $request->email;
$password = Hash::make($request->password); $password = Hash::make($request->password);
if($request->name != '' ) { if ($request->name != '') {
User::where('id', $userId)->update(['name' => $name]); User::where('id', $userId)->update(['name' => $name]);
} elseif($request->email != '' ) { } elseif ($request->email != '') {
User::where('id', $userId)->update(['email' => $email]); User::where('id', $userId)->update(['email' => $email]);
} elseif($request->password != '' ) { } elseif ($request->password != '') {
User::where('id', $userId)->update(['password' => $password]); User::where('id', $userId)->update(['password' => $password]);
} }
return back(); return back();
} }
@ -480,13 +598,32 @@ if($request->name != '' ) {
if (empty($id)) { if (empty($id)) {
return abort(404); return abort(404);
} }
$userinfo = User::select('name', 'littlelink_name', 'littlelink_description', 'theme')->where('id', $id)->first(); $userinfo = User::select('name', 'littlelink_name', 'littlelink_description', 'theme')->where('id', $id)->first();
$information = User::select('name', 'littlelink_name', 'littlelink_description', 'theme')->where('id', $id)->get(); $information = User::select('name', 'littlelink_name', 'littlelink_description', 'theme')->where('id', $id)->get();
$links = DB::table('links')->join('buttons', 'buttons.id', '=', 'links.button_id')->select('links.link', 'links.id', 'links.button_id', 'links.title', 'links.custom_css', 'links.custom_icon', 'buttons.name')->where('user_id', $id)->orderBy('up_link', 'asc')->orderBy('order', 'asc')->get(); $links = DB::table('links')->join('buttons', 'buttons.id', '=', 'links.button_id')->select('links.link', 'links.id', 'links.button_id', 'links.title', 'links.custom_css', 'links.custom_icon', 'buttons.name')->where('user_id', $id)->orderBy('up_link', 'asc')->orderBy('order', 'asc')->get();
return view('components/theme', ['userinfo' => $userinfo, 'information' => $information, 'links' => $links, 'littlelink_name' => $littlelink_name]); return view('components/theme', ['userinfo' => $userinfo, 'information' => $information, 'links' => $links, 'littlelink_name' => $littlelink_name]);
} }
//Delete existing user
public function deleteUser(request $request)
{
// echo $request->id;
// echo "<br>";
// echo Auth::id();
$id = $request->id;
if($id == Auth::id() and $id != "1") {
$user = User::find($id);
Schema::disableForeignKeyConstraints();
$user->forceDelete();
Schema::enableForeignKeyConstraints();
}
return redirect('/');
}
} }

View File

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use Auth;
use Illuminate\Foundation\Http\FormRequest;
class LinkTypeRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return Auth::user()->role == 'admin';
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required',
'typename' => 'required',
'icon' => 'required',
'params' => 'json|nullable'
];
}
}

View File

@ -5,7 +5,11 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class Link extends Model class Link extends Model
{ {
use HasFactory; use HasFactory;
protected $fillable = ['link', 'title', 'button_id', 'type_params', 'typename'];
} }

13
app/Models/LinkType.php Normal file
View File

@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class LinkType extends Model
{
use HasFactory;
protected $fillable = ['typename', 'title', 'description', 'icon', 'params'];
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SocialAccount extends Model
{
use HasFactory;
protected $fillable = [
'user_id', 'provider_name', 'provider_id'
];
// User
public function user()
{
return $this->belongsTo(User::class);
}
}

View File

@ -19,7 +19,13 @@ class User extends Authenticatable implements MustVerifyEmail
protected $fillable = [ protected $fillable = [
'name', 'name',
'email', 'email',
'image',
'password', 'password',
'provider',
'provider_id',
'email_verified_at',
'littlelink_name',
]; ];
/** /**
@ -40,4 +46,13 @@ class User extends Authenticatable implements MustVerifyEmail
protected $casts = [ protected $casts = [
'email_verified_at' => 'datetime', 'email_verified_at' => 'datetime',
]; ];
public function visits()
{
return visits($this)->relation();
}
public function socialAccounts()
{
return $this->hasMany(socialAccount::class);
}
} }

View File

@ -0,0 +1,40 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Modal extends Component
{
public $title = '';
public $id = '';
/**
* Create a new component instance.
*
* @return void
*/
public function __construct($title, $id = '')
{
$this->title = $title;
$this->id = $id;
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
return view('components.modal');
}
public function getModalIdString(): string
{
if ($this->id != '') {
return $this->id;
}
return "model" . rand(1111, 9999);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Cohensive\OEmbed\Facades\OEmbed;
class PageItemDisplay extends Component
{
public $link;
public $id = '';
/**
* Create a new component instance.
*
* @return void
*/
public function __construct($link)
{
// $this->title = $title;
$this->link = $link;
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
$params = json_decode($this->link->type_params);
return view('components.pageitems.'.$this->link->typename.'-display', ['link' => $this->link, 'params' => $params]);
}
public function getModalIdString(): string
{
if ($this->id != '') {
return $this->id;
}
return "model" . rand(1111, 9999);
}
}

View File

@ -7,21 +7,26 @@
"require": { "require": {
"php": "^7.3|^8.0", "php": "^7.3|^8.0",
"codedge/laravel-selfupdater": "^3.4", "codedge/laravel-selfupdater": "^3.4",
"awssat/laravel-visits": "^4.0",
"doctrine/dbal": "^3.0",
"fideloper/proxy": "^4.4", "fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^3.0", "fruitcake/laravel-cors": "^3.0",
"geo-sot/laravel-env-editor": "^1.1", "geo-sot/laravel-env-editor": "^2.0",
"guzzlehttp/guzzle": "^7.4", "guzzlehttp/guzzle": "^7.4",
"laravel/framework": "^8.12", "laravel/framework": "^9.0",
"laravel/socialite": "^5.5",
"laravel/tinker": "^2.5", "laravel/tinker": "^2.5",
"spatie/laravel-backup": "^6.16" "spatie/laravel-backup": "^8.13",
"cohensive/oembed": "^0.16"
}, },
"require-dev": { "require-dev": {
"facade/ignition": "^2.5", "barryvdh/laravel-ide-helper": "^2.12",
"spatie/laravel-ignition": "^1.0",
"fakerphp/faker": "^1.9.1", "fakerphp/faker": "^1.9.1",
"laravel/breeze": "^1.1", "laravel/breeze": "^1.1",
"laravel/sail": "^1.0.1", "laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.2", "mockery/mockery": "^1.4.2",
"nunomaduro/collision": "^5.0", "nunomaduro/collision": "^6.1",
"phpunit/phpunit": "^9.3.3" "phpunit/phpunit": "^9.3.3"
}, },
"autoload": { "autoload": {

3651
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ return [
| |
*/ */
'name' => env('APP_NAME', 'Admin Littlelink'), 'name' => env('APP_NAME', 'LittleLink Custom'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -161,11 +161,15 @@ return [
Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class, Illuminate\View\ViewServiceProvider::class,
Laravel\Socialite\SocialiteServiceProvider::class,
/* /*
* Package Service Providers... * Package Service Providers...
*/ */
/* /*
* Application Service Providers... * Application Service Providers...
*/ */
@ -226,6 +230,8 @@ return [
'URL' => Illuminate\Support\Facades\URL::class, 'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class, 'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class, 'View' => Illuminate\Support\Facades\View::class,
'Socialite'=> Laravel\Socialite\Facades\Socialite::class,
'OEmbed' => Cohensive\OEmbed\Facades\OEmbed::class
], ],

View File

@ -29,5 +29,20 @@ return [
'secret' => env('AWS_SECRET_ACCESS_KEY'), 'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
], ],
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('FACEBOOK_CALLBACK_URL'),
],
'twitter' => [
'client_id' => env('TWITTER_CLIENT_ID'),
'client_secret' => env('TWITTER_CLIENT_SECRET'),
'redirect' => env('TWITTER_CALLBACK_URL'),
],
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_CALLBACK_URL'),
],
]; ];

78
config/visits.php Normal file
View File

@ -0,0 +1,78 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Database Engine & Connection Name
|--------------------------------------------------------------------------
|
| Supported Engines: "redis", "eloquent"
| Connection Name: see config/database.php
|
*/
'engine' => \Awssat\Visits\DataEngines\EloquentEngine::class,
'connection' => 'laravel-visits',
/*
|--------------------------------------------------------------------------
| Counters periods
|--------------------------------------------------------------------------
|
| Record visits (total) of each one of these periods in this set (can be empty)
|
*/
'periods' => [
'day',
'week',
'month',
'year',
],
/*
|--------------------------------------------------------------------------
| Redis prefix
|--------------------------------------------------------------------------
*/
'keys_prefix' => 'visits',
/*
|--------------------------------------------------------------------------
| Remember ip for x seconds of time
|--------------------------------------------------------------------------
|
| Will count only one visit of an IP during this specified time.
|
*/
'remember_ip' => 15 * 60,
/*
|--------------------------------------------------------------------------
| Always return uncached fresh top/low lists
|--------------------------------------------------------------------------
*/
'always_fresh' => false,
/*
|--------------------------------------------------------------------------
| Ignore Crawlers
|--------------------------------------------------------------------------
|
| Ignore counting crawlers visits or not
|
*/
'ignore_crawlers' => true,
/*
|--------------------------------------------------------------------------
| Global Ignore Recording
|--------------------------------------------------------------------------
|
| stop recording specific items (can be any of these: 'country', 'refer', 'periods', 'operatingSystem', 'language')
|
*/
'global_ignore' => ['country'],
];

2
css/app-dark.css vendored
View File

@ -1,4 +1,4 @@
* { * {
background-color: #292929 !important; background-color: #292929 !important;
color: #FFFFFF !important; color: #FFFFFF !important;
} }

View File

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\User;
class Sociallogin extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('provider')->nullable();
$table->string('provider_id')->nullable();
$table->string('image')->nullable();
$table->string('password')->nullable()->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
User::whereNull('password')->delete();
}
}

View File

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class SocialAccounts extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('social_accounts', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->string('provider_name')->nullable();
$table->string('provider_id')->unique()->nullable();
$table->timestamps();
});
Schema::table('users', function (Blueprint $table) {
//$table->dropColumn('provider');
//$table->dropColumn('provider_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
Schema::dropIfExists('social_accounts');
}
}

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateVisitsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('visits', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('primary_key');
$table->string('secondary_key')->nullable();
$table->unsignedBigInteger('score');
$table->json('list')->nullable();
$table->timestamp('expired_at')->nullable();
$table->timestamps();
$table->unique(['primary_key', 'secondary_key']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('visits');
}
}

View File

@ -0,0 +1,186 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class LinkType extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
var $TableName = 'link_types';
public function up()
{
Schema::create($this->TableName, function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('typename', 100);
$table->string('title')->nullable();
$table->string('description')->nullable();
$table->string('icon');
$table->string('params', 65535)->nullable();
$table->boolean('active')->default(true);
});
$this->SeedLinkTypes();
Schema::table('links', function (Blueprint $table) {
$table->string('link')->nullable()->change();
$table->string('title')->nullable()->change();
$table->unsignedBigInteger('button_id')->nullable()->constrained()->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('link_types');
Schema::table('links', function (Blueprint $table) {
$table->string('link')->nullable(false)->change();
$table->unsignedBigInteger('button_id')->nullable(false)->change();
$table->string('title')->nullable(false)->change();
});
}
public function SeedLinkTypes() {
DB::table($this->TableName)->updateOrInsert([
'typename' => 'link',
'title' => 'Website Link',
'icon' => 'bi bi-link',
'description' => 'Create a Custom Button that goes to any website. ',
'params' => '[{
"tag": "input",
"id": "link_title",
"for": "link_title",
"label": "Link Title *",
"type": "text",
"name": "link_title",
"class": "form-control",
"tip": "Enter a title for this link",
"required": "required"
},
{
"tag": "input",
"id": "link_url",
"for": "link_url",
"label": "Link Address *",
"type": "text",
"name": "link_url",
"class": "form-control",
"tip": "Enter the website address",
"required": "required"
}
]'
]);
DB::table($this->TableName)->updateOrInsert([
'typename' => 'predefined',
'title' => 'Predefined Site',
'icon' => 'bi bi-boxes',
'description' => 'Select from a list of predefined websites and have your link automatically styled using that sites brand colors and icon.'
]);
DB::table($this->TableName)->updateOrInsert([
'typename' => 'heading',
'title' => 'Heading',
'icon' => 'bi bi-card-heading',
'description' => 'Use headings to organize your links and separate them into groups',
'params' => '[{
"tag": "input",
"id": "heading-text",
"for": "link_title",
"label": "Heading Text",
"type": "text",
"name": "link_title",
"class": "form-control"
}]'
]);
DB::table($this->TableName)->updateOrInsert([
'typename' => 'spacer',
'title' => 'Spacer',
'icon' => 'bi bi-distribute-vertical',
'description' => 'Add blank space to your list of links. You can choose how big. ',
'params' => '[
{
"tag": "input",
"type": "number",
"min": "1","max":"10",
"name": "spacer_size",
"id": "spacer_size",
"value": 3
}
]'
]);
// DB::table($this->TableName)->updateOrInsert([
// 'typename' => 'video',
// 'title' => 'Video',
// 'icon' => 'bi bi-play-btn',
// 'description' => 'Embed or link to a video on another website, such as TikTok, YouTube etc.',
// 'params' => '[
// {
// "tag": "input",
// "id": "link_url",
// "for": "link_url",
// "label": "URL of video",
// "type": "text",
// "name": "link_url",
// "class": "form-control",
// "tip": "Enter the website address",
// "required": "required"
// }
// {
// "tag": "select",
// "name": "video-option",
// "id": "video-option",
// "value": [
// {
// "tag": "option",
// "label": "Link to video ",
// "value": "link"
// },
// {
// "tag": "option",
// "label": "Embed Video",
// "value": "embed"
// },
// ]
// }
// ]'
// ]);
// DB::table($this->TableName)->updateOrInsert([
// 'typename' => 'text',
// 'title' => 'Text',
// 'icon' => 'bi bi-fonts',
// 'description' => 'Add static text to your page that is not clickable.',
// 'params' => '[{
// "tag": "textarea",
// "id": "static-text",
// "for": "static_text",
// "label": "Text",
// "name": "static_text",
// "class": "form-control"
// }
// ]'
// ]);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
<svg width="74" height="79" viewBox="0 0 74 79" fill="white" xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M73.7014 17.9592C72.5616 9.62034 65.1774 3.04876 56.424 1.77536C54.9472 1.56019 49.3517 0.7771 36.3901 0.7771H36.2933C23.3281 0.7771 20.5465 1.56019 19.0697 1.77536C10.56 3.01348 2.78877 8.91838 0.903306 17.356C-0.00357857 21.5113 -0.100361 26.1181 0.068112 30.3439C0.308275 36.404 0.354874 42.4535 0.91406 48.489C1.30064 52.498 1.97502 56.4751 2.93215 60.3905C4.72441 67.6217 11.9795 73.6395 19.0876 76.0945C26.6979 78.6548 34.8821 79.0799 42.724 77.3221C43.5866 77.1245 44.4398 76.8953 45.2833 76.6342C47.1867 76.0381 49.4199 75.3714 51.0616 74.2003C51.0841 74.1839 51.1026 74.1627 51.1156 74.1382C51.1286 74.1138 51.1359 74.0868 51.1368 74.0592V68.2108C51.1364 68.185 51.1302 68.1596 51.1185 68.1365C51.1069 68.1134 51.0902 68.0932 51.0695 68.0773C51.0489 68.0614 51.0249 68.0503 50.9994 68.0447C50.9738 68.0391 50.9473 68.0392 50.9218 68.045C45.8976 69.226 40.7491 69.818 35.5836 69.8087C26.694 69.8087 24.3031 65.6569 23.6184 63.9285C23.0681 62.4347 22.7186 60.8764 22.5789 59.2934C22.5775 59.2669 22.5825 59.2403 22.5934 59.216C22.6043 59.1916 22.621 59.1702 22.6419 59.1533C22.6629 59.1365 22.6876 59.1248 22.714 59.1191C22.7404 59.1134 22.7678 59.1139 22.794 59.1206C27.7345 60.2936 32.799 60.8856 37.8813 60.8843C39.1036 60.8843 40.3223 60.8843 41.5447 60.8526C46.6562 60.7115 52.0437 60.454 57.0728 59.4874C57.1983 59.4628 57.3237 59.4416 57.4313 59.4098C65.3638 57.9107 72.9128 53.2051 73.6799 41.2895C73.7086 40.8204 73.7803 36.3758 73.7803 35.889C73.7839 34.2347 74.3216 24.1533 73.7014 17.9592ZM61.4925 47.6918H53.1514V27.5855C53.1514 23.3526 51.3591 21.1938 47.7136 21.1938C43.7061 21.1938 41.6988 23.7476 41.6988 28.7919V39.7974H33.4078V28.7919C33.4078 23.7476 31.3969 21.1938 27.3894 21.1938C23.7654 21.1938 21.9552 23.3526 21.9516 27.5855V47.6918H13.6176V26.9752C13.6176 22.7423 14.7157 19.3795 16.9118 16.8868C19.1772 14.4 22.1488 13.1231 25.8373 13.1231C30.1064 13.1231 33.3325 14.7386 35.4832 17.9662L37.5587 21.3949L39.6377 17.9662C41.7884 14.7386 45.0145 13.1231 49.2765 13.1231C52.9614 13.1231 55.9329 14.4 58.2055 16.8868C60.4017 19.3772 61.4997 22.74 61.4997 26.9752L61.4925 47.6918Z" fill="inherit"/> <path d="M22.53 14.35c-.32 1.66-2.9 3.48-5.85 3.84-1.54.18-3.06.35-4.68.28-2.65-.12-4.73-.63-4.73-.63 0 .26.02.5.05.73.34 2.61 2.59 2.77 4.72 2.84 2.15.07 4.06-.53 4.06-.53l.09 1.94s-1.5.81-4.18.95c-1.47.08-3.31-.04-5.44-.6-4.63-1.22-5.42-6.16-5.54-11.16-.04-1.49-.01-2.89-.01-4.06 0-5.12 3.35-6.62 3.35-6.62C6.06.55 8.96.23 11.98.2h.07c3.02.02 5.92.35 7.61 1.13 0 0 3.35 1.5 3.35 6.62-.01 0 .03 3.78-.48 6.4" fill="#3088d4"/>
<path d="M19.05 8.35v6.2H16.6V8.53c0-1.27-.53-1.91-1.6-1.91-1.18 0-1.77.76-1.77 2.27v3.29h-2.44V8.89c0-1.51-.59-2.27-1.77-2.27-1.07 0-1.6.64-1.6 1.91v6.01H4.95v-6.2c0-1.27.32-2.27.97-3.02.67-.74 1.54-1.13 2.63-1.13 1.26 0 2.21.48 2.84 1.45L12 6.68l.61-1.02c.63-.97 1.58-1.45 2.84-1.45 1.09 0 1.96.38 2.63 1.13.64.74.97 1.74.97 3.01" fill="#fff"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 846 B

View File

@ -62,7 +62,7 @@ var copyToClipboard = (function () {
copyToClipboard.handleCopyIconClick(); copyToClipboard.handleCopyIconClick();
document.querySelector('#share-share-button').addEventListener('click', () => { $('#share-share-button').on('click', () => {
if (navigator.share) { if (navigator.share) {
navigator.share({ navigator.share({
title: '', title: '',

2
resources/js/app.js vendored
View File

@ -1,3 +1,3 @@
require('./bootstrap'); require('./bootstrap');
require('alpinejs'); // require('alpinejs');

View File

@ -0,0 +1,90 @@
@extends('layouts.sidebar')
@section('content')
<div class='card'>
<div class='card-header'>
<h4 class='card-title'>Create New Link Type</h1>
</div>
<div class='card-body'>
<!-- if there are creation errors, they will show here -->
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action='/admin/linktype' method="post">
@csrf
<div class="form-group">
<label for="icon">Icon</label>
<input type='text' value="{{@old('icon')}}" name='icon' class="form-control @error('icon') is-invalid @enderror">
<a href="https://icons.getbootstrap.com/" target="_blank">View Icons Here</a>
@error('icon')
<span class="text-danger d-blcok">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="typename">Short Name</label>
<input type='text' value="{{@old('typename')}}" name='typename' class="form-control @error('typename') is-invalid @enderror">
@error('typename')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="title">Title</label>
<input type='text' name='title' value="{{@old('title')}}" class='form-control @error("title") is-invalid @enderror'>
@error('title')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea name='description' class="form-control @error('description') is-invalid @enderror" rows='3'>{{@old('description')}}</textarea>
@error('description')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="params">Parms</label>
<textarea name='params' class="form-control @error('params') is-invalid @enderror" rows='20'>{{@old('params')}}</textarea>
@error('params')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div>
<button type="submit" class="mt-3 ml-3 btn btn-info">Save</button>
</div>
</form>
</div>
</div>
@endsection

View File

@ -0,0 +1,95 @@
@extends('layouts.sidebar')
@section('content')
<div class='card'>
<div class='card-header'>
<h4 class='card-title'>Edit Link Type</h1>
</div>
<div class='card-body'>
<!-- if there are creation errors, they will show here -->
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action='/admin/linktype/{{$linktype->id}}' method="POST">
@method('PUT')
@csrf
<div class="form-group">
<label for="icon">Icon</label>
<input type='text' value="{{$linktype->icon}}" name='icon' class="form-control @error('icon') is-invalid @enderror">
<a href="https://icons.getbootstrap.com/" target="_blank">View Icons Here</a>
@error('icon')
<span class="text-danger d-blcok">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="typename">Short Name</label>
<input type='text' value="{{$linktype->typename}}" name='typename' readonly class="form-control @error('typename') is-invalid @enderror">
@error('typename')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="title">Title</label>
<input type='text' name='title' value="{{$linktype->title}}" class='form-control @error("title") is-invalid @enderror'>
@error('title')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea name='description' class="form-control @error('description') is-invalid @enderror" rows='3'>{{$linktype->description}}</textarea>
@error('description')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="form-group">
<label for="params">Parms</label>
<textarea name='params' class="form-control @error('params') is-invalid @enderror" rows='20'>{{$linktype->params}}</textarea>
@error('params')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div>
<button type="submit" class="mt-3 ml-3 btn btn-info">Save</button>
</div>
</form>
</div>
</div>
@endsection

View File

@ -0,0 +1,43 @@
@extends('layouts.sidebar')
@section('content')
<h1>Link Types</h1>
<div>
<a href="{{ url('admin/linktype/create') }}">Add New</a>
</div>
<!-- will be used to show any messages -->
@if (Session::has('message'))
<div class="alert alert-info">{{ Session::get('message') }}</div>
@endif
<div class=''>
@foreach($linktype as $key => $value)
<div class='row mb-2 border shdadow'>
<div class='col-12 h5'><i class='{{ $value->icon }}'></i> {{ $value->title }} <span class='small text-muted'>[{{ $value->typename }}]</span></div>
<div class='col-12'>{{ $value->description }}</div>
<div class='col-12 card-footer bg-light border border-top border-light'>
<form action='/admin/linktype/{{$value->id}}' method="POST">
<a class="btn btn-link btn-sm" href="{{ URL::to('admin/linktype/' . $value->id . '/edit') }}"><i class='bi bi-pencil'></i> Edit</a>
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-link p-2 text-danger" href="{{ URL::to('admin/linktype/' . $value->id . '/edit') }}" onclick="return confirm('Are you sure?')"><i class='bi bi-trash'></i> Delete</a>
</form>
</div>
</div>
@endforeach
</div>
@endsection

View File

@ -1,17 +1,65 @@
<x-guest-layout> <x-guest-layout>
<x-auth-card> <x-auth-card>
<x-slot name="logo"> <x-slot name="logo">
<a href="{{ url('') }}"> <a href="{{ url('') }}">
<x-application-logo class="w-20 h-20 fill-current text-gray-500" /> <x-application-logo class="w-20 h-20 fill-current text-gray-500" />
</a> </a>
</x-slot> </x-slot>
<!-- Session Status --> <!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" /> <x-auth-session-status class="mb-4" :status="session('status')" />
<!-- Validation Errors --> <!-- Validation Errors -->
<x-auth-validation-errors class="mb-4" :errors="$errors" /> <x-auth-validation-errors class="mb-4" :errors="$errors" />
@if(env('ENABLE_SOCIAL_LOGIN') === 'true')
<p class="mb-4 text-center">Login with</p>
<div class="flex flex-nowrap justify-center mb-3">
@if(!empty(env('FACEBOOK_CLIENT_ID')))
<a href="{{ route('social.redirect','facebook') }}" class="text-white bg-blue-600 bg-blue-600/90 focus:ring-4 focus:outline-none focus:ring-[#3b5998]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#3b5998]/55 mr-2 mb-2">
<svg class="mr-2 -ml-1 w-4 h-4" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="facebook-f" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path fill="currentColor" d="M279.1 288l14.22-92.66h-88.91v-60.13c0-25.35 12.42-50.06 52.24-50.06h40.42V6.26S260.4 0 225.4 0c-73.22 0-121.1 44.38-121.1 124.7v70.62H22.89V288h81.39v224h100.2V288z"></path>
</svg>
Facebook
</a>
@endif
@if(!empty(env('TWITTER_CLIENT_ID')))
<a href="{{ route('social.redirect','twitter') }}" class="text-white bg-blue-400 bg-blue-400/90 focus:ring-4 focus:outline-none focus:ring-[#1da1f2]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#1da1f2]/55 mr-2 mb-2">
<svg class="mr-2 -ml-1 w-4 h-4" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M459.4 151.7c.325 4.548 .325 9.097 .325 13.65 0 138.7-105.6 298.6-298.6 298.6-59.45 0-114.7-17.22-161.1-47.11 8.447 .974 16.57 1.299 25.34 1.299 49.06 0 94.21-16.57 130.3-44.83-46.13-.975-84.79-31.19-98.11-72.77 6.498 .974 12.99 1.624 19.82 1.624 9.421 0 18.84-1.3 27.61-3.573-48.08-9.747-84.14-51.98-84.14-102.1v-1.299c13.97 7.797 30.21 12.67 47.43 13.32-28.26-18.84-46.78-51.01-46.78-87.39 0-19.49 5.197-37.36 14.29-52.95 51.65 63.67 129.3 105.3 216.4 109.8-1.624-7.797-2.599-15.92-2.599-24.04 0-57.83 46.78-104.9 104.9-104.9 30.21 0 57.5 12.67 76.67 33.14 23.72-4.548 46.46-13.32 66.6-25.34-7.798 24.37-24.37 44.83-46.13 57.83 21.12-2.273 41.58-8.122 60.43-16.24-14.29 20.79-32.16 39.31-52.63 54.25z"></path>
</svg>
Twitter
</a>
@endif
@if(!empty(env('GOOGLE_CLIENT_ID')))
<a href="{{ route('social.redirect','google') }}" class="text-white bg-blue-500 hover:bg-blue-500/90 focus:ring-4 focus:outline-none focus:ring-[#4285F4]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#4285F4]/55 mr-2 mb-2">
<svg class="mr-2 -ml-1 w-4 h-4" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="google" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 488 512">
<path fill="currentColor" d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"></path>
</svg>
Google
</a>
@endif
</div>
<hr class="block w-full mb-4 border-0 border-t border-gray-300" />
<p class="mb-4 text-center">OR</p>
@endif
<form method="POST" action="{{ route('login') }}"> <form method="POST" action="{{ route('login') }}">
@csrf @csrf
@ -26,10 +74,7 @@
<div class="mt-4"> <div class="mt-4">
<x-label for="password" :value="__('Password')" /> <x-label for="password" :value="__('Password')" />
<x-input id="password" class="block mt-1 w-full" <x-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="current-password" />
type="password"
name="password"
required autocomplete="current-password" />
</div> </div>
<!-- Remember Me --> <!-- Remember Me -->
@ -42,15 +87,22 @@
<div class="flex items-center justify-end mt-4"> <div class="flex items-center justify-end mt-4">
@if (Route::has('password.request')) @if (Route::has('password.request'))
<a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('password.request') }}"> <a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('password.request') }}">
{{ __('Forgot your password?') }} {{ __('Forgot your password?') }}
</a> </a>
@endif @endif
<x-button class="ml-3"> <x-button class="ml-3">
{{ __('Log in') }} {{ __('Log in') }}
</x-button> </x-button>
</div> </div>
</div>
</form> </form>
</x-auth-card> </x-auth-card>
</x-guest-layout> </x-guest-layout>

View File

@ -17,6 +17,44 @@ foreach($pages as $page)
<!-- Validation Errors --> <!-- Validation Errors -->
<x-auth-validation-errors class="mb-4" :errors="$errors" /> <x-auth-validation-errors class="mb-4" :errors="$errors" />
<p class="mb-4 text-center">Login with</p>
<div class="flex flex-nowrap justify-center mb-3">
@if(!empty(env('FACEBOOK_CLIENT_ID')))
<a href="{{ route('social.redirect','facebook') }}" class="text-white bg-blue-600 bg-blue-600/90 focus:ring-4 focus:outline-none focus:ring-[#3b5998]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#3b5998]/55 mr-2 mb-2">
<svg class="mr-2 -ml-1 w-4 h-4" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="facebook-f" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path fill="currentColor" d="M279.1 288l14.22-92.66h-88.91v-60.13c0-25.35 12.42-50.06 52.24-50.06h40.42V6.26S260.4 0 225.4 0c-73.22 0-121.1 44.38-121.1 124.7v70.62H22.89V288h81.39v224h100.2V288z"></path>
</svg>
Facebook
</a>
@endif
@if(!empty(env('TWITTER_CLIENT_ID')))
<a href="{{ route('social.redirect','twitter') }}" class="text-white bg-blue-400 bg-blue-400/90 focus:ring-4 focus:outline-none focus:ring-[#1da1f2]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#1da1f2]/55 mr-2 mb-2">
<svg class="mr-2 -ml-1 w-4 h-4" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M459.4 151.7c.325 4.548 .325 9.097 .325 13.65 0 138.7-105.6 298.6-298.6 298.6-59.45 0-114.7-17.22-161.1-47.11 8.447 .974 16.57 1.299 25.34 1.299 49.06 0 94.21-16.57 130.3-44.83-46.13-.975-84.79-31.19-98.11-72.77 6.498 .974 12.99 1.624 19.82 1.624 9.421 0 18.84-1.3 27.61-3.573-48.08-9.747-84.14-51.98-84.14-102.1v-1.299c13.97 7.797 30.21 12.67 47.43 13.32-28.26-18.84-46.78-51.01-46.78-87.39 0-19.49 5.197-37.36 14.29-52.95 51.65 63.67 129.3 105.3 216.4 109.8-1.624-7.797-2.599-15.92-2.599-24.04 0-57.83 46.78-104.9 104.9-104.9 30.21 0 57.5 12.67 76.67 33.14 23.72-4.548 46.46-13.32 66.6-25.34-7.798 24.37-24.37 44.83-46.13 57.83 21.12-2.273 41.58-8.122 60.43-16.24-14.29 20.79-32.16 39.31-52.63 54.25z"></path>
</svg>
Twitter
</a>
@endif
@if(!empty(env('GOOGLE_CLIENT_ID')))
<a href="{{ route('social.redirect','google') }}" class="text-white bg-blue-500 hover:bg-blue-500/90 focus:ring-4 focus:outline-none focus:ring-[#4285F4]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#4285F4]/55 mr-2 mb-2">
<svg class="mr-2 -ml-1 w-4 h-4" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="google" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 488 512">
<path fill="currentColor" d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"></path>
</svg>
Google
</a>
@endif
</div>
<form method="POST" action="{{ route('register') }}"> <form method="POST" action="{{ route('register') }}">
@csrf @csrf

View File

@ -0,0 +1,4 @@
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>{{ $slot }}</strong>
</div>

View File

@ -1,5 +1,5 @@
@if(file_exists(base_path("littlelink/images/avatar.png" ))) @if(file_exists(base_path("/littlelink/images/avatar.png" )))
<img class="mb-5" src="{{ asset('littlelink/images/avatar.png') }}" srcset="{{ asset('littlelink/images/avatar@2x.png 2x') }}" style="width: 150px;"> <img class="mb-5" src="{{ asset('/littlelink/images/avatar.png') }}" srcset="{{ asset('/littlelink/images/avatar@2x.png 2x') }}" style="width: 150px;">
@else @else
<img class="mb-5" src="{{ asset('littlelink/images/avatar@2x.png') }}"> <img class="mb-5" src="{{ asset('/littlelink/images/avatar@2x.png') }}">
@endif @endif

View File

@ -19,6 +19,9 @@
if(EnvEditor::keyExists('ENABLE_THEME_UPDATER')){ /* Do nothing if key already exists */ if(EnvEditor::keyExists('ENABLE_THEME_UPDATER')){ /* Do nothing if key already exists */
} else {EnvEditor::addKey('ENABLE_THEME_UPDATER', 'true');} } else {EnvEditor::addKey('ENABLE_THEME_UPDATER', 'true');}
if(EnvEditor::keyExists('ENABLE_SOCIAL_LOGIN')){ /* Do nothing if key already exists */
} else {EnvEditor::addKey('ENABLE_SOCIAL_LOGIN', 'false');}
if (!config()->has('advanced-config.expand_panel_admin_menu_permanently') and !config()->has('disable_default_password_notice')) { if (!config()->has('advanced-config.expand_panel_admin_menu_permanently') and !config()->has('disable_default_password_notice')) {
function getStringBetween($string, $start, $end) { function getStringBetween($string, $start, $end) {

View File

@ -0,0 +1,42 @@
@if ($message = Session::get('success'))
<div class="alert alert-success alert-block">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('error'))
<div class="alert alert-danger alert-block">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('warning'))
<div class="alert alert-warning alert-block">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('info'))
<div class="alert alert-info alert-block">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>{{ $message }}</strong>
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

View File

@ -0,0 +1,25 @@
<div class="modal" tabindex="-1" role="dialog" id="{{ $getModalIdString() }}">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ $title }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{{ $slot }}
</div>
<div class="modal-footer">
{{$buttons}}
{{-- <button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> --}}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,5 @@
<div class='button-heading'>
<h2>{{$link->title}}</h2>
</div>

View File

@ -0,0 +1,4 @@
<label for='title' class='form-label'>Heading Text:</label>
<input type='text' name='title' value='{{$link_title}}' class='form-control' />

View File

@ -0,0 +1,13 @@
@php
$BaseURL = str_replace(array('http://', 'https://'), '', $link->link)
@endphp
<a class="button button-custom button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') !="true" )target="_blank" @endif>
@if($params->GetSiteIcon ?? true)
<img alt="button-icon" class="icon hvr-icon" src="https://icons.duckduckgo.com/ip3/{{$BaseURL}}.ico">
@endif
{{ $link->title }}
</a>

View File

@ -0,0 +1,15 @@
<label for='title' class='form-label'>Title</label>
<input type='text' name='title' value='{{$link_title}}' class='form-control' required />
<label for='title' class='form-label'>URL</label>
<input type='url' name='link' value='{{$link_url}}' class='form-control' />
<div class="custom-control custom-checkbox m-2">
<input type="checkbox" class="custom-control-input" value='1' {{((isset($params->GetSiteIcon) ? boolval($params->GetSiteIcon) : false) ? 'checked': '') }} name='GetSiteIcon' id="GetSiteIcon">
<label class="custom-control-label" for="GetSiteIcon">Show website icon on button</label>
</div>

View File

@ -0,0 +1,6 @@
<a class="button button-{{ $params->button }} button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') !="true" )target="_blank" @endif>
<img alt="button-icon" class="icon hvr-icon" src="@if(theme('use_custom_icons') == " true"){{ url('themes/' . $GLOBALS['themeName'] . '/extra/custom-icons')}}/{{$params->button}}{{theme('custom_icon_extension')}} @else{{ asset('\/littlelink/icons\/') . $params->button }}.svg @endif">
{{ ucfirst($link->title) }}
</a>

View File

@ -0,0 +1,18 @@
<label for='button' class='form-label'>Select a predefined site</label>
<select name='button' class='form-control'>
@foreach ($buttons as $b)
<option class='button button-{{$b["name"]}}' value='{{$b["name"]}}' {{ $b["selected"] == true ? "selected" : ""}}>{{$b["title"]}}</option>
@endforeach
</select>
<label for='title' class='form-label'>Custom Title</label>
<input type='text' name='title' value='{{$link_title}}' class='form-control' />
<span class='small text-muted'>Leave blank for default title</span>
<label for='link' class='form-label'>URL</label>
<input type='url' name='link' value='{{$link_url}}' class='form-control' required />
<span class='small text-muted'>Enter the URL for to your profile page</span>

View File

@ -0,0 +1,7 @@
<div class='button-spacer' style='height: {{$params->height *5}}px'> </div>

View File

@ -0,0 +1,8 @@
<label for='title' class='form-label'>Spacing height</label>
{{-- <input type='number' name='height' value="{{$params->height ?? ''}}" class='form-control w-25' /> --}}
<input type="range" class="custom-range" id="height" name='height' value={{$params->height??5}} oninput="this.nextElementSibling.value = this.value"><output class='font-weight-bold'>{{$params->height??5}}</output>

View File

@ -0,0 +1,3 @@
<div class='button-text'> {{$params->text}}</div>

View File

@ -0,0 +1,3 @@
<label for='text' class='form-label'>Text to display</label>
<textarea name='text' class='form-control'>{{$params->text ?? ''}}
</textarea>

View File

@ -0,0 +1,34 @@
<div class='button-video'>
<?php
$embed = OEmbed::get($link->link);
if ($embed) {
echo $embed->html();
// Print default embed html code.
//--echo $embed->html();
// Print embed html code with custom width. Works for IFRAME and VIDEO html embed code.
// echo $embed->html(['width' => 600]);
// Checks if embed data contains details on thumbnail.
//--$embed->hasThumbnail();
// Returns an array containing thumbnail details: url, width and height.
//--$embed->thumbnail();
// Return thumbnail url if it exists or null.
//--$embed->thumbnailUrl();
// Returns an array containing all available embed data including default HTML code.
//print_r( $embed->data());
}
?>
</div>

View File

@ -0,0 +1,7 @@
<label for='title' class='form-label'>Title</label>
<input type='text' name='title' value='{{$link_title}}' placeholder="Leave blank for default video title" class='form-control' />
<label for='link' class='form-label'>URL</label>
<input type='url' name='link' value='{{$link_url}}' class='form-control' />
<span class='small text-muted'>URL to the video</span>

View File

@ -88,6 +88,46 @@ return $path;}
{{-- custom font for logo text --}} {{-- custom font for logo text --}}
<style>@font-face{font-family:'ll';src:url({{ asset('littlelink/fonts/littlelink-custom.otf') }}) format("opentype")}</style> <style>@font-face{font-family:'ll';src:url({{ asset('littlelink/fonts/littlelink-custom.otf') }}) format("opentype")}</style>
<style>
.reg {
background-color: #0085FF;
border: 1px solid transparent;
}
.reg a {
color: #fff;
}
.log {
background-color: #fefefe;
border: 1px solid #000;
}
.log a {
color: #333;
}
.btns {
display: inline-block;
font-weight: 400;
text-align: center;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin-left: 0.75rem;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
-webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
-o-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
}
</style>
</head> </head>
<body> <body>
@ -127,14 +167,14 @@ foreach($pages as $page)
<div class="sign" style="margin-top: 30px; text-align: right;"> <div class="sign" style="margin-top: 30px; text-align: right;">
@if (Route::has('login')) @if (Route::has('login'))
@auth @auth
<a href="{{ route('studioIndex') }}" class="underline spacing">Studio</a> <div class="fadein btns log"><a href="{{ route('studioIndex') }}" class="underline spacing">Studio</a></div>
@else @else
<a href="{{ route('login') }}" class="underline spacing">Log in</a> <div class="fadein btns log"><a href="{{ route('login') }}" class="underline spacing">Log in</a></div>
@if (Route::has('register') and $page->register == 'true') @if (Route::has('register') and $page->register == 'true')
<a href="{{ route('register') }}" class="underline spacing">Register</a> <div class="fadein btns reg"><a href="{{ route('register') }}" class="underline spacing">Register</a></div>
@elseif (env('REGISTER_OVERRIDE') === true) @elseif (env('REGISTER_OVERRIDE') === true)
<a href="{{ route('register') }}" class="underline spacing">Register</a> <div class="fadein btns reg"><a href="{{ route('register') }}" class="underline spacing">Register</a></div>
@endif @endif
@endauth @endauth
@endif @endif

View File

@ -6,6 +6,8 @@
@include('layouts.analytics') @include('layouts.analytics')
<base href="{{url()->current()}}" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="csrf-token" content="{{ csrf_token() }}"> <meta name="csrf-token" content="{{ csrf_token() }}">
<link rel="stylesheet" href="{{ asset('littlelink/css/hover-min.css') }}"> <link rel="stylesheet" href="{{ asset('littlelink/css/hover-min.css') }}">
@ -109,9 +111,67 @@ if($url1sb == '200' or $url2sb == '200') {
@endif @endif
@stack('sidebar-stylesheets') @stack('sidebar-stylesheets')
<style>
.segmented-button {
display: flex;
margin-right: 0.75rem;
margin-top: 0.1rem;
}
.segmented-button .dropdown-button {
padding: 0 1rem;
margin-left: 1px;
}
.btn-seg, .btn-seg-large {
text-decoration: none;
color: #fff;
background-color: #f8b739;
text-align: center;
letter-spacing: .5px;
transition: .2s ease-out;
cursor: pointer;
}
.btn-seg:hover {
color: #fff;
}
.btn-seg, .btn-seg-large, .btn-seg-floating, .btn-seg-large, .btn-seg-flat {
outline: 0;
}
.btn-seg, .btn-seg-large, .btn-seg-flat {
border: none;
border-radius: 0.25rem;
display: inline-block;
height: 36px;
line-height: 36px;
padding: 0 2rem;
text-transform: uppercase;
vertical-align: middle;
-webkit-tap-highlight-color: transparent;
}
.z-depth-1, nav, .card-panel, .card, .toast, .btn-seg, .btn-seg-large, .btn-seg-floating, .dropdown-content, .collapsible, .side-nav {
box-shadow: 0 2px 2px 0 rgb(0 0 0 / 14%), 0 1px 5px 0 rgb(0 0 0 / 12%), 0 3px 1px -2px rgb(0 0 0 / 20%);
}
</style>
{{-- Couldn't get this fixed so I did this: --}}
@if (request()->route()->getName() == 'env-editor.index')
<style>
.btn-seg-ico {
position: relative;
top: 10px;
left: 1px;
}
</style>
@endif
</head> </head>
<body> <body>
<div class="wrapper d-flex align-items-stretch"> <div class="wrapper d-flex align-items-stretch">
<nav id="sidebar"> <nav id="sidebar">
<div class="p-4 pt-5"> <div class="p-4 pt-5">
@ -138,13 +198,13 @@ if($url1sb == '200' or $url2sb == '200') {
<a href="{{ url('env-editor') }}">Config</a> <a href="{{ url('env-editor') }}">Config</a>
</li> </li>
<li> <li>
<a href="{{ url('panel/users/all') }}">Users</a> <a href="{{ url('panel/users/all') }}">Manage Users</a>
</li> </li>
<li> <li>
<a href="{{ url('panel/pages') }}">Pages</a> <a href="{{ url('panel/pages') }}">Footer Pages</a>
</li> </li>
<li> <li>
<a href="{{ url('panel/site') }}">Site</a> <a href="{{ url('panel/site') }}">Home Page</a>
</li> </li>
</ul> </ul>
</li> </li>
@ -152,19 +212,19 @@ if($url1sb == '200' or $url2sb == '200') {
<li> <li>
<li class="active"> <li class="active">
<a href="{{ url('/studio/add-link') }}">Add Link</a> <a href="{{ url('/studio/add-link') }}">Add Page Item</a>
</li> </li>
<li> <li>
<a href="{{ url('/studio/links') }}">Links</a> <a href="{{ url('/studio/links') }}">Your Links</a>
</li> </li>
<li> <li>
<a href="{{ url('/studio/page') }}">Page</a> <a href="{{ url('/studio/page') }}">Your Page</a>
</li> </li>
<li> <li>
<a href="{{ url('/studio/theme') }}">Themes</a> <a href="{{ url('/studio/theme') }}">Your Themes</a>
</li> </li>
<li> <li>
<a href="{{ url('/studio/profile') }}">Profile</a> <a href="{{ url('/studio/profile') }}">Account Settings</a>
</li> </li>
<form action="{{ route('logout') }}" method="post"> <form action="{{ route('logout') }}" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}"> <input type="hidden" name="_token" value="{{ csrf_token() }}">
@ -236,29 +296,95 @@ if($url1sb == '200' or $url2sb == '200') {
@endif @endif
@endif @endif
@elseif(env('NOTIFY_UPDATES') == 'true' or env('NOTIFY_UPDATES') === 'major' or env('NOTIFY_UPDATES') === 'all') @elseif(env('NOTIFY_UPDATES') == 'true' or env('NOTIFY_UPDATES') === 'major' or env('NOTIFY_UPDATES') === 'all')
<?php // Checks if URL exists
try {
function URL_exists(string $urlsb): bool
{
return str_contains(get_headers($urlsb)[0], "200 OK");
}
// Sets $ServerExists to true if URL exists
if (URL_exists("https://julianprieber.github.io/littlelink-custom/version.json")){
$ServerExists = "true";
}
} catch (exception $e) {
$ServerExists = "false";
}
?>
<! Checks if file version.json exists AND if version.json exists on server to continue (without this PHP will throw ErrorException ) > <! Checks if file version.json exists AND if version.json exists on server to continue (without this PHP will throw ErrorException ) >
@if(file_exists(base_path("version.json"))) @if(file_exists(base_path("version.json")) and $ServerExists == 'true')
<?php // Requests newest version from server and sets it as variable <?php // Requests newest version from server and sets it as variable
$Vgit = file_get_contents("https://julianprieber.github.io/littlelink-custom/version.json");
try{
$Vgit = file_get_contents("https://version.littlelink-custom.com/");
// Requests current version from the local version file and sets it as variable // Requests current version from the local version file and sets it as variable
$Vlocal = file_get_contents(base_path("version.json")); $Vlocal = file_get_contents(base_path("version.json"));
}
catch (Exception $e){
$Vgit = "0";
$Vlocal = "0";
}
?> ?>
<! If user has role admin AND newest GitHub release version is higher than the local one an update notice will be displayed > <! If user has role admin AND newest GitHub release version is higher than the local one an update notice will be displayed >
@if(auth()->user()->role == 'admin' and $Vgit > $Vlocal) @if(auth()->user()->role == 'admin' and $Vgit > $Vlocal)
<a style="color:#007bff" class="nav-link" href="{{ url('update') }}" title="Click here to learn more about how to update">An update is available</a> <button class="update-notification"><a class="update-link nav-link" href="{{ url('update') }}" title="Click here to learn more about how to update">Update</a></button>
<?php
$version1 = $Vlocal;
$version2 = $Vgit;
$version1_steps = explode(".", $version1);
$version2_steps = explode(".", $version2);
$count = 0;
// first digit
if ($version2_steps[0] - $version1_steps[0] == 1) {
$count += 10;
}
// second digit
if ($version2_steps[1] - $version1_steps[1] == 1) {
$count += 10;
}
for ($i = 2; $i < count($version1_steps); $i++) {
$count += $version2_steps[$i] - $version1_steps[$i];
}
$count = abs($count);
?>
<style>
:root {
@if($count < 4)
--bg-color: rgba(63, 144, 90, 0.2);
--bo-color: rgb(63, 144, 90);
@elseif($count > 3 and $count < 6)
--bg-color: rgb(213, 184, 95, 0.2);
--bo-color: rgba(213, 183, 95);
@else
--bg-color: rgb(255, 99, 71, 0.2);
--bo-color: rgb(255, 99, 71);
@endif
}
.update-link{
color: var(--bo-color) !important;
}
.update-notification{
display: inline-block;
margin-bottom: 0;
font-size: 14px;
height: 2.5rem;
line-height: 1rem;
width: auto;
font-weight: 500;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: var(--bg-color);
border: 1px solid var(--bo-color);
border-radius: 25px;
}
</style>
@endif @endif
@endif @endif
@endif @endif
@ -283,7 +409,23 @@ if($url1sb == '200' or $url2sb == '200') {
<script>function ColorOverrride(){document.cookie="color_scheme_override=dark; path=/",location.reload()}var btn=document.getElementById("myBtn");btn.addEventListener("click",ColorOverrride);</script> <script>function ColorOverrride(){document.cookie="color_scheme_override=dark; path=/",location.reload()}var btn=document.getElementById("myBtn");btn.addEventListener("click",ColorOverrride);</script>
@endif @endif
<a class="nav-link" href="{{ url('') }}/@<?= Auth::user()->littlelink_name ?>" target="_blank">View Page</a> <div class="segmented-button">
<a style="font-weight: 130%" href="{{ url('') }}/@<?= Auth::user()->littlelink_name ?>" target="_blank" class="btn-seg">View Page</a>
<a onclick="copyText('{{ url('') }}/@<?= Auth::user()->littlelink_name ?>')" style="color:#fff" class="btn-seg dropdown-button"><i class="btn-seg-ico bi bi-share-fill"></i></a>
</div>
<div id='dropdown1' class='dropdown-content'>
</div>
<script>
function copyText(text) {
var dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
alert('URL has been copied to your clipboard!')
}
</script>
</div> </div>
</li> </li>
</ul> </ul>
@ -301,7 +443,7 @@ $userdbs = DB::table('users')->where('id', $littlelink_current)->get();
@foreach($userdbs as $userdb) @foreach($userdbs as $userdb)
@if(Hash::check('12345678', $userdb->password)) @if(Hash::check('12345678', $userdb->password))
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <nav class="shadow navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid"> <div class="container-fluid">
<a style="background-color:tomato;color:#fff;border-radius:5px;" class="nav-link" href="{{ url('/studio/profile') }}" target=""><i class="bi bi-exclamation-circle-fill"></i> <strong>You are still using the default password! Click here to change this.</strong></a> <a style="background-color:tomato;color:#fff;border-radius:5px;" class="nav-link" href="{{ url('/studio/profile') }}" target=""><i class="bi bi-exclamation-circle-fill"></i> <strong>You are still using the default password! Click here to change this.</strong></a>
</div> </div>
@ -312,10 +454,22 @@ $userdbs = DB::table('users')->where('id', $littlelink_current)->get();
@endif @endif
<! #### begin event detection #### > <! #### begin event detection #### >
@if(env('NOTIFY_EVENTS') === true) <?php
try {
function URL_event_exists(string $urlsb): bool
{
return str_contains(get_headers($urlsb)[0], "200 OK");
}
if (URL_event_exists("https://julianprieber.github.io/littlelink-custom-events/event.json")){
$EventServerExists = "true";
}
} catch (exception $e) {
$EventServerExists = "false";
}
?>
@if(env('NOTIFY_EVENTS') === true and $EventServerExists == 'true')
<?php <?php
try{ $GetEventJson = file_get_contents("https://julianprieber.github.io/littlelink-custom-events/event.json");
$GetEventJson = file_get_contents("https://event.littlelink-custom.com/");
$EventJson = json_decode($GetEventJson, true); $EventJson = json_decode($GetEventJson, true);
if(isset($_COOKIE['HideEvent']) == NULL) { if(isset($_COOKIE['HideEvent']) == NULL) {
setcookie("HideEvent",$_COOKIE['ID'] = "0", time()+60*60*24*5, "/"); setcookie("HideEvent",$_COOKIE['ID'] = "0", time()+60*60*24*5, "/");
@ -350,7 +504,6 @@ if(localStorage.getItem("firstTime")==null){
} }
</script> </script>
@endif @endif
<?php } catch (Exception $e){} ?>
@endif @endif
<! #### end event detection #### > <! #### end event detection #### >
@yield('content') @yield('content')

View File

@ -95,11 +95,7 @@ return $path;}
<meta name="designer" href="{{ url('') . "/theme/@" . $littlelink_name}}" content="{{ url('') . "/theme/@" . $littlelink_name}}"> <meta name="designer" href="{{ url('') . "/theme/@" . $littlelink_name}}" content="{{ url('') . "/theme/@" . $littlelink_name}}">
<link rel="stylesheet" href="themes/{{$info->theme}}/share.button.css"> <link rel="stylesheet" href="themes/{{$info->theme}}/share.button.css">
@if(theme('use_default_buttons') == "true")
<link rel="stylesheet" href="{{ asset('littlelink/css/brands.css') }}">
@else
<link rel="stylesheet" href="themes/{{$info->theme}}/brands.css"> <link rel="stylesheet" href="themes/{{$info->theme}}/brands.css">
@endif
<link rel="stylesheet" href="themes/{{$info->theme}}/skeleton-auto.css"> <link rel="stylesheet" href="themes/{{$info->theme}}/skeleton-auto.css">
@if(file_exists(base_path('themes/' . $info->theme . '/animations.css'))) @if(file_exists(base_path('themes/' . $info->theme . '/animations.css')))
<link rel="stylesheet" href="<?php echo asset('themes/' . $info->theme . '/animations.css') ?>"> <link rel="stylesheet" href="<?php echo asset('themes/' . $info->theme . '/animations.css') ?>">
@ -272,6 +268,8 @@ function get_operating_system() {
<div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-hover icon-hover" style="{{ $link->custom_css }}" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><i style="color: {{$link->custom_icon}}" class="icon hvr-icon fa {{$link->custom_icon}}"></i>{{ $link->title }}</a></div> <div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-hover icon-hover" style="{{ $link->custom_css }}" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><i style="color: {{$link->custom_icon}}" class="icon hvr-icon fa {{$link->custom_icon}}"></i>{{ $link->title }}</a></div>
@elseif($link->name === "buy me a coffee") @elseif($link->name === "buy me a coffee")
<div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-coffee button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><img alt="button-icon" class="icon hvr-icon" src="@if(theme('use_custom_icons') == "true"){{ url('themes/' . $GLOBALS['themeName'] . '/extra/custom-icons')}}/coffee{{theme('custom_icon_extension')}} @else{{ asset('\/littlelink/icons\/')}}coffee.svg @endif">Buy me a Coffee</a></div> <div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-coffee button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><img alt="button-icon" class="icon hvr-icon" src="@if(theme('use_custom_icons') == "true"){{ url('themes/' . $GLOBALS['themeName'] . '/extra/custom-icons')}}/coffee{{theme('custom_icon_extension')}} @else{{ asset('\/littlelink/icons\/')}}coffee.svg @endif">Buy me a Coffee</a></div>
@elseif($link->name === "mastodon")
<div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-{{ $link->name }} button button-hover icon-hover" rel="me noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><img alt="button-icon" class="icon hvr-icon" src="@if(theme('use_custom_icons') == "true"){{ url('themes/' . $GLOBALS['themeName'] . '/extra/custom-icons')}}/{{$linkName}}{{theme('custom_icon_extension')}} @else{{ asset('\/littlelink/icons\/') . $linkName }}.svg @endif">{{ $link->title }}</a></div>
@elseif($link->name === "custom_website"and $link->custom_css === "" or $link->custom_css === "NULL" or (theme('allow_custom_buttons') == "false" and $link->name === "custom")) @elseif($link->name === "custom_website"and $link->custom_css === "" or $link->custom_css === "NULL" or (theme('allow_custom_buttons') == "false" and $link->name === "custom"))
<div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-custom_website button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><img alt="button-icon" class="icon hvr-icon" src="https://icons.duckduckgo.com/ip3/{{strp($link->link)}}.ico">{{ $link->title }}</a></div> <div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-custom_website button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><img alt="button-icon" class="icon hvr-icon" src="https://icons.duckduckgo.com/ip3/{{strp($link->link)}}.ico">{{ $link->title }}</a></div>
@elseif($link->name === "custom_website" and $link->custom_css != "") @elseif($link->name === "custom_website" and $link->custom_css != "")
@ -288,8 +286,7 @@ function get_operating_system() {
@elseif($link->name === "heading") @elseif($link->name === "heading")
<h2>{{ $link->title }}</h2> <h2>{{ $link->title }}</h2>
@else @else
<?php include base_path('config/button-names.php'); $newLinkName = $linkName; $isNewName = "false"; foreach($buttonNames as $key => $value) { if($newLinkName == $key) { $newLinkName = $value; $isNewName = "true"; } } ?> <div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-{{ $link->name }} button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><img alt="button-icon" class="icon hvr-icon" src="@if(theme('use_custom_icons') == "true"){{ url('themes/' . $GLOBALS['themeName'] . '/extra/custom-icons')}}/{{$linkName}}{{theme('custom_icon_extension')}} @else{{ asset('\/littlelink/icons\/') . $linkName }}.svg @endif">{{ $link->title }}</a></div>
<div style="--delay: {{ $initial++ }}s" class="button-entrance"><a class="button button-{{ $link->name }} button button-hover icon-hover" rel="noopener noreferrer nofollow" href="{{ route('clickNumber') . '/' . $link->id . '/' . $link->link}}" @if(theme('open_links_in_same_tab') != "true")target="_blank"@endif ><img alt="button-icon" class="icon hvr-icon" src="@if(theme('use_custom_icons') == "true"){{ url('themes/' . $GLOBALS['themeName'] . '/extra/custom-icons')}}/{{$linkName}}{{theme('custom_icon_extension')}} @else{{ asset('\/littlelink/icons\/') . $linkName }}.svg @endif">@if($isNewName == "true"){{ $newLinkName }}@else{{ ucfirst($newLinkName) }}@endif</a></div>
@endif @endif
@endforeach @endforeach

View File

@ -2,7 +2,10 @@
@section('content') @section('content')
<h2 class="mb-4"><i class="bi bi-person"> Edit User</i></h2> <section class="shadow text-gray-400">
<h2 class="mb-4 card-header"><i class="bi bi-person"> Edit User</i></h2>
<div class="card-body p-0 p-md-3">
@foreach($user as $user) @foreach($user as $user)
<form action="{{ route('editUser', $user->id) }}" enctype="multipart/form-data" method="post"> <form action="{{ route('editUser', $user->id) }}" enctype="multipart/form-data" method="post">
@csrf @csrf
@ -65,4 +68,6 @@
<button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button> <button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button>
</form> </form>
</div>
</section>
@endsection @endsection

View File

@ -2,7 +2,9 @@
@section('content') @section('content')
<h2 class="mb-4"><i class="bi bi-link-45deg"> Links</i></h2> <section class="shadow text-gray-400">
<h2 class="mb-4 card-header"><i class="bi bi-link-45deg"> Links</i></h2>
<div class="card-body p-0 p-md-3">
<div style="overflow-y: auto;"> <div style="overflow-y: auto;">
<table class="table table-bordered"> <table class="table table-bordered">
@ -33,4 +35,6 @@
<a class="btn btn-primary" href="{{ url('/panel/users/all') }}"> Back</a> <a class="btn btn-primary" href="{{ url('/panel/users/all') }}"> Back</a>
</div>
</section>
@endsection @endsection

View File

@ -4,7 +4,9 @@
<script src="{{ asset('resources/ckeditor/ckeditor.js') }}"></script> <script src="{{ asset('resources/ckeditor/ckeditor.js') }}"></script>
<h2 class="mb-4"><i class="bi bi-person"> Edit Pages</i></h2> <section class="shadow text-gray-400">
<h2 class="mb-4 card-header"><i class="bi bi-person"> Edit Pages</i></h2>
<div class="card-body p-0 p-md-3">
<form action="{{ route('editSitePage') }}" method="post"> <form action="{{ route('editSitePage') }}" method="post">
@csrf @csrf
@ -37,4 +39,6 @@
<button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button> <button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button>
</form> </form>
</div>
</section>
@endsection @endsection

View File

@ -4,7 +4,9 @@
<script src="{{ asset('resources/ckeditor/ckeditor.js') }}"></script> <script src="{{ asset('resources/ckeditor/ckeditor.js') }}"></script>
<h2 class="mb-4"><i class="bi bi-person"> Site</i></h2> <section class="shadow text-gray-400">
<h2 class="mb-4 card-header"><i class="bi bi-person"> Site</i></h2>
<div class="card-body p-0 p-md-3">
<form action="{{ route('editSite') }}" enctype="multipart/form-data" method="post"> <form action="{{ route('editSite') }}" enctype="multipart/form-data" method="post">
@csrf @csrf
@ -19,4 +21,7 @@
<button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button> <button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button>
</form> </form>
</div>
</section>
@endsection @endsection

File diff suppressed because one or more lines are too long

View File

@ -156,7 +156,9 @@ var button_css = {
<script src="https://kit.fontawesome.com/c4a5e06183.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/c4a5e06183.js" crossorigin="anonymous"></script>
<div> <div>
<h2 class="mb-4"><i class="bi bi-pen"> Button Editor</i> <section class="shadow text-gray-400">
<h2 class="mb-4 card-header"><i class="bi bi-pen"> Button Editor</i></h2>
<div class="card-body p-0 p-md-3">
<br><br><a class="btn btn-primary" href="{{ url('/studio/links') }}"> Back</a> <br><br><a class="btn btn-primary" href="{{ url('/studio/links') }}"> Back</a>
@ -336,5 +338,7 @@ NULL
<a class="btn btn-primary" href="{{ url('/studio/links') }}"> Back</a> <a class="btn btn-primary" href="{{ url('/studio/links') }}"> Back</a>
</div>
</section>
@endsection @endsection
@endif @endif

View File

@ -2,44 +2,127 @@
@section('content') @section('content')
<h2 class="mb-4"><i class="bi bi-pen"> Edit Link</i></h2> <section class=' shadow text-gray-400'>
<form action="{{ route('editLink', $id) }}" method="post"> <h3 class="card-header"><i class="bi bi-journal-plus"> @if($LinkID !== 0) Edit @else Add @endif Page Item</i></h3>
@csrf
<div class="form-group col-lg-8"> <div class='card-body'>
<label>Link</label> <form action="{{ route('addLink') }}" method="post">
<input type="text" name="link" value="{{ $link }}" class="form-control" placeholder="https://example.com" required> @method('POST')
</div> @csrf
<div class="form-group col-lg-8"> <input type='hidden' name='linkid' value="{{ $LinkID }}" />
<label>Title</label>
<input type="text" name="title" value="{{ $title }}" class="form-control" placeholder="Example"> <div class="form-group col-lg-8 flex justify-around">
</div> <label class='font-weight-bold'>Link Type: </label>
<div class="form-group col-lg-8"> <div class="btn-group shadow m-2">
<label for="exampleFormControlSelect1">Button</label> <button type="button" id='btnLinkType' class="border-0 p-1" title='Click to change link type' data-toggle="modal" data-target="#SelectLinkType">link</button>
<select class="form-control" name="button">
<option style="background-color:#1e90ff;color:#fff"> {{ $buttonName }} </option>
<button type="button" class="dropdown-toggle border-0 border-left-1 px-2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu">
@foreach ( $LinkTypes as $lt )
<a data-typeid='{{$lt['id']}}' data-typename='{{$lt['title']}}' class="dropdown-item doSelectLinkType" href="#">
<i class="{{$lt['icon']}}"></i> {{$lt['title']}}
</a>
@endforeach
</div>
<input type='hidden' name='linktype_id' value='1'>
</div>
</div>
{{-- @include("components.pageitems.".$SelectedLinkType['typename']."-form", ['some' => 'data']) --}}
<div id='link_params' class='col-lg-8'></div>
<div class="row">
<a class="btn btn-secondary mt-3 ml-3 btn" href="{{ url()->previous() }}">Cancel</a>
<button type="submit" class="mt-3 ml-3 btn btn-primary">Save</button>
</div>
@if ($buttonName != "custom")<option style="background-color:#ffe8e4;"> custom </option>@endif
@if ($buttonName != "custom_website")<option style="background-color:#ffe8e4;"> custom_website </option>@endif
@foreach($buttons as $button)
@if (!in_array($button->name, ['custom', 'custom_website', 'heading', 'space']))
@if ($button->name != $buttonName)
<option> {{ $button->name }} </option>
@endif
@endif
@endforeach
@if ($buttonName != "heading")<option style="background-color:#ebebeb;"> heading </option>@endif
@if ($buttonName != "space")<option style="background-color:#ebebeb;"> space </option>@endif
</select>
</div>
<div class="form-group col-lg-8">
<label>Order</label>
<input type="number" name="order" value="{{ $order }}" class="form-control" placeholder="use for ordering links">
</div>
<button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button>
</form> </form>
</div>
</section>
<br><br>
{{-- <details>
<summary>More information</summary>
<pre style="color: grey;">
The 'Custom' button allows you to add a custom link, where the text on the button is determined with the link title set above.
The 'Custom_website' button functions similar to the Custom button, with the addition of a function that requests the favicon from the chosen URL and uses it as the button icon.
The 'Space' button will be replaced with an empty space, so buttons could be visually separated into groups. Entering a number between 1-10 in the title section will change the empty space's distance.
The 'Heading' button will be replaced with a sub-heading, where the title defines the text on that heading.
</pre>
</details> --}}
<!-- Modal -->
<x-modal title="Select Link Type" id="SelectLinkType">
<div class="d-flex flex-row flex-wrap p-3">
@foreach ( $LinkTypes as $lt )
<a href="#" data-typeid='{{$lt['id']}}' data-typename='{{$lt['title']}}'' class="hvr-grow m-2 w-100 d-block doSelectLinkType">
<div class="card w-100">
<div class="row no-gutters">
<div class="col-auto bg-light justify-content-center p-3">
<i class="card-img h1 text-primary {{$lt['icon']}} d-block"></i>
</div>
<div class="col">
<div class="card-body">
<h5 class="card-title mb-0">{{$lt['title']}}</h5>
<p class="card-text text-muted">{{$lt['description']}}</p>
</div>
</div>
</div>
</div>
</a>
@endforeach
</div>
<x-slot name="buttons">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</x-slot>
</x-modal>
@endsection @endsection
@push("sidebar-scripts")
<script>
$(function() {
LoadLinkTypeParams($("input[name='linktype_id']").val() , $("input[name=linkid]").val());
$('.doSelectLinkType').on('click', function() {
$("input[name='linktype_id']").val($(this).data('typeid'));
$("#btnLinkType").html($(this).data('typename'));
LoadLinkTypeParams($(this).data('typeid'), $("input[name=linkid]").val());
$('#SelectLinkType').modal('hide');
});
function LoadLinkTypeParams($TypeId, $LinkId) {
var baseURL = <?php echo "\"" . url('') . "\""; ?>;
$("#link_params").html(' <img width="70px" src="' + baseURL + '/img/loading.gif" />').load(baseURL + `/studio/linkparamform_part/${$TypeId}/${$LinkId}`);
}
});
</script>
@endpush

View File

@ -0,0 +1,45 @@
@extends('layouts.sidebar')
@section('content')
<h2 class="mb-4"><i class="bi bi-pen"> Edit Link</i></h2>
<form action="{{ route('editLink', $id) }}" method="post">
@csrf
<div class="form-group col-lg-8">
<label>Link</label>
<input type="text" name="link" value="{{ $link }}" class="form-control" placeholder="https://example.com" required>
</div>
<div class="form-group col-lg-8">
<label>Title</label>
<input type="text" name="title" value="{{ $title }}" class="form-control" placeholder="Example">
</div>
<div class="form-group col-lg-8">
<label for="exampleFormControlSelect1">Button</label>
<select class="form-control" name="button">
<option style="background-color:#1e90ff;color:#fff"> {{ $buttonName }} </option>
@if ($buttonName != "custom")<option style="background-color:#ffe8e4;"> custom </option>@endif
@if ($buttonName != "custom_website")<option style="background-color:#ffe8e4;"> custom_website </option>@endif
@foreach($buttons as $button)
@if (!in_array($button->name, ['custom', 'custom_website', 'heading', 'space']))
@if ($button->name != $buttonName)
<option> {{ $button->name }} </option>
@endif
@endif
@endforeach
@if ($buttonName != "heading")<option style="background-color:#ebebeb;"> heading </option>@endif
@if ($buttonName != "space")<option style="background-color:#ebebeb;"> space </option>@endif
</select>
</div>
<div class="form-group col-lg-8">
<label>Order</label>
<input type="number" name="order" value="{{ $order }}" class="form-control" placeholder="use for ordering links">
</div>
<button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button>
</form>
@endsection

View File

@ -1,18 +1,95 @@
@extends('layouts.sidebar') @extends('layouts.sidebar')
@section('content')
@if($littlelink_name == '')
<h2 class="mb-4"> 👋 Hi, stranger</h2>
<h5>You do not have a Page URL set, yet you can change that on the <a href="{{ url('/studio/page') }}">Page section</a></h5>
@else
<h2 class="mb-4"> 👋 Hi, @<?= $littlelink_name ?></h2>
@endif
<p>
Welcome to {{ config('app.name') }}!
</p>
<div class="mt-5 row">
<h5 class="mb-4"><i class="bi bi-link"> link: {{ $links }} </i></h5>
<h5 class="mb-4 ml-5"><i class="bi bi-eye"> click: {{ $clicks }} </i></h5>
</div>
@endsection @section('content')
@if($greeting == '')
<h3 class="mb-3"> 👋 Hi, stranger</h2>
<h5>You do not have a Page URL set, yet you can change that on the <a href="{{ url('/studio/page') }}">Page section</a></h5>
@else
<h3 class="mb-3"> 👋 Hi, <?= $greeting ?></h2>
@endif
<p>
Welcome to {{ config('app.name') }}!
</p>
<!-- Section: Design Block -->
<section class="mb-3 text-gray-800 text-center shadow p-4 w-full">
<div class='font-weight-bold text-left'>Visitor analytics:</div>
<div class="d-flex flex-wrap justify-content-around">
<div class="p-2">
<h3 class="text-primary"><strong>{{ $pageStats['visitors']['day']}}</strong></h3>
<span class="text-muted">Today</span>
</div>
<div class="p-2">
<h3 class="text-primary"><strong>{{ $pageStats['visitors']['week']}}</strong></h3>
<span class="text-muted">Week</span>
</div>
<div class="p-2">
<h3 class="text-primary"><strong>{{ $pageStats['visitors']['month']}}</strong></h3>
<span class="text-muted">Month</span>
</div>
<div class="p-2">
<h3 class="text-primary"><strong>{{ $pageStats['visitors']['year']}}</strong></h3>
<span class="text-muted">Year</span>
</div>
<div class="p-2">
<h3 class="text-primary"><strong>{{ $pageStats['visitors']['all']}}</strong></h3>
<span class="text-muted">All Time</span>
</div>
</div>
</section>
<section class="mb-3 text-center shadow p-4 w-full">
<div class=" d-flex">
<div class='p-2 h6'><i class="bi bi-link"></i> Total Links: <span class='text-primary'>{{ $links }} </span></div>
<div class='p-2 h6'><i class="bi bi-eye"></i> Link Clicks: <span class='text-primary'>{{ $clicks }}</span></div>
</div>
<div class='text-center w-100'>
<a href="{{ url('/studio/links') }}">View/Edit Links</a>
</div>
<div class='w-100 text-left'>
<h6><i class="bi bi-sort-up"></i> Top Links:</h6>
@php $i = 0; @endphp
@foreach($toplinks as $link)
@php $linkName = str_replace('default ','',$link->name) @endphp
@php $i++; @endphp
<ol class='list-group list-group-flush bg-transparent'>
@if($link->name !== "phone" && $link->name !== 'heading')
<li class="list-group-item bg-transparent">
{{ $i }}.) {{$link->title}} -- <span class='text-primary' title='Click Count'>{{$link->click_number}} </span> <br />
<a href="{{$link->link}}" class='small ml-3' title='{{$link->link}}' target="_blank">{{$link->link}}</a></li>
@endif
</ol>
@endforeach
</section>
{{-- <pre>{{ print_r($pageStats) }}</pre> --}}
@endsection

View File

@ -2,67 +2,121 @@
@section('content') @section('content')
@push('sidebar-stylesheets')
<base href="{{asset('')}}" />
@endpush
@if(Request::is('studio/links/10')) @if(Request::is('studio/links/10'))
@php setcookie("LinkCount", "10", time()+60*60*24*5, "/"); @endphp @php setcookie("LinkCount", "10", time()+60*60*24*5, "/"); @endphp
@elseif(Request::is('studio/links/20')) @elseif(Request::is('studio/links/20'))
@php setcookie("LinkCount", "20", time()+60*60*24*5, "/"); @endphp @php setcookie("LinkCount", "20", time()+60*60*24*5, "/"); @endphp
@elseif(Request::is('studio/links/30')) @elseif(Request::is('studio/links/30'))
@php setcookie("LinkCount", "30", time()+60*60*24*5, "/"); @endphp @php setcookie("LinkCount", "30", time()+60*60*24*5, "/"); @endphp
@elseif(Request::is('studio/links/all')) @elseif(Request::is('studio/links/all'))
@php setcookie("LinkCount", "all", time()+60*60*24*5, "/"); @endphp @php setcookie("LinkCount", "all", time()+60*60*24*5, "/"); @endphp
@endif @endif
<h2 class="mb-4"><i class="bi bi-link-45deg"> Links</i></h2> <section class=' shadow text-gray-400'>
<h3 class="card-header"><i class="bi bi-link-45deg">My Links</i>
<a class="btn btn-primary float-right" href="{{ url('/studio/add-link') }}">Add new <span class='d-none d-md-inline'>item</span></a>
</h3>
<div class='card-body p-0 p-md-3'>
{{-- <div style="text-align: right;"><a href="{{ url('/studio/links') }}/10">10</a> | <a href="{{ url('/studio/links') }}/20">20</a> | <a href="{{ url('/studio/links') }}/30">30</a> | <a href="{{ url('/studio/links') }}/all">all</a></div> --}}
<div style="overflow-y: none;" class="col col-md-7 ">
<div id="links-table-body" data-page="{{request('page', 1)}}" data-per-page="{{$pagePage ? $pagePage : 0}}">
@foreach($links as $link)
<div class='row h-100 pb-0 mb-2 border rounded hvr-glow ' data-id="{{$link->id}}">
<div class='col-auto p-2 my-auto mr-2' title="{{ $link->link }}">
<span class=" sortable-handle"></span>
</div>
<div class='col border-left h-100'>
<div class='row h-100'>
<div class='col-12 p-2' title="{{ $link->title }}">
<span class='h6'>
@if($link->typename == 'predefined')
<span class='button button-{{$link->params['button']}} p-0' style='max-width: 25px; max-height: 25px; line-height: 0; cursor: none;'>
<img alt="button-icon" height="15" class="m-1 " src="{{ asset('\/littlelink/icons\/') . $link->params['button'] }}.svg ">
</span>
@else
{{-- Change later!!!! fa-external-link --}}
<i class="fa-external-link" title="{{$link->title}}"></i>
@endif
{{$link->title}}</span>
@if(!empty($link->link))
<br /><a title='{{$link->link}}' href="{{ $link->link}}" target="_blank" class="ml-4 text-muted small">{{Str::limit($link->link, 75 )}}</a>
@endif
</div>
<div class='col' class="text-right">
{{Str::limit($link->params['text'] ?? null, 150) }}
@if($link->typename == 'video')
@php
$embed = OEmbed::get($link->link);
if ($embed && $embed->hasThumbnail()) {
echo "<img style='max-height: 150px;' src='".$embed->thumbnailUrl()."' />";
}
@endphp
@endif
</div>
<div class='col-12 py-1 px-3 m-0 bg-blend-darken card-footer mt-2'>
<a href="{{ route('editLink', $link->id ) }}" class="hvr-grow mr-2"><i class='bi bi-pencil'></i> Edit</a>
@if(env('ENABLE_BUTTON_EDITOR') === true)
@if($link->id == '1' or $link->id == '2')
<a href="{{ route('editCSS', $link->id ) }}" class="mr-2 hvr-grow text-success">Customize</a>
@endif
@endif
@if(!empty($link->link))
<span class='hvr-grow'><i class="bi bi-bar-chart-line"></i> {{ $link->click_number }} Clicks</span>
@endif
<a href="{{ route('deleteLink', $link->id ) }}" onclick="return confirm('Are you sure you want to delete `{{$link->title}}` ?')" class="float-right hvr-grow p-1 text-danger"><i class='bi bi-trash'></i></a>
</div>
</div>
</div>
</div>
@endforeach
</div>
<div style="text-align: right;"><a href="{{ url('/studio/links') }}/10">10</a> | <a href="{{ url('/studio/links') }}/20">20</a> | <a href="{{ url('/studio/links') }}/30">30</a> | <a href="{{ url('/studio/links') }}/all">all</a></div>
<div style="overflow-y: auto;">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Link</th>
<th scope="col">Title</th>
<th scope="col">Clicks</th>
<th scope="col">Order </th>
{{-- <th scope="col">Pin Link </th> --}}
<th scope="col">Edit</th>
@if(env('ENABLE_BUTTON_EDITOR') === true)<th scope="col">Button Editor</th>@endif
<th scope="col">Delete</th>
</tr>
</thead>
<tbody id="links-table-body" data-page="{{request('page', 1)}}" data-per-page="{{$pagePage ? $pagePage : 0}}">
@foreach($links as $link)
<tr data-id="{{$link->id}}">
<td title="{{ $link->link }}"><span class="sortable-handle"></span> {{ Str::limit($link->link, 30) }}</td>
<td title="{{ $link->title }}">{{ Str::limit($link->title, 30) }}</td>
<td class="text-right">{{ $link->click_number }}</td>
<td class="text-right">{{ $link->order }}</td>
{{-- <td><a href="{{ route('upLink', ['up' => $link->up_link, 'id' => $link->id]) }}" class="text-primary">{{ $link->up_link }}</a></td> --}}
<td><a href="{{ route('editLink', $link->id ) }}">Edit</a></td>
@if(env('ENABLE_BUTTON_EDITOR') === true)
@if($link->button_id == 1 or $link->button_id == 2)
<td><a href="{{ route('editCSS', $link->id ) }}" class="text-success">Customize Button</a></td>
@else
<td><a> - </a></td>
@endif
@endif
<td><a href="{{ route('deleteLink', $link->id ) }}" class="text-danger">Delete</a></td>
</tr>
@endforeach
</tbody>
</table>
<script type="text/javascript"> <script type="text/javascript">
const linksTableOrders = "{{ implode("|", $links->pluck('id')->toArray()) }}" const linksTableOrders = "{{ implode(' | ', $links->pluck('id')->toArray()) }}"
</script> </script>
</div> </div>
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{!! $links ?? ''->links() !!} {!! $links ?? ''->links() !!}
</ul> </ul>
<a class="btn btn-primary" href="{{ url('/studio/add-link') }}">Add a link</a>
<a class="btn btn-primary" href="{{ url('/studio/add-link') }}">Add new item</a>
</div>
</section>
@endsection @endsection

View File

@ -2,53 +2,135 @@
@section('content') @section('content')
@if(env('ALLOW_USER_HTML') === true) <script src="{{ asset('resources/ckeditor/ckeditor.js') }}"></script> @endif <style>
.ck-editor__editable[role="textbox"] {
/* editing area */
min-height: 200px;
}
<h2 class="mb-4"><i class="bi bi-file-earmark-break"> Page</i></h2> .ck-content .image {
/* block images */
@foreach($pages as $page) max-width: 80%;
<form action="{{ route('editPage') }}" enctype="multipart/form-data" method="post"> margin: 20px auto;
@csrf }
@if($page->littlelink_name != '')
<div class="form-group col-lg-8"> </style>
<label>Logo</label>
<input type="file" accept="image/jpeg,image/jpg,image/png" class="form-control-file" name="image"> <section class=' shadow text-gray-400'>
</div> <h3 class="mb-4 card-header"><i class="bi bi-file-earmark-break"> My Page</i></h3>
<div class="card-body p-0 p-md-3">
<div class="card-body"></div>
@foreach($pages as $page)
<form action="{{ route('editPage') }}" enctype="multipart/form-data" method="post">
@csrf
@if($page->littlelink_name != '')
<div class="form-group col-lg-8">
<label>Logo</label>
<input type="file" accept="image/jpeg,image/jpg,image/png" class="form-control-file" name="image">
</div>
@endif
<div class="form-group col-lg-8">
@if(file_exists(base_path("img/$page->littlelink_name" . ".png" )))
<img src="{{ asset("img/$page->littlelink_name" . ".png") }}" style="width: 75px; height: 75px; border-radius: 50%; object-fit: cover;">
@else
@if(!empty($page->image))
<img src="{{ $page->image }}" style="width: 75px; height: 75px; object-fit: cover;">
@else
<img src="{{ asset('littlelink/images/logo.svg') }}" style="width: 75px; height: 75px; object-fit: cover;">
@endif @endif
@endif
<div class="form-group col-lg-8"> </div>
@if(file_exists(base_path("img/$page->littlelink_name" . ".png" )))
<img src="{{ asset("img/$page->littlelink_name" . ".png") }}" style="width: 75px; height: 75px; border-radius: 50%; object-fit: cover;"> <!--<div class="form-group col-lg-8">
@else
<img src="{{ asset('littlelink/images/logo.svg') }}" style="width: 75px; height: 75px; object-fit: cover;">
@endif
</div>
<!--<div class="form-group col-lg-8">
<label>Path name</label> <label>Path name</label>
@<input type="text" class="form-control" name="pageName" value="{{ $page->littlelink_name ?? '' }}"> @<input type="text" class="form-control" name="pageName" value="{{ $page->littlelink_name ?? '' }}">
</div>--> </div>-->
<div class="form-group col-lg-8"> <div class="form-group col-lg-8">
<?php <?php
$url = $_SERVER['REQUEST_URI']; $url = $_SERVER['REQUEST_URI'];
if( strpos( $url, "no_page_name" ) == true ) echo '<span style="color:#FF0000; font-size:120%;">You do not have a Page URL</span>'; ?> if( strpos( $url, "no_page_name" ) == true ) echo '<span style="color:#FF0000; font-size:120%;">You do not have a Page URL</span>'; ?>
<br> <br>
<label>Page URL</label> <label>Page URL</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text">{{ url('') }}/@</div> <div class="input-group-text">{{ url('') }}/@</div>
</div> </div>
<input type="text" class="form-control" name="pageName" value="{{ $page->littlelink_name ?? '' }}" required> <input type="text" class="form-control" name="pageName" value="{{ $page->littlelink_name ?? '' }}" required>
</div> </div>
</div>
<div class="form-group col-lg-8">
<label>Page Description</label>
<textarea class="form-control @if(env('ALLOW_USER_HTML') === true) ckeditor @endif" name="pageDescription" rows="3">{{ $page->littlelink_description ?? '' }}</textarea>
</div>
@endforeach
<button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button>
</form>
<label style="margin-top:15px">Display name</label>
<div class="input-group">
{{-- <div class="input-group-prepend">
<div class="input-group-text">Name:</div>
</div> --}}
<input type="text" class="form-control" name="Name" value="{{ $page->name }}" required>
</div>
</div>
<div class="form-group col-lg-8">
<label>Page Description</label>
<textarea class="form-control @if(env('ALLOW_USER_HTML') === true) ckeditor @endif" name="pageDescription" rows="3">{{ $page->littlelink_description ?? '' }}</textarea>
</div>
@endforeach
<button type="submit" class="mt-3 ml-3 btn btn-info">Submit</button>
</form>
@if(env('ALLOW_USER_HTML') === true)
<script src="https://cdn.ckeditor.com/ckeditor5/35.1.0/classic/ckeditor.js"></script>
<script>
ClassicEditor
.create(document.querySelector('.ckeditor'), {
toolbar: {
items: [
'exportPDF', 'exportWord', '|'
, 'findAndReplace', 'selectAll', '|'
, 'heading', '|'
, 'bold', 'italic', 'strikethrough', 'underline', 'code', 'subscript', 'superscript', 'removeFormat', '|'
, 'bulletedList', 'numberedList', 'todoList', '|'
, 'outdent', 'indent', '|'
, 'undo', 'redo'
, 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', 'highlight', '|'
, 'alignment', '|'
, 'link', 'blockQuote', '|'
, 'specialCharacters', 'horizontalLine', '|'
, 'textPartLanguage', '|'
]
, shouldNotGroupWhenFull: true
}
, fontFamily: {
options: [
'default'
, 'Arial, Helvetica, sans-serif'
, 'Courier New, Courier, monospace'
, 'Georgia, serif'
, 'Lucida Sans Unicode, Lucida Grande, sans-serif'
, 'Tahoma, Geneva, sans-serif'
, 'Times New Roman, Times, serif'
, 'Trebuchet MS, Helvetica, sans-serif'
, 'Verdana, Geneva, sans-serif'
]
, supportAllValues: true
},
fontSize: {
options: [ 10, 12, 14, 'default', 18, 20, 22 ],
supportAllValues: true
},
})
.catch(error => {
console.error(error);
});
</script>
@endif
</div>
</div>
</section>
@endsection @endsection

View File

@ -2,24 +2,27 @@
@section('content') @section('content')
<h2 class="mb-4"><i class="bi bi-person"> Profile</i></h2> @if($_SERVER['QUERY_STRING'] === '')
<section class="shadow text-gray-400">
<h3 class="mb-4 card-header"><i class="bi bi-person"> Account Settings</i></h3>
<div class="card-body p-0 p-md-3">
@foreach($profile as $profile) @foreach($profile as $profile)
<form action="{{ route('editProfile') }}" method="post"> {{-- <form action="{{ route('editProfile') }}" method="post">
@csrf @csrf
<div class="form-group col-lg-8"> <div class="form-group col-lg-8">
<h3>Name</h3> <h3>Name</h3>
<input type="text" class="form-control" name="name" value="{{ $profile->name }}" required> <input type="text" class="form-control" name="name" value="{{ $profile->name }}" required>
</div> </div>
<button type="Change " class="mt-3 ml-3 btn btn-info">Change name</button> <button type="Change " class="mt-3 ml-3 btn btn-info">Change name</button>
</form> </form><br><br> --}}
@if(env('REGISTER_AUTH') != 'verified' or auth()->user()->role == 'admin') @if(env('REGISTER_AUTH') != 'verified' or auth()->user()->role == 'admin')
<br><br><form action="{{ route('editProfile') }}" method="post"> <form action="{{ route('editProfile') }}" method="post">
@csrf @csrf
<div class="form-group col-lg-8"> <div class="form-group col-lg-8">
<h3>Email</h3> <h4>Email</h4>
<input type="email" class="form-control" name="email" value="{{ $profile->email }}" required> <input type="email" class="form-control" name="email" value="{{ $profile->email }}" required>
</div> </div>
<button type="Change " class="mt-3 ml-3 btn btn-info">Change email</button> <button type="Change " class="mt-3 ml-3 btn btn-info">Change email</button>
@ -29,7 +32,7 @@
<br><br><form action="{{ route('editProfile') }}" method="post"> <br><br><form action="{{ route('editProfile') }}" method="post">
@csrf @csrf
<div class="form-group col-lg-8"> <div class="form-group col-lg-8">
<h3>Password</h3> <h4>Password</h4>
<input type="password" name="password" class="form-control" placeholder="At least 8 characters" required> <input type="password" name="password" class="form-control" placeholder="At least 8 characters" required>
</div> </div>
<button type="Change " class="mt-3 ml-3 btn btn-info">Change password</button> <button type="Change " class="mt-3 ml-3 btn btn-info">Change password</button>
@ -37,8 +40,44 @@
@csrf @csrf
<br><br><div class="form-group col-lg-8"> <br><br><div class="form-group col-lg-8">
<h3>Role</h3> <h4>Role</h4>
<input type="text" class="form-control" value="{{ strtoupper($profile->role) }}" readonly> <input type="text" class="form-control" value="{{ strtoupper($profile->role) }}" readonly>
</div> </div>
<br><button class="mt-3 ml-3 btn btn-primary" style="margin-bottom:2rem;margin-top:2rem!important;background-color:tomato!important;border-color:tomato!important;"><a href="{{ url('/studio/profile/?delete')}}" style="color:#FFFFFF;"><i class="bi bi-exclamation-octagon-fill"></i> Delete your account</a></button>
</div>
</section>
@endforeach @endforeach
@endif
@if($_SERVER['QUERY_STRING'] === 'delete')
<center style="margin-top: 14%;">
<h2 style="text-decoration: underline;">You are about to delete your account!</h2>
<p>You are about to delete your account! This action cannot be undone.</p>
<div>
<button class="redButton mt-3 ml-3 btn btn-primary" style="width:10rem; background-color:tomato!important;border-color:tomato!important; filter: grayscale(100%);" disabled onclick="window.location.href = '{{ url('/studio/delete-user/') . "/" . Auth::id() }}';"><i class="bi bi-exclamation-diamond-fill"></i></button>
<button type="submit" class="mt-3 ml-3 btn btn-info"><a style="color:#fff;" href="{{ url('/studio/profile') }}">Cancel</a></button>
</div>
<script>
var seconds = 10;
var interval = setInterval(function() {
document.querySelector(".redButton").innerHTML = --seconds;
if (seconds <= 0)
clearInterval(interval);
}, 1000);
setTimeout(function(){
document.querySelector(".redButton").disabled = false;
document.querySelector(".redButton").innerHTML = 'Delete account';
document.querySelector(".redButton").style.filter = "none";
}, 10000);
</script>
</center>
@endif
@endsection @endsection

View File

@ -2,145 +2,174 @@
@section('content') @section('content')
@foreach($pages as $page) @foreach($pages as $page)
<h2 class="mb-4"><i class="bi bi-brush"> Select a theme</i></h2>
<form action="{{ route('editTheme') }}" enctype="multipart/form-data" method="post"> <section class=' shadow text-gray-400'>
@csrf <h3 class="mb-4 card-header"><i class="bi bi-brush"> Select a theme</i></h3>
<div class="card-body p-0 p-md-3">
<br><br><div class="form-group col-lg-8"> <section class="shadow text-gray-400"></section>
<div class="card-body p-0 p-md-3">
<form action="{{ route('editTheme') }}" enctype="multipart/form-data" method="post">
@csrf
<div class="form-group row">
<div class="col-8 col-md-4">
<select style="margin-left: 15px; margin-bottom: 20px;" class="form-control" name="theme" data-base-url="{{ url('') }}/@<?= Auth::user()->littlelink_name ?>">
<?php if ($handle = opendir('themes')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
echo '<option >'; print_r($entry); echo '</option>'; }}} ?>
<option selected>default</option>
</select>
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Apply</button>
</div>
</div>
{{-- <br><br><div class="form-group col-lg-8">
<h3>Current theme</h3> <h3>Current theme</h3>
@if(empty($page->theme)) @if(empty($page->theme))
<input type="text" class="form-control" value="default" readonly> <input type="text" class="form-control" value="default" readonly>
@else @else
<input type="text" class="form-control" value="{{ $page->theme }}" readonly> <input type="text" class="form-control" value="{{ $page->theme }}" readonly>
@endif
</div><br> --}}
<div id="result" style="left: 1%; position: relative; background-color:#2c2d3a; border-radius: 25px; min-width:300px; max-width:950px; box-shadow: 0 10px 20px -10px rgba(0,0,0, 0.6);">
<div style="padding:5%5%;">
<h3 align="center" style="color:white">Preview:</h3>
@if(env('USE_THEME_PREVIEW_IFRAME') === false or $page->littlelink_name == '')
<center><img style="width:95%;max-width:700px;argin-left:1rem!important;" src="@if(file_exists(base_path() . '/themes/' . $page->theme . '/preview.png')){{url('/themes/' . $page->theme . '/preview.png')}}@elseif($page->theme === 'default' or empty($page->theme)){{url('/littlelink/images/themes/default.png')}}@else{{url('/littlelink/images/themes/no-preview.png')}}@endif"></img></center>
@else
<iframe allowtransparency="true" id="frPreview" style="background: #FFFFFF;height:400px;" class='w-100' src="{{ url('') }}/@<?= Auth::user()->littlelink_name ?>">Your browser isn't compatible</iframe>
@endif @endif
</div><br>
<div id="result" style="left: 1%; position: relative; background-color:#2c2d3a; border-radius: 25px; min-width:300px; max-width:950px; box-shadow: 0 10px 20px -10px rgba(0,0,0, 0.6);">
<div style="padding:5%5%;">
<h3 align="center" style="color:white">Preview:</h3>
<center><img style="width:95%;max-width:700px;argin-left:1rem!important;" src="@if(file_exists(base_path() . '/themes/' . $page->theme . '/preview.png')){{url('/themes/' . $page->theme . '/preview.png')}}@elseif($page->theme === 'default' or empty($page->theme)){{url('/littlelink/images/themes/default.png')}}@else{{url('/littlelink/images/themes/no-preview.png')}}@endif"></img></center>
</div></div><br>
<div class="form-group col-lg-8">
<h3>Select a theme</h3>
<select class="form-control" name="theme">
<?php if ($handle = opendir('themes')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
echo '<option>'; print_r($entry); echo '</option>'; }}} ?>
<option>default</option>
</select>
</div> </div>
<button type="submit" class="mt-3 ml-3 btn btn-info">Update theme</button> </div><br>
</form>
</details>
@if(auth()->user()->role == 'admin')
</form>
</details>
@if(auth()->user()->role == 'admin')
@if(env('ENABLE_THEME_UPDATER') == 'true') @if(env('ENABLE_THEME_UPDATER') == 'true')
<style> <style>
details { details {
width: 65%; width: 65%;
margin-left: 15px; margin-left: 15px;
{{-- max-width: calc(100% - 20rem); --}}
position: relative; {
border: 1px solid #78909C; {
border-radius: 6px; -- max-width: calc(100% - 20rem);
background-color: #ECEFF1; --
color: #263238; }
transition: background-color .15s; }
> :last-child { position: relative;
margin-bottom: 1rem; border: 1px solid #78909C;
} border-radius: 6px;
background-color: #ECEFF1;
&::before { color: #263238;
width: 100%; transition: background-color .15s;
height: 100%;
content: ''; > :last-child {
position: absolute; margin-bottom: 1rem;
top: 0; }
left: 0;
border-radius: inherit; &::before {
opacity: .15; width: 100%;
box-shadow: 0 .25em .5em #263238; height: 100%;
pointer-events: none; content: '';
transition: opacity .2s; position: absolute;
z-index: -1; top: 0;
} left: 0;
border-radius: inherit;
&[open] { opacity: .15;
background-color: #FFF; box-shadow: 0 .25em .5em #263238;
pointer-events: none;
&::before { transition: opacity .2s;
opacity: .6; z-index: -1;
}
&[open] {
background-color: #FFF;
&::before {
opacity: .6;
}
}
} }
}
}
summary { summary {
padding: 0.375rem 0.75rem; padding: 0.375rem 0.75rem;
width: 100%; width: 100%;
display: block; display: block;
position: relative; position: relative;
font-size: 1.33em; font-size: 1.33em;
font-weight: bold; font-weight: bold;
cursor: pointer; cursor: pointer;
&::before, &::before,
&::after { &::after {
width: .75em; width: .75em;
height: 2px; height: 2px;
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 0; right: 0;
content: ''; content: '';
background-color: currentColor; background-color: currentColor;
text-align: right; text-align: right;
transform: translateY(-50%); transform: translateY(-50%);
transition: transform .2s ease-in-out; transition: transform .2s ease-in-out;
} }
&::after { &::after {
transform: translateY(-50%) rotate(90deg); transform: translateY(-50%) rotate(90deg);
[open] & { [open] & {
transform: translateY(-50%) rotate(180deg); transform: translateY(-50%) rotate(180deg);
}
}
&::-webkit-details-marker {
display: none;
}
} }
}
&::-webkit-details-marker {
display: none;
}
}
table, th, td {
border:1px solid black;
}
.updatespin { table,
animation: upspin 1s linear infinite; th,
display:inline-block; td {
} border: 1px solid black;
}
@keyframes upspin { .updatespin {
100% { animation: upspin 1s linear infinite;
transform: rotate(360deg) display: inline-block;
} }
}
@keyframes upspin {
100% {
transform: rotate(360deg)
}
}
</style> </style>
<br><br><br> <br><br><br>
<details> <details>
<summary><i class="bi bi-caret-down-fill"></i> Theme updater </summary> <summary><i class="bi bi-caret-down-fill"></i> Theme updater </summary>
<div class="content" style="padding:10px;"> <div class="content" style="padding:10px;">
<table> <table>
<tr> <tr>
<th style="width:85%;">Theme name:</th> <th style="width:85%;">Theme name:</th>
<th style="width: 15%;">Update status:</th> <th style="width: 15%;">Update status:</th>
<th>Version:&nbsp;</th> <th>Version:&nbsp;</th>
</tr> </tr>
<?php <?php
if ($handle = opendir('themes')) { if ($handle = opendir('themes')) {
while (false !== ($entry = readdir($handle))) { while (false !== ($entry = readdir($handle))) {
@ -149,7 +178,10 @@ table, th, td {
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md'); $text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
$pattern = '/Theme Version:.*/'; $pattern = '/Theme Version:.*/';
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE); preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
$verNr = substr($matches[0][0],15);} if(sizeof($matches) > 0) {
$verNr = substr($matches[0][0],15);
}
}
$themeVe = NULL; $themeVe = NULL;
@ -210,163 +242,190 @@ table, th, td {
echo '<th>' . $verNr . '</th>'; echo '<th>' . $verNr . '</th>';
echo '</tr>';} echo '</tr>';}
}} ?> }} ?>
</table> </table>
</div> </div>
<a href="{{url('update/theme')}}" onclick="updateicon()" class="mt-3 ml-3 btn btn-info row"><span id="updateicon" class=""><i class="bi bi-arrow-repeat"></i></span> Update all themes</a><br><br> <a href="{{url('update/theme')}}" onclick="updateicon()" class="mt-3 ml-3 btn btn-info row"><span id="updateicon" class=""><i class="bi bi-arrow-repeat"></i></span> Update all themes</a><br><br>
<script>function updateicon() { var element = document.getElementById("updateicon"); element.classList.add("updatespin");}</script> <script>
function updateicon() {
var element = document.getElementById("updateicon");
element.classList.add("updatespin");
}
</script>
</details> </details>
<?php <?php
try{ if($GLOBALS['updateAv'] == true) echo '<img style="padding-left:40px; padding-top:15px; scale: 1.5;" src="https://img.shields.io/static/v1?label=&message=A theme needs updating&color=brightgreen">'; try{ if($GLOBALS['updateAv'] == true) echo '<img style="padding-left:40px; padding-top:15px; scale: 1.5;" src="https://img.shields.io/static/v1?label=&message=A theme needs updating&color=brightgreen">';
}catch(Exception $ex){} }catch(Exception $ex){}
?> ?>
@endif @endif
<br><br><br> <br><br><br>
<form action="{{ route('editTheme') }}" enctype="multipart/form-data" method="post"> <form action="{{ route('editTheme') }}" enctype="multipart/form-data" method="post">
@csrf @csrf
<h3>Upload themes</h3> <h3>Upload themes</h3>
<div style="display: none;" class="form-group col-lg-8"> <div style="display: none;" class="form-group col-lg-8">
<select class="form-control" name="theme"> <select class="form-control" name="theme">
<option>{{ $page->theme }}</option> <option>{{ $page->theme }}</option>
</select> </select>
<br> <br>
</div> </div>
<div class="form-group col-lg-8"> <div class="form-group col-lg-8">
<label>Upload theme</label> <label>Upload theme</label>
<input type="file" accept=".zip" class="form-control-file" name="zip"> <input type="file" accept=".zip" class="form-control-file" name="zip">
</div> </div>
<style>.deltheme{color:tomato;font-size:120%;}.deltheme:hover{color:red;text-decoration:underline;}</style> <style>
<a class="deltheme" href="{{ url('/panel/theme') }}">&emsp; Delete themes</a> .deltheme {
<div class="row"> color: tomato;
font-size: 120%;
}
.deltheme:hover {
color: red;
text-decoration: underline;
}
</style>
<div class="row">
<button type="submit" class="mt-3 ml-3 btn btn-info">Upload theme</button> <button type="submit" class="mt-3 ml-3 btn btn-info">Upload theme</button>
<button class="mt-3 ml-3 btn btn-primary" style="background-color:tomato!important;border-color:tomato!important;" title="Delete themes"><a href="{{ url('/panel/theme') }}" target="_blank" style="color:#FFFFFF;">Delete themes</a></button>
<button class="mt-3 ml-3 btn btn-primary" title="Download more themes"><a href="https://littlelink-custom.com/themes.php" target="_blank" style="color:#FFFFFF;">Download themes</a></button> <button class="mt-3 ml-3 btn btn-primary" title="Download more themes"><a href="https://littlelink-custom.com/themes.php" target="_blank" style="color:#FFFFFF;">Download themes</a></button>
</div> </div>
</form> </form>
</details> </details>
@push('sidebar-scripts')
<script> <script>
class Accordion { $(function() {
constructor(el) { $('select[name=theme]').on('change', function() {
// Store the <details> element var s = $(this).data('base-url') + "?t=" + $(this).val();
this.el = el; $("#frPreview").prop('src', s);
// Store the <summary> element })
this.summary = el.querySelector('summary');
// Store the <div class="content"> element
this.content = el.querySelector('.content');
// Store the animation object (so we can cancel it if needed)
this.animation = null;
// Store if the element is closing
this.isClosing = false;
// Store if the element is expanding
this.isExpanding = false;
// Detect user clicks on the summary element
this.summary.addEventListener('click', (e) => this.onClick(e));
}
onClick(e) {
// Stop default behaviour from the browser
e.preventDefault();
// Add an overflow on the <details> to avoid content overflowing
this.el.style.overflow = 'hidden';
// Check if the element is being closed or is already closed
if (this.isClosing || !this.el.open) {
this.open();
// Check if the element is being openned or is already open
} else if (this.isExpanding || this.el.open) {
this.shrink();
}
}
shrink() {
// Set the element as "being closed"
this.isClosing = true;
// Store the current height of the element
const startHeight = `${this.el.offsetHeight}px`;
// Calculate the height of the summary
const endHeight = `${this.summary.offsetHeight}px`;
// If there is already an animation running
if (this.animation) {
// Cancel the current animation
this.animation.cancel();
}
// Start a WAAPI animation
this.animation = this.el.animate({
// Set the keyframes from the startHeight to endHeight
height: [startHeight, endHeight]
}, {
duration: 400,
easing: 'ease-out'
}); });
// When the animation is complete, call onAnimationFinish()
this.animation.onfinish = () => this.onAnimationFinish(false);
// If the animation is cancelled, isClosing variable is set to false
this.animation.oncancel = () => this.isClosing = false;
}
open() { class Accordion {
// Apply a fixed height on the element constructor(el) {
this.el.style.height = `${this.el.offsetHeight}px`; // Store the <details> element
// Force the [open] attribute on the details element this.el = el;
this.el.open = true; // Store the <summary> element
// Wait for the next frame to call the expand function this.summary = el.querySelector('summary');
window.requestAnimationFrame(() => this.expand()); // Store the <div class="content"> element
} this.content = el.querySelector('.content');
expand() { // Store the animation object (so we can cancel it if needed)
// Set the element as "being expanding" this.animation = null;
this.isExpanding = true; // Store if the element is closing
// Get the current fixed height of the element this.isClosing = false;
const startHeight = `${this.el.offsetHeight}px`; // Store if the element is expanding
// Calculate the open height of the element (summary height + content height) this.isExpanding = false;
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`; // Detect user clicks on the summary element
this.summary.addEventListener('click', (e) => this.onClick(e));
// If there is already an animation running }
if (this.animation) {
// Cancel the current animation onClick(e) {
this.animation.cancel(); // Stop default behaviour from the browser
e.preventDefault();
// Add an overflow on the <details> to avoid content overflowing
this.el.style.overflow = 'hidden';
// Check if the element is being closed or is already closed
if (this.isClosing || !this.el.open) {
this.open();
// Check if the element is being openned or is already open
} else if (this.isExpanding || this.el.open) {
this.shrink();
}
}
shrink() {
// Set the element as "being closed"
this.isClosing = true;
// Store the current height of the element
const startHeight = `${this.el.offsetHeight}px`;
// Calculate the height of the summary
const endHeight = `${this.summary.offsetHeight}px`;
// If there is already an animation running
if (this.animation) {
// Cancel the current animation
this.animation.cancel();
}
// Start a WAAPI animation
this.animation = this.el.animate({
// Set the keyframes from the startHeight to endHeight
height: [startHeight, endHeight]
}, {
duration: 400
, easing: 'ease-out'
});
// When the animation is complete, call onAnimationFinish()
this.animation.onfinish = () => this.onAnimationFinish(false);
// If the animation is cancelled, isClosing variable is set to false
this.animation.oncancel = () => this.isClosing = false;
}
open() {
// Apply a fixed height on the element
this.el.style.height = `${this.el.offsetHeight}px`;
// Force the [open] attribute on the details element
this.el.open = true;
// Wait for the next frame to call the expand function
window.requestAnimationFrame(() => this.expand());
}
expand() {
// Set the element as "being expanding"
this.isExpanding = true;
// Get the current fixed height of the element
const startHeight = `${this.el.offsetHeight}px`;
// Calculate the open height of the element (summary height + content height)
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
// If there is already an animation running
if (this.animation) {
// Cancel the current animation
this.animation.cancel();
}
// Start a WAAPI animation
this.animation = this.el.animate({
// Set the keyframes from the startHeight to endHeight
height: [startHeight, endHeight]
}, {
duration: 400
, easing: 'ease-out'
});
// When the animation is complete, call onAnimationFinish()
this.animation.onfinish = () => this.onAnimationFinish(true);
// If the animation is cancelled, isExpanding variable is set to false
this.animation.oncancel = () => this.isExpanding = false;
}
onAnimationFinish(open) {
// Set the open attribute based on the parameter
this.el.open = open;
// Clear the stored animation
this.animation = null;
// Reset isClosing & isExpanding
this.isClosing = false;
this.isExpanding = false;
// Remove the overflow hidden and the fixed height
this.el.style.height = this.el.style.overflow = '';
}
} }
// Start a WAAPI animation document.querySelectorAll('details').forEach((el) => {
this.animation = this.el.animate({ new Accordion(el);
// Set the keyframes from the startHeight to endHeight
height: [startHeight, endHeight]
}, {
duration: 400,
easing: 'ease-out'
}); });
// When the animation is complete, call onAnimationFinish()
this.animation.onfinish = () => this.onAnimationFinish(true);
// If the animation is cancelled, isExpanding variable is set to false
this.animation.oncancel = () => this.isExpanding = false;
}
onAnimationFinish(open) {
// Set the open attribute based on the parameter
this.el.open = open;
// Clear the stored animation
this.animation = null;
// Reset isClosing & isExpanding
this.isClosing = false;
this.isExpanding = false;
// Remove the overflow hidden and the fixed height
this.el.style.height = this.el.style.overflow = '';
}
}
document.querySelectorAll('details').forEach((el) => {
new Accordion(el);
});
</script> </script>
</div>
</section>
@endpush
@endif
@endif @endforeach
@endforeach
@endsection @endsection

View File

@ -4,7 +4,7 @@
<div class="container"> <div class="container">
<?php // Requests newest version from server and sets it as variable <?php // Requests newest version from server and sets it as variable
$Vgit = file_get_contents("https://version.littlelink-custom.com/"); $Vgit = file_get_contents("https://julianprieber.github.io/littlelink-custom/version.json");
// Requests current version from the local version file and sets it as variable // Requests current version from the local version file and sets it as variable
$Vlocal = file_get_contents(base_path("version.json")); $Vlocal = file_get_contents(base_path("version.json"));
@ -210,7 +210,5 @@ exit(); ?>
@endif @endif
@if("8" > phpversion()) <br><br><a style="background-color:tomato;color:#fff;border-radius:5px;" class="nav-link" href="{{ url('/studio/profile') }}" target=""><i class="bi bi-exclamation-circle-fill"></i> <strong>You are using an outdated version of PHP! Official support for this version will end soon.</strong></a> @endif
</div> </div>
@endpush @endpush

View File

@ -4,7 +4,9 @@ use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AdminController; use App\Http\Controllers\AdminController;
use App\Http\Controllers\UserController; use App\Http\Controllers\UserController;
use App\Http\Controllers\Auth\SocialLoginController;
use App\Http\Controllers\LinkTypeViewController;
use App\Http\Controllers\PagesController;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -29,7 +31,7 @@ if(file_exists(base_path('storage/app/ISINSTALLED'))){
// Disables routes if in Maintenance Mode // Disables routes if in Maintenance Mode
if(env('MAINTENANCE_MODE') != 'true' and !file_exists(base_path("storage/MAINTENANCE"))){ if(env('MAINTENANCE_MODE') != 'true' and !file_exists(base_path("storage/MAINTENANCE"))){
//Changes the homepage to a LittleLink Custom profile if set in the config //Changes the homepage to a littlelink Custom profile if set in the config
if(config('advanced-config.custom_home_url') != '') { if(config('advanced-config.custom_home_url') != '') {
$custom_home_page_url = config('advanced-config.custom_home_url'); $custom_home_page_url = config('advanced-config.custom_home_url');
} else { } else {
@ -61,8 +63,7 @@ Route::get('/panel/diagnose', function () {
//Public route //Public route
$custom_prefix = config('advanced-config.custom_url_prefix'); $custom_prefix = config('advanced-config.custom_url_prefix');
Route::get('/going/{id?}/{link?}', [UserController::class, 'clickNumber'])->where('link', '.*')->name('clickNumber'); Route::get('/going/{id?}/{link?}', [UserController::class, 'clickNumber'])->where('link', '.*')->name('clickNumber');
if (!str_contains(url()->full(), '@') and !in_array(url()->full(), [url('login'), url('register'), url('update'), url('update?error='), url('update?success='), url('update?finishing='), url('update?updating='), url('update?backups='), url('update?backup='), url('update?updating-windows='), url('updating'), url('backup')])) { Route::get('/' . $custom_prefix . '{littlelink}', [UserController::class, 'littlelink'])->name('littlelink');
Route::get('/' . $custom_prefix . '{littlelink}', [UserController::class, 'littlelink'])->name('littlelink');}
Route::get('/@{littlelink}', [UserController::class, 'littlelink'])->name('littlelink'); Route::get('/@{littlelink}', [UserController::class, 'littlelink'])->name('littlelink');
Route::get('/pages/{name}', [AdminController::class, 'pages'])->name('pages'); Route::get('/pages/{name}', [AdminController::class, 'pages'])->name('pages');
Route::get('/theme/@{littlelink}', [UserController::class, 'theme'])->name('theme'); Route::get('/theme/@{littlelink}', [UserController::class, 'theme'])->name('theme');
@ -74,9 +75,10 @@ Route::group([
if(env('FORCE_HTTPS') == 'true'){URL::forceScheme('https');} if(env('FORCE_HTTPS') == 'true'){URL::forceScheme('https');}
if(isset($_COOKIE['LinkCount'])){if($_COOKIE['LinkCount'] == '20'){$LinkPage = 'showLinks20';}elseif($_COOKIE['LinkCount'] == '30'){$LinkPage = 'showLinks30';}elseif($_COOKIE['LinkCount'] == 'all'){$LinkPage = 'showLinksAll';} else {$LinkPage = 'showLinks';}} else {$LinkPage = 'showLinks';} //Shows correct link number if(isset($_COOKIE['LinkCount'])){if($_COOKIE['LinkCount'] == '20'){$LinkPage = 'showLinks20';}elseif($_COOKIE['LinkCount'] == '30'){$LinkPage = 'showLinks30';}elseif($_COOKIE['LinkCount'] == 'all'){$LinkPage = 'showLinksAll';} else {$LinkPage = 'showLinks';}} else {$LinkPage = 'showLinks';} //Shows correct link number
Route::get('/studio/index', [UserController::class, 'index'])->name('studioIndex'); Route::get('/studio/index', [UserController::class, 'index'])->name('studioIndex');
Route::get('/studio/add-link', [UserController::class, 'showButtons'])->name('showButtons'); Route::get('/studio/add-link', [UserController::class, 'AddUpdateLink'])->name('showButtons');
Route::post('/studio/edit-link', [UserController::class, 'saveLink'])->name('addLink');
Route::get('/studio/edit-link/{id}', [UserController::class, 'AddUpdateLink'])->name('showLink');
Route::post('/studio/sort-link', [UserController::class, 'sortLinks'])->name('sortLinks'); Route::post('/studio/sort-link', [UserController::class, 'sortLinks'])->name('sortLinks');
Route::post('/studio/add-link', [UserController::class, 'addLink'])->name('addLink');
Route::get('/studio/links', [UserController::class, $LinkPage])->name($LinkPage); Route::get('/studio/links', [UserController::class, $LinkPage])->name($LinkPage);
Route::get('/studio/links/10', [UserController::class, 'showLinks'])->name('showLinks'); Route::get('/studio/links/10', [UserController::class, 'showLinks'])->name('showLinks');
Route::get('/studio/links/20', [UserController::class, 'showLinks20'])->name('showLinks20'); Route::get('/studio/links/20', [UserController::class, 'showLinks20'])->name('showLinks20');
@ -86,7 +88,6 @@ Route::get('/studio/theme', [UserController::class, 'showTheme'])->name('showThe
Route::post('/studio/theme', [UserController::class, 'editTheme'])->name('editTheme'); Route::post('/studio/theme', [UserController::class, 'editTheme'])->name('editTheme');
Route::get('/deleteLink/{id}', [UserController::class, 'deleteLink'])->name('deleteLink'); Route::get('/deleteLink/{id}', [UserController::class, 'deleteLink'])->name('deleteLink');
Route::get('/upLink/{up}/{id}', [UserController::class, 'upLink'])->name('upLink'); Route::get('/upLink/{up}/{id}', [UserController::class, 'upLink'])->name('upLink');
Route::get('/studio/edit-link/{id}', [UserController::class, 'showLink'])->name('showLink');
Route::post('/studio/edit-link/{id}', [UserController::class, 'editLink'])->name('editLink'); Route::post('/studio/edit-link/{id}', [UserController::class, 'editLink'])->name('editLink');
Route::get('/studio/button-editor/{id}', [UserController::class, 'showCSS'])->name('showCSS'); Route::get('/studio/button-editor/{id}', [UserController::class, 'showCSS'])->name('showCSS');
Route::post('/studio/button-editor/{id}', [UserController::class, 'editCSS'])->name('editCSS'); Route::post('/studio/button-editor/{id}', [UserController::class, 'editCSS'])->name('editCSS');
@ -95,43 +96,58 @@ Route::get('/studio/no_page_name', [UserController::class, 'showPage'])->name('s
Route::post('/studio/page', [UserController::class, 'editPage'])->name('editPage'); Route::post('/studio/page', [UserController::class, 'editPage'])->name('editPage');
Route::get('/studio/profile', [UserController::class, 'showProfile'])->name('showProfile'); Route::get('/studio/profile', [UserController::class, 'showProfile'])->name('showProfile');
Route::post('/studio/profile', [UserController::class, 'editProfile'])->name('editProfile'); Route::post('/studio/profile', [UserController::class, 'editProfile'])->name('editProfile');
Route::get('/studio/delete-user/{id}', [UserController::class, 'deleteUser'])->name('deleteUser')->middleware('verified');
Route::get('/studio/linkparamform_part/{typeid}/{linkid}', [LinkTypeViewController::class, 'getParamForm'])->name('linkparamform.part');
}); });
} }
//Social login route
Route::get('/social-auth/{provider}/callback', [SocialLoginController::class, 'providerCallback']);
Route::get('/social-auth/{provider}', [SocialLoginController::class, 'redirectToProvider'])->name('social.redirect');
//Admin route //Admin route
Route::group([ Route::group([
'middleware' => 'admin', 'middleware' => 'admin',
], function () { ], function () {
if(env('FORCE_HTTPS') == 'true'){URL::forceScheme('https');} if(env('FORCE_HTTPS') == 'true'){URL::forceScheme('https');}
Route::get('/panel/index', [AdminController::class, 'index'])->name('panelIndex'); Route::get('/panel/index', [AdminController::class, 'index'])->name('panelIndex');
Route::get('/panel/users/{type}', [AdminController::class, 'users'])->name('showUsers'); Route::get('/panel/users/{type}', [AdminController::class, 'users'])->name('showUsers');
Route::post('/panel/users/{name?}', [AdminController::class, 'searchUser'])->name('searchUser'); Route::post('/panel/users/{name?}', [AdminController::class, 'searchUser'])->name('searchUser');
Route::get('/panel/links/{id}', [AdminController::class, 'showLinksUser'])->name('showLinksUser'); Route::get('/panel/links/{id}', [AdminController::class, 'showLinksUser'])->name('showLinksUser');
Route::get('/panel/deleteLink/{id}', [AdminController::class, 'deleteLinkUser'])->name('deleteLinkUser'); Route::get('/panel/deleteLink/{id}', [AdminController::class, 'deleteLinkUser'])->name('deleteLinkUser');
Route::get('/panel/users/block/{block}/{id}', [AdminController::class, 'blockUser'])->name('blockUser'); Route::get('/panel/users/block/{block}/{id}', [AdminController::class, 'blockUser'])->name('blockUser');
Route::get('/panel/users/verify/-{verify}/{id}', [AdminController::class, 'verifyUser'])->name('verifyUser'); Route::get('/panel/users/verify/-{verify}/{id}', [AdminController::class, 'verifyUser'])->name('verifyUser');
Route::get('/panel/edit-user/{id}', [AdminController::class, 'showUser'])->name('showUser'); Route::get('/panel/edit-user/{id}', [AdminController::class, 'showUser'])->name('showUser');
Route::post('/panel/edit-user/{id}', [AdminController::class, 'editUser'])->name('editUser'); Route::post('/panel/edit-user/{id}', [AdminController::class, 'editUser'])->name('editUser');
Route::get('/panel/new-user', [AdminController::class, 'createNewUser'])->name('createNewUser'); Route::get('/panel/new-user', [AdminController::class, 'createNewUser'])->name('createNewUser');
Route::get('/panel/delete-user/{id}', [AdminController::class, 'deleteUser'])->name('deleteUser'); Route::get('/panel/delete-user/{id}', [AdminController::class, 'deleteUser'])->name('deleteUser');
Route::get('/panel/pages', [AdminController::class, 'showSitePage'])->name('showSitePage'); Route::get('/panel/pages', [AdminController::class, 'showSitePage'])->name('showSitePage');
Route::post('/panel/pages', [AdminController::class, 'editSitePage'])->name('editSitePage'); Route::post('/panel/pages', [AdminController::class, 'editSitePage'])->name('editSitePage');
Route::get('/panel/advanced-config', [AdminController::class, 'showFileEditor'])->name('showFileEditor'); Route::get('/panel/advanced-config', [AdminController::class, 'showFileEditor'])->name('showFileEditor');
Route::post('/panel/advanced-config', [AdminController::class, 'editAC'])->name('editAC'); Route::post('/panel/advanced-config', [AdminController::class, 'editAC'])->name('editAC');
Route::get('/panel/env', [AdminController::class, 'showFileEditor'])->name('showFileEditor'); Route::get('/panel/env', [AdminController::class, 'showFileEditor'])->name('showFileEditor');
Route::post('/panel/env', [AdminController::class, 'editENV'])->name('editENV'); Route::post('/panel/env', [AdminController::class, 'editENV'])->name('editENV');
Route::get('/panel/site', [AdminController::class, 'showSite'])->name('showSite'); Route::get('/panel/site', [AdminController::class, 'showSite'])->name('showSite');
Route::post('/panel/site', [AdminController::class, 'editSite'])->name('editSite'); Route::post('/panel/site', [AdminController::class, 'editSite'])->name('editSite');
Route::get('/panel/phpinfo', [AdminController::class, 'phpinfo'])->name('phpinfo'); Route::get('/panel/phpinfo', [AdminController::class, 'phpinfo'])->name('phpinfo');
Route::get('/panel/backups', [AdminController::class, 'showBackups'])->name('showBackups'); Route::get('/panel/backups', [AdminController::class, 'showBackups'])->name('showBackups');
Route::post('/panel/theme', [AdminController::class, 'deleteTheme'])->name('deleteTheme'); Route::post('/panel/theme', [AdminController::class, 'deleteTheme'])->name('deleteTheme');
Route::get('/panel/theme', [AdminController::class, 'showThemes'])->name('showThemes'); Route::get('/panel/theme', [AdminController::class, 'showThemes'])->name('showThemes');
Route::get('/update/theme', [AdminController::class, 'updateThemes'])->name('updateThemes'); Route::get('/update/theme', [AdminController::class, 'updateThemes'])->name('updateThemes');
Route::get('/update', function () {return view('update', []);}); Route::get('/update', function () {return view('update', []);});
Route::get('/backup', function () {return view('backup', []);}); Route::get('/backup', function () {return view('backup', []);});
Route::get('/updating', function (\Codedge\Updater\UpdaterManager $updater) { Route::group(['namespace'=>'App\Http\Controllers\Admin', 'prefix'=>'admin', 'as'=>'admin'],function() {
//Route::resource('/admin/linktype', LinkTypeController::class);
Route::resources([
'linktype'=>LinkTypeController::class
]);
});
Route::get('/updating', function (\Codedge\Updater\UpdaterManager $updater) {
// Check if new version is available // Check if new version is available
if($updater->source()->isNewVersionAvailable() and (file_exists(base_path("backups/CANUPDATE")) or env('SKIP_UPDATE_BACKUP') == true)) { if($updater->source()->isNewVersionAvailable() and (file_exists(base_path("backups/CANUPDATE")) or env('SKIP_UPDATE_BACKUP') == true)) {
@ -161,7 +177,7 @@ Route::get('/updating', function (\Codedge\Updater\UpdaterManager $updater) {
}); });
}); }); // ENd Admin authenticated routes
// Displays Maintenance Mode page // Displays Maintenance Mode page
if(env('MAINTENANCE_MODE') == 'true' or file_exists(base_path("storage/MAINTENANCE"))){ if(env('MAINTENANCE_MODE') == 'true' or file_exists(base_path("storage/MAINTENANCE"))){

View File

@ -88,4 +88,11 @@ ALLOW_CUSTOM_CODE_IN_THEMES=true
#ENABLE_THEME_UPDATER=Determines if the theme updater should be enabled or not, default is true. #ENABLE_THEME_UPDATER=Determines if the theme updater should be enabled or not, default is true.
#=ENABLE_THEME_UPDATER either true or false. #=ENABLE_THEME_UPDATER either true or false.
ENABLE_THEME_UPDATER=true ENABLE_THEME_UPDATER=true
#Needs to be configured first.
#Read more at: https://s.llc.ovh/social-login
ENABLE_SOCIAL_LOGIN=false
#Sets if a plain PNG or iframe should be used for the theme preview image
USE_THEME_PREVIEW_IFRAME=true

View File

@ -105,7 +105,7 @@ return [
// The URL prefix is the symbol that comes before a LittleLink URL. // The URL prefix is the symbol that comes before a LittleLink URL.
// For example the '@' in 'example.com/@admin'. // For example the '@' in 'example.com/@admin'.
// If empty no prefix is required. // If empty no prefix is required. Use with caution.
'custom_url_prefix' => '+', // The '@' prefix will always work regardless of this setting. 'custom_url_prefix' => '+', // The '@' prefix will always work regardless of this setting.

File diff suppressed because it is too large Load Diff

6
studio/js/ckeditor.js vendored Normal file

File diff suppressed because one or more lines are too long

21
studio/js/components/addlink.js vendored Normal file
View File

@ -0,0 +1,21 @@
// for use in add-link blade
$(function() {
LoadLinkTypeParams($("input[name='linktype_id']").val() , $("input[name=linkid]").val());
$('.doSelectLinkType').on('click', function() {
$("input[name='linktype_id']").val($(this).data('typeid'));
$("#btnLinkType").html($(this).data('typename'));
LoadLinkTypeParams($(this).data('typeid'), $("input[name=linkid]").val());
$('#SelectLinkType').hide();
$('body').removeClass('modal-open');
$('.modal-backdrop').remove();
});
function LoadLinkTypeParams($TypeId, $LinkId) {
$("#link_params").html(' <img width="70px" src="/img/loading.gif" />').load(`/studio/linkparamform_part/${$TypeId}/${$LinkId}`);
}
});

View File

@ -1,68 +1,80 @@
(function($) {
"use strict"; (function ($) {
var fullHeight = function() { "use strict";
$('.js-fullheight').css('height', $(window).height()); var fullHeight = function () {
$(window).resize(function(){
$('.js-fullheight').css('height', $(window).height());
});
}; $('.js-fullheight').css('height', $(window).height());
fullHeight(); $(window).resize(function () {
$('.js-fullheight').css('height', $(window).height());
});
$('#sidebarCollapse').on('click', function () { };
$('#sidebar').toggleClass('active'); fullHeight();
});
$('#sidebarCollapse').on('click', function () {
$.ajaxSetup({ $('#sidebar').toggleClass('active');
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
}); });
var sortableTbody = document.getElementById("links-table-body");
if (sortableTbody) { $.ajaxSetup({
const sortableLinkTable = Sortable.create(sortableTbody, { headers: {
handle: ".sortable-handle", 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
onChange: function(event) { }
}, });
store: { var sortableTbody = document.getElementById("links-table-body");
get: function (sortable) { if (sortableTbody) {
var order = linksTableOrders || ""; const sortableLinkTable = Sortable.create(sortableTbody, {
return order ? order.split('|') : []; handle: ".sortable-handle",
}, animation: 150,
set: function (sortable) { swapThreshold: 0.60,
const linkOrders = sortable.toArray(); ghostClass: 'bg-info',
const currentPage = sortableTbody.dataset.page || 1; onChange: function (event) {
const perPage = sortableTbody.dataset.perPage || 0; },
const formData = { store: {
'linkOrders': linkOrders, get: function (sortable) {
'currentPage': currentPage, var order = linksTableOrders || "";
'perPage': perPage, return order ? order.split('|') : [];
}; },
$.blockUI({ set: function (sortable) {
message: '<img width="70px" src="img/loading.gif" />', const linkOrders = sortable.toArray();
css: { const currentPage = sortableTbody.dataset.page || 1;
backgroundColor: 'transparent', const perPage = sortableTbody.dataset.perPage || 0;
border: 'none', const formData = {
color: '#444444', 'linkOrders': linkOrders,
} 'currentPage': currentPage,
}); 'perPage': perPage,
$.post("studio/sort-link", formData, function(response) { };
if (response.linkOrders) { // $.blockUI({
$.each(response.linkOrders, function(linkId, linkOrder){ // message: '<img width="70px" src="img/loading.gif" />',
$("#links-table-body tr[data-id='"+linkId+"']") // css: {
.find("td:eq(3)") // backgroundColor: 'transparent',
.html(linkOrder); // border: 'none',
}); // color: '#444444',
$.unblockUI(); // }
} else { // });
alert("Something went wrong! Please, Try again.")
} // VERY janky solution; have to fix later
}); var str = window.location.pathname;
} str = str.replace("/studio/links", "");
}
}); $.post(str + "/studio/sort-link", formData, function (response) {
} if (response.linkOrders) {
$.each(response.linkOrders, function (linkId, linkOrder) {
// $("#links-table-body div[data-id='"+linkId+"']")
// .find("div:eq(3)")
// .html(linkOrder);
});
//$.unblockUI();
} else {
alert("Something went wrong! Please, Try again.")
}
});
}
}
});
}
})(jQuery); })(jQuery);