diff --git a/README-EN.md b/README-EN.md index a8d258e1..1c8bbe9a 100644 --- a/README-EN.md +++ b/README-EN.md @@ -34,6 +34,7 @@ Complete PT website building solution. Based on NexusPHP + Laravel + Filament. - Custom menu - Lucky draw - Custom role permission +- Section H&R ## System Requirements - PHP: 8.0, must have extensions: bcmath, ctype, curl, fileinfo, json, mbstring, openssl, pdo_mysql, tokenizer, xml, mysqli, gd, redis, pcntl, sockets, posix, gmp - Mysql: 5.7 latest version or above diff --git a/README.md b/README.md index 9e7b4285..bfb4d790 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ - 自定义菜单 - 幸运大转盘 - 自定义角色权限 +- 分区 H&R ## 系统要求 - PHP: 8.0,必须扩展:bcmath, ctype, curl, fileinfo, json, mbstring, openssl, pdo_mysql, tokenizer, xml, mysqli, gd, redis, pcntl, sockets, posix, gmp diff --git a/app/Console/Commands/BackupDatabase.php b/app/Console/Commands/BackupDatabase.php index 9b8825fa..3c3bb059 100644 --- a/app/Console/Commands/BackupDatabase.php +++ b/app/Console/Commands/BackupDatabase.php @@ -12,7 +12,7 @@ class BackupDatabase extends Command * * @var string */ - protected $signature = 'backup:database'; + protected $signature = 'backup:database {--transfer=}'; /** * The console command description. @@ -39,7 +39,9 @@ class BackupDatabase extends Command public function handle() { $rep = new ToolRepository(); - $result = $rep->backupDatabase(); + $transfer = $this->option('transfer'); + $this->info("transfer: $transfer"); + $result = $rep->backupDatabase($transfer); $log = sprintf('[%s], %s, result: %s', nexus()->getRequestId(), __METHOD__, var_export($result, true)); $this->info($log); do_log($log); diff --git a/app/Console/Commands/BackupWeb.php b/app/Console/Commands/BackupWeb.php index fd09dbeb..097f469f 100644 --- a/app/Console/Commands/BackupWeb.php +++ b/app/Console/Commands/BackupWeb.php @@ -12,7 +12,7 @@ class BackupWeb extends Command * * @var string */ - protected $signature = 'backup:web {--method=}'; + protected $signature = 'backup:web {--method=} {--transfer=}'; /** * The console command description. @@ -39,9 +39,10 @@ class BackupWeb extends Command public function handle() { $method = $this->option('method'); - $this->info("method: $method"); + $transfer = $this->option('transfer'); + $this->info("method: $method, transfer: $transfer"); $rep = new ToolRepository(); - $result = $rep->backupWeb($method); + $result = $rep->backupWeb($method, $transfer); $log = sprintf('[%s], %s, result: %s', nexus()->getRequestId(), __METHOD__, var_export($result, true)); $this->info($log); do_log($log); diff --git a/app/Console/Commands/HitAndRunUpdateStatus.php b/app/Console/Commands/HitAndRunUpdateStatus.php index b0f09ecc..7ca934c0 100644 --- a/app/Console/Commands/HitAndRunUpdateStatus.php +++ b/app/Console/Commands/HitAndRunUpdateStatus.php @@ -44,8 +44,8 @@ class HitAndRunUpdateStatus extends Command $rep = new HitAndRunRepository(); $result = $rep->cronjobUpdateStatus($uid, $torrentId, $ignoreTime); $log = sprintf( - '[%s], %s, uid: %s, torrentId: %s, result: %s', - nexus()->getRequestId(), __METHOD__, $uid, $torrentId, var_export($result, true) + '[%s], %s, uid: %s, torrentId: %s, ignoreTime: %s, result: %s', + nexus()->getRequestId(), __METHOD__, $uid, $torrentId, $ignoreTime, var_export($result, true) ); $this->info($log); do_log($log); diff --git a/app/Console/Commands/PluginCronjob.php b/app/Console/Commands/PluginCronjob.php new file mode 100644 index 00000000..ba9c1e67 --- /dev/null +++ b/app/Console/Commands/PluginCronjob.php @@ -0,0 +1,42 @@ +option('action'); + $id = $this->option('id'); + $force = $this->option('force'); + $pluginRep = new PluginRepository(); + $pluginRep->cronjob($action, $id, $force); + $log = sprintf("[%s], action: %s, id: %s, force: %s run done !", nexus()->getRequestId(), $action, $id, $force); + $this->info($log); + do_log($log); + return 0; + } +} diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 6167ef95..b7215c6c 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -14,15 +14,18 @@ use App\Models\HitAndRun; use App\Models\Medal; use App\Models\Peer; use App\Models\SearchBox; +use App\Models\Setting; use App\Models\Snatch; use App\Models\Tag; use App\Models\Torrent; use App\Models\TorrentOperationLog; use App\Models\User; +use App\Models\UserBanLog; use App\Repositories\AgentAllowRepository; use App\Repositories\AttendanceRepository; use App\Repositories\ExamRepository; use App\Repositories\HitAndRunRepository; +use App\Repositories\PluginRepository; use App\Repositories\SearchBoxRepository; use App\Repositories\SearchRepository; use App\Repositories\TagRepository; @@ -87,8 +90,7 @@ class Test extends Command */ public function handle() { - $rep = new SearchBoxRepository(); - $rep->migrateToModeRelated(); + } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 93288ef9..0e2cec11 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -35,6 +35,8 @@ class Kernel extends ConsoleKernel $schedule->command('claim:settle')->hourly()->when(function () { return Carbon::now()->format('d') == '01'; })->withoutOverlapping(); + +// $schedule->command('plugin:cronjob')->everyMinute()->withoutOverlapping(); } /** diff --git a/app/Filament/OptionsTrait.php b/app/Filament/OptionsTrait.php index 0da89a5f..c210d280 100644 --- a/app/Filament/OptionsTrait.php +++ b/app/Filament/OptionsTrait.php @@ -15,4 +15,12 @@ trait OptionsTrait $disableValue => __('label.disabled'), ]; } + + private static function getYesNoOptions($yesValue = 1, $noValue = 0): array + { + return [ + $yesValue => 'Yes', + $noValue => 'No', + ]; + } } diff --git a/app/Filament/PageList.php b/app/Filament/PageList.php index a1507d1c..ae28ce43 100644 --- a/app/Filament/PageList.php +++ b/app/Filament/PageList.php @@ -3,6 +3,7 @@ namespace App\Filament; use Filament\Resources\Pages\ListRecords; +use Filament\Tables\Filters\Layout; use Illuminate\Database\Eloquent\Model; class PageList extends ListRecords @@ -15,4 +16,9 @@ class PageList extends ListRecords return null; }; } + + protected function getTableFiltersLayout(): ?string + { + return Layout::AboveContent; + } } diff --git a/app/Filament/PageListSingle.php b/app/Filament/PageListSingle.php new file mode 100644 index 00000000..f75df167 --- /dev/null +++ b/app/Filament/PageListSingle.php @@ -0,0 +1,16 @@ +schema([ Forms\Components\DateTimePicker::make('begin')->label(__('label.begin')), - Forms\Components\DateTimePicker::make('end')->label(__('label.begin')), + Forms\Components\DateTimePicker::make('end')->label(__('label.end')), Forms\Components\TextInput::make('duration') ->integer() ->columnSpan(['sm' => 2]) @@ -118,7 +118,7 @@ class ExamResource extends Resource Tables\Columns\TextColumn::make('name')->searchable()->label(__('label.name')), Tables\Columns\TextColumn::make('indexFormatted')->label(__('label.exam.index_formatted'))->html(), Tables\Columns\TextColumn::make('begin')->label(__('label.begin')), - Tables\Columns\TextColumn::make('end')->label(__('label.begin')), + Tables\Columns\TextColumn::make('end')->label(__('label.end')), Tables\Columns\TextColumn::make('durationText')->label(__('label.duration')), Tables\Columns\TextColumn::make('filterFormatted')->label(__('label.exam.filter_formatted'))->html(), Tables\Columns\BooleanColumn::make('is_discovered')->label(__('label.exam.is_discovered')), diff --git a/app/Filament/Resources/System/PluginResource.php b/app/Filament/Resources/System/PluginResource.php new file mode 100644 index 00000000..c3ae5800 --- /dev/null +++ b/app/Filament/Resources/System/PluginResource.php @@ -0,0 +1,114 @@ +schema([ + Forms\Components\TextInput::make('package_name')->label(__('label.plugin.package_name')), + Forms\Components\TextInput::make('remote_url')->label(__('label.plugin.remote_url')), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id'), + Tables\Columns\TextColumn::make('package_name')->label(__('plugin.labels.package_name')), + Tables\Columns\TextColumn::make('remote_url')->label(__('plugin.labels.remote_url')), + Tables\Columns\TextColumn::make('installed_version')->label(__('plugin.labels.installed_version')), + Tables\Columns\TextColumn::make('statusText')->label(__('label.status')), + ]) + ->filters([ + // + ]) + ->actions(self::getActions()) + ->bulkActions([ + Tables\Actions\DeleteBulkAction::make(), + ]); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ManagePlugins::route('/'), + ]; + } + + private static function getActions() + { + $actions = []; + $actions[] = Tables\Actions\EditAction::make(); + $actions[] = self::buildInstallAction(); + $actions[] = self::buildUpdateAction(); + $actions[] = Tables\Actions\DeleteAction::make('delete') + ->hidden(fn ($record) => $record->status == Plugin::STATUS_NOT_INSTALLED) + ->using(function ($record) { + $record->update(['status' => Plugin::STATUS_PRE_DELETE]); + }); + return $actions; + } + + private static function buildInstallAction() + { + return Tables\Actions\Action::make('install') + ->label(__('plugin.actions.install')) + ->icon('heroicon-o-arrow-down') + ->requiresConfirmation() + ->hidden(fn ($record) => $record->status == Plugin::STATUS_NORMAL) + ->action(function ($record) { + $record->update(['status' => Plugin::STATUS_PRE_INSTALL]); + }) + ; + } + + private static function buildUpdateAction() + { + return Tables\Actions\Action::make('update') + ->label(__('plugin.actions.update')) + ->icon('heroicon-o-arrow-up') + ->requiresConfirmation() + ->hidden(fn ($record) => in_array($record->status, [Plugin::STATUS_NOT_INSTALLED, Plugin::STATUS_PRE_UPDATE])) + ->action(function ($record) { + $record->update(['status' => Plugin::STATUS_PRE_UPDATE]); + }) + ; + } + +} diff --git a/app/Filament/Resources/System/PluginResource/Pages/ManagePlugins.php b/app/Filament/Resources/System/PluginResource/Pages/ManagePlugins.php new file mode 100644 index 00000000..c46a6317 --- /dev/null +++ b/app/Filament/Resources/System/PluginResource/Pages/ManagePlugins.php @@ -0,0 +1,21 @@ +columns([ Tables\Columns\TextColumn::make('id'), Tables\Columns\TextColumn::make('typeText')->label(__('label.seed_box_record.type')), + Tables\Columns\TextColumn::make('uid')->searchable(), Tables\Columns\TextColumn::make('user.username')->label(__('label.username'))->searchable(), Tables\Columns\TextColumn::make('operator')->label(__('label.seed_box_record.operator'))->searchable(), Tables\Columns\TextColumn::make('bandwidth')->label(__('label.seed_box_record.bandwidth')), Tables\Columns\TextColumn::make('ip') ->label(__('label.seed_box_record.ip')) - ->searchable() + ->searchable(true, function (Builder $query, $search) { + try { + $ip = IP::create($search); + $ipNumeric = $ip->numeric(); + return $query->orWhere(function (Builder $query) use ($ipNumeric) { + return $query->where('ip_begin_numeric', '<=', $ipNumeric)->where('ip_end_numeric', '>=', $ipNumeric); + }); + } catch (\Exception $exception) { + do_log("Invalid IP: $search, error: " . $exception->getMessage()); + } + }) ->formatStateUsing(fn ($record) => $record->ip ?: sprintf('%s ~ %s', $record->ip_begin, $record->ip_end)), Tables\Columns\TextColumn::make('comment')->label(__('label.comment')), Tables\Columns\BadgeColumn::make('status') @@ -73,6 +85,16 @@ class SeedBoxRecordResource extends Resource ->label(__('label.seed_box_record.status')), ]) ->filters([ + Tables\Filters\Filter::make('uid') + ->form([ + Forms\Components\TextInput::make('uid') + ->label('UID') + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['uid'], fn (Builder $query, $uid) => $query->where("uid", $uid)); + }) + , Tables\Filters\SelectFilter::make('type')->options(SeedBoxRecord::listTypes('text'))->label(__('label.seed_box_record.type')), Tables\Filters\SelectFilter::make('status')->options(SeedBoxRecord::listStatus('text'))->label(__('label.seed_box_record.status')), ]) diff --git a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php index c65d8386..53c207db 100644 --- a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php +++ b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php @@ -6,6 +6,7 @@ use App\Filament\OptionsTrait; use App\Filament\Resources\System\SettingResource; use App\Models\HitAndRun; use App\Models\Setting; +use App\Models\Tag; use Filament\Facades\Filament; use Filament\Forms\ComponentContainer; use Filament\Forms\Concerns\InteractsWithForms; @@ -58,9 +59,6 @@ class EditSetting extends Page implements Forms\Contracts\HasForms $data = []; foreach ($formData as $prefix => $parts) { foreach ($parts as $name => $value) { - if (is_null($value)) { - continue; - } if (in_array($name, $notAutoloadNames)) { $autoload = 'no'; } else { @@ -78,6 +76,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms } } Setting::query()->upsert($data, ['name'], ['value']); + do_action("nexus_setting_update"); clear_setting_cache(); Filament::notify('success', __('filament::resources/pages/edit-record.messages.saved'), true); } @@ -87,13 +86,9 @@ class EditSetting extends Page implements Forms\Contracts\HasForms $tabs = []; $tabs[] = Forms\Components\Tabs\Tab::make(__('label.setting.hr.tab_header')) ->id('hr') - ->schema([ - Forms\Components\Radio::make('hr.mode')->options(HitAndRun::listModes(true))->inline(true)->label(__('label.setting.hr.mode')), - Forms\Components\TextInput::make('hr.inspect_time')->helperText(__('label.setting.hr.inspect_time_help'))->label(__('label.setting.hr.inspect_time'))->integer(), - Forms\Components\TextInput::make('hr.seed_time_minimum')->helperText(__('label.setting.hr.seed_time_minimum_help'))->label(__('label.setting.hr.seed_time_minimum'))->integer(), - Forms\Components\TextInput::make('hr.ignore_when_ratio_reach')->helperText(__('label.setting.hr.ignore_when_ratio_reach_help'))->label(__('label.setting.hr.ignore_when_ratio_reach'))->integer(), - Forms\Components\TextInput::make('hr.ban_user_when_counts_reach')->helperText(__('label.setting.hr.ban_user_when_counts_reach_help'))->label(__('label.setting.hr.ban_user_when_counts_reach'))->integer(), - ])->columns(2); + ->schema($this->getHitAndRunSchema()) + ->columns(2) + ; $tabs[] = Forms\Components\Tabs\Tab::make(__('label.setting.backup.tab_header')) ->id('backup') @@ -117,6 +112,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms Forms\Components\TextInput::make('seed_box.not_seed_box_max_speed')->label(__('label.setting.seed_box.not_seed_box_max_speed'))->helperText(__('label.setting.seed_box.not_seed_box_max_speed_help'))->integer(), Forms\Components\Radio::make('seed_box.no_promotion')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.seed_box.no_promotion'))->helperText(__('label.setting.seed_box.no_promotion_help')), Forms\Components\TextInput::make('seed_box.max_uploaded')->label(__('label.setting.seed_box.max_uploaded'))->helperText(__('label.setting.seed_box.max_uploaded_help'))->integer(), + Forms\Components\TextInput::make('seed_box.max_uploaded_duration')->label(__('label.setting.seed_box.max_uploaded_duration'))->helperText(__('label.setting.seed_box.max_uploaded_duration_help'))->integer(), ])->columns(2); $tabs[] = Forms\Components\Tabs\Tab::make(__('label.setting.system.tab_header')) @@ -137,4 +133,16 @@ class EditSetting extends Page implements Forms\Contracts\HasForms return $tabs; } + private function getHitAndRunSchema() + { + $default = [ + Forms\Components\Radio::make('hr.mode')->options(HitAndRun::listModes(true))->inline(true)->label(__('label.setting.hr.mode')), + Forms\Components\TextInput::make('hr.inspect_time')->helperText(__('label.setting.hr.inspect_time_help'))->label(__('label.setting.hr.inspect_time'))->integer(), + Forms\Components\TextInput::make('hr.seed_time_minimum')->helperText(__('label.setting.hr.seed_time_minimum_help'))->label(__('label.setting.hr.seed_time_minimum'))->integer(), + Forms\Components\TextInput::make('hr.ignore_when_ratio_reach')->helperText(__('label.setting.hr.ignore_when_ratio_reach_help'))->label(__('label.setting.hr.ignore_when_ratio_reach'))->integer(), + Forms\Components\TextInput::make('hr.ban_user_when_counts_reach')->helperText(__('label.setting.hr.ban_user_when_counts_reach_help'))->label(__('label.setting.hr.ban_user_when_counts_reach'))->integer(), + ]; + return apply_filter("hit_and_run_setting_schema", $default); + } + } diff --git a/app/Filament/Resources/System/UsernameChangeLogResource.php b/app/Filament/Resources/System/UsernameChangeLogResource.php index f9a5e6a6..b31a4b35 100644 --- a/app/Filament/Resources/System/UsernameChangeLogResource.php +++ b/app/Filament/Resources/System/UsernameChangeLogResource.php @@ -12,6 +12,7 @@ use Filament\Resources\Table; use Filament\Tables; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use Illuminate\Support\HtmlString; class UsernameChangeLogResource extends Resource { @@ -50,13 +51,30 @@ class UsernameChangeLogResource extends Resource Tables\Columns\TextColumn::make('uid')->searchable(), Tables\Columns\TextColumn::make('user.username')->searchable()->label(__('label.username')), Tables\Columns\TextColumn::make('username_old')->searchable()->label(__('username-change-log.labels.username_old')), - Tables\Columns\TextColumn::make('username_new')->searchable()->label(__('username-change-log.labels.username_new')), - Tables\Columns\TextColumn::make('operator')->searchable()->label(__('label.operator')), + Tables\Columns\TextColumn::make('username_new') + ->searchable() + ->label(__('username-change-log.labels.username_new')) + ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->uid, false, true, true, true))) + , + Tables\Columns\TextColumn::make('operator') + ->searchable() + ->label(__('label.operator')) + , Tables\Columns\TextColumn::make('created_at')->label(__('label.created_at'))->formatStateUsing(fn ($state) => format_datetime($state)), ]) ->defaultSort('id', 'desc') ->filters([ + Tables\Filters\Filter::make('uid') + ->form([ + Forms\Components\TextInput::make('uid') + ->label('UID') + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['uid'], fn (Builder $query, $uid) => $query->where("uid", $uid)); + }) + , Tables\Filters\SelectFilter::make('change_type')->options(UsernameChangeLog::listChangeType())->label(__('username-change-log.labels.change_type')), ]) ->actions([ diff --git a/app/Filament/Resources/System/UsernameChangeLogResource/Pages/ManageUsernameChangeLogs.php b/app/Filament/Resources/System/UsernameChangeLogResource/Pages/ManageUsernameChangeLogs.php index c34d4cee..8dee8b4e 100644 --- a/app/Filament/Resources/System/UsernameChangeLogResource/Pages/ManageUsernameChangeLogs.php +++ b/app/Filament/Resources/System/UsernameChangeLogResource/Pages/ManageUsernameChangeLogs.php @@ -2,14 +2,13 @@ namespace App\Filament\Resources\System\UsernameChangeLogResource\Pages; +use App\Filament\PageListSingle; use App\Filament\Resources\System\UsernameChangeLogResource; use Filament\Pages\Actions; use Filament\Resources\Pages\ManageRecords; -class ManageUsernameChangeLogs extends ManageRecords +class ManageUsernameChangeLogs extends PageListSingle { - protected ?string $maxContentWidth = 'full'; - protected static string $resource = UsernameChangeLogResource::class; protected function getActions(): array diff --git a/app/Filament/Resources/Torrent/TorrentResource.php b/app/Filament/Resources/Torrent/TorrentResource.php index bc046f67..41fb6bbf 100644 --- a/app/Filament/Resources/Torrent/TorrentResource.php +++ b/app/Filament/Resources/Torrent/TorrentResource.php @@ -9,6 +9,7 @@ use App\Models\Setting; use App\Models\Tag; use App\Models\Torrent; use App\Models\TorrentTag; +use App\Models\User; use App\Repositories\TagRepository; use App\Repositories\TorrentRepository; use Filament\Facades\Filament; @@ -24,6 +25,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; +use Illuminate\Validation\Rule; class TorrentResource extends Resource { @@ -83,10 +85,20 @@ class TorrentResource extends Resource })->label(__('label.name'))->searchable(), Tables\Columns\TextColumn::make('posStateText')->label(__('label.torrent.pos_state')), Tables\Columns\TextColumn::make('spStateText')->label(__('label.torrent.sp_state')), - Tables\Columns\TextColumn::make('size')->label(__('label.torrent.size'))->formatStateUsing(fn ($state) => mksize($state)), - Tables\Columns\TextColumn::make('seeders')->label(__('label.torrent.seeders')), - Tables\Columns\TextColumn::make('leechers')->label(__('label.torrent.leechers')), -// Tables\Columns\TextColumn::make('times_completed')->label(__('label.torrent.times_completed')), + Tables\Columns\TextColumn::make('pickInfoText') + ->label(__('label.torrent.picktype')) + ->formatStateUsing(fn ($record) => $record->pickInfo['text']) + , + Tables\Columns\BooleanColumn::make('hr') + ->label(__('label.torrent.hr')) + , + Tables\Columns\TextColumn::make('size') + ->label(__('label.torrent.size')) + ->formatStateUsing(fn ($state) => mksize($state)) + ->sortable() + , + Tables\Columns\TextColumn::make('seeders')->label(__('label.torrent.seeders'))->sortable(), + Tables\Columns\TextColumn::make('leechers')->label(__('label.torrent.leechers'))->sortable(), Tables\Columns\BadgeColumn::make('approval_status') ->visible($showApproval) ->label(__('label.torrent.approval_status')) @@ -95,12 +107,22 @@ class TorrentResource extends Resource Tables\Columns\TextColumn::make('added')->label(__('label.added'))->dateTime(), Tables\Columns\TextColumn::make('user.username') ->label(__('label.torrent.owner')) - ->url(fn ($record) => sprintf('/userdetails.php?id=%s', $record->owner)) - ->openUrlInNewTab(true) + ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->owner, false, true, true, true))) , ]) ->defaultSort('id', 'desc') ->filters([ + Tables\Filters\Filter::make('owner') + ->form([ + Forms\Components\TextInput::make('owner') + ->label(__('label.torrent.owner')) + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['owner'], fn (Builder $query, $owner) => $query->where("owner", $owner)); + }) + , + Tables\Filters\SelectFilter::make('visible') ->options(self::$yesOrNo) ->label(__('label.torrent.visible')), @@ -113,10 +135,18 @@ class TorrentResource extends Resource ->options(Torrent::listPromotionTypes(true)) ->label(__('label.torrent.sp_state')), + Tables\Filters\SelectFilter::make('picktype') + ->options(Torrent::listPickInfo(true)) + ->label(__('label.torrent.picktype')), + Tables\Filters\SelectFilter::make('approval_status') ->options(Torrent::listApprovalStatus(true)) ->visible($showApproval) ->label(__('label.torrent.approval_status')), + + Tables\Filters\SelectFilter::make('hr') + ->options(self::getYesNoOptions()) + ->label(__('label.torrent.hr')), ]) ->actions(self::getActions()) ->bulkActions(self::getBulkActions()); @@ -146,6 +176,7 @@ class TorrentResource extends Resource private static function getBulkActions(): array { + $user = Auth::user(); $actions = []; if (user_can('torrentsticky')) { $actions[] = Tables\Actions\BulkAction::make('posState') @@ -155,13 +186,77 @@ class TorrentResource extends Resource ->label(__('label.torrent.pos_state')) ->options(Torrent::listPosStates(true)) ->required() + , + Forms\Components\DateTimePicker::make('pos_state_until') + ->label(__('label.deadline')) + , ]) ->icon('heroicon-o-arrow-circle-up') ->action(function (Collection $records, array $data) { $idArr = $records->pluck('id')->toArray(); try { $torrentRep = new TorrentRepository(); - $torrentRep->setPosState($idArr, $data['pos_state']); + $torrentRep->setPosState($idArr, $data['pos_state'], $data['pos_state_until']); + } catch (\Exception $exception) { + do_log($exception->getMessage() . $exception->getTraceAsString(), 'error'); + Filament::notify('danger', class_basename($exception)); + } + }) + ->deselectRecordsAfterCompletion(); + } + + if (user_can('torrentonpromotion')) { + $actions[] = Tables\Actions\BulkAction::make('sp_state') + ->label(__('admin.resources.torrent.bulk_action_sp_state')) + ->form([ + Forms\Components\Select::make('sp_state') + ->label(__('label.torrent.sp_state')) + ->options(Torrent::listPromotionTypes(true)) + ->required() + , + Forms\Components\Select::make('promotion_time_type') + ->label(__('label.torrent.promotion_time_type')) + ->options(Torrent::listPromotionTimeTypes(true)) + ->required() + , + Forms\Components\DateTimePicker::make('promotion_until') + ->label(__('label.deadline')) + , + ]) + ->icon('heroicon-o-speakerphone') + ->action(function (Collection $records, array $data) { + $idArr = $records->pluck('id')->toArray(); + try { + $torrentRep = new TorrentRepository(); + $torrentRep->setSpState($idArr, $data['sp_state'], $data['promotion_time_type'], $data['promotion_until']); + } catch (\Exception $exception) { + do_log($exception->getMessage() . $exception->getTraceAsString(), 'error'); + Filament::notify('danger', $exception->getMessage()); + } + }) + ->deselectRecordsAfterCompletion(); + } + + if (user_can('torrentmanage') && ($user->picker == 'yes' || $user->class >= User::CLASS_SYSOP)) { + $actions[] = Tables\Actions\BulkAction::make('recommend') + ->label(__('admin.resources.torrent.bulk_action_recommend')) + ->form([ + Forms\Components\Radio::make('picktype') + ->label(__('admin.resources.torrent.bulk_action_recommend')) + ->inline() + ->options(Torrent::listPickInfo(true)) + ->required(), + + ]) + ->icon('heroicon-o-fire') + ->action(function (Collection $records, array $data) { + if (empty($data['picktype'])) { + return; + } + $idArr = $records->pluck('id')->toArray(); + try { + $torrentRep = new TorrentRepository(); + $torrentRep->setPickType($idArr, $data['picktype']); } catch (\Exception $exception) { do_log($exception->getMessage() . $exception->getTraceAsString(), 'error'); Filament::notify('danger', class_basename($exception)); @@ -213,6 +308,38 @@ class TorrentResource extends Resource } }) ->deselectRecordsAfterCompletion(); + + $actions[] = Tables\Actions\BulkAction::make('hr') + ->label(__('admin.resources.torrent.bulk_action_hr')) + ->form([ + Forms\Components\Radio::make('hr') + ->label(__('admin.resources.torrent.bulk_action_hr')) + ->inline() + ->options(self::getYesNoOptions()) + ->required(), + + ]) + ->icon('heroicon-o-sparkles') + ->action(function (Collection $records, array $data) { + if (empty($data['hr'])) { + return; + } + $idArr = $records->pluck('id')->toArray(); + try { + $torrentRep = new TorrentRepository(); + $torrentRep->setHr($idArr, $data['hr']); + } catch (\Exception $exception) { + do_log($exception->getMessage() . $exception->getTraceAsString(), 'error'); + Filament::notify('danger', class_basename($exception)); + } + }) + ->deselectRecordsAfterCompletion(); + } + + if (user_can('torrent-delete')) { + $actions[] = Tables\Actions\DeleteBulkAction::make('bulk-delete')->using(function (Collection $records) { + deletetorrent($records->pluck('id')->toArray()); + }); } return $actions; @@ -244,7 +371,7 @@ class TorrentResource extends Resource }); } - if (user_can('torrentmanage')) { + if (user_can('torrent-delete')) { $actions[] = Tables\Actions\DeleteAction::make('delete')->using(function ($record) { deletetorrent($record->id); }); diff --git a/app/Filament/Resources/Torrent/TorrentResource/Pages/ListTorrents.php b/app/Filament/Resources/Torrent/TorrentResource/Pages/ListTorrents.php index e9f4e076..55da176d 100644 --- a/app/Filament/Resources/Torrent/TorrentResource/Pages/ListTorrents.php +++ b/app/Filament/Resources/Torrent/TorrentResource/Pages/ListTorrents.php @@ -7,6 +7,7 @@ use App\Filament\Resources\Torrent\TorrentResource; use Filament\Pages\Actions; use Filament\Resources\Pages\ListRecords; + class ListTorrents extends PageList { protected static string $resource = TorrentResource::class; @@ -17,4 +18,6 @@ class ListTorrents extends PageList // Actions\CreateAction::make(), ]; } + + } diff --git a/app/Filament/Resources/User/ClaimResource.php b/app/Filament/Resources/User/ClaimResource.php index d7970ce8..6559b76f 100644 --- a/app/Filament/Resources/User/ClaimResource.php +++ b/app/Filament/Resources/User/ClaimResource.php @@ -13,6 +13,7 @@ use Filament\Tables; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletingScope; +use Illuminate\Support\HtmlString; class ClaimResource extends Resource { @@ -48,7 +49,11 @@ class ClaimResource extends Resource ->columns([ Tables\Columns\TextColumn::make('id')->sortable(), Tables\Columns\TextColumn::make('uid')->searchable(), - Tables\Columns\TextColumn::make('user.username')->label(__('label.user.label'))->searchable(), + Tables\Columns\TextColumn::make('user.username') + ->label(__('label.user.label')) + ->searchable() + ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->uid, false, true, true, true))) + , Tables\Columns\TextColumn::make('torrent.name')->limit(40)->label(__('label.torrent.label'))->searchable(), Tables\Columns\TextColumn::make('torrent.size')->label(__('label.torrent.size'))->formatStateUsing(fn (Model $record) => mksize($record->torrent->size)), Tables\Columns\TextColumn::make('torrent.added')->label(__('label.torrent.ttl'))->formatStateUsing(fn (Model $record) => mkprettytime($record->torrent->added->diffInSeconds())), @@ -60,7 +65,26 @@ class ClaimResource extends Resource ]) ->defaultSort('id', 'desc') ->filters([ - // + Tables\Filters\Filter::make('uid') + ->form([ + Forms\Components\TextInput::make('uid') + ->label('UID') + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['uid'], fn (Builder $query, $uid) => $query->where("uid", $uid)); + }) + , + Tables\Filters\Filter::make('torrent_id') + ->form([ + Forms\Components\TextInput::make('torrent_id') + ->label(__('claim.fields.torrent_id')) + ->placeholder(__('claim.fields.torrent_id')) + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['torrent_id'], fn (Builder $query, $value) => $query->where("torrent_id", $value)); + }) + , ]) ->actions([ // Tables\Actions\EditAction::make(), diff --git a/app/Filament/Resources/User/ExamUserResource.php b/app/Filament/Resources/User/ExamUserResource.php index b1b24dae..387597eb 100644 --- a/app/Filament/Resources/User/ExamUserResource.php +++ b/app/Filament/Resources/User/ExamUserResource.php @@ -4,6 +4,7 @@ namespace App\Filament\Resources\User; use App\Filament\Resources\User\ExamUserResource\Pages; use App\Filament\Resources\User\ExamUserResource\RelationManagers; +use App\Models\Exam; use App\Models\ExamUser; use App\Repositories\ExamRepository; use App\Repositories\HitAndRunRepository; @@ -16,6 +17,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\HtmlString; class ExamUserResource extends Resource { @@ -51,7 +53,11 @@ class ExamUserResource extends Resource ->columns([ Tables\Columns\TextColumn::make('id')->sortable(), Tables\Columns\TextColumn::make('uid')->searchable(), - Tables\Columns\TextColumn::make('user.username')->label(__('label.username'))->searchable(), + Tables\Columns\TextColumn::make('user.username') + ->label(__('label.username')) + ->searchable() + ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->uid, false, true, true, true))) + , Tables\Columns\TextColumn::make('exam.name')->label(__('label.exam.label')), Tables\Columns\TextColumn::make('begin')->label(__('label.begin'))->dateTime(), Tables\Columns\TextColumn::make('end')->label(__('label.end'))->dateTime(), @@ -61,6 +67,20 @@ class ExamUserResource extends Resource ]) ->defaultSort('id', 'desc') ->filters([ + Tables\Filters\Filter::make('uid') + ->form([ + Forms\Components\TextInput::make('uid') + ->label('UID') + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['uid'], fn (Builder $query, $uid) => $query->where("uid", $uid)); + }) + , + Tables\Filters\SelectFilter::make('exam_id') + ->options(Exam::query()->pluck('name', 'id')->toArray()) + ->label(__('exam.label')) + , Tables\Filters\SelectFilter::make('status')->options(ExamUser::listStatus(true))->label(__("label.status")), Tables\Filters\SelectFilter::make('is_done')->options(['0' => 'No', '1' => 'yes'])->label(__('label.exam_user.is_done')), ]) diff --git a/app/Filament/Resources/User/HitAndRunResource.php b/app/Filament/Resources/User/HitAndRunResource.php index 17d4e132..94558ec0 100644 --- a/app/Filament/Resources/User/HitAndRunResource.php +++ b/app/Filament/Resources/User/HitAndRunResource.php @@ -15,6 +15,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\HtmlString; class HitAndRunResource extends Resource { @@ -42,7 +43,12 @@ class HitAndRunResource extends Resource ->columns([ Tables\Columns\TextColumn::make('id')->sortable(), Tables\Columns\TextColumn::make('uid')->searchable(), - Tables\Columns\TextColumn::make('user.username')->searchable()->label(__('label.username')), + Tables\Columns\TextColumn::make('user.username') + ->searchable() + ->label(__('label.username')) + ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->uid, false, true, true, true))) + , + Tables\Columns\TextColumn::make('torrent.name')->limit(30)->label(__('label.torrent.label')), Tables\Columns\TextColumn::make('snatch.uploadText')->label(__('label.uploaded')), Tables\Columns\TextColumn::make('snatch.downloadText')->label(__('label.downloaded')), @@ -53,6 +59,16 @@ class HitAndRunResource extends Resource ]) ->defaultSort('id', 'desc') ->filters([ + Tables\Filters\Filter::make('uid') + ->form([ + Forms\Components\TextInput::make('uid') + ->label('UID') + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['uid'], fn (Builder $query, $uid) => $query->where("uid", $uid)); + }) + , Tables\Filters\SelectFilter::make('status')->options(HitAndRun::listStatus(true))->label(__('label.status')), ]) ->actions([ @@ -72,7 +88,7 @@ class HitAndRunResource extends Resource public static function getEloquentQuery(): Builder { - return parent::getEloquentQuery()->with(['user', 'torrent', 'snatch']); + return parent::getEloquentQuery()->with(['user', 'torrent', 'snatch', 'torrent.basic_category']); } public static function getRelations(): array diff --git a/app/Filament/Resources/User/UserMedalResource.php b/app/Filament/Resources/User/UserMedalResource.php index 7d83cfc4..4d2b59c4 100644 --- a/app/Filament/Resources/User/UserMedalResource.php +++ b/app/Filament/Resources/User/UserMedalResource.php @@ -4,6 +4,7 @@ namespace App\Filament\Resources\User; use App\Filament\Resources\User\UserMedalResource\Pages; use App\Filament\Resources\User\UserMedalResource\RelationManagers; +use App\Models\Medal; use App\Models\UserMedal; use Filament\Forms; use Filament\Resources\Form; @@ -12,6 +13,7 @@ use Filament\Resources\Table; use Filament\Tables; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use Illuminate\Support\HtmlString; class UserMedalResource extends Resource { @@ -47,14 +49,31 @@ class UserMedalResource extends Resource ->columns([ Tables\Columns\TextColumn::make('id')->sortable(), Tables\Columns\TextColumn::make('uid')->searchable(), - Tables\Columns\TextColumn::make('user.username')->label(__('label.username'))->searchable(), + Tables\Columns\TextColumn::make('user.username') + ->label(__('label.username')) + ->searchable() + ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->uid, false, true, true, true))) + , Tables\Columns\TextColumn::make('medal.name')->label(__('label.medal.label'))->searchable(), Tables\Columns\ImageColumn::make('medal.image_large')->label(__('label.image')), Tables\Columns\TextColumn::make('expire_at')->label(__('label.expire_at'))->dateTime(), ]) ->defaultSort('id', 'desc') ->filters([ - + Tables\Filters\Filter::make('uid') + ->form([ + Forms\Components\TextInput::make('uid') + ->label('UID') + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['uid'], fn (Builder $query, $uid) => $query->where("uid", $uid)); + }) + , + Tables\Filters\SelectFilter::make('medal_id') + ->options(Medal::query()->pluck('name', 'id')->toArray()) + ->label(__('medal.label')) + , ]) ->actions([ Tables\Actions\DeleteAction::make(), diff --git a/app/Filament/Resources/User/UserResource.php b/app/Filament/Resources/User/UserResource.php index e2f86cde..a0a6417a 100644 --- a/app/Filament/Resources/User/UserResource.php +++ b/app/Filament/Resources/User/UserResource.php @@ -61,7 +61,7 @@ class UserResource extends Resource ->columns([ Tables\Columns\TextColumn::make('id')->sortable()->searchable(), Tables\Columns\TextColumn::make('username')->searchable()->label(__("label.user.username")) - ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->id, false, true, false, true))), + ->formatStateUsing(fn ($record) => new HtmlString(get_username($record->id, false, true, true, true))), Tables\Columns\TextColumn::make('email')->searchable()->label(__("label.email")), Tables\Columns\TextColumn::make('class')->label('Class') ->formatStateUsing(fn(Tables\Columns\Column $column) => $column->getRecord()->classText) @@ -80,6 +80,15 @@ class UserResource extends Resource ]) ->defaultSort('added', 'desc') ->filters([ + Tables\Filters\Filter::make('id') + ->form([ + Forms\Components\TextInput::make('id') + ->placeholder('UID') + , + ])->query(function (Builder $query, array $data) { + return $query->when($data['id'], fn (Builder $query, $id) => $query->where("id", $id)); + }) + , Tables\Filters\SelectFilter::make('class')->options(array_column(User::$classes, 'text'))->label(__('label.user.class')), Tables\Filters\SelectFilter::make('status')->options(['confirmed' => 'confirmed', 'pending' => 'pending'])->label(__('label.user.status')), Tables\Filters\SelectFilter::make('enabled')->options(self::$yesOrNo)->label(__('label.user.enabled')), diff --git a/app/Filament/Resources/User/UserResource/Pages/ListUsers.php b/app/Filament/Resources/User/UserResource/Pages/ListUsers.php index 897de5d4..bf81f3fb 100644 --- a/app/Filament/Resources/User/UserResource/Pages/ListUsers.php +++ b/app/Filament/Resources/User/UserResource/Pages/ListUsers.php @@ -7,6 +7,7 @@ use App\Filament\Resources\User\UserResource; use Filament\Pages\Actions; use Filament\Resources\Pages\ListRecords; use Illuminate\Database\Eloquent\Model; +use Filament\Tables\Filters\Layout; class ListUsers extends PageList { @@ -25,6 +26,9 @@ class ListUsers extends PageList // } - + protected function getTableFiltersLayout(): ?string + { + return Layout::AboveContent; + } } diff --git a/app/Filament/Resources/User/UserResource/Pages/UserProfile.php b/app/Filament/Resources/User/UserResource/Pages/UserProfile.php index dfec0818..5a45a121 100644 --- a/app/Filament/Resources/User/UserResource/Pages/UserProfile.php +++ b/app/Filament/Resources/User/UserResource/Pages/UserProfile.php @@ -278,11 +278,6 @@ class UserProfile extends ViewRecord ->action(function ($data) { $rep = $this->getRep(); try { - if (!empty($data['duration'])) { - $data['deadline'] = now()->addDays($data['duration']); - } else { - $data['deadline'] = null; - } $rep->addMeta($this->record, $data, $data); $this->notify('success', 'Success!'); $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); diff --git a/app/Models/BonusLogs.php b/app/Models/BonusLogs.php index 1152b046..aae920a6 100644 --- a/app/Models/BonusLogs.php +++ b/app/Models/BonusLogs.php @@ -25,6 +25,7 @@ class BonusLogs extends NexusModel const BUSINESS_TYPE_NO_AD = 11; const BUSINESS_TYPE_GIFT_TO_LOW_SHARE_RATIO = 12; const BUSINESS_TYPE_LUCKY_DRAW = 13; + const BUSINESS_TYPE_EXCHANGE_DOWNLOAD = 14; public static array $businessTypes = [ self::BUSINESS_TYPE_CANCEL_HIT_AND_RUN => ['text' => 'Cancel H&R'], @@ -40,6 +41,7 @@ class BonusLogs extends NexusModel self::BUSINESS_TYPE_NO_AD => ['text' => 'No ad'], self::BUSINESS_TYPE_GIFT_TO_LOW_SHARE_RATIO => ['text' => 'Gift to low share ratio'], self::BUSINESS_TYPE_LUCKY_DRAW => ['text' => 'Lucky draw'], + self::BUSINESS_TYPE_EXCHANGE_DOWNLOAD => ['text' => 'Exchange download'], ]; public static function getBonusForCancelHitAndRun() diff --git a/app/Models/HitAndRun.php b/app/Models/HitAndRun.php index 96c948f5..c93fc77c 100644 --- a/app/Models/HitAndRun.php +++ b/app/Models/HitAndRun.php @@ -2,6 +2,8 @@ namespace App\Models; +use Carbon\Carbon; +use Carbon\Exceptions\InvalidArgumentException; use Illuminate\Database\Eloquent\Casts\Attribute; class HitAndRun extends NexusModel @@ -17,13 +19,18 @@ class HitAndRun extends NexusModel const STATUS_UNREACHED = 3; const STATUS_PARDONED = 4; - public static $status = [ + public static array $status = [ self::STATUS_INSPECTING => ['text' => 'Inspecting'], self::STATUS_REACHED => ['text' => 'Reached'], self::STATUS_UNREACHED => ['text' => 'Unreached'], self::STATUS_PARDONED => ['text' => 'Pardoned'], ]; + const CAN_PARDON_STATUS = [ + self::STATUS_INSPECTING, + self::STATUS_UNREACHED, + ]; + const MODE_DISABLED = 'disabled'; const MODE_MANUAL = 'manual'; const MODE_GLOBAL = 'global'; @@ -39,17 +46,37 @@ class HitAndRun extends NexusModel protected function seedTimeRequired(): Attribute { return new Attribute( - get: fn($value, $attributes) => $this->status == self::STATUS_INSPECTING ? mkprettytime(3600 * Setting::get('hr.seed_time_minimum') - $this->snatch->seedtime) : '---' + get: fn($value, $attributes) => $this->doGetSeedTimeRequired() ); } protected function inspectTimeLeft(): Attribute { return new Attribute( - get: fn($value, $attributes) => $this->status == self::STATUS_INSPECTING ? mkprettytime(\Carbon\Carbon::now()->diffInSeconds($this->snatch->completedat->addHours(Setting::get('hr.inspect_time')))) : '---' + get: fn($value, $attributes) => $this->doGetInspectTimeLeft() ); } + private function doGetInspectTimeLeft(): string + { + if ($this->status != self::STATUS_INSPECTING) { + return '---'; + } + $inspectTime = HitAndRun::getConfig('inspect_time', $this->torrent->basic_category->mode); + $diffInSeconds = Carbon::now()->diffInSeconds($this->snatch->completedat->addHours($inspectTime)); + return mkprettytime($diffInSeconds); + } + + private function doGetSeedTimeRequired(): string + { + if ($this->status != self::STATUS_INSPECTING) { + return '---'; + } + $seedTimeMinimum = HitAndRun::getConfig('seed_time_minimum', $this->torrent->basic_category->mode); + $diffInSeconds = 3600 * $seedTimeMinimum - $this->snatch->seedtime; + return mkprettytime($diffInSeconds); + } + public function getStatusTextAttribute() { return nexus_trans('hr.status_' . $this->status); @@ -87,8 +114,35 @@ class HitAndRun extends NexusModel public static function getIsEnabled(): bool { - $result = Setting::get('hr.mode'); - return $result && in_array($result, [self::MODE_GLOBAL, self::MODE_MANUAL]); + $enableSpecialSection = Setting::get('main.spsct') == 'yes'; + $browseMode = self::getConfig('mode', Setting::get('main.browsecat')); + $browseEnabled = $browseMode && in_array($browseMode, [self::MODE_GLOBAL, self::MODE_MANUAL]); + if (!$enableSpecialSection) { + do_log("Not enable special section, browseEnabled: $browseEnabled"); + return $browseEnabled; + } + $specialMode = self::getConfig('mode', Setting::get('main.specialcat')); + $specialEnabled = $specialMode && in_array($specialMode, [self::MODE_GLOBAL, self::MODE_MANUAL]); + $result = $browseEnabled || $specialEnabled; + do_log("Enable special section, browseEnabled: $browseEnabled, specialEnabled: $specialEnabled, result: $result"); + return $result; + } + + public static function getConfig($name, $searchBoxId) + { + if ($name == '*') { + $key = "hr"; + } else { + $key = "hr.$name"; + } + $default = Setting::get($key); + return apply_filter("nexus_setting_get", $default, $name, ['mode' => $searchBoxId]); + } + + public static function diffInSection(): bool + { + $enableSpecialSection = Setting::get('main.spsct') == 'yes'; + return $enableSpecialSection && apply_filter("hit_and_run_diff_in_section", false); } public function torrent(): \Illuminate\Database\Eloquent\Relations\BelongsTo diff --git a/app/Models/NexusModel.php b/app/Models/NexusModel.php index fcead220..75248fe4 100644 --- a/app/Models/NexusModel.php +++ b/app/Models/NexusModel.php @@ -51,4 +51,19 @@ class NexusModel extends Model return sprintf('%s: %s', nexus_trans('label.deadline'), $raw); } + public static function listStaticProps($dataSource, $textTransPrefix, $onlyKeyValue = false, $valueField = 'text'): array + { + $result = $dataSource; + $keyValue = []; + foreach ($result as $key => &$info) { + $text = $textTransPrefix ? nexus_trans("$textTransPrefix.$key") : $info['text']; + $info['text'] = $text; + $keyValue[$key] = $info[$valueField]; + } + if ($onlyKeyValue) { + return $keyValue; + } + return $result; + } + } diff --git a/app/Models/Plugin.php b/app/Models/Plugin.php new file mode 100644 index 00000000..d18ff2c3 --- /dev/null +++ b/app/Models/Plugin.php @@ -0,0 +1,32 @@ + __('plugin.status.' . $attributes['status']) + ); + } +} diff --git a/app/Models/SearchBox.php b/app/Models/SearchBox.php index 57871d39..0da7f976 100644 --- a/app/Models/SearchBox.php +++ b/app/Models/SearchBox.php @@ -24,6 +24,13 @@ class SearchBox extends NexusModel ]; const EXTRA_TAXONOMY_LABELS = 'taxonomy_labels'; + const SECTION_BROWSE = 'browse'; + const SECTION_SPECIAL = 'special'; + + public static array $sections = [ + self::SECTION_BROWSE => ['text' => 'Browse'], + self::SECTION_SPECIAL => ['text' => 'Special'], + ]; const EXTRA_DISPLAY_COVER_ON_TORRENT_LIST = 'display_cover_on_torrent_list'; const EXTRA_DISPLAY_SEED_BOX_ICON_ON_TORRENT_LIST = 'display_seed_box_icon_on_torrent_list'; @@ -89,6 +96,36 @@ class SearchBox extends NexusModel return array_combine(array_keys(self::$taxonomies), array_keys(self::$taxonomies)); } + public static function listSections($field = null): array + { + $result = []; + foreach (self::$sections as $key => $value) { + $value['text'] = nexus_trans("searchbox.sections.$key"); + $value['mode'] = Setting::get("main.{$key}cat"); + if ($field !== null && isset($value[$field])) { + $result[$key] = $value[$field]; + } else { + $result[$key] = $value; + } + } + return $result; + } + + public function getCustomFieldsAttribute($value): array + { + if (!is_array($value)) { + return explode(',', $value); + } + } + + public function setCustomFieldsAttribute($value) + { + if (is_array($value)) { + $this->attributes['custom_fields'] = implode(',', $value); + } + } + + public function categories(): \Illuminate\Database\Eloquent\Relations\HasMany { return $this->hasMany(Category::class, 'mode'); diff --git a/app/Models/Tag.php b/app/Models/Tag.php index b9d42700..03858aec 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -48,6 +48,14 @@ class Tag extends NexusModel ], ]; + public static function listSpecial(): array + { + return array_filter([ + Setting::get('system.official_tag'), + Setting::get('system.zero_bonus_tag'), + ]); + } + public function torrents(): \Illuminate\Database\Eloquent\Relations\BelongsToMany { return $this->belongsToMany(Torrent::class, 'torrent_tags', 'tag_id', 'torrent_id'); diff --git a/app/Models/Torrent.php b/app/Models/Torrent.php index aa7b762f..9f792ef1 100644 --- a/app/Models/Torrent.php +++ b/app/Models/Torrent.php @@ -3,9 +3,8 @@ namespace App\Models; use App\Repositories\TagRepository; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Casts\Attribute; -use JeroenG\Explorer\Application\Explored; -use Laravel\Scout\Searchable; class Torrent extends NexusModel { @@ -15,11 +14,9 @@ class Torrent extends NexusModel 'size', 'added', 'type', 'numfiles', 'owner', 'nfo', 'sp_state', 'promotion_time_type', 'promotion_until', 'anonymous', 'url', 'pos_state', 'cache_stamp', 'picktype', 'picktime', 'last_reseed', 'pt_gen', 'technical_info', 'leechers', 'seeders', 'cover', 'last_action', - 'times_completed', 'approval_status', 'banned', 'visible', + 'times_completed', 'approval_status', 'banned', 'visible', 'pos_state_until', ]; - private static $globalPromotionState; - const VISIBLE_YES = 'yes'; const VISIBLE_NO = 'no'; @@ -30,11 +27,13 @@ class Torrent extends NexusModel 'added' => 'datetime', 'pt_gen' => 'array', 'promotion_until' => 'datetime', + 'pos_state_until' => 'datetime', ]; public static $commentFields = [ 'id', 'name', 'added', 'visible', 'banned', 'owner', 'sp_state', 'pos_state', 'hr', 'picktype', 'picktime', - 'last_action', 'leechers', 'seeders', 'times_completed', 'views', 'size', 'cover', 'anonymous', 'approval_status' + 'last_action', 'leechers', 'seeders', 'times_completed', 'views', 'size', 'cover', 'anonymous', 'approval_status', + 'pos_state_until', 'category' ]; public static $basicRelations = [ @@ -129,6 +128,16 @@ class Torrent extends NexusModel self::PICK_RECOMMENDED => ['text' => self::PICK_RECOMMENDED, 'color' => '#820084'], ]; + const PROMOTION_TIME_TYPE_GLOBAL = 0; + const PROMOTION_TIME_TYPE_PERMANENT = 1; + const PROMOTION_TIME_TYPE_DEADLINE = 2; + + public static array $promotionTimeTypes = [ + self::PROMOTION_TIME_TYPE_GLOBAL => ['text' => 'Global'], + self::PROMOTION_TIME_TYPE_PERMANENT => ['text' => 'Permanent'], + self::PROMOTION_TIME_TYPE_DEADLINE => ['text' => 'Until'], + ]; + const BONUS_REWARD_VALUES = [50, 100, 200, 500, 1000]; const APPROVAL_STATUS_NONE = 0; @@ -153,6 +162,14 @@ class Torrent extends NexusModel ], ]; + const NFO_VIEW_STYLE_DOS = 'magic'; + const NFO_VIEW_STYLE_WINDOWS = 'latin-1'; + + public static array $nfoViewStyles = [ + self::NFO_VIEW_STYLE_DOS => ['text' => 'DOS-vy'], + self::NFO_VIEW_STYLE_WINDOWS => ['text' => 'Windows-vy'], + ]; + public function getPickInfoAttribute() { $info = self::$pickTypes[$this->picktype] ?? null; @@ -193,11 +210,18 @@ class Torrent extends NexusModel return $spState; } - protected function posStateText(): Attribute + protected function getPosStateTextAttribute() { - return new Attribute( - get: fn($value, $attributes) => nexus_trans('torrent.pos_state_' . $attributes['pos_state']) - ); + $text = nexus_trans('torrent.pos_state_' . $this->pos_state); + if ($this->pos_state != Torrent::POS_STATE_STICKY_NONE) { + if ($this->pos_state_until) { + $append = format_datetime($this->pos_state_until); + } else { + $append = nexus_trans('label.permanent'); + } + $text .= "($append)"; + } + return $text; } protected function approvalStatusText(): Attribute @@ -256,9 +280,30 @@ class Torrent extends NexusModel return $result; } + public static function listPromotionTimeTypes($onlyKeyValue = false, $valueField = 'text'): array + { + return self::listStaticProps(self::$promotionTimeTypes, 'torrent.promotion_time_types', $onlyKeyValue, $valueField); + } + + public static function listPickInfo($onlyKeyValue = false, $valueField = 'text'): array + { + $result = self::$pickTypes; + $keyValue = []; + foreach ($result as $status => &$info) { + $text = nexus_trans('torrent.pick_info.' . $status); + $info['text'] = $text; + $keyValue[$status] = $info[$valueField]; + } + if ($onlyKeyValue) { + return $keyValue; + } + return $result; + } + public function getHrAttribute(): string { - $hrMode = Setting::get('hr.mode'); +// $hrMode = Setting::get('hr.mode'); + $hrMode = HitAndRun::getConfig('mode', $this->basic_category->mode); if ($hrMode == HitAndRun::MODE_GLOBAL) { return self::HR_YES; } diff --git a/app/Models/TorrentCustomField.php b/app/Models/TorrentCustomField.php index 932fecce..d80f713d 100644 --- a/app/Models/TorrentCustomField.php +++ b/app/Models/TorrentCustomField.php @@ -2,13 +2,14 @@ namespace App\Models; +use Nexus\Database\NexusDB; class TorrentCustomField extends NexusModel { protected $table = 'torrents_custom_fields'; protected $fillable = [ - 'name', 'label', 'type', 'required', 'is_single_row', 'options', 'help' + 'name', 'label', 'type', 'required', 'is_single_row', 'options', 'help', 'display', 'priority' ]; public static function getCheckboxOptions(): array @@ -20,4 +21,7 @@ class TorrentCustomField extends NexusModel } return $result; } + public $timestamps = true; + + } diff --git a/app/Models/TorrentCustomFieldValue.php b/app/Models/TorrentCustomFieldValue.php index a4f99c4e..c0e92e16 100644 --- a/app/Models/TorrentCustomFieldValue.php +++ b/app/Models/TorrentCustomFieldValue.php @@ -2,12 +2,23 @@ namespace App\Models; +<<<<<<< HEAD +======= +use Nexus\Database\NexusDB; +>>>>>>> php8 class TorrentCustomFieldValue extends NexusModel { protected $table = 'torrents_custom_field_values'; +<<<<<<< HEAD protected $fillable = [ 'torrent_id', 'custom_field_id', 'custom_field_value', ]; +======= + public $timestamps = true; + + protected $fillable = ['torrent_id', 'custom_field_id', 'custom_field_value', ]; + +>>>>>>> php8 } diff --git a/app/Models/User.php b/app/Models/User.php index 4fa1ccd1..e6f21f62 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -211,7 +211,7 @@ class User extends Authenticatable implements FilamentUser, HasName 'id', 'username', 'email', 'class', 'status', 'added', 'avatar', 'uploaded', 'downloaded', 'seedbonus', 'seedtime', 'leechtime', 'invited_by', 'enabled', 'seed_points', 'last_access', 'invites', - 'lang', 'attendance_card', 'privacy', 'noad', 'downloadpos', + 'lang', 'attendance_card', 'privacy', 'noad', 'downloadpos', 'donoruntil', 'donor' ]; public static function getDefaultUserAttributes(): array @@ -535,4 +535,6 @@ class User extends Authenticatable implements FilamentUser, HasName } + + } diff --git a/app/Models/UserBanLog.php b/app/Models/UserBanLog.php index c6201fbc..081ed06b 100644 --- a/app/Models/UserBanLog.php +++ b/app/Models/UserBanLog.php @@ -7,4 +7,23 @@ class UserBanLog extends NexusModel protected $table = 'user_ban_logs'; protected $fillable = ['uid', 'username', 'operator', 'reason']; + + public static function clearUserBanLogDuplicate() + { + $lists = UserBanLog::query() + ->selectRaw("min(id) as id, uid, count(*) as counts") + ->groupBy('uid') + ->having("counts", ">", 1) + ->get(); + if ($lists->isEmpty()) { + do_log("sql: " . last_query() . ", no data to delete"); + return; + } + $idArr = $lists->pluck("id")->toArray(); + $uidArr = $lists->pluck('uid')->toArray(); + $result = UserBanLog::query()->whereIn("uid", $uidArr)->whereNotIn("id", $idArr)->delete(); + do_log("sql: " . last_query() . ", result: $result"); + } + + } diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 5771074f..03f7fd92 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -22,14 +22,14 @@ class BaseRepository protected function handleAnonymous($username, User $user, User $authenticator, Torrent $torrent = null) { - $canViewAnonymousClass = Setting::get('authority.viewanonymous'); if($user->privacy == "strong" || ($torrent && $torrent->anonymous == 'yes' && $user->id == $torrent->owner)) { //用户强私密,或者种子作者匿名而当前项作者刚好为种子作者 - if($authenticator->class >= $canViewAnonymousClass || $user->id == $authenticator->id) { + $anonymousText = nexus_trans('label.anonymous'); + if(user_can('viewanonymous', false, $authenticator->id) || $user->id == $authenticator->id) { //但当前用户权限可以查看匿名者,或当前用户查看自己的数据,显示个匿名,后边加真实用户名 - return sprintf('匿名(%s)', $username); + return sprintf('%s(%s)', $anonymousText, $username); } else { - return '匿名'; + return $anonymousText; } } else { return $username; @@ -55,4 +55,20 @@ class BaseRepository return User::query()->findOrFail(intval($user), $fields); } + protected function executeCommand($command, $format = 'string'): string|array + { + $append = " 2>&1"; + if (!str_ends_with($command, $append)) { + $command .= $append; + } + do_log("command: $command"); + $result = exec($command, $output, $result_code); + $outputString = implode("\n", $output); + do_log(sprintf('result_code: %s, result: %s, output: %s', $result_code, $result, $outputString)); + if ($result_code != 0) { + throw new \RuntimeException($outputString); + } + return $format == 'string' ? $outputString : $output; + } + } diff --git a/app/Repositories/HitAndRunRepository.php b/app/Repositories/HitAndRunRepository.php index 25448b28..bcb6cac5 100644 --- a/app/Repositories/HitAndRunRepository.php +++ b/app/Repositories/HitAndRunRepository.php @@ -3,13 +3,16 @@ namespace App\Repositories; use App\Models\HitAndRun; use App\Models\Message; +use App\Models\SearchBox; use App\Models\Setting; use App\Models\User; use App\Models\UserBanLog; use Carbon\Carbon; +use Elasticsearch\Endpoints\Search; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; +use Nexus\Database\NexusDB; class HitAndRunRepository extends BaseRepository { @@ -87,12 +90,31 @@ class HitAndRunRepository extends BaseRepository return $query; } - public function cronjobUpdateStatus($uid = null, $torrentId = null, $ignoreTime = false): bool|int + public function cronjobUpdateStatus($uid = null, $torrentId = null, $ignoreTime = false) + { + $diffInSection = HitAndRun::diffInSection(); + $browseMode = Setting::get('main.browsecat'); + $setting = HitAndRun::getConfig('*', $browseMode); + $setting['diff_in_section'] = $diffInSection; + $setting['search_box_id'] = $browseMode; + $this->doCronjobUpdateStatus($setting, $uid, $torrentId, $ignoreTime); + $this->checkAndDisableUser($setting); + + $specialMode = Setting::get('main.specialcat'); + if ($diffInSection && $browseMode != $specialMode) { + $setting = HitAndRun::getConfig('*', $specialMode); + $setting['diff_in_section'] = $diffInSection; + $setting['search_box_id'] = $specialMode; + $this->doCronjobUpdateStatus($setting, $uid, $torrentId, $ignoreTime); + $this->checkAndDisableUser($setting); + } + } + + private function doCronjobUpdateStatus(array $setting, $uid = null, $torrentId = null, $ignoreTime = false) { do_log("uid: $uid, torrentId: $torrentId, ignoreTime: " . var_export($ignoreTime, true)); $size = 1000; $page = 1; - $setting = Setting::get('hr'); if (empty($setting['mode'])) { do_log("H&R not set."); return false; @@ -108,7 +130,7 @@ class HitAndRunRepository extends BaseRepository $query = HitAndRun::query() ->where('status', HitAndRun::STATUS_INSPECTING) ->with([ - 'torrent' => function ($query) {$query->select(['id', 'size', 'name']);}, + 'torrent' => function ($query) {$query->select(['id', 'size', 'name', 'category']);}, 'snatch', 'user' => function ($query) {$query->select(['id', 'username', 'lang', 'class', 'donoruntil', 'enabled']);}, 'user.language', @@ -122,6 +144,12 @@ class HitAndRunRepository extends BaseRepository if (!$ignoreTime) { $query->where('created_at', '<', Carbon::now()->subHours($setting['inspect_time'])); } + if ($setting['diff_in_section']) { + $query->whereHas('torrent.basic_category', function (Builder $query) use ($setting) { + return $query->where('mode', $setting['search_box_id']); + }); + } + $successCounts = 0; $disabledUsers = []; while (true) { @@ -164,7 +192,7 @@ class HitAndRunRepository extends BaseRepository $requireSeedTime = bcmul($setting['seed_time_minimum'], 3600); do_log("$currentLog, targetSeedTime: $targetSeedTime, requireSeedTime: $requireSeedTime"); if ($targetSeedTime >= $requireSeedTime) { - $result = $this->reachedBySeedTime($row); + $result = $this->reachedBySeedTime($row, $setting); if ($result) { $successCounts++; } @@ -176,7 +204,7 @@ class HitAndRunRepository extends BaseRepository $requireShareRatio = $setting['ignore_when_ratio_reach']; do_log("$currentLog, targetShareRatio: $targetShareRatio, requireShareRatio: $requireShareRatio"); if ($targetShareRatio >= $requireShareRatio) { - $result = $this->reachedByShareRatio($row); + $result = $this->reachedByShareRatio($row, $setting); if ($result) { $successCounts++; } @@ -185,7 +213,7 @@ class HitAndRunRepository extends BaseRepository //unreached if ($row->created_at->addHours($setting['inspect_time'])->lte(Carbon::now())) { - $result = $this->unreached($row, !isset($disabledUsers[$row->uid])); + $result = $this->unreached($row, $setting, !isset($disabledUsers[$row->uid])); if ($result) { $successCounts++; $disabledUsers[$row->uid] = true; @@ -212,15 +240,15 @@ class HitAndRunRepository extends BaseRepository ]; } - private function reachedByShareRatio(HitAndRun $hitAndRun): bool + private function reachedByShareRatio(HitAndRun $hitAndRun, array $setting): bool { do_log(__METHOD__); $comment = nexus_trans('hr.reached_by_share_ratio_comment', [ 'now' => Carbon::now()->toDateTimeString(), - 'seed_time_minimum' => Setting::get('hr.seed_time_minimum'), + 'seed_time_minimum' => $setting['seed_time_minimum'], 'seed_time' => bcdiv($hitAndRun->snatch->seedtime, 3600, 1), 'share_ratio' => get_hr_ratio($hitAndRun->snatch->uploaded, $hitAndRun->snatch->downloaded), - 'ignore_when_ratio_reach' => Setting::get('hr.ignore_when_ratio_reach'), + 'ignore_when_ratio_reach' => $setting['ignore_when_ratio_reach'], ], $hitAndRun->user->locale); $update = [ 'comment' => $comment @@ -228,13 +256,13 @@ class HitAndRunRepository extends BaseRepository return $this->inspectingToReached($hitAndRun, $update, __FUNCTION__); } - private function reachedBySeedTime(HitAndRun $hitAndRun): bool + private function reachedBySeedTime(HitAndRun $hitAndRun, array $setting): bool { do_log(__METHOD__); $comment = nexus_trans('hr.reached_by_seed_time_comment', [ 'now' => Carbon::now()->toDateTimeString(), 'seed_time' => bcdiv($hitAndRun->snatch->seedtime, 3600, 1), - 'seed_time_minimum' => Setting::get('hr.seed_time_minimum') + 'seed_time_minimum' => $setting['seed_time_minimum'], ], $hitAndRun->user->locale); $update = [ 'comment' => $comment @@ -271,16 +299,16 @@ class HitAndRunRepository extends BaseRepository return true; } - private function unreached(HitAndRun $hitAndRun, $disableUser = true): bool + private function unreached(HitAndRun $hitAndRun, array $setting, $disableUser = true): bool { do_log(sprintf('hitAndRun: %s, disableUser: %s', $hitAndRun->toJson(), var_export($disableUser, true))); $comment = nexus_trans('hr.unreached_comment', [ 'now' => Carbon::now()->toDateTimeString(), 'seed_time' => bcdiv($hitAndRun->snatch->seedtime, 3600, 1), - 'seed_time_minimum' => Setting::get('hr.seed_time_minimum'), + 'seed_time_minimum' => $setting['seed_time_minimum'], 'share_ratio' => get_hr_ratio($hitAndRun->snatch->uploaded, $hitAndRun->snatch->downloaded), 'torrent_size' => mksize($hitAndRun->torrent->size), - 'ignore_when_ratio_reach' => Setting::get('hr.ignore_when_ratio_reach') + 'ignore_when_ratio_reach' => $setting['ignore_when_ratio_reach'] ], $hitAndRun->user->locale); $update = [ 'status' => HitAndRun::STATUS_UNREACHED, @@ -307,31 +335,43 @@ class HitAndRunRepository extends BaseRepository ]; Message::query()->insert($message); - if (!$disableUser) { - do_log("[DO_NOT_DISABLE_USER], return"); - return true; + return true; + } + + private function checkAndDisableUser(array $setting): void + { + $disableCounts = HitAndRun::getConfig('ban_user_when_counts_reach', $setting['search_box_id']); + $query = HitAndRun::query() + ->selectRaw("count(*) as counts, uid") + ->where('status', HitAndRun::STATUS_UNREACHED) + ->groupBy('uid') + ->having("counts", '>=', $disableCounts) + ; + if ($setting['diff_in_section']) { + $query->whereHas('torrent.basic_category', function (Builder $query) use ($setting) { + return $query->where('mode', $setting['search_box_id']); + }); } - if ($hitAndRun->user->enabled == 'no') { - do_log("[USER_ALREADY_DISABLED], return"); - return true; + $result = $query->get(); + if ($result->isEmpty()) { + do_log("No user to disable"); + return; } - //disable user - /** @var User $user */ - $user = $hitAndRun->user; - $counts = $user->hitAndRuns()->where('status', HitAndRun::STATUS_UNREACHED)->count(); - $disableCounts = Setting::get('hr.ban_user_when_counts_reach'); - do_log("user: {$user->id}, H&R counts: $counts, disableCounts: $disableCounts", 'notice'); - if ($counts >= $disableCounts) { - do_log("[DISABLE_USER_DUE_TO_H&R_UNREACHED]", 'notice'); - $comment = nexus_trans('hr.unreached_disable_comment', [], $user->locale); - $user->updateWithModComment(['enabled' => User::ENABLED_NO], $comment); + $users = User::query() + ->with('language') + ->where('enabled', User::ENABLED_YES) + ->find($result->pluck('id')->toArray(), ['id', 'username', 'lang']); + foreach ($users as $user) { + $locale = $user->locale; + $comment = nexus_trans('hr.unreached_disable_comment', [], $locale); + $user->updateWithModComment(['enabled' => User::ENABLED_NO], sprintf('%s - %s', date('Y-m-d'), $comment)); $message = [ - 'receiver' => $hitAndRun->uid, + 'receiver' => $user->id, 'added' => Carbon::now()->toDateTimeString(), 'subject' => $comment, 'msg' => nexus_trans('hr.unreached_disable_message_content', [ 'ban_user_when_counts_reach' => Setting::get('hr.ban_user_when_counts_reach'), - ], $hitAndRun->user->locale), + ], $locale), ]; Message::query()->insert($message); $userBanLog = [ @@ -340,30 +380,59 @@ class HitAndRunRepository extends BaseRepository 'reason' => $comment ]; UserBanLog::query()->insert($userBanLog); + do_log("Disable user: " . nexus_json_encode($userBanLog)); } - - return true; } public function getStatusStats($uid, $formatted = true) { - $results = HitAndRun::query()->where('uid', $uid) - ->selectRaw('status, count(*) as counts') - ->groupBy('status') - ->get() - ->pluck('counts', 'status'); - if ($formatted) { - return sprintf( - '%s/%s/%s', - $results->get(HitAndRun::STATUS_INSPECTING, 0), - $results->get(HitAndRun::STATUS_UNREACHED, 0), - Setting::get('hr.ban_user_when_counts_reach') - ); + $diffInSection = HitAndRun::diffInSection(); + if ($diffInSection) { + $sql = "select hit_and_runs.status, categories.mode, count(*) as counts from hit_and_runs left join torrents on torrents.id = hit_and_runs.torrent_id left join categories on categories.id = torrents.category where hit_and_runs.uid = $uid group by hit_and_runs.status, categories.mode"; + } else { + $sql = "select hit_and_runs.status, count(*) as counts from hit_and_runs where uid = $uid group by status"; + } + $results = NexusDB::select($sql); + do_log("user: $uid, sql: $sql, results: " . json_encode($results)); + if (!$formatted) { + return $results; + } + if ($diffInSection) { + $grouped = []; + foreach ($results as $item) { + $grouped[$item['mode']][$item['status']] = $item['counts']; + } + $out = []; + foreach (SearchBox::listSections() as $key => $info) { + $out[] = sprintf( + '%s: %s/%s/%s', + $info['text'], + $grouped[$info['mode']][HitAndRun::STATUS_INSPECTING] ?? 0, + $grouped[$info['mode']][HitAndRun::STATUS_UNREACHED] ?? 0, + HitAndRun::getConfig('ban_user_when_counts_reach', $info['mode']) + ); + } + return implode(" ", $out); + } else { + $grouped = []; + foreach ($results as $item) { + $grouped[$item['status']] = $item['counts']; + } + foreach (SearchBox::listSections() as $key => $info) { + if ($key == SearchBox::SECTION_BROWSE) { + return sprintf( + '%s/%s/%s', + $grouped[HitAndRun::STATUS_INSPECTING] ?? 0, + $grouped[HitAndRun::STATUS_UNREACHED] ?? 0, + HitAndRun::getConfig('ban_user_when_counts_reach', $info['mode']) + ); + } + } } - return $results; - } + + public function listStatus(): array { $results = []; @@ -407,6 +476,16 @@ class HitAndRunRepository extends BaseRepository private function getCanPardonStatus(): array { - return [HitAndRun::STATUS_INSPECTING, HitAndRun::STATUS_UNREACHED]; + return HitAndRun::CAN_PARDON_STATUS; + } + + public function renderOnUploadPage($value, $searchBoxId): string + { + if (HitAndRun::getConfig('mode', $searchBoxId) == \App\Models\HitAndRun::MODE_MANUAL && user_can('torrent_hr')) { + $hrRadio = sprintf('', $searchBoxId, $value == 0 ? ' checked' : ''); + $hrRadio .= sprintf('', $searchBoxId, $value == 1 ? ' checked' : ''); + return tr('H&R', $hrRadio, 1, "mode_$searchBoxId", true); + } + return ''; } } diff --git a/app/Repositories/PluginRepository.php b/app/Repositories/PluginRepository.php new file mode 100644 index 00000000..396f49ac --- /dev/null +++ b/app/Repositories/PluginRepository.php @@ -0,0 +1,187 @@ +doCronjob('install', $id, $force, Plugin::STATUS_PRE_INSTALL, Plugin::STATUS_INSTALLING); + } + if ($action == 'delete' || $action === null) { + $this->doCronjob('delete', $id, $force, Plugin::STATUS_PRE_DELETE, Plugin::STATUS_DELETING); + } + if ($action == 'update' || $action === null) { + $this->doCronjob('update', $id, $force, Plugin::STATUS_PRE_UPDATE, Plugin::STATUS_UPDATING); + } + } + + private function doCronjob($action, $id, $force, $preStatus, $doingStatus) + { + $query = Plugin::query(); + if (!$force) { + $query->where('status', $preStatus); + } + if ($id !== null) { + $query->where("id", $id); + } + $list = $query->get(); + if ($list->isEmpty()) { + do_log("No plugin need to be $action..."); + return; + } + $idArr = $list->pluck('id')->toArray(); + Plugin::query()->whereIn('id', $idArr)->update(['status' => $doingStatus]); + foreach ($list as $item) { + match ($action) { + 'install' => $this->doInstall($item), + 'update' => $this->doUpdate($item), + 'delete' => $this->doDelete($item), + default => throw new \InvalidArgumentException("Invalid action: $action") + }; + } + } + + public function doInstall(Plugin $plugin) + { + $packageName = $plugin->package_name; + try { + $this->execComposerConfig($plugin); + $this->execComposerRequire($plugin); + $output = $this->execPluginInstall($plugin); + $version = $this->getInstalledVersion($packageName); + do_log("success install plugin: $packageName version: $version"); + $update = [ + 'status' => Plugin::STATUS_NORMAL, + 'status_result' => $output, + 'installed_version' => $version + ]; + } catch (\Throwable $throwable) { + $update = [ + 'status' => Plugin::STATUS_INSTALL_FAILED, + 'status_result' => $throwable->getMessage() + ]; + do_log("fail install plugin: " . $packageName); + } finally { + $this->updateResult($plugin, $update); + } + + } + + public function doDelete(Plugin $plugin) + { + $packageName = $plugin->package_name; + $removeSuccess = true; + try { + $output = $this->execComposerRemove($plugin); + do_log("success remove plugin: $packageName"); + $update = [ + 'status' => Plugin::STATUS_NOT_INSTALLED, + 'status_result' => $output, + 'installed_version' => null, + ]; + } catch (\Throwable $throwable) { + $update = [ + 'status' => Plugin::STATUS_DELETE_FAILED, + 'status_result' => $throwable->getMessage() + ]; + $removeSuccess = false; + do_log("fail remove plugin: " . $packageName); + } finally { + if ($removeSuccess) { + $plugin->delete(); + } else { + $this->updateResult($plugin, $update); + } + } + + } + + public function doUpdate(Plugin $plugin) + { + $packageName = $plugin->package_name; + try { + $output = $this->execComposerUpdate($plugin); + $this->execPluginInstall($plugin); + $version = $this->getInstalledVersion($packageName); + do_log("success update plugin: $packageName to version: $version"); + $update = [ + 'status' => Plugin::STATUS_NORMAL, + 'status_result' => $output, + 'installed_version' => $version, + ]; + } catch (\Throwable $throwable) { + $update = [ + 'status' => Plugin::STATUS_UPDATE_FAILED, + 'status_result' => $throwable->getMessage() + ]; + do_log("fail update plugin: " . $packageName); + } finally { + $this->updateResult($plugin, $update); + } + + } + + private function getRepositoryKey(Plugin $plugin) + { + return str_replace("xiaomlove/nexusphp-", "", $plugin->package_name); + } + + private function execComposerConfig(Plugin $plugin) + { + $command = sprintf("composer config repositories.%s git %s", $this->getRepositoryKey($plugin), $plugin->remote_url); + do_log("[COMPOSER_CONFIG]: $command"); + return $this->executeCommand($command); + } + + private function execComposerRequire(Plugin $plugin) + { + $command = sprintf("composer require %s", $plugin->package_name); + do_log("[COMPOSER_REQUIRE]: $command"); + return $this->executeCommand($command); + } + + private function execComposerRemove(Plugin $plugin) + { + $command = sprintf("composer remove %s", $plugin->package_name); + do_log("[COMPOSER_REMOVE]: $command"); + return $this->executeCommand($command); + } + + private function execComposerUpdate(Plugin $plugin) + { + $command = sprintf("composer update %s", $plugin->package_name); + do_log("[COMPOSER_UPDATE]: $command"); + return $this->executeCommand($command); + } + + private function execPluginInstall(Plugin $plugin) + { + $command = sprintf("php artisan plugin install %s", $plugin->package_name); + do_log("[PLUGIN_INSTALL]: $command"); + return $this->executeCommand($command); + } + + private function updateResult(Plugin $plugin, array $update) + { + $update['status_result'] = $update['status_result'] . "\n\nREQUEST_ID: " . nexus()->getRequestId(); + do_log("[UPDATE]: " . json_encode($update)); + $plugin->update($update); + } + + public function getInstalledVersion($packageName) + { + $command = sprintf('composer info |grep -E %s', $packageName); + $result = $this->executeCommand($command); + $parts = preg_split("/[\s]+/", trim($result)); + $version = $parts[1]; + if (str_contains($version, 'dev')) { + $version .= " $parts[2]"; + } + return $version; + } + + +} diff --git a/app/Repositories/SeedBoxRepository.php b/app/Repositories/SeedBoxRepository.php index 13666832..2649046a 100644 --- a/app/Repositories/SeedBoxRepository.php +++ b/app/Repositories/SeedBoxRepository.php @@ -154,7 +154,7 @@ class SeedBoxRepository extends BaseRepository private function clearCache() { - return true; + NexusDB::cache_del('SEED_BOX_RECORD_APPROVAL_NONE'); // SeedBoxRecordUpdated::dispatch(); } diff --git a/app/Repositories/TagRepository.php b/app/Repositories/TagRepository.php index 13a84e56..ab107dd0 100644 --- a/app/Repositories/TagRepository.php +++ b/app/Repositories/TagRepository.php @@ -11,6 +11,8 @@ class TagRepository extends BaseRepository { private static $orderByFieldIdString; + private static $allTags; + public function getList(array $params) { $query = $this->createBasicQuery(); @@ -48,10 +50,14 @@ class TagRepository extends BaseRepository return Tag::query()->orderBy('priority', 'desc')->orderBy('id', 'desc'); } - public function renderCheckbox(array $checked = []): string + public function renderCheckbox(array $checked = [], $ignorePermission = false): string { $html = ''; - $results = $this->createBasicQuery()->get(); + $results = $this->listAll(); + if (!$ignorePermission && !user_can('torrent-set-special-tag')) { + $specialTags = Tag::listSpecial(); + $results = $results->filter(fn ($item) => !in_array($item->id, $specialTags)); + } foreach ($results as $value) { $html .= sprintf( '', @@ -61,20 +67,22 @@ class TagRepository extends BaseRepository return $html; } - public function renderSpan(Collection $tagKeyById, array $renderIdArr = [], $withFilterLink = false): string + public function renderSpan(array $renderIdArr = [], $withFilterLink = false): string { $html = ''; - foreach ($renderIdArr as $tagId) { - $value = $tagKeyById->get($tagId); - if ($value) { - $item = sprintf( - "%s", - $value->color, $value->font_color, $value->border_radius, $value->font_size, $value->margin, $value->padding, $value->name - ); - if ($withFilterLink) { - $html .= sprintf('%s', $tagId, $item); - } else { - $html .= $item; + foreach ($this->listAll() as $value) { + if (in_array($value->id, $renderIdArr) || (isset($renderIdArr[0]) && $renderIdArr[0] == '*')) { + $tagId = $value->id; + if ($value) { + $item = sprintf( + "%s", + $value->color, $value->font_color, $value->border_radius, $value->font_size, $value->margin, $value->padding, $value->name + ); + if ($withFilterLink) { + $html .= sprintf('%s', $tagId, $item); + } else { + $html .= $item; + } } } } @@ -141,5 +149,28 @@ class TagRepository extends BaseRepository return self::$orderByFieldIdString; } + public function listAll() + { + if (empty(self::$allTags)) { + self::$allTags = self::createBasicQuery()->get(); + } + return self::$allTags; + } + + public function buildSelect($name, $value): string + { + $list = $this->listAll(); + $select = sprintf(''; + return $select; + } + } diff --git a/app/Repositories/ToolRepository.php b/app/Repositories/ToolRepository.php index eaf51a18..a63182ea 100644 --- a/app/Repositories/ToolRepository.php +++ b/app/Repositories/ToolRepository.php @@ -24,7 +24,7 @@ class ToolRepository extends BaseRepository { const BACKUP_EXCLUDES = ['vendor', 'node_modules', '.git', '.idea', '.settings', '.DS_Store', '.github']; - public function backupWeb($method = null): array + public function backupWeb($method = null, $transfer = false): array { $webRoot = base_path(); $dirName = basename($webRoot); @@ -76,10 +76,13 @@ class ToolRepository extends BaseRepository $result_code = 0; do_log("No tar command, use zip."); } - return compact('result_code', 'filename'); + if (!$transfer) { + return compact('result_code', 'filename'); + } + return $this->transfer($filename, $result_code); } - public function backupDatabase(): array + public function backupDatabase($transfer = false): array { $connectionName = config('database.default'); $config = config("database.connections.$connectionName"); @@ -93,10 +96,13 @@ class ToolRepository extends BaseRepository "command: %s, output: %s, result_code: %s, result: %s, filename: %s", $command, json_encode($output), $result_code, $result, $filename )); - return compact('result_code', 'filename'); + if (!$transfer) { + return compact('result_code', 'filename'); + } + return $this->transfer($filename, $result_code); } - public function backupAll($method = null): array + public function backupAll($method = null, $transfer = false): array { $backupWeb = $this->backupWeb($method); if ($backupWeb['result_code'] != 0) { @@ -134,8 +140,10 @@ class ToolRepository extends BaseRepository $result_code = 0; do_log("No tar command, use zip."); } - return compact('result_code', 'filename'); - + if (!$transfer) { + return compact('result_code', 'filename'); + } + return $this->transfer($filename, $result_code); } /** @@ -178,27 +186,33 @@ class ToolRepository extends BaseRepository } $backupResult = $this->backupAll(); do_log("Backup all result: " . json_encode($backupResult)); - if ($backupResult['result_code'] != 0) { - throw new \RuntimeException("Backup all fail."); - } - $filename = $backupResult['filename']; + $transferResult = $this->transfer($backupResult['filename'], $backupResult['result_code'], $setting); + $backupResult['transfer_result'] = $transferResult; + do_log("[BACKUP_ALL_DONE]: " . json_encode($backupResult)); + return $backupResult; + } + public function transfer($filename, $result_code, $setting = null): array + { + if ($result_code != 0) { + throw new \RuntimeException("file: $filename backup fail!"); + } + $result = compact('filename', 'result_code'); + if (empty($setting)) { + $setting = Setting::get('backup'); + } $saveResult = $this->saveToGoogleDrive($setting, $filename); do_log("[BACKUP_GOOGLE_DRIVE]: $saveResult"); - $backupResult['google_drive'] = $saveResult; + $result['google_drive'] = $saveResult; $saveResult = $this->saveToFtp($setting, $filename); do_log("[BACKUP_FTP]: $saveResult"); - $backupResult['ftp'] = $saveResult; + $result['ftp'] = $saveResult; $saveResult = $this->saveToSftp($setting, $filename); do_log("[BACKUP_SFTP]: $saveResult"); - $backupResult['sftp'] = $saveResult; - - do_log("[BACKUP_ALL_DONE]: " . json_encode($backupResult)); - - return $backupResult; - + $result['sftp'] = $saveResult; + return $result; } private function saveToGoogleDrive(array $setting, $filename): bool|string diff --git a/app/Repositories/TorrentRepository.php b/app/Repositories/TorrentRepository.php index 0f10aa2b..2d161b7e 100644 --- a/app/Repositories/TorrentRepository.php +++ b/app/Repositories/TorrentRepository.php @@ -598,11 +598,72 @@ class TorrentRepository extends BaseRepository } - public function setPosState($id, $posState): int + public function setPosState($id, $posState, $posStateUntil = null): int { user_can('torrentsticky', true); + if ($posState == Torrent::POS_STATE_STICKY_NONE) { + $posStateUntil = null; + } + if ($posStateUntil && Carbon::parse($posStateUntil)->lte(now())) { + $posState = Torrent::POS_STATE_STICKY_NONE; + $posStateUntil = null; + } + $update = [ + 'pos_state' => $posState, + 'pos_state_until' => $posStateUntil, + ]; $idArr = Arr::wrap($id); - return Torrent::query()->whereIn('id', $idArr)->update(['pos_state' => $posState]); + return Torrent::query()->whereIn('id', $idArr)->update($update); + } + + public function setPickType($id, $pickType): int + { + user_can('torrentmanage', true); + if (!isset(Torrent::$pickTypes[$pickType])) { + throw new \InvalidArgumentException("Invalid pickType: $pickType"); + } + $update = [ + 'picktype' => $pickType, + 'picktime' => now(), + ]; + $idArr = Arr::wrap($id); + return Torrent::query()->whereIn('id', $idArr)->update($update); + } + + public function setHr($id, $hrStatus): int + { + user_can('torrentmanage', true); + if (!isset(Torrent::$hrStatus[$hrStatus])) { + throw new \InvalidArgumentException("Invalid hrStatus: $hrStatus"); + } + $update = [ + 'hr' => $hrStatus, + ]; + $idArr = Arr::wrap($id); + return Torrent::query()->whereIn('id', $idArr)->update($update); + } + + public function setSpState($id, $spState, $promotionTimeType, $promotionUntil = null): int + { + user_can('torrentonpromotion', true); + if (!isset(Torrent::$promotionTypes[$spState])) { + throw new \InvalidArgumentException("Invalid spState: $spState"); + } + if (!isset(Torrent::$promotionTimeTypes[$promotionTimeType])) { + throw new \InvalidArgumentException("Invalid promotionTimeType: $promotionTimeType"); + } + if (in_array($promotionTimeType, [Torrent::PROMOTION_TIME_TYPE_GLOBAL, Torrent::PROMOTION_TIME_TYPE_PERMANENT])) { + $promotionUntil = null; + } elseif (!$promotionUntil || Carbon::parse($promotionUntil)->lte(now())) { + throw new \InvalidArgumentException("Invalid promotionUntil: $promotionUntil"); + } + $update = [ + 'sp_state' => $spState, + 'promotion_time_type' => $promotionTimeType, + 'promotion_until' => $promotionUntil, + ]; + $idArr = Arr::wrap($id); + return Torrent::query()->whereIn('id', $idArr)->update($update); } public function buildUploadFieldInput($name, $value, $noteText, $btnText): string diff --git a/app/Repositories/TrackerRepository.php b/app/Repositories/TrackerRepository.php index 251151be..c438d3b0 100644 --- a/app/Repositories/TrackerRepository.php +++ b/app/Repositories/TrackerRepository.php @@ -593,7 +593,7 @@ class TrackerRepository extends BaseRepository $notSeedBoxMaxSpeedMbps = Setting::get('seed_box.not_seed_box_max_speed'); do_log("upSpeedMbps: $upSpeedMbps, notSeedBoxMaxSpeedMbps: $notSeedBoxMaxSpeedMbps"); if ($upSpeedMbps > $notSeedBoxMaxSpeedMbps) { - (new \App\Repositories\UserRepository())->updateDownloadPrivileges(null, $user, 'no'); + (new \App\Repositories\UserRepository())->updateDownloadPrivileges(null, $user, 'no', 'upload_over_speed'); do_log("user: {$user->id} downloading privileges have been disabled! (over speed)", 'error'); throw new TrackerException("Your downloading privileges have been disabled! (over speed)"); } @@ -1092,7 +1092,8 @@ class TrackerRepository extends BaseRepository if ($user->isDonating()) { return; } - $hrMode = Setting::get('hr.mode'); +// $hrMode = Setting::get('hr.mode'); + $hrMode = HitAndRun::getConfig('mode', $torrent->basic_category->mode); if ($hrMode == HitAndRun::MODE_DISABLED) { return; } diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index fccf5f78..f09af9c9 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -162,12 +162,15 @@ class UserRepository extends BaseRepository return User::listClass(); } - public function disableUser(User $operator, $uid, $reason) + public function disableUser(User $operator, $uid, $reason = '') { $targetUser = User::query()->findOrFail($uid, ['id', 'enabled', 'username', 'class']); if ($targetUser->enabled == User::ENABLED_NO) { throw new NexusException('Already disabled !'); } + if (empty($reason)) { + $reason = nexus_trans("user.disable_by_admin"); + } $this->checkPermission($operator, $targetUser); $banLog = [ 'uid' => $uid, @@ -326,7 +329,7 @@ class UserRepository extends BaseRepository } - public function updateDownloadPrivileges($operator, $user, $status) + public function updateDownloadPrivileges($operator, $user, $status, $disableReasonKey = null) { if (!in_array($status, ['yes', 'no'])) { throw new \InvalidArgumentException("Invalid status: $status"); @@ -345,8 +348,12 @@ class UserRepository extends BaseRepository if ($status == 'no') { $update = ['downloadpos' => 'no']; $modComment = date('Y-m-d') . " - Download disable by " . $operatorUsername; - $message['subject'] = nexus_trans('message.download_disable.subject', [], $targetUser->locale); - $message['msg'] = nexus_trans('message.download_disable.body', ['operator' => $operatorUsername], $targetUser->locale); + $msgTransPrefix = "message.download_disable"; + if ($disableReasonKey !== null) { + $msgTransPrefix .= "_$disableReasonKey"; + } + $message['subject'] = nexus_trans("$msgTransPrefix.subject", [], $targetUser->locale); + $message['msg'] = nexus_trans("$msgTransPrefix.body", ['operator' => $operatorUsername], $targetUser->locale); } else { $update = ['downloadpos' => 'yes']; $modComment = date('Y-m-d') . " - Download enable by " . $operatorUsername; @@ -463,23 +470,41 @@ class UserRepository extends BaseRepository $user = $this->getUser($user); $metaKey = $metaData['meta_key']; $allowMultiple = UserMeta::$metaKeys[$metaKey]['multiple']; + $log = "user: {$user->id}, metaKey: $metaKey, allowMultiple: $allowMultiple"; if ($allowMultiple) { //Allow multiple, just insert $result = $user->metas()->create($metaData); + $log .= ", allowMultiple, just insert"; } else { $metaExists = $user->metas()->where('meta_key', $metaKey)->first(); + $log .= ", metaExists: " . ($metaExists->id ?? ''); if (!$metaExists) { $result = $user->metas()->create($metaData); + $log .= ", meta not exists, just create"; } else { - if (empty($keyExistsUpdates)) { - $keyExistsUpdates = ['updated_at' => now()]; + $log .= ", meta exists"; + $keyExistsUpdates['updated_at'] = now(); + if (!empty($keyExistsUpdates['duration'])) { + $log .= ", has duration: {$keyExistsUpdates['duration']}"; + if ($metaExists->deadline && $metaExists->deadline->gte(now())) { + $log .= ", not expire"; + $keyExistsUpdates['deadline'] = $metaExists->deadline->addDays($keyExistsUpdates['duration']); + } else { + $log .= ", expired or not set"; + $keyExistsUpdates['deadline'] = now()->addDays($keyExistsUpdates['duration']); + } + unset($keyExistsUpdates['duration']); + } else { + $keyExistsUpdates['deadline'] = null; } + $log .= ", update: " . json_encode($keyExistsUpdates); $result = $metaExists->update($keyExistsUpdates); } } if ($result) { clear_user_cache($user->id, $user->passkey); } + do_log($log); return $result; } @@ -497,9 +522,13 @@ class UserRepository extends BaseRepository return true; } - public function destroy($id) + public function destroy($id, $reasonKey = 'user.destroy_by_admin') { - user_can('user-delete', true); + if (!isRunningInConsole()) { + user_can('user-delete', true); + } + $uidArr = Arr::wrap($id); + $users = User::query()->with('language')->whereIn('id', $uidArr)->get(['id', 'username', 'lang']); $tables = [ 'users' => 'id', 'hit_and_runs' => 'uid', @@ -508,9 +537,18 @@ class UserRepository extends BaseRepository 'exam_progress' => 'uid', ]; foreach ($tables as $table => $key) { - \Nexus\Database\NexusDB::table($table)->where($key, $id)->delete(); + \Nexus\Database\NexusDB::table($table)->whereIn($key, $uidArr)->delete(); } - do_log("[DESTROY_USER]: $id", 'error'); + do_log("[DESTROY_USER]: " . json_encode($uidArr), 'error'); + $userBanLogs = []; + foreach ($users as $user) { + $userBanLogs[] = [ + 'uid' => $user->id, + 'username' => $user->username, + 'reason' => nexus_trans($reasonKey, [], $user->locale) + ]; + } + UserBanLog::query()->insert($userBanLogs); return true; } diff --git a/database/migrations/2022_09_16_164224_create_plugins_table.php b/database/migrations/2022_09_16_164224_create_plugins_table.php new file mode 100644 index 00000000..b904aca8 --- /dev/null +++ b/database/migrations/2022_09_16_164224_create_plugins_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('display_name')->nullable(true); + $table->string('package_name')->nullable(false)->unique(); + $table->string('remote_url')->nullable(true); + $table->string('installed_version')->nullable(true); + $table->text('description')->nullable(true); + $table->integer('status')->default(-1); + $table->text('status_result')->nullable(true); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('plugins'); + } +}; diff --git a/database/migrations/2022_09_17_150606_add_pos_state_until_to_torrents_table.php b/database/migrations/2022_09_17_150606_add_pos_state_until_to_torrents_table.php new file mode 100644 index 00000000..7435ecc3 --- /dev/null +++ b/database/migrations/2022_09_17_150606_add_pos_state_until_to_torrents_table.php @@ -0,0 +1,32 @@ +dateTime('pos_state_until')->nullable(true)->after('pos_state'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('torrents', function (Blueprint $table) { + $table->dropColumn('pos_state_until'); + }); + } +}; diff --git a/database/migrations/2022_09_19_043749_add_display_to_torrents_custom_fields_table.php b/database/migrations/2022_09_19_043749_add_display_to_torrents_custom_fields_table.php new file mode 100644 index 00000000..315d4abe --- /dev/null +++ b/database/migrations/2022_09_19_043749_add_display_to_torrents_custom_fields_table.php @@ -0,0 +1,33 @@ +text('display')->nullable(true)->after('help'); + $table->integer('priority')->default(0)->after('display'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('torrents_custom_fields', function (Blueprint $table) { + $table->dropColumn('display', 'priority'); + }); + } +}; diff --git a/database/migrations/2022_10_13_002653_add_ip_to_complains_table.php b/database/migrations/2022_10_13_002653_add_ip_to_complains_table.php new file mode 100644 index 00000000..84eca8be --- /dev/null +++ b/database/migrations/2022_10_13_002653_add_ip_to_complains_table.php @@ -0,0 +1,38 @@ +string('ip')->nullable(true); + }); + Schema::table('complain_replies', function (Blueprint $table) { + $table->string('ip')->nullable(true); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('complains', function (Blueprint $table) { + $table->dropColumn('ip'); + }); + Schema::table('complain_replies', function (Blueprint $table) { + $table->dropColumn('ip'); + }); + } +}; diff --git a/include/cleanup.php b/include/cleanup.php index 3f2c22a3..c23af043 100644 --- a/include/cleanup.php +++ b/include/cleanup.php @@ -204,7 +204,7 @@ function ban_user_with_leech_warning_expired() } -function delete_user(\Illuminate\Database\Eloquent\Builder $query, $reasonKey) +function disable_user(\Illuminate\Database\Eloquent\Builder $query, $reasonKey) { $results = $query->where('enabled', \App\Models\User::ENABLED_YES)->get(['id', 'username', 'modcomment', 'lang']); if ($results->isEmpty()) { @@ -228,7 +228,7 @@ function delete_user(\Illuminate\Database\Eloquent\Builder $query, $reasonKey) ); sql_query($sql); \App\Models\UserBanLog::query()->insert($userBanLogData); - do_log("[DELETE_USER]($reasonKey): " . implode(', ', $uidArr)); + do_log("[DISABLE_USER]($reasonKey): " . implode(', ', $uidArr)); return $uidArr; } @@ -265,22 +265,36 @@ function docleanup($forceAll = 0, $printProgress = false) { if (mysql_num_rows($res) > 0) { $haremAdditionFactor = get_setting('bonus.harem_addition'); + $officialAdditionFactor = get_setting('bonus.official_addition'); while ($arr = mysql_fetch_assoc($res)) //loop for different users { + $userInfo = get_user_row($arr['userid']); + $isDonor = is_donor($userInfo); $seedBonusResult = calculate_seed_bonus($arr['userid']); - $dividend = 3600 / $autoclean_interval_one; - $all_bonus = $seedBonusResult['all_bonus'] / $dividend; - $seed_points = $seedBonusResult['seed_points'] / $dividend; - $bonusLog = "[CLEANUP_CALCULATE_SEED_BONUS], user: {$arr['userid']}, seedBonusResult: " . nexus_json_encode($seedBonusResult) . ", all_bonus: $all_bonus, seed_points: $seed_points"; - if ($haremAdditionFactor > 0) { - $haremAddition = calculate_harem_addition($arr['userid']) * $haremAdditionFactor / $dividend; - $all_bonus += $haremAddition; - $bonusLog .= ", haremAddition: $haremAddition, new all_bonus: $all_bonus"; + $bonusLog = "[CLEANUP_CALCULATE_SEED_BONUS], user: {$arr['userid']}, seedBonusResult: " . nexus_json_encode($seedBonusResult); + $all_bonus = $seedBonusResult['seed_bonus']; + $bonusLog .= ", all_bonus: $all_bonus"; + if ($isDonor) { + $all_bonus = $all_bonus * $donortimes_bonus; + $bonusLog .= ", isDonor, donortimes_bonus: $donortimes_bonus, all_bonus: $all_bonus"; } + if ($officialAdditionFactor > 0) { + $officialAddition = $seedBonusResult['official_bonus'] * $officialAdditionFactor; + $all_bonus += $officialAddition; + $bonusLog .= ", officialAdditionFactor: $officialAdditionFactor, official_bonus: {$seedBonusResult['official_bonus']}, officialAddition: $officialAddition, all_bonus: $all_bonus"; + } + if ($haremAdditionFactor > 0) { + $haremBonus = calculate_harem_addition($arr['userid']); + $haremAddition = $haremBonus * $haremAdditionFactor; + $all_bonus += $haremAddition; + $bonusLog .= ", haremAdditionFactor: $haremAdditionFactor, haremBonus: $haremBonus, haremAddition: $haremAddition, all_bonus: $all_bonus"; + } + $dividend = 3600 / $autoclean_interval_one; + $all_bonus = $all_bonus / $dividend; + $seed_points = $seedBonusResult['seed_points'] / $dividend; $sql = "update users set seed_points = ifnull(seed_points, 0) + $seed_points, seedbonus = seedbonus + $all_bonus where id = {$arr["userid"]}"; do_log("$bonusLog, query: $sql"); sql_query($sql); - } } $log = 'calculate seeding bonus'; @@ -462,6 +476,28 @@ function docleanup($forceAll = 0, $printProgress = false) { if ($printProgress) { printProgress($log); } + + //expire torrent sticky + $toBeExpirePosStates = [ + \App\Models\Torrent::POS_STATE_STICKY_FIRST, + \App\Models\Torrent::POS_STATE_STICKY_SECOND, + ]; + $update = [ + 'pos_state' => \App\Models\Torrent::POS_STATE_STICKY_NONE, + 'pos_state_until' => null, + ]; + \App\Models\Torrent::query() + ->whereIn('pos_state', $toBeExpirePosStates) + ->whereNotNull('pos_state_until') + ->where('pos_state_until', '<', now()) + ->update($update); + $log = "expire torrent pos state"; + do_log($log); + if ($printProgress) { + printProgress($log); + } + + //automatically pick hot if ($hotdays_torrent) { @@ -501,7 +537,7 @@ function docleanup($forceAll = 0, $printProgress = false) { ->whereRaw("added < FROM_UNIXTIME($deadtime)") ->whereRaw("last_login < FROM_UNIXTIME($deadtime)") ->whereRaw("last_access < FROM_UNIXTIME($deadtime)"); - delete_user($query, "cleanup.delete_user_unconfirmed"); + disable_user($query, "cleanup.disable_user_unconfirmed"); $log = "delete unconfirmed accounts"; do_log($log); if ($printProgress) { @@ -554,7 +590,7 @@ function docleanup($forceAll = 0, $printProgress = false) { ->where(function (\Illuminate\Database\Eloquent\Builder $query) use ($iniupload_main) { $query->where('uploaded', 0)->orWhere('uploaded', $iniupload_main); }); - delete_user($query, "cleanup.delete_user_no_transfer_alt_last_access_time"); + disable_user($query, "cleanup.disable_user_no_transfer_alt_last_access_time"); } $log = "delete inactive user accounts, no transfer. Alt. 1: last access time"; do_log($log); @@ -577,7 +613,7 @@ function docleanup($forceAll = 0, $printProgress = false) { ->where(function (\Illuminate\Database\Eloquent\Builder $query) use ($iniupload_main) { $query->where('uploaded', 0)->orWhere('uploaded', $iniupload_main); }); - delete_user($query, "cleanup.delete_user_no_transfer_alt_register_time"); + disable_user($query, "cleanup.disable_user_no_transfer_alt_register_time"); } $log = "delete inactive user accounts, no transfer. Alt. 2: registering time"; do_log($log); @@ -596,7 +632,7 @@ function docleanup($forceAll = 0, $printProgress = false) { ->where('status', 'confirmed') ->where("class","<", $maxclass) ->where("last_access","<", $dt); - delete_user($query, "cleanup.delete_user_not_parked"); + disable_user($query, "cleanup.disable_user_not_parked"); } $log = "delete inactive user accounts, not parked"; do_log($log); @@ -615,7 +651,7 @@ function docleanup($forceAll = 0, $printProgress = false) { ->where('status', 'confirmed') ->where("class","<", $maxclass) ->where("last_access","<", $dt); - delete_user($query, "cleanup.delete_user_parked"); + disable_user($query, "cleanup.disable_user_parked"); } $log = "delete parked user accounts, parked"; do_log($log); @@ -623,6 +659,26 @@ function docleanup($forceAll = 0, $printProgress = false) { printProgress($log); } + //destroy disabled accounts + $destroyDisabledDays = get_setting('account.destroy_disabled'); + if ($destroyDisabledDays > 0) { + $secs = $destroyDisabledDays*24*60*60; + $dt = date("Y-m-d H:i:s",(TIMENOW - $secs)); + $users = \App\Models\User::query() + ->where('enabled', 'no') + ->where("last_access","<", $dt) + ->get(['id']); + if ($users->isNotEmpty()) { + $userRep = new \App\Repositories\UserRepository(); + $userRep->destroy($users->pluck('id')->toArray(), 'cleanup.destroy_disabled_account'); + } + } + $log = "destroy disabled accounts"; + do_log($log); + if ($printProgress) { + printProgress($log); + } + //remove VIP status if time's up $res = sql_query("SELECT id, modcomment FROM users WHERE vip_added='yes' AND vip_until < NOW()") or sqlerr(__FILE__, __LINE__); if (mysql_num_rows($res) > 0) @@ -754,7 +810,7 @@ function docleanup($forceAll = 0, $printProgress = false) { } //17.update total seeding and leeching time of users - $res = sql_query("SELECT * FROM users") or sqlerr(__FILE__, __LINE__); + $res = sql_query("SELECT id FROM users where enabled = 'yes' and status = 'confirmed'") or sqlerr(__FILE__, __LINE__); while($arr = mysql_fetch_assoc($res)) { //die("s" . $arr['id']); @@ -983,6 +1039,16 @@ function docleanup($forceAll = 0, $printProgress = false) { if ($printProgress) { printProgress($log); } + + //remove duplicate user ban logs + //No need to do that, disable + destroy will have two records, sometimes disable will enable again +// $log = "clear user ban log duplicate"; +// \App\Models\UserBanLog::clearUserBanLogDuplicate(); +// do_log($log); +// if ($printProgress) { +// printProgress($log); +// } + $log = 'Full cleanup is done'; do_log($log); if ($printProgress) { diff --git a/include/cleanup_cli.php b/include/cleanup_cli.php index 1118c783..e1b4152a 100644 --- a/include/cleanup_cli.php +++ b/include/cleanup_cli.php @@ -23,13 +23,14 @@ if (isset($_SERVER['argv'][1])) { $force = $_SERVER['argv'][1] ? 1 : 0; } $logPrefix = "[CLEANUP_CLI]"; +$begin = time(); try { if ($force) { $result = docleanup(1, true); } else { $result = autoclean(true); } - $log = "$logPrefix, DONE: $result"; + $log = "$logPrefix, DONE: $result, cost time in seconds: " . (time() - $begin); do_log($log); printProgress($log); } catch (\Exception $exception) { diff --git a/include/constants.php b/include/constants.php index 17b6d75f..a8d55c01 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@
%s
%s
', + '
%s
%s
', $lang_functions['spoiler_expand_collapse'], $title, $contentClass, $content ); return addTempCode($HTML); @@ -312,8 +312,6 @@ function format_comment($text, $strip_html = true, $xssclean = false, $newtab = if ($strip_html) { $s = htmlspecialchars($s); } - // Linebreaks - $s = nl2br($s); if (strpos($s,"[code]") !== false && strpos($s,"[/code]") !== false) { // $s = preg_replace("/\[code\](.+?)\[\/code\]/eis","formatCode('\\1')", $s); @@ -322,6 +320,15 @@ function format_comment($text, $strip_html = true, $xssclean = false, $newtab = }, $s); } + if (strpos($s,"[raw]") !== false && strpos($s,"[/raw]") !== false) { + $s = preg_replace_callback("/\[raw\](.+?)\[\/raw\]/is",function ($matches) { + return addTempCode($matches[1]); + }, $s); + } + + // Linebreaks + $s = nl2br($s); + $originalBbTagArray = array('[siteurl]', '[site]','[*]', '[b]', '[/b]', '[i]', '[/i]', '[u]', '[/u]', '[pre]', '[/pre]', '[/color]', '[/font]', '[/size]', " "); $replaceXhtmlTagArray = array(get_protocol_prefix().get_setting('basic.BASEURL'), get_setting('basic.SITENAME'), 'list', '', '', '', '', '', '', '
', '
', '', '', '', '  '); $s = str_replace($originalBbTagArray, $replaceXhtmlTagArray, $s); @@ -1657,13 +1664,13 @@ function registration_check($type = "invitesystem", $maxuserscheck = true, $ipch global $invitesystem, $registration, $maxusers, $SITENAME, $maxip; if ($type == "invitesystem") { if ($invitesystem == "no") { - stderr($lang_functions['std_oops'], $lang_functions['std_invite_system_disabled'], 0); + stderr($lang_functions['std_oops'], $lang_functions['std_invite_system_disabled'], 0, false); } } if ($type == "normal") { if ($registration == "no") { - stderr($lang_functions['std_sorry'], $lang_functions['std_open_registration_disabled'], 0); + stderr($lang_functions['std_sorry'], $lang_functions['std_open_registration_disabled'], 0, false); } } @@ -1671,14 +1678,14 @@ function registration_check($type = "invitesystem", $maxuserscheck = true, $ipch $res = sql_query("SELECT COUNT(*) FROM users") or sqlerr(__FILE__, __LINE__); $arr = mysql_fetch_row($res); if ($arr[0] >= $maxusers) - stderr($lang_functions['std_sorry'], $lang_functions['std_account_limit_reached'], 0); + stderr($lang_functions['std_sorry'], $lang_functions['std_account_limit_reached'], 0, false); } if ($ipcheck) { $ip = getip () ; $a = (@mysql_fetch_row(@sql_query("select count(*) from users where ip='" . mysql_real_escape_string($ip) . "'"))) or sqlerr(__FILE__, __LINE__); if ($a[0] > $maxip) - stderr($lang_functions['std_sorry'], $lang_functions['std_the_ip']."" . htmlspecialchars($ip) ."". $lang_functions['std_used_many_times'],false); + stderr($lang_functions['std_sorry'], $lang_functions['std_the_ip']."" . htmlspecialchars($ip) ."". $lang_functions['std_used_many_times'],false, false); } return true; } @@ -2486,6 +2493,7 @@ $cssupdatedate=($cssupdatedate ? "?".htmlspecialchars($cssupdatedate) : ""); " type="text/css" /> " type="text/css" /> + ] []: '.$lang_functions['text_attended'].'', $attendance->points, $CURUSER['attendance_card']); }else{ printf(' %s', $lang_functions['text_attendance']);}?> - : []: + = \App\Models\User::CLASS_ADMINISTRATOR) printf('[%s]', nexus_env('FILAMENT_PATH', 'nexusphp'), $lang_functions['text_management_system'])?>
@@ -2642,27 +2650,47 @@ else { H&R: %s]', (new \App\Repositories\HitAndRunRepository())->getStatusStats($CURUSER['id']))?> %s]', $CURUSER['id'], (new \App\Repositories\ClaimRepository())->getStats($CURUSER['id']))?> - = \App\Models\User::CLASS_ADMINISTRATOR) printf('[%s]', nexus_env('FILAMENT_PATH', 'nexusphp'), $lang_functions['text_management_system'])?> -
+ + +
+
+
+
+ +
+
+ 'width: 88px'])?> +
+
+
+
+
+ + + get_value('staff_report_count'); - if ($totalreports == ""){ - $totalreports = get_row_count("reports"); - $Cache->cache_value('staff_report_count', $totalreports, 900); - } - $totalcheaters = $Cache->get_value('staff_cheater_count'); - if ($totalcheaters == ""){ - $totalcheaters = get_row_count("cheaters"); - $Cache->cache_value('staff_cheater_count', $totalcheaters, 900); - } - print( - "\"cheaterbox\" ".$totalcheaters - ." \"reportbox\" ".$totalreports - ); - } +if (user_can('staffmem')) { + $totalreports = $Cache->get_value('staff_report_count'); + if ($totalreports == ""){ + $totalreports = get_row_count("reports"); + $Cache->cache_value('staff_report_count', $totalreports, 900); + } + $totalcheaters = $Cache->get_value('staff_cheater_count'); + if ($totalcheaters == ""){ + $totalcheaters = get_row_count("cheaters"); + $Cache->cache_value('staff_cheater_count', $totalcheaters, 900); + } + print( + "\"cheaterbox\" ".$totalcheaters + ." \"reportbox\" ".$totalreports + ); +} +print(" \"Buddylist\""); +print(" \"RSS\""); +print '
'; +//echo $lang_functions['text_the_time_is_now'].$datum['hours'].":".$datum['minutes'] . '
'; // $cacheKey = "staff_message_count_" . $CURUSER['id']; // $totalsm = $Cache->get_value($cacheKey); $totalsm = \App\Repositories\MessageRepository::getStaffMessageCountCache($CURUSER['id'], 'total'); @@ -2677,8 +2705,7 @@ else { print("".$inboxpic." ".($messages ? $messages." (".$unread.$lang_functions['text_message_new'].")" : "0")); print(" \"sentbox\" ".($outmessages ? $outmessages : "0")); - print(" \"Buddylist\""); - print(" \"RSS\""); + ?>
@@ -2794,6 +2821,19 @@ if ($msgalert) } } + //seed box approval + if (get_user_class() >= \App\Models\User::CLASS_ADMINISTRATOR && get_setting('seed_box.enabled') == 'yes') { + $cacheKey = 'SEED_BOX_RECORD_APPROVAL_NONE'; + $toApprovalCounts = $Cache->get_value($cacheKey); + if ($toApprovalCounts === false) { + $toApprovalCounts = get_row_count('seed_box_records', 'where status = 0'); + $Cache->cache_value($cacheKey, $toApprovalCounts, 60); + } + if ($toApprovalCounts) { + msgalert('/nexusphp/seed-box-records?tableFilters[status][value]=0', sprintf($lang_functions['text_seed_box_record_to_approval'], is_or_are($toApprovalCounts), $toApprovalCounts, add_s($toApprovalCounts)), 'darkred'); + } + } + if (user_can('staffmem')) { @@ -3043,18 +3083,21 @@ function loggedinorreturn($mainpage = false) { } function deletetorrent($id) { - $id = intval($id); + $idArr = \Illuminate\Support\Arr::wrap($id); + $idStr = implode(', ', $idArr ?: [0]); $torrent_dir = get_setting('main.torrent_dir'); - \Nexus\Database\NexusDB::statement("DELETE FROM torrents WHERE id = $id"); - \Nexus\Database\NexusDB::statement("DELETE FROM snatched WHERE torrentid = $id"); + \Nexus\Database\NexusDB::statement("DELETE FROM torrents WHERE id in ($idStr)"); + \Nexus\Database\NexusDB::statement("DELETE FROM snatched WHERE torrentid in ($idStr)"); foreach(array("peers", "files", "comments") as $x) { - \Nexus\Database\NexusDB::statement("DELETE FROM $x WHERE torrent = $id"); + \Nexus\Database\NexusDB::statement("DELETE FROM $x WHERE torrent in ($idStr)"); } - \Nexus\Database\NexusDB::statement("DELETE FROM hit_and_runs WHERE torrent_id = $id"); - \Nexus\Database\NexusDB::statement("DELETE FROM claims WHERE torrent_id = $id"); - do_action("torrent_delete", $id); - do_log("delete torrent: $id", "error"); - unlink(getFullDirectory("$torrent_dir/$id.torrent")); + \Nexus\Database\NexusDB::statement("DELETE FROM hit_and_runs WHERE torrent_id in ($idStr)"); + \Nexus\Database\NexusDB::statement("DELETE FROM claims WHERE torrent_id in ($idStr)"); + foreach ($idArr as $_id) { + do_action("torrent_delete", $_id); + do_log("delete torrent: $_id", "error"); + unlink(getFullDirectory("$torrent_dir/$_id.torrent")); + } } function pager($rpp, $count, $href, $opts = array(), $pagename = "page") { @@ -3323,18 +3366,16 @@ function torrenttable($rows, $variant = "torrent", $searchBoxId = 0) { $torrentSeedingLeechingStatus = $torrent->listLeechingSeedingStatus($CURUSER['id'], $torrentIdArr); $tagRep = new \App\Repositories\TagRepository(); - $tagCollection = $tagRep->createBasicQuery()->get(); - $tagIdStr = $tagCollection->implode('id', ',') ?: '0'; - $torrentTagCollection = \App\Models\TorrentTag::query()->whereIn('torrent_id', $torrentIdArr)->orderByRaw("field(tag_id,$tagIdStr)")->get(); - $tagKeyById = $tagCollection->keyBy('id'); + $torrentTagCollection = \App\Models\TorrentTag::query()->whereIn('torrent_id', $torrentIdArr)->get(); $torrentTagResult = $torrentTagCollection->groupBy('torrent_id'); $showCover = false; - $showSeedBoxIcon = get_setting('seed_box.enabled') == 'yes'; + $showSeedBoxIcon = false; if ($searchBoxId) { $searchBoxExtra = get_searchbox_value($searchBoxId, "extra"); if (!empty($searchBoxExtra[\App\Models\SearchBox::EXTRA_DISPLAY_COVER_ON_TORRENT_LIST])) { $showCover = true; } + $showSeedBoxIcon = get_setting('seed_box.enabled') == 'yes'; if (empty($searchBoxExtra[\App\Models\SearchBox::EXTRA_DISPLAY_SEED_BOX_ICON_ON_TORRENT_LIST])) { $showSeedBoxIcon = false; } @@ -3526,7 +3567,7 @@ foreach ($rows as $row) } $stickyicon = apply_filter('sticky_icon', $stickyicon, $row); $sp_torrent = get_torrent_promotion_append($row['sp_state'],"",true,$row["added"], $row['promotion_time_type'], $row['promotion_until'], $row['__ignore_global_sp_state'] ?? false); - $hrImg = get_hr_img($row); + $hrImg = get_hr_img($row, $row['search_box_id']); //cover $coverSrc = $tdCover = ''; @@ -3576,7 +3617,7 @@ foreach ($rows as $row) */ $tagOwns = $torrentTagResult->get($id); if ($tagOwns) { - $tags = $tagRep->renderSpan($tagKeyById, $tagOwns->pluck('tag_id')->toArray()); + $tags = $tagRep->renderSpan($tagOwns->pluck('tag_id')->toArray()); } else { $tags = ''; } @@ -3696,7 +3737,7 @@ foreach ($rows as $row) if ( $row["anonymous"] == "yes" - && (user_can('torrentmanage') || (isset($row['owner']) && $row['owner'] == $CURUSER['id'])) + && (user_can('viewanonymous') || (isset($row['owner']) && $row['owner'] == $CURUSER['id'])) ) { print("".$lang_functions['text_anonymous']."
".(isset($row["owner"]) ? "(" . get_username($row["owner"]) .")" : "".$lang_functions['text_orphaned']."") . "\n"); } @@ -3729,7 +3770,6 @@ create_tooltip_container($torrent_tooltip, 500); function get_username($id, $big = false, $link = true, $bold = true, $target = false, $bracket = false, $withtitle = false, $link_ext = "", $underline = false) { static $usernameArray = array(); - global $lang_functions; $id = (int)$id; if (func_num_args() == 1 && isset($usernameArray[$id])) { //One argument=is default display of username. Get it directly from static array if available @@ -3808,7 +3848,7 @@ function get_username($id, $big = false, $link = true, $bold = true, $target = f } else { - $username = "".$lang_functions['text_orphaned'].""; + $username = "".nexus_trans('nexus.user_not_exists').""; $username = "" . ( $bracket == true ? "(" . $username . ")" : $username) . ""; } if (func_num_args() == 1) { //One argument=is default display of username, save it in static array @@ -3906,7 +3946,11 @@ function validusername($username) // Some Swedish Latin-1 letters collide with popular DOS glyphs. If these // characters are between ASCII-characters (a-zA-Z and more) they are // treated like the Swedish letters, otherwise like the DOS glyphs. -function code($ibm_437, $swedishmagic = false) { +function code($ibm_437, $view) { + $swedishmagic = false; + if ($view == "magic") { + $swedishmagic = true; + } $table437 = array("\200", "\201", "\202", "\203", "\204", "\205", "\206", "\207", "\210", "\211", "\212", "\213", "\214", "\215", "\216", "\217", "\220", "\221", "\222", "\223", "\224", "\225", "\226", "\227", "\230", "\231", @@ -3992,6 +4036,44 @@ $s = str_replace($table437, $tablehtml, $s); return $s; } +/** + * @param $ibm_437 + * @param $view + * @return array|string|string[] + * @ref https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/master/app/Helpers/Nfo.php + */ +function code_new($ibm_437, $view) +{ + $swedishmagic = false; + if ($view == "magic") { + $swedishmagic = true; + } + $cf = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 8962, 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 8359, 402, 225, 237, 243, 250, 241, 209, 170, 186, 191, 8976, 172, 189, 188, 161, 171, 187, 9617, 9618, 9619, 9474, 9508, 9569, 9570, 9558, 9557, 9571, 9553, 9559, 9565, 9564, 9563, 9488, 9492, 9524, 9516, 9500, 9472, 9532, 9566, 9567, 9562, 9556, 9577, 9574, 9568, 9552, 9580, 9575, 9576, 9572, 9573, 9561, 9560, 9554, 9555, 9579, 9578, 9496, 9484, 9608, 9604, 9612, 9616, 9600, 945, 223, 915, 960, 931, 963, 181, 964, 934, 920, 937, 948, 8734, 966, 949, 8745, 8801, 177, 8805, 8804, 8992, 8993, 247, 8776, 176, 8729, 183, 8730, 8319, 178, 9632, 160); + $s = ""; + for ($c = 0; $c < strlen($ibm_437); $c++) { // cyctle through the whole file doing a byte at a time. + $byte = $ibm_437[$c]; + $ob = ord($byte); + if ($ob >= 127) { // is it in the normal ascii range + $s .= '&#' . $cf[$ob] . ';'; + } else { + $s .= $byte; + } + } + + if ($swedishmagic) { + $s = str_replace("\345","\206",$s); + $s = str_replace("\344","\204",$s); + $s = str_replace("\366","\224",$s); + $s = preg_replace("/([ -~])\305([ -~])/", "\\1\217\\2", $s); + $s = preg_replace("/([ -~])\304([ -~])/", "\\1\216\\2", $s); + $s = preg_replace("/([ -~])\326([ -~])/", "\\1\231\\2", $s); + $s = str_replace ( "\311", "\220", $s ); // + $s = str_replace ( "\351", "\202", $s ); // + } + + return $s; +} + //Tooltip container for hot movie, classic movie, etc function create_tooltip_container($id_content_arr, $width = 400) @@ -4663,14 +4745,15 @@ function get_torrent_promotion_append_sub($promotion = 1,$forcemode = "",$showti return $sp_torrent; } -function get_hr_img(array $torrent) +function get_hr_img(array $torrent, $searchBoxId) { - $mode = get_setting('hr.mode'); +// $mode = get_setting('hr.mode'); + $mode = \App\Models\HitAndRun::getConfig('mode', $searchBoxId); $result = ''; if ($mode == \App\Models\HitAndRun::MODE_GLOBAL || ($mode == \App\Models\HitAndRun::MODE_MANUAL && isset($torrent['hr']) && $torrent['hr'] == \App\Models\Torrent::HR_YES)) { $result = 'H&R'; } - do_log("mode: $mode, result: $result"); + do_log("searchBoxId: $searchBoxId, mode: $mode, result: $result"); return $result; } @@ -5173,7 +5256,7 @@ function saveSetting($prefix, $nameAndValue, $autoload = 'yes') $sql .= implode(",", $data) . " on duplicate key update value = values(value)"; \Nexus\Database\NexusDB::statement($sql); clear_setting_cache(); - do_action("nexus_setting_update", $prefix, $nameAndValue); + do_action("nexus_setting_update"); } function getFullDirectory($dir) @@ -5634,6 +5717,7 @@ function list_require_search_box_id() 'userdetails' => [$setting['browsecat'], $setting['specialcat']], 'offers' => [$setting['browsecat'], $setting['specialcat']], 'details' => [$setting['browsecat'], $setting['specialcat']], + 'search' => [$setting['browsecat'], $setting['specialcat']], ]; return $maps[nexus()->getScript()] ?? []; } @@ -5720,7 +5804,7 @@ function get_ip_location_from_geoip($ip): bool|array function msgalert($url, $text, $bgcolor = "red") { print("
\n"); - print("".$text.""); + print("".$text.""); print("

"); } @@ -5746,6 +5830,8 @@ function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = function insert_torrent_tags($torrentId, $tagIdArr, $sync = false) { + $specialTags = \App\Models\Tag::listSpecial(); + $canSetSpecialTag = user_can('torrent-set-special-tag'); $dateTimeStringNow = date('Y-m-d H:i:s'); if ($sync) { sql_query("delete from torrent_tags where torrent_id = $torrentId"); @@ -5756,6 +5842,10 @@ function insert_torrent_tags($torrentId, $tagIdArr, $sync = false) $insertTagsSql = 'insert into torrent_tags (`torrent_id`, `tag_id`, `created_at`, `updated_at`) values '; $values = []; foreach ($tagIdArr as $tagId) { + if (in_array($tagId, $specialTags) && !$canSetSpecialTag) { + do_log("special tag: $tagId, and user no permission"); + continue; + } $values[] = sprintf("(%s, %s, '%s', '%s')", $torrentId, $tagId, $dateTimeStringNow, $dateTimeStringNow); } $insertTagsSql .= implode(', ', $values); @@ -5815,8 +5905,8 @@ function calculate_seed_bonus($uid, $torrentIdArr = null): array $timenow = time(); $sectoweek = 7*24*60*60; - $A = 0; - $count = $torrent_peer_count = 0; + $A = $official_a = $size = $official_size = 0; + $count = $torrent_peer_count = $official_torrent_peer_count = 0; $logPrefix = "[CALCULATE_SEED_BONUS], uid: $uid, torrentIdArr: " . json_encode($torrentIdArr); if ($torrentIdArr !== null) { if (empty($torrentIdArr)) { @@ -5827,35 +5917,56 @@ function calculate_seed_bonus($uid, $torrentIdArr = null): array } else { $sql = "select torrents.id, torrents.added, torrents.size, torrents.seeders, peers.id as peerID from torrents LEFT JOIN peers ON peers.torrent = torrents.id WHERE peers.userid = $uid AND peers.seeder ='yes' group by peers.torrent, peers.peer_id"; } + $tagGrouped = []; $torrentResult = \Nexus\Database\NexusDB::select($sql); - do_log("$logPrefix, sql: $sql, count: " . count($torrentResult)); + if (!empty($torrentResult)) { + $torrentIdArrReal = array_column($torrentResult, 'id'); + $tagResult = \Nexus\Database\NexusDB::select(sprintf("select torrent_id, tag_id from torrent_tags where torrent_id in (%s)", implode(',', $torrentIdArrReal))); + foreach ($tagResult as $tagItem) { + $tagGrouped[$tagItem['torrent_id']][$tagItem['tag_id']] = 1; + } + } + $officialTag = \App\Models\Setting::get('bonus.official_tag'); + $officialAdditionalFactor = \App\Models\Setting::get('bonus.official_addition'); + $zeroBonusTag = \App\Models\Setting::get('bonus.zero_bonus_tag'); + $zeroBonusFactor = \App\Models\Setting::get('bonus.zero_bonus_factor'); + do_log("$logPrefix, sql: $sql, count: " . count($torrentResult) . ", officialTag: $officialTag, officialAdditionalFactor: $officialAdditionalFactor, zeroBonusTag: $zeroBonusTag, zeroBonusFactor: $zeroBonusFactor"); foreach ($torrentResult as $torrent) { + $size = bcadd($size, $torrent['size']); $weeks_alive = ($timenow - strtotime($torrent['added'])) / $sectoweek; - $gb_size = $torrent['size'] / 1073741824; + $gb_size = $gb_size_raw = $torrent['size'] / 1073741824; + if ($zeroBonusTag && isset($tagGrouped[$torrent['id']][$zeroBonusTag]) && is_numeric($zeroBonusFactor)) { + $gb_size = $gb_size * $zeroBonusFactor; + } $temp = (1 - exp($valueone * $weeks_alive)) * $gb_size * (1 + $sqrtof2 * exp($valuethree * ($torrent['seeders'] - 1))); - do_log(sprintf( - "$logPrefix, torrent: %s, peer ID: %s, weeks: %s, size: %s GB, increase A: %s", - $torrent['id'], $torrent['peerID'], $weeks_alive, $gb_size, $temp - )); $A += $temp; $count++; $torrent_peer_count++; + $officialAIncrease = 0; + if ($officialTag && isset($tagGrouped[$torrent['id']][$officialTag])) { + $officialAIncrease = $temp; + $official_torrent_peer_count++; + $official_size = bcadd($official_size, $torrent['size']); + } + $official_a += $officialAIncrease; + do_log(sprintf( + "$logPrefix, torrent: %s, peer ID: %s, weeks: %s, size_raw: %s GB, size: %s GB, increase A: %s, increase official A: %s", + $torrent['id'], $torrent['peerID'], $weeks_alive, $gb_size_raw, $gb_size, $temp, $officialAIncrease + )); } if ($count > $maxseeding_bonus) $count = $maxseeding_bonus; - $all_bonus = $seed_bonus = $seed_points = $valuetwo * atan($A / $l_bonus) + ($perseeding_bonus * $count); - $is_donor_info = \Nexus\Database\NexusDB::getOne('users', "id = $uid", "donor, donoruntil"); - $is_donor_until = $is_donor_info['donoruntil']; - $is_donor = $is_donor_info['donor'] == 'yes' && ($is_donor_until === null || $is_donor_until == '0000-00-00 00:00:00' || $is_donor_until >= date('Y-m-d H:i:s')); - $is_donor = intval($is_donor); - $log = "$logPrefix, original bonus: $all_bonus, is_donor: $is_donor, donortimes_bonus: $donortimes_bonus"; - if ($is_donor && $donortimes_bonus > 0) { - $all_bonus = $all_bonus * $donortimes_bonus; - $log .= ", do multiple, all_bonus: $all_bonus"; - } - $result = compact('seed_points','seed_bonus', 'all_bonus', 'A', 'count', 'torrent_peer_count'); - do_log("$log, result: " . json_encode($result)); + $seed_bonus = $seed_points = $valuetwo * atan($A / $l_bonus) + ($perseeding_bonus * $count); + //Official addition don't think about the minimum value + $official_bonus = $valuetwo * atan($official_a / $l_bonus); + $result = compact( + 'seed_points','seed_bonus', 'A', 'count', 'torrent_peer_count', 'size', + 'official_bonus', 'official_a', 'official_torrent_peer_count', 'official_size' + ); + $result['donor_times'] = $donortimes_bonus; + $result['official_additional_factor'] = $officialAdditionalFactor; + do_log("$logPrefix, result: " . json_encode($result)); return $result; } @@ -5870,7 +5981,7 @@ function calculate_harem_addition($uid) $haremsCount = $harems->count(); foreach ($harems as $harem) { $result = calculate_seed_bonus($harem->id); - $addition += $result['all_bonus']; + $addition += $result['seed_points']; } do_log("[HAREM_ADDITION], user: $uid, haremsCount: $haremsCount ,addition: $addition"); return $addition; @@ -5951,4 +6062,128 @@ TD; return $html; } +function datetimepicker_input($name, $value = '', $label = '') +{ + \Nexus\Nexus::css('vendor/jquery-datetimepicker/jquery.datetimepicker.min.css', 'footer', true); + \Nexus\Nexus::js('vendor/jquery-datetimepicker/jquery.datetimepicker.full.min.js', 'footer', true); + $id = "datetime-picker-$name"; + $input = sprintf('%s', $label, $id, $name, $value); + $lang = get_langfolder_cookie(true); + if ($lang == 'zh_CN') { + $lang = 'zh'; + } + $lang = str_replace('_', '-', $lang); + $js = << 0) { + $rowSpan++; + $hasHaremAddition = true; + } + if ($officialAdditionalFactor > 0 && $officialTag) { + $rowSpan++; + $hasOfficialAddition = true; + } + + $table = sprintf('', $options['table_style'] ?? ''); + $table .= ''; + $table .= sprintf('', nexus_trans('bonus.table_thead.reward_type')); + $table .= sprintf('', nexus_trans('bonus.table_thead.count')); + $table .= sprintf('', nexus_trans('bonus.table_thead.size')); + $table .= sprintf('', nexus_trans('bonus.table_thead.a_value')); + $table .= sprintf('', nexus_trans('bonus.table_thead.bonus_base')); + $table .= sprintf('', nexus_trans('bonus.table_thead.factor')); + $table .= sprintf('', nexus_trans('bonus.table_thead.got_bonus')); + $table .= sprintf('', nexus_trans('bonus.table_thead.total')); + $table .= ''; + + $table .= sprintf( + '', + nexus_trans('bonus.reward_types.basic'), + $bonusResult['torrent_peer_count'], + mksize($bonusResult['size']), + number_format($bonusResult['A'], 3), + number_format($bonusResult['seed_bonus'],3), + $baseBonusFactor, + number_format($baseBonus,3), + $rowSpan, + $totalBonus + ); + + if ($hasOfficialAddition) { + $table .= sprintf( + '', + nexus_trans('bonus.reward_types.official_addition'), + $bonusResult['official_torrent_peer_count'], + mksize($bonusResult['official_size']), + number_format($bonusResult['official_a'], 3), + number_format($bonusResult['official_bonus'], 3), + $officialAdditionalFactor, + number_format($bonusResult['official_bonus'] * $officialAdditionalFactor, 3) + ); + } + + if ($hasHaremAddition) { + $table .= sprintf( + '', + nexus_trans('bonus.reward_types.harem_addition'), + '--', + '--', + '--', + number_format($haremAddition, 3), + $haremFactor, + number_format($haremAddition * $haremFactor, 3) + ); + } + $table .= '
%s%s%s%s%s%s%s%s
%s%s%s%s%s%s%s%s
%s%s%s%s%s%s%s
%s%s%s%s%s%s%s
'; + + return [ + 'table' => $table, + 'has_harem_addition' => $hasHaremAddition, + 'harem_addition_factor' => $haremFactor, + 'has_official_addition' => $hasOfficialAddition, + 'official_addition_factor' => $officialAdditionalFactor, + ]; + +} + +function build_search_area($searchArea, array $options = []) +{ + $result = sprintf(''; + return $result; +} ?> diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 52700dfd..e56f2dfe 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -730,6 +730,7 @@ function get_user_row($id) } else { $arr['__is_rainbow'] = 0; } + $arr['__is_donor'] = is_donor($arr); return apply_filter("user_row", $arr); }); @@ -866,8 +867,8 @@ function getDataTraffic(array $torrent, array $queries, array $user, $peer, $sna throw new \InvalidArgumentException("user no '__is_donor' field"); } $log = sprintf( - "torrent: %s, user: %s, peerUploaded: %s, peerDownloaded: %s, queriesUploaded: %s, queriesDownloaded: %s", - $torrent['id'], $user['id'], $peer['uploaded'] ?? '', $peer['downloaded'] ?? '', $queries['uploaded'], $queries['downloaded'] + "torrent: %s, owner: %s, user: %s, peerUploaded: %s, peerDownloaded: %s, queriesUploaded: %s, queriesDownloaded: %s", + $torrent['id'], $torrent['owner'], $user['id'], $peer['uploaded'] ?? '', $peer['downloaded'] ?? '', $queries['uploaded'], $queries['downloaded'] ); if (!empty($peer)) { $realUploaded = max(bcsub($queries['uploaded'], $peer['uploaded']), 0); @@ -929,7 +930,7 @@ function getDataTraffic(array $torrent, array $queries, array $user, $peer, $sna */ $isSeedBoxRuleEnabled = get_setting('seed_box.enabled') == 'yes'; $log .= ", isSeedBoxRuleEnabled: $isSeedBoxRuleEnabled, user class: {$user['class']}, __is_donor: {$user['__is_donor']}"; - if ($isSeedBoxRuleEnabled && !($user['class'] >= \App\Models\User::CLASS_VIP || $user['__is_donor'])) { + if ($isSeedBoxRuleEnabled && $torrent['owner'] != $user['id'] && !($user['class'] >= \App\Models\User::CLASS_VIP || $user['__is_donor'])) { $isIPSeedBox = isIPSeedBox($queries['ip'], $user['id']); $log .= ", isIPSeedBox: $isIPSeedBox"; if ($isIPSeedBox) { @@ -941,9 +942,20 @@ function getDataTraffic(array $torrent, array $queries, array $user, $peer, $sna $log .= ", isIPSeedBox && isSeedBoxNoPromotion, increment for user = real"; } $maxUploadedTimes = get_setting('seed_box.max_uploaded'); - if (!empty($snatch) && isset($torrent['size']) && $snatch['uploaded'] >= $torrent['size'] * $maxUploadedTimes) { - $log .= ", snatchUploaded >= torrentSize * times($maxUploadedTimes), uploadedIncrementForUser = 0"; - $uploadedIncrementForUser = 0; + $maxUploadedDurationSeconds = get_setting('seed_box.max_uploaded_duration', 0) * 3600; + $torrentTTL = time() - strtotime($torrent['added']); + $timeRangeValid = ($maxUploadedDurationSeconds == 0) || ($torrentTTL < $maxUploadedDurationSeconds); + $log .= ", maxUploadedTimes: $maxUploadedTimes, maxUploadedDurationSeconds: $maxUploadedDurationSeconds, timeRangeValid: $timeRangeValid"; + if ($maxUploadedTimes > 0 && $timeRangeValid) { + $log .= ", [LIMIT_UPLOADED]"; + if (!empty($snatch) && isset($torrent['size']) && $snatch['uploaded'] >= $torrent['size'] * $maxUploadedTimes) { + $log .= ", snatchUploaded({$snatch['uploaded']}) >= torrentSize({$torrent['size']}) * times($maxUploadedTimes), uploadedIncrementForUser = 0"; + $uploadedIncrementForUser = 0; + } else { + $log .= ", snatchUploaded({$snatch['uploaded']}) < torrentSize({$torrent['size']}) * times($maxUploadedTimes), uploadedIncrementForUser do not change to 0"; + } + } else { + $log .= ", [NOT_LIMIT_UPLOADED]"; } } } @@ -970,7 +982,7 @@ function clear_user_cache($uid, $passkey = '') } } -function clear_setting_cache($buildPermissionCache = false) +function clear_setting_cache() { \Nexus\Database\NexusDB::cache_del('nexus_settings_in_laravel'); \Nexus\Database\NexusDB::cache_del('nexus_settings_in_nexus'); @@ -1037,3 +1049,8 @@ function user_can($permission, $fail = false, $uid = 0): bool } throw new \App\Exceptions\InsufficientPermissionException(); } + +function is_donor(array $userInfo): bool +{ + return $userInfo['donor'] == 'yes' && ($userInfo['donoruntil'] === null || $userInfo['donoruntil'] == '0000-00-00 00:00:00' || $userInfo['donoruntil'] >= date('Y-m-d H:i:s')); +} diff --git a/lang/chs/lang_details.php b/lang/chs/lang_details.php index f0f0b672..e9d12c43 100644 --- a/lang/chs/lang_details.php +++ b/lang/chs/lang_details.php @@ -239,6 +239,7 @@ $lang_details = array 'claim_label' => '认领种子', 'claim_confirm' => '确定要认领此种子吗?', 'action_approval' => '审核', + 'row_tags' => '标签', ); ?> diff --git a/lang/chs/lang_fields.php b/lang/chs/lang_fields.php index 167f349c..a8e99d3a 100644 --- a/lang/chs/lang_fields.php +++ b/lang/chs/lang_fields.php @@ -26,5 +26,6 @@ $lang_fields = [ 'field_type_checkbox' => '横向多选', 'field_type_select' => '下拉单选', 'field_type_image' => '图片', + 'col_display' => '自定义展示', -]; \ No newline at end of file +]; diff --git a/lang/chs/lang_functions.php b/lang/chs/lang_functions.php index 7bbb9b1f..b55f6d5e 100644 --- a/lang/chs/lang_functions.php +++ b/lang/chs/lang_functions.php @@ -80,14 +80,14 @@ $lang_functions = array 'text_logout' => "退出", 'text_bonus' => "魔力值 ", 'text_use' => "使用", - 'text_ratio' => "分享率:", - 'text_uploaded' => "上传量:", - 'text_downloaded' => "下载量:", - 'text_active_torrents' => "当前活动:", + 'text_ratio' => "分享率:", + 'text_uploaded' => "上传量:", + 'text_downloaded' => "下载量:", + 'text_active_torrents' => "当前活动:", 'title_torrents_seeding' => "当前做种", 'title_torrents_leeching' => "当前下载", - 'text_connectable' => "可连接:", - 'text_the_time_is_now' => "当前时间:", + 'text_connectable' => "可连接:", + 'text_the_time_is_now' => "时间:", 'text_message_new' => " 新", 'title_sentbox' => "发件箱", 'title_inbox' => "收件箱", @@ -329,6 +329,7 @@ $lang_functions = array 'input_check_all' => "全选", 'input_uncheck_all' => "全不选", 'select_at_least_one_record' => '至少选择一条记录!', + 'text_seed_box_record_to_approval' => '有 %s%u 条待审核的 SeedBox 记录%s', ); ?> diff --git a/lang/chs/lang_getusertorrentlistajax.php b/lang/chs/lang_getusertorrentlistajax.php index ed72e43b..7dc0428c 100644 --- a/lang/chs/lang_getusertorrentlistajax.php +++ b/lang/chs/lang_getusertorrentlistajax.php @@ -18,5 +18,6 @@ $lang_getusertorrentlistajax = array 'text_no_record' => "没有记录", 'text_total_size' => " | 总大小:", 'col_added' => "发布时间", + 'col_client' => '客户端', ); ?> diff --git a/lang/chs/lang_index.php b/lang/chs/lang_index.php index c0ce3bd3..f1109feb 100644 --- a/lang/chs/lang_index.php +++ b/lang/chs/lang_index.php @@ -105,6 +105,8 @@ $lang_index = array 'top_uploader_toggle_time_range_tab' => '点击切换时间范围', 'top_uploader_toggle_time_range_recently' => '最近 30 天', 'top_uploader_toggle_time_range_all' => '全部时间', + 'clear_shout_box' => '清空群聊区', + 'sure_to_clear_shout_box' => '确定要清空群聊区吗?', ); diff --git a/lang/chs/lang_invite.php b/lang/chs/lang_invite.php index 8dac3c80..97f4b674 100644 --- a/lang/chs/lang_invite.php +++ b/lang/chs/lang_invite.php @@ -48,8 +48,9 @@ $lang_invite = array 'std_only' => "只有", 'std_or_above_can_invite' => "及以上的用户才能发送邀请。", 'text_email_restriction_note' => "你只能发送邀请给以下邮箱:", - 'harem_addition' => '后宫加成', + 'signup_link_help' => '右键复制', + 'signup_link' => '注册链接', ); ?> diff --git a/lang/chs/lang_mybonus.php b/lang/chs/lang_mybonus.php index 11ef47eb..21653080 100644 --- a/lang/chs/lang_mybonus.php +++ b/lang/chs/lang_mybonus.php @@ -6,6 +6,7 @@ $lang_mybonus = array 'std_karma_system_disabled' => "魔力值系统当前处于关闭中。", 'std_points_active' => "不过你的魔力值仍在计算中。", 'text_success_upload' => "祝贺你,你成功增加了上传值!", + 'text_success_download' => "祝贺你,你成功增加了下载值!", 'text_success_invites' => "祝贺你,你获得了1个新的邀请名额!", 'text_success_vip' => "祝贺你,你获得了一个月的", 'text_success_vip_two' => "资格!", @@ -74,8 +75,12 @@ $lang_mybonus = array 'text_not_enough_bonus' => "对不起,你没有足够的魔力值。还有,你怎么到这来的?", 'text_uploaded_one' => "1.0 GB上传量", 'text_uploaded_note' => "如果有足够的魔力值,你可以用它来换取上传量。交易完成后,你的魔力值会减少,上传量则会增加。", + 'text_download_note' => "如果有足够的魔力值,你可以用它来换取下载量。交易完成后,你的魔力值会减少,下载量则会增加。", 'text_uploaded_two' => "5.0 GB上传量", 'text_uploaded_three' => "10.0 GB上传量", + 'text_uploaded_four' => "100.0 GB上传量", + 'text_downloaded_ten_gb' => "10.0 GB 下载量", + 'text_downloaded_hundred_gb' => "100.0 GB 下载量", 'text_buy_invite' => "1个邀请名额", 'text_buy_invite_note' => "如果有足够的魔力值,你可以用它来换取邀请名额。交易完成后,你的魔力值会减少,邀请名额数则会增加。", 'text_custom_title' => "自定义头衔", @@ -86,12 +91,13 @@ $lang_mybonus = array 'text_bonus_gift_note' => "可能你不需要魔力值,为什么不把它送给那些需要的人呢?你可以把自己的魔力值作为礼物送给别人。交易完成后,你的魔力值会减少,礼物接收者的魔力值则会增加。同时,接收者会收到一条关于你的馈赠的短讯。", 'text_error' => "错误", 'text_ratio_too_high' => "分享率已很高", - 'text_bonus_formula_one' => "每小时获得的魔力值点数由下面的公式给出
    \"A
    \"B
+ 'text_bonus_formula_one' => "每小时获得的魔力值点数由下面的公式给出

    
    
式中简言之,为做种人数少、文件体积大的种子做种能获得更多魔力值。", + 'text_bonus_formula_wi' => "
  • Wi为第i个种子的权重系数,默认为 1,零魔种子为 ", 'text_user_with_ratio_above' => "分享率高于", 'text_and_uploaded_amount_above' => "且上传量大于", 'text_cannot_exchange_uploading' => "GB的用户不能换取更多的上传量。", @@ -132,13 +138,24 @@ $lang_mybonus = array 'text_success_buy_attendance_card' => '成功购买了一张补签卡。', 'text_harem_addition_get' => '当前后宫加成每小时获得 %s 魔力', 'reward_type' => '奖励类型', - 'addition' => '加成', + 'factor' => '系数', 'got_bonus' => '获得魔力', 'total' => '合计', 'reward_type_basic' => '基本奖励', 'reward_type_harem_addition' => '后宫加成', 'bonus_base' => '基础魔力', 'lock_text' => '系统限制 %s 秒内只能点击交换按钮一次!', + 'text_get_by_seeding_official' => '官种加成每小时将额外得到如下的魔力值', + 'official_calculate_method' => '官种奖励计算公式同上,只是仅针对官种进行计算,不考虑低保', + 'official_tag_bonus_additional_factor' => '最终奖励为计算所得官种奖励乘以官种系数,当前官种系数为: ', + 'reward_type_official_addition' => '官种加成', + 'text_get_by_harem' => '后宫加成每小时将额外得到如下的魔力值', + 'harem_additional_desc' => '后宫只考虑直属后宫。每个后宫加成值可在此查看', + 'harem_additional_factor' => '所得奖励为全部后宫的时魔(不考虑加成)之和,乘以后宫加成系数,当前值为:', + 'text_bonus_summary' => '每小时获得的合计魔力值', + 'col_count' => '数量', + 'col_size' => '体积', + 'col_a' => 'A 值', ); ?> diff --git a/lang/chs/lang_settings.php b/lang/chs/lang_settings.php index 9566cb22..75e82e5b 100644 --- a/lang/chs/lang_settings.php +++ b/lang/chs/lang_settings.php @@ -218,6 +218,7 @@ $lang_settings = array 'text_bonus_formula_four' => "。默认为'4'", 'text_bonus_formula_five' => "Si为第i个种子的大小,单位是GB", 'text_bonus_formula_six' => "Ni为第i个种子当前的做种者数
  • N0为参数。N0 = ", + 'text_bonus_formula_zero_bonus_factor' => "Wi为第i个种子权重系数。默认为 1,零魔种子为 ", 'text_bonus_formula_seven' => "。默认为'7'", 'text_bonus_formula_eight' => "B为1小时中用户获得的做种魔力值点数", 'text_bonus_formula_nine' => "B0为参数,代表用户1小时获得魔力值的上限。B0 = ", @@ -773,6 +774,26 @@ $lang_settings = array 'text_offer_skip_approved_count_note' => '当通过的候选数大于等于此数值时,可直接发布不用提交候选。', 'row_torrent_delete' => '删除种子', 'text_torrent_delete_note' => '。删除种子', + 'row_hundred_gb_credit' => "100.0 GB 上传量", + 'text_hundred_gb_credit_note' => "个魔力值,如果他选择交换100.0 GB上传量。默认'10000'。", + 'row_ten_gb_download_credit' => "10.0 GB 下载量", + 'text_ten_gb_download_credit_note' => " 个魔力值,如果他选择交换10.0 GB下载量。默认'1000'。", + 'row_hundred_gb_download_credit' => "100.0 GB 下载量", + 'text_hundred_gb_download_credit_note' => " 个魔力值,如果他选择交换100.0 GB下载量。默认'8000'。", + 'row_official_addition' => '官种加成', + 'text_user_would_get_by_official' => '用户将获得官种正常魔力值的', + 'text_addition_addition_note' => '倍作为奖励(系数,如填入 0.01,官种获得 100 魔力则奖励用户 100 * 0.01 = 1)', + 'zero_bonus_factor_default' => '。默认为:0.2', + 'row_official_tag' => '官种标签', + 'text_official_tag_note' => '。带此标签的种子为官种', + 'row_zero_bonus_tag' => '零魔标签', + 'text_zero_bonus_tag_note' => '。带此标签的种子为零魔种子', + 'row_upload_deny_approval_deny_count' => '拒绝发布审核不通过数', + 'text_upload_deny_approval_deny_count_note' => "当审核不通过的种子数大于等于此数值时,不允许发布。设置为 '0' 不使用此规则", + 'row_nfo_view_style_default' => 'NFO 默认查看样式', + 'row_destroy_disabled' => '彻底删除账号', + 'text_destroy_disabled_note_one' => '被封禁的账号如果连续', + 'text_destroy_disabled_note_two' => "天不登录,将被从数据库彻底物理删除。默认'500',请设置一个大于上边任何一种导致封禁的值。设为'0'来禁止此规则。", ); ?> diff --git a/lang/chs/lang_upload.php b/lang/chs/lang_upload.php index 773cc5af..172beca0 100644 --- a/lang/chs/lang_upload.php +++ b/lang/chs/lang_upload.php @@ -41,6 +41,7 @@ $lang_upload = array 'text_english_title' => "英文名:", 'text_titles_note' => "(如果英文名不存在,使用拼音或不填写)", 'fill_quality' => '填写质量', + 'approval_deny_reach_upper_limit' => '当前审核被拒绝的种子数:%s 达到上限,不允许发布。', ); ?> diff --git a/lang/chs/lang_userdetails.php b/lang/chs/lang_userdetails.php index d27d81cd..4546c3a2 100644 --- a/lang/chs/lang_userdetails.php +++ b/lang/chs/lang_userdetails.php @@ -155,5 +155,6 @@ $lang_userdetails = array 'row_user_props' => '道具', 'meta_key_change_username_username' => '新用户名', 'consume' => '使用', + 'text_bonus_table' => '时魔', ); ?> diff --git a/lang/cht/lang_details.php b/lang/cht/lang_details.php index f963817f..30a723fe 100644 --- a/lang/cht/lang_details.php +++ b/lang/cht/lang_details.php @@ -238,6 +238,7 @@ $lang_details = array 'claim_label' => '認領種子', 'claim_confirm' => '確定要認領此種子嗎?', 'action_approval' => '審核', + 'row_tags' => '標簽', ); ?> diff --git a/lang/cht/lang_fields.php b/lang/cht/lang_fields.php index 8e3eea26..096544b9 100644 --- a/lang/cht/lang_fields.php +++ b/lang/cht/lang_fields.php @@ -25,5 +25,6 @@ $lang_fields = [ 'field_type_radio' => '橫向單選', 'field_type_checkbox' => '橫向多選', 'field_type_select' => '下拉單選', + 'col_display' => '自定義展示', -]; \ No newline at end of file +]; diff --git a/lang/cht/lang_functions.php b/lang/cht/lang_functions.php index d1f50e6b..f7429a4a 100644 --- a/lang/cht/lang_functions.php +++ b/lang/cht/lang_functions.php @@ -87,7 +87,7 @@ $lang_functions = array 'title_torrents_seeding' => "當前做種", 'title_torrents_leeching' => "當前下載", 'text_connectable' => "可連接:", - 'text_the_time_is_now' => "當前時間:", + 'text_the_time_is_now' => "時間: ", 'text_message_new' => " 新", 'title_sentbox' => "發件箱", 'title_inbox' => "收件箱", @@ -336,6 +336,7 @@ $lang_functions = array 'input_check_all' => "全選", 'input_uncheck_all' => "全不選", 'select_at_least_one_record' => '至少選擇一條記錄!', + 'text_seed_box_record_to_approval' => '有 %s%u 條待審核的 SeedBox 記錄%s', ); ?> diff --git a/lang/cht/lang_getusertorrentlistajax.php b/lang/cht/lang_getusertorrentlistajax.php index 3f8f1835..7de473ba 100644 --- a/lang/cht/lang_getusertorrentlistajax.php +++ b/lang/cht/lang_getusertorrentlistajax.php @@ -18,5 +18,6 @@ $lang_getusertorrentlistajax = array 'text_no_record' => "沒有記錄", 'text_total_size' => " | 總大小:", 'col_added' => "發布時間", + 'col_client' => '客戶端', ); ?> diff --git a/lang/cht/lang_index.php b/lang/cht/lang_index.php index 593a43ba..2d80f9f1 100644 --- a/lang/cht/lang_index.php +++ b/lang/cht/lang_index.php @@ -104,6 +104,8 @@ $lang_index = array 'top_uploader_toggle_time_range_tab' => '點擊切換時間範圍', 'top_uploader_toggle_time_range_recently' => '最近 30 天', 'top_uploader_toggle_time_range_all' => '全部時間', + 'clear_shout_box' => '清空群聊區', + 'sure_to_clear_shout_box' => '確定要清空群聊區嗎?', ); ?> diff --git a/lang/cht/lang_invite.php b/lang/cht/lang_invite.php index 27840ebc..bfe290c5 100644 --- a/lang/cht/lang_invite.php +++ b/lang/cht/lang_invite.php @@ -49,6 +49,8 @@ $lang_invite = array 'std_or_above_can_invite' => "及以上的用戶才能發送邀請。", 'text_email_restriction_note' => "你只能發送邀請給以下郵箱:", 'harem_addition' => '後宮加成', + 'signup_link_help' => '右鍵復製', + 'signup_link' => '註冊鏈接', ); ?> diff --git a/lang/cht/lang_mybonus.php b/lang/cht/lang_mybonus.php index d7376a0b..70c265bf 100644 --- a/lang/cht/lang_mybonus.php +++ b/lang/cht/lang_mybonus.php @@ -6,6 +6,7 @@ $lang_mybonus = array 'std_karma_system_disabled' => "魔力值系統當前處於關閉中。", 'std_points_active' => "不過你的魔力值仍在計算中。", 'text_success_upload' => "祝賀你,你成功增加了上傳值!", + 'text_success_download' => "祝賀你,你成功增加了下載值!", 'text_success_invites' => "祝賀你,你獲得了1個新的邀請名額!", 'text_success_vip' => "祝賀你,你獲得了一個月的", 'text_success_vip_two' => "資格!", @@ -74,8 +75,12 @@ $lang_mybonus = array 'text_not_enough_bonus' => "對不起,你沒有足夠的魔力值。還有,你怎麼到這來的?", 'text_uploaded_one' => "1.0 GB上傳量", 'text_uploaded_note' => "如果有足夠的魔力值,你可以用它來換取上傳量。交易完成後,你的魔力值會減少,上傳量則會增加。", + 'text_download_note' => "如果有足夠的魔力值,你可以用它來換取下載量。交易完成後,你的魔力值會減少,下載量則會增加。", 'text_uploaded_two' => "5.0 GB上傳量", 'text_uploaded_three' => "10.0 GB上傳量", + 'text_uploaded_four' => "100.0 GB上傳量", + 'text_downloaded_ten_gb' => "10.0 GB 下載量", + 'text_downloaded_hundred_gb' => "100.0 GB 下載量", 'text_buy_invite' => "1個邀請名額", 'text_buy_invite_note' => "如果有足夠的魔力值,你可以用它來換取邀請名額。交易完成後,你的魔力值會減少,邀請名額數則會增加。", 'text_custom_title' => "自定義頭銜", @@ -86,12 +91,13 @@ $lang_mybonus = array 'text_bonus_gift_note' => "可能你不需要魔力值,為什麼不把它送給那些需要的人呢?你可以把自己的魔力值作為禮物送給別人。交易完成後,你的魔力值會減少,禮物接收者的魔力值則會增加。同時,接收者會收到一條關於你的饋贈的短訊。", 'text_error' => "錯誤", 'text_ratio_too_high' => "分享率已很高", - 'text_bonus_formula_one' => "每小時獲得的魔力值點數由下面的公式給出
        \"A
        \"B
    + 'text_bonus_formula_one' => "每小時獲得的魔力值點數由下面的公式給出

        
        
    式中
    • A為中間變量
    • Ti為第i個種子的生存時間, 即自種子發布起到現在所經過的時間, 單位是周
    • T0為參數。T0 = ", 'text_bonus_formula_two' => "
    • Si為第i個種子的大小,單位是GB
    • Ni為第i個種子當前的做種者數
    • N0為參數。N0 = ", 'text_bonus_formula_three' => "
    • B為1小時中用戶獲得的做種魔力值點數
    • B0為參數,代表用戶1小時獲得魔力值的上限。B0 = ", 'text_bonus_formula_four' => "
    • L為參數。L = ", 'text_bonus_formula_five' => "
    簡言之,為做種人數少、文件體積大的種子做種能獲得更多魔力值。", + 'text_bonus_formula_wi' => "
  • Wi為第i個種子的權重系數,默認為 1,零魔種子為 ", 'text_user_with_ratio_above' => "分享率高于", 'text_and_uploaded_amount_above' => "且上傳量大于", 'text_cannot_exchange_uploading' => "GB的用戶不能換取更多的上傳量。", @@ -132,13 +138,24 @@ $lang_mybonus = array 'text_success_buy_attendance_card' => '成功購買了一張補簽卡。', 'text_harem_addition_get' => '當前後宮加成每小時獲得 %s 魔力', 'reward_type' => '獎勵類型', - 'addition' => '加成', + 'factor' => '系數', 'got_bonus' => '獲得魔力', 'total' => '合計', 'reward_type_basic' => '基本獎勵', 'reward_type_harem_addition' => '後宮加成', 'bonus_base' => '基礎魔力', 'lock_text' => '系統限製 %s 秒內只能點擊交換按鈕一次!', + 'text_get_by_seeding_official' => '官種加成每小時將額外得到如下的魔力值', + 'official_calculate_method' => '官種獎勵計算公式同上,只是僅針對官種進行計算,不考慮低保', + 'official_tag_bonus_additional_factor' => '最終獎勵為計算所得官種獎勵乘以官種系數,當前官種系數為: ', + 'reward_type_official_addition' => '官種加成', + 'text_get_by_harem' => '後宮加成每小時將額外得到如下的魔力值', + 'harem_additional_desc' => '後宮只考慮直屬後宮。每個後宮加成值可在此查看', + 'harem_additional_factor' => '所得獎勵為全部後宮的時魔(不考慮加成)之和,乘以後宮加成系數,當前值為:', + 'text_bonus_summary' => '每小時獲得的合計魔力值', + 'col_count' => '數量', + 'col_size' => '體積', + 'col_a' => 'A 值', ); ?> diff --git a/lang/cht/lang_settings.php b/lang/cht/lang_settings.php index 6ec1fc42..6aaf03e8 100644 --- a/lang/cht/lang_settings.php +++ b/lang/cht/lang_settings.php @@ -218,6 +218,7 @@ $lang_settings = array 'text_bonus_formula_four' => "。預設為'4'", 'text_bonus_formula_five' => "Si為第i個種子的大小,單位是GB", 'text_bonus_formula_six' => "Ni為第i個種子目前的做種者數
  • N0為參數。N0 = ", + 'text_bonus_formula_zero_bonus_factor' => "Wi為第i個種子權重系數。默認為 1,零魔種子為 ", 'text_bonus_formula_seven' => "。預設為'7'", 'text_bonus_formula_eight' => "B為1小時中用戶獲得的做種魔力值點數", 'text_bonus_formula_nine' => "B0為參數,代表用戶1小時獲得魔力值的上限。B0 = ", @@ -773,6 +774,26 @@ $lang_settings = array 'text_offer_skip_approved_count_note' => '當通過的候選數大於等於此數值時,可直接發布不用提交候選。', 'row_torrent_delete' => '移除種子', 'text_torrent_delete_note' => '。移除種子', + 'row_hundred_gb_credit' => "100.0 GB 上傳量", + 'text_hundred_gb_credit_note' => "個魔力值,如果他選擇交換100.0 GB上傳量。默認'10000'。", + 'row_ten_gb_download_credit' => "10.0 GB 下載量", + 'text_ten_gb_download_credit_note' => " 個魔力值,如果他選擇交換10.0 GB下載量。默認'1000'。", + 'row_hundred_gb_download_credit' => "100.0 GB 下載量", + 'text_hundred_gb_download_credit_note' => " 個魔力值,如果他選擇交換100.0 GB下載量。默認'8000'。", + 'row_official_addition' => '官種加成', + 'text_user_would_get_by_official' => '用戶將獲得官種正常魔力值的', + 'text_addition_addition_note' => '倍作為獎勵(系數,如填入 0.01,官種獲得 100 魔力則獎勵用戶 100 * 0.01 = 1)', + 'zero_bonus_factor_default' => '。默認為:0.2', + 'row_official_tag' => '官種標簽', + 'text_official_tag_note' => '。帶此標簽的種子為官種', + 'row_zero_bonus_tag' => '零魔標簽', + 'text_zero_bonus_tag_note' => '。帶此標簽的種子為零魔種子', + 'row_upload_deny_approval_deny_count' => '拒絕發布審核不通過數', + 'text_upload_deny_approval_deny_count_note' => "當審核不通過的種子數大於等於此數值時,不允許發布。設置為 '0' 不使用此規則", + 'row_nfo_view_style_default' => 'NFO 默認查看樣式', + 'row_destroy_disabled' => '徹底刪除賬號', + 'text_destroy_disabled_note_one' => '被封禁的賬號如果連續', + 'text_destroy_disabled_note_two' => "天不登錄,將被從數據庫徹底物理刪除。默認'500',請設置一個大於上邊任何一種導致封禁的值。設為'0'來禁止此規則。", ); ?> diff --git a/lang/cht/lang_upload.php b/lang/cht/lang_upload.php index 705d00ca..d8e870b1 100644 --- a/lang/cht/lang_upload.php +++ b/lang/cht/lang_upload.php @@ -41,6 +41,7 @@ $lang_upload = array 'text_english_title' => "英文名:", 'text_titles_note' => "(如果英文名不存在,請使用拼音或不填寫)", 'fill_quality' => '填寫質量', + 'approval_deny_reach_upper_limit' => '當前審核被拒絕的種子數:%s 達到上限,不允許發布。', ); ?> diff --git a/lang/cht/lang_userdetails.php b/lang/cht/lang_userdetails.php index f9dc757d..4a3c0a98 100644 --- a/lang/cht/lang_userdetails.php +++ b/lang/cht/lang_userdetails.php @@ -155,5 +155,6 @@ $lang_userdetails = array 'row_user_props' => '道具', 'meta_key_change_username_username' => '新用戶名', 'consume' => '使用', + 'text_bonus_table' => '時魔', ); ?> diff --git a/lang/en/lang_details.php b/lang/en/lang_details.php index 53621957..7a193e4e 100644 --- a/lang/en/lang_details.php +++ b/lang/en/lang_details.php @@ -238,6 +238,7 @@ $lang_details = array 'claim_label' => 'Claim torrent', 'claim_confirm' => 'Are you sure to claim this torrent?', 'action_approval' => 'Approval', + 'row_tags' => 'Tags', ); ?> diff --git a/lang/en/lang_fields.php b/lang/en/lang_fields.php index d3bddc60..f0829af1 100644 --- a/lang/en/lang_fields.php +++ b/lang/en/lang_fields.php @@ -26,5 +26,6 @@ $lang_fields = [ 'field_type_checkbox' => 'Horizontal multiple select', 'field_type_select' => 'Vertical single select', 'field_type_image' => 'Image', + 'col_display' => 'Custom display', -]; \ No newline at end of file +]; diff --git a/lang/en/lang_functions.php b/lang/en/lang_functions.php index 421ec32a..2f8d66ec 100644 --- a/lang/en/lang_functions.php +++ b/lang/en/lang_functions.php @@ -42,7 +42,7 @@ $lang_functions = array 'std_by' => 'By', 'row_security_image' => "Security Image:", 'row_security_code' => "Security Code:", - 'text_slots' => "Slots: ", + 'text_slots' => "Slots:", 'text_unlimited' => "Unlimited", 'std_server_load_very_high' => "The server load is very high at the moment. Retrying, please wait...", 'std_too_many_users' => "Too many users. Please press the Refresh button in your browser to retry.", @@ -83,11 +83,11 @@ $lang_functions = array 'text_ratio' => "Ratio:", 'text_uploaded' => "Uploaded:", 'text_downloaded' => "Downloaded:", - 'text_active_torrents' => "Active Torrents: ", + 'text_active_torrents' => "Active:", 'title_torrents_seeding' => "Torrents Seeding", 'title_torrents_leeching' => "Torrents Leeching", 'text_connectable' => "Connectable: ", - 'text_the_time_is_now' => "The time is now: ", + 'text_the_time_is_now' => "Time: ", 'text_message_new' => " New", 'title_sentbox' => "sentbox", 'title_inbox' => "inbox", @@ -300,7 +300,7 @@ $lang_functions = array 'text_please_download_something_within' => "Please download something within ", 'text_inactive_account_be_deleted' => ". Inactive accounts (with no transfer amount) will be deleted.", 'text_attendance' => '[Attend get bouns]', - 'text_attended' => '[Attend got bouns %u, card: %d]', + 'text_attended' => '[Attend got: %u, card: %d]', 'row_pt_gen_douban_url' => "PT-Gen douban link", 'text_pt_gen_douban_url_note' => "(URL taken from douban. e.g. for movie Transformers the URL is https://movie.douban.com/subject/1794171//)", 'row_pt_gen_imdb_url' => "PT-Gen imdb link", @@ -322,7 +322,7 @@ $lang_functions = array 'text_invalid' => 'Invalid', 'text_technical_info' => 'MediaInfo', 'text_technical_info_help_text' => 'MediaInfo comes from software MediaInfo,open file, click the view menu > text > right click in the box > select all > copy > past into this box.', - 'text_management_system' => 'Management system', + 'text_management_system' => 'Management', 'text_seed_points' => 'Seed points', 'spoiler_expand_collapse' => 'Click to expand/collapse', 'spoiler_default_title' => 'Collapse content', @@ -337,6 +337,7 @@ $lang_functions = array 'input_check_all' => "Check All", 'input_uncheck_all' => "Uncheck All", 'select_at_least_one_record' => 'Select at least one record!', + 'text_seed_box_record_to_approval' => 'There %s%u not approval seed box record%s.', ); ?> diff --git a/lang/en/lang_getusertorrentlistajax.php b/lang/en/lang_getusertorrentlistajax.php index a86a31bd..1be0f156 100644 --- a/lang/en/lang_getusertorrentlistajax.php +++ b/lang/en/lang_getusertorrentlistajax.php @@ -18,5 +18,6 @@ $lang_getusertorrentlistajax = array 'text_no_record' => "No record.", 'text_total_size' => " | Total size: ", 'col_added' => "Added", + 'col_client' => 'Client', ); ?> diff --git a/lang/en/lang_index.php b/lang/en/lang_index.php index 93a190db..d4dede07 100644 --- a/lang/en/lang_index.php +++ b/lang/en/lang_index.php @@ -104,6 +104,8 @@ $lang_index = array 'top_uploader_toggle_time_range_tab' => 'Click to toggle the time range', 'top_uploader_toggle_time_range_recently' => 'Last 30 days', 'top_uploader_toggle_time_range_all' => 'All time', + 'clear_shout_box' => 'Clear shout box', + 'sure_to_clear_shout_box' => 'Are you sure you want to clear the shout box?', ); ?> diff --git a/lang/en/lang_invite.php b/lang/en/lang_invite.php index 55710fa1..4185e322 100644 --- a/lang/en/lang_invite.php +++ b/lang/en/lang_invite.php @@ -52,6 +52,8 @@ Best Regards, 'std_or_above_can_invite' => " or above can send invitation.", 'text_email_restriction_note' => "You could only send invitation to email from those domains: ", 'harem_addition' => 'Harem addition', + 'signup_link_help' => 'Right click to copy', + 'signup_link' => 'Signup link', ); ?> diff --git a/lang/en/lang_mybonus.php b/lang/en/lang_mybonus.php index 8820f00f..70179d9f 100644 --- a/lang/en/lang_mybonus.php +++ b/lang/en/lang_mybonus.php @@ -6,6 +6,7 @@ $lang_mybonus = array 'std_karma_system_disabled' => "Karma Bonus Point System is currently disabled. ", 'std_points_active' => "However your points is still active.", 'text_success_upload' => "Congratulations! You have just increased your Uploaded Amount!", + 'text_success_download' => "Congratulations! You have just increased your Downloaded Amount!", 'text_success_invites' => "Congratulations! You have got yourself 1 new invite!", 'text_success_vip' => "Congratulations! You have got yourself ", 'text_success_vip_two' => " status for one month!", @@ -74,8 +75,12 @@ $lang_mybonus = array 'text_not_enough_bonus' => "Sorry, you do not have enough bonus. BTW, how dou you get here?", 'text_uploaded_one' => "1.0 GB Uploaded", 'text_uploaded_note' => "With enough bonus points acquired, you are able to exchange them for an Upload Credit. The points are then removed from your Bonus Bank and the credit is added to your total uploaded amount.", + 'text_download_note' => "With enough bonus points acquired, you are able to exchange them for an Download Credit. The points are then removed from your Bonus Bank and the credit is added to your total downloaded amount.", 'text_uploaded_two' => "5.0 GB Uploaded", 'text_uploaded_three' => "10.0 GB Uploaded", + 'text_uploaded_four' => "100.0 GB Uploaded", + 'text_downloaded_ten_gb' => "10.0 GB Downloaded", + 'text_downloaded_hundred_gb' => "100.0 GB Downloaded", 'text_buy_invite' => "1 Invite", 'text_buy_invite_note' => "With enough bonus points acquired, you are able to exchange them for a few invites. The points are then removed from your Bonus Bank and the invitations are added to your invites amount.", 'text_custom_title' => "Custom Title!", @@ -86,12 +91,13 @@ $lang_mybonus = array 'text_bonus_gift_note' => "Well perhaps you don't need the upload credit, but you know somebody that could use the Karma boost! You are now able to give your Karma credits as a gift! The points are then removed from your Bonus Bank and added to the account of a user of your choice! And they receive a PM with all the info as well as who it came from...", 'text_error' => "Error", 'text_ratio_too_high' => "Your ratio is high", - 'text_bonus_formula_one' => "The number of karma points gained per hour is given by the following formula
        \"A
        \"B
    + 'text_bonus_formula_one' => "The number of karma points gained per hour is given by the following formula

        
        
    where
    • A is an intermediate variable
    • Ti is the ith torrent's Time Alive (TA), i.e. time elapsed since the torrent was uploaded, in weeks
    • T0 is a parameter. T0 = ", 'text_bonus_formula_two' => "
    • Si is the ith torrent's size, in GB
    • Ni is the number of current seeders of the ith torrent
    • N0 is a parameter. N0 = ", 'text_bonus_formula_three' => "
    • B is the number of karma points gained by seeding in an hour
    • B0 is a parameter, which stands for the maximum bonus points per hour a user can get by seeding. B0 = ", 'text_bonus_formula_four' => "
    • L is a parameter. L = ", 'text_bonus_formula_five' => "
    In a nutshell, you can get more bonus by seeding less-seeded and larger torrents.", + 'text_bonus_formula_wi' => "
  • Wi is the ith torrent's weight, default is 1, zero bonus torrent is ", 'text_user_with_ratio_above' => "User with ratio above ", 'text_and_uploaded_amount_above' => " and uploaded amount above ", 'text_cannot_exchange_uploading' => " GB cannot exchange for more uploading credit.", @@ -132,13 +138,24 @@ where
    • A is an intermediate variable
    • Ti is the i< 'text_success_buy_attendance_card' => 'Success buy 1 attendance card.', 'text_harem_addition_get' => 'Current harem addition gains %s bonus per hour', 'reward_type' => 'Reward type', - 'addition' => 'Addition', + 'factor' => 'Factor', 'got_bonus' => 'Got bonus', 'total' => 'Total', 'reward_type_basic' => 'Basic reward', 'reward_type_harem_addition' => 'Harem addition', 'bonus_base' => 'Base bonus', 'lock_text' => 'The system limits you to one click on the exchange button within %s seconds!', + 'text_get_by_seeding_official' => 'The official torrents will receive the following additional bonus value per hour', + 'official_calculate_method' => 'The formula for calculating the official reward is the same as above, but only for the official type, No consideration for low income', + 'official_tag_bonus_additional_factor' => 'The final reward is the calculated official type reward multiplied by the official type factor, the current official type factor is: ', + 'reward_type_official_addition' => 'Official addition', + 'text_get_by_harem' => 'The harem addition will give the following additional bonus value per hour', + 'harem_additional_desc' => "Only direct harems will be considered for the harem. Each harem's bonus addition value can be viewed here", + 'harem_additional_factor' => 'The reward obtained is the sum of the hourly bonus of all the harems (regardless of the addition), multiplied by the harem bonus factor, with the current value of ', + 'text_bonus_summary' => 'Total bonus gained per hour', + 'col_count' => 'Counts', + 'col_size' => 'Size', + 'col_a' => 'A Value', ); ?> diff --git a/lang/en/lang_settings.php b/lang/en/lang_settings.php index 9e0fc03b..a82eb6ad 100644 --- a/lang/en/lang_settings.php +++ b/lang/en/lang_settings.php @@ -218,6 +218,7 @@ $lang_settings = array 'text_bonus_formula_four' => ". Default '4'", 'text_bonus_formula_five' => "Si is the ith torrent's size, in GB", 'text_bonus_formula_six' => "Ni is the number of current seeders of the ith torrent
    • N0 is a parameter. N0 = ", + 'text_bonus_formula_zero_bonus_factor' => "Wi is the ith torrent's weight. Default is 1, zero torrent is ", 'text_bonus_formula_seven' => ". Default '7'", 'text_bonus_formula_eight' => "B is the number of karma points gained by seeding in an hour", 'text_bonus_formula_nine' => "B0 is a parameter, which standards for the maximum bonus points per hour a user can get by seeding. B0 = ", @@ -773,6 +774,26 @@ $lang_settings = array 'text_offer_skip_approved_count_note' => 'When the number of approved offer is greater than or equal to this value, you can upload directly without submitting offers.', 'row_torrent_delete' => 'Delete torrent', 'text_torrent_delete_note' => '.Delete torrent', + 'row_hundred_gb_credit' => "100.0 GB uploading credit", + 'text_hundred_gb_credit_note' => " bonus points to exchange for 100.0 GB uploading credit. Default '10000'.", + 'row_ten_gb_download_credit' => "10.0 GB downloading credit", + 'text_ten_gb_download_credit_note' => " bonus points to exchange for 10.0 GB downloading credit. Default '1000'.", + 'row_hundred_gb_download_credit' => "100.0 GB downloading credit", + 'text_hundred_gb_download_credit_note' => " bonus points to exchange for 100.0 GB downloading credit. Default '8000'.", + 'row_official_addition' => 'Official addition', + 'text_user_would_get_by_official' => 'The user will receive the normal bonus value of the official torrent', + 'text_addition_addition_note' => 'times as reward (factor, e.g. fill in 0.01, if official torrens gets 100 bonus then reward user 100 * 0.01 = 1)', + 'zero_bonus_factor_default' => '. Default is: 0.2', + 'row_official_tag' => 'Official tag', + 'text_official_tag_note' => '. Torrents with this tag are official torrents', + 'row_zero_bonus_tag' => 'Zero bonus tag', + 'text_zero_bonus_tag_note' => '. Torrents with this tag are zero bonus torrents', + 'row_upload_deny_approval_deny_count' => 'Refuse to upload approval deny count', + 'text_upload_deny_approval_deny_count_note' => "When the number of torrents approval deny is greater than or equal to this value, publishing is not allowed. Set to '0' to not use this rule", + 'row_nfo_view_style_default' => 'NFO view style default', + 'row_destroy_disabled' => 'Delete account completely', + 'text_destroy_disabled_note_one' => 'Disabled accounts if they are continuously', + 'text_destroy_disabled_note_two' => "Days without logging in, will be physically deleted from the database completely. Default '500', please set a value greater than any of the above to cause disable. Set to '0' to disable this rule." , ); ?> diff --git a/lang/en/lang_upload.php b/lang/en/lang_upload.php index 341b9f8f..ae6125d1 100644 --- a/lang/en/lang_upload.php +++ b/lang/en/lang_upload.php @@ -41,6 +41,7 @@ $lang_upload = array 'text_english_title' => "English Name:", 'text_titles_note' => "(If no English Name exists, use pinyin or leave it blank)", 'fill_quality' => 'Fill quality', + 'approval_deny_reach_upper_limit' => 'The number of torrents whose current approval was denied: %s reached the upper limit and is not allowed to be upload.', ); ?> diff --git a/lang/en/lang_userdetails.php b/lang/en/lang_userdetails.php index 44546aae..6be9dc95 100644 --- a/lang/en/lang_userdetails.php +++ b/lang/en/lang_userdetails.php @@ -155,5 +155,6 @@ $lang_userdetails = array 'row_user_props' => 'Props', 'meta_key_change_username_username' => 'New username', 'consume' => 'Use', + 'text_bonus_table' => 'Bonus per hour', ); ?> diff --git a/nexus/Field/Field.php b/nexus/Field/Field.php index b44f69b5..9906c060 100644 --- a/nexus/Field/Field.php +++ b/nexus/Field/Field.php @@ -2,6 +2,11 @@ namespace Nexus\Field; +use App\Models\SearchBox; +use App\Models\Tag; +use App\Models\TorrentCustomField; +use App\Models\TorrentCustomFieldValue; +use Elasticsearch\Endpoints\Search; use Nexus\Database\NexusDB; class Field @@ -86,7 +91,7 @@ class Field function buildFieldForm(array $row = []) { - global $lang_fields, $lang_functions; + global $lang_fields, $lang_functions, $lang_catmanage; $trName = tr($lang_fields['col_name'] . '*', '  ' . $lang_fields['col_name_help'], 1, '', true); $trLabel = tr($lang_fields['col_label'] . '*', '', 1, '', true); $trType = tr($lang_fields['col_type'] . '*', $this->radio('type', $this->getTypeRadioOptions(), $row['type'] ?? null), 1, '', true); @@ -94,6 +99,9 @@ class Field $trHelp = tr($lang_fields['col_help'], '', 1, '', true); $trOptions = tr($lang_fields['col_options'], '
      ' . $lang_fields['col_options_help'], 1, '', true); $trIsSingleRow = tr($lang_fields['col_is_single_row'] . '*', $this->radio('is_single_row', ['0' => $lang_functions['text_no'], '1' => $lang_functions['text_yes']], $row['is_single_row'] ?? null), 1, '', true); + $trPriority = tr(nexus_trans('label.priority') . '*', '', 1, '', true); + $trDisplay = tr($lang_fields['col_display'], '
      ' . $lang_catmanage['row_custom_field_display_help'], 1, '', true); + $id = $row['id'] ?? 0; $form = << @@ -109,6 +117,8 @@ class Field {$trHelp} {$trOptions} {$trIsSingleRow} + {$trPriority} + {$trDisplay}
      @@ -126,7 +136,7 @@ HTML; $perPage = 10; $total = get_row_count('torrents_custom_fields'); list($paginationTop, $paginationBottom, $limit) = pager($perPage, $total, "?"); - $sql = "select * from torrents_custom_fields order by id asc $limit"; + $sql = "select * from torrents_custom_fields order by priority desc $limit"; $res = sql_query($sql); $header = [ 'id' => $lang_fields['col_id'], @@ -135,6 +145,7 @@ HTML; 'type_text' => $lang_fields['col_type'], 'required_text' => $lang_fields['col_required'], 'is_single_row_text' => $lang_fields['col_is_single_row'], + 'priority' => nexus_trans('label.priority'), 'action' => $lang_fields['col_action'], ]; $rows = []; @@ -203,6 +214,8 @@ HEAD; $attributes['help'] = $data['help'] ?? ''; $attributes['options'] = trim($data['options'] ?? ''); + $attributes['display'] = trim($data['display'] ?? ''); + $attributes['priority'] = trim($data['priority'] ?? '0'); $now = date('Y-m-d H:i:s'); $attributes['updated_at'] = $now; $table = 'torrents_custom_fields'; @@ -252,24 +265,29 @@ HEAD; } - public function renderOnUploadPage($torrentId = 0) + public function renderOnUploadPage($torrentId = 0, $searchBoxId) { - global $browsecatmode; - $searchBox = NexusDB::getOne('searchbox', "id = $browsecatmode"); + $searchBox = NexusDB::getOne('searchbox', "id = $searchBoxId"); if (empty($searchBox)) { - throw new \RuntimeException("Invalid search box: $browsecatmode"); + throw new \RuntimeException("Invalid search box: $searchBoxId"); } - $customValues = $this->listTorrentCustomField($torrentId); - $sql = sprintf('select * from torrents_custom_fields where id in (%s)', $searchBox['custom_fields'] ?: 0); + $customValues = $this->listTorrentCustomField($torrentId, $searchBoxId); + $sql = sprintf('select * from torrents_custom_fields where id in (%s) order by priority desc', $searchBox['custom_fields'] ?: 0); $res = sql_query($sql); $html = ''; while ($row = mysql_fetch_assoc($res)) { - $name = "custom_fields[{$row['id']}]"; + $name = "custom_fields[$searchBoxId][{$row['id']}]"; $currentValue = $customValues[$row['id']]['custom_field_value'] ?? ''; + $requireText = ''; + if ($row['required']) { + $requireText = "*"; + } + $trLabel = $row['label'] . $requireText; + $trRelation = "mode_$searchBoxId"; if ($row['type'] == self::TYPE_TEXT) { - $html .= tr($row['label'], sprintf('', $name, $currentValue, '99%'), 1); + $html .= tr($trLabel, sprintf('', $name, $currentValue, '99%'), 1, $trRelation); } elseif ($row['type'] == self::TYPE_TEXTAREA) { - $html .= tr($row['label'], sprintf('', $name, '99%', $currentValue), 1); + $html .= tr($trLabel, sprintf('', $name, '99%', $currentValue), 1, $trRelation); } elseif ($row['type'] == self::TYPE_RADIO || $row['type'] == self::TYPE_CHECKBOX) { if ($row['type'] == self::TYPE_CHECKBOX) { $name .= '[]'; @@ -293,7 +311,7 @@ HEAD; $row['type'], $name, $value, $checked, $label ); } - $html .= tr($row['label'], $part, 1); + $html .= tr($trLabel, $part, 1, $trRelation); } elseif ($row['type'] == self::TYPE_SELECT) { $part = ''; - $html .= tr($row['label'], $part, 1); + $html .= tr($trLabel, $part, 1, $trRelation); } elseif ($row['type'] == self::TYPE_IMAGE) { $callbackFunc = "preview_custom_field_image_" . $row['id']; $iframeId = "iframe_$callbackFunc"; @@ -351,18 +369,14 @@ HEAD; } JS; - $html .= tr($row['label'], $y, 1); + $html .= tr($trLabel, $y, 1, $trRelation, true); } } return $html; } - public function listTorrentCustomField($torrentId, $searchBoxId = 0) + public function listTorrentCustomField($torrentId, $searchBoxId) { - global $browsecatmode; - if ($searchBoxId <= 0) { - $searchBoxId = $browsecatmode; - } //suppose torrentId is array $isArray = true; $torrentIdArr = $torrentId; @@ -371,7 +385,7 @@ JS; $torrentIdArr = [$torrentId]; } $torrentIdStr = implode(',', $torrentIdArr); - $res = sql_query("select f.*, v.custom_field_value, v.torrent_id from torrents_custom_field_values v inner join torrents_custom_fields f on v.custom_field_id = f.id inner join searchbox box on box.id = $searchBoxId and find_in_set(f.id, box.custom_fields) where torrent_id in ($torrentIdStr)"); + $res = sql_query("select f.*, v.custom_field_value, v.torrent_id from torrents_custom_field_values v inner join torrents_custom_fields f on v.custom_field_id = f.id inner join searchbox box on box.id = $searchBoxId and find_in_set(f.id, box.custom_fields) where torrent_id in ($torrentIdStr) order by f.priority desc"); $values = []; $result = []; while ($row = mysql_fetch_assoc($res)) { @@ -402,12 +416,11 @@ JS; return $isArray ? $result : ($result[$torrentId] ?? []); } - public function renderOnTorrentDetailsPage($torrentId) + public function renderOnTorrentDetailsPage($torrentId, $searchBoxId) { - global $browsecatmode; - $displayName = get_searchbox_value($browsecatmode, 'custom_fields_display_name'); - $customFields = $this->listTorrentCustomField($torrentId); - $mixedRowContent = get_searchbox_value($browsecatmode, 'custom_fields_display'); + $displayName = get_searchbox_value($searchBoxId, 'custom_fields_display_name'); + $customFields = $this->listTorrentCustomField($torrentId, $searchBoxId); + $mixedRowContent = get_searchbox_value($searchBoxId, 'custom_fields_display'); $rowByRowHtml = ''; $shouldRenderMixRow = false; foreach ($customFields as $field) { @@ -422,12 +435,20 @@ JS; $mixedRowContent = str_replace("<%{$field['name']}.label%>", $field['label'], $mixedRowContent); $mixedRowContent = str_replace("<%{$field['name']}.value%>", $contentNotFormatted, $mixedRowContent); if ($field['is_single_row']) { - $contentFormatted = $this->formatCustomFieldValue($field, true); - $rowByRowHtml .= tr($field['label'], $contentFormatted, 1); + if (!empty($field['display'])) { + $customFieldDisplay = $field['display']; + $customFieldDisplay = str_replace("<%{$field['name']}.label%>", $field['label'], $customFieldDisplay); + $customFieldDisplay = str_replace("<%{$field['name']}.value%>", $contentNotFormatted, $customFieldDisplay); + $rowByRowHtml .= tr($field['label'], format_comment($customFieldDisplay, false), 1); + } else { + $contentFormatted = $this->formatCustomFieldValue($field, true); + $rowByRowHtml .= tr($field['label'], $contentFormatted, 1); + } } } + $result = $rowByRowHtml; - if ($shouldRenderMixRow) { + if ($shouldRenderMixRow && $mixedRowContent) { $result .= tr($displayName, format_comment($mixedRowContent), 1); } return $result; @@ -442,13 +463,13 @@ JS; switch ($customFieldWithValue['type']) { case self::TYPE_TEXT: case self::TYPE_TEXTAREA: - $result .= $doFormatComment ? format_comment($fieldValue) : $fieldValue; + $result .= $doFormatComment ? format_comment($fieldValue, false) : $fieldValue; break; case self::TYPE_IMAGE: if (substr($fieldValue, 0, 4) == 'http') { $result .= $doFormatComment ? formatImg($fieldValue, true, 700, 0, "attach{$customFieldWithValue['id']}") : $fieldValue; } else { - $result .= $doFormatComment ? format_comment($fieldValue) : $fieldValue; + $result .= $doFormatComment ? format_comment($fieldValue, false) : $fieldValue; } break; case self::TYPE_RADIO: @@ -491,6 +512,32 @@ JS; } + public function saveFieldValues($searchBoxId, $torrentId, array $data) + { + $searchBox = SearchBox::query()->findOrFail($searchBoxId); + $enabledFields = TorrentCustomField::query()->find($searchBox->custom_fields); + $insert = []; + $now = now(); + foreach ($enabledFields as $field) { + if (empty($data[$field->id])) { + if ($field->required) { +// throw new \InvalidArgumentException(nexus_trans("nexus.require_argument", ['argument' => $field->label])); + do_log("Field: {$field->label} required, but empty"); + } + continue; + } + $insert[] = [ + 'torrent_id' => $torrentId, + 'custom_field_id' => $field->id, + 'custom_field_value' => $data[$field->id], + 'created_at' => $now, + 'updated_at' => $now, + ]; + } + TorrentCustomFieldValue::query()->where('torrent_id', $torrentId)->delete(); + TorrentCustomFieldValue::query()->insert($insert); + } + } diff --git a/nexus/Imdb/Imdb.php b/nexus/Imdb/Imdb.php index baad02f9..78fe2bd5 100644 --- a/nexus/Imdb/Imdb.php +++ b/nexus/Imdb/Imdb.php @@ -34,7 +34,8 @@ class Imdb public static function listSupportLanguages(): array { - $data = require_once sprintf('%s/resources/lang/%s/imdb.php', ROOT_PATH, get_langfolder_cookie(true)); + $file = sprintf('%s/resources/lang/%s/imdb.php', ROOT_PATH, get_langfolder_cookie(true)); + $data = include $file; return $data['languages']; } diff --git a/nexus/Install/Update.php b/nexus/Install/Update.php index 54ff934a..f9d4d312 100644 --- a/nexus/Install/Update.php +++ b/nexus/Install/Update.php @@ -15,6 +15,7 @@ use App\Models\Tag; use App\Models\Torrent; use App\Models\TorrentTag; use App\Models\User; +use App\Models\UserBanLog; use App\Repositories\AttendanceRepository; use App\Repositories\BonusRepository; use App\Repositories\ExamRepository; @@ -391,6 +392,8 @@ class Update extends Install $command .= " --exclude=$exclude"; } $this->executeCommand($command); + //remove original file + unlink($filename); break; } } @@ -444,5 +447,4 @@ class Update extends Install } - } diff --git a/nexus/Install/settings.default.php b/nexus/Install/settings.default.php index eeb0c938..1f8398ea 100644 --- a/nexus/Install/settings.default.php +++ b/nexus/Install/settings.default.php @@ -92,6 +92,7 @@ return array ( 'show_top_uploader' => 'no', 'imdb_language' => 'en-US', 'offer_skip_approved_count' => 5, + 'upload_deny_approval_deny_count' => 2, ), 'smtp' => array ( @@ -179,6 +180,8 @@ return array ( 'torrent-delete' => User::CLASS_ADMINISTRATOR, 'user-delete' => User::CLASS_ADMINISTRATOR, 'user-change-class' => User::CLASS_ADMINISTRATOR, + 'torrent-set-special-tag' => User::CLASS_ADMINISTRATOR, + 'torrent-approval-allow-automatic' => User::CLASS_UPLOADER, ), 'tweak' => array ( @@ -233,6 +236,11 @@ return array ( 'cancel_hr' => BonusLogs::DEFAULT_BONUS_CANCEL_ONE_HIT_AND_RUN, 'attendance_card' => BonusLogs::DEFAULT_BONUS_BUY_ATTENDANCE_CARD, 'harem_addition' => 0, + 'hundredgbupload' => 10000, + 'tengbdownload' => 1000, + 'hundredgbdownload' => 8000, + 'official_addition' => '0.5', + 'zero_bonus_factor' => '0.2', ), 'account' => array ( @@ -296,6 +304,7 @@ return array ( 8 => '5', 9 => '10', ), + 'destroy_disabled' => 0, ), 'torrent' => array ( @@ -341,6 +350,7 @@ return array ( 'claim_reach_standard_uploaded' => \App\Models\Claim::STANDARD_UPLOADED_TIMES, 'approval_status_icon_enabled' => 'no', 'approval_status_none_visible' => 'yes', + 'nfo_view_style_default' => \App\Models\Torrent::NFO_VIEW_STYLE_DOS, ), 'attachment' => array ( @@ -418,6 +428,7 @@ return array ( 'no_promotion' => 'yes', 'max_uploaded' => 3, 'not_seed_box_max_speed' => 10240, + 'max_uploaded_duration' => 0, ], 'system' => [ 'change_username_min_interval_in_days' => '365', diff --git a/nexus/Torrent/TechnicalInformation.php b/nexus/Torrent/TechnicalInformation.php index b7080573..048f4d8f 100644 --- a/nexus/Torrent/TechnicalInformation.php +++ b/nexus/Torrent/TechnicalInformation.php @@ -163,21 +163,27 @@ class TechnicalInformation $subtitles = $this->getSubtitles(); // dd($videos, $audios, $subtitles); if (empty($videos) && empty($audios) && empty($subtitles)) { -// return ''; - return sprintf('
      %s
      ', $this->mediaInfo); + return sprintf('
      %s
      ', $this->mediaInfo); } $result = ''; + $cols = 0; if (!empty($videos)) { + $cols++; $result .= $this->buildTdTable($videos); } if (!empty($audios)) { + $cols++; $result .= $this->buildTdTable($audios); } if (!empty($subtitles)) { + $cols++; $result .= $this->buildTdTable($subtitles); } - $result .= '
      '; + $result .= ''; + $rawMediaInfo = sprintf('[spoiler=%s][raw]
      %s
      [/raw][/spoiler]', nexus_trans('torrent.show_hide_media_info'), $this->mediaInfo); + $result .= sprintf('%s', $cols, format_comment($rawMediaInfo, false)); + $result .= ''; return $result; } @@ -189,7 +195,8 @@ class TechnicalInformation $table .= sprintf('%s: %s', $key, $value); $table .= ''; } - $table .= ''; + $table .= ''; + $table .= ''; return sprintf('%s', $table); } diff --git a/public/ajax.php b/public/ajax.php index 97115177..8fe245a5 100644 --- a/public/ajax.php +++ b/public/ajax.php @@ -123,3 +123,11 @@ function consumeBenefit($params) $rep = new \App\Repositories\UserRepository(); return $rep->consumeBenefit($CURUSER['id'], $params); } + +function clearShoutBox($params) +{ + global $CURUSER; + user_can('sbmanage', true); + \Nexus\Database\NexusDB::table('shoutbox')->delete(); + return true; +} diff --git a/public/announce.php b/public/announce.php index 02737e76..19cee081 100644 --- a/public/announce.php +++ b/public/announce.php @@ -121,7 +121,7 @@ if (!$az) err("Invalid passkey! Re-download the .torrent from $BASEURL"); $userid = intval($az['id'] ?? 0); unset($GLOBALS['CURUSER']); $CURUSER = $GLOBALS["CURUSER"] = $az; -$isDonor = $az['donor'] == 'yes' && ($az['donoruntil'] === null || $az['donoruntil'] == '0000-00-00 00:00:00' || $az['donoruntil'] > date('Y-m-d H:i:s')); +$isDonor = is_donor($az); $az['__is_donor'] = $isDonor; $log = "user: $userid, isDonor: $isDonor, seeder: $seeder, ip: $ip, ipv4: $ipv4, ipv6: $ipv6"; @@ -156,7 +156,7 @@ elseif ($az['showclienterror'] == 'yes'){ } // check torrent based on info_hash -$checkTorrentSql = "SELECT id, size, owner, sp_state, seeders, leechers, UNIX_TIMESTAMP(added) AS ts, banned, hr, approval_status FROM torrents WHERE " . hash_where("info_hash", $info_hash); +$checkTorrentSql = "SELECT torrents.id, size, owner, sp_state, seeders, leechers, UNIX_TIMESTAMP(added) AS ts, added, banned, hr, approval_status, categories.mode FROM torrents left join categories on torrents.category = categories.id WHERE " . hash_where("info_hash", $info_hash); if (!$torrent = $Cache->get_value('torrent_hash_'.$info_hash.'_content')){ $res = sql_query($checkTorrentSql); $torrent = mysql_fetch_array($res); @@ -405,7 +405,7 @@ else // continue an existing session $upSpeedMbps = number_format(($trueupthis / $self['announcetime'] / 1024 / 1024) * 8); do_log("notSeedBoxMaxSpeedMbps: $notSeedBoxMaxSpeedMbps, upSpeedMbps: $upSpeedMbps"); if ($upSpeedMbps > $notSeedBoxMaxSpeedMbps) { - (new \App\Repositories\UserRepository())->updateDownloadPrivileges(null, $userid, 'no'); + (new \App\Repositories\UserRepository())->updateDownloadPrivileges(null, $userid, 'no', 'upload_over_speed'); do_log("user: $userid downloading privileges have been disabled! (over speed), notSeedBoxMaxSpeedMbps: $notSeedBoxMaxSpeedMbps > upSpeedMbps: $upSpeedMbps", 'error'); err("Your downloading privileges have been disabled! (over speed)"); } @@ -553,9 +553,10 @@ elseif(isset($self)) if (!empty($snatchInfo)) { sql_query("UPDATE snatched SET uploaded = uploaded + $trueupthis, downloaded = downloaded + $truedownthis, to_go = $left, $announcetime, last_action = ".$dt." $finished_snatched WHERE torrentid = $torrentid AND userid = $userid") or err("SL Err 2"); do_action('snatched_saved', $torrent, $snatchInfo); - if ($event == 'completed' && $az['class'] < \App\Models\HitAndRun::MINIMUM_IGNORE_USER_CLASS && !$isDonor) { + if ($event == 'completed' && $az['class'] < \App\Models\HitAndRun::MINIMUM_IGNORE_USER_CLASS && !$isDonor && isset($torrent['mode'])) { //think about H&R - $hrMode = get_setting('hr.mode'); +// $hrMode = get_setting('hr.mode'); + $hrMode = \App\Models\HitAndRun::getConfig('mode', $torrent['mode']); if ($hrMode == \App\Models\HitAndRun::MODE_GLOBAL || ($hrMode == \App\Models\HitAndRun::MODE_MANUAL && $torrent['hr'] == \App\Models\Torrent::HR_YES)) { $sql = "insert into hit_and_runs (uid, torrent_id, snatched_id) values ($userid, $torrentid, {$snatchInfo['id']}) on duplicate key update updated_at = " . sqlesc(date('Y-m-d H:i:s')); $affectedRows = sql_query($sql); diff --git a/public/catmanage.php b/public/catmanage.php index d0db9a55..9593e9cc 100644 --- a/public/catmanage.php +++ b/public/catmanage.php @@ -784,6 +784,7 @@ elseif($action == 'submit') $updateset[] = "name=".sqlesc($name); $updateset[] = "image=".sqlesc($image); $updateset[] = "class_name=".sqlesc($class_name); + $updateset[] = "source=".sqlesc($source); $updateset[] = "medium=".sqlesc($medium); $updateset[] = "codec=".sqlesc($codec); $updateset[] = "standard=".sqlesc($standard); diff --git a/public/complains.php b/public/complains.php index 20827801..9c994922 100644 --- a/public/complains.php +++ b/public/complains.php @@ -18,7 +18,11 @@ if($_SERVER['REQUEST_METHOD'] === 'POST'){ $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); $body = filter_input(INPUT_POST, 'body', FILTER_SANITIZE_FULL_SPECIAL_CHARS); if(empty($email) || empty($body)) stderr($lang_functions['std_error'], $lang_complains['text_new_failure']); - sql_query(sprintf('INSERT INTO complains (uuid, email, body, added) VALUES (UUID(), %s, %s, NOW())', sqlesc($email), sqlesc($body))) or sqlerr(__FILE__, __LINE__); + $user = \App\Models\User::query()->where('email', $email)->first(); + if (!$user) { + stderr($lang_functions['std_error'], $lang_complains['text_new_failure']); + } + sql_query(sprintf('INSERT INTO complains (uuid, email, body, added, ip) VALUES (UUID(), %s, %s, NOW(), %s)', sqlesc($email), sqlesc($body), sqlesc(getip()))) or sqlerr(__FILE__, __LINE__); $Cache->delete_value('COMPLAINTS_COUNT_CACHE'); nexus_redirect(sprintf('complains.php?action=view&id=%s', get_single_value('complains', 'uuid', 'WHERE id = ' . mysql_insert_id()))); break; @@ -27,7 +31,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST'){ $body = filter_input(INPUT_POST, 'body', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $complain = \App\Models\Complain::query()->findOrFail($id); if(empty($id) || empty($body)) stderr($lang_functions['std_error'], $lang_complains['text_new_failure']); - sql_query(sprintf('INSERT INTO complain_replies (complain, userid, added, body) VALUES (%u, %u, NOW(), %s)', $id, $uid, sqlesc($body))) or sqlerr(__FILE__, __LINE__); + sql_query(sprintf('INSERT INTO complain_replies (complain, userid, added, body, ip) VALUES (%u, %u, NOW(), %s, %s)', $id, $uid, sqlesc($body), sqlesc(getip()))) or sqlerr(__FILE__, __LINE__); if ($uid > 0) { try { $toolRep = new \App\Repositories\ToolRepository(); @@ -110,6 +114,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST'){ if ($user) { printf(' [%s]', urlencode($user->username), $lang_complains['text_view_band_log']); } + printf('
      IP: ' . htmlspecialchars($complain['ip'])); } echo '
      ', format_comment($complain['body']); end_frame(); @@ -118,7 +123,11 @@ if($_SERVER['REQUEST_METHOD'] === 'POST'){ $res = sql_query(sprintf('SELECT * FROM `complain_replies` WHERE complain = %u ORDER BY id DESC', $complain['id'])) or sqlerr(__FILE__, __LINE__); if(mysql_num_rows($res)){ while($row = mysql_fetch_assoc($res)){ - printf('%s @ %s: ', $row['userid'] ? get_plain_username($row['userid']) : $lang_complains['text_complainer'], gettime($row['added'])); + printf('%s @ %s', $row['userid'] ? get_plain_username($row['userid']) : $lang_complains['text_complainer'], gettime($row['added'])); + if ($isAdmin) { + printf(' (%s)', htmlspecialchars($row['ip'])); + } + echo ': '; echo format_comment($row['body']) . '
      '; } }else{ diff --git a/public/details.php b/public/details.php index 74831a1d..b6a440bd 100644 --- a/public/details.php +++ b/public/details.php @@ -70,7 +70,7 @@ if (!$row) { $banned_torrent = ($row["banned"] == 'yes' ? " (".$lang_functions['text_banned'].")" : ""); $sp_torrent = get_torrent_promotion_append($row['sp_state'],'word', false, '', 0, '', $row['__ignore_global_sp_state'] ?? false); $sp_torrent_sub = get_torrent_promotion_append_sub($row['sp_state'],"",true,$row['added'], $row['promotion_time_type'], $row['promotion_until'], $row['__ignore_global_sp_state'] ?? false); - $hrImg = get_hr_img($row); + $hrImg = get_hr_img($row, $row['search_box_id']); $approvalStatusIcon = $torrentRep->renderApprovalStatus($row["approval_status"]); $s=htmlspecialchars($row["name"]).$banned_torrent.($sp_torrent ? "   ".$sp_torrent : "").($sp_torrent_sub) . $hrImg . $approvalStatusIcon; print("

      ".$s."

      \n"); @@ -101,7 +101,7 @@ if (!$row) { // ------------- start upped by block ------------------// if($row['anonymous'] == 'yes') { - if (!user_can('viewanonymous')) + if (!user_can('viewanonymous') && $row['owner'] != $CURUSER['id']) $uprow = "".$lang_details['text_anonymous'].""; else $uprow = "".$lang_details['text_anonymous']." (" . get_username($row['owner'], false, true, true, false, false, true) . ")"; @@ -126,6 +126,13 @@ if (!$row) { if ($smalldescription_main == 'yes') tr($lang_details['row_small_description'],htmlspecialchars(trim($row["small_descr"])),true); + //tag + $torrentTags = \App\Models\TorrentTag::query()->where('torrent_id', $row['id'])->get(); + if ($torrentTags->isNotEmpty()) { + $tagRep = new \App\Repositories\TagRepository(); + tr($lang_details['row_tags'], $tagRep->renderSpan($torrentTags->pluck('tag_id')->toArray()),true); + } + $size_info = "".$lang_details['text_size']."" . mksize($row["size"]); $type_info = "   ".$lang_details['row_type'].": ".$row["cat_name"]; $source_info = $medium_info = $codec_info = $audiocodec_info = $standard_info = $processing_info = $team_info = ''; @@ -267,8 +274,11 @@ JS; print("\n"); // ---------------- end subtitle block -------------------// + //hook before desc + do_action('torrent_detail_before_desc', $row['id'], $CURUSER['id']); + /**************start custom fields****************/ - echo $customField->renderOnTorrentDetailsPage($id); + echo $customField->renderOnTorrentDetailsPage($id, $row['search_box_id']); /**************end custom fields****************/ @@ -290,10 +300,10 @@ JS; if (user_can('viewnfo') && $CURUSER['shownfo'] != 'no' && $row["nfosz"] > 0){ if (!$nfo = $Cache->get_value('nfo_block_torrent_id_'.$id)){ - $nfo = code($row["nfo"], $view == "magic"); + $nfo = code_new($row["nfo"], get_setting('torrent.nfo_view_style_default')); $Cache->cache_value('nfo_block_torrent_id_'.$id, $nfo, 604800); } - tr("\"Show/Hide\" ".$lang_details['text_nfo']."
      ". $lang_details['text_view_nfo']. "", "
      ".$nfo."
      \n", 1); + tr("\"Show/Hide\" ".$lang_details['text_nfo']."
      ". $lang_details['text_view_nfo']. "", "
      ".$nfo."
      \n", 1); } if ($imdb_id && $showextinfo['imdb'] == 'yes' && $CURUSER['showimdb'] != 'no') @@ -376,7 +386,7 @@ JS; { // $where_area = " url = " . sqlesc((int)$imdb_id) ." AND torrents.id != ".sqlesc($id); $where_area = sprintf('torrents.id in (%s)', implode(',', $otherCopiesIdArr)); - $copies_res = sql_query("SELECT torrents.id, torrents.name, torrents.sp_state, torrents.size, torrents.added, torrents.seeders, torrents.leechers, torrents.hr,categories.id AS catid, categories.name AS catname, categories.image AS catimage, sources.name AS source_name, media.name AS medium_name, codecs.name AS codec_name, standards.name AS standard_name, processings.name AS processing_name FROM torrents LEFT JOIN categories ON torrents.category=categories.id LEFT JOIN sources ON torrents.source = sources.id LEFT JOIN media ON torrents.medium = media.id LEFT JOIN codecs ON torrents.codec = codecs.id LEFT JOIN standards ON torrents.standard = standards.id LEFT JOIN processings ON torrents.processing = processings.id WHERE " . $where_area . " ORDER BY torrents.id DESC") or sqlerr(__FILE__, __LINE__); + $copies_res = sql_query("SELECT torrents.id, torrents.name, torrents.sp_state, torrents.size, torrents.added, torrents.seeders, torrents.leechers, torrents.hr,categories.id AS catid, categories.name AS catname, categories.image AS catimage, sources.name AS source_name, media.name AS medium_name, codecs.name AS codec_name, standards.name AS standard_name, processings.name AS processing_name, categories.mode as search_box_id FROM torrents LEFT JOIN categories ON torrents.category=categories.id LEFT JOIN sources ON torrents.source = sources.id LEFT JOIN media ON torrents.medium = media.id LEFT JOIN codecs ON torrents.codec = codecs.id LEFT JOIN standards ON torrents.standard = standards.id LEFT JOIN processings ON torrents.processing = processings.id WHERE " . $where_area . " ORDER BY torrents.id DESC") or sqlerr(__FILE__, __LINE__); $copies_count = mysql_num_rows($copies_res); if($copies_count > 0) @@ -406,7 +416,7 @@ JS; $sphighlight = get_torrent_bg_color($copy_row['sp_state']); $sp_info = get_torrent_promotion_append($copy_row['sp_state'], '', false, '', 0, '', $copy_row['__ignore_global_sp_state'] ?? false); - $hrImg = get_hr_img($copy_row); + $hrImg = get_hr_img($copy_row, $copy_row['search_box_id']); $s .= "".return_category_image($copy_row["catid"], "torrents.php?allsec=1&")."" . $dispname ."". $sp_info. $hrImg ."" . "" . rtrim(trim($other_source_info . $other_medium_info . $other_codec_info . $other_standard_info . $other_processing_info), ","). "" . diff --git a/public/edit.php b/public/edit.php index adf57b6b..672982fb 100644 --- a/public/edit.php +++ b/public/edit.php @@ -17,6 +17,7 @@ if (!$row) die(); * @since v1.6 */ $customField = new \Nexus\Field\Field(); +$hitAndRunRep = new \App\Repositories\HitAndRunRepository(); $tagIdArr = \App\Models\TorrentTag::query()->where('torrent_id', $id)->get()->pluck('tag_id')->toArray(); $searchBoxRep = new \App\Repositories\SearchBoxRepository(); @@ -68,8 +69,6 @@ else { echo $ptGen->renderUploadPageFormInput($row['pt_gen']); } - $customField->renderOnUploadPage($id); - if ($enablenfo_main=='yes') tr($lang_edit['row_nfo_file'], "".$lang_edit['radio_keep_current']. "".$lang_edit['radio_remove']. @@ -82,7 +81,7 @@ else { tr($lang_functions['text_technical_info'], '
      ' . $lang_functions['text_technical_info_help_text'], 1); } - $s = ""; $cats = genrelist($sectionmode); foreach ($cats as $subrow) { @@ -94,7 +93,7 @@ else { $s .= "\n"; if ($allowmove){ - $s2 = "\n"; $cats2 = genrelist($othermode); foreach ($cats2 as $subrow) { $s2 .= ""; } - $pickcontent .= "".$lang_edit['row_torrent_position'].": "."   "; + $pickcontent .= "".$lang_edit['row_torrent_position'].": "."   "; + $pickcontent .= datetimepicker_input('pos_state_until', $row['pos_state_until'], nexus_trans('label.deadline') . ": "); } if(user_can('torrentmanage') && ($CURUSER["picker"] == 'yes' || get_user_class() >= \App\Models\User::CLASS_SYSOP)) { @@ -208,11 +212,6 @@ else { } tr($lang_edit['row_pick'], $pickcontent, 1); } - if (get_setting('hr.mode') == \App\Models\HitAndRun::MODE_MANUAL && user_can('torrent_hr')) { - $hrRadio = sprintf('', (string)$row['hr'] === '0' ? ' checked' : ''); - $hrRadio .= sprintf('', (string)$row['hr'] === '1' ? ' checked' : ''); - tr('H&R', $hrRadio, 1); - } print(" \n"); print("\n"); @@ -293,6 +292,26 @@ EOT; } \Nexus\Nexus::js('vendor/jquery-loading/jquery.loading.min.js', 'footer', true); \Nexus\Nexus::js('js/ptgen.js', 'footer', true); +$customFieldJs = <<".$lang_getusertorrentlistajax['col_type']."".$lang_getusertorrentlistajax['col_name']."".$lang_getusertorrentlistajax['col_added']."". ($showsize ? "\"size\"" : "").($showsenum ? "\"seeders\"" : "").($showlenum ? "\"leechers\"" : "").($showuploaded ? "".$lang_getusertorrentlistajax['col_uploaded']."" : "") . ($showdownloaded ? "".$lang_getusertorrentlistajax['col_downloaded']."" : "").($showratio ? "".$lang_getusertorrentlistajax['col_ratio']."" : "").($showsetime ? "".$lang_getusertorrentlistajax['col_se_time']."" : "").($showletime ? "".$lang_getusertorrentlistajax['col_le_time']."" : "").($showcotime ? "".$lang_getusertorrentlistajax['col_time_completed']."" : "").($showanonymous ? "".$lang_getusertorrentlistajax['col_anonymous']."" : ""); + if ($shouldShowClient) { + $ret .= sprintf('%sIP', $lang_getusertorrentlistajax['col_client']); + } $ret .= sprintf('%s', $lang_functions['std_action']); $ret .= ""; $total_size = 0; @@ -150,7 +160,7 @@ function maketable($res, $mode = 'seeding') $total_size += $arr['size']; } - $hrImg = get_hr_img($arr); + $hrImg = get_hr_img($arr, $arr['search_box_id']); $approvalStatusIcon = $torrentRep->renderApprovalStatus($arr["approval_status"]); //torrent name $dispname = $nametitle = htmlspecialchars($arr["torrentname"]); @@ -212,6 +222,17 @@ function maketable($res, $mode = 'seeding') $ret .= ""."". str_replace(" ", "
      ", gettime($arr['completedat'],false)). ""; if ($showanonymous) $ret .= "".$arr['anonymous'].""; + if ($shouldShowClient) { + $ipArr = array_filter([$arr['ipv4'], $arr['ipv6']]); + foreach ($ipArr as &$_ip) { + $_ip = sprintf('%s', $_ip . $seedBoxRep->renderIcon($_ip, $arr['userid'])); + } + $ret .= sprintf( + '%s
      %s%s', + get_agent($arr['peer_id'], $arr['agent']), $arr['port'], + implode('
      ', $ipArr) + ); + } $claimButton = ''; if ( $showActionClaim @@ -245,7 +266,7 @@ switch ($type) { // $res = sql_query("SELECT torrents.id AS torrent, torrents.name as torrentname, small_descr, seeders, leechers, anonymous, torrents.banned, torrents.approval_status, categories.name AS catname, categories.image, category, sp_state, size, torrents.hr, snatched.seedtime, snatched.uploaded FROM torrents LEFT JOIN snatched ON torrents.id = snatched.torrentid LEFT JOIN categories ON torrents.category = categories.id WHERE torrents.owner=$id AND snatched.userid=$id " . (($CURUSER["id"] != $id)?((get_user_class() < $viewanonymous_class) ? " AND anonymous = 'no'":""):"") ." ORDER BY torrents.added DESC") or sqlerr(__FILE__, __LINE__); // $res = sql_query("SELECT torrents.id AS torrent, torrents.name as torrentname, small_descr, seeders, leechers, anonymous, torrents.banned, torrents.approval_status, categories.name AS catname, categories.image, category, sp_state, size, torrents.hr, torrents.added FROM torrents LEFT JOIN categories ON torrents.category = categories.id WHERE torrents.owner=$id " . (($CURUSER["id"] != $id)?((!user_can('viewanonymous')) ? " AND anonymous = 'no'":""):"") ." ORDER BY torrents.id DESC") or sqlerr(__FILE__, __LINE__); - $fields = "torrents.id AS torrent, torrents.name as torrentname, small_descr, seeders, leechers, anonymous, torrents.banned, torrents.approval_status, categories.name AS catname, categories.image, category, sp_state, size, torrents.hr, torrents.added,torrents.owner as userid"; + $fields = "torrents.id AS torrent, torrents.name as torrentname, small_descr, seeders, leechers, anonymous, torrents.banned, torrents.approval_status, categories.name AS catname, categories.image, category, sp_state, size, torrents.hr, torrents.added,torrents.owner as userid, categories.mode as search_box_id"; $tableWhere = "torrents LEFT JOIN categories ON torrents.category = categories.id WHERE torrents.owner=$id"; if ($CURUSER['id'] != $id && !user_can('viewanonymous')) { $tableWhere .= " AND anonymous = 'no'"; @@ -258,7 +279,7 @@ switch ($type) case 'seeding': { // $res = sql_query("SELECT torrent,added,snatched.uploaded,snatched.downloaded,torrents.name as torrentname, torrents.small_descr, torrents.sp_state, torrents.banned, torrents.approval_status, categories.name as catname,size,torrents.hr,image,category,seeders,leechers FROM peers LEFT JOIN torrents ON peers.torrent = torrents.id LEFT JOIN categories ON torrents.category = categories.id LEFT JOIN snatched ON torrents.id = snatched.torrentid WHERE peers.userid=$id AND snatched.userid = $id AND peers.seeder='yes' ORDER BY torrents.id DESC") or sqlerr(); - $fields = "torrent,added,snatched.uploaded,snatched.downloaded,torrents.name as torrentname, torrents.small_descr, torrents.sp_state, torrents.banned, torrents.approval_status, categories.name as catname,size,torrents.hr,image,category,seeders,leechers,snatched.seedtime,snatched.uploaded,snatched.userid"; + $fields = "torrent,added,snatched.uploaded,snatched.downloaded,snatched.seedtime,torrents.name as torrentname, torrents.small_descr, torrents.sp_state, torrents.banned, torrents.approval_status, categories.name as catname,size,torrents.hr,image,category,seeders,leechers,snatched.userid, categories.mode as search_box_id, peers.peer_id, peers.agent, peers.port, peers.ipv4, peers.ipv6"; $tableWhere = "peers LEFT JOIN torrents ON peers.torrent = torrents.id LEFT JOIN categories ON torrents.category = categories.id LEFT JOIN snatched ON torrents.id = snatched.torrentid WHERE peers.userid=$id AND snatched.userid = $id AND peers.seeder='yes'"; $order = "torrents.id DESC"; break; @@ -268,7 +289,7 @@ switch ($type) case 'leeching': { // $res = sql_query("SELECT torrent,snatched.uploaded,snatched.downloaded,torrents.name as torrentname, torrents.small_descr, torrents.sp_state, torrents.banned, torrents.approval_status, categories.name as catname,size,torrents.hr,image,category,seeders,leechers, torrents.added FROM peers LEFT JOIN torrents ON peers.torrent = torrents.id LEFT JOIN categories ON torrents.category = categories.id LEFT JOIN snatched ON torrents.id = snatched.torrentid WHERE peers.userid=$id AND snatched.userid = $id AND peers.seeder='no' ORDER BY torrents.id DESC") or sqlerr(); - $fields = "torrent,snatched.uploaded,snatched.downloaded,torrents.name as torrentname, torrents.small_descr, torrents.sp_state, torrents.banned, torrents.approval_status, categories.name as catname,size,torrents.hr,image,category,seeders,leechers, torrents.added,snatched.seedtime,snatched.uploaded,snatched.userid"; + $fields = "torrent,snatched.uploaded,snatched.downloaded,snatched.seedtime,torrents.name as torrentname, torrents.small_descr, torrents.sp_state, torrents.banned, torrents.approval_status, categories.name as catname,size,torrents.hr,image,category,seeders,leechers, torrents.added,snatched.userid, categories.mode as search_box_id, peers.peer_id, peers.agent, peers.port, peers.ipv4, peers.ipv6"; $tableWhere = "peers LEFT JOIN torrents ON peers.torrent = torrents.id LEFT JOIN categories ON torrents.category = categories.id LEFT JOIN snatched ON torrents.id = snatched.torrentid WHERE peers.userid=$id AND snatched.userid = $id AND peers.seeder='no'"; $order = "torrents.id DESC"; break; @@ -278,7 +299,7 @@ switch ($type) case 'completed': { // $res = sql_query("SELECT torrents.id AS torrent, torrents.name AS torrentname, small_descr, categories.name AS catname, torrents.banned, torrents.approval_status, categories.image, category, sp_state, size, torrents.hr, torrents.added,snatched.uploaded, snatched.seedtime, snatched.leechtime, snatched.completedat FROM torrents LEFT JOIN snatched ON torrents.id = snatched.torrentid LEFT JOIN categories on torrents.category = categories.id WHERE snatched.finished='yes' AND torrents.owner != $id AND userid=$id ORDER BY snatched.id DESC") or sqlerr(); - $fields = "torrents.id AS torrent, torrents.name AS torrentname, small_descr, categories.name AS catname, torrents.banned, torrents.approval_status, categories.image, category, sp_state, size, torrents.hr, torrents.added,snatched.uploaded, snatched.seedtime,snatched.uploaded, snatched.leechtime, snatched.completedat,snatched.userid"; + $fields = "torrents.id AS torrent, torrents.name AS torrentname, small_descr, categories.name AS catname, torrents.banned, torrents.approval_status, categories.image, category, sp_state, size, torrents.hr, torrents.added,snatched.uploaded, snatched.seedtime,snatched.uploaded, snatched.leechtime, snatched.completedat,snatched.userid, categories.mode as search_box_id"; $tableWhere = "torrents LEFT JOIN snatched ON torrents.id = snatched.torrentid LEFT JOIN categories on torrents.category = categories.id WHERE snatched.finished='yes' AND userid=$id AND torrents.owner != $id"; $order = "snatched.id DESC"; break; @@ -288,7 +309,7 @@ switch ($type) case 'incomplete': { // $res = sql_query("SELECT torrents.id AS torrent, torrents.name AS torrentname, small_descr, torrents.banned, torrents.approval_status, categories.name AS catname, categories.image, category, sp_state, size, torrents.hr, torrents.added,snatched.uploaded, snatched.downloaded, snatched.leechtime FROM torrents LEFT JOIN snatched ON torrents.id = snatched.torrentid LEFT JOIN categories on torrents.category = categories.id WHERE snatched.finished='no' AND userid=$id AND torrents.owner != $id ORDER BY snatched.id DESC") or sqlerr(); - $fields = "torrents.id AS torrent, torrents.name AS torrentname, small_descr, torrents.banned, torrents.approval_status, categories.name AS catname, categories.image, category, sp_state, size, torrents.hr, torrents.added,snatched.uploaded, snatched.downloaded, snatched.leechtime,snatched.seedtime,snatched.uploaded,snatched.userid"; + $fields = "torrents.id AS torrent, torrents.name AS torrentname, small_descr, torrents.banned, torrents.approval_status, categories.name AS catname, categories.image, category, sp_state, size, torrents.hr, torrents.added,snatched.uploaded, snatched.downloaded, snatched.leechtime,snatched.seedtime,snatched.userid, categories.mode as search_box_id"; $tableWhere = "torrents LEFT JOIN snatched ON torrents.id = snatched.torrentid LEFT JOIN categories on torrents.category = categories.id WHERE snatched.finished='no' AND userid=$id AND torrents.owner != $id"; $order = "snatched.id DESC"; break; diff --git a/public/increment-bulk.php b/public/increment-bulk.php index a2c5f723..dd60d4b0 100644 --- a/public/increment-bulk.php +++ b/public/increment-bulk.php @@ -64,7 +64,7 @@ $classes = array_chunk(\App\Models\User::$classes, 4, true); - + Subject Reason diff --git a/public/index.php b/public/index.php index 46130b2f..5f1626d6 100644 --- a/public/index.php +++ b/public/index.php @@ -137,10 +137,33 @@ if ($showfunbox_main == "yes" && (!isset($CURUSER) || $CURUSER['showfb'] == "yes // ------------- start: shoutbox ------------------// if ($showshoutbox_main == "yes") { ?> -

      -

      +

      + - + + ['.$lang_index['clear_shout_box'].']'; + $clearShoutBoxJs = << +

      \n"); - print("

      \n"); + print("

      \n"); print("
      \n"); print('
      '); print(" "); diff --git a/public/invite.php b/public/invite.php index 3de3dd13..01efb9bd 100644 --- a/public/invite.php +++ b/public/invite.php @@ -10,19 +10,19 @@ $menuSelected = $_REQUEST['menu'] ?? 'invitee'; $pageSize = 50; function inviteMenu ($selected = "invitee") { - global $lang_invite, $id, $CURUSER; + global $lang_invite, $id, $CURUSER, $invitesystem; begin_main_frame("", false, "100%"); print (""); + if (user_can('sendinvite') && $invitesystem == 'yes') { + print ("
    "); + } end_main_frame(); } if (($CURUSER['id'] != $id && !user_can('viewinvite')) || !is_valid_id($id)) stderr($lang_invite['std_sorry'],$lang_invite['std_permission_denied']); -if (!user_can('sendinvite')) -stderr($lang_invite['std_sorry'],$lang_invite['std_only'].get_user_class_name($sendinvite_class,false,true,true).$lang_invite['std_or_above_can_invite'],false); $res = sql_query("SELECT username FROM users WHERE id = ".mysql_real_escape_string($id)) or sqlerr(); $user = mysql_fetch_assoc($res); stdhead($lang_invite['head_invites']); @@ -46,6 +46,8 @@ if ($inv["invites"] != 1){ } if ($type == 'new'){ + if (!user_can('sendinvite')) + stderr($lang_invite['std_sorry'],$lang_invite['std_only'].get_user_class_name($sendinvite_class,false,true,true).$lang_invite['std_or_above_can_invite'],false, false); registration_check('invitesystem',true,false); if ($CURUSER['invites'] <= 0) { stdmsg($lang_invite['std_sorry'],$lang_invite['std_no_invites_left']. @@ -164,12 +166,17 @@ if ($type == 'new'){ for ($i = 0; $i < $num1; ++$i) { $arr1 = mysql_fetch_assoc($rer); + $isHashValid = $arr1['valid'] == \App\Models\Invite::VALID_YES; + $registerLink = ''; + if ($isHashValid) { + $registerLink = sprintf(' [%s]', $arr1['hash'], $lang_invite['signup_link_help'], $lang_invite['signup_link']); + } $tr = ""; $tr .= "{$arr1['invitee']}"; - $tr .= "{$arr1['hash']}"; + $tr .= sprintf('%s%s', $arr1['hash'], $registerLink); $tr .= "{$arr1['time_invited']}"; $tr .= "".\App\Models\Invite::$validInfo[$arr1['valid']]['text'].""; - if ($arr1['valid'] == \App\Models\Invite::VALID_NO) { + if (!$isHashValid) { $tr .= "".$arr1['invitee_register_username'].""; } else { $tr .= ""; diff --git a/public/ipcheck.php b/public/ipcheck.php index 415b54d7..e7168110 100644 --- a/public/ipcheck.php +++ b/public/ipcheck.php @@ -5,8 +5,21 @@ loggedinorreturn(); if (get_user_class() < UC_MODERATOR) stderr("Sorry", "Access denied."); -stdhead("Duplicate IP users"); -begin_frame("Duplicate IP users:", true); + +$tabs = ['users', 'peers']; +$tab = 'users'; +if (!empty($_REQUEST['tab']) && in_array($_REQUEST['tab'], $tabs)) { + $tab = $_REQUEST['tab']; +} +$page = $_REQUEST['page'] ?? 0; +$title = 'Duplicate IP users'; +stdhead($title); +print '

    '.$title.'

    '; +//print ''; begin_table(); if (get_user_class() >= UC_MODERATOR || $CURUSER["guard"] == "yes") @@ -44,7 +57,7 @@ if (get_user_class() >= UC_MODERATOR || $CURUSER["guard"] == "yes") $ratio = number_format($arr["uploaded"] / $arr["downloaded"], 3); else $ratio="---"; - + $ratio = "$ratio"; $uploaded = mksize($arr["uploaded"]); $downloaded = mksize($arr["downloaded"]); @@ -54,7 +67,7 @@ if (get_user_class() >= UC_MODERATOR || $CURUSER["guard"] == "yes") $utc = ""; else $utc = " bgcolor=\"ECE9D8\""; - + $peer_res = sql_query("SELECT count(*) FROM peers WHERE ip = " . sqlesc($ras['ip']) . " AND userid = " . $arr['id']); $peer_row = mysql_fetch_row($peer_res); print("" . get_username($arr["id"])." @@ -64,7 +77,7 @@ if (get_user_class() >= UC_MODERATOR || $CURUSER["guard"] == "yes") $downloaded $uploaded $ratio - $arr[ip]\n" . + $arr[ip]\n" . ($peer_row[0] ? "ja" : "nein") . "\n"); $ip = $arr["ip"]; } diff --git a/public/mybonus.php b/public/mybonus.php index 5be81c7e..3a51e839 100644 --- a/public/mybonus.php +++ b/public/mybonus.php @@ -39,6 +39,33 @@ function bonusarray($option = 0){ $bonus['description'] = $lang_mybonus['text_uploaded_note']; $results[] = $bonus; + //100.0 GB Uploaded + $bonus = array(); + $bonus['points'] = get_setting('bonus.hundredgbupload'); + $bonus['art'] = 'traffic'; + $bonus['menge'] = 107374182400; + $bonus['name'] = $lang_mybonus['text_uploaded_four']; + $bonus['description'] = $lang_mybonus['text_uploaded_note']; + $results[] = $bonus; + + //10.0 GB Downloaded + $bonus = array(); + $bonus['points'] = get_setting('bonus.tengbdownload'); + $bonus['art'] = 'traffic_downloaded'; + $bonus['menge'] = 10737418240; + $bonus['name'] = $lang_mybonus['text_downloaded_ten_gb']; + $bonus['description'] = $lang_mybonus['text_download_note']; + $results[] = $bonus; + + //100.0 GB Downloaded + $bonus = array(); + $bonus['points'] = get_setting('bonus.hundredgbdownload'); + $bonus['art'] = 'traffic_downloaded'; + $bonus['menge'] = 107374182400; + $bonus['name'] = $lang_mybonus['text_downloaded_hundred_gb']; + $bonus['description'] = $lang_mybonus['text_download_note']; + $results[] = $bonus; + //Invite $bonus = array(); $bonus['points'] = $oneinvite_bonus; @@ -69,7 +96,7 @@ function bonusarray($option = 0){ //Bonus Gift $bonus = array(); - $bonus['points'] = 25; + $bonus['points'] = 100; $bonus['art'] = 'gift_1'; $bonus['menge'] = 0; $bonus['name'] = $lang_mybonus['text_bonus_gift']; @@ -246,6 +273,8 @@ unset($msg); if (isset($do)) { if ($do == "upload") $msg = $lang_mybonus['text_success_upload']; + if ($do == "download") + $msg = $lang_mybonus['text_success_download']; elseif ($do == "invite") $msg = $lang_mybonus['text_success_invites']; elseif ($do == "vip") @@ -310,8 +339,8 @@ for ($i=0; $i < count($allBonus); $i++) print("

    ".$bonusarray['name']."

    ".$bonusarray['description']."

    ".$lang_mybonus['text_enter_titile'].$otheroption_title.$lang_mybonus['text_click_exchange']."".number_format($bonusarray['points']).""); } elseif ($bonusarray['art'] == 'gift_1'){ //for Give A Karma Gift - $otheroption = "
    ".$lang_mybonus['text_username']."".$lang_mybonus['text_to_be_given']."".$lang_mybonus['text_karma_points']."
    ".$lang_mybonus['text_message']."
    "; - print("

    ".$bonusarray['name']."

    ".$bonusarray['description']."

    ".$lang_mybonus['text_enter_receiver_name']."
    $otheroption".$lang_mybonus['text_min']."25
    ".$lang_mybonus['text_max']."10,000"); + $otheroption = "
    ".$lang_mybonus['text_username']."".$lang_mybonus['text_to_be_given']."".$lang_mybonus['text_karma_points']."
    ".$lang_mybonus['text_message']."
    "; + print("

    ".$bonusarray['name']."

    ".$bonusarray['description']."

    ".$lang_mybonus['text_enter_receiver_name']."
    $otheroption".$lang_mybonus['text_min']."100"); } elseif ($bonusarray['art'] == 'gift_2'){ //charity giving $otheroption = "
    ".$lang_mybonus['text_ratio_below']."".$lang_mybonus['text_and_downloaded_above']." 10 GB".$lang_mybonus['text_to_be_given']."".$lang_mybonus['text_karma_points']."
    "; @@ -391,74 +420,44 @@ print("

    ".$lang_mybonus['text_get_by_seeding']."

    "); print("
      "); if ($perseeding_bonus > 0) print("
    • ".$perseeding_bonus.$lang_mybonus['text_point'].add_s($perseeding_bonus).$lang_mybonus['text_for_seeding_torrent'].$maxseeding_bonus.$lang_mybonus['text_torrent'].add_s($maxseeding_bonus).")
    • "); -print("
    • ".$lang_mybonus['text_bonus_formula_one'].$tzero_bonus.$lang_mybonus['text_bonus_formula_two'].$nzero_bonus.$lang_mybonus['text_bonus_formula_three'].$bzero_bonus.$lang_mybonus['text_bonus_formula_four'].$l_bonus.$lang_mybonus['text_bonus_formula_five']."
    • "); +print("
    • ".$lang_mybonus['text_bonus_formula_one'].$tzero_bonus.$lang_mybonus['text_bonus_formula_two'].$nzero_bonus.$lang_mybonus['text_bonus_formula_wi'].get_setting('bonus.zero_bonus_factor').$lang_mybonus['text_bonus_formula_three'].$bzero_bonus.$lang_mybonus['text_bonus_formula_four'].$l_bonus.$lang_mybonus['text_bonus_formula_five']."
    • "); if ($donortimes_bonus) print("
    • ".$lang_mybonus['text_donors_always_get'].$donortimes_bonus.$lang_mybonus['text_times_of_bonus']."
    • "); print("
    "); -// $sqrtof2 = sqrt(2); -// $logofpointone = log(0.1); -// $valueone = $logofpointone / $tzero_bonus; -// $pi = 3.141592653589793; -// $valuetwo = $bzero_bonus * ( 2 / $pi); -// $valuethree = $logofpointone / ($nzero_bonus - 1); -// $timenow = strtotime(date("Y-m-d H:i:s")); -// $sectoweek = 7*24*60*60; -// $A = 0; -// $count = 0; -// $torrentres = sql_query("select torrents.id, torrents.added, torrents.size, torrents.seeders from torrents LEFT JOIN peers ON peers.torrent = torrents.id WHERE peers.userid = $CURUSER[id] AND peers.seeder ='yes' GROUP BY torrents.id") or sqlerr(__FILE__, __LINE__); -// while ($torrent = mysql_fetch_array($torrentres)) -// { -// $weeks_alive = ($timenow - strtotime($torrent['added'])) / $sectoweek; -// $gb_size = $torrent['size'] / 1073741824; -// $temp = (1 - exp($valueone * $weeks_alive)) * $gb_size * (1 + $sqrtof2 * exp($valuethree * ($torrent['seeders'] - 1))); -// $A += $temp; -// $count++; -// } -// if ($count > $maxseeding_bonus) -// $count = $maxseeding_bonus; -// $all_bonus = $valuetwo * atan($A / $l_bonus) + ($perseeding_bonus * $count); - $seedBonusResult = calculate_seed_bonus($CURUSER['id']); -$all_bonus = $seedBonusResult['all_bonus']; $A = $seedBonusResult['A']; - $percent = $all_bonus * 100 / ($bzero_bonus + $perseeding_bonus * $maxseeding_bonus); - print("
    ".$lang_mybonus['text_you_are_currently_getting'].round($all_bonus,3).$lang_mybonus['text_point'].add_s($all_bonus).$lang_mybonus['text_per_hour']." (A = ".round($A,1).")
    "); +$bonusTableResult = build_bonus_table($CURUSER, $seedBonusResult, ['table_style' => 'width: 50%']); - if ($percent <= 30) $loadpic = "loadbarred"; - elseif ($percent <= 60) $loadpic = "loadbaryellow"; - else $loadpic = "loadbargreen"; - $width = $percent * 4; - print("\"".$percent."%\"
    "); +$percent = $seedBonusResult['seed_bonus'] * 100 / ($bzero_bonus + $perseeding_bonus * $maxseeding_bonus); +print("
    ".$lang_mybonus['text_you_are_currently_getting'].round($seedBonusResult['seed_bonus'],3).$lang_mybonus['text_point'].add_s($seedBonusResult['seed_bonus']).$lang_mybonus['text_per_hour']." (A = ".round($A,1).")
    "); -$factor = get_setting('bonus.harem_addition'); -$addition = calculate_harem_addition($CURUSER['id']); -$totalBonus = number_format($all_bonus + $addition * $factor, 3); -$summaryTable = ''; -$summaryTable .= ''; -$summaryTable .= sprintf( - '', - $lang_mybonus['reward_type_basic'], - round($all_bonus,3), - '-', - round($all_bonus,3), - $totalBonus -); -if ($factor > 0) { - $summaryTable .= sprintf( - '', - $lang_mybonus['reward_type_harem_addition'], - number_format($addition, 3), - number_format($factor * 100, 2) . '%', - number_format($addition * $factor, 3) - ); +if ($percent <= 30) $loadpic = "loadbarred"; +elseif ($percent <= 60) $loadpic = "loadbaryellow"; +else $loadpic = "loadbargreen"; +$width = $percent * 4; +print("\"".$percent."%\"
    '.$lang_mybonus['reward_type'].''.$lang_mybonus['bonus_base'].''.$lang_mybonus['addition'].''.$lang_mybonus['got_bonus'].''.$lang_mybonus['total'].'
    %s%s%s%s%s
    %s%s%s%s
    "); + +if ($bonusTableResult['has_official_addition']) { + print("

    ".$lang_mybonus['text_get_by_seeding_official']."

    "); + print("
      "); + print("
    • ".$lang_mybonus['official_calculate_method']."
    • "); + print("
    • ".$lang_mybonus['official_tag_bonus_additional_factor'].$bonusTableResult['official_addition_factor']."
    • "); + print("
    "); } -$summaryTable .= '
    '; +if ($bonusTableResult['has_harem_addition']) { + print("

    ".$lang_mybonus['text_get_by_harem']."

    "); + print("
      "); + print("
    • ".sprintf($lang_mybonus['harem_additional_desc'], $CURUSER['id'])."
    • "); + print("
    • ".$lang_mybonus['harem_additional_factor'].$bonusTableResult['harem_addition_factor']."
    • "); + print("
    "); +} -print '
    '.$summaryTable.'
    '; +print("

    ".$lang_mybonus['text_bonus_summary']."

    "); +print '
    '.$bonusTableResult['table'].'
    '; print("

    ".$lang_mybonus['text_other_things_get_bonus']."

    "); print("
      "); @@ -534,10 +533,16 @@ if ($action == "exchange") { $up = $upload + $bonusarray['menge']; // $bonuscomment = date("Y-m-d") . " - " .$points. " Points for upload bonus.\n " .$bonuscomment; // sql_query("UPDATE users SET uploaded = ".sqlesc($up).", seedbonus = seedbonus - $points, bonuscomment = ".sqlesc($bonuscomment)." WHERE id = ".sqlesc($userid)) or sqlerr(__FILE__, __LINE__); - $bonusRep->consumeUserBonus($CURUSER['id'], $points, \App\Models\BonusLogs::BUSINESS_TYPE_EXCHANGE_UPLOAD, $points. " Points for upload bonus.", ['uploaded' => $up]); + $bonusRep->consumeUserBonus($CURUSER['id'], $points, \App\Models\BonusLogs::BUSINESS_TYPE_EXCHANGE_UPLOAD, $points. " Points for uploaded.", ['uploaded' => $up]); nexus_redirect("" . get_protocol_prefix() . "$BASEURL/mybonus.php?do=upload"); } } + if($art == "traffic_downloaded") { + $downloaded = $CURUSER['downloaded']; + $down = $downloaded + $bonusarray['menge']; + $bonusRep->consumeUserBonus($CURUSER['id'], $points, \App\Models\BonusLogs::BUSINESS_TYPE_EXCHANGE_DOWNLOAD, $points. " Points for downloaded.", ['downloaded' => $down]); + nexus_redirect("" . get_protocol_prefix() . "$BASEURL/mybonus.php?do=download"); + } //=== trade for one month VIP status ***note "SET class = '10'" change "10" to whatever your VIP class number is elseif($art == "class") { if (get_user_class() >= UC_VIP) { @@ -628,10 +633,15 @@ if ($action == "exchange") { $usernamegift = sqlesc(trim($_POST["username"])); $res = sql_query("SELECT id, bonuscomment FROM users WHERE username=" . $usernamegift); $arr = mysql_fetch_assoc($res); + if (empty($arr)) { + stdmsg($lang_mybonus['text_error'], $lang_mybonus['text_receiver_not_exists'], 0); + stdfoot(); + die; + } $useridgift = $arr['id']; $userseedbonus = $arr['seedbonus']; $receiverbonuscomment = $arr['bonuscomment']; - if ($points < 25 || $points > 10000) { + if (!is_numeric($points) || $points < $bonusarray['points']) { //write_log("User " . $CURUSER["username"] . "," . $CURUSER["ip"] . " is hacking bonus system",'mod'); stdmsg($lang_mybonus['text_error'], $lang_mybonus['bonus_amount_not_allowed']); stdfoot(); @@ -654,11 +664,6 @@ if ($action == "exchange") { stdfoot(); die; } - if (!$useridgift){ - stdmsg($lang_mybonus['text_error'], $lang_mybonus['text_receiver_not_exists'], 0); - stdfoot(); - die; - } // sql_query("UPDATE users SET seedbonus = seedbonus - $points, bonuscomment = ".sqlesc($bonuscomment)." WHERE id = ".sqlesc($userid)) or sqlerr(__FILE__, __LINE__); $bonusRep->consumeUserBonus($CURUSER['id'], $points, \App\Models\BonusLogs::BUSINESS_TYPE_GIFT_TO_SOMEONE, $points2 . " Points as gift to ".htmlspecialchars(trim($_POST["username"]))); diff --git a/public/myhr.php b/public/myhr.php index 1df8fa02..e5730e0f 100644 --- a/public/myhr.php +++ b/public/myhr.php @@ -67,7 +67,8 @@ if ($rescount) { $query = (clone $baseQuery) ->with([ - 'torrent' => function ($query) {$query->select(['id', 'size', 'name']);}, + 'torrent' => function ($query) {$query->select(['id', 'size', 'name', 'category']);}, + 'torrent.basic_category', 'snatch', 'user' => function ($query) {$query->select(['id', 'lang']);}, 'user.language', @@ -81,20 +82,21 @@ if ($rescount) { $list = $query->get(); $hasActionRemove = false; foreach($list as $row) { - $columnAction = ''; - if ($row->uid == $CURUSER['id'] && $row->status == \App\Models\HitAndRun::STATUS_INSPECTING) { + $columnAction = ''; + if ($row->uid == $CURUSER['id'] && in_array($row->status, \App\Models\HitAndRun::CAN_PARDON_STATUS)) { $hasActionRemove = true; - $columnAction = sprintf('', $lang_myhr['action_remove'], $row->id); + $columnAction .= sprintf('', $lang_myhr['action_remove'], $row->id); } + $columnAction .= ''; print(" " . $row->id . " " . optional($row->torrent)->name . " " . mksize($row->snatch->uploaded) . " " . mksize($row->snatch->downloaded) . " " . get_hr_ratio($row->snatch->uploaded, $row->snatch->downloaded) . " - " . ($row->status == \App\Models\HitAndRun::STATUS_INSPECTING ? mkprettytime(3600 * get_setting('hr.seed_time_minimum') - $row->snatch->seedtime) : '---') . " + " . $row->seedTimeRequired . " " . format_datetime($row->snatch->completedat) . " - " . ($row->status == \App\Models\HitAndRun::STATUS_INSPECTING ? mkprettytime(\Carbon\Carbon::now()->diffInSeconds($row->snatch->completedat->addHours(get_setting('hr.inspect_time')))) : '---') . " + " . $row->inspectTimeLeft . " " . nl2br(trim($row->comment)) . " {$columnAction} "); diff --git a/public/pic/bonusformulaa.svg b/public/pic/bonusformulaa.svg new file mode 100644 index 00000000..6b95be41 --- /dev/null +++ b/public/pic/bonusformulaa.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/public/search.php b/public/search.php new file mode 100644 index 00000000..3c6c9b54 --- /dev/null +++ b/public/search.php @@ -0,0 +1,125 @@ +join($tableCategory, "$tableTorrent.category", "=", "$tableCategory.id"); + if (get_setting('main.spsct') == 'yes' && !user_can('view_special_torrent')) { + $torrentQuery->where("$tableCategory.mode", get_setting('main.browsecat')); + } + if ($searchArea == \App\Repositories\SearchRepository::SEARCH_AREA_TITLE) { + foreach ($searchArr as $queryString) { + $q = "%{$queryString}%"; + $torrentQuery->where(function (\Illuminate\Database\Query\Builder $query) use ($q, $tableTorrent) { + return $query->where("$tableTorrent.name", 'like', $q)->orWhere("$tableTorrent.small_descr", "like", $q); + }); + } + } elseif ($searchArea == \App\Repositories\SearchRepository::SEARCH_AREA_DESC) { + foreach ($searchArr as $queryString) { + $q = "%{$queryString}%"; + $torrentQuery->where("$tableTorrent.descr", "like", $q); + } + } elseif ($searchArea == \App\Repositories\SearchRepository::SEARCH_AREA_OWNER) { + $torrentQuery->join($tableUser, "$tableTorrent.owner", "=", "$tableUser.id"); + foreach ($searchArr as $queryString) { + $q = "%{$queryString}%"; + $torrentQuery->where("$tableUser.username", "like", $q); + } + } elseif ($searchArea == \App\Repositories\SearchRepository::SEARCH_AREA_IMDB) { + foreach ($searchArr as $queryString) { + $q = "%{$queryString}%"; + $torrentQuery->where("$tableTorrent.url", "like", $q); + } + } else { + foreach ($searchArr as $queryString) { + $q = "%{$queryString}%"; + $torrentQuery->where("$tableTorrent.name", "like", $q); + } + write_log("User " . $CURUSER["username"] . "," . $CURUSER["ip"] . " is hacking search_area field in" . $_SERVER['SCRIPT_NAME'], 'mod'); + } + if ($approvalStatus !== null) { + $torrentQuery->where("$tableTorrent.approval_status", $approvalStatus); + } + $torrentQuery->where("$tableTorrent.visible", 'yes'); + + $count = $torrentQuery->count(); +} + +if ($CURUSER["torrentsperpage"]) + $torrentsperpage = (int)$CURUSER["torrentsperpage"]; +elseif ($torrentsperpage_main) + $torrentsperpage = $torrentsperpage_main; +else $torrentsperpage = 50; + +// sorting by MarkoStamcar +$column = 'id'; +$ascdesc = 'desc'; +$addparam = "?search=$search&search_area=$searchArea&"; +if (isset($_GET['sort']) && $_GET['sort'] && isset($_GET['type']) && $_GET['type']) { + + switch($_GET['sort']) { + case '1': $column = "name"; break; + case '2': $column = "numfiles"; break; + case '3': $column = "comments"; break; + case '4': $column = "added"; break; + case '5': $column = "size"; break; + case '6': $column = "times_completed"; break; + case '7': $column = "seeders"; break; + case '8': $column = "leechers"; break; + case '9': $column = "owner"; break; + default: $column = "id"; break; + } + + switch($_GET['type']) { + case 'asc': $ascdesc = "ASC"; $linkascdesc = "asc"; break; + case 'desc': $ascdesc = "DESC"; $linkascdesc = "desc"; break; + default: $ascdesc = "DESC"; $linkascdesc = "desc"; break; + } + + $addparam .= "sort=" . intval($_GET['sort']) . "&type=" . $linkascdesc . "&"; + +} + +list($pagertop, $pagerbottom, $limit, $offset, $size, $page) = pager($torrentsperpage, $count, $addparam); + +stdhead(nexus_trans('search.global_search')); +print("
      "); +if ($search && $count > 0) { + $fieldsStr = implode(', ', \App\Models\Torrent::getFieldsForList(true)); + $rows = $torrentQuery->selectRaw("$fieldsStr, categories.mode as search_box_id") + ->forPage($page + 1, $torrentsperpage) + ->orderBy("$tableTorrent.$column", $ascdesc) + ->get() + ->toArray(); + print($pagertop); + torrenttable(json_decode(json_encode($rows), true)); + print($pagerbottom); +} else { + stdmsg($lang_torrents['std_search_results_for'] . $search . "\"",$lang_torrents['std_try_again']); +} +print("
      "); +stdfoot(); + + + diff --git a/public/settings.php b/public/settings.php index d97ba88f..7cfdfaed 100644 --- a/public/settings.php +++ b/public/settings.php @@ -41,7 +41,8 @@ if ($action == 'savesettings_main') // save main 'showpolls','showstats','showlastxtorrents', 'showtrackerload','showshoutbox','showfunbox','showoffer','sptime','showhelpbox','enablebitbucket', 'smalldescription','altname','extforum','extforumurl','defaultlang','defstylesheet', 'donation','spsct','browsecat','specialcat','waitsystem', 'maxdlsystem','bitbucket','torrentnameprefix', 'showforumstats','verification','invite_count','invite_timeout', 'seeding_leeching_time_calc_start', - 'startsubid', 'logo', 'showlastxforumposts', 'enable_technical_info', 'site_language_enabled', 'show_top_uploader', 'imdb_language', 'offer_skip_approved_count' + 'startsubid', 'logo', 'showlastxforumposts', 'enable_technical_info', 'site_language_enabled', 'show_top_uploader', 'imdb_language', 'offer_skip_approved_count', + 'upload_deny_approval_deny_count' ); GetVar($validConfig); $MAIN = []; @@ -97,7 +98,7 @@ elseif ($action == 'savesettings_bonus') // save bonus 'addcomment','pollvote','offervote', 'funboxvote','saythanks','receivethanks','funboxreward','onegbupload','fivegbupload', 'tengbupload', 'ratiolimit','dlamountlimit','oneinvite','customtitle','vipstatus','bonusgift', 'basictax', 'taxpercentage', 'prolinkpoint', 'prolinktime', 'attendance_initial', 'attendance_step', 'attendance_max', 'cancel_hr', 'attendance_card', - 'harem_addition' + 'harem_addition', 'hundredgbupload', 'tengbdownload', 'hundredgbdownload', 'official_addition', 'official_tag', 'zero_bonus_tag', 'zero_bonus_factor', ); GetVar($validConfig); $BONUS = []; @@ -133,7 +134,7 @@ elseif ($action == 'savesettings_account') // save account 'exutime', 'exudl', \App\Models\User::CLASS_EXTREME_USER . '_min_seed_points', 'exuprratio', 'exuderatio', \App\Models\User::CLASS_EXTREME_USER . '_alias', 'uutime', 'uudl', \App\Models\User::CLASS_ULTIMATE_USER . '_min_seed_points', 'uuprratio', 'uuderatio', \App\Models\User::CLASS_ULTIMATE_USER . '_alias', 'nmtime', 'nmdl', \App\Models\User::CLASS_NEXUS_MASTER . '_min_seed_points', 'nmprratio', 'nmderatio', \App\Models\User::CLASS_NEXUS_MASTER . '_alias', - 'getInvitesByPromotion' + 'getInvitesByPromotion', 'destroy_disabled' ); GetVar($validConfig); $ACCOUNT = []; @@ -155,7 +156,8 @@ elseif($action == 'savesettings_torrent') // save account 'twoupbecome','twoupfreebecome', 'twouphalfleechbecome','normalbecome','uploaderdouble','deldeadtorrent', 'randomthirtypercentdown', 'thirtypercentleechbecome', 'expirethirtypercentleech', 'sticky_first_level_background_color', 'sticky_second_level_background_color', 'download_support_passkey', 'claim_enabled', 'claim_torrent_ttl', 'claim_torrent_user_counts_up_limit', 'claim_user_torrent_counts_up_limit', 'claim_remove_deduct_user_bonus', - 'claim_give_up_deduct_user_bonus', 'claim_bonus_multiplier', 'claim_reach_standard_seed_time', 'claim_reach_standard_uploaded', 'approval_status_icon_enabled', 'approval_status_none_visible' + 'claim_give_up_deduct_user_bonus', 'claim_bonus_multiplier', 'claim_reach_standard_seed_time', 'claim_reach_standard_uploaded', 'approval_status_icon_enabled', 'approval_status_none_visible', + 'nfo_view_style_default', ); $validConfig = apply_filter('setting_valid_config', $validConfig); GetVar($validConfig); @@ -222,7 +224,7 @@ elseif ($action == 'savesettings_authority') // save user authority 'torrentstructure','sendinvite','viewhistory','topten','log','confilog','userprofile', 'torrenthistory','prfmanage', 'cruprfmanage', 'uploadsub','delownsub','submanage','updateextinfo', 'viewanonymous','beanonymous','addoffer','offermanage', 'upload','uploadspecial', 'view_special_torrent','movetorrent','chrmanage','viewinvite', 'buyinvite','seebanned','againstoffer','userbar', 'torrent-approval', - 'torrent-delete', 'user-delete', 'user-change-class', + 'torrent-delete', 'user-delete', 'user-change-class', 'torrent-set-special-tag', 'torrent-approval-allow-automatic' ); GetVar($validConfig); $AUTHORITY = []; @@ -450,7 +452,10 @@ elseif ($action == 'authoritysettings') //Authority settings tr($lang_settings['row_torrent_sticky'], $lang_settings['text_minimum_class'].classlist('torrentsticky',$maxclass,$AUTHORITY['torrentsticky'],0,true).$lang_settings['text_default'].get_user_class_name(UC_ADMINISTRATOR,false,true,true).$lang_settings['text_torrent_sticky_note'],1); tr($lang_settings['row_torrent_on_promotion'], $lang_settings['text_minimum_class'].classlist('torrentonpromotion',$maxclass,$AUTHORITY['torrentonpromotion'] ?? '',0,true).$lang_settings['text_default'].get_user_class_name(UC_ADMINISTRATOR,false,true,true).$lang_settings['text_torrent_promotion_note'],1); tr($lang_settings['row_torrent_hr'], $lang_settings['text_minimum_class'].classlist('torrent_hr',$maxclass,$AUTHORITY['torrent_hr'] ?? '',0,true).$lang_settings['text_default'].get_user_class_name(UC_ADMINISTRATOR,false,true,true).$lang_settings['text_torrent_hr_note'],1); + tr(nexus_trans('permission.torrent-set-special-tag.text'), $lang_settings['text_minimum_class'].classlist('torrent-set-special-tag',$maxclass,$AUTHORITY['torrent-set-special-tag'] ?? '',0,true).$lang_settings['text_default'].get_user_class_name(UC_ADMINISTRATOR,false,true,true).nexus_trans('permission.torrent-set-special-tag.desc'),1); tr(nexus_trans('permission.torrent-approval.text'), $lang_settings['text_minimum_class'].classlist('torrent-approval',$maxclass,$AUTHORITY['torrent-approval'] ?? '',0,true).$lang_settings['text_default'].get_user_class_name(UC_ADMINISTRATOR,false,true,true).nexus_trans('permission.torrent-approval.desc'),1); + tr(nexus_trans('permission.torrent-approval-allow-automatic.text'), $lang_settings['text_minimum_class'].classlist('torrent-approval-allow-automatic',$maxclass,$AUTHORITY['torrent-approval-allow-automatic'] ?? '',0,true).$lang_settings['text_default'].get_user_class_name(UC_UPLOADER,false,true,true).nexus_trans('permission.torrent-approval-allow-automatic.desc'),1); + tr($lang_settings['row_ask_for_reseed'], $lang_settings['text_minimum_class'].classlist('askreseed',$maxclass,$AUTHORITY['askreseed'],0,true).$lang_settings['text_default'].get_user_class_name(UC_POWER_USER,false,true,true).$lang_settings['text_ask_for_reseed_note'],1); tr($lang_settings['row_view_nfo'], $lang_settings['text_minimum_class'].classlist('viewnfo',$maxclass,$AUTHORITY['viewnfo'],0,true).$lang_settings['text_default'].get_user_class_name(UC_POWER_USER,false,true,true).$lang_settings['text_view_nfo_note'],1); tr($lang_settings['row_view_torrent_structure'], $lang_settings['text_minimum_class'].classlist('torrentstructure',$maxclass,$AUTHORITY['torrentstructure'],0,true).$lang_settings['text_default'].get_user_class_name(UC_ULTIMATE_USER,false,true,true).$lang_settings['text_view_torrent_structure_note'],1); @@ -481,8 +486,12 @@ elseif ($action == 'authoritysettings') //Authority settings tr($lang_settings['row_see_banned_torrents'], $lang_settings['text_minimum_class'].classlist('seebanned',$maxclass,$AUTHORITY['seebanned'],0,true).$lang_settings['text_default'].get_user_class_name(UC_UPLOADER,false,true,true).$lang_settings['text_see_banned_torrents_note'],1); tr($lang_settings['row_vote_against_offers'], $lang_settings['text_minimum_class'].classlist('againstoffer',$maxclass,$AUTHORITY['againstoffer'],0,true).$lang_settings['text_default'].get_user_class_name(UC_USER,false,true,true).$lang_settings['text_vote_against_offers_note'],1); tr($lang_settings['row_allow_userbar'], $lang_settings['text_minimum_class'].classlist('userbar',$maxclass,$AUTHORITY['userbar'],0,true).$lang_settings['text_default'].get_user_class_name(UC_POWER_USER,false,true,true).$lang_settings['text_allow_userbar_note'],1); - tr($lang_settings['row_save_settings'],"", 1); - print (""); + +// tr(nexus_trans('permission.not-counting-downloaded.text'), $lang_settings['text_minimum_class'].classlist('not-counting-downloaded',$maxclass,$AUTHORITY['not-counting-downloaded'] ?? '',0,true).nexus_trans('permission.not-counting-downloaded.desc'),1); +// tr(nexus_trans('permission.not-counting-hit-and-run.text'), $lang_settings['text_minimum_class'].classlist('not-counting-hit-and-run',$maxclass,$AUTHORITY['not-counting-hit-and-run'] ?? '',0,true).nexus_trans('permission.not-counting-hit-and-run.desc'),1); + + tr($lang_settings['row_save_settings'],"", 1); + print (""); } elseif ($action == 'basicsettings') // basic settings { @@ -562,7 +571,18 @@ elseif ($action == 'bonussettings'){ print("".$lang_settings['text_bonus_by_seeding'].""); tr($lang_settings['row_donor_gets_double'], $lang_settings['text_donor_gets']."".$lang_settings['text_times_as_many'],1); tr($lang_settings['row_basic_seeding_bonus'], $lang_settings['text_user_would_get']."".$lang_settings['text_bonus_points']."".$lang_settings['text_torrents_default'], 1); - tr($lang_settings['row_seeding_formula'], $lang_settings['text_bonus_formula_one']."
          \"A
          \"B
      ".$lang_settings['text_where']."
      • ".$lang_settings['text_bonus_formula_two']."
      • ".$lang_settings['text_bonus_formula_three']."".$lang_settings['text_bonus_formula_four']."
      • ".$lang_settings['text_bonus_formula_five']."
      • ".$lang_settings['text_bonus_formula_six']."".$lang_settings['text_bonus_formula_seven']."
      • ".$lang_settings['text_bonus_formula_eight']."
      • ".$lang_settings['text_bonus_formula_nine']."".$lang_settings['text_bonus_formula_ten']."
      • ".$lang_settings['text_bonus_formula_eleven']."".$lang_settings['text_bonus_formula_twelve']."
      \n", 1); + + $formulaLiArr = []; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_two']."
    • "; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_three']."".$lang_settings['text_bonus_formula_four']."
    • "; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_five']."
    • "; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_six']."".$lang_settings['text_bonus_formula_seven']."
    • "; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_zero_bonus_factor']."".$lang_settings['zero_bonus_factor_default']."
    • "; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_eight']."
    • "; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_nine']."".$lang_settings['text_bonus_formula_ten']."
    • "; + $formulaLiArr[] = "
    • ".$lang_settings['text_bonus_formula_eleven']."".$lang_settings['text_bonus_formula_twelve']."
    • "; + tr($lang_settings['row_seeding_formula'], $lang_settings['text_bonus_formula_one']."

          
          \"B
      ".$lang_settings['text_where']."
        ".implode("", $formulaLiArr)."
      ", 1); + print("".$lang_settings['text_misc_ways_get_bonus'].""); tr($lang_settings['row_uploading_torrent'],$lang_settings['text_user_would_get']."".$lang_settings['text_uploading_torrent_note'], 1); tr($lang_settings['row_uploading_subtitle'],$lang_settings['text_user_would_get']."".$lang_settings['text_uploading_subtitle_note'], 1); @@ -576,12 +596,21 @@ elseif ($action == 'bonussettings'){ tr($lang_settings['row_funbox_stuff_reward'],$lang_settings['text_user_would_get']."".$lang_settings['text_funbox_stuff_reward_note'], 1); tr($lang_settings['row_promotion_link_click'],$lang_settings['text_user_would_get']."".$lang_settings['text_promotion_link_note_one']."".$lang_settings['text_promotion_link_note_two'], 1); tr($lang_settings['row_harem_addition'],$lang_settings['text_user_would_get_by_harem']."".$lang_settings['text_harem_addition_note'], 1); + tr($lang_settings['row_official_addition'],$lang_settings['text_user_would_get_by_official']."".$lang_settings['text_addition_addition_note'], 1); + $tagRep = new \App\Repositories\TagRepository(); + tr($lang_settings['row_official_tag'], $tagRep->buildSelect('official_tag', $BONUS["official_tag"] ?? '') . $lang_settings['text_official_tag_note'], 1); + tr($lang_settings['row_zero_bonus_tag'], $tagRep->buildSelect('zero_bonus_tag', $BONUS["zero_bonus_tag"] ?? '') . $lang_settings['text_zero_bonus_tag_note'], 1); print("".$lang_settings['text_things_cost_bonus'].""); tr($lang_settings['row_one_gb_credit'],$lang_settings['text_it_costs_user']."".$lang_settings['text_one_gb_credit_note'], 1); tr($lang_settings['row_five_gb_credit'],$lang_settings['text_it_costs_user']."".$lang_settings['text_five_gb_credit_note'], 1); tr($lang_settings['row_ten_gb_credit'],$lang_settings['text_it_costs_user']."".$lang_settings['text_ten_gb_credit_note'], 1); + tr($lang_settings['row_hundred_gb_credit'],$lang_settings['text_it_costs_user']."".$lang_settings['text_hundred_gb_credit_note'], 1); + tr($lang_settings['row_ten_gb_download_credit'],$lang_settings['text_it_costs_user']."".$lang_settings['text_ten_gb_download_credit_note'], 1); + tr($lang_settings['row_hundred_gb_download_credit'],$lang_settings['text_it_costs_user']."".$lang_settings['text_hundred_gb_download_credit_note'], 1); + + tr($lang_settings['row_ratio_limit'],$lang_settings['text_user_with_ratio']."".$lang_settings['text_uploaded_amount_above']."".$lang_settings['text_ratio_limit_default'], 1); tr($lang_settings['row_buy_an_invite'],$lang_settings['text_it_costs_user']."".$lang_settings['text_buy_an_invite_note'], 1); tr($lang_settings['row_custom_title'],$lang_settings['text_it_costs_user']."".$lang_settings['text_custom_title_note'], 1); @@ -626,6 +655,9 @@ elseif ($action == 'accountsettings'){ tr($lang_settings['row_delete_packed'],$lang_settings['text_delete_packed_note_one']."".$lang_settings['text_delete_packed_note_two'], 1); tr($lang_settings['row_delete_unpacked'],$lang_settings['text_delete_unpacked_note_one']."".$lang_settings['text_delete_unpacked_note_two'], 1); tr($lang_settings['row_delete_no_transfer'],$lang_settings['text_delete_transfer_note_one']."".$lang_settings['text_delete_transfer_note_two']."".$lang_settings['text_delete_transfer_note_three'], 1); + tr($lang_settings['row_destroy_disabled'],$lang_settings['text_destroy_disabled_note_one']."".$lang_settings['text_destroy_disabled_note_two'], 1); + + print("".$lang_settings['text_user_promotion_demotion'].""); tr($lang_settings['row_ban_peasant_one'].get_user_class_name(UC_PEASANT,false,false,true).$lang_settings['row_ban_peasant_two'],get_user_class_name(UC_PEASANT,false,true,true).$lang_settings['text_ban_peasant_note_one']."".$lang_settings['text_ban_peasant_note_two'], 1); $inputAlias = "0_alias"; @@ -689,6 +721,17 @@ elseif ($action == 'torrentsettings') yesorno($lang_settings['row_download_support_passkey'], 'download_support_passkey', $TORRENT["download_support_passkey"], $lang_settings['text_download_support_passkey_note']); yesorno($lang_settings['row_approval_status_icon_enabled'], 'approval_status_icon_enabled', $TORRENT["approval_status_icon_enabled"], $lang_settings['text_approval_status_icon_enabled_note']); yesorno($lang_settings['row_approval_status_none_visible'], 'approval_status_none_visible', $TORRENT["approval_status_none_visible"], $lang_settings['text_approval_status_none_visible_note']); + + $nfoViewStyleRadio = ''; + $name = 'nfo_view_style_default'; + foreach (\App\Models\Torrent::$nfoViewStyles as $style => $info) { + $nfoViewStyleRadio .= sprintf( + '', + $name, $style, $TORRENT[$name] == $style ? ' checked' : '', $info['text'] + ); + } + tr($lang_settings['row_' . $name], $nfoViewStyleRadio, 1); + yesorno($lang_settings['row_promotion_rules'], 'prorules', $TORRENT["prorules"], $lang_settings['text_promotion_rules_note']); tr($lang_settings['row_random_promotion'], $lang_settings['text_random_promotion_note_one']."
      • ".$lang_settings['text_halfleech_chance_becoming']."
      • ".$lang_settings['text_free_chance_becoming']."
      • ".$lang_settings['text_twoup_chance_becoming']."
      • ".$lang_settings['text_freetwoup_chance_becoming']."
      • ".$lang_settings['text_twouphalfleech_chance_becoming']."
      • ".$lang_settings['text_thirtypercentleech_chance_becoming']."
      ".$lang_settings['text_random_promotion_note_two'], 1); tr($lang_settings['row_large_torrent_promotion'], $lang_settings['text_torrent_larger_than']."".$lang_settings['text_gb_promoted_to']."".$lang_settings['text_by_system_upon_uploading']."
      ".$lang_settings['text_large_torrent_promotion_note'], 1); @@ -851,6 +894,7 @@ JS; tr($lang_settings['row_offer_vote_timeout']," ".$lang_settings['text_offer_vote_timeout_note'], 1); tr($lang_settings['row_offer_upload_timeout']," ".$lang_settings['text_offer_upload_timeout_note'], 1); tr($lang_settings['row_offer_skip_approved_count']," ".$lang_settings['text_offer_skip_approved_count_note'], 1); + tr($lang_settings['row_upload_deny_approval_deny_count']," ".$lang_settings['text_upload_deny_approval_deny_count_note'], 1); tr($lang_settings['row_max_subtitle_size']," ". $lang_settings['text_max_subtitle_size_note'], 1); tr($lang_settings['row_posts_per_page']," ".$lang_settings['text_posts_per_page_note'], 1); diff --git a/public/staffbox.php b/public/staffbox.php index b865cab5..764a53ee 100644 --- a/public/staffbox.php +++ b/public/staffbox.php @@ -239,6 +239,9 @@ header("Refresh: 0; url=staffbox.php?action=viewpm&pmid=$id"); ////////////////////////// if ($action == "takecontactanswered") { + if (empty($_POST['setanswered'])) { + stderr($lang_staffbox['std_sorry'], nexus_trans('nexus.select_one_please')); + } if ($_POST['setdealt']){ $res = sql_query ("SELECT * FROM staffmessages WHERE answered=0 AND id IN (" . implode(", ", $_POST['setanswered']) . ")"); diff --git a/public/staffmess.php b/public/staffmess.php index 699c31a2..ac297e90 100644 --- a/public/staffmess.php +++ b/public/staffmess.php @@ -24,35 +24,43 @@ if ($_GET["returnto"] || $_SERVER["HTTP_REFERER"]) -The message has ben sent. +The message has ben sent. -Send to:
      - - '); - foreach ($chunk as $class => $info) { - printf('', $class, $info['text']); - } - printf(''); - } - ?> -
      - + Send to class: + + + '); + foreach ($chunk as $class => $info) { + printf('', $class, $info['text']); + } + printf(''); + } + ?> +
      + -Subject - + -
      Sender:   + Subject + + + + Message + + + +
      Sender:     System
      - + > diff --git a/public/styles/nexus.css b/public/styles/nexus.css new file mode 100644 index 00000000..7038afea --- /dev/null +++ b/public/styles/nexus.css @@ -0,0 +1,66 @@ +img.hitandrun { + width: 35px; + height: 12px; + background: url(icons.gif) no-repeat -100px -171px; + margin-left: 0.5em; +} +.spoiler-title { + line-height: 28px; + color: #4d6c99; + cursor: pointer; + font-weight: 700; + background-color: rgba(77, 108, 153, 0.1); + display: inline-block; + padding: 0 10px; +} +.spoiler-content { + display: inline-block; + height: 0; + overflow: hidden; + transition: height 0.3s ease; +} +.layui-layer { + color: black; +} +.layer-form { + padding: 10px 15px; +} +.layer-form .form-control-row input{ + padding: 4px; +} +.layer-form .form-control-row .label{ + margin-bottom: 4px; +} +.rainbow { + text-align: center; + text-decoration: underline; + /*font-size: 32px;*/ + /*font-family: monospace;*/ + /*letter-spacing: 5px;*/ + background: linear-gradient(to right, #6666ff, #0099ff , #00ff00, #ff3399, #6666ff); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + animation: rainbow_animation 6s ease-in-out infinite; + background-size: 400% 100%; +} + +@keyframes rainbow_animation { + 0%,100% { + background-position: 0 0; + } + + 50% { + background-position: 100% 0; + } +} +.nexus-media-info-raw { + padding: 0 0.5rem; + border: none; +} +.nexus-media-info-raw .spoiler-title-box{ + text-align: center; +} +.nexus-media-info-raw pre { + white-space: break-spaces; +} diff --git a/public/styles/sprites.css b/public/styles/sprites.css index 0c51de22..7e2f3ae1 100644 --- a/public/styles/sprites.css +++ b/public/styles/sprites.css @@ -474,62 +474,3 @@ img.sltbar height:10px; background:url(bar.gif) repeat-x 0 -116px; } -img.hitandrun { - width: 35px; - height: 12px; - background: url(icons.gif) no-repeat -100px -171px; - margin-left: 0.5em; -} -.spoiler-title { - line-height: 40px; - color: #4d6c99; - cursor: pointer; - font-weight: 700; - background-color: rgba(77, 108, 153, 0.1); - display: inline-block; - padding: 0 10px; -} -.spoiler-content { - display: inline-block; - height: 0; - overflow: hidden; - transition: height 0.3s ease; -} -.layui-layer { - color: black; -} -.layer-form { - padding: 10px 15px; -} -.layer-form .form-control-row input{ - padding: 4px; -} -.layer-form .form-control-row .label{ - margin-bottom: 4px; -} -.rainbow { - text-align: center; - text-decoration: underline; - /*font-size: 32px;*/ - /*font-family: monospace;*/ - /*letter-spacing: 5px;*/ - background: linear-gradient(to right, #6666ff, #0099ff , #00ff00, #ff3399, #6666ff); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - animation: rainbow_animation 6s ease-in-out infinite; - background-size: 400% 100%; -} - -@keyframes rainbow_animation { - 0%,100% { - background-position: 0 0; - } - - 50% { - background-position: 100% 0; - } -} -.hide { - display: none; -} diff --git a/public/take-increment-bulk.php b/public/take-increment-bulk.php index 85a45968..9a570566 100644 --- a/public/take-increment-bulk.php +++ b/public/take-increment-bulk.php @@ -37,7 +37,7 @@ $conditions = []; if (!empty($_POST['classes'])) { $conditions[] = "class IN (" . implode(', ', $_POST['classes']) . ")"; } -$conditions = apply_filter("increment_bulk_query_conditions", $conditions, $_POST); +$conditions = apply_filter("role_query_conditions", $conditions, $_POST); if (empty($conditions)) { stderr("Error","No valid filter"); } diff --git a/public/takeedit.php b/public/takeedit.php index 5278e0f4..ad310b0f 100644 --- a/public/takeedit.php +++ b/public/takeedit.php @@ -53,13 +53,6 @@ if (!empty($_POST['pt_gen'])) { $updateset[] = "technical_info = " . sqlesc($_POST['technical_info'] ?? ''); $torrentOperationLog = []; -/** - * hr - * @since 1.6.0-beta12 - */ -if (isset($_POST['hr']) && isset(\App\Models\Torrent::$hrStatus[$_POST['hr']]) && user_can('torrent_hr')) { - $updateset[] = "hr = " . sqlesc($_POST['hr']); -} if ($enablenfo_main=='yes'){ @@ -143,9 +136,22 @@ if(user_can('torrentonpromotion')) } } } -if(user_can('torrentsticky') && isset($_POST['sel_posstate']) && isset(\App\Models\Torrent::$posStates[$_POST['sel_posstate']])) +if(user_can('torrentsticky')) { - $updateset[] = "pos_state = '" . $_POST['sel_posstate'] . "'"; + if (isset($_POST['pos_state']) && isset(\App\Models\Torrent::$posStates[$_POST['pos_state']])) { + $posStateUntil = $_POST['pos_state_until'] ?: null; + $posState = $_POST['pos_state']; + if ($posState == \App\Models\Torrent::POS_STATE_STICKY_NONE) { + $posStateUntil = null; + } + if ($posStateUntil && \Carbon\Carbon::parse($posStateUntil)->lte(now())) { + $posState = \App\Models\Torrent::POS_STATE_STICKY_NONE; + $posStateUntil = null; + } + $updateset[] = sprintf("pos_state = %s", sqlesc($posState)); + $updateset[] = sprintf("pos_state_until = %s", sqlesc($posStateUntil)); + } + } $pick_info = ""; @@ -202,7 +208,17 @@ $descriptionArr = format_description($descr); $cover = get_image_from_description($descriptionArr, true, false); $updateset[] = "cover = " . sqlesc($cover); -$affectedRows = sql_query("UPDATE torrents SET " . join(",", $updateset) . " WHERE id = $id") or sqlerr(__FILE__, __LINE__); +/** + * hr + * @since 1.6.0-beta12 + */ +if (isset($_POST['hr'][$newcatmode]) && isset(\App\Models\Torrent::$hrStatus[$_POST['hr'][$newcatmode]]) && user_can('torrent_hr')) { + $updateset[] = "hr = " . sqlesc($_POST['hr'][$newcatmode]); +} + +$sql = "UPDATE torrents SET " . join(",", $updateset) . " WHERE id = $id"; +do_log("[UPDATE_TORRENT]: $sql"); +$affectedRows = sql_query($sql) or sqlerr(__FILE__, __LINE__); $dateTimeStringNow = date("Y-m-d H:i:s"); @@ -210,20 +226,9 @@ $dateTimeStringNow = date("Y-m-d H:i:s"); * add custom fields * @since v1.6 */ -if (!empty($_POST['custom_fields'])) { - \Nexus\Database\NexusDB::delete('torrents_custom_field_values', "torrent_id = $id"); - foreach ($_POST['custom_fields'] as $customField => $customValue) { - foreach ((array)$customValue as $value) { - $customData = [ - 'torrent_id' => $id, - 'custom_field_id' => $customField, - 'custom_field_value' => $value, - 'created_at' => $dateTimeStringNow, - 'updated_at' => $dateTimeStringNow, - ]; - \Nexus\Database\NexusDB::insert('torrents_custom_field_values', $customData); - } - } +if (!empty($_POST['custom_fields'][$newcatmode])) { + $customField = new \Nexus\Field\Field(); + $customField->saveFieldValues($newcatmode, $id, $_POST['custom_fields'][$newcatmode]); } /** diff --git a/public/takestaffmess.php b/public/takestaffmess.php index e30f6f25..57d86e64 100644 --- a/public/takestaffmess.php +++ b/public/takestaffmess.php @@ -28,11 +28,19 @@ $subject = trim($_POST['subject']); $size = 10000; $page = 1; set_time_limit(300); -$classStr = implode(",", $updateset); +$conditions = []; +if (!empty($_POST['classes'])) { + $conditions[] = "class IN (" . implode(', ', $_POST['classes']) . ")"; +} +$conditions = apply_filter("role_query_conditions", $conditions, $_POST); +if (empty($conditions)) { + stderr("Error","No valid filter"); +} +$whereStr = implode(' OR ', $conditions); while (true) { $msgValues = []; $offset = ($page - 1) * $size; - $query = sql_query("SELECT id FROM users WHERE class IN ($classStr) and `enabled` = 'yes' and `status` = 'confirmed' limit $offset, $size"); + $query = sql_query("SELECT id FROM users WHERE ($whereStr) and `enabled` = 'yes' and `status` = 'confirmed' limit $offset, $size"); while($dat=mysql_fetch_assoc($query)) { $msgValues[] = sprintf('(%s, %s, %s, %s, %s)', $sender_id, $dat['id'], $dt, sqlesc($subject), sqlesc($msg)); diff --git a/public/takeupload.php b/public/takeupload.php index f1312c3d..08bfdcdb 100644 --- a/public/takeupload.php +++ b/public/takeupload.php @@ -339,9 +339,38 @@ $insert = [ 'technical_info' => $_POST['technical_info'] ?? '', 'cover' => $cover, ]; -if (isset($_POST['hr']) && isset(\App\Models\Torrent::$hrStatus[$_POST['hr']]) && user_can('torrent_hr')) { - $insert['hr'] = $_POST['hr']; +if (isset($_POST['hr'][$catmod]) && isset(\App\Models\Torrent::$hrStatus[$_POST['hr'][$catmod]]) && user_can('torrent_hr')) { + $insert['hr'] = $_POST['hr'][$catmod]; } +if(user_can('torrentsticky')) { + if (isset($_POST['pos_state']) && isset(\App\Models\Torrent::$posStates[$_POST['pos_state']])) { + $posStateUntil = $_POST['pos_state_until'] ?: null; + $posState = $_POST['pos_state']; + if ($posState == \App\Models\Torrent::POS_STATE_STICKY_NONE) { + $posStateUntil = null; + } + if ($posStateUntil && \Carbon\Carbon::parse($posStateUntil)->lte(now())) { + $posState = \App\Models\Torrent::POS_STATE_STICKY_NONE; + $posStateUntil = null; + } + $insert['pos_state'] = $posState; + $insert['pos_state_until'] = $posStateUntil; + } +} +if(user_can('torrentmanage') && ($CURUSER['picker'] == 'yes' || get_user_class() >= \App\Models\User::CLASS_SYSOP)) { + if (isset($_POST['picktype']) && isset(\App\Models\Torrent::$pickTypes[$_POST['picktype']])) { + $insert['picktype'] = $_POST['picktype']; + if ($insert['picktype'] == \App\Models\Torrent::PICK_NORMAL) { + $insert['picktime'] = null; + } else { + $insert['picktime'] = now()->toDateTimeString(); + } + } +} +if (user_can('torrent-approval-allow-automatic')) { + $insert['approval_status'] = \App\Models\Torrent::APPROVAL_STATUS_ALLOW; +} +do_log("[INSERT_TORRENT]: " . nexus_json_encode($insert)); $id = \Nexus\Database\NexusDB::insert('torrents', $insert); //$ret = sql_query("INSERT INTO torrents (filename, owner, visible, anonymous, name, size, numfiles, type, url, small_descr, descr, ori_descr, category, source, medium, codec, audiocodec, standard, processing, team, save_as, sp_state, added, last_action, nfo, info_hash, pt_gen, technical_info) VALUES (".sqlesc($fname).", ".sqlesc($CURUSER["id"]).", 'yes', ".sqlesc($anonymous).", ".sqlesc($torrent).", ".sqlesc($totallen).", ".count($filelist).", ".sqlesc($type).", ".sqlesc($url).", ".sqlesc($small_descr).", ".sqlesc($descr).", ".sqlesc($descr).", ".sqlesc($catid).", ".sqlesc($sourceid).", ".sqlesc($mediumid).", ".sqlesc($codecid).", ".sqlesc($audiocodecid).", ".sqlesc($standardid).", ".sqlesc($processingid).", ".sqlesc($teamid).", ".sqlesc($dname).", ".sqlesc($sp_state) . @@ -365,20 +394,9 @@ if ($saveResult === false) { * add custom fields * @since v1.6 */ -if (!empty($_POST['custom_fields'])) { - $now = date('Y-m-d H:i:s'); - foreach ($_POST['custom_fields'] as $customField => $customValue) { - foreach ((array)$customValue as $value) { - $customData = [ - 'torrent_id' => $id, - 'custom_field_id' => $customField, - 'custom_field_value' => $value, - 'created_at' => $now, - 'updated_at' => $now, - ]; - \Nexus\Database\NexusDB::insert('torrents_custom_field_values', $customData); - } - } +if (!empty($_POST['custom_fields'][$catmod])) { + $customField = new \Nexus\Field\Field(); + $customField->saveFieldValues($catmod, $id, $_POST['custom_fields'][$catmod]); } /** diff --git a/public/testip.php b/public/testip.php index 341b2684..90606199 100644 --- a/public/testip.php +++ b/public/testip.php @@ -29,7 +29,7 @@ if ($ip) $banstable .= "$first$last$comment\n"; } $banstable .= "\n"; - stderr("Result", "
      The IP address ". htmlspecialchars($ip) ." is banned:

      ". htmlspecialchars($banstable) ."

      "); + stderr("Result", "
      The IP address ". $ip ." is banned:

      ". $banstable ."

      ", false); } } stdhead(); diff --git a/public/torrents.php b/public/torrents.php index 32377e3d..4d4acebc 100644 --- a/public/torrents.php +++ b/public/torrents.php @@ -10,8 +10,7 @@ parked(); * tags */ $tagRep = new \App\Repositories\TagRepository(); -$tagKeyById = $tagRep->createBasicQuery()->get()->keyBy('id'); -$renderKeyArr = $tagKeyById->keys()->toArray(); +$allTags = $tagRep->listAll(); $elasticsearchEnabled = nexus_env('ELASTICSEARCH_ENABLED'); //check searchbox @@ -914,11 +913,11 @@ if ($count) list($pagertop, $pagerbottom, $limit, $offset, $size, $page) = pager($torrentsperpage, $count, "?" . $addparam); $fieldsStr = implode(', ', \App\Models\Torrent::getFieldsForList(true)); - if ($allsec == 1 || $enablespecial != 'yes') { - $query = "SELECT $fieldsStr FROM torrents ".($search_area == 3 || $column == "owner" ? "LEFT JOIN users ON torrents.owner = users.id " : "")." $tagFilter $where $orderby $limit"; - } else { - $query = "SELECT $fieldsStr FROM torrents ".($search_area == 3 || $column == "owner" ? "LEFT JOIN users ON torrents.owner = users.id " : "")." LEFT JOIN categories ON torrents.category=categories.id $tagFilter $where $orderby $limit"; - } +// if ($allsec == 1 || $enablespecial != 'yes') { +// $query = "SELECT $fieldsStr FROM torrents ".($search_area == 3 || $column == "owner" ? "LEFT JOIN users ON torrents.owner = users.id " : "")." $tagFilter $where $orderby $limit"; +// } else { + $query = "SELECT $fieldsStr, categories.mode as search_box_id FROM torrents ".($search_area == 3 || $column == "owner" ? "LEFT JOIN users ON torrents.owner = users.id " : "")." LEFT JOIN categories ON torrents.category=categories.id $tagFilter $where $orderby $limit"; +// } do_log("[TORRENT_LIST_SQL] $query", 'debug'); if (!$elasticsearchEnabled) { $res = sql_query($query); @@ -1130,7 +1129,7 @@ if (!$Cache->get_page()){ $hotsearch = ""; while ($searchrow = mysql_fetch_assoc($searchres)) { - $hotsearch .= "" . $searchrow["keywords"] . "  "; + $hotsearch .= "" . htmlspecialchars($searchrow["keywords"]) . "  "; $hotcount += mb_strlen($searchrow["keywords"],"UTF-8"); if ($hotcount > 60) break; @@ -1143,8 +1142,8 @@ if (!$Cache->get_page()){ } echo $Cache->next_row(); -if ($tagKeyById->isNotEmpty()) { - echo '' . $tagRep->renderSpan($tagKeyById, $renderKeyArr, true) . ''; +if ($allTags->isNotEmpty()) { + echo '' . $tagRep->renderSpan(['*'], true) . ''; } ?> diff --git a/public/upload.php b/public/upload.php index 54a36406..e79783ed 100644 --- a/public/upload.php +++ b/public/upload.php @@ -2,6 +2,7 @@ require_once("../include/bittorrent.php"); dbconn(); require_once(get_langfile_path()); +require_once(get_langfile_path('edit.php')); loggedinorreturn(); parked(); $userInfo = \App\Models\User::query()->findOrFail($CURUSER['id']); @@ -13,10 +14,15 @@ if ($enableoffer == 'yes') else $has_allowed_offer = 0; $uploadfreely = user_can_upload("torrents"); $offerSkipApprovedCount = get_setting('main.offer_skip_approved_count'); -do_log("uploadfreely: $uploadfreely, has_allowed_offer: $has_allowed_offer, offerSkipApprovedCount: $offerSkipApprovedCount"); +$uploadDenyApprovalDenyCount = get_setting('main.upload_deny_approval_deny_count'); +$approvalDenyCount = \App\Models\Torrent::query()->where('owner', $CURUSER['id'])->where('approval_status', \App\Models\Torrent::APPROVAL_STATUS_DENY)->count(); +do_log("uploadfreely: $uploadfreely, has_allowed_offer: $has_allowed_offer, offerSkipApprovedCount: $offerSkipApprovedCount, uploadDenyApprovalDenyCount: $uploadDenyApprovalDenyCount, approvalDenyCount: $approvalDenyCount"); $allowtorrents = ($has_allowed_offer || $uploadfreely || ($userInfo->offer_allowed_count >= $offerSkipApprovedCount)); $allowspecial = user_can_upload("music"); +if ($uploadDenyApprovalDenyCount > 0 && $approvalDenyCount >= $uploadDenyApprovalDenyCount) { + stderr($lang_upload['std_sorry'],sprintf($lang_upload['approval_deny_reach_upper_limit'], $uploadDenyApprovalDenyCount),false); +} if (!$allowtorrents && !$allowspecial) stderr($lang_upload['std_sorry'],$lang_upload['std_please_offer'],false); $allowtwosec = ($allowtorrents && $allowspecial); @@ -71,8 +77,6 @@ stdhead($lang_upload['head_upload']); $ptGen = new \Nexus\PTGen\PTGen(); echo $ptGen->renderUploadPageFormInput(""); } - $field = new \Nexus\Field\Field(); - $field->renderOnUploadPage(); if ($enablenfo_main=='yes') { tr($lang_upload['row_nfo_file'], "
      ".$lang_upload['text_only_viewed_by'].get_user_class_name($viewnfo_class,false,true,true).$lang_upload['text_or_above']."", 1); } @@ -86,7 +90,7 @@ stdhead($lang_upload['head_upload']); if ($allowtorrents){ $disablespecial = " onchange=\"disableother('browsecat','specialcat')\""; - $s = "\n\n"; $cats = genrelist($browsecatmode); foreach ($cats as $row) $s .= "\n"; @@ -95,7 +99,7 @@ stdhead($lang_upload['head_upload']); else $s = ""; if ($allowspecial){ $disablebrowse = " onchange=\"disableother('specialcat','browsecat')\""; - $s2 = "\n\n"; $cats2 = genrelist($specialcatmode); foreach ($cats2 as $row) $s2 .= "\n"; @@ -147,13 +151,19 @@ stdhead($lang_upload['head_upload']); tr($lang_upload['row_content'],$team_select,1); } */ + $customField = new \Nexus\Field\Field(); + $hitAndRunRep = new \App\Repositories\HitAndRunRepository(); if ($allowtorrents) { $selectNormal = $searchBoxRep->renderQualitySelect($browsecatmode); tr($lang_upload['row_quality'], $selectNormal, 1, "hide mode mode_$browsecatmode"); + echo $customField->renderOnUploadPage(0, $browsecatmode); + echo $hitAndRunRep->renderOnUploadPage('', $browsecatmode); } if ($allowspecial) { $selectNormal = $searchBoxRep->renderQualitySelect($specialcatmode); tr($lang_upload['row_quality'], $selectNormal, 1, "hide mode mode_$specialcatmode"); + echo $customField->renderOnUploadPage(0, $specialcatmode); + echo $hitAndRunRep->renderOnUploadPage('', $specialcatmode); } //==== offer dropdown for offer mod from code by S4NE @@ -191,16 +201,35 @@ JS; } //===end + //pick + $pickcontent = ''; + if(user_can('torrentsticky')) + { + $options = []; + foreach (\App\Models\Torrent::listPosStates() as $key => $value) { + $options[] = "".$value['text'].""; + } + $pickcontent .= "".$lang_edit['row_torrent_position'].": "."   "; + $pickcontent .= datetimepicker_input('pos_state_until', '', nexus_trans('label.deadline') . ": "); + } + if(user_can('torrentmanage') && ($CURUSER["picker"] == 'yes' || get_user_class() >= \App\Models\User::CLASS_SYSOP)) + { + if ($pickcontent) $pickcontent .= '
      '; + $pickcontent .= "".$lang_edit['row_recommended_movie'].": "."'; + } + if ($pickcontent) { + tr($lang_edit['row_pick'], $pickcontent, 1); + } + if(user_can('beanonymous')) { tr($lang_upload['row_show_uploader'], "".$lang_upload['checkbox_hide_uploader_note'], 1); } tr($lang_functions['text_tags'], (new \App\Repositories\TagRepository())->renderCheckbox(), 1); - if (get_setting('hr.mode') == \App\Models\HitAndRun::MODE_MANUAL && user_can('torrent_hr')) { - $hrRadio = sprintf('', ''); - $hrRadio .= sprintf('', ''); - tr('H&R', $hrRadio, 1); - } ?> @@ -208,4 +237,18 @@ JS; 0) { + jQuery("tr[relation=mode_" + mode +"]").show(); + } +}) +jQuery("tr[relation]").hide(); +JS; +\Nexus\Nexus::js($customFieldJs, 'footer', false); stdfoot(); diff --git a/public/userdetails.php b/public/userdetails.php index 59da6d2b..f5900081 100644 --- a/public/userdetails.php +++ b/public/userdetails.php @@ -241,7 +241,7 @@ if (mysql_num_rows($res) > 0) $clientselect .= ""; $clientselect .= sprintf('%s', get_agent($arr['peer_id'], $arr['agent'])); if (user_can('userprofile') || $user["id"] == $CURUSER["id"]) { - $clientselect .= sprintf('%s%s%s', $arr['ipv4'].$seedBoxRep->renderIcon($arr['ipv4'], $CURUSER['id']), $arr['ipv6'].$seedBoxRep->renderIcon($arr['ipv6'], $CURUSER['id']), $arr['port']); + $clientselect .= sprintf('%s%s%s', $arr['ipv4'].$seedBoxRep->renderIcon($arr['ipv4'], $user['id']), $arr['ipv6'].$seedBoxRep->renderIcon($arr['ipv6'], $user['id']), $arr['port']); } else { $clientselect .= sprintf('%s%s%s', '---', '---', '---'); } @@ -394,6 +394,10 @@ if ($user["id"] == $CURUSER["id"] || user_can('viewhistory')) { tr_small($lang_functions['text_seed_points'], number_format($user['seed_points'], 1), 1); } +if (user_can('prfmanage') && $user["class"] < get_user_class()) { + $bonusTable = build_bonus_table($user); + tr_small($lang_userdetails['text_bonus_table'], $bonusTable['table'], 1); +} if ($user["ip"] && (user_can('torrenthistory') || $user["id"] == $CURUSER["id"])){ diff --git a/public/vendor/jquery-datetimepicker/jquery.datetimepicker.full.min.js b/public/vendor/jquery-datetimepicker/jquery.datetimepicker.full.min.js new file mode 100644 index 00000000..7947c33a --- /dev/null +++ b/public/vendor/jquery-datetimepicker/jquery.datetimepicker.full.min.js @@ -0,0 +1 @@ +var DateFormatter;!function(){"use strict";var D,s,r,a,n;D=function(e,t){return"string"==typeof e&&"string"==typeof t&&e.toLowerCase()===t.toLowerCase()},s=function(e,t,a){var n=a||"0",r=e.toString();return r.length
      '),u=L('
      '),d.append(u),l.addClass("xdsoft_scroller_box").append(d),p=function(e){var t=a(e).y-r+g;t<0&&(t=0),t+u[0].offsetHeight>h&&(t=h-u[0].offsetHeight),l.trigger("scroll_element.xdsoft_scroller",[c?t/c:0])},u.on("touchstart.xdsoft_scroller mousedown.xdsoft_scroller",function(e){i||l.trigger("resize_scroll.xdsoft_scroller",[y]),r=a(e).y,g=parseInt(u.css("marginTop"),10),h=d[0].offsetHeight,"mousedown"===e.type||"touchstart"===e.type?(D.ownerDocument&&L(D.ownerDocument.body).addClass("xdsoft_noselect"),L([D.ownerDocument.body,D.contentWindow]).on("touchend mouseup.xdsoft_scroller",function e(){L([D.ownerDocument.body,D.contentWindow]).off("touchend mouseup.xdsoft_scroller",e).off("mousemove.xdsoft_scroller",p).removeClass("xdsoft_noselect")}),L(D.ownerDocument.body).on("mousemove.xdsoft_scroller",p)):(t=!0,e.stopPropagation(),e.preventDefault())}).on("touchmove",function(e){t&&(e.preventDefault(),p(e))}).on("touchend touchcancel",function(){t=!1,g=0}),l.on("scroll_element.xdsoft_scroller",function(e,t){i||l.trigger("resize_scroll.xdsoft_scroller",[t,!0]),t=1'),e=L(''),g=L('
      '),F=L('
      '),C=L('
      '),o=L('
      '),u=o.find(".xdsoft_time_box").eq(0),P=L('
      '),i=L(''),Y=L('
      '),A=L('
      '),s=!1,d=0;I.id&&_.attr("id",I.id),I.style&&_.attr("style",I.style),I.weeks&&_.addClass("xdsoft_showweeks"),I.rtl&&_.addClass("xdsoft_rtl"),_.addClass("xdsoft_"+I.theme),_.addClass(I.className),F.find(".xdsoft_month span").after(Y),F.find(".xdsoft_year span").after(A),F.find(".xdsoft_month,.xdsoft_year").on("touchstart mousedown.xdsoft",function(e){var t,a,n=L(this).find(".xdsoft_select").eq(0),r=0,o=0,i=n.is(":visible");for(F.find(".xdsoft_select").hide(),W.currentTime&&(r=W.currentTime[L(this).hasClass("xdsoft_month")?"getMonth":"getFullYear"]()),n[i?"hide":"show"](),t=n.find("div.xdsoft_option"),a=0;aI.touchMovedThreshold&&(this.touchMoved=!0)};function f(){var e,t=!1;return I.startDate?t=W.strToDate(I.startDate):(t=I.value||(O&&O.val&&O.val()?O.val():""))?(t=W.strToDateTime(t),I.yearOffset&&(t=new Date(t.getFullYear()-I.yearOffset,t.getMonth(),t.getDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()))):I.defaultDate&&(t=W.strToDateTime(I.defaultDate),I.defaultTime&&(e=W.strtotime(I.defaultTime),t.setHours(e.getHours()),t.setMinutes(e.getMinutes()))),t&&W.isValidDate(t)?_.data("changed",!0):t="",t||0}function c(m){var h=function(e,t){var a=e.replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g,"\\$1").replace(/_/g,"{digit+}").replace(/([0-9]{1})/g,"{digit$1}").replace(/\{digit([0-9]{1})\}/g,"[0-$1_]{1}").replace(/\{digit[\+]\}/g,"[0-9_]{1}");return new RegExp(a).test(t)},g=function(e,t){if(!(e="string"==typeof e||e instanceof String?m.ownerDocument.getElementById(e):e))return!1;if(e.createTextRange){var a=e.createTextRange();return a.collapse(!0),a.moveEnd("character",t),a.moveStart("character",t),a.select(),!0}return!!e.setSelectionRange&&(e.setSelectionRange(t,t),!0)};m.mask&&O.off("keydown.xdsoft"),!0===m.mask&&(E.formatMask?m.mask=E.formatMask(m.format):m.mask=m.format.replace(/Y/g,"9999").replace(/F/g,"9999").replace(/m/g,"19").replace(/d/g,"39").replace(/H/g,"29").replace(/i/g,"59").replace(/s/g,"59")),"string"===L.type(m.mask)&&(h(m.mask,O.val())||(O.val(m.mask.replace(/[0-9]/g,"_")),g(O[0],0)),O.on("paste.xdsoft",function(e){var t=(e.clipboardData||e.originalEvent.clipboardData||window.clipboardData).getData("text"),a=this.value,n=this.selectionStart;return a=a.substr(0,n)+t+a.substr(n+t.length),n+=t.length,h(m.mask,a)?(this.value=a,g(this,n)):""===L.trim(a)?this.value=m.mask.replace(/[0-9]/g,"_"):O.trigger("error_input.xdsoft"),e.preventDefault(),!1}),O.on("keydown.xdsoft",function(e){var t,a=this.value,n=e.which,r=this.selectionStart,o=this.selectionEnd,i=r!==o;if(48<=n&&n<=57||96<=n&&n<=105||8===n||46===n){for(t=8===n||46===n?"_":String.fromCharCode(96<=n&&n<=105?n-48:n),8===n&&r&&!i&&(r-=1);;){var s=m.mask.substr(r,1),d=r",I.weeks&&(l+=""),e=0;e<7;e+=1)l+=""+I.i18n[R].dayOfWeekShort[(e+I.dayOfWeekStart)%7]+"";for(l+="",l+="",!1!==I.maxDate&&(h=W.strToDate(I.maxDate),h=new Date(h.getFullYear(),h.getMonth(),h.getDate(),23,59,59,999)),!1!==I.minDate&&(g=W.strToDate(I.minDate),g=new Date(g.getFullYear(),g.getMonth(),g.getDate())),!1!==I.minDateTime&&(p=W.strToDate(I.minDateTime),p=new Date(p.getFullYear(),p.getMonth(),p.getDate(),p.getHours(),p.getMinutes(),p.getSeconds())),!1!==I.maxDateTime&&(D=W.strToDate(I.maxDateTime),D=new Date(D.getFullYear(),D.getMonth(),D.getDate(),D.getHours(),D.getMinutes(),D.getSeconds())),!1!==D&&(u=31*(12*D.getFullYear()+D.getMonth())+D.getDate());c",v=!1,I.weeks&&(l+=""+o+"")),l+='
      '+n+"
      ",f.getDay()===I.dayOfWeekStartPrev&&(l+="",v=!0),f.setDate(n+1)}l+="",C.html(l),F.find(".xdsoft_label span").eq(0).text(I.i18n[R].months[W.currentTime.getMonth()]),F.find(".xdsoft_label span").eq(1).text(W.currentTime.getFullYear()+I.yearOffset),M=k="";var x=0;if(!1!==I.minTime){var T=W.strtotime(I.minTime);x=60*T.getHours()+T.getMinutes()}var S=1440;if(!1!==I.maxTime){T=W.strtotime(I.maxTime);S=60*T.getHours()+T.getMinutes()}if(!1!==I.minDateTime){T=W.strToDateTime(I.minDateTime);if(E.formatDate(W.currentTime,I.formatDate)===E.formatDate(T,I.formatDate)){var M=60*T.getHours()+T.getMinutes();x'+E.formatDate(n,I.formatTime)+""},I.allowTimes&&L.isArray(I.allowTimes)&&I.allowTimes.length)for(c=0;c'+(c+I.yearOffset)+"";for(A.children().eq(0).html(H),c=parseInt(I.monthStart,10),H="";c<=parseInt(I.monthEnd,10);c+=1)H+='
      '+I.i18n[R].months[c]+"
      ";Y.children().eq(0).html(H),L(_).trigger("generate.xdsoft")},10),e.stopPropagation()}).on("afterOpen.xdsoft",function(){var e,t,a,n;I.timepicker&&(P.find(".xdsoft_current").length?e=".xdsoft_current":P.find(".xdsoft_init_time").length&&(e=".xdsoft_init_time"),e?(t=u[0].clientHeight,(a=P[0].offsetHeight)-t<(n=P.find(e).index()*I.timeHeightInTimePicker+1)&&(n=a-t),u.trigger("scroll_element.xdsoft_scroller",[parseInt(n,10)/(a-t)])):u.trigger("scroll_element.xdsoft_scroller",[0]))}),n=0,C.on("touchend click.xdsoft","td",function(e){e.stopPropagation(),n+=1;var t=L(this),a=W.currentTime;if(null==a&&(W.currentTime=W.now(),a=W.currentTime),t.hasClass("xdsoft_disabled"))return!1;a.setDate(1),a.setFullYear(t.data("year")),a.setMonth(t.data("month")),a.setDate(t.data("date")),_.trigger("select.xdsoft",[a]),O.val(W.str()),I.onSelectDate&&L.isFunction(I.onSelectDate)&&I.onSelectDate.call(_,W.currentTime,_.data("input"),e),_.data("changed",!0),_.trigger("xchange.xdsoft"),_.trigger("changedatetime.xdsoft"),(1f+c?(u="bottom",a=f+c-e.top):a-=c):a+_[0].offsetHeight>f+c&&(a=e.top-_[0].offsetHeight+1),a<0&&(a=0),n+t.offsetWidth>d&&(n=d-t.offsetWidth)),o=_[0],h(o,function(e){if("relative"===I.contentWindow.getComputedStyle(e).getPropertyValue("position")&&d>=e.offsetWidth)return n-=(d-e.offsetWidth)/2,!1}),l={position:r,left:I.insideParent?t.offsetLeft:n,top:"",bottom:""},I.insideParent?l[u]=t.offsetTop+t.offsetHeight:l[u]=a,_.css(l)},_.on("open.xdsoft",function(e){var t=!0;I.onShow&&L.isFunction(I.onShow)&&(t=I.onShow.call(_,W.currentTime,_.data("input"),e)),!1!==t&&(_.show(),r(),L(I.contentWindow).off("resize.xdsoft",r).on("resize.xdsoft",r),I.closeOnWithoutClick&&L([I.ownerDocument.body,I.contentWindow]).on("touchstart mousedown.xdsoft",function e(){_.trigger("close.xdsoft"),L([I.ownerDocument.body,I.contentWindow]).off("touchstart mousedown.xdsoft",e)}))}).on("close.xdsoft",function(e){var t=!0;F.find(".xdsoft_month,.xdsoft_year").find(".xdsoft_select").hide(),I.onClose&&L.isFunction(I.onClose)&&(t=I.onClose.call(_,W.currentTime,_.data("input"),e)),!1===t||I.opened||I.inline||_.hide(),e.stopPropagation()}).on("toggle.xdsoft",function(){_.is(":visible")?_.trigger("close.xdsoft"):_.trigger("open.xdsoft")}).data("input",O),d=0,_.data("xdsoft_datetime",W),_.setOptions(I),W.setCurrentTime(f()),O.data("xdsoft_datetimepicker",_).on("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",function(){O.is(":disabled")||O.data("xdsoft_datetimepicker").is(":visible")&&I.closeOnInputClick||I.openOnFocus&&(clearTimeout(d),d=setTimeout(function(){O.is(":disabled")||(s=!0,W.setCurrentTime(f(),!0),I.mask&&c(I),_.trigger("open.xdsoft"))},100))}).on("keydown.xdsoft",function(e){var t,a=e.which;return-1!==[D].indexOf(a)&&I.enterLikeTab?(t=L("input:visible,textarea:visible,button:visible,a:visible"),_.trigger("close.xdsoft"),t.eq(t.index(this)+1).focus(),!1):-1!==[T].indexOf(a)?(_.trigger("close.xdsoft"),!0):void 0}).on("blur.xdsoft",function(){_.trigger("close.xdsoft")})},r=function(e){var t=e.data("xdsoft_datetimepicker");t&&(t.data("xdsoft_datetime",null),t.remove(),e.data("xdsoft_datetimepicker",null).off(".xdsoft"),L(I.contentWindow).off("resize.xdsoft"),L([I.contentWindow,I.ownerDocument.body]).off("mousedown.xdsoft touchstart"),e.unmousewheel&&e.unmousewheel())},L(I.ownerDocument).off("keydown.xdsoftctrl keyup.xdsoftctrl").off("keydown.xdsoftcmd keyup.xdsoftcmd").on("keydown.xdsoftctrl",function(e){e.keyCode===p&&(N=!0)}).on("keyup.xdsoftctrl",function(e){e.keyCode===p&&(N=!1)}).on("keydown.xdsoftcmd",function(e){91===e.keyCode&&!0}).on("keyup.xdsoftcmd",function(e){91===e.keyCode&&!1}),this.each(function(){var t,e=L(this).data("xdsoft_datetimepicker");if(e){if("string"===L.type(H))switch(H){case"show":L(this).select().focus(),e.trigger("open.xdsoft");break;case"hide":e.trigger("close.xdsoft");break;case"toggle":e.trigger("toggle.xdsoft");break;case"destroy":r(L(this));break;case"reset":this.value=this.defaultValue,this.value&&e.data("xdsoft_datetime").isValidDate(E.parseDate(this.value,I.format))||e.data("changed",!1),e.data("xdsoft_datetime").setCurrentTime(this.value);break;case"validate":e.data("input").trigger("blur.xdsoft");break;default:e[H]&&L.isFunction(e[H])&&(o=e[H](a))}else e.setOptions(H);return 0}"string"!==L.type(H)&&(!I.lazyInit||I.open||I.inline?n(L(this)):(t=L(this)).on("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",function e(){t.is(":disabled")||t.data("xdsoft_datetimepicker")||(clearTimeout(i),i=setTimeout(function(){t.data("xdsoft_datetimepicker")||n(t),t.off("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",e).trigger("open.xdsoft")},100))}))}),o},L.fn.datetimepicker.defaults=s};!function(e){"function"==typeof define&&define.amd?define(["jquery","jquery-mousewheel"],e):"object"==typeof exports?module.exports=e(require("jquery")):e(jQuery)}(datetimepickerFactory),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof exports?module.exports=e:e(jQuery)}(function(c){var m,h,e=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],t="onwheel"in document||9<=document.documentMode?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],g=Array.prototype.slice;if(c.event.fixHooks)for(var a=e.length;a;)c.event.fixHooks[e[--a]]=c.event.mouseHooks;var p=c.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var e=t.length;e;)this.addEventListener(t[--e],n,!1);else this.onmousewheel=n;c.data(this,"mousewheel-line-height",p.getLineHeight(this)),c.data(this,"mousewheel-page-height",p.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var e=t.length;e;)this.removeEventListener(t[--e],n,!1);else this.onmousewheel=null;c.removeData(this,"mousewheel-line-height"),c.removeData(this,"mousewheel-page-height")},getLineHeight:function(e){var t=c(e),a=t["offsetParent"in c.fn?"offsetParent":"parent"]();return a.length||(a=c("body")),parseInt(a.css("fontSize"),10)||parseInt(t.css("fontSize"),10)||16},getPageHeight:function(e){return c(e).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};function n(e){var t,a=e||window.event,n=g.call(arguments,1),r=0,o=0,i=0,s=0,d=0;if((e=c.event.fix(a)).type="mousewheel","detail"in a&&(i=-1*a.detail),"wheelDelta"in a&&(i=a.wheelDelta),"wheelDeltaY"in a&&(i=a.wheelDeltaY),"wheelDeltaX"in a&&(o=-1*a.wheelDeltaX),"axis"in a&&a.axis===a.HORIZONTAL_AXIS&&(o=-1*i,i=0),r=0===i?o:i,"deltaY"in a&&(r=i=-1*a.deltaY),"deltaX"in a&&(o=a.deltaX,0===i&&(r=-1*o)),0!==i||0!==o){if(1===a.deltaMode){var u=c.data(this,"mousewheel-line-height");r*=u,i*=u,o*=u}else if(2===a.deltaMode){var l=c.data(this,"mousewheel-page-height");r*=l,i*=l,o*=l}if(t=Math.max(Math.abs(i),Math.abs(o)),(!h||tdiv>div{background:#f5f5f5;border-top:1px solid #ddd;color:#666;font-size:12px;text-align:center;border-collapse:collapse;cursor:pointer;border-bottom-width:0;height:25px;line-height:25px}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div:first-child{border-top-width:0}.xdsoft_datetimepicker .xdsoft_today_button:hover,.xdsoft_datetimepicker .xdsoft_next:hover,.xdsoft_datetimepicker .xdsoft_prev:hover{opacity:1;-ms-filter:"alpha(opacity=100)"}.xdsoft_datetimepicker .xdsoft_label{display:inline;position:relative;z-index:9999;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:bold;background-color:#fff;float:left;width:182px;text-align:center;cursor:pointer}.xdsoft_datetimepicker .xdsoft_label:hover>span{text-decoration:underline}.xdsoft_datetimepicker .xdsoft_label:hover i{opacity:1.0}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select{border:1px solid #ccc;position:absolute;right:0;top:30px;z-index:101;display:none;background:#fff;max-height:160px;overflow-y:hidden}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select.xdsoft_monthselect{right:-7px}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select.xdsoft_yearselect{right:2px}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option:hover{color:#fff;background:#ff8000}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option{padding:2px 10px 2px 5px;text-decoration:none !important}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option.xdsoft_current{background:#3af;box-shadow:#178fe5 0 1px 3px 0 inset;color:#fff;font-weight:700}.xdsoft_datetimepicker .xdsoft_month{width:100px;text-align:right}.xdsoft_datetimepicker .xdsoft_calendar{clear:both}.xdsoft_datetimepicker .xdsoft_year{width:48px;margin-left:5px}.xdsoft_datetimepicker .xdsoft_calendar table{border-collapse:collapse;width:100%}.xdsoft_datetimepicker .xdsoft_calendar td>div{padding-right:5px}.xdsoft_datetimepicker .xdsoft_calendar th{height:25px}.xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th{width:14.2857142%;background:#f5f5f5;border:1px solid #ddd;color:#666;font-size:12px;text-align:right;vertical-align:middle;padding:0;border-collapse:collapse;cursor:pointer;height:25px}.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th{width:12.5%}.xdsoft_datetimepicker .xdsoft_calendar th{background:#f1f1f1}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today{color:#3af}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_default{background:#ffe9d2;box-shadow:#ffb871 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_mint{background:#c1ffc9;box-shadow:#00dd1c 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current{background:#3af;box-shadow:#178fe5 0 1px 3px 0 inset;color:#fff;font-weight:700}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month,.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled,.xdsoft_datetimepicker .xdsoft_time_box>div>div.xdsoft_disabled{opacity:.5;-ms-filter:"alpha(opacity=50)";cursor:default}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled{opacity:.2;-ms-filter:"alpha(opacity=20)"}.xdsoft_datetimepicker .xdsoft_calendar td:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div:hover{color:#fff !important;background:#ff8000 !important;box-shadow:none !important}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current.xdsoft_disabled:hover{background:#3af !important;box-shadow:#178fe5 0 1px 3px 0 inset !important;color:#fff !important}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_disabled:hover{color:inherit !important;background:inherit !important;box-shadow:inherit !important}.xdsoft_datetimepicker .xdsoft_calendar th{font-weight:700;text-align:center;color:#999;cursor:default}.xdsoft_datetimepicker .xdsoft_copyright{color:#ccc !important;font-size:10px;clear:both;float:none;margin-left:8px}.xdsoft_datetimepicker .xdsoft_copyright a{color:#eee !important}.xdsoft_datetimepicker .xdsoft_copyright a:hover{color:#aaa !important}.xdsoft_time_box{position:relative;border:1px solid #ccc}.xdsoft_scrollbar>.xdsoft_scroller{background:#ccc !important;height:20px;border-radius:3px}.xdsoft_scrollbar{position:absolute;width:7px;right:0;top:0;bottom:0;cursor:pointer}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_scrollbar{left:0;right:auto}.xdsoft_scroller_box{position:relative}.xdsoft_datetimepicker.xdsoft_dark{box-shadow:0 5px 15px -5px rgba(255,255,255,0.506);background:#000;border-bottom:1px solid #444;border-left:1px solid #333;border-right:1px solid #333;border-top:1px solid #333;color:#ccc}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box{border-bottom:1px solid #222}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div{background:#0a0a0a;border-top:1px solid #222;color:#999}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label{background-color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select{border:1px solid #333;background:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select>div>.xdsoft_option:hover{color:#000;background:#007fff}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select>div>.xdsoft_option.xdsoft_current{background:#c50;box-shadow:#b03e00 0 1px 3px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_next,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==)}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{background:#0a0a0a;border:1px solid #222;color:#999}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{background:#0e0e0e}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today{color:#c50}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_default{background:#ffe9d2;box-shadow:#ffb871 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_mint{background:#c1ffc9;box-shadow:#00dd1c 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current{background:#c50;box-shadow:#b03e00 0 1px 3px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div:hover{color:#000 !important;background:#007fff !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{color:#666}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright{color:#333 !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a{color:#111 !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover{color:#555 !important}.xdsoft_dark .xdsoft_time_box{border:1px solid #333}.xdsoft_dark .xdsoft_scrollbar>.xdsoft_scroller{background:#333 !important}.xdsoft_datetimepicker .xdsoft_save_selected{display:block;border:1px solid #ddd !important;margin-top:5px;width:100%;color:#454551;font-size:13px}.xdsoft_datetimepicker .blue-gradient-button{font-family:"museo-sans","Book Antiqua",sans-serif;font-size:12px;font-weight:300;color:#82878c;height:28px;position:relative;padding:4px 17px 4px 33px;border:1px solid #d7d8da;background:-moz-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(73%,#f4f8fa));background:-webkit-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-o-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-ms-linear-gradient(top,#fff 0,#f4f8fa 73%);background:linear-gradient(to bottom,#fff 0,#f4f8fa 73%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff',endColorstr='#f4f8fa',GradientType=0)}.xdsoft_datetimepicker .blue-gradient-button:hover,.xdsoft_datetimepicker .blue-gradient-button:focus,.xdsoft_datetimepicker .blue-gradient-button:hover span,.xdsoft_datetimepicker .blue-gradient-button:focus span{color:#454551;background:-moz-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#f4f8fa),color-stop(73%,#FFF));background:-webkit-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-o-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-ms-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:linear-gradient(to bottom,#f4f8fa 0,#FFF 73%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f8fa',endColorstr='#FFF',GradientType=0)} diff --git a/public/viewnfo.php b/public/viewnfo.php index c63fcd91..6d5b7ed6 100644 --- a/public/viewnfo.php +++ b/public/viewnfo.php @@ -27,12 +27,14 @@ if ($view == "latin-1" || $view == "fonthack") { // Do not convert from ibm-437, read bytes as is. // NOTICE: TBSource specifies Latin-1 encoding in include/bittorrent.php: // stdhead() -$nfo = htmlspecialchars(($a["nfo"])); +//$nfo = htmlspecialchars(($a["nfo"])); +$nfo = code_new($a["nfo"], $view); } else { // Convert from ibm-437 to html unicode entities. // take special care of Swedish letters if in magic view. -$nfo = code($a["nfo"], $view == "magic"); +//$nfo = code($a["nfo"], $view == "magic"); +$nfo = code_new($a["nfo"], $view); } stdhead($lang_viewnfo['head_view_nfo']); @@ -65,14 +67,14 @@ if ($view == "fonthack") { // Please notice: MS LineDraw's glyphs are included in the Courier New font // as of Courier New version 2.0, but uses the correct mappings instead. // http://support.microsoft.com/kb/q179422/ -print("
      ");
      +print("
      ");
       }
       else {
       // IE6.0 need to know which font to use, Mozilla can figure it out in its own
       // (windows firefox at least)
       // Anything else than 'Courier New' looks pretty broken.
       // 'Lucida Console', 'FixedSys'
      -print("
      ");
      +print("
      ");
       }
       // Writes the (eventually modified) nfo data to output, first formating urls.
       print(format_urls($nfo));
      diff --git a/public/viewsnatches.php b/public/viewsnatches.php
      index 0d7c062d..1dd22577 100644
      --- a/public/viewsnatches.php
      +++ b/public/viewsnatches.php
      @@ -13,7 +13,7 @@ begin_main_frame();
       $torrent_name = get_single_value("torrents", "name", "WHERE id = ".sqlesc($id));
       print("

      ".$lang_viewsnatches['text_snatch_detail_for'] . "".htmlspecialchars($torrent_name)."

      "); $count = get_row_count("snatched", "WHERE finished = 'yes' AND torrentid = ".sqlesc($id)); - +$seedBoxRep = new \App\Repositories\SeedBoxRepository(); if ($count){ $perpage = 25; list($pagertop, $pagerbottom, $limit) = pager($perpage, $count, $_SERVER["SCRIPT_NAME"] . "?id=" . htmlspecialchars($id) . "&" ); @@ -53,7 +53,7 @@ if ($count){ } else $username = get_username($arr['userid']); $reportImage = "\"Report\""; - print("" . $username ."".(user_can('userprofile') ? "".$arr['ip']."" : "")."".$uploaded."@".$uprate.$lang_viewsnatches['text_per_second']."
      ".$downloaded."@".$downrate.$lang_viewsnatches['text_per_second']."$ratio$seedtime$leechtime".gettime($arr['completedat'],true,false)."".gettime($arr['last_action'],true,false)."".($userrow['privacy'] != 'strong' || user_can('viewanonymous') ? "$reportImage" : $reportImage)."\n"); + print("" . $username ."".(user_can('userprofile') || $arr['userid'] == $CURUSER['id'] ? "".$arr['ip'].$seedBoxRep->renderIcon($arr['ip'], $arr['userid'])."" : "")."".$uploaded."@".$uprate.$lang_viewsnatches['text_per_second']."
      ".$downloaded."@".$downrate.$lang_viewsnatches['text_per_second']."$ratio$seedtime$leechtime".gettime($arr['completedat'],true,false)."".gettime($arr['last_action'],true,false)."".($userrow['privacy'] != 'strong' || user_can('viewanonymous') ? "$reportImage" : $reportImage)."\n"); } print("\n"); print($pagerbottom); diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index 82480c32..ce81cbeb 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -89,6 +89,9 @@ return [ 'bulk_action_attach_tag' => 'Attach tag', 'action_approval' => 'Approval', 'bulk_action_attach_tag_remove_old' => 'Also delete old tags', + 'bulk_action_recommend' => 'Recommend', + 'bulk_action_sp_state' => 'Promotion', + 'bulk_action_hr' => 'H&R', ], 'seed_box_record' => [ 'toggle_status' => 'Change status', diff --git a/resources/lang/en/bonus.php b/resources/lang/en/bonus.php index 323dbe07..3b772e0d 100644 --- a/resources/lang/en/bonus.php +++ b/resources/lang/en/bonus.php @@ -3,4 +3,19 @@ return [ 'comment_buy_medal' => 'Spend :bonus bonus buy :medal_name', 'comment_buy_attendance_card' => 'Spend :bonus bonus buy one attend card', + 'table_thead' => [ + 'reward_type' => 'Reward type', + 'count' => 'Count', + 'size' => 'Size', + 'a_value' => 'A Value', + 'bonus_base' => 'Basic bonus', + 'factor' => 'Factor', + 'got_bonus' => 'Got bonus', + 'total' => 'Total', + ], + 'reward_types' => [ + 'basic' => 'Basic reward', + 'harem_addition' => 'Harem addition', + 'official_addition' => 'Official addition', + ], ]; diff --git a/resources/lang/en/claim.php b/resources/lang/en/claim.php index a0f3d0e9..b2caf10b 100644 --- a/resources/lang/en/claim.php +++ b/resources/lang/en/claim.php @@ -31,4 +31,7 @@ return [ 'remove_claim_confirm' => 'Confirm to give up the claim?', 'already_claimed' => 'Claimed', 'not_claim_yet' => 'Unclaimed', + 'fields' => [ + 'torrent_id' => 'Torrent ID', + ], ]; diff --git a/resources/lang/en/cleanup.php b/resources/lang/en/cleanup.php index cefcc9fb..f747be84 100644 --- a/resources/lang/en/cleanup.php +++ b/resources/lang/en/cleanup.php @@ -2,9 +2,10 @@ return [ 'ban_user_with_leech_warning_expired' => 'Banned by system because of leech warning expired.', - 'delete_user_unconfirmed' => 'Disable by system because of unconfirmed excess deadline.', - 'delete_user_no_transfer_alt_last_access_time' => 'Disable inactive user accounts, no transfer. Alt: last access time.', - 'delete_user_no_transfer_alt_register_time' => 'Disable inactive user accounts, no transfer. Alt: register time.', - 'delete_user_not_parked' => 'Disable inactive user accounts, not parked.', - 'delete_user_parked' => 'Disable inactive user accounts, parked.', + 'disable_user_unconfirmed' => 'Disable by system because of unconfirmed excess deadline.', + 'disable_user_no_transfer_alt_last_access_time' => 'Disable inactive user accounts, no transfer. Alt: last access time.', + 'disable_user_no_transfer_alt_register_time' => 'Disable inactive user accounts, no transfer. Alt: register time.', + 'disable_user_not_parked' => 'Disable inactive user accounts, not parked.', + 'disable_user_parked' => 'Disable inactive user accounts, parked.', + 'destroy_disabled_account' => 'Timed physical deletion of disabled accounts', ]; diff --git a/resources/lang/en/exam.php b/resources/lang/en/exam.php index c6c48728..b5163037 100644 --- a/resources/lang/en/exam.php +++ b/resources/lang/en/exam.php @@ -1,6 +1,7 @@ 'Exam', 'name' => 'Exam name', 'index' => 'Exam index', 'time_range' => 'Exam time', diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php index 9b43e750..3273d595 100644 --- a/resources/lang/en/label.php +++ b/resources/lang/en/label.php @@ -33,6 +33,7 @@ return [ 'submit' => 'Submit', 'cancel' => 'Cancel', 'reset' => 'Reset', + 'anonymous' => 'Anonymous', 'setting' => [ 'nav_text' => 'Setting', 'backup' => [ @@ -72,9 +73,11 @@ return [ 'no_promotion' => 'No promotion', 'no_promotion_help' => 'No Promotion, uploaded/downloaded are calculated according to the actual value', 'max_uploaded' => 'Maximum upload volume multiplier', - 'max_uploaded_help' => 'The total upload volume is at most a multiple of its volume', + 'max_uploaded_help' => 'The total upload volume is at most a multiple of its volume. Set to 0 No limitation', 'not_seed_box_max_speed' => 'Not SeedBox max upload speed', 'not_seed_box_max_speed_help' => 'Unit: Mbps, if this value is exceeded and the SeedBox record cannot be matched, download permission is disabled', + 'max_uploaded_duration' => 'Maximum upload volume multiplier effective time range', + 'max_uploaded_duration_help' => 'Unit: hours. The maximum upload volume multiplier takes effect within this time range after the torrent is published, and does not take effect beyond this range. A setting of 0 is always in effect', ], 'system' => [ 'tab_header' => 'System', @@ -148,6 +151,9 @@ return [ 'pos_state' => 'Pos state', 'sp_state' => 'Promotion', 'visible' => 'Visible', + 'picktype' => 'Recommend', + 'promotion_time_type' => 'Promotion type time', + 'hr' => 'H&R', ], 'hit_and_run' => [ 'label' => 'User H&R', diff --git a/resources/lang/en/medal.php b/resources/lang/en/medal.php index 9c953e4d..1800bd16 100644 --- a/resources/lang/en/medal.php +++ b/resources/lang/en/medal.php @@ -1,6 +1,7 @@ 'Medal', 'action_wearing' => 'Wear', 'admin' => [ 'list' => [ diff --git a/resources/lang/en/message.php b/resources/lang/en/message.php index c9fb81d4..3d27859b 100644 --- a/resources/lang/en/message.php +++ b/resources/lang/en/message.php @@ -12,9 +12,13 @@ return [ 'field_value_change_message_subject' => ':field changed', 'download_disable' => [ - 'subject' => 'Download permission cancellation', + 'subject' => 'Download permission canceled', 'body' => 'Your download privileges has revoked, possibly due to low sharing rates or misbehavior. By: :operator', ], + 'download_disable_upload_over_speed' => [ + 'subject' => 'Download permission canceled', + 'body' => 'Your download permission has been cancelled due to excessive upload speed, please file if you are a seed box user.' , + ], 'download_enable' => [ 'subject' => 'Download permission restored', 'body' => 'Your download privileges restored, you can now download torrents. By: :operator', diff --git a/resources/lang/en/nexus.php b/resources/lang/en/nexus.php index d924ff86..bd9196f0 100644 --- a/resources/lang/en/nexus.php +++ b/resources/lang/en/nexus.php @@ -2,4 +2,7 @@ return [ 'invalid_argument' => 'Invalid argument', + 'require_argument' => ':argument cannot be empty', + 'select_one_please' => 'Please select one', + 'user_not_exists' => '(orphaned)', ]; diff --git a/resources/lang/en/permission.php b/resources/lang/en/permission.php index 47ae27aa..fa0c5a1c 100644 --- a/resources/lang/en/permission.php +++ b/resources/lang/en/permission.php @@ -5,6 +5,14 @@ return [ 'text' => 'Approval Torrent', 'desc' => 'Allow, Deny, or mark as unreviewed', ], + 'torrent-set-special-tag' => [ + 'text' => 'Set torrent special tags', + 'desc' => 'Set the Official/Zero bonus tag to torrents', + ], + 'torrent-approval-allow-automatic' => [ + 'text' => 'Torrent approval allow automatically', + 'desc' => 'Torrent is the approval allow status after upload automatically', + ], 'defaultclass' => [ 'text' => 'Default Class', 'desc' => ' Class upon registration', diff --git a/resources/lang/en/search.php b/resources/lang/en/search.php new file mode 100644 index 00000000..7eb78151 --- /dev/null +++ b/resources/lang/en/search.php @@ -0,0 +1,13 @@ + 'Global Search', + 'search_keyword' => 'Keyword', + 'search_area' => 'Range', + 'search_area_options' => [ + '0' => 'Title', + '1' => 'Description', + '3' => 'Uploader', + '4' => 'IMDB URL' + ], +]; diff --git a/resources/lang/en/searchbox.php b/resources/lang/en/searchbox.php index ceb08948..c4396836 100644 --- a/resources/lang/en/searchbox.php +++ b/resources/lang/en/searchbox.php @@ -13,4 +13,8 @@ return [ \App\Models\SearchBox::EXTRA_DISPLAY_COVER_ON_TORRENT_LIST => 'Display cover on torrent list', \App\Models\SearchBox::EXTRA_DISPLAY_SEED_BOX_ICON_ON_TORRENT_LIST => 'Display seed box icon on torrent list', ], + 'sections' => [ + 'browse' => 'Torrents', + 'special' => 'Special', + ], ]; diff --git a/resources/lang/en/torrent.php b/resources/lang/en/torrent.php index 3c52d255..f30409bd 100644 --- a/resources/lang/en/torrent.php +++ b/resources/lang/en/torrent.php @@ -75,4 +75,10 @@ return [ 'deny_comment_show' => 'Denied, reason: :reason', 'logs_label' => 'Approval logs' ], + 'show_hide_media_info' => 'Show/Hide raw MediaInfo', + 'promotion_time_types' => [ + \App\Models\Torrent::PROMOTION_TIME_TYPE_GLOBAL => 'Global', + \App\Models\Torrent::PROMOTION_TIME_TYPE_PERMANENT => 'Permanent', + \App\Models\Torrent::PROMOTION_TIME_TYPE_DEADLINE => 'Until', + ], ]; diff --git a/resources/lang/en/user.php b/resources/lang/en/user.php index 33e2e17d..8524f42e 100644 --- a/resources/lang/en/user.php +++ b/resources/lang/en/user.php @@ -27,4 +27,6 @@ return [ \App\Models\User::CLASS_STAFF_LEADER => 'Staff Leader', ], 'change_username_lte_min_interval' => 'Last change time: :last_change_time, unmet minimum interval: :interval days', + 'destroy_by_admin' => 'Physical delete by administrator', + 'disable_by_admin' => 'Disable by administrator', ]; diff --git a/resources/lang/zh_CN/admin.php b/resources/lang/zh_CN/admin.php index e9d6a068..2e7c28d7 100644 --- a/resources/lang/zh_CN/admin.php +++ b/resources/lang/zh_CN/admin.php @@ -26,6 +26,7 @@ return [ 'permissions' => '权限', 'section' => '分区', 'icon' => '分类图标', + 'plugin' => '插件', ], 'resources' => [ 'agent_allow' => [ @@ -91,6 +92,9 @@ return [ 'bulk_action_attach_tag' => '设置标签', 'action_approval' => '审核', 'bulk_action_attach_tag_remove_old' => '同时删除旧标签', + 'bulk_action_recommend' => '推荐', + 'bulk_action_sp_state' => '优惠', + 'bulk_action_hr' => 'H&R', ], 'seed_box_record' => [ 'toggle_status' => '更改状态', diff --git a/resources/lang/zh_CN/bonus.php b/resources/lang/zh_CN/bonus.php index 26000772..aa7ead52 100644 --- a/resources/lang/zh_CN/bonus.php +++ b/resources/lang/zh_CN/bonus.php @@ -3,4 +3,19 @@ return [ 'comment_buy_medal' => '花费 :bonus 魔力购买了 :medal_name', 'comment_buy_attendance_card' => '花费 :bonus 魔力购买了 1 张补签卡', + 'table_thead' => [ + 'reward_type' => '奖励类型', + 'count' => '数量', + 'size' => '体积', + 'a_value' => 'A 值', + 'bonus_base' => '基础魔力', + 'factor' => '系数', + 'got_bonus' => '获得魔力', + 'total' => '合计', + ], + 'reward_types' => [ + 'basic' => '基本奖励', + 'harem_addition' => '后宫加成', + 'official_addition' => '官种加成', + ], ]; diff --git a/resources/lang/zh_CN/claim.php b/resources/lang/zh_CN/claim.php index 1f510b5a..075539a7 100644 --- a/resources/lang/zh_CN/claim.php +++ b/resources/lang/zh_CN/claim.php @@ -31,4 +31,8 @@ return [ 'remove_claim_confirm' => '确认要放弃认领?', 'already_claimed' => '已认领', 'not_claim_yet' => '未认领', + + 'fields' => [ + 'torrent_id' => '种子 ID', + ], ]; diff --git a/resources/lang/zh_CN/cleanup.php b/resources/lang/zh_CN/cleanup.php index 9528b3b8..e74ced00 100644 --- a/resources/lang/zh_CN/cleanup.php +++ b/resources/lang/zh_CN/cleanup.php @@ -2,9 +2,10 @@ return [ 'ban_user_with_leech_warning_expired' => '上传警告到期,被系统禁用.', - 'delete_user_unconfirmed' => '超时未确认,被系统封禁.', - 'delete_user_no_transfer_alt_last_access_time' => '封禁非活跃的无流量账号,由最近访问时间断定.', - 'delete_user_no_transfer_alt_register_time' => '封禁非活跃的无流量账号,由注册时间时间断定.', - 'delete_user_not_parked' => '定时封禁未挂起的非活跃账号.', - 'delete_user_parked' => '定时封禁已挂起的非活跃账号.', + 'disable_user_unconfirmed' => '超时未确认,被系统封禁.', + 'disable_user_no_transfer_alt_last_access_time' => '封禁非活跃的无流量账号,由最近访问时间断定.', + 'disable_user_no_transfer_alt_register_time' => '封禁非活跃的无流量账号,由注册时间时间断定.', + 'disable_user_not_parked' => '定时封禁未挂起的非活跃账号.', + 'disable_user_parked' => '定时封禁已挂起的非活跃账号.', + 'destroy_disabled_account' => '定时物理删除已封禁账号', ]; diff --git a/resources/lang/zh_CN/exam.php b/resources/lang/zh_CN/exam.php index bff1252e..fd488e0d 100644 --- a/resources/lang/zh_CN/exam.php +++ b/resources/lang/zh_CN/exam.php @@ -1,6 +1,7 @@ '考核', 'name' => '考核名称', 'index' => '考核指标', 'time_range' => '考核时间', diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php index 6d9b96f6..2f59a2b8 100644 --- a/resources/lang/zh_CN/label.php +++ b/resources/lang/zh_CN/label.php @@ -33,6 +33,7 @@ return [ 'submit' => '提交', 'cancel' => '取消', 'reset' => '重置', + 'anonymous' => '匿名', 'setting' => [ 'nav_text' => '设置', 'backup' => [ @@ -72,9 +73,11 @@ return [ 'no_promotion' => '无优惠', 'no_promotion_help' => '不享受任何优惠,上传量/下载量按实际值计算', 'max_uploaded' => '最大上传量倍数', - 'max_uploaded_help' => '总上传量最多为其体积的多少倍', + 'max_uploaded_help' => '总上传量最多为其体积的多少倍。设置为 0 无此限制', 'not_seed_box_max_speed' => '非 SeedBox 最高限速', 'not_seed_box_max_speed_help' => '单位:Mbps。若超过此值又不能匹配 SeedBox 记录,禁用下载权限', + 'max_uploaded_duration' => '最大上传量倍数有效时间范围', + 'max_uploaded_duration_help' => '单位:小时。种子发布后的这个时间范围内,最大上传量倍数生效,超过此范围不生效。设置为 0 一直生效', ], 'system' => [ 'tab_header' => '系统', @@ -154,6 +157,9 @@ return [ 'team' => '制作组', 'processing' => '处理', 'standard' => '分辨率', + 'picktype' => '推荐', + 'promotion_time_type' => '优惠时间类型', + 'hr' => 'H&R', ], 'hit_and_run' => [ 'label' => '用户 H&R', diff --git a/resources/lang/zh_CN/medal.php b/resources/lang/zh_CN/medal.php index 7993d4e5..b3099d15 100644 --- a/resources/lang/zh_CN/medal.php +++ b/resources/lang/zh_CN/medal.php @@ -1,6 +1,7 @@ '勋章', 'action_wearing' => '佩戴', 'admin' => [ 'list' => [ diff --git a/resources/lang/zh_CN/message.php b/resources/lang/zh_CN/message.php index 30ff49e6..f030b00e 100644 --- a/resources/lang/zh_CN/message.php +++ b/resources/lang/zh_CN/message.php @@ -15,6 +15,10 @@ return [ 'subject' => '下载权限取消', 'body' => '你的下载权限被取消,可能的原因是过低的分享率或行为不当。By: :operator', ], + 'download_disable_upload_over_speed' => [ + 'subject' => '下载权限取消', + 'body' => '你因上传速度过快下载权限被取消,若是盒子用户请备案。', + ], 'download_enable' => [ 'subject' => '下载权限恢复', 'body' => '你的下载权限恢复,你现在可以下载种子。By: :operator', diff --git a/resources/lang/zh_CN/nexus.php b/resources/lang/zh_CN/nexus.php index e142c245..9d206b96 100644 --- a/resources/lang/zh_CN/nexus.php +++ b/resources/lang/zh_CN/nexus.php @@ -2,5 +2,7 @@ return [ 'invalid_argument' => '参数错误', - 'select_one_please' => '请选择', + 'require_argument' => ':argument 不能为空', + 'select_one_please' => '请选择一项', + 'user_not_exists' => '(无此帐户)', ]; diff --git a/resources/lang/zh_CN/permission.php b/resources/lang/zh_CN/permission.php index 1e50fba3..ba4a205c 100644 --- a/resources/lang/zh_CN/permission.php +++ b/resources/lang/zh_CN/permission.php @@ -5,6 +5,14 @@ return [ 'text' => '审核种子', 'desc' => '通过种子、拒绝种子、或将种子标记为未审', ], + 'torrent-set-special-tag' => [ + 'text' => '设定种子特殊标签', + 'desc' => '设定种子的官方、零魔标签', + ], + 'torrent-approval-allow-automatic' => [ + 'text' => '种子自动通过审核', + 'desc' => '种子发布即为审核通过状态', + ], 'defaultclass' => [ 'text' => '默认等级', 'desc' => '注册时获得的等级', @@ -205,4 +213,12 @@ return [ 'text' => '允许个性条', 'desc' => '允许用户使用个性条', ], +// 'not-counting-downloaded' => [ +// 'text' => '不计下载量', +// 'desc' => '用户下载量不增加', +// ], +// 'not-counting-hit-and-run' => [ +// 'text' => '不计 H&R', +// 'desc' => '下载带 H&R 标记的种子不记 H&R。注意:等级为 VIP 是固定不计的', +// ], ]; diff --git a/resources/lang/zh_CN/plugin.php b/resources/lang/zh_CN/plugin.php new file mode 100644 index 00000000..23da8598 --- /dev/null +++ b/resources/lang/zh_CN/plugin.php @@ -0,0 +1,32 @@ + [ + 'install' => '安装', + 'delete' => '删除', + 'update' => '升级', + ], + 'labels' => [ + 'display_name' => '名称', + 'package_name' => '包名', + 'remote_url' => '仓库地址', + 'installed_version' => '已安装版本', + 'status' => '状态', + ], + 'status' => [ + \App\Models\Plugin::STATUS_NORMAL => '正常', + \App\Models\Plugin::STATUS_NOT_INSTALLED => '未安装', + + \App\Models\Plugin::STATUS_PRE_INSTALL => '准备安装', + \App\Models\Plugin::STATUS_INSTALLING => '安装中', + \App\Models\Plugin::STATUS_INSTALL_FAILED => '安装失败', + + \App\Models\Plugin::STATUS_PRE_UPDATE => '准备升级', + \App\Models\Plugin::STATUS_UPDATING => '升级中', + \App\Models\Plugin::STATUS_UPDATE_FAILED => '升级失败', + + \App\Models\Plugin::STATUS_PRE_DELETE => '准备删除', + \App\Models\Plugin::STATUS_DELETING => '删除中', + \App\Models\Plugin::STATUS_DELETE_FAILED => '删除失败', + ], +]; diff --git a/resources/lang/zh_CN/search.php b/resources/lang/zh_CN/search.php new file mode 100644 index 00000000..edac195d --- /dev/null +++ b/resources/lang/zh_CN/search.php @@ -0,0 +1,13 @@ + '全站搜索', + 'search_keyword' => '关键字', + 'search_area' => '范围', + 'search_area_options' => [ + '0' => '标题', + '1' => '简介', + '3' => '发布者', + '4' => 'IMDB链接' + ], +]; diff --git a/resources/lang/zh_CN/searchbox.php b/resources/lang/zh_CN/searchbox.php index 69344bbe..501d36ca 100644 --- a/resources/lang/zh_CN/searchbox.php +++ b/resources/lang/zh_CN/searchbox.php @@ -13,4 +13,8 @@ return [ \App\Models\SearchBox::EXTRA_DISPLAY_COVER_ON_TORRENT_LIST => '种子列表页展示封面', \App\Models\SearchBox::EXTRA_DISPLAY_SEED_BOX_ICON_ON_TORRENT_LIST => '种子列表页展示 SeedBox 图标', ], + 'sections' => [ + 'browse' => '种子区', + 'special' => '特别区', + ], ]; diff --git a/resources/lang/zh_CN/torrent.php b/resources/lang/zh_CN/torrent.php index 96ff4895..7740eb33 100644 --- a/resources/lang/zh_CN/torrent.php +++ b/resources/lang/zh_CN/torrent.php @@ -75,4 +75,10 @@ return [ 'deny_comment_show' => '审核不通过,原因::reason', 'logs_label' => '审核记录', ], + 'show_hide_media_info' => '显示/隐藏原始 MediaInfo', + 'promotion_time_types' => [ + \App\Models\Torrent::PROMOTION_TIME_TYPE_GLOBAL => '全局', + \App\Models\Torrent::PROMOTION_TIME_TYPE_PERMANENT => '永久', + \App\Models\Torrent::PROMOTION_TIME_TYPE_DEADLINE => '直到', + ], ]; diff --git a/resources/lang/zh_CN/user.php b/resources/lang/zh_CN/user.php index 68c51678..ad427709 100644 --- a/resources/lang/zh_CN/user.php +++ b/resources/lang/zh_CN/user.php @@ -27,4 +27,6 @@ return [ \App\Models\User::CLASS_STAFF_LEADER => '主管', ], 'change_username_lte_min_interval' => '上次修改时间::last_change_time,未满足最小间隔::interval 天', + 'destroy_by_admin' => '被管理员物理删除', + 'disable_by_admin' => '被管理員封禁', ]; diff --git a/resources/lang/zh_TW/admin.php b/resources/lang/zh_TW/admin.php index c46b91fd..07664aed 100644 --- a/resources/lang/zh_TW/admin.php +++ b/resources/lang/zh_TW/admin.php @@ -89,6 +89,9 @@ return [ 'bulk_action_attach_tag' => '設置標簽', 'action_approval' => '審核', 'bulk_action_attach_tag_remove_old' => '同時刪除舊標簽', + 'bulk_action_recommend' => '推薦', + 'bulk_action_sp_state' => '優惠', + 'bulk_action_hr' => 'H&R', ], 'seed_box_record' => [ 'toggle_status' => '更改狀態', diff --git a/resources/lang/zh_TW/bonus.php b/resources/lang/zh_TW/bonus.php index 0e57807c..a5cae0da 100644 --- a/resources/lang/zh_TW/bonus.php +++ b/resources/lang/zh_TW/bonus.php @@ -3,4 +3,19 @@ return [ 'comment_buy_medal' => '花費 :bonus 魔力購買了 :medal_name', 'comment_buy_attendance_card' => '花費 :bonus 魔力購買了 1 張補簽卡', + 'table_thead' => [ + 'reward_type' => '獎勵類型', + 'count' => '數量', + 'size' => '體積', + 'a_value' => 'A 值', + 'bonus_base' => '基礎魔力', + 'factor' => '系數', + 'got_bonus' => '獲得魔力', + 'total' => '合計', + ], + 'reward_types' => [ + 'basic' => '基本獎勵', + 'harem_addition' => '後宮加成', + 'official_addition' => '官種加成', + ], ]; diff --git a/resources/lang/zh_TW/claim.php b/resources/lang/zh_TW/claim.php index 473e4650..9fe68ede 100644 --- a/resources/lang/zh_TW/claim.php +++ b/resources/lang/zh_TW/claim.php @@ -30,4 +30,7 @@ return [ 'remove_claim_confirm' => '確認要放棄認領?', 'already_claimed' => '已認領', 'not_claim_yet' => '未認領', + 'fields' => [ + 'torrent_id' => '種子 ID', + ], ]; diff --git a/resources/lang/zh_TW/cleanup.php b/resources/lang/zh_TW/cleanup.php index f996b106..86de0547 100644 --- a/resources/lang/zh_TW/cleanup.php +++ b/resources/lang/zh_TW/cleanup.php @@ -2,9 +2,10 @@ return [ 'ban_user_with_leech_warning_expired' => '上傳警告到期,被系統禁用.', - 'delete_user_unconfirmed' => '超時未確認,被系統封禁.', - 'delete_user_no_transfer_alt_last_access_time' => '封禁非活躍的無流量賬號,由最近訪問時間斷定.', - 'delete_user_no_transfer_alt_register_time' => '封禁非活躍的無流量賬號,由註冊時間時間斷定.', - 'delete_user_not_parked' => '定時封禁未掛起的非活躍賬號.', - 'delete_user_parked' => '定時封禁已掛起的非活躍賬號.', + 'disable_user_unconfirmed' => '超時未確認,被系統封禁.', + 'disable_user_no_transfer_alt_last_access_time' => '封禁非活躍的無流量賬號,由最近訪問時間斷定.', + 'disable_user_no_transfer_alt_register_time' => '封禁非活躍的無流量賬號,由註冊時間時間斷定.', + 'disable_user_not_parked' => '定時封禁未掛起的非活躍賬號.', + 'disable_user_parked' => '定時封禁已掛起的非活躍賬號.', + 'destroy_disabled_account' => '定時物理刪除已封禁賬號', ]; diff --git a/resources/lang/zh_TW/exam.php b/resources/lang/zh_TW/exam.php index e6e5a1b7..21490e57 100644 --- a/resources/lang/zh_TW/exam.php +++ b/resources/lang/zh_TW/exam.php @@ -1,6 +1,7 @@ '考核', 'name' => '考核名稱', 'index' => '考核指標', 'time_range' => '考核時間', diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php index 2cbe7e00..d1ac1dca 100644 --- a/resources/lang/zh_TW/label.php +++ b/resources/lang/zh_TW/label.php @@ -33,6 +33,7 @@ return [ 'submit' => '提交', 'cancel' => '取消', 'reset' => '重置', + 'anonymous' => '匿名', 'setting' => [ 'nav_text' => '設置', 'backup' => [ @@ -72,9 +73,11 @@ return [ 'no_promotion' => '無優惠', 'no_promotion_help' => '不享受任何優惠,上傳量/下載量按實際值計算', 'max_uploaded' => '最大上傳量倍數', - 'max_uploaded_help' => '總上傳量最多為其體積的多少倍', + 'max_uploaded_help' => '總上傳量最多為其體積的多少倍。設置為 0 無此限製', 'not_seed_box_max_speed' => '非 SeedBox 最高限速', 'not_seed_box_max_speed_help' => '單位:Mbps。若超過此值又不能匹配 SeedBox 記錄,禁用下載權限', + 'max_uploaded_duration' => '最大上傳量倍數有效時間範圍', + 'max_uploaded_duration_help' => '單位:小時。種子發布後的這個時間範圍內,最大上傳量倍數生效,超過此範圍不生效。設置為 0 一直生效', ], 'system' => [ 'tab_header' => '系統', @@ -148,6 +151,9 @@ return [ 'pos_state' => '置頂', 'sp_state' => '優惠', 'visible' => '活種', + 'picktype' => '推薦', + 'promotion_time_type' => '優惠時間類型', + 'hr' => 'H&R', ], 'hit_and_run' => [ 'label' => '用戶 H&R', diff --git a/resources/lang/zh_TW/medal.php b/resources/lang/zh_TW/medal.php index 3f6af04a..3ec07fd5 100644 --- a/resources/lang/zh_TW/medal.php +++ b/resources/lang/zh_TW/medal.php @@ -1,6 +1,7 @@ '勛章', 'action_wearing' => '佩戴', 'admin' => [ 'list' => [ diff --git a/resources/lang/zh_TW/message.php b/resources/lang/zh_TW/message.php index b2978f75..99aaceb2 100644 --- a/resources/lang/zh_TW/message.php +++ b/resources/lang/zh_TW/message.php @@ -14,6 +14,10 @@ return [ 'subject' => '下載權限取消', 'body' => '你的下載權限被取消,可能的原因是過低的分享率或行為不當。By: :operator', ], + 'download_disable_upload_over_speed' => [ + 'subject' => '下載權限取消', + 'body' => '你因上傳速度過快下載權限被取消,若是盒子用戶請備案。', + ], 'download_enable' => [ 'subject' => '下載權限恢復', 'body' => '你的下載權限恢復,你現在可以下載種子。By: :operator', diff --git a/resources/lang/zh_TW/nexus.php b/resources/lang/zh_TW/nexus.php index 69a3f32b..2fd6b606 100644 --- a/resources/lang/zh_TW/nexus.php +++ b/resources/lang/zh_TW/nexus.php @@ -2,4 +2,7 @@ return [ 'invalid_argument' => '參數錯誤', + 'require_argument' => ':argument 不能為空', + 'select_one_please' => '請選擇一項', + 'user_not_exists' => '(無此帳戶)', ]; diff --git a/resources/lang/zh_TW/permission.php b/resources/lang/zh_TW/permission.php index da0889d6..95cb74ca 100644 --- a/resources/lang/zh_TW/permission.php +++ b/resources/lang/zh_TW/permission.php @@ -5,6 +5,14 @@ return [ 'text' => '審核種子', 'desc' => '通過種子、拒絕種子、或將種子標記為未審', ], + 'torrent-set-special-tag' => [ + 'text' => '設定種子特殊標簽', + 'desc' => '設定種子的官方、零魔標簽', + ], + 'torrent-approval-allow-automatic' => [ + 'text' => '種子自動通過審核', + 'desc' => '種子發布即為審核通過狀態', + ], 'defaultclass' => [ 'text' => '預設等級', 'desc' => '註冊時獲得的等級', diff --git a/resources/lang/zh_TW/search.php b/resources/lang/zh_TW/search.php new file mode 100644 index 00000000..0862f011 --- /dev/null +++ b/resources/lang/zh_TW/search.php @@ -0,0 +1,13 @@ + '全站搜索', + 'search_keyword' => '關鍵字', + 'search_area' => '範圍', + 'search_area_options' => [ + '0' => '標題', + '1' => '簡介', + '3' => '發布者', + '4' => 'IMDB鏈接' + ], +]; diff --git a/resources/lang/zh_TW/searchbox.php b/resources/lang/zh_TW/searchbox.php index e053f2b7..73558bae 100644 --- a/resources/lang/zh_TW/searchbox.php +++ b/resources/lang/zh_TW/searchbox.php @@ -13,4 +13,8 @@ return [ \App\Models\SearchBox::EXTRA_DISPLAY_COVER_ON_TORRENT_LIST => '種子列表頁展示封面', \App\Models\SearchBox::EXTRA_DISPLAY_SEED_BOX_ICON_ON_TORRENT_LIST => '種子列表頁展示 SeedBox 圖標', ], + 'sections' => [ + 'browse' => '種子區', + 'special' => '特別區', + ], ]; diff --git a/resources/lang/zh_TW/torrent.php b/resources/lang/zh_TW/torrent.php index 23246c22..790c82e0 100644 --- a/resources/lang/zh_TW/torrent.php +++ b/resources/lang/zh_TW/torrent.php @@ -75,4 +75,10 @@ return [ 'deny_comment_show' => '審核不通過,原因::reason', 'logs_label' => '審核記錄' ], + 'show_hide_media_info' => '顯示/隱藏原始 MediaInfo', + 'promotion_time_types' => [ + \App\Models\Torrent::PROMOTION_TIME_TYPE_GLOBAL => '全局', + \App\Models\Torrent::PROMOTION_TIME_TYPE_PERMANENT => '永久', + \App\Models\Torrent::PROMOTION_TIME_TYPE_DEADLINE => '直到', + ], ]; diff --git a/resources/lang/zh_TW/user.php b/resources/lang/zh_TW/user.php index 9fa65910..9f5c1a80 100644 --- a/resources/lang/zh_TW/user.php +++ b/resources/lang/zh_TW/user.php @@ -27,4 +27,6 @@ return [ \App\Models\User::CLASS_STAFF_LEADER => '主管', ], 'change_username_lte_min_interval' => '上次修改時間::last_change_time,未滿足最小間隔::interval 天', + 'destroy_by_admin' => '被管理員物理刪除', + 'disable_by_admin' => '被管理员封禁', ]; diff --git a/resources/views/filament/resources/system/plugin-resource/pages/plugins.blade.php b/resources/views/filament/resources/system/plugin-resource/pages/plugins.blade.php new file mode 100644 index 00000000..cd00dafb --- /dev/null +++ b/resources/views/filament/resources/system/plugin-resource/pages/plugins.blade.php @@ -0,0 +1,3 @@ + + +