Merge branch 'php8' into section

This commit is contained in:
xiaomlove
2022-10-25 19:16:56 +08:00
168 changed files with 3072 additions and 621 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Console\Commands;
use App\Repositories\PluginRepository;
use Illuminate\Console\Command;
use Nexus\Plugin\BasePlugin;
class PluginCronjob extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'plugin:cronjob {--action=} {--id=} {--force=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Plugin install / update / delete cronjob handler';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$action = $this->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;
}
}

View File

@@ -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();
}

View File

@@ -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();
}
/**

View File

@@ -15,4 +15,12 @@ trait OptionsTrait
$disableValue => __('label.disabled'),
];
}
private static function getYesNoOptions($yesValue = 1, $noValue = 0): array
{
return [
$yesValue => 'Yes',
$noValue => 'No',
];
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Filament;
use Filament\Resources\Pages\ManageRecords;
use Filament\Tables\Filters\Layout;
class PageListSingle extends ManageRecords
{
protected ?string $maxContentWidth = 'full';
protected function getTableFiltersLayout(): ?string
{
return Layout::AboveContent;
}
}

View File

@@ -85,7 +85,7 @@ class ExamResource extends Resource
Forms\Components\Section::make(__('label.exam.section_time'))->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')),

View File

@@ -0,0 +1,114 @@
<?php
namespace App\Filament\Resources\System;
use App\Filament\Resources\System\PluginResource\Pages;
use App\Filament\Resources\System\PluginResource\RelationManagers;
use App\Models\Plugin;
use Filament\Forms;
use Filament\Resources\Form;
use Filament\Resources\Resource;
use Filament\Resources\Table;
use Filament\Tables;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\HtmlString;
class PluginResource extends Resource
{
protected static ?string $model = Plugin::class;
protected static ?string $navigationIcon = 'heroicon-o-plus-circle';
protected static ?string $navigationGroup = 'System';
protected static ?int $navigationSort = 99;
protected static bool $shouldRegisterNavigation = false;
protected static function getNavigationLabel(): string
{
return __('admin.sidebar.plugin');
}
public static function getBreadcrumb(): string
{
return self::getNavigationLabel();
}
public static function form(Form $form): Form
{
return $form
->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]);
})
;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Filament\Resources\System\PluginResource\Pages;
use App\Filament\Resources\System\PluginResource;
use Filament\Pages\Actions;
use Filament\Resources\Pages\ManageRecords;
class ManagePlugins extends ManageRecords
{
protected static string $resource = PluginResource::class;
protected ?string $maxContentWidth = 'full';
protected function getActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@@ -15,6 +15,7 @@ use Filament\Tables;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use phpDocumentor\Reflection\DocBlock\Tags\See;
use PhpIP\IP;
class SeedBoxRecordResource extends Resource
{
@@ -55,12 +56,23 @@ class SeedBoxRecordResource extends Resource
->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')),
])

View File

@@ -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);
}
}

View File

@@ -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([

View File

@@ -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

View File

@@ -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);
});

View File

@@ -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(),
];
}
}

View File

@@ -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(),

View File

@@ -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')),
])

View File

@@ -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

View File

@@ -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(),

View File

@@ -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')),

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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()

View File

@@ -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

View File

@@ -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;
}
}

32
app/Models/Plugin.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Plugin extends NexusModel
{
protected $fillable = ['display_name', 'package_name', 'remote_url', 'installed_version', 'status', 'description', 'status_result'];
const STATUS_NOT_INSTALLED = -1;
const STATUS_NORMAL = 0;
const STATUS_PRE_INSTALL = 1;
const STATUS_INSTALLING = 2;
const STATUS_INSTALL_FAILED = 3;
const STATUS_PRE_UPDATE = 11;
const STATUS_UPDATING = 12;
const STATUS_UPDATE_FAILED = 13;
const STATUS_PRE_DELETE = 101;
const STATUS_DELETING = 102;
const STATUS_DELETE_FAILED = 103;
public function statusText(): Attribute
{
return new Attribute(
get: fn($value, $attributes) => __('plugin.status.' . $attributes['status'])
);
}
}

View File

@@ -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');

View File

@@ -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');

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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('<label><input type="radio" name="hr[%s]" value="0"%s />NO</label>', $searchBoxId, $value == 0 ? ' checked' : '');
$hrRadio .= sprintf('<label><input type="radio" name="hr[%s]" value="1"%s />YES</label>', $searchBoxId, $value == 1 ? ' checked' : '');
return tr('H&R', $hrRadio, 1, "mode_$searchBoxId", true);
}
return '';
}
}

View File

@@ -0,0 +1,187 @@
<?php
namespace App\Repositories;
use App\Models\Plugin;
class PluginRepository extends BaseRepository
{
public function cronjob($action = null, $id = null, $force = false)
{
if ($action == 'install' || $action === null) {
$this->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;
}
}

View File

@@ -154,7 +154,7 @@ class SeedBoxRepository extends BaseRepository
private function clearCache()
{
return true;
NexusDB::cache_del('SEED_BOX_RECORD_APPROVAL_NONE');
// SeedBoxRecordUpdated::dispatch();
}

View File

@@ -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(
'<label><input type="checkbox" name="tags[]" value="%s"%s />%s</label>',
@@ -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(
"<span style=\"background-color:%s;color:%s;border-radius:%s;font-size:%s;margin:%s;padding:%s\">%s</span>",
$value->color, $value->font_color, $value->border_radius, $value->font_size, $value->margin, $value->padding, $value->name
);
if ($withFilterLink) {
$html .= sprintf('<a href="?tag_id=%s">%s</a>', $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(
"<span style=\"background-color:%s;color:%s;border-radius:%s;font-size:%s;margin:%s;padding:%s\">%s</span>",
$value->color, $value->font_color, $value->border_radius, $value->font_size, $value->margin, $value->padding, $value->name
);
if ($withFilterLink) {
$html .= sprintf('<a href="?tag_id=%s">%s</a>', $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('<select name="%s"><option value="">%s</option>', $name, nexus_trans('nexus.select_one_please'));
foreach ($list as $item) {
$selected = '';
if ($item->id == $value) {
$selected = ' selected';
}
$select .= sprintf('<option value="%s"%s>%s</option>', $item->id, $selected, $item->name);
}
$select .= '</select>';
return $select;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('plugins', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('torrents', function (Blueprint $table) {
$table->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');
});
}
};

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('torrents_custom_fields', function (Blueprint $table) {
$table->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');
});
}
};

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('complains', function (Blueprint $table) {
$table->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');
});
}
};

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -1,6 +1,6 @@
<?php
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.7.25');
defined('RELEASE_DATE') || define('RELEASE_DATE', '2022-09-12');
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.7.30');
defined('RELEASE_DATE') || define('RELEASE_DATE', '2022-10-24');
defined('IN_TRACKER') || define('IN_TRACKER', false);
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");

View File

@@ -281,7 +281,7 @@ function formatSpoiler($content, $title = '', $defaultCollapsed = true): string
$contentClass .= " collapse";
}
$HTML = sprintf(
'<div><div><div class="spoiler-title" title="%s">%s</div></div><div class="%s"><pre>%s</pre></div></div>',
'<div><div class="spoiler-title-box"><div class="spoiler-title" title="%s">%s</div></div><div class="%s">%s</div></div>',
$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'), '<img class="listicon listitem" src="pic/trans.gif" alt="list" />', '<b>', '</b>', '<i>', '</i>', '<u>', '</u>', '<pre>', '</pre>', '</span>', '</font>', '</font>', ' &nbsp;');
$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']."<b>" . htmlspecialchars($ip) ."</b>". $lang_functions['std_used_many_times'],false);
stderr($lang_functions['std_sorry'], $lang_functions['std_the_ip']."<b>" . htmlspecialchars($ip) ."</b>". $lang_functions['std_used_many_times'],false, false);
}
return true;
}
@@ -2486,6 +2493,7 @@ $cssupdatedate=($cssupdatedate ? "?".htmlspecialchars($cssupdatedate) : "");
<link rel="stylesheet" href="<?php echo $css_uri."theme.css".$cssupdatedate?>" type="text/css" />
<link rel="stylesheet" href="<?php echo $css_uri."DomTT.css".$cssupdatedate?>" type="text/css" />
<link rel="stylesheet" href="styles/curtain_imageresizer.css<?php echo $cssupdatedate?>" type="text/css" />
<link rel="stylesheet" href="styles/nexus.css<?php echo $cssupdatedate?>" type="text/css" />
<?php
if ($CURUSER){
// $caticonrow = get_category_icon_row($CURUSER['caticon']);
@@ -2632,8 +2640,8 @@ else {
[<a href="torrents.php?inclbookmarked=1&amp;allsec=1&amp;incldead=0"><?php echo $lang_functions['text_bookmarks'] ?></a>]
<font class = 'color_bonus'><?php echo $lang_functions['text_bonus'] ?></font>[<a href="mybonus.php"><?php echo $lang_functions['text_use'] ?></a>]: <?php echo number_format($CURUSER['seedbonus'], 1)?>
<?php if($attendance){ printf(' <a href="attendance.php" class="">'.$lang_functions['text_attended'].'</a>', $attendance->points, $CURUSER['attendance_card']); }else{ printf(' <a href="attendance.php" class="faqlink">%s</a>', $lang_functions['text_attendance']);}?>
<font class = 'color_bonus'><?php echo $lang_functions['text_seed_points'] ?></font>: <?php echo number_format($CURUSER['seed_points'], 1)?>
<font class = 'color_invite'><?php echo $lang_functions['text_invite'] ?></font>[<a href="invite.php?id=<?php echo $CURUSER['id']?>"><?php echo $lang_functions['text_send'] ?></a>]: <?php echo $CURUSER['invites']?>
<?php if(get_user_class() >= \App\Models\User::CLASS_ADMINISTRATOR) printf('[<a href="%s" target="_blank">%s</a>]', nexus_env('FILAMENT_PATH', 'nexusphp'), $lang_functions['text_management_system'])?>
<br />
<font class="color_ratio"><?php echo $lang_functions['text_ratio'] ?></font> <?php echo $ratio?>
<font class='color_uploaded'><?php echo $lang_functions['text_uploaded'] ?></font> <?php echo mksize($CURUSER['uploaded'])?>
@@ -2642,27 +2650,47 @@ else {
<font class='color_connectable'><?php echo $lang_functions['text_connectable'] ?></font><?php echo $connectable?> <?php echo maxslots();?>
<?php if(\App\Models\HitAndRun::getIsEnabled()) { ?><font class='color_bonus'>H&R: </font> <?php echo sprintf('[<a href="myhr.php">%s</a>]', (new \App\Repositories\HitAndRunRepository())->getStatusStats($CURUSER['id']))?><?php }?>
<?php if(\App\Models\Claim::getConfigIsEnabled()) { ?><font class='color_bonus'><?php echo $lang_functions['menu_claim']?></font> <?php echo sprintf('[<a href="claim.php?uid=%s">%s</a>]', $CURUSER['id'], (new \App\Repositories\ClaimRepository())->getStats($CURUSER['id']))?><?php }?>
<?php if(get_user_class() >= \App\Models\User::CLASS_ADMINISTRATOR) printf('[<a href="%s" target="_blank">%s</a>]', nexus_env('FILAMENT_PATH', 'nexusphp'), $lang_functions['text_management_system'])?>
</span>
</td>
<td class="bottom" align="right"><span class="medium"><?php echo $lang_functions['text_the_time_is_now'] ?><?php echo $datum['hours'].":".$datum['minutes']?><br />
<?php if(get_setting('main.spsct') == 'yes'){?>
<td class="bottom" align="left" style="border: none">
<form action="search.php" method="get" target="<?php echo nexus()->getScript() == 'search' ? '_self' : '_blank'?>">
<div style="display: flex;align-items: center">
<div style="display: flex;flex-direction: column">
<div>
<span><input type="text" name="search" style="width: 80px;height: 12px" value="<?php echo $_GET['search'] ?? '' ?>" placeholder="<?php echo nexus_trans('search.search_keyword')?>"/></span>
</div>
<div>
<span><?php echo build_search_area($_GET['search_area'] ?? '', ['style' => 'width: 88px'])?></span>
</div>
</div>
<div><input type="submit" value="<?php echo nexus_trans('search.global_search')?>" style="width: 39px;white-space: break-spaces;padding: 0" /></div>
</div>
</form>
</td>
<?php }?>
<td class="bottom" align="right"><span class="medium">
<?php
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(
"<a href=\"cheaterbox.php\"><img class=\"cheaterbox\" alt=\"cheaterbox\" title=\"".$lang_functions['title_cheaterbox']."\" src=\"pic/trans.gif\" /> </a>".$totalcheaters
." <a href=\"reports.php\"><img class=\"reportbox\" alt=\"reportbox\" title=\"".$lang_functions['title_reportbox']."\" src=\"pic/trans.gif\" /> </a>".$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(
"<a href=\"cheaterbox.php\"><img class=\"cheaterbox\" alt=\"cheaterbox\" title=\"".$lang_functions['title_cheaterbox']."\" src=\"pic/trans.gif\" /> </a>".$totalcheaters
." <a href=\"reports.php\"><img class=\"reportbox\" alt=\"reportbox\" title=\"".$lang_functions['title_reportbox']."\" src=\"pic/trans.gif\" /> </a>".$totalreports
);
}
print(" <a href=\"friends.php\"><img class=\"buddylist\" alt=\"Buddylist\" title=\"".$lang_functions['title_buddylist']."\" src=\"pic/trans.gif\" /></a>");
print(" <a href=\"getrss.php\"><img class=\"rss\" alt=\"RSS\" title=\"".$lang_functions['title_get_rss']."\" src=\"pic/trans.gif\" /></a>");
print '<br/>';
//echo $lang_functions['text_the_time_is_now'].$datum['hours'].":".$datum['minutes'] . '<br />';
// $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("<a href=\"messages.php\">".$inboxpic."</a> ".($messages ? $messages." (".$unread.$lang_functions['text_message_new'].")" : "0"));
print(" <a href=\"messages.php?action=viewmailbox&amp;box=-1\"><img class=\"sentbox\" alt=\"sentbox\" title=\"".$lang_functions['title_sentbox']."\" src=\"pic/trans.gif\" /></a> ".($outmessages ? $outmessages : "0"));
print(" <a href=\"friends.php\"><img class=\"buddylist\" alt=\"Buddylist\" title=\"".$lang_functions['title_buddylist']."\" src=\"pic/trans.gif\" /></a>");
print(" <a href=\"getrss.php\"><img class=\"rss\" alt=\"RSS\" title=\"".$lang_functions['title_get_rss']."\" src=\"pic/trans.gif\" /></a>");
?>
</span></td>
@@ -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("<td class=\"rowfollow\" align=\"center\"><i>".$lang_functions['text_anonymous']."</i><br />".(isset($row["owner"]) ? "(" . get_username($row["owner"]) .")" : "<i>".$lang_functions['text_orphaned']."</i>") . "</td>\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 = "<i>".$lang_functions['text_orphaned']."</i>";
$username = "<i>".nexus_trans('nexus.user_not_exists')."</i>";
$username = "<span class=\"nowrap\">" . ( $bracket == true ? "(" . $username . ")" : $username) . "</span>";
}
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 = '<img class="hitandrun" src="pic/trans.gif" alt="H&R" title="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("<table border=\"0\" cellspacing=\"0\" cellpadding=\"10\"><tr><td style='border: none; padding: 10px; background: ".$bgcolor."'>\n");
print("<b><a href=\"".$url."\"><font color=\"white\">".$text."</font></a></b>");
print("<b><a href=\"".$url."\" target='_blank'><font color=\"white\">".$text."</font></a></b>");
print("</td></tr></table><br />");
}
@@ -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<input type="text" id="%s" name="%s" value="%s" autocomplete="off" >', $label, $id, $name, $value);
$lang = get_langfolder_cookie(true);
if ($lang == 'zh_CN') {
$lang = 'zh';
}
$lang = str_replace('_', '-', $lang);
$js = <<<JS
jQuery.datetimepicker.setLocale('{$lang}');
jQuery("#{$id}").datetimepicker({
format: 'Y-m-d H:i'
})
JS;
\Nexus\Nexus::js($js, 'footer', false);
return $input;
}
function build_bonus_table(array $user, array $bonusResult = [], array $options = [])
{
if (empty($bonusResult)) {
$bonusResult = calculate_seed_bonus($user['id']);
}
$officialTag = get_setting('bonus.official_tag');
$officialAdditionalFactor = get_setting('bonus.official_addition', 0);
$haremFactor = get_setting('bonus.harem_addition');
$haremAddition = calculate_harem_addition($user['id']);
$isDonor = is_donor($user);
$donortimes_bonus = get_setting('bonus.donortimes');
$baseBonusFactor = 1;
if ($isDonor) {
$baseBonusFactor = $donortimes_bonus;
}
$baseBonus = $bonusResult['seed_bonus'] * $baseBonusFactor;
$totalBonus = number_format( $baseBonus + $haremAddition * $haremFactor + $bonusResult['official_bonus'] * $officialAdditionalFactor, 3);
$rowSpan = 1;
$hasHaremAddition = $hasOfficialAddition = false;
if ($haremFactor > 0) {
$rowSpan++;
$hasHaremAddition = true;
}
if ($officialAdditionalFactor > 0 && $officialTag) {
$rowSpan++;
$hasOfficialAddition = true;
}
$table = sprintf('<table cellpadding="5" style="%s">', $options['table_style'] ?? '');
$table .= '<tr>';
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.reward_type'));
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.count'));
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.size'));
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.a_value'));
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.bonus_base'));
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.factor'));
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.got_bonus'));
$table .= sprintf('<td class="colhead">%s</td>', nexus_trans('bonus.table_thead.total'));
$table .= '</tr>';
$table .= sprintf(
'<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td rowspan="%s">%s</td></tr>',
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(
'<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>',
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(
'<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>',
nexus_trans('bonus.reward_types.harem_addition'),
'--',
'--',
'--',
number_format($haremAddition, 3),
$haremFactor,
number_format($haremAddition * $haremFactor, 3)
);
}
$table .= '</table>';
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('<select name="search_area" style="%s">', $options['style'] ?? '');
foreach ([0, 1, 3, 4] as $item) {
$result .= sprintf(
'<option value="%s"%s>%s</option>',
$item, $item == $searchArea ? ' selected' : '', nexus_trans("search.search_area_options.$item")
);
}
$result .= '</select>';
return $result;
}
?>

View File

@@ -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'));
}

View File

@@ -239,6 +239,7 @@ $lang_details = array
'claim_label' => '认领种子',
'claim_confirm' => '确定要认领此种子吗?',
'action_approval' => '审核',
'row_tags' => '标签',
);
?>

View File

@@ -26,5 +26,6 @@ $lang_fields = [
'field_type_checkbox' => '横向多选',
'field_type_select' => '下拉单选',
'field_type_image' => '图片',
'col_display' => '自定义展示',
];
];

View File

@@ -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',
);
?>

View File

@@ -18,5 +18,6 @@ $lang_getusertorrentlistajax = array
'text_no_record' => "没有记录",
'text_total_size' => " | 总大小:",
'col_added' => "发布时间",
'col_client' => '客户端',
);
?>

View File

@@ -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' => '确定要清空群聊区吗?',
);

View File

@@ -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' => '注册链接',
);
?>

View File

@@ -6,6 +6,7 @@ $lang_mybonus = array
'std_karma_system_disabled' => "魔力值系统当前处于关闭中。",
'std_points_active' => "不过你的魔力值仍在计算中。",
'text_success_upload' => "祝贺你,你成功增加了<b>上传值</b>",
'text_success_download' => "祝贺你,你成功增加了<b>下载值</b>",
'text_success_invites' => "祝贺你,你获得了<b>1</b>个新的邀请名额!",
'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' => "如果有足够的魔力值,你可以用它来换取<font color='#ff4500'>下载量</font>。交易完成后,你的魔力值会减少,<font color='#ff4500'>下载量</font>则会增加。",
'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 <font color='#ff4500'>下载量</font>",
'text_downloaded_hundred_gb' => "100.0 GB <font color='#ff4500'>下载量</font>",
'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' => "每小时获得的魔力值点数由下面的公式给出<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulaa.png alt=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) )\" title=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) )\"><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulab.png alt=\"B = B0 * 2 / pi * arctan( A / L )\" title=\"B = B0 * 2 / pi * arctan( A / L )\"><br />
'text_bonus_formula_one' => "每小时获得的魔力值点数由下面的公式给出<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulaa.svg title=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) ) * Wi\"><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulab.png title=\"B = B0 * 2 / pi * arctan( A / L )\"><br />
式中<ul><li><b>A</b>为中间变量</li><li><b>Ti</b>为第<b>i</b>个种子的生存时间, 即自种子发布起到现在所经过的时间, 单位是周</li><li><b>T0</b>为参数。<b>T0</b> = ",
'text_bonus_formula_two' => "</li><li><b>Si</b>为第<b>i</b>个种子的大小单位是GB</li><li><b>Ni</b>为第<b>i</b>个种子当前的做种者数</li><li><b>N0</b>为参数。<b>N0</b> = ",
'text_bonus_formula_three' => "</li><li><b>B</b>为1小时中用户获得的做种魔力值点数</li><li><b>B0</b>为参数代表用户1小时获得魔力值的上限。<b>B0</b> = ",
'text_bonus_formula_four' => "</li><li><b>L</b>为参数。<b>L</b> = ",
'text_bonus_formula_five' => "</li></ul>简言之,为做种人数少、文件体积大的种子做种能获得更多魔力值。",
'text_bonus_formula_wi' => "</li><li><b>Wi</b>为第<b>i</b>个种子的权重系数,默认为 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' => '后宫只考虑直属后宫。每个后宫加成值可<a href="invite.php?id=%s" target="_blank" class="altlink">在此</a>查看',
'harem_additional_factor' => '所得奖励为全部后宫的时魔(不考虑加成)之和,乘以后宫加成系数,当前值为:',
'text_bonus_summary' => '每小时获得的合计魔力值',
'col_count' => '数量',
'col_size' => '体积',
'col_a' => 'A 值',
);
?>

View File

@@ -218,6 +218,7 @@ $lang_settings = array
'text_bonus_formula_four' => "。默认为'4'",
'text_bonus_formula_five' => "<b>Si</b>为第<b>i</b>个种子的大小单位是GB",
'text_bonus_formula_six' => "<b>Ni</b>为第<b>i</b>个种子当前的做种者数</li><li><b>N0</b>为参数。<b>N0</b> = ",
'text_bonus_formula_zero_bonus_factor' => "<b>Wi</b>为第<b>i</b>个种子权重系数。默认为 1零魔种子为 ",
'text_bonus_formula_seven' => "。默认为'7'",
'text_bonus_formula_eight' => "<b>B</b>为1小时中用户获得的做种魔力值点数",
'text_bonus_formula_nine' => "<b>B0</b>为参数代表用户1小时获得魔力值的上限。<b>B0</b> = ",
@@ -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'来禁止此规则。",
);
?>

View File

@@ -41,6 +41,7 @@ $lang_upload = array
'text_english_title' => "英文名:",
'text_titles_note' => "(如果英文名不存在,使用拼音或不填写)",
'fill_quality' => '填写质量',
'approval_deny_reach_upper_limit' => '当前审核被拒绝的种子数:%s 达到上限,不允许发布。',
);
?>

View File

@@ -155,5 +155,6 @@ $lang_userdetails = array
'row_user_props' => '道具',
'meta_key_change_username_username' => '新用户名',
'consume' => '使用',
'text_bonus_table' => '时魔',
);
?>

View File

@@ -238,6 +238,7 @@ $lang_details = array
'claim_label' => '認領種子',
'claim_confirm' => '確定要認領此種子嗎?',
'action_approval' => '審核',
'row_tags' => '標簽',
);
?>

View File

@@ -25,5 +25,6 @@ $lang_fields = [
'field_type_radio' => '橫向單選',
'field_type_checkbox' => '橫向多選',
'field_type_select' => '下拉單選',
'col_display' => '自定義展示',
];
];

View File

@@ -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',
);
?>

View File

@@ -18,5 +18,6 @@ $lang_getusertorrentlistajax = array
'text_no_record' => "沒有記錄",
'text_total_size' => " | 總大小:",
'col_added' => "發布時間",
'col_client' => '客戶端',
);
?>

View File

@@ -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' => '確定要清空群聊區嗎?',
);
?>

View File

@@ -49,6 +49,8 @@ $lang_invite = array
'std_or_above_can_invite' => "及以上的用戶才能發送邀請。",
'text_email_restriction_note' => "你只能發送邀請給以下郵箱:",
'harem_addition' => '後宮加成',
'signup_link_help' => '右鍵復製',
'signup_link' => '註冊鏈接',
);
?>

View File

@@ -6,6 +6,7 @@ $lang_mybonus = array
'std_karma_system_disabled' => "魔力值系統當前處於關閉中。",
'std_points_active' => "不過你的魔力值仍在計算中。",
'text_success_upload' => "祝賀你,你成功增加了<b>上傳值</b>",
'text_success_download' => "祝賀你,你成功增加了<b>下載值</b>",
'text_success_invites' => "祝賀你,你獲得了<b>1</b>個新的邀請名額!",
'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' => "如果有足夠的魔力值,你可以用它來換取<font color='#ff4500'>下載量</font>。交易完成後,你的魔力值會減少,<font color='#ff4500'>下載量</font>則會增加。",
'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 <font color='#ff4500'>下載量</font>",
'text_downloaded_hundred_gb' => "100.0 GB <font color='#ff4500'>下載量</font>",
'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' => "每小時獲得的魔力值點數由下面的公式給出<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulaa.png alt=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) )\" title=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) )\"><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulab.png alt=\"B = B0 * 2 / pi * arctan( A / L )\" title=\"B = B0 * 2 / pi * arctan( A / L )\"><br />
'text_bonus_formula_one' => "每小時獲得的魔力值點數由下面的公式給出<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulaa.svg title=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) ) * Wi\"><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=pic/bonusformulab.png title=\"B = B0 * 2 / pi * arctan( A / L )\"><br />
式中<ul><li><b>A</b>為中間變量</li><li><b>Ti</b>為第<b>i</b>個種子的生存時間, 即自種子發布起到現在所經過的時間, 單位是周</li><li><b>T0</b>為參數。<b>T0</b> = ",
'text_bonus_formula_two' => "</li><li><b>Si</b>為第<b>i</b>個種子的大小單位是GB</li><li><b>Ni</b>為第<b>i</b>個種子當前的做種者數</li><li><b>N0</b>為參數。<b>N0</b> = ",
'text_bonus_formula_three' => "</li><li><b>B</b>為1小時中用戶獲得的做種魔力值點數</li><li><b>B0</b>為參數代表用戶1小時獲得魔力值的上限。<b>B0</b> = ",
'text_bonus_formula_four' => "</li><li><b>L</b>為參數。<b>L</b> = ",
'text_bonus_formula_five' => "</li></ul>簡言之,為做種人數少、文件體積大的種子做種能獲得更多魔力值。",
'text_bonus_formula_wi' => "</li><li><b>Wi</b>為第<b>i</b>個種子的權重系數,默認為 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' => '後宮只考慮直屬後宮。每個後宮加成值可<a href="invite.php?id=%s" target="_blank" class="altlink">在此</a>查看',
'harem_additional_factor' => '所得獎勵為全部後宮的時魔(不考慮加成)之和,乘以後宮加成系數,當前值為:',
'text_bonus_summary' => '每小時獲得的合計魔力值',
'col_count' => '數量',
'col_size' => '體積',
'col_a' => 'A 值',
);
?>

View File

@@ -218,6 +218,7 @@ $lang_settings = array
'text_bonus_formula_four' => "。預設為'4'",
'text_bonus_formula_five' => "<b>Si</b>為第<b>i</b>個種子的大小單位是GB",
'text_bonus_formula_six' => "<b>Ni</b>為第<b>i</b>個種子目前的做種者數</li><li><b>N0</b>為參數。<b>N0</b> = ",
'text_bonus_formula_zero_bonus_factor' => "<b>Wi</b>為第<b>i</b>個種子權重系數。默認為 1零魔種子為 ",
'text_bonus_formula_seven' => "。預設為'7'",
'text_bonus_formula_eight' => "<b>B</b>為1小時中用戶獲得的做種魔力值點數",
'text_bonus_formula_nine' => "<b>B0</b>為參數代表用戶1小時獲得魔力值的上限。<b>B0</b> = ",
@@ -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'來禁止此規則。",
);
?>

View File

@@ -41,6 +41,7 @@ $lang_upload = array
'text_english_title' => "英文名:",
'text_titles_note' => "(如果英文名不存在,請使用拼音或不填寫)",
'fill_quality' => '填寫質量',
'approval_deny_reach_upper_limit' => '當前審核被拒絕的種子數:%s 達到上限,不允許發布。',
);
?>

View File

@@ -155,5 +155,6 @@ $lang_userdetails = array
'row_user_props' => '道具',
'meta_key_change_username_username' => '新用戶名',
'consume' => '使用',
'text_bonus_table' => '時魔',
);
?>

View File

@@ -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',
);
?>

View File

@@ -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',
];
];

View File

@@ -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&nbsp;Seeding",
'title_torrents_leeching' => "Torrents&nbsp;Leeching",
'text_connectable' => "Connectable:&nbsp;",
'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 <strong><a href=\"https://www.douban.com/\">douban</a></strong>. e.g.&nbsp;for movie <b>Transformers</b> the URL is <b> https://movie.douban.com/subject/1794171//</b>)",
'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 <b><a href="https://mediaarea.net/en/MediaInfo" target=\'_blank\'>MediaInfo</a></b>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.',
);
?>

View File

@@ -18,5 +18,6 @@ $lang_getusertorrentlistajax = array
'text_no_record' => "No record.",
'text_total_size' => " | Total size: ",
'col_added' => "Added",
'col_client' => 'Client',
);
?>

View File

@@ -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?',
);
?>

View File

@@ -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',
);
?>

View File

@@ -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 <b>Uploaded Amount</b>!",
'text_success_download' => "Congratulations! You have just increased your <b>Downloaded Amount</b>!",
'text_success_invites' => "Congratulations! You have got yourself <b>1</b> 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 <font color='#ff4500'>Download</font> Credit. The points are then removed from your Bonus Bank and the credit is added to your total <font color='#ff4500'>downloaded</font> 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 <font color='#ff4500'>Downloaded</font>",
'text_downloaded_hundred_gb' => "100.0 GB <font color='#ff4500'>Downloaded</font>",
'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<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=\"pic/bonusformulaa.png\" alt=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) )\" title=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) )\" /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=\"pic/bonusformulab.png\" alt=\"B = B0 * 2 / pi * arctan( A / L )\" title=\"B = B0 * 2 / pi * arctan( A / L )\" /><br />
'text_bonus_formula_one' => "The number of karma points gained per hour is given by the following formula<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=\"pic/bonusformulaa.svg\" title=\"A = sigma( ( 1 - 10 ^ ( - Ti / T0 ) ) * Si * ( 1 + sqrt( 2 ) * 10 ^ ( - ( Ni - 1 ) / ( N0 - 1 ) ) ) * Wi\" /><br />&nbsp;&nbsp;&nbsp;&nbsp;<img src=\"pic/bonusformulab.png\" title=\"B = B0 * 2 / pi * arctan( A / L )\" /><br />
where<ul><li><b>A</b> is an intermediate variable</li><li><b>Ti</b> is the <b>i</b>th torrent's Time Alive (TA), i.e. time elapsed since the torrent was uploaded, in weeks</li><li><b>T0</b> is a parameter. <b>T0</b> = ",
'text_bonus_formula_two' => "</li><li><b>Si</b> is the <b>i</b>th torrent's size, in GB</li><li><b>Ni</b> is the number of current seeders of the <b>i</b>th torrent</li><li><b>N0</b> is a parameter. <b>N0</b> = ",
'text_bonus_formula_three' => "</li><li><b>B</b> is the number of karma points gained by seeding in an hour</li><li><b>B0</b> is a parameter, which stands for the maximum bonus points per hour a user can get by seeding. <b>B0</b> = ",
'text_bonus_formula_four' => "</li><li><b>L</b> is a parameter. <b>L</b> = ",
'text_bonus_formula_five' => "</li></ul>In a nutshell, you can get more bonus by seeding less-seeded and larger torrents.",
'text_bonus_formula_wi' => "</li><li><b>Wi</b> is the <b>i</b>th 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<ul><li><b>A</b> is an intermediate variable</li><li><b>Ti</b> is the <b>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 <a href=\"invite.php?id=%s\" class='altlink' target='_blank'>here</a>",
'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',
);
?>

View File

@@ -218,6 +218,7 @@ $lang_settings = array
'text_bonus_formula_four' => ". Default '4'",
'text_bonus_formula_five' => "<b>Si</b> is the <b>i</b>th torrent's size, in GB",
'text_bonus_formula_six' => "<b>Ni</b> is the number of current seeders of the <b>i</b>th torrent</li><li><b>N0</b> is a parameter. <b>N0</b> = ",
'text_bonus_formula_zero_bonus_factor' => "<b>Wi</b> is the <b>i</b>th torrent's weight. Default is 1, zero torrent is ",
'text_bonus_formula_seven' => ". Default '7'",
'text_bonus_formula_eight' => "<b>B</b> is the number of karma points gained by seeding in an hour",
'text_bonus_formula_nine' => "<b>B0</b> is a parameter, which standards for the maximum bonus points per hour a user can get by seeding. <b>B0</b> = ",
@@ -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." ,
);
?>

View File

@@ -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.',
);
?>

View File

@@ -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',
);
?>

View File

@@ -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'] . '<font color="red">*</font>', '<input type="text" name="name" value="' . ($row['name'] ?? '') . '" style="width: 300px" />&nbsp;&nbsp;' . $lang_fields['col_name_help'], 1, '', true);
$trLabel = tr($lang_fields['col_label'] . '<font color="red">*</font>', '<input type="text" name="label" value="' . ($row['label'] ?? '') . '" style="width: 300px" />', 1, '', true);
$trType = tr($lang_fields['col_type'] . '<font color="red">*</font>', $this->radio('type', $this->getTypeRadioOptions(), $row['type'] ?? null), 1, '', true);
@@ -94,6 +99,9 @@ class Field
$trHelp = tr($lang_fields['col_help'], '<textarea name="help" rows="4" cols="80">' . ($row['help'] ?? '') . '</textarea>', 1, '', true);
$trOptions = tr($lang_fields['col_options'], '<textarea name="options" rows="6" cols="80">' . ($row['options'] ?? '') . '</textarea><br/>' . $lang_fields['col_options_help'], 1, '', true);
$trIsSingleRow = tr($lang_fields['col_is_single_row'] . '<font color="red">*</font>', $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') . '<font color="red">*</font>', '<input type="number" name="priority" value="' . ($row['priority'] ?? '0') . '" style="width: 300px" />', 1, '', true);
$trDisplay = tr($lang_fields['col_display'], '<textarea name="display" rows="4" cols="80">' . ($row['display'] ?? '') . '</textarea><br/>' . $lang_catmanage['row_custom_field_display_help'], 1, '', true);
$id = $row['id'] ?? 0;
$form = <<<HTML
<div>
@@ -109,6 +117,8 @@ class Field
{$trHelp}
{$trOptions}
{$trIsSingleRow}
{$trPriority}
{$trDisplay}
</table>
</div>
<div style="text-align: center; margin-top: 10px;">
@@ -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 = "<font color=\"red\">*</font>";
}
$trLabel = $row['label'] . $requireText;
$trRelation = "mode_$searchBoxId";
if ($row['type'] == self::TYPE_TEXT) {
$html .= tr($row['label'], sprintf('<input type="text" name="%s" value="%s" style="width: %s"/>', $name, $currentValue, '99%'), 1);
$html .= tr($trLabel, sprintf('<input type="text" name="%s" value="%s" style="width: %s"/>', $name, $currentValue, '99%'), 1, $trRelation);
} elseif ($row['type'] == self::TYPE_TEXTAREA) {
$html .= tr($row['label'], sprintf('<textarea name="%s" rows="4" style="width: %s">%s</textarea>', $name, '99%', $currentValue), 1);
$html .= tr($trLabel, sprintf('<textarea name="%s" rows="4" style="width: %s">%s</textarea>', $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 = '<select name="' . $name . '">';
foreach (preg_split('/[\r\n]+/', trim($row['options'])) as $option) {
@@ -312,7 +330,7 @@ HEAD;
);
}
$part .= '</select>';
$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;
}
</script>
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);
}
}

View File

@@ -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'];
}

View File

@@ -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
}
}

View File

@@ -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',

View File

@@ -163,21 +163,27 @@ class TechnicalInformation
$subtitles = $this->getSubtitles();
// dd($videos, $audios, $subtitles);
if (empty($videos) && empty($audios) && empty($subtitles)) {
// return '';
return sprintf('<div style="padding: 0 0.5rem"><pre>%s</pre></div>', $this->mediaInfo);
return sprintf('<div class="nexus-media-info-raw"><pre>%s</pre></div>', $this->mediaInfo);
}
$result = '<table style="border: none;width: 100%"><tbody><tr>';
$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 .= '</tr></tbody></table>';
$result .= '</tr>';
$rawMediaInfo = sprintf('[spoiler=%s][raw]<pre>%s</pre>[/raw][/spoiler]', nexus_trans('torrent.show_hide_media_info'), $this->mediaInfo);
$result .= sprintf('<tr><td colspan="%s" class="nexus-media-info-raw">%s</td></tr>', $cols, format_comment($rawMediaInfo, false));
$result .= '</tbody></table>';
return $result;
}
@@ -189,7 +195,8 @@ class TechnicalInformation
$table .= sprintf('<td style="border: none; padding-right: 5px;padding-bottom: 5px;"><b>%s: </b>%s</td>', $key, $value);
$table .= '</tr>';
}
$table .= '</tbody></table>';
$table .= '</tbody>';
$table .= '</table>';
return sprintf('<td style="border: none; padding-right: 5px;padding-bottom: 5px">%s</td>', $table);
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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(' [<a href="user-ban-log.php?q=%s" class="faqlink" target="_blank">%s</a>]', urlencode($user->username), $lang_complains['text_view_band_log']);
}
printf('<br />IP: ' . htmlspecialchars($complain['ip']));
}
echo '<hr />', 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('<b>%s @ %s</b>: ', $row['userid'] ? get_plain_username($row['userid']) : $lang_complains['text_complainer'], gettime($row['added']));
printf('<b>%s @ %s', $row['userid'] ? get_plain_username($row['userid']) : $lang_complains['text_complainer'], gettime($row['added']));
if ($isAdmin) {
printf(' (%s)', htmlspecialchars($row['ip']));
}
echo ': </b>';
echo format_comment($row['body']) . '<hr />';
}
}else{

View File

@@ -70,7 +70,7 @@ if (!$row) {
$banned_torrent = ($row["banned"] == 'yes' ? " <b>(<font class=\"striking\">".$lang_functions['text_banned']."</font>)</b>" : "");
$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 ? "&nbsp;&nbsp;&nbsp;".$sp_torrent : "").($sp_torrent_sub) . $hrImg . $approvalStatusIcon;
print("<h1 align=\"center\" id=\"top\">".$s."</h1>\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 = "<i>".$lang_details['text_anonymous']."</i>";
else
$uprow = "<i>".$lang_details['text_anonymous']."</i> (" . 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 = "<b>".$lang_details['text_size']."</b>" . mksize($row["size"]);
$type_info = "&nbsp;&nbsp;&nbsp;<b>".$lang_details['row_type'].":</b>&nbsp;".$row["cat_name"];
$source_info = $medium_info = $codec_info = $audiocodec_info = $standard_info = $processing_info = $team_info = '';
@@ -267,8 +274,11 @@ JS;
print("</td></tr>\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("<a href=\"javascript: klappe_news('nfo')\"><img class=\"plus\" src=\"pic/trans.gif\" alt=\"Show/Hide\" id=\"picnfo\" title=\"".$lang_details['title_show_or_hide']."\" /> ".$lang_details['text_nfo']."</a><br /><a href=\"viewnfo.php?id=".$row['id']."\" class=\"sublink\">". $lang_details['text_view_nfo']. "</a>", "<div id='knfo' style=\"display: none;\"><pre style=\"font-size:10pt; font-family: 'Courier New', monospace;\">".$nfo."</pre></div>\n", 1);
tr("<a href=\"javascript: klappe_news('nfo')\"><img class=\"plus\" src=\"pic/trans.gif\" alt=\"Show/Hide\" id=\"picnfo\" title=\"".$lang_details['title_show_or_hide']."\" /> ".$lang_details['text_nfo']."</a><br /><a href=\"viewnfo.php?id=".$row['id']."\" class=\"sublink\">". $lang_details['text_view_nfo']. "</a>", "<div id='knfo' style=\"display: none;\"><pre style=\"font-size:10pt; font-family: 'Courier New', monospace;white-space: break-spaces\">".$nfo."</pre></div>\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 .= "<tr". $sphighlight."><td class=\"rowfollow nowrap\" valign=\"middle\" style='padding: 0px'>".return_category_image($copy_row["catid"], "torrents.php?allsec=1&amp;")."</td><td class=\"rowfollow\" align=\"left\"><a href=\"" . htmlspecialchars(get_protocol_prefix() . $BASEURL . "/details.php?id=" . $copy_row["id"]. "&hit=1")."\">" . $dispname ."</a>". $sp_info. $hrImg ."</td>" .
"<td class=\"rowfollow\" align=\"left\">" . rtrim(trim($other_source_info . $other_medium_info . $other_codec_info . $other_standard_info . $other_processing_info), ","). "</td>" .

View File

@@ -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'], "<font class=\"medium\"><input type=\"radio\" name=\"nfoaction\" value=\"keep\" checked=\"checked\" />".$lang_edit['radio_keep_current'].
"<input type=\"radio\" name=\"nfoaction\" value=\"remove\" />".$lang_edit['radio_remove'].
@@ -82,7 +81,7 @@ else {
tr($lang_functions['text_technical_info'], '<textarea name="technical_info" rows="8" style="width: 99%;">' . $row['technical_info'] . '</textarea><br/>' . $lang_functions['text_technical_info_help_text'], 1);
}
$s = "<select name=\"type\" id=\"oricat\">";
$s = "<select name=\"type\" id=\"oricat\" data-mode='$sectionmode'>";
$cats = genrelist($sectionmode);
foreach ($cats as $subrow) {
@@ -94,7 +93,7 @@ else {
$s .= "</select>\n";
if ($allowmove){
$s2 = "<select name=\"type\" id=newcat disabled>\n";
$s2 = "<select name=\"type\" id=newcat disabled data-mode='$othermode'>\n";
$cats2 = genrelist($othermode);
foreach ($cats2 as $subrow) {
$s2 .= "<option value=\"" . $subrow["id"] . "\"";
@@ -153,10 +152,14 @@ else {
$sectionCurrent = $searchBoxRep->renderQualitySelect($sectionmode);
tr($lang_edit['row_quality'], $sectionCurrent, 1, "hide mode mode_$sectionmode");
echo $customField->renderOnUploadPage($id, $sectionmode);
echo $hitAndRunRep->renderOnUploadPage($row['hr'], $sectionmode);
if ($allowmove && $othermode) {
$selectOther = $searchBoxRep->renderQualitySelect($othermode);
tr($lang_edit['row_quality'], $selectOther, 1, "hide mode mode_$othermode");
echo $customField->renderOnUploadPage($id, $othermode);
echo $hitAndRunRep->renderOnUploadPage($row['hr'], $othermode);
}
tr($lang_functions['text_tags'], (new \App\Repositories\TagRepository())->renderCheckbox($tagIdArr), 1);
@@ -194,7 +197,8 @@ else {
foreach (\App\Models\Torrent::listPosStates() as $key => $value) {
$options[] = "<option" . (($row["pos_state"] == $key) ? " selected=\"selected\"" : "" ) . " value=\"" . $key . "\">".$value['text']."</option>";
}
$pickcontent .= "<b>".$lang_edit['row_torrent_position'].":&nbsp;</b>"."<select name=\"sel_posstate\" style=\"width: 100px;\">" . implode('', $options) . "</select>&nbsp;&nbsp;&nbsp;";
$pickcontent .= "<b>".$lang_edit['row_torrent_position'].":&nbsp;</b>"."<select name=\"pos_state\" style=\"width: 100px;\">" . implode('', $options) . "</select>&nbsp;&nbsp;&nbsp;";
$pickcontent .= datetimepicker_input('pos_state_until', $row['pos_state_until'], nexus_trans('label.deadline') . ":&nbsp;");
}
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('<label><input type="radio" name="hr" value="0"%s />NO</label>', (string)$row['hr'] === '0' ? ' checked' : '');
$hrRadio .= sprintf('<label><input type="radio" name="hr" value="1"%s />YES</label>', (string)$row['hr'] === '1' ? ' checked' : '');
tr('H&R', $hrRadio, 1);
}
print("<tr><td class=\"toolbox\" colspan=\"2\" align=\"center\"><input id=\"qr\" type=\"submit\" value=\"".$lang_edit['submit_edit_it']."\" /> <input type=\"reset\" value=\"".$lang_edit['submit_revert_changes']."\" /></td></tr>\n");
print("</table>\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 = <<<JS
jQuery("#movecheck").on("change", function () {
let _this = jQuery(this);
let checked = _this.prop("checked");
let activeSelect
if (checked) {
activeSelect = jQuery("#newcat");
} else {
activeSelect = jQuery("#oricat");
}
let mode = activeSelect.attr("data-mode");
console.log(mode)
jQuery("tr[relation]").hide();
jQuery("tr[relation=mode_" + mode +"]").show();
})
jQuery("tr[relation]").hide();
jQuery("tr[relation=mode_{$sectionmode}]").show();
JS;
\Nexus\Nexus::js($customFieldJs, 'footer', false);
stdfoot();
function getAddedTimeOption($timeStamp, $addSeconds) {
$timeStamp += $addSeconds;

View File

@@ -2,6 +2,7 @@
require "../include/bittorrent.php";
dbconn();
require_once(get_langfile_path());
require_once(get_langfile_path('catmanage.php'));
loggedinorreturn();
if (get_user_class() < UC_ADMINISTRATOR) {
permissiondenied();

View File

@@ -11,6 +11,7 @@ header("Pragma: no-cache" );
$torrentRep = new \App\Repositories\TorrentRepository();
$claimRep = new \App\Repositories\ClaimRepository();
$seedBoxRep = new \App\Repositories\SeedBoxRepository();
$claimTorrentTTL = \App\Models\Claim::getConfigTorrentTTL();
$id = intval($_GET['userid'] ?? 0);
$type = $_GET['type'];
@@ -22,8 +23,8 @@ if(!user_can('torrenthistory') && $id != $CURUSER["id"])
function maketable($res, $mode = 'seeding')
{
global $lang_getusertorrentlistajax,$CURUSER,$smalldescription_main, $lang_functions, $id;
global $torrentRep, $claimRep, $claimTorrentTTL;
$showActionClaim = false;
global $torrentRep, $claimRep, $claimTorrentTTL, $seedBoxRep;
$showActionClaim = $showClient = false;
switch ($mode)
{
case 'uploaded': {
@@ -55,6 +56,7 @@ function maketable($res, $mode = 'seeding')
$showtotalsize = true;
$columncount = 8;
$showActionClaim = true;
$showClient = true;
break;
}
case 'leeching': {
@@ -69,6 +71,7 @@ function maketable($res, $mode = 'seeding')
$showcotime = false;
$showanonymous = false;
$showtotalsize = true;
$showClient = true;
$columncount = 8;
break;
}
@@ -105,6 +108,10 @@ function maketable($res, $mode = 'seeding')
}
default: break;
}
$shouldShowClient = false;
if ($showClient && (user_can('userprofile') || $CURUSER['id'] == $id)) {
$shouldShowClient = true;
}
$results = $torrentIdArr = [];
while ($row = mysql_fetch_assoc($res)) {
$results[] = $row;
@@ -129,6 +136,9 @@ function maketable($res, $mode = 'seeding')
$ret = "<table border=\"1\" cellspacing=\"0\" cellpadding=\"5\" width=\"100%\"><tr><td class=\"colhead\" style=\"padding: 0px\">".$lang_getusertorrentlistajax['col_type']."</td><td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_name']."</td><td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_added']."</td>".
($showsize ? "<td class=\"colhead\" align=\"center\"><img class=\"size\" src=\"pic/trans.gif\" alt=\"size\" title=\"".$lang_getusertorrentlistajax['title_size']."\" /></td>" : "").($showsenum ? "<td class=\"colhead\" align=\"center\"><img class=\"seeders\" src=\"pic/trans.gif\" alt=\"seeders\" title=\"".$lang_getusertorrentlistajax['title_seeders']."\" /></td>" : "").($showlenum ? "<td class=\"colhead\" align=\"center\"><img class=\"leechers\" src=\"pic/trans.gif\" alt=\"leechers\" title=\"".$lang_getusertorrentlistajax['title_leechers']."\" /></td>" : "").($showuploaded ? "<td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_uploaded']."</td>" : "") . ($showdownloaded ? "<td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_downloaded']."</td>" : "").($showratio ? "<td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_ratio']."</td>" : "").($showsetime ? "<td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_se_time']."</td>" : "").($showletime ? "<td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_le_time']."</td>" : "").($showcotime ? "<td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_time_completed']."</td>" : "").($showanonymous ? "<td class=\"colhead\" align=\"center\">".$lang_getusertorrentlistajax['col_anonymous']."</td>" : "");
if ($shouldShowClient) {
$ret .= sprintf('<td class="colhead" align="center">%s</td><td class="colhead" align="center">IP</td>', $lang_getusertorrentlistajax['col_client']);
}
$ret .= sprintf('<td class="colhead" align="center">%s</td>', $lang_functions['std_action']);
$ret .= "</tr>";
$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 .= "<td class=\"rowfollow\" align=\"center\">"."". str_replace("&nbsp;", "<br />", gettime($arr['completedat'],false)). "</td>";
if ($showanonymous)
$ret .= "<td class=\"rowfollow\" align=\"center\">".$arr['anonymous']."</td>";
if ($shouldShowClient) {
$ipArr = array_filter([$arr['ipv4'], $arr['ipv6']]);
foreach ($ipArr as &$_ip) {
$_ip = sprintf('<span class="nowrap">%s</span>', $_ip . $seedBoxRep->renderIcon($_ip, $arr['userid']));
}
$ret .= sprintf(
'<td class="rowfollow" align="center">%s<br/>%s</td><td class="rowfollow" align="center">%s</td>',
get_agent($arr['peer_id'], $arr['agent']), $arr['port'],
implode('<br/>', $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;

View File

@@ -64,7 +64,7 @@ $classes = array_chunk(\App\Models\User::$classes, 4, true);
</table>
</td>
</tr>
<?php do_action('increment_bulk_form_filters') ?>
<?php do_action('form_role_filter', 'Add to Role') ?>
<tr><td class="rowhead" valign="top">Subject </td><td class="rowfollow"><input type=text name=subject size=82></td></tr>
<tr><td class="rowhead" valign="top">Reason </td><td class="rowfollow"><textarea name=msg cols=80 rows=5><?php echo $body ?? ''?></textarea></td></tr>
<tr>

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