Compare commits

...

21 Commits

Author SHA1 Message Date
KodeStar
8f360f97ef Merge branch '2.x' into bugfix/two_dots 2024-11-05 10:44:15 +00:00
KodeStar
014f054862 Merge pull request #1390 from linuxserver/feature/search_from_url
Search from URL fixed #1369
2024-11-05 10:42:59 +00:00
Chris Hunt
5ccb87cd7f Fix 2 dots that are visible on light backgrounds #1383 2024-11-05 10:42:05 +00:00
Chris Hunt
8ba8f0c867 Prettier fix 2024-11-05 10:33:12 +00:00
Chris Hunt
c2a3368c7b Search from URL fixed #1369 2024-11-05 10:29:34 +00:00
KodeStar
395c775d4e Merge pull request #1387 from linuxserver/bugfix/search_category_tiles
Fix searching of tiles in category mode
2024-11-05 10:14:02 +00:00
KodeStar
1063d2cd09 Update app.php 2024-11-05 09:42:10 +00:00
Chris Hunt
b880333856 Fixes #1386 2024-11-04 10:30:24 +00:00
KodeStar
026bcf9e43 Merge pull request #1384 from linuxserver/feature/throttle_websitelookup
Throttle websitelookup's to 10 per minute to limit port scan effectiveness
2024-11-04 10:05:52 +00:00
Chris Hunt
837f5c49fa Throttle websitelookup's to 10 per minute to limit port scan effictiveness 2024-11-04 09:54:10 +00:00
KodeStar
a4973869d4 Merge pull request #1372 from tremor021/2.x
Added Serbian translation
2024-11-04 09:38:23 +00:00
KodeStar
07ea22dd10 Merge pull request #1328 from goodactive/2.x
chore: fix some typos in comments
2024-11-04 09:25:06 +00:00
tremor021
6188b1d669 Added Serbian translation 2024-09-27 22:04:30 +02:00
goodactive
184e19abbc chore: fix some typos in comments
Signed-off-by: goodactive <goodactive@qq.com>
2024-04-19 18:00:20 +08:00
KodeStar
f405cf2ddf Update validation.php
Fix missing comma in Japanese translation
2024-03-31 21:40:31 +01:00
KodeStar
c16a4847f7 Update version number 2024-02-19 11:08:45 +00:00
KodeStar
9d0209c9df Merge pull request #1293 from KodeStar/2.x
Fixes to tags and show tags on application list
2024-02-19 11:08:18 +00:00
Chris Hunt
599035b3f8 Fix empty title when creating 2024-02-19 11:06:42 +00:00
Chris Hunt
f3bc6ab618 Add tags to application list, and fix home dashboard tag 2024-02-19 10:58:19 +00:00
Chris Hunt
09e4bb8cad Fix required fields and default items and tags to pinned 2024-02-19 10:03:03 +00:00
Chris Hunt
4b8bf5156d Fix prettier complaints 2024-02-19 09:56:03 +00:00
20 changed files with 193 additions and 41 deletions

View File

@@ -12924,7 +12924,7 @@ namespace Illuminate\Support\Facades {
$instance->substituteImplicitBindings($route);
}
/**
* Register a callback to to run after implicit bindings are substituted.
* Register a callback to run after implicit bindings are substituted.
*
* @param callable $callback
* @return \Illuminate\Routing\Router

View File

@@ -47,7 +47,9 @@ class ItemController extends Controller
} elseif ($treat_tags_as == 'tags') {
$data['apps'] = Item::with('parents')->where('type', 0)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::where('type', 0)->orderBy('order', 'asc')->get();
$data['taglist'] = Item::where('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['taglist'] = Item::where('id', 0)->orWhere(function($query) {
$query->where('type', 1)->pinned();
})->orderBy('order', 'asc')->get();
} else {
$data['apps'] = Item::whereHas('parents', function ($query) {
@@ -56,7 +58,9 @@ class ItemController extends Controller
$data['all_apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere('type', 1)->orderBy('order', 'asc')->get();
})->orWhere(function ($query) {
$query->where('type', 1)->whereNot('id', 0);
})->orderBy('order', 'asc')->get();
}
//$data['all_apps'] = Item::doesntHave('parents')->get();
@@ -181,7 +185,7 @@ class ItemController extends Controller
{
// Get the item
$item = Item::find($id);
if ($item->appid === null && $item->class !== null) { // old apps wont have an app id so set it
if ($item->appid === null && $item->class !== null) { // old apps won't have an app id so set it
$app = Application::where('class', $item->class)->first();
if ($app) {
$item->appid = $app->appid;

View File

@@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Casts\Attribute;
use stdClass;
use Symfony\Component\ClassLoader\ClassMapGenerator;
@@ -133,26 +134,33 @@ class Item extends Model
$id = $this->id;
$tags = ItemTag::select('tag_id')->where('item_id', $id)->pluck('tag_id')->toArray();
$tagdetails = self::select('id', 'title', 'url', 'pinned')->whereIn('id', $tags)->get();
//print_r($tags);
if (in_array(0, $tags)) {
$details = new self([
'id' => 0,
'title' => __('app.dashboard'),
'url' => '',
'pinned' => 0,
]);
$tagdetails->prepend($details);
}
return $tagdetails;
}
protected function title(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => ($value === 'app.dashboard' ? __('app.dashboard') : $value),
);
}
protected function tagUrl(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => ($attributes['id'] === 0 ? '0-dash' : $attributes['url']),
);
}
public function getTagClass(): string
{
$tags = $this->tags();
$slugs = [];
foreach ($tags as $tag) {
if ($tag->id === 0) {
$tag->url = '0-dash';
}
if ($tag->url) {
$slugs[] = 'tag-'.$tag->url;
}
@@ -161,6 +169,20 @@ class Item extends Model
return implode(' ', $slugs);
}
public function getTagList(): string
{
$tags = $this->tags();
$titles = [];
// print_r($tags);
foreach ($tags as $tag) {
if ($tag->title) {
$titles[] = $tag->title;
}
}
return implode(', ', $titles);
}
public function parents(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_tag', 'item_id', 'tag_id');

View File

@@ -5,6 +5,7 @@ namespace App;
use Cache;
use Form;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Request as Input;
use Yaml;
abstract class Search
@@ -106,7 +107,7 @@ abstract class Search
if ((bool) $homepage_search !== true) {
return $output;
}
$user_search_provider = $user_search_provider ?? 'none';
$user_search_provider = Input::get('p') ?? $user_search_provider ?? 'none';
if ((bool) $search_provider) {
if ((bool) $user_search_provider) {
@@ -124,7 +125,7 @@ abstract class Search
$output .= '</select>';
$output .= Form::text(
'q',
null,
Input::get('q') ?? null,
[
'class' => 'homesearch',
'autofocus' => 'autofocus',

View File

@@ -17,7 +17,7 @@ return [
*/
'name' => env('APP_NAME', 'Heimdall'),
'version' => '2.6.0',
'version' => '2.6.3',
/*
|--------------------------------------------------------------------------

View File

@@ -93,7 +93,7 @@ return [
'timezone' => ':attribute は有効なゾーンでなければなりません。',
'unique' => ':attribute は既に取得されています。',
'uploaded' => ':attribute のアップロードに失敗しました。',
'url' => ':attribute 形式が無効です。'
'url' => ':attribute 形式が無効です。',
/*
|--------------------------------------------------------------------------

117
lang/rs/app.php Normal file
View File

@@ -0,0 +1,117 @@
<?php
return array (
'settings.system' => 'Sistem',
'settings.appearance' => 'Izgled',
'settings.miscellaneous' => 'Razno',
'settings.advanced' => 'Napredno',
'settings.support' => 'Podrška',
'settings.donate' => 'Doniraj',
'settings.version' => 'Verzija',
'settings.background_image' => 'Slika u pozadini',
'settings.trianglify' => 'Trianglify pozadine',
'settings.trianglify_seed' => 'Trianglify Random Seed',
'settings.window_target' => 'Linkovi se otvaraju',
'settings.window_target.current' => 'U ovom tab-u',
'settings.window_target.one' => 'U istom tab-u',
'settings.window_target.new' => 'U novom tab-u',
'settings.homepage_search' => 'Pretraga na početnoj strani',
'settings.search_provider' => 'Podrazumevani pretraživač',
'settings.language' => 'Jezik',
'settings.reset' => 'Vraćanje na podrazumevane vrednosti',
'settings.remove' => 'Ukloni',
'settings.search' => 'pretraga',
'settings.no_items' => 'Nije pronađen ni jedan rezultat',
'settings.label' => 'Labela',
'settings.value' => 'Vrednost',
'settings.edit' => 'Izmeni',
'settings.view' => 'Pregled',
'settings.custom_css' => 'Ručni CSS',
'settings.custom_js' => 'Ručni JavaScript',
'settings.treat_tags_as' => 'Tretiraj Oznake Kao:',
'settings.folders' => 'Fascikle',
'settings.tags' => 'Oznake',
'settings.categories' => 'Kategorije',
'options.none' => '- nepodešeno -',
'options.google' => 'Google',
'options.ddg' => 'DuckDuckGo',
'options.bing' => 'Bing',
'options.qwant' => 'Qwant',
'options.startpage' => 'StartPage',
'options.yes' => 'Da',
'options.no' => 'Ne',
'options.nzbhydra' => 'NZBHydra',
'options.jackett' => 'Jackett',
'buttons.save' => 'Sačuvaj',
'buttons.cancel' => 'Otkaži',
'buttons.add' => 'Dodaj',
'buttons.upload' => 'Učitaj ikonu',
'buttons.downloadapps' => 'Ažuriraj Listu Aplikacija',
'dash.pin_item' => 'Zakači na Dashboard',
'dash.no_apps' => 'Trenutno nema zakačenih aplikacija, :link1 or :link2',
'dash.link1' => 'Dodaj aplikaciju ovde',
'dash.link2' => 'Zakači stavku na Dashboard',
'dash.pinned_items' => 'Zakačene Stavke',
'apps.app_list' => 'Lista aplikacija',
'apps.view_trash' => 'Pogledaj smeće',
'apps.add_application' => 'Dodaj aplikaciju',
'apps.application_name' => 'Naziv aplikacije',
'apps.colour' => 'Boja',
'apps.icon' => 'Ikona',
'apps.pinned' => 'Zakačeno',
'apps.title' => 'Naziv',
'apps.hex' => 'Hexadecimalna boja',
'apps.username' => 'Korisnik',
'apps.password' => 'Šifra',
'apps.config' => 'Konfiguracija',
'apps.apikey' => 'API Ključ',
'apps.enable' => 'Omogući',
'apps.tag_list' => 'Lista oznaka',
'apps.add_tag' => 'Dodaj oznaku',
'apps.tag_name' => 'Naziv oznake',
'apps.tags' => 'Oznake',
'apps.override' => 'Ukoliko je različito od glavnom url-a',
'apps.preview' => 'Prikaz',
'apps.apptype' => 'Tip Aplikacije',
'apps.website' => 'Web sajt',
'apps.description' => 'Opis',
'apps.only_admin_account' => 'Samo ako imate administratorski nalog!',
'apps.autologin_url' => 'Url za automatsko logovanje',
'apps.show_deleted' => 'Prikazivanje obrisanih aplikacija',
'app.import' => 'Uvoz',
'dashboard' => 'Početni Dashboard',
'user.user_list' => 'Korisnici',
'user.add_user' => 'Dodaj korisnika',
'user.username' => 'Korisnik',
'user.avatar' => 'Slika',
'user.email' => 'E-mail',
'user.password_confirm' => 'Potvrdi šifu',
'user.secure_front' => 'Dozvoli javni pristup početnoj stranici - Forsira se samo ako je podešena šifra.',
'user.autologin' => 'Dozvoli logovanje sa specifičnog URL-a. Bilo ko sa linkom se može ulogovati.',
'url' => 'URL',
'title' => 'Naziv',
'delete' => 'Obriši',
'optional' => 'Opcionalno',
'restore' => 'Povrati',
'export' => 'Izvezi',
'import' => 'Uvezi',
'alert.success.item_created' => 'Stavka uspešno kreirana',
'alert.success.item_updated' => 'Stavka uspešno ažurirana',
'alert.success.item_deleted' => 'Stavka uspešno obrisana',
'alert.success.item_restored' => 'Stavka uspešno povraćena',
'alert.success.updating' => 'Ažuriranje liste aplikacija',
'alert.success.tag_created' => 'Oznaka uspešno kreirana',
'alert.success.tag_updated' => 'Oznaka uspešno ažurirana',
'alert.success.tag_deleted' => 'Oznaka uspešno obrisana',
'alert.success.tag_restored' => 'Oznaka uspešno povraćena',
'alert.success.setting_updated' => 'Uspešno ste izmenili ovo podešavanje',
'alert.error.not_exist' => 'Ovo podešavanje ne postoji.',
'alert.error.file_too_big' => 'Prevelik fajl.',
'alert.error.file_not_stored' => 'Nije moguće učitati fajl.',
'alert.success.user_created' => 'Korisnik uspešno kreiran',
'alert.success.user_updated' => 'Korisnik uspešnop ažuriran',
'alert.success.user_deleted' => 'Korisnik uspešno obrisan',
'alert.success.user_restored' => 'Korisnik uspešno povraćen',
'dashboard.reorder' => 'Preuredi i zakači stavke',
'dashboard.settings' => 'Podešavanja',
);

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
{
"/css/app.css": "/css/app.css?id=9ac09de3efefe57251cba47c5747c556",
"/js/app.js": "/js/app.js?id=3e28afce465cda43c0b0da9d29ad69a1"
"/css/app.css": "/css/app.css?id=e8db4d3d99fdd3f0c747bb894c843e8e",
"/js/app.js": "/js/app.js?id=3b306fb20ef1a3c96c09963a4f6ff712"
}

View File

@@ -25,7 +25,7 @@ Why not use it as your browser start page? It even has the ability to include a
If you want to see a quick video of Heimdall in use, go to https://youtu.be/GXnnMAxPzMc
## Supported applications
You can use the app to link to any site or application, but Foundation apps will auto fill in the icon for the app and supply a default color for the tile. In addition, Enhanced apps allow you provide details to an apps API, allowing you to view live stats directly on the dashboad. For example, the NZBGet and Sabnzbd Enhanced apps will display the queue size, and download speed while something is downloading.
You can use the app to link to any site or application, but Foundation apps will auto fill in the icon for the app and supply a default color for the tile. In addition, Enhanced apps allow you provide details to an apps API, allowing you to view live stats directly on the dashboard. For example, the NZBGet and Sabnzbd Enhanced apps will display the queue size, and download speed while something is downloading.
Supported applications are recognized by the title of the application as entered in the title field when adding an application. For example, to add a link to pfSense, begin by typing "p" in the title field and then select "pfSense" from the list of supported applications.

View File

@@ -111,7 +111,7 @@ $.when($.ready).then(() => {
$("#search-container")
.on("input", "input[name=q]", function () {
const search = this.value;
const items = $("#sortable").children(".item-container");
const items = $("#sortable").find(".item-container");
if ($("#search-container select[name=provider]").val() === "tiles") {
if (search.length > 0) {
items.hide();
@@ -129,7 +129,7 @@ $.when($.ready).then(() => {
}
})
.on("change", "select[name=provider]", function () {
const items = $("#sortable").children(".item-container");
const items = $("#sortable").find(".item-container");
if ($(this).val() === "tiles") {
$("#search-container button").hide();
const search = $("#search-container input[name=q]").val();
@@ -150,6 +150,8 @@ $.when($.ready).then(() => {
}
});
$("#search-container select[name=provider]").trigger("change");
$("#app")
.on("click", "#config-button", (e) => {
e.preventDefault();
@@ -178,7 +180,7 @@ $.when($.ready).then(() => {
$(e.target).addClass("current");
$("#sortable .item-container").show();
if (tag !== "all") {
$("#sortable .item-container:not(." + tag + ")").hide();
$(`#sortable .item-container:not(.${tag})`).hide();
}
})
.on("click", "#add-item, #pin-item", (e) => {

View File

@@ -141,6 +141,7 @@ body {
top: -58px;
transition: all .35s ease-in-out;
z-index: 1;
overflow: hidden;
ul {
display: inline-block;
list-style: none;

View File

@@ -7,8 +7,8 @@
{!! Form::hidden('pinned', '0') !!}
<label class="switch">
<?php
$checked = false;
if(isset($item->pinned) && (bool)$item->pinned === true) $checked = true;
$checked = true;
if(isset($item->pinned) && (bool)$item->pinned !== true) $checked = false;
$set_checked = ($checked) ? ' checked="checked"' : '';
?>
<input type="checkbox" name="pinned" value="1"<?php echo $set_checked;?> />
@@ -57,17 +57,17 @@
{!! 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')) !!}
{!! Form::text('title', null, array('placeholder' => __('app.apps.title'), 'id' => 'appname', 'class' => 'form-control', 'required')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.colour') }} *</label>
<label>{{ __('app.apps.colour') }}</label>
{!! Form::text('colour', $item->colour ?? '#161b1f', 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', $item->url ?? null, array('placeholder' => __('app.url'), 'id' => 'appurl', 'class' => 'form-control')) !!}
<label>{{ strtoupper(__('app.url')) }} *</label>
{!! Form::text('url', $item->url ?? null, array('placeholder' => __('app.url'), 'id' => 'appurl', 'class' => 'form-control', 'required')) !!}
<small class="help">Don't forget http(s)://</small>
</div>

View File

@@ -22,6 +22,7 @@
<tr>
<th>{{ __('app.title') }}</th>
<th>{{ __('app.url') }}</th>
<th>{{ __('app.apps.tags') }}</th>
<th class="text-center" width="100">{{ __('app.settings.edit') }}</th>
<th class="text-center" width="100">{{ __('app.delete') }}</th>
</tr>
@@ -32,6 +33,7 @@
<tr>
<td>{{ $app->title }}</td>
<td><a href="{{ $app->url }}">{{ $app->link }}</a></td>
<td>{{ $app->getTagList() }}</td>
<td class="text-center"><a{{ $app->target }} href="{!! route('items.edit', [$app->id]) !!}" title="{{ __('app.settings.edit') }} {{ $app->title }}"><i class="fas fa-edit"></i></a></td>
<td class="text-center">
{!! Form::open(['method' => 'DELETE','route' => ['items.destroy', $app->id],'style'=>'display:inline']) !!}
@@ -42,7 +44,7 @@
@endforeach
@else
<tr>
<td colspan="4" class="form-error text-center">
<td colspan="5" class="form-error text-center">
<strong>{{ __('app.settings.no_items') }}</strong>
</td>
</tr>

View File

@@ -6,7 +6,7 @@ $treat_tags_as = \App\Setting::fetch('treat_tags_as');
<div id="taglist" class="taglist">
<div class="tag white current" data-tag="all">All</div>
@foreach($taglist as $tag)
<div class="tag link{{ title_color($tag->colour) }}" style="background-color: {{ $tag->colour }}" data-tag="tag-{{ $tag->url }}">{{ $tag->title }}</div>
<div class="tag link{{ title_color($tag->colour) }}" style="background-color: {{ $tag->colour }}" data-tag="tag-{{ $tag->tag_url }}">{{ $tag->title }}</div>
@endforeach
</div>
@endif

View File

@@ -3,7 +3,7 @@
@foreach($categories as $category)
<?php $apps = $category->children; ?>
<div class="category item-container" data-name="{{ $category->title }}" data-id="{{ $category->id }}">
<div class="category item-containerz" data-name="{{ $category->title }}" data-id="{{ $category->id }}">
<div class="title"><a href="{{ $category->link }}" style="{{ $category->colour ? 'color: ' . $category->colour .';' : '' }}">{{ $category->title }}</a></div>
@foreach($apps as $app)
@include('item')

View File

@@ -11,14 +11,14 @@
<div class="input">
<label>{{ __('app.apps.tag_name') }} *</label>
{!! Form::text('title', null, array('placeholder' => __('app.apps.title'), 'class' => 'form-control')) !!}
{!! Form::text('title', null, array('placeholder' => __('app.apps.title'), 'class' => 'form-control', 'required')) !!}
<hr />
<label>{{ __('app.apps.pinned') }}</label>
{!! Form::hidden('pinned', '0') !!}
<label class="switch">
<?php
$checked = false;
if(isset($item->pinned) && (bool)$item->pinned === true) $checked = true;
$checked = true;
if(isset($item->pinned) && (bool)$item->pinned !== true) $checked = false;
$set_checked = ($checked) ? ' checked="checked"' : '';
?>
<input type="checkbox" name="pinned" value="1"<?php echo $set_checked;?> />
@@ -26,7 +26,7 @@
</label>
</div>
<div class="input">
<label>{{ __('app.apps.colour') }} *</label>
<label>{{ __('app.apps.colour') }}</label>
{!! Form::text('colour', null, array('placeholder' => __('app.apps.hex'),'class' => 'form-control color-picker')) !!}
<hr />
</div>

View File

@@ -56,10 +56,13 @@ Route::name('tags.')->prefix('tag')->group(function () {
/**
* Item Routes
*/
Route::middleware(['throttle:10,1'])->group(function () {
Route::get('/items/websitelookup/{url}', [ItemController::class, 'websitelookup'])->name('lookup');
});
Route::resource('items', ItemController::class);
Route::name('items.')->prefix('items')->group(function () {
Route::get('/websitelookup/{url}', [ItemController::class, 'websitelookup'])->name('lookup');
Route::get('/pin/{id}', [ItemController::class, 'pin'])->name('pin');
Route::get('/restore/{id}', [ItemController::class, 'restore'])->name('restore');
Route::get('/unpin/{id}', [ItemController::class, 'unpin'])->name('unpin');

File diff suppressed because one or more lines are too long