tag add mode

This commit is contained in:
xiaomlove
2022-10-30 17:30:24 +08:00
parent 0fe7b2f76f
commit 87a079b392
39 changed files with 164 additions and 76 deletions
@@ -2,8 +2,8 @@
namespace App\Filament\Resources\Section; namespace App\Filament\Resources\Section;
use App\Filament\Resources\System\SectionResource\Pages; use App\Filament\Resources\Section\SectionResource\Pages;
use App\Filament\Resources\System\SectionResource\RelationManagers; use App\Filament\Resources\Section\SectionResource\RelationManagers;
use App\Http\Middleware\Locale; use App\Http\Middleware\Locale;
use App\Models\Forum; use App\Models\Forum;
use App\Models\SearchBox; use App\Models\SearchBox;
@@ -1,6 +1,6 @@
<?php <?php
namespace App\Filament\Resources\System\SectionResource\Pages; namespace App\Filament\Resources\Section\SectionResource\Pages;
use App\Filament\Resources\Section\SectionResource; use App\Filament\Resources\Section\SectionResource;
use App\Models\SearchBox; use App\Models\SearchBox;
@@ -1,6 +1,6 @@
<?php <?php
namespace App\Filament\Resources\System\SectionResource\Pages; namespace App\Filament\Resources\Section\SectionResource\Pages;
use App\Filament\Resources\Section\SectionResource; use App\Filament\Resources\Section\SectionResource;
use App\Models\SearchBox; use App\Models\SearchBox;
@@ -1,6 +1,6 @@
<?php <?php
namespace App\Filament\Resources\System\SectionResource\Pages; namespace App\Filament\Resources\Section\SectionResource\Pages;
use App\Filament\PageList; use App\Filament\PageList;
use App\Filament\Resources\Section\SectionResource; use App\Filament\Resources\Section\SectionResource;
@@ -17,7 +17,7 @@ class CreateExam extends CreateRecord
$examRep = new ExamRepository(); $examRep = new ExamRepository();
try { try {
$this->record = $examRep->store($data); $this->record = $examRep->store($data);
$this->notify('success', $this->getCreatedNotificationMessage()); $this->notify('success', $this->getCreatedNotificationTitle());
if ($another) { if ($another) {
// Ensure that the form record is anonymized so that relationships aren't loaded. // Ensure that the form record is anonymized so that relationships aren't loaded.
$this->form->model($this->record::class); $this->form->model($this->record::class);
@@ -22,7 +22,7 @@ class CreateSeedBoxRecord extends CreateRecord
$rep = new SeedBoxRepository(); $rep = new SeedBoxRepository();
try { try {
$this->record = $rep->store($data); $this->record = $rep->store($data);
$this->notify('success', $this->getCreatedNotificationMessage()); $this->notify('success', $this->getCreatedNotificationTitle());
if ($another) { if ($another) {
// Ensure that the form record is anonymized so that relationships aren't loaded. // Ensure that the form record is anonymized so that relationships aren't loaded.
$this->form->model($this->record::class); $this->form->model($this->record::class);
+21 -1
View File
@@ -4,6 +4,7 @@ namespace App\Filament\Resources\Torrent;
use App\Filament\Resources\Torrent\TagResource\Pages; use App\Filament\Resources\Torrent\TagResource\Pages;
use App\Filament\Resources\Torrent\TagResource\RelationManagers; use App\Filament\Resources\Torrent\TagResource\RelationManagers;
use App\Models\SearchBox;
use App\Models\Tag; use App\Models\Tag;
use Filament\Forms; use Filament\Forms;
use Filament\Resources\Form; use Filament\Resources\Form;
@@ -45,6 +46,11 @@ class TagResource extends Resource
Forms\Components\TextInput::make('padding')->required()->label(__('label.tag.padding')), Forms\Components\TextInput::make('padding')->required()->label(__('label.tag.padding')),
Forms\Components\TextInput::make('border_radius')->required()->label(__('label.tag.border_radius')), Forms\Components\TextInput::make('border_radius')->required()->label(__('label.tag.border_radius')),
Forms\Components\TextInput::make('priority')->integer()->required()->label(__('label.priority'))->default(0), Forms\Components\TextInput::make('priority')->integer()->required()->label(__('label.priority'))->default(0),
Forms\Components\Select::make('mode')
->options(SearchBox::query()->pluck('name', 'id')->toArray())
->label(__('label.search_box.taxonomy.mode'))
->helperText(__('label.search_box.taxonomy.mode_help'))
,
]); ]);
} }
@@ -53,6 +59,10 @@ class TagResource extends Resource
return $table return $table
->columns([ ->columns([
Tables\Columns\TextColumn::make('id'), Tables\Columns\TextColumn::make('id'),
Tables\Columns\TextColumn::make('search_box.name')
->label(__('label.search_box.label'))
->formatStateUsing(fn ($record) => $record->search_box->name ?? 'All')
,
Tables\Columns\TextColumn::make('name')->label(__('label.name'))->searchable(), Tables\Columns\TextColumn::make('name')->label(__('label.name'))->searchable(),
Tables\Columns\TextColumn::make('color')->label(__('label.tag.color')), Tables\Columns\TextColumn::make('color')->label(__('label.tag.color')),
Tables\Columns\TextColumn::make('font_color')->label(__('label.tag.font_color')), Tables\Columns\TextColumn::make('font_color')->label(__('label.tag.font_color')),
@@ -66,7 +76,17 @@ class TagResource extends Resource
]) ])
->defaultSort('priority', 'desc') ->defaultSort('priority', 'desc')
->filters([ ->filters([
// Tables\Filters\SelectFilter::make('mode')
->options(SearchBox::query()->pluck('name', 'id')->toArray())
->label(__('label.search_box.taxonomy.mode'))
->query(function (Builder $query, array $data) {
return $query->when($data['value'], function (Builder $query, $value) {
return $query->where(function (Builder $query) use ($value) {
return $query->where('mode', $value)->orWhere('mode', 0);
});
});
})
,
]) ])
->actions(self::getActions()) ->actions(self::getActions())
->bulkActions([ ->bulkActions([
@@ -20,7 +20,7 @@ class CreateUser extends CreateRecord
$this->record = $userRep->store($data); $this->record = $userRep->store($data);
$this->notify( $this->notify(
'success ', 'success ',
$this->getCreatedNotificationMessage(), $this->getCreatedNotificationTitle(),
); );
$this->redirect($this->getRedirectUrl()); $this->redirect($this->getRedirectUrl());
} catch (\Exception $exception) { } catch (\Exception $exception) {
@@ -51,7 +51,12 @@ class AuthenticateController extends Controller
$user = User::query()->where('passkey', $passkey)->first(['id', 'passhash']); $user = User::query()->where('passkey', $passkey)->first(['id', 'passhash']);
if ($user) { if ($user) {
$ip = getip(); $ip = getip();
$passhash = md5($user->passhash . $ip); /**
* Not IP related
* @since 1.8.0
*/
// $passhash = md5($user->passhash . $ip);
$passhash = md5($user->passhash);
do_log(sprintf('passhash: %s, ip: %s, md5: %s', $user->passhash, $ip, $passhash)); do_log(sprintf('passhash: %s, ip: %s, md5: %s', $user->passhash, $ip, $passhash));
logincookie($user->id, $passhash,false, 0x7fffffff, true, true, true); logincookie($user->id, $passhash,false, 0x7fffffff, true, true, true);
$user->last_login = now(); $user->last_login = now();
+1 -1
View File
@@ -7,7 +7,7 @@ class AudioCodec extends NexusModel
{ {
protected $table = 'audiocodecs'; protected $table = 'audiocodecs';
protected $fillable = ['name', 'sort_index']; protected $fillable = ['name', 'sort_index', 'mode',];
public static function getLabelName() public static function getLabelName()
{ {
+1 -1
View File
@@ -7,7 +7,7 @@ class Codec extends NexusModel
{ {
protected $table = 'codecs'; protected $table = 'codecs';
protected $fillable = ['name', 'sort_index']; protected $fillable = ['name', 'sort_index', 'mode',];
public static function getLabelName() public static function getLabelName()
{ {
+1 -1
View File
@@ -7,7 +7,7 @@ class Media extends NexusModel
{ {
protected $table = 'media'; protected $table = 'media';
protected $fillable = ['name', 'sort_index']; protected $fillable = ['name', 'sort_index', 'mode',];
public static function getLabelName() public static function getLabelName()
{ {
+1 -1
View File
@@ -7,7 +7,7 @@ class Processing extends NexusModel
{ {
protected $table = 'processings'; protected $table = 'processings';
protected $fillable = ['name', 'sort_index']; protected $fillable = ['name', 'sort_index', 'mode',];
public static function getLabelName() public static function getLabelName()
{ {
+1 -1
View File
@@ -5,7 +5,7 @@ namespace App\Models;
class Source extends NexusModel class Source extends NexusModel
{ {
protected $fillable = ['name', 'sort_index']; protected $fillable = ['name', 'sort_index', 'mode',];
public static function getLabelName() public static function getLabelName()
{ {
+1 -1
View File
@@ -5,7 +5,7 @@ namespace App\Models;
class Standard extends NexusModel class Standard extends NexusModel
{ {
protected $fillable = ['name', 'sort_index']; protected $fillable = ['name', 'sort_index', 'mode',];
public static function getLabelName() public static function getLabelName()
{ {
+8 -1
View File
@@ -7,7 +7,9 @@ class Tag extends NexusModel
public $timestamps = true; public $timestamps = true;
protected $fillable = [ protected $fillable = [
'id', 'name', 'color', 'priority', 'created_at', 'updated_at', 'font_size', 'font_color', 'padding', 'margin', 'border_radius' 'id', 'name', 'color', 'priority', 'created_at', 'updated_at',
'font_size', 'font_color', 'padding', 'margin', 'border_radius',
'mode',
]; ];
const DEFAULTS = [ const DEFAULTS = [
@@ -66,6 +68,11 @@ class Tag extends NexusModel
return $this->hasMany(TorrentTag::class, 'tag_id'); return $this->hasMany(TorrentTag::class, 'tag_id');
} }
public function search_box(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(SearchBox::class, 'mode', 'id');
}
} }
+1 -1
View File
@@ -5,7 +5,7 @@ namespace App\Models;
class Team extends NexusModel class Team extends NexusModel
{ {
protected $fillable = ['name', 'sort_index']; protected $fillable = ['name', 'sort_index', 'mode',];
public static function getLabelName() public static function getLabelName()
{ {
+12 -9
View File
@@ -50,27 +50,27 @@ class TagRepository extends BaseRepository
return Tag::query()->orderBy('priority', 'desc')->orderBy('id', 'desc'); return Tag::query()->orderBy('priority', 'desc')->orderBy('id', 'desc');
} }
public function renderCheckbox(array $checked = [], $ignorePermission = false): string public function renderCheckbox(int $searchBoxId, array $checked = [], $ignorePermission = false): string
{ {
$html = ''; $html = '';
$results = $this->listAll(); $results = $this->listAll($searchBoxId);
if (!$ignorePermission && !user_can('torrent-set-special-tag')) { if (!$ignorePermission && !user_can('torrent-set-special-tag')) {
$specialTags = Tag::listSpecial(); $specialTags = Tag::listSpecial();
$results = $results->filter(fn ($item) => !in_array($item->id, $specialTags)); $results = $results->filter(fn ($item) => !in_array($item->id, $specialTags));
} }
foreach ($results as $value) { foreach ($results as $value) {
$html .= sprintf( $html .= sprintf(
'<label><input type="checkbox" name="tags[]" value="%s"%s />%s</label>', '<label><input type="checkbox" name="tags[%s][]" value="%s"%s />%s</label>',
$value->id, in_array($value->id, $checked) ? ' checked' : '', $value->name $searchBoxId, $value->id, in_array($value->id, $checked) ? ' checked' : '', $value->name
); );
} }
return $html; return $html;
} }
public function renderSpan(array $renderIdArr = [], $withFilterLink = false): string public function renderSpan(int $searchBoxId, array $renderIdArr = [], $withFilterLink = false): string
{ {
$html = ''; $html = '';
foreach ($this->listAll() as $value) { foreach ($this->listAll($searchBoxId) as $value) {
if (in_array($value->id, $renderIdArr) || (isset($renderIdArr[0]) && $renderIdArr[0] == '*')) { if (in_array($value->id, $renderIdArr) || (isset($renderIdArr[0]) && $renderIdArr[0] == '*')) {
$tagId = $value->id; $tagId = $value->id;
if ($value) { if ($value) {
@@ -149,17 +149,20 @@ class TagRepository extends BaseRepository
return self::$orderByFieldIdString; return self::$orderByFieldIdString;
} }
public function listAll() public function listAll(int $searchBoxId = 0): \Illuminate\Database\Eloquent\Collection|array
{ {
if (empty(self::$allTags)) { if (empty(self::$allTags)) {
self::$allTags = self::createBasicQuery()->get(); self::$allTags = self::createBasicQuery()->get();
} }
if ($searchBoxId > 0) {
return self::$allTags->filter(fn ($d) => in_array($d->mode, [0, $searchBoxId]));
}
return self::$allTags; return self::$allTags;
} }
public function buildSelect($name, $value): string public function buildSelect(int $searchBoxId, $name, $value): string
{ {
$list = $this->listAll(); $list = $this->listAll($searchBoxId);
$select = sprintf('<select name="%s"><option value="">%s</option>', $name, nexus_trans('nexus.select_one_please')); $select = sprintf('<select name="%s"><option value="">%s</option>', $name, nexus_trans('nexus.select_one_please'));
foreach ($list as $item) { foreach ($list as $item) {
$selected = ''; $selected = '';
@@ -0,0 +1,35 @@
<?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()
{
if (!\Nexus\Database\NexusDB::hasColumn('tags', 'mode')) {
Schema::table('tags', function (Blueprint $table) {
$table->integer('mode')->default(0);
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tags', function (Blueprint $table) {
$table->dropColumn(['mode']);
});
}
};
+7 -3
View File
@@ -1985,7 +1985,12 @@ function userlogin() {
if ($_COOKIE["c_secure_login"] == base64("yeah")) if ($_COOKIE["c_secure_login"] == base64("yeah"))
{ {
$md5 = md5($row["passhash"].$ip); /**
* Not IP related
* @since 1.8.0
*/
// $md5 = md5($row["passhash"].$ip);
$md5 = md5($row["passhash"]);
$log .= ", secure login == yeah, passhash: {$row['passhash']}, ip: $ip, md5: $md5"; $log .= ", secure login == yeah, passhash: {$row['passhash']}, ip: $ip, md5: $md5";
if ($_COOKIE["c_secure_pass"] != $md5) { if ($_COOKIE["c_secure_pass"] != $md5) {
do_log("$log, c_secure_pass != md5"); do_log("$log, c_secure_pass != md5");
@@ -3619,7 +3624,7 @@ foreach ($rows as $row)
*/ */
$tagOwns = $torrentTagResult->get($id); $tagOwns = $torrentTagResult->get($id);
if ($tagOwns) { if ($tagOwns) {
$tags = $tagRep->renderSpan($tagOwns->pluck('tag_id')->toArray()); $tags = $tagRep->renderSpan($row['search_box_id'], $tagOwns->pluck('tag_id')->toArray());
} else { } else {
$tags = ''; $tags = '';
} }
@@ -6025,7 +6030,6 @@ function build_search_box_category_table($mode, $checkboxValue, $categoryHrefPre
//Category //Category
$html .= sprintf('<tr><td class="embedded" align="left">%s</td></tr>', nexus_trans('label.search_box.category')); $html .= sprintf('<tr><td class="embedded" align="left">%s</td></tr>', nexus_trans('label.search_box.category'));
$categoryChunks = $searchBox->categories->chunk($searchBox->catsperrow); $categoryChunks = $searchBox->categories->chunk($searchBox->catsperrow);
$lang = get_langfolder_cookie();
foreach ($categoryChunks as $chunk) { foreach ($categoryChunks as $chunk) {
$html .= '<tr>'; $html .= '<tr>';
foreach ($chunk as $item) { foreach ($chunk as $item) {
+1 -1
View File
@@ -150,7 +150,7 @@ $lang_userdetails = array
'row_medal' => '勋章', 'row_medal' => '勋章',
'row_donoruntil' => '捐赠状态截止时间', 'row_donoruntil' => '捐赠状态截止时间',
'text_donoruntil_note' => "时间格式为'年年年年-月月-日日 时时:分分:秒秒'。留空永久有效。", 'text_donoruntil_note' => "时间格式为'年年年年-月月-日日 时时:分分:秒秒'。留空永久有效。",
'change_field_value_migrated' => '修改请到管理后台操作', 'change_field_value_migrated' => '修改请到%s操作',
'sure_to_remove_leech_warn' => '确定要清除此吸血警告吗?', 'sure_to_remove_leech_warn' => '确定要清除此吸血警告吗?',
'row_user_props' => '道具', 'row_user_props' => '道具',
'meta_key_change_username_username' => '新用户名', 'meta_key_change_username_username' => '新用户名',
+1 -1
View File
@@ -150,7 +150,7 @@ $lang_userdetails = array
'row_medal' => '勛章', 'row_medal' => '勛章',
'row_donoruntil' => '捐贈狀態截止時間', 'row_donoruntil' => '捐贈狀態截止時間',
'text_donoruntil_note' => "時間格式為'年年年年-月月-日日 時時:分分:秒秒'。留空永久有效。", 'text_donoruntil_note' => "時間格式為'年年年年-月月-日日 時時:分分:秒秒'。留空永久有效。",
'change_field_value_migrated' => '修改請到管理後臺操作', 'change_field_value_migrated' => '修改請到%s操作',
'sure_to_remove_leech_warn' => '確定要清除此吸血警告嗎?', 'sure_to_remove_leech_warn' => '確定要清除此吸血警告嗎?',
'row_user_props' => '道具', 'row_user_props' => '道具',
'meta_key_change_username_username' => '新用戶名', 'meta_key_change_username_username' => '新用戶名',
+1 -1
View File
@@ -150,7 +150,7 @@ $lang_userdetails = array
'row_medal' => 'Medal', 'row_medal' => 'Medal',
'row_donoruntil' => 'Donated until', 'row_donoruntil' => 'Donated until',
'text_donoruntil_note' => "Time format is YYYY-MM-DD hh:mm:ss. Leave blank permanently.", 'text_donoruntil_note' => "Time format is YYYY-MM-DD hh:mm:ss. Leave blank permanently.",
'change_field_value_migrated' => 'Modification please go to the background management system.', 'change_field_value_migrated' => 'Modification please go to the %s.',
'sure_to_remove_leech_warn' => 'Are you sure to remove this leech warn ?', 'sure_to_remove_leech_warn' => 'Are you sure to remove this leech warn ?',
'row_user_props' => 'Props', 'row_user_props' => 'Props',
'meta_key_change_username_username' => 'New username', 'meta_key_change_username_username' => 'New username',
+4 -2
View File
@@ -132,7 +132,10 @@ class Install
$filename = basename($path); $filename = basename($path);
$count = preg_match('/create_(.*)_table.php/', $filename, $matches); $count = preg_match('/create_(.*)_table.php/', $filename, $matches);
if ($count) { if ($count) {
$tables[$matches[1]] = "database/migrations/$filename"; $tableName = $matches[1];
//Special treatment
$tableName = str_replace("seedbox_records", "seed_box_records", $tableName);
$tables[$tableName] = "database/migrations/$filename";
} }
} }
return $tables; return $tables;
@@ -535,7 +538,6 @@ class Install
public function listShouldCreateTable() public function listShouldCreateTable()
{ {
$existsTable = $this->listExistsTable(); $existsTable = $this->listExistsTable();
// $tableCreate = $this->listAllTableCreate();
$tableCreate = $this->listAllTableCreateFromMigrations(); $tableCreate = $this->listAllTableCreateFromMigrations();
$shouldCreateTable = []; $shouldCreateTable = [];
foreach ($tableCreate as $table => $sql) { foreach ($tableCreate as $table => $sql) {
+1 -1
View File
@@ -132,7 +132,7 @@ if (!$row) {
$torrentTags = \App\Models\TorrentTag::query()->where('torrent_id', $row['id'])->get(); $torrentTags = \App\Models\TorrentTag::query()->where('torrent_id', $row['id'])->get();
if ($torrentTags->isNotEmpty()) { if ($torrentTags->isNotEmpty()) {
$tagRep = new \App\Repositories\TagRepository(); $tagRep = new \App\Repositories\TagRepository();
tr($lang_details['row_tags'], $tagRep->renderSpan($torrentTags->pluck('tag_id')->toArray()),true); tr($lang_details['row_tags'], $tagRep->renderSpan($row['search_box_id'], $torrentTags->pluck('tag_id')->toArray()),true);
} }
$size_info = "<b>".$lang_details['text_size']."</b>" . mksize($row["size"]); $size_info = "<b>".$lang_details['text_size']."</b>" . mksize($row["size"]);
+1 -1
View File
@@ -123,7 +123,7 @@ if (strlen($CURUSER['passkey']) != 32) {
$trackerReportAuthKey = $torrentRep->getTrackerReportAuthKey($id, $CURUSER['id'], true); $trackerReportAuthKey = $torrentRep->getTrackerReportAuthKey($id, $CURUSER['id'], true);
$dict = \Rhilip\Bencode\Bencode::load($fn); $dict = \Rhilip\Bencode\Bencode::load($fn);
$dict['announce'] = $ssl_torrent . $base_announce_url . "?authkey=$trackerReportAuthKey"; $dict['announce'] = $ssl_torrent . $base_announce_url . "?authkey=$trackerReportAuthKey";
do_log(sprintf("[ANNOUNCE_URL], user: %s, torrent: %s, url: %s", $CURUSER['id'] ?? '', $id, $dict['announce']));
/** /**
* does not support multi-tracker * does not support multi-tracker
* *
+3 -3
View File
@@ -18,7 +18,7 @@ if (!$row) die();
*/ */
$customField = new \Nexus\Field\Field(); $customField = new \Nexus\Field\Field();
$hitAndRunRep = new \App\Repositories\HitAndRunRepository(); $hitAndRunRep = new \App\Repositories\HitAndRunRepository();
$tagRep = new \App\Repositories\TagRepository();
$tagIdArr = \App\Models\TorrentTag::query()->where('torrent_id', $id)->get()->pluck('tag_id')->toArray(); $tagIdArr = \App\Models\TorrentTag::query()->where('torrent_id', $id)->get()->pluck('tag_id')->toArray();
$searchBoxRep = new \App\Repositories\SearchBoxRepository(); $searchBoxRep = new \App\Repositories\SearchBoxRepository();
if ($enablespecial == 'yes' && user_can('movetorrent')) if ($enablespecial == 'yes' && user_can('movetorrent'))
@@ -154,16 +154,16 @@ else {
tr($lang_edit['row_quality'], $sectionCurrent, 1, "mode_$sectionmode"); tr($lang_edit['row_quality'], $sectionCurrent, 1, "mode_$sectionmode");
echo $customField->renderOnUploadPage($id, $sectionmode); echo $customField->renderOnUploadPage($id, $sectionmode);
echo $hitAndRunRep->renderOnUploadPage($row['hr'], $sectionmode); echo $hitAndRunRep->renderOnUploadPage($row['hr'], $sectionmode);
tr($lang_functions['text_tags'], $tagRep->renderCheckbox($sectionmode, $tagIdArr), 1, "mode_$sectionmode");
if ($allowmove && $othermode) { if ($allowmove && $othermode) {
$selectOther = $searchBoxRep->renderTaxonomySelect($othermode, $row); $selectOther = $searchBoxRep->renderTaxonomySelect($othermode, $row);
tr($lang_edit['row_quality'], $selectOther, 1, "mode_$othermode"); tr($lang_edit['row_quality'], $selectOther, 1, "mode_$othermode");
echo $customField->renderOnUploadPage($id, $othermode); echo $customField->renderOnUploadPage($id, $othermode);
echo $hitAndRunRep->renderOnUploadPage($row['hr'], $othermode); echo $hitAndRunRep->renderOnUploadPage($row['hr'], $othermode);
tr($lang_functions['text_tags'], $tagRep->renderCheckbox($othermode, $tagIdArr), 1, "mode_$othermode");
} }
tr($lang_functions['text_tags'], (new \App\Repositories\TagRepository())->renderCheckbox($tagIdArr), 1);
$rowChecks = []; $rowChecks = [];
if (user_can('beanonymous') || user_can('torrentmanage')) { if (user_can('beanonymous') || user_can('torrentmanage')) {
$rowChecks[] = "<label><input type=\"checkbox\" name=\"anonymous\"" . ($row["anonymous"] == "yes" ? " checked=\"checked\"" : "" ) . " value=\"1\" />".$lang_edit['checkbox_anonymous_note']."</label>"; $rowChecks[] = "<label><input type=\"checkbox\" name=\"anonymous\"" . ($row["anonymous"] == "yes" ? " checked=\"checked\"" : "" ) . " value=\"1\" />".$lang_edit['checkbox_anonymous_note']."</label>";
+5 -3
View File
@@ -302,9 +302,11 @@ $categories .= "</table>";
$categories = build_search_box_category_table($browsecatmode, 'yes', 'torrents.php?allsec=1&', false, 3, '', ['section_name' => true]); $categories = build_search_box_category_table($browsecatmode, 'yes', 'torrents.php?allsec=1&', false, 3, '', ['section_name' => true]);
print($categories); print($categories);
print '<div style="height: 1px;background-color: #eee;margin: 10px 0"></div>'; if (get_setting('main.spsct') == 'yes') {
$categoriesSpecial = build_search_box_category_table($specialcatmode, 'yes', 'torrents.php?allsec=1&', false, 3, '', ['section_name' => true]); print '<div style="height: 1px;background-color: #eee;margin: 10px 0"></div>';
print($categoriesSpecial); $categoriesSpecial = build_search_box_category_table($specialcatmode, 'yes', 'torrents.php?allsec=1&', false, 3, '', ['section_name' => true]);
print($categoriesSpecial);
}
?> ?>
</td> </td>
</tr> </tr>
+2 -2
View File
@@ -599,8 +599,8 @@ elseif ($action == 'bonussettings'){
tr($lang_settings['row_official_addition'],$lang_settings['text_user_would_get_by_official']."<input type='text' style=\"width: 50px\" name=official_addition value='".(isset($BONUS["official_addition"]) ? $BONUS["official_addition"] : 0.5 )."'>".$lang_settings['text_addition_addition_note'], 1); tr($lang_settings['row_official_addition'],$lang_settings['text_user_would_get_by_official']."<input type='text' style=\"width: 50px\" name=official_addition value='".(isset($BONUS["official_addition"]) ? $BONUS["official_addition"] : 0.5 )."'>".$lang_settings['text_addition_addition_note'], 1);
$tagRep = new \App\Repositories\TagRepository(); $tagRep = new \App\Repositories\TagRepository();
tr($lang_settings['row_official_tag'], $tagRep->buildSelect('official_tag', $BONUS["official_tag"] ?? '') . $lang_settings['text_official_tag_note'], 1); tr($lang_settings['row_official_tag'], $tagRep->buildSelect(0,'official_tag', $BONUS["official_tag"] ?? '') . $lang_settings['text_official_tag_note'], 1);
tr($lang_settings['row_zero_bonus_tag'], $tagRep->buildSelect('zero_bonus_tag', $BONUS["zero_bonus_tag"] ?? '') . $lang_settings['text_zero_bonus_tag_note'], 1); tr($lang_settings['row_zero_bonus_tag'], $tagRep->buildSelect(0,'zero_bonus_tag', $BONUS["zero_bonus_tag"] ?? '') . $lang_settings['text_zero_bonus_tag_note'], 1);
print("<tr><td colspan=2 align=center><b>".$lang_settings['text_things_cost_bonus']."</b></td></tr>"); print("<tr><td colspan=2 align=center><b>".$lang_settings['text_things_cost_bonus']."</b></td></tr>");
tr($lang_settings['row_one_gb_credit'],$lang_settings['text_it_costs_user']."<input type='text' style=\"width: 50px\" name=onegbupload value='".(isset($BONUS["onegbupload"]) ? $BONUS["onegbupload"] : 300 )."'>".$lang_settings['text_one_gb_credit_note'], 1); tr($lang_settings['row_one_gb_credit'],$lang_settings['text_it_costs_user']."<input type='text' style=\"width: 50px\" name=onegbupload value='".(isset($BONUS["onegbupload"]) ? $BONUS["onegbupload"] : 300 )."'>".$lang_settings['text_one_gb_credit_note'], 1);
+1 -1
View File
@@ -236,7 +236,7 @@ if (!empty($_POST['custom_fields'][$newcatmode])) {
* *
* @since v1.6 * @since v1.6
*/ */
$tagIdArr = array_filter($_POST['tags'] ?? []); $tagIdArr = array_filter($_POST['tags'][$newcatmode] ?? []);
insert_torrent_tags($id, $tagIdArr, true); insert_torrent_tags($id, $tagIdArr, true);
if($CURUSER["id"] == $row["owner"]) if($CURUSER["id"] == $row["owner"])
+6 -1
View File
@@ -43,7 +43,12 @@ if ($row["enabled"] == "no")
if (isset($_POST["securelogin"]) && $_POST["securelogin"] == "yes") if (isset($_POST["securelogin"]) && $_POST["securelogin"] == "yes")
{ {
$securelogin_indentity_cookie = true; $securelogin_indentity_cookie = true;
$passh = md5($row["passhash"].$ip); /**
* Not IP related
* @since 1.8.0
*/
// $passh = md5($row["passhash"].$ip);
$passh = md5($row["passhash"]);
$log .= ", secure login == yeah, passhash: {$row['passhash']}, ip: $ip, md5: $passh"; $log .= ", secure login == yeah, passhash: {$row['passhash']}, ip: $ip, md5: $passh";
} }
else else
+1 -1
View File
@@ -407,7 +407,7 @@ if (!empty($_POST['custom_fields'][$catmod])) {
* *
* @since v1.6 * @since v1.6
*/ */
$tagIdArr = array_filter($_POST['tags'] ?? []); $tagIdArr = array_filter($_POST['tags'][$catmod] ?? []);
if (!empty($tagIdArr)) { if (!empty($tagIdArr)) {
insert_torrent_tags($id, $tagIdArr); insert_torrent_tags($id, $tagIdArr);
} }
+8 -9
View File
@@ -5,14 +5,6 @@ require_once(get_langfile_path('torrents.php'));
require_once(get_langfile_path('speical.php')); require_once(get_langfile_path('speical.php'));
loggedinorreturn(); loggedinorreturn();
parked(); parked();
/**
* tags
*/
$tagRep = new \App\Repositories\TagRepository();
$allTags = $tagRep->listAll();
$elasticsearchEnabled = nexus_env('ELASTICSEARCH_ENABLED');
//check searchbox //check searchbox
switch (nexus()->getScript()) { switch (nexus()->getScript()) {
case 'torrents': case 'torrents':
@@ -30,6 +22,13 @@ switch (nexus()->getScript()) {
default: default:
$sectiontype = 0; $sectiontype = 0;
} }
/**
* tags
*/
$tagRep = new \App\Repositories\TagRepository();
$allTags = $tagRep->listAll($sectiontype);
$elasticsearchEnabled = nexus_env('ELASTICSEARCH_ENABLED');
$showsubcat = get_searchbox_value($sectiontype, 'showsubcat');//whether show subcategory (i.e. sources, codecs) or not $showsubcat = get_searchbox_value($sectiontype, 'showsubcat');//whether show subcategory (i.e. sources, codecs) or not
$showsource = get_searchbox_value($sectiontype, 'showsource'); //whether show sources or not $showsource = get_searchbox_value($sectiontype, 'showsource'); //whether show sources or not
$showmedium = get_searchbox_value($sectiontype, 'showmedium'); //whether show media or not $showmedium = get_searchbox_value($sectiontype, 'showmedium'); //whether show media or not
@@ -1143,7 +1142,7 @@ if (!$Cache->get_page()){
echo $Cache->next_row(); echo $Cache->next_row();
if ($allTags->isNotEmpty()) { if ($allTags->isNotEmpty()) {
echo '<tr><td colspan="3" class="embedded" style="padding-top: 4px">' . $tagRep->renderSpan(['*'], true) . '</td></tr>'; echo '<tr><td colspan="3" class="embedded" style="padding-top: 4px">' . $tagRep->renderSpan($sectiontype, ['*'], true) . '</td></tr>';
} }
?> ?>
+3 -1
View File
@@ -41,6 +41,7 @@ $showaudiocodec = (($allowtorrents && get_searchbox_value($brsectiontype, 'showa
$settingMain = get_setting('main'); $settingMain = get_setting('main');
$torrentRep = new \App\Repositories\TorrentRepository(); $torrentRep = new \App\Repositories\TorrentRepository();
$searchBoxRep = new \App\Repositories\SearchBoxRepository(); $searchBoxRep = new \App\Repositories\SearchBoxRepository();
$tagRep = new \App\Repositories\TagRepository();
stdhead($lang_upload['head_upload']); stdhead($lang_upload['head_upload']);
?> ?>
<form id="compose" enctype="multipart/form-data" action="takeupload.php" method="post" name="upload"> <form id="compose" enctype="multipart/form-data" action="takeupload.php" method="post" name="upload">
@@ -158,12 +159,14 @@ stdhead($lang_upload['head_upload']);
tr($lang_upload['row_quality'], $selectNormal, 1, "mode_$browsecatmode"); tr($lang_upload['row_quality'], $selectNormal, 1, "mode_$browsecatmode");
echo $customField->renderOnUploadPage(0, $browsecatmode); echo $customField->renderOnUploadPage(0, $browsecatmode);
echo $hitAndRunRep->renderOnUploadPage('', $browsecatmode); echo $hitAndRunRep->renderOnUploadPage('', $browsecatmode);
tr($lang_functions['text_tags'], $tagRep->renderCheckbox($browsecatmode), 1, "mode_$browsecatmode");
} }
if ($allowspecial) { if ($allowspecial) {
$selectNormal = $searchBoxRep->renderTaxonomySelect($specialcatmode); $selectNormal = $searchBoxRep->renderTaxonomySelect($specialcatmode);
tr($lang_upload['row_quality'], $selectNormal, 1, "mode_$specialcatmode"); tr($lang_upload['row_quality'], $selectNormal, 1, "mode_$specialcatmode");
echo $customField->renderOnUploadPage(0, $specialcatmode); echo $customField->renderOnUploadPage(0, $specialcatmode);
echo $hitAndRunRep->renderOnUploadPage('', $specialcatmode); echo $hitAndRunRep->renderOnUploadPage('', $specialcatmode);
tr($lang_functions['text_tags'], $tagRep->renderCheckbox($specialcatmode), 1, "mode_$specialcatmode");
} }
//==== offer dropdown for offer mod from code by S4NE //==== offer dropdown for offer mod from code by S4NE
@@ -229,7 +232,6 @@ JS;
{ {
tr($lang_upload['row_show_uploader'], "<input type=\"checkbox\" name=\"uplver\" value=\"yes\" />".$lang_upload['checkbox_hide_uploader_note'], 1); tr($lang_upload['row_show_uploader'], "<input type=\"checkbox\" name=\"uplver\" value=\"yes\" />".$lang_upload['checkbox_hide_uploader_note'], 1);
} }
tr($lang_functions['text_tags'], (new \App\Repositories\TagRepository())->renderCheckbox(), 1);
?> ?>
<tr><td class="toolbox" align="center" colspan="2"><b><?php echo $lang_upload['text_read_rules']?></b> <input id="qr" type="submit" class="btn" value="<?php echo $lang_upload['submit_upload']?>" /></td></tr> <tr><td class="toolbox" align="center" colspan="2"><b><?php echo $lang_upload['text_read_rules']?></b> <input id="qr" type="submit" class="btn" value="<?php echo $lang_upload['submit_upload']?>" /></td></tr>
</table> </table>
+5 -3
View File
@@ -542,9 +542,11 @@ if ($showaudiocodec) $audiocodecs = searchbox_item_list("audiocodecs");
*/ */
$categories = build_search_box_category_table($browsecatmode, 'yes','torrents.php?allsec=1', false, 3, $CURUSER['notifs'], ['section_name' => true]); $categories = build_search_box_category_table($browsecatmode, 'yes','torrents.php?allsec=1', false, 3, $CURUSER['notifs'], ['section_name' => true]);
$delimiter = '<div style="height: 1px;background-color: #eee;margin: 10px 0"></div>'; $delimiter = '<div style="height: 1px;background-color: #eee;margin: 10px 0"></div>';
$categoriesSpecial = build_search_box_category_table($specialcatmode, 'yes','torrents.php?allsec=1', false, 3, $CURUSER['notifs'], ['section_name' => true]); if (get_setting('main.spsct') == 'yes') {
$extra = "<table><caption><font class='big'>{$lang_usercp['text_additional_selection']}</font></caption><tr><td class=bottom><b>".$lang_usercp['text_show_dead_active']."</b><br /><select name=\"incldead\"><option value=\"0\" ".(strpos($CURUSER['notifs'], "[incldead=0]") !== false ? " selected" : "").">".$lang_usercp['select_including_dead']."</option><option value=\"1\" ".(strpos($CURUSER['notifs'], "[incldead=1]") !== false || strpos($CURUSER['notifs'], "incldead") == false ? " selected" : "").">".$lang_usercp['select_active']."</option><option value=\"2\" ".(strpos($CURUSER['notifs'], "[incldead=2]") !== false ? " selected" : "").">".$lang_usercp['select_dead']."</option></select></td><td class=bottom align=left><b>".$lang_usercp['text_show_special_torrents']."</b><br /><select name=\"spstate\"><option value=\"0\" ".($special_state == 0 ? " selected" : "").">".$lang_usercp['select_all']."</option>".promotion_selection($special_state)."</select></td><td class=bottom><b>".$lang_usercp['text_show_bookmarked']."</b><br /><select name=\"inclbookmarked\"><option value=\"0\" ".(strpos($CURUSER['notifs'], "[inclbookmarked=0]") !== false ? " selected" : "").">".$lang_usercp['select_all']."</option><option value=\"1\" ".(strpos($CURUSER['notifs'], "[inclbookmarked=1]") !== false ? " selected" : "")." >".$lang_usercp['select_bookmarked']."</option><option value=\"2\" ".(strpos($CURUSER['notifs'], "[inclbookmarked=2]") !== false ? " selected" : "").">".$lang_usercp['select_bookmarked_exclude']."</option></select></td></tr></table>"; $categories .= $delimiter . build_search_box_category_table($specialcatmode, 'yes','torrents.php?allsec=1', false, 3, $CURUSER['notifs'], ['section_name' => true]);
tr_small($lang_usercp['row_browse_default_categories'],$categories . $delimiter . $categoriesSpecial . $delimiter . $extra,1); }
$categories .= $delimiter . "<table><caption><font class='big'>{$lang_usercp['text_additional_selection']}</font></caption><tr><td class=bottom><b>".$lang_usercp['text_show_dead_active']."</b><br /><select name=\"incldead\"><option value=\"0\" ".(strpos($CURUSER['notifs'], "[incldead=0]") !== false ? " selected" : "").">".$lang_usercp['select_including_dead']."</option><option value=\"1\" ".(strpos($CURUSER['notifs'], "[incldead=1]") !== false || strpos($CURUSER['notifs'], "incldead") == false ? " selected" : "").">".$lang_usercp['select_active']."</option><option value=\"2\" ".(strpos($CURUSER['notifs'], "[incldead=2]") !== false ? " selected" : "").">".$lang_usercp['select_dead']."</option></select></td><td class=bottom align=left><b>".$lang_usercp['text_show_special_torrents']."</b><br /><select name=\"spstate\"><option value=\"0\" ".($special_state == 0 ? " selected" : "").">".$lang_usercp['select_all']."</option>".promotion_selection($special_state)."</select></td><td class=bottom><b>".$lang_usercp['text_show_bookmarked']."</b><br /><select name=\"inclbookmarked\"><option value=\"0\" ".(strpos($CURUSER['notifs'], "[inclbookmarked=0]") !== false ? " selected" : "").">".$lang_usercp['select_all']."</option><option value=\"1\" ".(strpos($CURUSER['notifs'], "[inclbookmarked=1]") !== false ? " selected" : "")." >".$lang_usercp['select_bookmarked']."</option><option value=\"2\" ".(strpos($CURUSER['notifs'], "[inclbookmarked=2]") !== false ? " selected" : "").">".$lang_usercp['select_bookmarked_exclude']."</option></select></td></tr></table>";
tr_small($lang_usercp['row_browse_default_categories'], $categories,1);
$ss_r = sql_query("SELECT * FROM stylesheets") or die; $ss_r = sql_query("SELECT * FROM stylesheets") or die;
$ss_sa = array(); $ss_sa = array();
while ($ss_a = mysql_fetch_array($ss_r)) while ($ss_a = mysql_fetch_array($ss_r))
+12 -10
View File
@@ -120,6 +120,12 @@ if ($CURUSER['id'] == $user['id'] || user_can('cruprfmanage'))
?> ?>
<table width="100%" border="1" cellspacing="0" cellpadding="5"> <table width="100%" border="1" cellspacing="0" cellpadding="5">
<?php <?php
$userIdDisplay = $user['id'];
$userManageSystemUrl = sprintf('%s/%s/users/%s',getSchemeAndHttpHost(), nexus_env('FILAMENT_PATH', 'nexusphp'), $user['id']);
$userManageSystemText = sprintf('<a href="%s" target="_blank" class="altlink">%s</a>', $userManageSystemUrl, $lang_functions['text_management_system']);
if (user_can('prfmanage') && $user["class"] < get_user_class()) {
$userIdDisplay .= "&nbsp;[$userManageSystemText]";
}
if (($user["privacy"] != "strong") OR (user_can('prfmanage')) || $CURUSER['id'] == $user['id']){ if (($user["privacy"] != "strong") OR (user_can('prfmanage')) || $CURUSER['id'] == $user['id']){
//Xia Zuojie: Taste compatibility is extremely slow. It can takes thounsands of datebase queries. It is disabled until someone makes it fast. //Xia Zuojie: Taste compatibility is extremely slow. It can takes thounsands of datebase queries. It is disabled until someone makes it fast.
/* /*
@@ -188,7 +194,7 @@ if (($user["privacy"] != "strong") OR (user_can('prfmanage')) || $CURUSER['id']
print("<tr><td class=rowhead width=13%>".$lang_userdetails['row_compatibility']."</td><td class=rowfollow align=left width=87%>". $compatibility_info ."</td></tr>\n"); print("<tr><td class=rowhead width=13%>".$lang_userdetails['row_compatibility']."</td><td class=rowfollow align=left width=87%>". $compatibility_info ."</td></tr>\n");
} }
*/ */
tr_small($lang_userdetails['text_user_id'], $user['id'], 1); tr_small($lang_userdetails['text_user_id'], $userIdDisplay, 1);
if ($CURUSER['id'] == $user['id'] || user_can('viewinvite')){ if ($CURUSER['id'] == $user['id'] || user_can('viewinvite')){
if ($user["invites"] <= 0) if ($user["invites"] <= 0)
tr_small($lang_userdetails['row_invitation'], $lang_userdetails['text_no_invitation'], 1); tr_small($lang_userdetails['row_invitation'], $lang_userdetails['text_no_invitation'], 1);
@@ -600,15 +606,11 @@ JS;
if (user_can('cruprfmanage')) if (user_can('cruprfmanage'))
{ {
// tr($lang_userdetails['row_amount_uploaded'], "<input disabled type=\"text\" size=\"60\" name=\"uploaded\" value=\"" . htmlspecialchars($user['uploaded']) . "\" /><input type=\"hidden\" name=\"ori_uploaded\" value=\"" . htmlspecialchars($user['uploaded']) . "\" />".$lang_userdetails['change_field_value_migrated'], 1); $migratedHelp = sprintf($lang_userdetails['change_field_value_migrated'], $userManageSystemText);
// tr($lang_userdetails['row_amount_downloaded'], "<input disabled type=\"text\" size=\"60\" name=\"downloaded\" value=\"" .htmlspecialchars($user['downloaded']) . "\" /><input type=\"hidden\" name=\"ori_downloaded\" value=\"" .htmlspecialchars($user['downloaded']) . "\" />".$lang_userdetails['change_field_value_migrated'], 1); tr($lang_userdetails['row_amount_uploaded'], "<input disabled type=\"text\" size=\"60\" name=\"uploaded\" value=\"" . htmlspecialchars($user['uploaded']) . "\" /><input type=\"hidden\" name=\"ori_uploaded\" value=\"" . htmlspecialchars($user['uploaded']) . "\" />".$migratedHelp, 1);
// tr($lang_userdetails['row_seeding_karma'], "<input disabled type=\"text\" size=\"60\" name=\"bonus\" value=\"" .number_format($user['seedbonus'], 1) . "\" /><input type=\"hidden\" name=\"ori_bonus\" value=\"" .number_format($user['seedbonus'], 1) . "\" />".$lang_userdetails['change_field_value_migrated'], 1); tr($lang_userdetails['row_amount_downloaded'], "<input disabled type=\"text\" size=\"60\" name=\"downloaded\" value=\"" .htmlspecialchars($user['downloaded']) . "\" /><input type=\"hidden\" name=\"ori_downloaded\" value=\"" .htmlspecialchars($user['downloaded']) . "\" />".$migratedHelp, 1);
// tr($lang_userdetails['row_invites'], "<input disabled type=\"text\" size=\"60\" name=\"invites\" value=\"" .htmlspecialchars($user['invites']) . "\" />".$lang_userdetails['change_field_value_migrated'], 1); tr($lang_userdetails['row_seeding_karma'], "<input disabled type=\"text\" size=\"60\" name=\"bonus\" value=\"" .number_format($user['seedbonus'], 1) . "\" /><input type=\"hidden\" name=\"ori_bonus\" value=\"" .number_format($user['seedbonus'], 1) . "\" />".$migratedHelp, 1);
tr($lang_userdetails['row_invites'], "<input disabled type=\"text\" size=\"60\" name=\"invites\" value=\"" .htmlspecialchars($user['invites']) . "\" />".$migratedHelp, 1);
tr($lang_userdetails['row_amount_uploaded'], "<input type=\"text\" size=\"60\" name=\"uploaded\" value=\"" . htmlspecialchars($user['uploaded']) . "\" /><input type=\"hidden\" name=\"ori_uploaded\" value=\"" . htmlspecialchars($user['uploaded']) . "\" />", 1);
tr($lang_userdetails['row_amount_downloaded'], "<input type=\"text\" size=\"60\" name=\"downloaded\" value=\"" .htmlspecialchars($user['downloaded']) . "\" /><input type=\"hidden\" name=\"ori_downloaded\" value=\"" .htmlspecialchars($user['downloaded']) . "\" />", 1);
tr($lang_userdetails['row_seeding_karma'], "<input type=\"text\" size=\"60\" name=\"bonus\" value=\"" .number_format($user['seedbonus'], 1) . "\" /><input type=\"hidden\" name=\"ori_bonus\" value=\"" .number_format($user['seedbonus'], 1) . "\" />", 1);
tr($lang_userdetails['row_invites'], "<input type=\"text\" size=\"60\" name=\"invites\" value=\"" .htmlspecialchars($user['invites']) . "\" />", 1);
} }
tr($lang_userdetails['row_passkey'], "<input name=\"resetkey\" value=\"yes\" type=\"checkbox\" />".$lang_userdetails['checkbox_reset_passkey'], 1); tr($lang_userdetails['row_passkey'], "<input name=\"resetkey\" value=\"yes\" type=\"checkbox\" />".$lang_userdetails['checkbox_reset_passkey'], 1);
+1 -1
View File
@@ -264,7 +264,7 @@ then it's label<%artist.label%>it's value<%artist.value%>",
'image_help' => 'The name of image file. Allowed Characters: [a-z] (in lower case), [0-9], [_./].', 'image_help' => 'The name of image file. Allowed Characters: [a-z] (in lower case), [0-9], [_./].',
'icon_id' => 'Category icon pack', 'icon_id' => 'Category icon pack',
'mode' => 'SearchBox', 'mode' => 'SearchBox',
'mode_help' => 'Leave blank to indicate that it applies to all sections', 'mode_help' => 'Leave blank to indicate that it applies to all SearchBox',
], ],
], ],
'icon' => [ 'icon' => [
+2 -2
View File
@@ -271,8 +271,8 @@ return [
'image' => '图片文件名', 'image' => '图片文件名',
'image_help' => '图片文件的名字。允许的字符:[a-z](小写),[0-9][_./]。', 'image_help' => '图片文件的名字。允许的字符:[a-z](小写),[0-9][_./]。',
'icon_id' => '分类图标', 'icon_id' => '分类图标',
'mode' => '分', 'mode' => '分类模式',
'mode_help' => '留空表示适用于全部分', 'mode_help' => '留空表示适用于全部分类模式',
], ],
], ],
'icon' => [ 'icon' => [
+2 -2
View File
@@ -261,8 +261,8 @@ return [
'image' => '圖片文件名', 'image' => '圖片文件名',
'image_help' => '圖片文件的名字。允許的字符:[a-z](小寫),[0-9][_./]。', 'image_help' => '圖片文件的名字。允許的字符:[a-z](小寫),[0-9][_./]。',
'icon_id' => '分類圖標', 'icon_id' => '分類圖標',
'mode' => '分', 'mode' => '分類模式',
'mode_help' => '留空表示適用於全部分', 'mode_help' => '留空表示適用於全部分類模式',
], ],
], ],
'icon' => [ 'icon' => [