Browse Source

Merge pull request #282 from linuxserver/2.1.0

2.1.0
pull/308/head
KodeStar 6 years ago
committed by GitHub
parent
commit
7a02986982
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 55
      app/Application.php
  2. 61
      app/Console/Commands/RegisterApp.php
  3. 12
      app/EnhancedApps.php
  4. 8
      app/Helper.php
  5. 95
      app/Http/Controllers/ItemController.php
  6. 6
      app/Http/Controllers/TagController.php
  7. 146
      app/Item.php
  8. 63
      app/Jobs/ProcessApps.php
  9. 2
      app/Providers/AppServiceProvider.php
  10. 154
      app/SupportedApps.php
  11. 11
      app/SupportedApps/AirSonic.php
  12. 14
      app/SupportedApps/Bazarr.php
  13. 12
      app/SupportedApps/Bitwarden.php
  14. 14
      app/SupportedApps/BookStack.php
  15. 12
      app/SupportedApps/Booksonic.php
  16. 11
      app/SupportedApps/Cardigann.php
  17. 9
      app/SupportedApps/Contracts/Applications.php
  18. 11
      app/SupportedApps/Contracts/Livestats.php
  19. 122
      app/SupportedApps/CouchPotato.php
  20. 109
      app/SupportedApps/Deluge.php
  21. 12
      app/SupportedApps/Dokuwiki.php
  22. 12
      app/SupportedApps/Duplicati.php
  23. 12
      app/SupportedApps/Emby.php
  24. 12
      app/SupportedApps/Flood.php
  25. 11
      app/SupportedApps/FreshRSS.php
  26. 12
      app/SupportedApps/Gitea.php
  27. 14
      app/SupportedApps/Glances.php
  28. 12
      app/SupportedApps/Grafana.php
  29. 12
      app/SupportedApps/Graylog.php
  30. 12
      app/SupportedApps/Headphones.php
  31. 12
      app/SupportedApps/HomeAssistant.php
  32. 12
      app/SupportedApps/Jackett.php
  33. 12
      app/SupportedApps/Jdownloader.php
  34. 11
      app/SupportedApps/Krusader.php
  35. 12
      app/SupportedApps/Lidarr.php
  36. 12
      app/SupportedApps/Mailcow.php
  37. 12
      app/SupportedApps/Mcmyadmin.php
  38. 12
      app/SupportedApps/Medusa.php
  39. 12
      app/SupportedApps/Monica.php
  40. 12
      app/SupportedApps/MusicBrainz.php
  41. 12
      app/SupportedApps/Mylar.php
  42. 12
      app/SupportedApps/Netdata.php
  43. 12
      app/SupportedApps/Nextcloud.php
  44. 11
      app/SupportedApps/NowShowing.php
  45. 84
      app/SupportedApps/Nzbget.php
  46. 12
      app/SupportedApps/Nzbhydra.php
  47. 12
      app/SupportedApps/Ombi.php
  48. 11
      app/SupportedApps/OpenMediaVault.php
  49. 12
      app/SupportedApps/Openhab.php
  50. 12
      app/SupportedApps/Opnsense.php
  51. 12
      app/SupportedApps/Pfsense.php
  52. 73
      app/SupportedApps/Pihole.php
  53. 12
      app/SupportedApps/Plex.php
  54. 77
      app/SupportedApps/Plexpy.php
  55. 12
      app/SupportedApps/Plexrequests.php
  56. 12
      app/SupportedApps/Portainer.php
  57. 80
      app/SupportedApps/Proxmox.php
  58. 12
      app/SupportedApps/Radarr.php
  59. 12
      app/SupportedApps/Rancher.php
  60. 95
      app/SupportedApps/Runeaudio.php
  61. 89
      app/SupportedApps/Sabnzbd.php
  62. 12
      app/SupportedApps/Sickrage.php
  63. 12
      app/SupportedApps/Sonarr.php
  64. 11
      app/SupportedApps/Syncthing.php
  65. 14
      app/SupportedApps/TVheadend.php
  66. 77
      app/SupportedApps/Tautulli.php
  67. 78
      app/SupportedApps/Traefik.php
  68. 169
      app/SupportedApps/Transmission.php
  69. 12
      app/SupportedApps/Ttrss.php
  70. 12
      app/SupportedApps/Unifi.php
  71. 12
      app/SupportedApps/Unraid.php
  72. 12
      app/SupportedApps/Virtualmin.php
  73. 11
      app/SupportedApps/Watcher3.php
  74. 11
      app/SupportedApps/WebTools.php
  75. 12
      app/SupportedApps/Webmin.php
  76. 11
      app/SupportedApps/pyLoad.php
  77. 12
      app/SupportedApps/ruTorrent.php
  78. 4
      composer.json
  79. 992
      composer.lock
  80. 5
      config/app.php
  81. 91
      config/github.php
  82. 41
      database/migrations/2018_10_18_110905_create_applications_table.php
  83. 32
      database/migrations/2018_10_23_132008_add_class_to_items_table.php
  84. 36
      database/migrations/2018_10_31_191604_create_jobs_table.php
  85. 4599
      package-lock.json
  86. 2
      package.json
  87. 85
      public/css/app.css
  88. 2
      public/js/app.js
  89. 4
      public/mix-manifest.json
  90. 2
      resources/assets/js/app.js
  91. 46
      resources/assets/sass/_app.scss
  92. 13
      resources/assets/sass/_rune.scss
  93. 4
      resources/lang/en/app.php
  94. 56
      resources/lang/fi/app.php
  95. 63
      resources/lang/sv/app.php
  96. 4
      resources/views/item.blade.php
  97. 13
      resources/views/items/enable.blade.php
  98. 64
      resources/views/items/form.blade.php
  99. 1
      resources/views/items/list.blade.php
  100. 21
      resources/views/items/preview.blade.php

55
app/Application.php

@ -0,0 +1,55 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Application extends Model
{
public $incrementing = false;
protected $primaryKey = 'appid';
//
public function icon()
{
return $this->icon;
}
public function iconView()
{
return asset('storage/'.$this->icon);
}
public function defaultColour()
{
// check if light or dark
if($this->tile_background == 'light') return '#fafbfc';
return '#161b1f';
}
public function class()
{
$name = $this->name;
$name = preg_replace('/\PL/u', '', $name);
$class = '\App\SupportedApps\\'.$name.'\\'.$name;
return $class;
}
public static function applist()
{
$list = [];
$all = self::all();
$list['null'] = 'None';
foreach($all as $app) {
$name = $app->name;
$name = preg_replace('/\PL/u', '', $name);
$list['\App\SupportedApps\\'.$name.'\\'.$name] = $app->name;
}
return $list;
}
}

61
app/Console/Commands/RegisterApp.php

@ -0,0 +1,61 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Application;
use App\SupportedApps;
class RegisterApp extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'register:app {folder}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Add a local app to the registry';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$folder = $this->argument('folder');
$json = app_path('SupportedApps/'.$folder.'/app.json');
if(file_exists($json)) {
$app = json_decode(file_get_contents($json));
$exists = Application::find($app->appid);
if($exists) {
$this->error('This app is already registered');
exit;
}
// Doesn't exist so add it
SupportedApps::saveApp($app, new Application);
$this->info("Application Added - ".$app->name." - ".$app->appid);
} else {
$this->error('Could not find '.$json);
}
}
}

12
app/EnhancedApps.php

@ -0,0 +1,12 @@
<?php namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
interface EnhancedApps
{
public function test();
public function livestats();
public function url($endpoint);
}

8
app/Helper.php

@ -34,3 +34,11 @@ function title_color($hex)
return ' white';
}
}
function className($name)
{
$name = preg_replace('/\PL/u', '', $name);
return $name;
}

95
app/Http/Controllers/ItemController.php

@ -2,12 +2,16 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Artisan;
use App\Application;
use App\Item;
use App\Setting;
use App\User;
use App\SupportedApps\Nzbget;
use GrahamCampbell\GitHub\Facades\GitHub;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\SupportedApps;
use App\Jobs\ProcessApps;
class ItemController extends Controller
{
@ -42,8 +46,7 @@ class ItemController extends Controller
}
}
/**
/**
* Pin item on the dashboard.
*
* @return \Illuminate\Http\Response
@ -154,6 +157,12 @@ class ItemController extends Controller
'user_id' => $current_user->id
]);
if($request->input('class') === 'null') {
$request->merge([
'class' => null,
]);
}
//die(print_r($request->input('config')));
@ -222,6 +231,13 @@ class ItemController extends Controller
'user_id' => $current_user->id
]);
if($request->input('class') === 'null') {
$request->merge([
'class' => null,
]);
}
$item = Item::find($id);
$item->update($request->all());
@ -273,16 +289,6 @@ class ItemController extends Controller
->with('success',__('app.alert.success.item_restored'));
}
public function isSupportedAppByKey($app)
{
$output = false;
$all_supported = Item::supportedList();
if(array_key_exists($app, $all_supported)) {
$output = new $all_supported[$app];
}
return $output;
}
/**
* Return details for supported apps
*
@ -291,19 +297,25 @@ class ItemController extends Controller
public function appload(Request $request)
{
$output = [];
$app = $request->input('app');
if(($app_details = $this->isSupportedAppByKey($app)) !== false) {
// basic details
$output['icon'] = $app_details->icon();
$output['colour'] = $app_details->defaultColour();
// live details
if($app_details instanceof \App\SupportedApps\Contracts\Livestats) {
$output['config'] = $app_details->configDetails();
} else {
$output['config'] = null;
}
$appname = $request->input('app');
//die($appname);
$app_details = Application::where('name', $appname)->firstOrFail();
$appclass = $app_details->class();
$app = new $appclass;
// basic details
$output['icon'] = $app_details->icon();
$output['name'] = $app_details->name;
$output['iconview'] = $app_details->iconView();
$output['colour'] = $app_details->defaultColour();
$output['class'] = $appclass;
// live details
if($app instanceof \App\EnhancedApps) {
$output['config'] = className($app_details->name).'.config';
} else {
$output['config'] = null;
}
return json_encode($output);
@ -318,25 +330,34 @@ class ItemController extends Controller
$app_details = new $app();
$app_details->config = (object)$data;
$app_details->testConfig();
$app_details->test();
}
public function getStats($id)
{
$item = Item::find($id);
$config = json_decode($item->description);
if(isset($config->type)) {
$config->url = $item->url;
if(isset($config->override_url) && !empty($config->override_url)) {
$config->url = $config->override_url;
}
$app_details = new $config->type;
$app_details->config = $config;
echo $app_details->executeConfig();
$config = $item->getconfig();
if(isset($item->class)) {
$application = new $item->class;
$application->config = $config;
echo $application->livestats();
}
}
public function checkAppList()
{
ProcessApps::dispatch();
$route = route('items.index');
return redirect($route)
->with('success', __('app.alert.success.updating'));
}
}

6
app/Http/Controllers/TagController.php

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Item;
use App\User;
use DB;
class TagController extends Controller
@ -62,10 +63,13 @@ class TagController extends Controller
$slug = str_slug($request->title, '-');
$current_user = User::currentUser();
// set item type to tag
$request->merge([
'type' => '1',
'url' => $slug
'url' => $slug,
'user_id' => $current_user->id
]);
//die(print_r($request->all()));
Item::create($request->all());

146
app/Item.php

@ -24,7 +24,7 @@ class Item extends Model
//
protected $fillable = [
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type', 'user_id'
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type', 'class', 'user_id'
];
/**
@ -34,82 +34,6 @@ class Item extends Model
*/
protected $dates = ['deleted_at'];
public static function supportedList()
{
return [
'AirSonic' => \App\SupportedApps\AirSonic::class,
'Cardigann' => \App\SupportedApps\Cardigann::class,
'CouchPotato' => \App\SupportedApps\CouchPotato::class,
'Bazarr' => \App\SupportedApps\Bazarr::class,
'Bitwarden' => \App\SupportedApps\Bitwarden::class,
'Booksonic' => \App\SupportedApps\Booksonic::class,
'BookStack' => \App\SupportedApps\BookStack::class,
'Deluge' => \App\SupportedApps\Deluge::class,
'Dokuwiki' => \App\SupportedApps\Dokuwiki::class,
'Duplicati' => \App\SupportedApps\Duplicati::class,
'Emby' => \App\SupportedApps\Emby::class,
'Flood' => \App\SupportedApps\Flood::class,
'FreshRSS' => \App\SupportedApps\FreshRSS::class,
'Gitea' => \App\SupportedApps\Gitea::class,
'Glances' => \App\SupportedApps\Glances::class,
'Grafana' => \App\SupportedApps\Grafana::class,
'Graylog' => \App\SupportedApps\Graylog::class,
'Headphones' => \App\SupportedApps\Headphones::class,
'Home Assistant' => \App\SupportedApps\HomeAssistant::class,
'Jackett' => \App\SupportedApps\Jackett::class,
'Jdownloader' => \App\SupportedApps\Jdownloader::class,
'Krusader' => \App\SupportedApps\Krusader::class,
'LibreNMS' => \App\SupportedApps\LibreNMS::class,
'Lidarr' => \App\SupportedApps\Lidarr::class,
'Mailcow' => \App\SupportedApps\Mailcow::class,
'Mcmyadmin' => \App\SupportedApps\Mcmyadmin::class,
'Medusa' => \App\SupportedApps\Medusa::class,
'Monica' => \App\SupportedApps\Monica::class,
'MusicBrainz' => \App\SupportedApps\MusicBrainz::class,
'Mylar' => \App\SupportedApps\Mylar::class,
'NZBGet' => \App\SupportedApps\Nzbget::class,
'Netdata' => \App\SupportedApps\Netdata::class,
'Nextcloud' => \App\SupportedApps\Nextcloud::class,
'Now Showing' => \App\SupportedApps\NowShowing::class,
'Nzbhydra' => \App\SupportedApps\Nzbhydra::class,
'OPNSense' => \App\SupportedApps\Opnsense::class,
'Ombi' => \App\SupportedApps\Ombi::class,
'Openhab' => \App\SupportedApps\Openhab::class,
'OpenMediaVault' => \App\SupportedApps\OpenMediaVault::class,
'Pihole' => \App\SupportedApps\Pihole::class,
'Plex' => \App\SupportedApps\Plex::class,
'Plexpy' => \App\SupportedApps\Plexpy::class,
'Plexrequests' => \App\SupportedApps\Plexrequests::class,
'Portainer' => \App\SupportedApps\Portainer::class,
'Proxmox' => \App\SupportedApps\Proxmox::class,
'Radarr' => \App\SupportedApps\Radarr::class,
'Rancher' => \App\SupportedApps\Rancher::class,
'Runeaudio' => \App\SupportedApps\Runeaudio::class,
'Sabnzbd' => \App\SupportedApps\Sabnzbd::class,
'Sickrage' => \App\SupportedApps\Sickrage::class,
'Sonarr' => \App\SupportedApps\Sonarr::class,
'Syncthing' => \App\SupportedApps\Syncthing::class,
'Tautulli' => \App\SupportedApps\Tautulli::class,
'Transmission' => \App\SupportedApps\Transmission::class,
'Traefik' => \App\SupportedApps\Traefik::class,
'tt-rss' => \App\SupportedApps\Ttrss::class,
'TVheadend' => \App\SupportedApps\TVheadend::class,
'UniFi' => \App\SupportedApps\Unifi::class,
'unRAID' => \App\SupportedApps\Unraid::class,
'pfSense' => \App\SupportedApps\Pfsense::class,
'pyLoad' => \App\SupportedApps\pyLoad::class,
'ruTorrent' => \App\SupportedApps\ruTorrent::class,
'Virtualmin' => \App\SupportedApps\Virtualmin::class,
'Watcher3' => \App\SupportedApps\Watcher3::class,
'Webmin' => \App\SupportedApps\Webmin::class,
'WebTools' => \App\SupportedApps\WebTools::class,
];
}
public static function supportedOptions()
{
return array_keys(self::supportedList());
}
/**
* Scope a query to only include pinned items.
*
@ -121,24 +45,6 @@ class Item extends Model
return $query->where('pinned', 1);
}
public function getConfigAttribute()
{
$output = null;
$view = null;
if(isset($this->description) && !empty($this->description)){
$output = json_decode($this->description);
$output = is_object($output) ? $output : new \stdClass();
if(isset($output->type) && !empty($output->type)) {
$class = $output->type;
$sap = new $class();
$view = $sap->configDetails();
$output->view = $view;
}
if(!isset($output->dataonly)) $output->dataonly = '0';
}
return (object)$output;
}
public static function checkConfig($config)
{
if(empty($config)) {
@ -232,6 +138,56 @@ class Item extends Model
return $query->where('type', $typeid);
}
public function enhanced()
{
if(isset($this->class) && !empty($this->class)) {
$app = new $this->class;
} else {
return false;
}
return (bool)($app instanceof \App\EnhancedApps);
}
public function enabled()
{
if($this->enhanced()) {
$config = $this->getconfig();
if($config) {
return (bool) $config->enabled;
}
}
return false;
}
public function getconfig()
{
$explode = explode('\\', $this->class);
if(!isset($this->description) || empty($this->description)) {
$config = new \stdClass;
$config->name = end($explode);
$config->enabled = false;
return $config;
}
$config = json_decode($this->description);
$config->name = end($explode);
$config->url = $this->url;
if(isset($config->override_url) && !empty($config->override_url)) {
$config->url = $config->override_url;
}
return $config;
}
/**
* Get the user that owns the item.
*/

63
app/Jobs/ProcessApps.php

@ -0,0 +1,63 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Application;
use App\SupportedApps;
class ProcessApps implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$localapps = Application::all();
$list = json_decode(SupportedApps::getList()->getBody());
$validapps = [];
foreach($list->apps as $app) {
$validapps[] = $app->appid;
if(!file_exists(app_path('SupportedApps/'.className($app->name)))) {
SupportedApps::getFiles($app);
$application = new Application;
SupportedApps::saveApp($app, $application);
} else {
// check if there has been an update for this app
$localapp = $localapps->where('appid', $app->appid)->first();
if($localapp) {
if($localapp->sha !== $app->sha) {
SupportedApps::getFiles($app);
SupportedApps::saveApp($app, $localapp);
}
} else {
SupportedApps::getFiles($app);
$application = new Application;
SupportedApps::saveApp($app, $application);
}
}
}
//$delete = Application::whereNotIn('appid', $validapps)->delete(); // delete any apps not in list
// removed the delete so local apps can be added
}
}

2
app/Providers/AppServiceProvider.php

@ -91,6 +91,8 @@ class AppServiceProvider extends ServiceProvider
});
$this->app['view']->addNamespace('SupportedApps', app_path('SupportedApps'));
if (env('FORCE_HTTPS') === true) {
\URL::forceScheme('https');

154
app/SupportedApps.php

@ -0,0 +1,154 @@
<?php namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
abstract class SupportedApps
{
protected $jar = false;
protected $method = 'GET';
protected $error;
public function appTest($url, $attrs = [], $overridevars=false)
{
$res = $this->execute($url, $attrs);
if($res == null) {
return (object)[
'code' => null,
'status' => $this->error,
'response' => 'Connection failed',
];
}
switch($res->getStatusCode()) {
case 200:
$status = 'Successfully communicated with the API';
break;
case 401:
$status = 'Failed: Invalid credentials';
break;
case 404:
$status = 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
$status = 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
return (object)[
'code' => $res->getStatusCode(),
'status' => $status,
'response' => $res->getBody(),
];
}
public function execute($url, $attrs = [], $overridevars=false, $overridemethod=false)
{
$res = null;
$vars = ($overridevars !== false) ?
$overridevars : [
'http_errors' => false,
'timeout' => 15,
'connect_timeout' => 15,
];
$client = new Client($vars);
$method = ($overridemethod !== false) ? $overridemethod : $this->method;
try {
return $client->request($method, $url, $attrs);
} catch (\GuzzleHttp\Exception\ConnectException $e) {
Log::error("Connection refused");
Log::debug($e->getMessage());
$this->error = "Connection refused - ".(string) $e->getMessage();
} catch (\GuzzleHttp\Exception\ServerException $e) {
Log::debug($e->getMessage());
$this->error = (string) $e->getResponse()->getBody();
}
$this->error = 'General error connecting with API';
return $res;
}
public function login()
{
}
public function normaliseurl($url, $addslash=true)
{
$url = rtrim($url, '/');
if($addslash) $url .= '/';
return $url;
}
public function getLiveStats($status, $data)
{
$className = get_class($this);
$explode = explode('\\', $className);
$name = end($explode);
$html = view('SupportedApps::'.$name.'.livestats', $data)->with('data', $data)->render();
return json_encode(['status' => $status, 'html' => $html]);
//return
}
public static function getList()
{
$list_url = 'https://apps.heimdall.site/list';
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
return $client->request('GET', $list_url);
}
public static function getFiles($app)
{
$zipurl = $app->files;
$client = new Client(['http_errors' => false, 'timeout' => 60, 'connect_timeout' => 15]);
$res = $client->request('GET', $zipurl);
if(!file_exists(app_path('SupportedApps'))) {
mkdir(app_path('SupportedApps'), 0777, true);
}
$src = app_path('SupportedApps/'.className($app->name).'.zip');
file_put_contents($src, $res->getBody());
$zip = new \ZipArchive();
$x = $zip->open($src); // open the zip file to extract
if ($x === true) {
$zip->extractTo(app_path('SupportedApps')); // place in the directory with same name
$zip->close();
unlink($src); //Deleting the Zipped file
}
}
public static function saveApp($details, $app)
{
$img_src = app_path('SupportedApps/'.className($details->name).'/'.$details->icon);
$img_dest = public_path('storage/supportedapps/'.$details->icon);
//die("i: ".$img_src);
copy($img_src, $img_dest);
$app->appid = $details->appid;
$app->name = $details->name;
$app->sha = $details->sha ?? null;
$app->icon = 'supportedapps/'.$details->icon;
$app->website = $details->website;
$app->license = $details->license;
$app->description = $details->description;
$appclass = $app->class();
$application = new $appclass;
$enhanced = (bool)($application instanceof \App\EnhancedApps);
$app->enhanced = $enhanced;
$app->tile_background = $details->tile_background;
$app->save();
}
}

11
app/SupportedApps/AirSonic.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class AirSonic implements Contracts\Applications {
public function defaultColour()
{
return '#08F';
}
public function icon()
{
return 'supportedapps/airsonic.png';
}
}

14
app/SupportedApps/Bazarr.php

@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class Bazarr implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/bazarr.png';
}
}

12
app/SupportedApps/Bitwarden.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Bitwarden implements Contracts\Applications {
public function defaultColour()
{
return '#3c8dbc';
}
public function icon()
{
return 'supportedapps/bitwarden.png';
}
}

14
app/SupportedApps/BookStack.php

@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class BookStack implements Contracts\Applications {
public function defaultColour()
{
return '#02679E';
}
public function icon()
{
return 'supportedapps/bookstack.png';
}
}

12
app/SupportedApps/Booksonic.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Booksonic implements Contracts\Applications {
public function defaultColour()
{
return '#58a';
}
public function icon()
{
return 'supportedapps/booksonic.png';
}
}

11
app/SupportedApps/Cardigann.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Cardigann implements Contracts\Applications {
public function defaultColour()
{
return '#753';
}
public function icon()
{
return 'supportedapps/cardigann.png';
}
}

9
app/SupportedApps/Contracts/Applications.php

@ -1,9 +0,0 @@
<?php namespace App\SupportedApps\Contracts;
interface Applications {
public function defaultColour();
public function icon();
}

11
app/SupportedApps/Contracts/Livestats.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps\Contracts;
interface Livestats {
public function configDetails();
public function testConfig();
public function executeConfig();
}

122
app/SupportedApps/CouchPotato.php

@ -1,122 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
class CouchPotato implements Contracts\Applications, Contracts\Livestats
{
private $_client;
public function __construct()
{
$this->_client = new Client(
['http_errors' => false,
'timeout' => 10]
);
}
public function defaultColour()
{
return '#363840';
}
public function icon()
{
return 'supportedapps/couchpotato.png';
}
public function configDetails()
{
return 'couchpotato';
}
public function testConfig()
{
$res = $this->sendRequest();
if ($res == null) {
echo 'CouchPotato connection failed';
return;
}
switch($res->getStatusCode()) {
case 200:
echo "Successfully connected to CouchPotato";
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and includes the port';
break;
case 409:
echo 'Failed: Incorrect session id';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$res = $this->sendRequest();
if ($res == null) {
Log::debug('CouchPotato connection failed');
return '';
}
$data = json_decode($res->getBody());
if (! isset($data->movies)) {
Log::debug('Failed to fetch data from CouchPotato');
return '';
}
$movies = $data->movies;
$wantedMovies = $availableMovies = 0;
foreach ($movies as $v) {
switch ($v->status) {
case 'active':
$wantedMovies++;
break;
case 'done':
$availableMovies++;
break;
default:
Log::warning('Unexpected CouchPotato status received: '.$v['status']);
break;
}
}
$html = '
<ul class="livestats">
<li><span class="title">Wanted</span><sub>'.$wantedMovies.'</sub></li>
<li><span class="title">Available</span><sub>'.$availableMovies.'</sub></li>
</ul>
';
return json_encode(['status' => 'inactive', 'html' => $html]);
}
private function sendRequest()
{
$res = null;
try{
$res = $this->_client->request(
'GET',
$this->getApiUrl()
);
}catch(\GuzzleHttp\Exception\BadResponseException $e){
Log::error("Connection to {$e->getRequest()->getUrl()} failed");
Log::debug($e->getMessage());
$res = $e->getRequest();
}catch(\GuzzleHttp\Exception\ConnectException $e) {
Log::error("CouchPotato connection refused");
Log::debug($e->getMessage());
}
return $res;
}
private function getApiUrl()
{
$url = $this->config->url;
$url = rtrim($url, '/');
$apiUrl = $url.'/api/'.$this->config->apikey.'/movie.list';
return $apiUrl;
}
}

109
app/SupportedApps/Deluge.php

@ -1,109 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Deluge implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#357';
}
public function icon()
{
return 'supportedapps/deluge.png';
}
public function configDetails()
{
return 'deluge';
}
public function testConfig()
{
$res = $this->login()[0];
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(!isset($data->result) || is_null($data->result) || $data->result == false) {
echo 'Failed: Invalid Credentials';
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$jar = $this->login()[1];
$res = $this->getDetails($jar);
$data = json_decode($res->getBody());
$download_rate = $data->result->stats->download_rate;
$upload_rate = $data->result->stats->upload_rate;
$seed_count = $data->result->filters->state[2];
$leech_count = $data->result->filters->state[1];
$html = '
<ul class="livestats">
<li><span class="title"><i class="fas fa-arrow-down"></i> '.$this->formatBytes($download_rate).'</span></li>
<li><span class="title"><i class="fas fa-arrow-up"></i> '.$this->formatBytes($upload_rate).'</span></li>
</ul>
<ul class="livestats">
<li><span class="title">Leech: '.$leech_count[1].'</span></li>
<li><span class="title">Seed: '.$seed_count[1].'</span></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function getDetails($jar)
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/json';
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('POST', $api_url, [
'body' => '{"method": "web.update_ui", "params": [["none"], {}], "id": 1}',
'cookies' => $jar,
'headers' => ['content-type' => 'application/json', 'Accept' => 'application/json']
]);
return $res;
}
public function login()
{
$config = $this->config;
$url = $config->url;
$password = $config->password;
$url = rtrim($url, '/');
$api_url = $url.'/json';
$jar = new \GuzzleHttp\Cookie\CookieJar();
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('POST', $api_url, [
'body' => '{"method": "auth.login", "params": ["'.$password.'"], "id": 1}',
'cookies' => $jar,
'headers' => ['content-type' => 'application/json', 'Accept' => 'application/json']
]);
return array($res,$jar);
}
function formatBytes($bytes, $precision = 2) {
$units = array('B', 'KB', 'MB', 'GB', 'TB');
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
// Uncomment one of the following alternatives
$bytes /= pow(1024, $pow);
// $bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow] . 'ps';
}
}

12
app/SupportedApps/Dokuwiki.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Dokuwiki implements Contracts\Applications {
public function defaultColour()
{
return '#9d7056';
}
public function icon()
{
return 'supportedapps/dokuwiki.png';
}
}

12
app/SupportedApps/Duplicati.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Duplicati implements Contracts\Applications {
public function defaultColour()
{
return '#2c3744';
}
public function icon()
{
return 'supportedapps/duplicati.png';
}
}

12
app/SupportedApps/Emby.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Emby implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/emby.png';
}
}

12
app/SupportedApps/Flood.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Flood implements Contracts\Applications {
public function defaultColour()
{
return '##00D091';
}
public function icon()
{
return 'supportedapps/Flood.png';
}
}

11
app/SupportedApps/FreshRSS.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class FreshRSS implements Contracts\Applications {
public function defaultColour()
{
return '#003B73';
}
public function icon()
{
return 'supportedapps/freshrss.png';
}
}

12
app/SupportedApps/Gitea.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Gitea implements Contracts\Applications {
public function defaultColour()
{
return '#585e52';
}
public function icon()
{
return 'supportedapps/gitea.png';
}
}

14
app/SupportedApps/Glances.php

@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class Glances implements Contracts\Applications {
public function defaultColour()
{
return '#2c363f';
}
public function icon()
{
return 'supportedapps/glances.png';
}
}

12
app/SupportedApps/Grafana.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Grafana implements Contracts\Applications {
public function defaultColour()
{
return '#a56e4d';
}
public function icon()
{
return 'supportedapps/grafana.png';
}
}

12
app/SupportedApps/Graylog.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Graylog implements Contracts\Applications {
public function defaultColour()
{
return '#158';
}
public function icon()
{
return 'supportedapps/graylog.png';
}
}

12
app/SupportedApps/Headphones.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Headphones implements Contracts\Applications {
public function defaultColour()
{
return '#185';
}
public function icon()
{
return 'supportedapps/headphones.png';
}
}

12
app/SupportedApps/HomeAssistant.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class HomeAssistant implements Contracts\Applications {
public function defaultColour()
{
return '#073c52';
}
public function icon()
{
return 'supportedapps/homeassistant.png';
}
}

12
app/SupportedApps/Jackett.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Jackett implements Contracts\Applications {
public function defaultColour()
{
return '#484814';
}
public function icon()
{
return 'supportedapps/jackett.png';
}
}

12
app/SupportedApps/Jdownloader.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Jdownloader implements Contracts\Applications {
public function defaultColour()
{
return '#2b494f';
}
public function icon()
{
return 'supportedapps/jdownloader.png';
}
}

11
app/SupportedApps/Krusader.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Krusader implements Contracts\Applications {
public function defaultColour()
{
return '#5A5';
}
public function icon()
{
return 'supportedapps/krusader.png';
}
}

12
app/SupportedApps/Lidarr.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Lidarr implements Contracts\Applications {
public function defaultColour()
{
return '#183c18';
}
public function icon()
{
return 'supportedapps/lidarr.png';
}
}

12
app/SupportedApps/Mailcow.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Mailcow implements Contracts\Applications {
public function defaultColour()
{
return '#161b1f';
}
public function icon()
{
return 'supportedapps/mailcow.svg';
}
}

12
app/SupportedApps/Mcmyadmin.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Mcmyadmin implements Contracts\Applications {
public function defaultColour()
{
return '#30404b';
}
public function icon()
{
return 'supportedapps/mcmyadmin.png';
}
}

12
app/SupportedApps/Medusa.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Medusa implements Contracts\Applications {
public function defaultColour()
{
return '#4b5e55';
}
public function icon()
{
return 'supportedapps/medusa.png';
}
}

12
app/SupportedApps/Monica.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Monica implements Contracts\Applications {
public function defaultColour()
{
return '#fafbfc';
}
public function icon()
{
return 'supportedapps/monica.png';
}
}

12
app/SupportedApps/MusicBrainz.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class MusicBrainz implements Contracts\Applications {
public function defaultColour()
{
return '#a0a';
}
public function icon()
{
return 'supportedapps/musicbrainz.png';
}
}

12
app/SupportedApps/Mylar.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Mylar implements Contracts\Applications {
public function defaultColour()
{
return '#aa0';
}
public function icon()
{
return 'supportedapps/mylar.png';
}
}

12
app/SupportedApps/Netdata.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Netdata implements Contracts\Applications {
public function defaultColour()
{
return '#543737';
}
public function icon()
{
return 'supportedapps/netdata.png';
}
}

12
app/SupportedApps/Nextcloud.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Nextcloud implements Contracts\Applications {
public function defaultColour()
{
return '#0e2c3e';
}
public function icon()
{
return 'supportedapps/nextcloud.png';
}
}

11
app/SupportedApps/NowShowing.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class NowShowing implements Contracts\Applications {
public function defaultColour()
{
return '#690000';
}
public function icon()
{
return 'supportedapps/nowshowing.png';
}
}

84
app/SupportedApps/Nzbget.php

@ -1,84 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Nzbget implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#253827';
}
public function icon()
{
return 'supportedapps/nzbget.png';
}
public function configDetails()
{
return 'nzbget';
}
public function testConfig()
{
$res = $this->buildRequest('status');
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'inactive';
$res = $this->buildRequest('status');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
if($data) {
$size = $data->result->RemainingSizeMB;
$rate = $data->result->DownloadRate;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate, false, ' <span>');
$active = ($size > 0 || $rate > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$username = $config->username;
$password = $config->password;
$rebuild_url = str_replace('http://', 'http://'.$username.':'.$password.'@', $url);
$rebuild_url = str_replace('https://', 'https://'.$username.':'.$password.'@', $rebuild_url);
$rebuild_url = rtrim($rebuild_url, '/');
$api_url = $rebuild_url.'/jsonrpc/'.$endpoint;
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

12
app/SupportedApps/Nzbhydra.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Nzbhydra implements Contracts\Applications {
public function defaultColour()
{
return '#53644d';
}
public function icon()
{
return 'supportedapps/nzbhydra.png';
}
}

12
app/SupportedApps/Ombi.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Ombi implements Contracts\Applications {
public function defaultColour()
{
return '#150f09';
}
public function icon()
{
return 'supportedapps/ombi.png';
}
}

11
app/SupportedApps/OpenMediaVault.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class OpenMediaVault implements Contracts\Applications {
public function defaultColour()
{
return '#5AF';
}
public function icon()
{
return 'supportedapps/openmediavault.png';
}
}

12
app/SupportedApps/Openhab.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Openhab implements Contracts\Applications {
public function defaultColour()
{
return '#2b2525';
}
public function icon()
{
return 'supportedapps/openhab.png';
}
}

12
app/SupportedApps/Opnsense.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Opnsense implements Contracts\Applications {
public function defaultColour()
{
return '#211914';
}
public function icon()
{
return 'supportedapps/opnsense.png';
}
}

12
app/SupportedApps/Pfsense.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Pfsense implements Contracts\Applications {
public function defaultColour()
{
return '#243699';
}
public function icon()
{
return 'supportedapps/pfsense.png';
}
}

73
app/SupportedApps/Pihole.php

@ -1,73 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Pihole implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#352222';
}
public function icon()
{
return 'supportedapps/pihole.png';
}
public function configDetails()
{
return 'pihole';
}
public function testConfig()
{
$res = $this->buildRequest();
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->buildRequest();
$data = json_decode($res->getBody());
$html = '
<ul class="livestats">
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest()
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/api.php';
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

12
app/SupportedApps/Plex.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Plex implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/plex.png';
}
}

77
app/SupportedApps/Plexpy.php

@ -1,77 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Plexpy implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#2d2208';
}
public function icon()
{
return 'supportedapps/plexpy.png';
}
public function configDetails()
{
return 'plexpy';
}
public function testConfig()
{
$res = $this->buildRequest('arnold');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->buildRequest('get_activity');
$data = json_decode($res->getBody());
$stream_count = $data->response->data->stream_count;
$html = '
<ul class="livestats">
<li><span class="title">Stream Count</span><strong>'.$stream_count.'</strong></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
$url = rtrim($url, '/');
$api_url = $url.'/api/v2?apikey='.$apikey.'&cmd='.$endpoint;
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

12
app/SupportedApps/Plexrequests.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Plexrequests implements Contracts\Applications {
public function defaultColour()
{
return '#3c2d1c';
}
public function icon()
{
return 'supportedapps/plexrequests.png';
}
}

12
app/SupportedApps/Portainer.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Portainer implements Contracts\Applications {
public function defaultColour()
{
return '#283f44';
}
public function icon()
{
return 'supportedapps/portainer.png';
}
}

80
app/SupportedApps/Proxmox.php

@ -1,80 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Proxmox implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#542e0a';
}
public function icon()
{
return 'supportedapps/proxmox.png';
}
public function configDetails()
{
//return 'proxmox';
return null;
}
public function testConfig()
{
/*$res = $this->buildRequest();
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}*/
return null;
}
public function executeConfig()
{
/*
$output = '';
$res = $this->buildRequest();
$data = json_decode($res->getBody());
$output = '
<ul class="livestats">
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
</ul>
';
return $output;
*/
return null;
}
public function buildRequest($endpoint='')
{
$config = $this->config;
$username = $config->username;
$password = $config->password;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/api2/json/'.$endpoint.'?username='.$username.'&password='.$password;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'verify' => false ]);
$res = $client->request('GET', $api_url);
return $res;
}
}

12
app/SupportedApps/Radarr.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Radarr implements Contracts\Applications {
public function defaultColour()
{
return '#3e3726';
}
public function icon()
{
return 'supportedapps/radarr.png';
}
}

12
app/SupportedApps/Rancher.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Rancher implements Contracts\Applications {
public function defaultColour()
{
return '#78c9cf';
}
public function icon()
{
return 'supportedapps/rancher.png';
}
}

95
app/SupportedApps/Runeaudio.php

@ -1,95 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Runeaudio implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#05A';
}
public function icon()
{
return 'supportedapps/runeaudio.png';
}
public function configDetails()
{
return 'runeaudio';
}
public function testConfig()
{
$res = $this->buildRequest('status');
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$output = '';
$active = 'active';
$artist = '';
$song_title = '';
$res = $this->buildRequest('currentsong');
$array = explode("\n", $res->getBody());
foreach($array as $item) {
$item_array = explode(": ", $item);
if ($item_array[0] == 'Artist') {
$artist = $item_array[1];
} elseif ($item_array[0] == 'Title') {
$song_title = $item_array[1];
}
}
$output = '<ul class="livestats">';
if (strlen($artist) > 12) {
$output = $output.'<li><span class="title-marquee"><span>'.$artist.'</span></span></li>';
} else {
$output = $output.'<li><span class="title">'.$artist.'</span></li>';
}
$output = $output.'</ul><ul class="livestats">';
if (strlen($song_title) > 12) {
$output = $output.'<li><span class="title-marquee"><span>'.$song_title.'</span></span></li>';
} else {
$output = $output.'<li><span class="title">'.$song_title.'</span></li>';
}
$output = $output.'</ul>';
return json_encode(['status' => $active, 'html' => $output]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/command/?cmd='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

89
app/SupportedApps/Sabnzbd.php

@ -1,89 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Sabnzbd implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#3e3924';
}
public function icon()
{
return 'supportedapps/sabnzbd.png';
}
public function configDetails()
{
return 'sabnzbd';
}
public function testConfig()
{
$res = $this->buildRequest('queue');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'inactive';
$res = $this->buildRequest('queue');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
if($data) {
$size = $data->queue->mbleft;
$rate = $data->queue->kbpersec;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate*1000, false, ' <span>');
$active = ($size > 0 || $rate > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
//print_r($config);
//die();
$url = rtrim($url, '/');
$api_url = $url.'/api?output=json&apikey='.$apikey.'&mode='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

12
app/SupportedApps/Sickrage.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Sickrage implements Contracts\Applications {
public function defaultColour()
{
return '#6185a6';
}
public function icon()
{
return 'supportedapps/sickrage.png';
}
}

12
app/SupportedApps/Sonarr.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Sonarr implements Contracts\Applications {
public function defaultColour()
{
return '#163740';
}
public function icon()
{
return 'supportedapps/sonarr.png';
}
}

11
app/SupportedApps/Syncthing.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Syncthing implements Contracts\Applications {
public function defaultColour()
{
return '#888';
}
public function icon()
{
return 'supportedapps/syncthing.png';
}
}

14
app/SupportedApps/TVheadend.php

@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class TVheadend implements Contracts\Applications {
public function defaultColour()
{
return '#006080';
}
public function icon()
{
return 'supportedapps/tvheadend.png';
}
}

77
app/SupportedApps/Tautulli.php

@ -1,77 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Tautulli implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#2d2208';
}
public function icon()
{
return 'supportedapps/tautulli.png';
}
public function configDetails()
{
return 'tautulli';
}
public function testConfig()
{
$res = $this->buildRequest('arnold');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->buildRequest('get_activity');
$data = json_decode($res->getBody());
$stream_count = $data->response->data->stream_count;
$html = '
<ul class="livestats">
<li><span class="title">Stream Count</span><strong>'.$stream_count.'</strong></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
$url = rtrim($url, '/');
$api_url = $url.'/api/v2?apikey='.$apikey.'&cmd='.$endpoint;
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

78
app/SupportedApps/Traefik.php

@ -1,78 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class Traefik implements Contracts\Applications, Contracts\Livestats
{
public function defaultColour()
{
return '#28434a';
}
public function icon()
{
return 'supportedapps/traefik.png';
}
public function configDetails()
{
return 'traefik';
}
public function testConfig()
{
$res = $this->sendRequest();
if ($res == null) {
echo 'Traefik connection failed';
return;
}
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
echo "Successfully connected with status: ".$data->result."\n";
break;
case 404:
echo 'Failed: Please make sure your URL is correct and includes the port';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'inactive';
$res = $this->sendRequest();
$data = json_decode($res->getBody());
if ($data) {
$avg_response_time = $data->average_response_time_sec;
$time = $avg_response_time*1000;
$time_output = number_format($time, 2);
$active = ($time > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Avg. Response Time</span><sub><i class="fas fa-heartbeat"></i> '.$time_output.' ms</sub></li>
</ul>
';
}
return json_encode(['status' => $active, 'html' => $html]);
}
public function sendRequest()
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/health';
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

169
app/SupportedApps/Transmission.php

@ -1,169 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
class Transmission implements Contracts\Applications, Contracts\Livestats
{
private $_client;
private $_clientOptions = array();
public function __construct()
{
$body = array();
$body["method"] = "torrent-get";
$body["arguments"] = array("fields" => ["percentDone","status","rateDownload","rateUpload"]);
$this->_client = new Client(
['http_errors' => false,
'timeout' => 10,
'body' => json_encode($body)]
);
}
public function defaultColour()
{
return '#950003';
}
public function icon()
{
return 'supportedapps/transmission.png';
}
public function configDetails()
{
return 'transmission';
}
public function testConfig()
{
$res = $this->sendRequest();
if ($res == null) {
echo 'Transmission connection failed';
return;
}
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
echo "Successfully connected with status: ".$data->result."\n";
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and includes the port';
break;
case 409:
echo 'Failed: Incorrect session id';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->sendRequest();
if ($res == null) {
Log::debug('Transmission connection failed');
return '';
}
$data = json_decode($res->getBody());
if (! isset($data->arguments)) {
Log::debug('Failed to fetch data from Transmission');
return '';
}
$torrents = $data->arguments->torrents;
$torrentCount = count($torrents);
$rateDownload = $rateUpload = $completedTorrents = 0;
foreach ($torrents as $thisTorrent) {
$rateDownload += $thisTorrent->rateDownload;
$rateUpload += $thisTorrent->rateUpload;
if ($thisTorrent->percentDone == 1) {
$completedTorrents += 1;
}
}
if ($torrentCount - $completedTorrents == 0) {
// Don't poll as frequently if we don't have any active torrents
$active = 'inactive';
}
$html = '
<ul class="livestats">
<li><span class="title">Done</span><sub>'.$completedTorrents.' / '.$torrentCount.'</sub></li>
<li><span class="title">Down</span><sub>'.format_bytes($rateDownload).'</sub></li>
<li><span class="title">Up</span><sub>'.format_bytes($rateUpload).'</sub></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);;
}
private function sendRequest()
{
$optionsSet = $this->setClientOptions();
if (! $optionsSet) {
// Pass the failed response back up the chain
return null;
}
$res = $this->torrentGet();
if ($res->getStatusCode() == 409) {
$this->setClientOptions();
$res = $this->torrentGet();
}
return $res;
}
private function torrentGet()
{
$res = null;
try{
$res = $this->_client->request(
'POST',
$this->getApiUrl(),
$this->_clientOptions
);
}catch(\GuzzleHttp\Exception\BadResponseException $e){
Log::error("Connection to {$e->getRequest()->getUrl()} failed");
Log::debug($e->getMessage());
$res = $e->getRequest();
}catch(\GuzzleHttp\Exception\ConnectException $e) {
Log::error("Transmission connection refused");
Log::debug($e->getMessage());
}
return $res;
}
private function setClientOptions()
{
if ($this->config->username != '' || $this->config->password != '') {
$this->_clientOptions = ['auth'=> [$this->config->username, $this->config->password, 'Basic']];
}
try{
$res = $this->_client->request('HEAD', $this->getApiUrl(), $this->_clientOptions);
$xtId = $res->getHeaderLine('X-Transmission-Session-Id');
if ($xtId != null) {
$this->_clientOptions['headers'] = ['X-Transmission-Session-Id' => $xtId];
} else {
Log::error("Unable to get Transmission session information");
Log::debug("Status Code: ".$res->getStatusCode());
}
}catch(\GuzzleHttp\Exception\ConnectException $e){
Log::error("Failed connection to Transmission");
return false;
}
return true;
}
private function getApiUrl()
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/transmission/rpc';
return $api_url;
}
}

12
app/SupportedApps/Ttrss.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Ttrss implements Contracts\Applications {
public function defaultColour()
{
return '#9d704c';
}
public function icon()
{
return 'supportedapps/tt-rss.png';
}
}

12
app/SupportedApps/Unifi.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Unifi implements Contracts\Applications {
public function defaultColour()
{
return '#363840';
}
public function icon()
{
return 'supportedapps/unifi.png';
}
}

12
app/SupportedApps/Unraid.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Unraid implements Contracts\Applications {
public function defaultColour()
{
return '#A12624';
}
public function icon()
{
return 'supportedapps/unraid.png';
}
}

12
app/SupportedApps/Virtualmin.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Virtualmin implements Contracts\Applications {
public function defaultColour()
{
return '#161b1f';
}
public function icon()
{
return 'supportedapps/virtualmin.svg';
}
}

11
app/SupportedApps/Watcher3.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Watcher3 implements Contracts\Applications {
public function defaultColour()
{
return '#500';
}
public function icon()
{
return 'supportedapps/watcher3.png';
}
}

11
app/SupportedApps/WebTools.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class WebTools implements Contracts\Applications {
public function defaultColour()
{
return '#555';
}
public function icon()
{
return 'supportedapps/webtools.png';
}
}

12
app/SupportedApps/Webmin.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Webmin implements Contracts\Applications {
public function defaultColour()
{
return '#161b1f';
}
public function icon()
{
return 'supportedapps/webmin.svg';
}
}

11
app/SupportedApps/pyLoad.php

@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class pyLoad implements Contracts\Applications {
public function defaultColour()
{
return '#881';
}
public function icon()
{
return 'supportedapps/pyload.png';
}
}

12
app/SupportedApps/ruTorrent.php

@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class ruTorrent implements Contracts\Applications {
public function defaultColour()
{
return '#004';
}
public function icon()
{
return 'supportedapps/rutorrent.png';
}
}

4
composer.json

@ -7,10 +7,12 @@
"require": {
"php": ">=7.0.0",
"fideloper/proxy": "^4.0",
"graham-campbell/github": "^7.5",
"guzzlehttp/guzzle": "^6.3",
"laravel/framework": "5.7.*",
"laravel/tinker": "~1.0",
"laravelcollective/html": "^5.5"
"laravelcollective/html": "^5.5",
"php-http/guzzle6-adapter": "^1.1"
},
"require-dev": {
"filp/whoops": "~2.0",

992
composer.lock

File diff suppressed because it is too large

5
config/app.php

@ -14,7 +14,7 @@ return [
*/
'name' => env('APP_NAME', 'Heimdall'),
'version' => '2.0.2',
'version' => '2.0.101',
/*
|--------------------------------------------------------------------------
@ -229,6 +229,9 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'SupportedApps' => App\SupportedApps::class,
'EnhancedApps' => App\EnhancedApps::class,
],
];

91
config/github.php

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/*
* This file is part of Laravel GitHub.
*
* (c) Graham Campbell <graham@alt-three.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| Default Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the connections below you wish to use as
| your default connection for all work. Of course, you may use many
| connections at once using the manager class.
|
*/
'default' => 'main',
/*
|--------------------------------------------------------------------------
| GitHub Connections
|--------------------------------------------------------------------------
|
| Here are each of the connections setup for your application. Example
| configuration has been included, but you may add as many connections as
| you would like. Note that the 5 supported authentication methods are:
| "application", "jwt", "none", "password", and "token".
|
*/
'connections' => [
'main' => [
'token' => 'your-token',
'method' => 'token',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'app' => [
'clientId' => 'your-client-id',
'clientSecret' => 'your-client-secret',
'method' => 'application',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'jwt' => [
'token' => 'your-jwt-token',
'method' => 'jwt',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'other' => [
'username' => 'your-username',
'password' => 'your-password',
'method' => 'password',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'none' => [
'method' => 'none',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
],
];

41
database/migrations/2018_10_18_110905_create_applications_table.php

@ -0,0 +1,41 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateApplicationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('applications', function (Blueprint $table) {
$table->string('appid')->unique();
$table->string('name')->unique();
$table->string('sha')->unique()->nullable();
$table->string('icon')->nullable();
$table->string('website')->nullable();
$table->string('license')->nullable();
$table->mediumText('description')->nullable();
$table->boolean('enhanced')->default(false);
$table->string('tile_background')->default('dark');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('applications');
}
}

32
database/migrations/2018_10_23_132008_add_class_to_items_table.php

@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddClassToItemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('items', function (Blueprint $table) {
$table->string('class')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('items', function (Blueprint $table) {
$table->dropColumn(['class']);
});
}
}

36
database/migrations/2018_10_31_191604_create_jobs_table.php

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('jobs');
}
}

4599
package-lock.json

File diff suppressed because it is too large

2
package.json

@ -13,7 +13,7 @@
"bootstrap-sass": "^3.3.7",
"cross-env": "^5.2.0",
"jquery": "^3.2",
"laravel-mix": "^2.0"
"laravel-mix": "^2.1.14"
},
"dependencies": {
"select2": "^4.0.6-rc.1"

85
public/css/app.css

@ -703,6 +703,8 @@ body {
max-width: 1000px;
width: 100%;
margin: 10px 40px;
border-radius: 5px;
overflow: hidden;
}
.module-container header, .module-container footer {
@ -787,6 +789,26 @@ body {
height: 51px;
}
.toggleinput {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: reverse;
-ms-flex-direction: column-reverse;
flex-direction: column-reverse;
line-height: 1;
font-size: 9px;
font-weight: 400;
text-transform: uppercase;
color: #ababab;
padding: 0 20px;
}
.toggleinput label.name {
margin-top: 6px;
}
.module-actions {
display: -webkit-box;
display: -ms-flexbox;
@ -1142,17 +1164,18 @@ a.settinglink {
}
#appimage img {
max-width: 150px;
max-width: 95px;
}
#sapconfig {
#sapconfig, .newblock {
display: none;
width: 100%;
}
#sapconfig h2 {
#sapconfig h2, .newblock h2 {
background: #f2f3f6;
padding: 18px 25px;
padding: 2px 25px;
height: 60px;
margin-left: -15px;
width: calc(100% + 30px);
/* margin-right: -30px; */
@ -1161,9 +1184,18 @@ a.settinglink {
font-size: 18px;
color: #5b5b5b;
font-weight: 500;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
#sapconfig .items {
#sapconfig .items, .newblock .items {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
@ -1230,7 +1262,7 @@ hr {
}
.livestats-container .livestats {
margin: 5px 0px -5px;
margin: 5px 0px 0px;
padding: 0;
display: -webkit-box;
display: -ms-flexbox;
@ -1255,11 +1287,35 @@ hr {
font-weight: 500;
opacity: 0.5;
line-height: 1;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
text-align: left;
}
.livestats-container .livestats strong {
display: block;
line-height: 1;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
color: white;
font-size: 12px;
line-height: 1.2;
}
.livestats-container .livestats strong span {
margin-left: 4px;
}
.livestats-container .livestats.flexcolumn {
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
}
input:-webkit-autofill,
@ -1283,9 +1339,16 @@ select:-webkit-autofill:focus {
.title-marquee {
width: 125px;
overflow: hidden;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: start;
-ms-flex-align: start;
align-items: flex-start;
margin-top: 2px;
}
.title-marquee span {
.title-marquee > span, .title-marquee > strong {
white-space: nowrap;
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
@ -1293,6 +1356,14 @@ select:-webkit-autofill:focus {
animation: marquee 8s linear;
}
.title-marquee .title {
margin-right: 4px;
}
.no-marquee .title {
margin-right: 4px;
}
@-webkit-keyframes marquee {
0% {
-webkit-transform: translate(0, 0);

2
public/js/app.js

@ -135,7 +135,7 @@ $.when( $.ready ).then(function() {
var data = {};
data['url'] = apiurl;
$('input.config-item').each(function(index){
$('.config-item').each(function(index){
var config = $(this).data('config');
data[config] = $(this).val();
});

4
public/mix-manifest.json

@ -1,4 +1,4 @@
{
"/css/app.css": "/css/app.css?id=24e9bb4fa993b66f0572",
"/js/app.js": "/js/app.js?id=f18d23b8fc7a094a2c66"
"/css/app.css": "/css/app.css?id=286d5fcf1350566c1475",
"/js/app.js": "/js/app.js?id=0db2e72b5cd42d83e306"
}

2
resources/assets/js/app.js

@ -126,7 +126,7 @@ $.when( $.ready ).then(function() {
var data = {};
data['url'] = apiurl;
$('input.config-item').each(function(index){
$('.config-item').each(function(index){
var config = $(this).data('config');
data[config] = $(this).val();
});

46
resources/assets/sass/_app.scss

@ -374,6 +374,8 @@ body {
max-width: 1000px;
width: 100%;
margin: 10px 40px;
border-radius: 5px;
overflow: hidden;
header, footer {
display: flex;
justify-content: space-between;
@ -447,10 +449,27 @@ body {
.homesearch {
height: 51px;
}
.toggleinput {
display: flex;
flex-direction: column-reverse;
line-height: 1;
font-size: 9px;
font-weight: 400;
text-transform: uppercase;
color: #ababab;
padding: 0 20px;
label.name {
margin-top: 6px;
}
}
.module-actions {
display: flex;
justify-content:space-between;
align-items: center;
align-items: center;
.button {
font-size: 18px;
@ -755,16 +774,17 @@ div.create {
#appimage {
img {
max-width: 150px;
max-width: 95px;
}
}
#sapconfig {
#sapconfig, .newblock {
display: none;
width: 100%;
h2 {
background: #f2f3f6;
padding: 18px 25px;
padding: 2px 25px;
height: 60px;
margin-left: -15px;
width: calc(100% + 30px);
/* margin-right: -30px; */
@ -773,6 +793,9 @@ div.create {
font-size: 18px;
color: #5b5b5b;
font-weight: 500;
display: flex;
justify-content: space-between;
align-items: center;
}
.items {
display: flex;
@ -836,7 +859,7 @@ hr {
.livestats-container {
.livestats {
margin: 5px 0px -5px;
margin: 5px 0px 0px;
padding: 0;
display: flex;
list-style: none;
@ -855,10 +878,23 @@ hr {
font-weight: 500;
opacity: 0.5;
line-height: 1;
display: flex;
text-align: left;
}
strong {
display: block;
line-height: 1;
display: flex;
align-items: center;
color: white;
font-size: 12px;
line-height: 1.2;
span {
margin-left: 4px;
}
}
&.flexcolumn {
flex-direction: column;
}
}

13
resources/assets/sass/_rune.scss

@ -3,11 +3,22 @@
.title-marquee {
width: 125px;
overflow: hidden;
span {
display: flex;
align-items: flex-start;
margin-top: 2px;
> span, > strong {
white-space: nowrap;
transform: translate(0, 0);
animation: marquee 8s linear;
}
.title {
margin-right: 4px;
}
}
.no-marquee {
.title {
margin-right: 4px;
}
}
@keyframes marquee {

4
resources/lang/en/app.php

@ -47,6 +47,7 @@ return [
'buttons.cancel' => 'Cancel',
'buttons.add' => 'Add',
'buttons.upload' => 'Upload a file',
'buttons.downloadapps' => 'Update Apps List',
'dash.pin_item' => 'Pin item to dashboard',
'dash.no_apps' => 'There are currently no pinned applications, :link1 or :link2',
@ -73,6 +74,8 @@ return [
'apps.tag_name' => 'Tag name',
'apps.tags' => 'Tags',
'apps.override' => 'If different to main url',
'apps.preview' => 'Preview',
'apps.apptype' => 'Application Type',
'user.user_list' => 'Users',
'user.add_user' => 'Add user',
@ -93,6 +96,7 @@ return [
'alert.success.item_updated' => 'Item updated successfully',
'alert.success.item_deleted' => 'Item deleted successfully',
'alert.success.item_restored' => 'Item restored successfully',
'alert.success.updating' => 'Updating apps list',
'alert.success.tag_created' => 'Tag created successfully',
'alert.success.tag_updated' => 'Tag updated successfully',

56
resources/lang/fi/app.php

@ -4,8 +4,15 @@ return array (
'settings.system' => 'Järjestelmä',
'settings.appearance' => 'Ulkonäkö',
'settings.miscellaneous' => 'Sekalainen',
'settings.support' => 'Tue',
'settings.donate' => 'Lahjoita',
'settings.version' => 'Versio',
'settings.background_image' => 'Taustakuva',
'settings.window_target' => 'Linkit aukeaa',
'settings.window_target.current' => 'Avaa tässä välilehdessä',
'settings.window_target.one' => 'Avaa samassa välilehdessä',
'settings.window_target.new' => 'Avaa uudessa välilehdessä',
'settings.homepage_search' => 'Kotisivu Haku',
'settings.search_provider' => 'Hakupalvelu',
'settings.language' => 'Kieli',
@ -13,25 +20,37 @@ return array (
'settings.remove' => 'Poista',
'settings.search' => 'haku',
'settings.no_items' => 'Kohteita ei löytynyt',
'settings.label' => 'Etiketti',
'settings.value' => 'Arvo',
'settings.edit' => 'Muokkaa',
'settings.view' => 'Näkymä',
'options.none' => '- ei asetettu -',
'options.google' => 'Google',
'options.ddg' => 'DuckDuckGo',
'options.bing' => 'Bing',
'options.startpage' => 'Etusivu',
'options.yes' => 'Kyllä',
'options.no' => 'Ei',
'buttons.save' => 'Tallenna',
'buttons.cancel' => 'Peruuta',
'buttons.add' => 'Lisää',
'buttons.upload' => 'Lataa tiedosto',
'buttons.downloadapps' => 'Päivitä sovelluslistaa',
'dash.pin_item' => 'Kiinnitä kohde hallintapaneliin',
'dash.no_apps' => 'Tällä hetkellä ei ole kiinnitettyjä sovelluksia :link1 tai :link2',
'dash.link1' => 'Lisää sovellus tähän',
'dash.link2' => 'Kiinnitä kohde hallintapaneliin',
'dash.pinned_items' => 'Kiinnitetyt Kohteet',
'apps.app_list' => 'Sovellusluettelo',
'apps.view_trash' => 'Näytä roskakori',
'apps.add_application' => 'Lisää sovellus',
@ -44,15 +63,52 @@ return array (
'apps.username' => 'Käyttäjätunnus',
'apps.password' => 'Salasana',
'apps.config' => 'Konfiguraatio',
'apps.apikey' => 'API Avain',
'apps.enable' => 'Aktivoi',
'apps.tag_list' => 'Tagi lista',
'apps.add_tag' => 'Lisää tagi',
'apps.tag_name' => 'Tagin nimi',
'apps.tags' => 'Tagit',
'apps.override' => 'Jos eri kuin pää-osoite',
'apps.preview' => 'Esikatsele',
'user.user_list' => 'KÄyttäjät',
'user.add_user' => 'Lisää käyttäjä',
'user.username' => 'Käyttäjänimi',
'user.avatar' => 'Avatar',
'user.email' => 'Sähköposti',
'user.password_confirm' => 'Vahvista salasana',
'user.secure_front' => 'Salli yleinen pääsy etusivulle - Pakotetaan ainoastaan jos salasana on asetettu.',
'user.autologin' => 'Salli sisäänkirjautuminen tietystä URLista. Linkilläk uka tahansa pystyy kirjautua.',
'url' => 'Url',
'title' => 'Otsikko',
'delete' => 'Poistaa',
'optional' => 'Valinnainen',
'restore' => 'Palauta',
'alert.success.item_created' => 'Kohde luotu onnistuneesti',
'alert.success.item_updated' => 'Kohde päivitetty onnistuneesti',
'alert.success.item_deleted' => 'Kohde poistettu onnistuneesti',
'alert.success.item_restored' => 'Kohde palautettu onnistuneesti',
'alert.success.tag_created' => 'Tagi luotu onnistuneesti',
'alert.success.tag_updated' => 'Tagi päivitetty onnistuneesti',
'alert.success.tag_deleted' => 'Tagi poistettu onnistuneesti',
'alert.success.tag_restored' => 'Tagi palautettu onnistuneesti',
'alert.success.updating' => 'Päivitetään sovelluslistaa',
'alert.success.setting_updated' => 'Asetus muokattu onnistuneesti',
'alert.error.not_exist' => 'Tätä asetusta ei ole olemassa.',
'alert.success.user_created' => 'Käyttäjä luotu onnistuneesti',
'alert.success.user_updated' => 'Käyttäjä päivitetty onnistuneesti',
'alert.success.user_deleted' => 'Käyttäjä poistettu onnistuneesti',
'alert.success.user_restored' => 'Käyttäjä palautettu onnistuneesti',
);

63
resources/lang/sv/app.php

@ -4,8 +4,14 @@ return array (
'settings.system' => 'Systemet',
'settings.appearance' => 'Utseende',
'settings.miscellaneous' => 'Övrigt',
'settings.version' => 'Version',
'settings.background_image' => 'Bakgrundsbild',
'settings.window_target' => 'Länken öppnas i',
'settings.window_target.current' => 'Öppna i denna flik',
'settings.window_target.one' => 'Öppna i samma flik',
'settings.window_target.new' => 'Öppna i ny flik',
'settings.homepage_search' => 'Startsida Sök',
'settings.search_provider' => 'Sökmotor',
'settings.language' => 'Språk',
@ -13,46 +19,95 @@ return array (
'settings.remove' => 'Avlägsna',
'settings.search' => 'sök',
'settings.no_items' => 'Inga poster hittades',
'settings.label' => 'Etikett',
'settings.value' => 'Värde',
'settings.edit' => 'Ändra',
'settings.view' => 'Visa',
'options.none' => '- inte valt -',
'options.google' => 'Google',
'options.ddg' => 'DuckDuckGo',
'options.bing' => 'Bing',
'options.startpage' => 'Startsida',
'options.yes' => 'Ja',
'options.no' => 'Nej',
'buttons.save' => 'Spara',
'buttons.cancel' => 'Avbryt',
'buttons.add' => 'Lägg till',
'buttons.upload' => 'Ladda upp en fil',
'dash.pin_item' => 'Pin objekt till instrumentpanelen',
'buttons.downloadapps' => 'Uppdatera app lista',
'dash.pin_item' => 'Fäst på instrumentpanelen',
'dash.no_apps' => 'Det finns för närvarande inga fästa applikationer, :link1 eller :link2',
'dash.link1' => 'Lägg till en applikation här',
'dash.link2' => 'Pin-ett objekt till instrumentpanelen',
'dash.pinned_items' => 'Fasta Objekt',
'dash.link2' => 'Fäst ett objekt till instrumentpanelen',
'dash.pinned_items' => 'Fästa Objekt',
'apps.app_list' => 'Applikationslista',
'apps.view_trash' => 'Visa papperskorgen',
'apps.add_application' => 'Lägg till applikation',
'apps.application_name' => 'Applikationens namn',
'apps.colour' => 'Färg',
'apps.icon' => 'Ikon',
'apps.pinned' => 'Nålas',
'apps.pinned' => 'Fäst',
'apps.title' => 'Titel',
'apps.hex' => 'Hex-färg',
'apps.username' => 'Användarnamn',
'apps.password' => 'Lösenord',
'apps.config' => 'Konfiguration',
'apps.apikey' => 'API Nyckel',
'apps.enable' => 'Aktivera',
'apps.tag_list' => 'Tagg lista',
'apps.add_tag' => 'Lägg till tagg',
'apps.tag_name' => 'Tagg namn',
'apps.tags' => 'Taggar',
'apps.override' => 'Om annan än huvudlänk',
'apps.preview' => 'Förhandsvisa',
'user.user_list' => 'Avnändare',
'user.add_user' => 'Lägg till användare',
'user.username' => 'Användarnamn',
'user.avatar' => 'Avatar',
'user.email' => 'Epost',
'user.password_confirm' => 'Upprepa lösenord',
'user.secure_front' => 'Tillåt allmän åtkonst till framsidan - Upprätthålls endast om ett lösenord är satt.',
'user.autologin' => 'Tillåt inloggning från en specifik URL. Vem som helst med länken kan logga in.',
'url' => 'Url',
'title' => 'Titel',
'delete' => 'Radera',
'optional' => 'Valfri',
'restore' => 'Återställ',
'alert.success.item_created' => 'Artickeln skapad',
'alert.success.item_updated' => 'Artickeln uppdaterad',
'alert.success.item_deleted' => 'Artickeln borttagen',
'alert.success.item_restored' => 'Artikeln återställd',
'alert.success.updating' => 'Uppdaterar app lista',
'alert.success.tag_created' => 'Tagg skapad',
'alert.success.tag_updated' => 'Tagg uppdaterad',
'alert.success.tag_deleted' => 'Tagg borttagen',
'alert.success.tag_restored' => 'Tagg återställd',
'alert.success.setting_updated' => 'Inställningen uppdaterad',
'alert.error.not_exist' => 'Denna inställning existerar inte.',
'alert.success.user_created' => 'Användare skapad',
'alert.success.user_updated' => 'Anvädare uppdaterad',
'alert.success.user_deleted' => 'Användare borttagen',
'alert.success.user_restored' => 'Användare återställd',
);

4
resources/views/item.blade.php

@ -7,8 +7,8 @@
@endif
<div class="details">
<div class="title{{ title_color($app->colour) }}">{{ $app->title }}</div>
@if(isset($app->config->enabled) && ((bool)$app->config->enabled === true))
<div data-id="{{ $app->id }}" data-dataonly="{{ $app->config->dataonly or '0' }}" class="livestats-container"></div>
@if($app->enabled())
<div data-id="{{ $app->id }}" data-dataonly="{{ $app->getconfig()->dataonly ?? '0' }}" class="livestats-container"></div>
@endif
</div>
<a class="link{{ title_color($app->colour) }}"{!! $app->link_target !!} href="{{ $app->link }}"><i class="fas {{ $app->link_icon }}"></i></a>

13
resources/views/items/enable.blade.php

@ -0,0 +1,13 @@
<div class="toggleinput">
<label class="name">{{ __('app.apps.enable') }}</label>
{!! Form::hidden('config[enabled]', '0') !!}
<label class="switch">
<?php
$checked = false;
if(isset($item) && !empty($item)) $checked = $item->enabled();
$set_checked = ($checked) ? ' checked="checked"' : '';
?>
<input type="checkbox" name="config[enabled]" value="1"<?php echo $set_checked;?> />
<span class="slider round"></span>
</label>
</div>

64
resources/views/items/form.blade.php

@ -2,21 +2,8 @@
<header>
<div class="section-title">{{ __('app.apps.add_application') }}</div>
<div class="module-actions">
<button type="submit"class="button"><i class="fa fa-save"></i><span>{{ __('app.buttons.save') }}</span></button>
<a href="{{ route('items.index', [], false) }}" class="button"><i class="fa fa-ban"></i><span>{{ __('app.buttons.cancel') }}</span></a>
</div>
</header>
<div id="create" class="create">
{!! csrf_field() !!}
<div class="input">
<label>{{ __('app.apps.application_name') }} *</label>
{!! Form::text('title', null, array('placeholder' => __('app.apps.title'), 'id' => 'appname', 'class' => 'form-control')) !!}
<hr />
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('url', null, array('placeholder' => __('app.url'), 'id' => 'appurl', 'class' => 'form-control')) !!}
<hr />
<label>{{ __('app.apps.pinned') }}</label>
<div class="toggleinput">
<label class="name">{{ __('app.apps.pinned') }}</label>
{!! Form::hidden('pinned', '0') !!}
<label class="switch">
<?php
@ -27,17 +14,38 @@
<input type="checkbox" name="pinned" value="1"<?php echo $set_checked;?> />
<span class="slider round"></span>
</label>
</div>
<button type="submit"class="button"><i class="fa fa-save"></i><span>{{ __('app.buttons.save') }}</span></button>
<a href="{{ route('items.index', [], false) }}" class="button"><i class="fa fa-ban"></i><span>{{ __('app.buttons.cancel') }}</span></a>
</div>
</header>
<div id="create" class="create">
{!! csrf_field() !!}
<div class="input">
<label>{{ __('app.apps.application_name') }} *</label>
{!! Form::text('title', null, array('placeholder' => __('app.apps.title'), 'id' => 'appname', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.apptype') }} *</label>
{!! Form::select('class', App\Application::applist(), null, array('class' => 'form-control config-item', 'data-config' => 'type')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.colour') }} *</label>
{!! Form::text('colour', null, array('placeholder' => __('app.apps.hex'),'class' => 'form-control color-picker')) !!}
<hr />
{!! Form::text('colour', null, array('placeholder' => __('app.apps.hex'), 'id' => 'appcolour', 'class' => 'form-control color-picker set-bg-elem')) !!}
</div>
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('url', null, array('placeholder' => __('app.url'), 'id' => 'appurl', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.tags') }} ({{ __('app.optional') }})</label>
{!! Form::select('tags[]', $tags, $current_tags, ['class' => 'tags', 'multiple']) !!}
</div>
<div class="input">
<label>{{ __('app.apps.icon') }}</label>
<div class="icon-container">
<div id="appimage">
@if(isset($item->icon) && !empty($item->icon) || old('icon'))
@ -58,10 +66,24 @@
</div>
</div>
@if(isset($item) && isset($item->config->view))
<div class="newblock" style="display: block;">
<h2>Preview</h2>
</div>
<div id="tile-preview" class="input">
@include('items.preview')
</div>
@if(isset($item) && $item->enhanced())
<div id="sapconfig" style="display: block;">
@if(isset($item))
@include('supportedapps.'.$item->config->view)
@include('SupportedApps::'.$item->getconfig()->name.'.config')
@endif
</div>
@else

1
resources/views/items/list.blade.php

@ -11,6 +11,7 @@
</div>
<div class="module-actions">
<a href="{{ route('applist', [], false) }}" class="button"><i class="fa fa-cloud-download"></i><span>{{ __('app.buttons.downloadapps') }}</span></a>
<a href="{{ route('items.create', [], false) }}" title="" class="button"><i class="fa fa-plus"></i><span>{{ __('app.buttons.add') }}</span></a>
<a href="{{ route('dash', [], false) }}" class="button"><i class="fa fa-ban"></i><span>{{ __('app.buttons.cancel') }}</span></a>
</div>

21
resources/views/items/preview.blade.php

@ -0,0 +1,21 @@
<?php
$item = $item ?? new App\Item;
?>
<section class="item-container" data-id="">
<div class="item set-bg-elem" style="background-color: {{ $item->colour ?? '#222' }}">
@if(isset($item->icon) && !empty($item->icon))
<img class="app-icon" src="{{ asset('/storage/'.$item->icon) }}" />
@else
<img class="app-icon" src="{{ asset('/img/heimdall-icon-small.png') }}" />
@endif
<div class="details">
<div class="title{{ title_color($item->colour) ?? 'white' }}">{{ $item->title ?? '' }}</div>
@if($item->enhanced())
<div data-id="{{ $item->id }}" data-dataonly="{{ $item->getconfig()->dataonly ?? '0' }}" class="no-livestats-container"></div>
@endif
</div>
<a class="link{{ title_color($item->colour) }}"{!! $item->link_target !!} href="{{ $item->link }}"><i class="fas {{ $item->link_icon }}"></i></a>
</div>
<a class="item-edit" href="{{ route($item->link_type.'.edit', [ $item->id ], false) }}"><i class="fas fa-pencil"></i></a>
</section>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save