Update collection regex pages

This commit is contained in:
DariusIII
2025-04-15 12:43:43 +02:00
parent 7f7783a27e
commit 3db7d3d78d
3 changed files with 479 additions and 170 deletions
@@ -1,80 +1,155 @@
<h1>{$title}</h1>
{if isset($error) && $error != ''}
<div class="error">{$error}</div>
{/if}
<div class="card card-body">
<a class="btn btn-success" href="{{url("/admin/collection_regexes-list")}}"><i
class="fa fa-arrow-left"></i> Go back</a>
<form action="collection_regexes-edit?action=submit" method="POST">
{{csrf_field()}}
<table class="input data table table-striped responsive-utilities jambo-table">
<tr>
<td><label for="group_regex">Group:</label></td>
<td>
<input type="hidden" name="id" value="{$regex.id}"/>
<input type="text" id="group_regex" name="group_regex" value="{$regex.group_regex|escape:html}"/>
<div class="hint">
Regex to match against a group or multiple groups.<br/>
Delimiters are already added, and PCRE_CASELESS is added after for case insensitivity.
An example of matching a single group: alt\.binaries\.example<br/>
An example of matching multiple groups: alt\.binaries.*
</div>
</td>
</tr>
<tr>
<td><label for="regex">Regex:</label></td>
<td>
<textarea id="regex" name="regex">{$regex.regex|escape:html}</textarea>
<div class="hint">
The regex to use when matching (grouping) collections.<br/>
The regex delimiters are not added, you MUST add them. See <a
href="http://php.net/manual/en/regexp.reference.delimiters">this</a> page.<br/>
To make the regex case insensitive, add i after the last delimiter.<br/>
You MUST include at least one regex capture group.<br/>
You MUST name your regex capture groups (the ones you want to be included).<br/>
A string will be created from your matched capture groups.<br/>
The string will form part of a "collection hash".<br/>
The collection hash is used to group many parts together, to form the finalized release.<br/>
The usenet group and name of the poster are added automatically when hashing.<br/>
Capture groups are sorted alphabetically (by capture group name) when concatenating the
string.<br/>
</div>
</td>
</tr>
<tr>
<td><label for="description">Description:</label></td>
<td>
<textarea id="description" name="description">{$regex.description|escape:html}</textarea>
<div class="hint">
Description for this regex.<br/>
You can include an example usenet subject this regex would match on.
</div>
</td>
</tr>
<tr>
<td><label for="ordinal">Ordinal:</label></td>
<td>
<input class="ordinal" id="ordinal" name="ordinal" type="text" value="{$regex.ordinal}"/>
<div class="hint">
The order to run this regex in.<br/>
Must be a number, 0 or higher.<br/>
If multiple regex have the same ordinal, MySQL will randomly sort them.
</div>
</td>
</tr>
<tr>
<td><label for="status">Active:</label></td>
<td>
{html_radios id="status" name='status' values=$status_ids output=$status_names selected=$regex.status separator='<br />'}
<div class="hint">Only active regex are used during the collection matching process.</div>
</td>
</tr>
<tr>
<td></td>
<td>
<input class="btn btn-success" type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h4 class="mb-0">{$title}</h4>
<a href="{{url("/admin/collection_regexes-list")}}" class="btn btn-outline-secondary">
<i class="fa fa-arrow-left me-2"></i>Back to Regexes List
</a>
</div>
</div>
<div class="card-body">
{if isset($error) && $error != ''}
<div class="alert alert-danger">{$error}</div>
{/if}
<form action="{{url("/admin/collection_regexes-edit?action=submit")}}" method="POST" id="regexForm">
{{csrf_field()}}
<input type="hidden" name="id" value="{$regex.id}"/>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="group_regex" class="form-label fw-bold">Group:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-layer-group"></i></span>
<input type="text" id="group_regex" name="group_regex" class="form-control" value="{$regex.group_regex|escape:html}"/>
</div>
<small class="text-muted">
Regex to match against a group or multiple groups.<br/>
Delimiters are already added, and PCRE_CASELESS is added after for case insensitivity.<br/>
An example of matching a single group: alt\.binaries\.example<br/>
An example of matching multiple groups: alt\.binaries.*
</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="regex" class="form-label fw-bold">Regex:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-code"></i></span>
<textarea id="regex" name="regex" class="form-control" rows="6">{$regex.regex|escape:html}</textarea>
</div>
<small class="text-muted">
The regex to use when matching (grouping) collections.<br/>
The regex delimiters are not added, you MUST add them. See <a href="http://php.net/manual/en/regexp.reference.delimiters">this</a> page.<br/>
To make the regex case insensitive, add i after the last delimiter.<br/>
You MUST include at least one regex capture group.<br/>
You MUST name your regex capture groups (the ones you want to be included).<br/>
A string will be created from your matched capture groups.<br/>
The string will form part of a "collection hash".<br/>
The collection hash is used to group many parts together, to form the finalized release.<br/>
The usenet group and name of the poster are added automatically when hashing.<br/>
Capture groups are sorted alphabetically (by capture group name) when concatenating the string.
</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="description" class="form-label fw-bold">Description:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-align-left"></i></span>
<textarea id="description" name="description" class="form-control" rows="4">{$regex.description|escape:html}</textarea>
</div>
<small class="text-muted">
Description for this regex.<br/>
You can include an example usenet subject this regex would match on.
</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="ordinal" class="form-label fw-bold">Ordinal:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-sort-numeric-up"></i></span>
<input class="form-control" id="ordinal" name="ordinal" type="number" value="{$regex.ordinal}"/>
</div>
<small class="text-muted">
The order to run this regex in.<br/>
Must be a number, 0 or higher.<br/>
If multiple regex have the same ordinal, MySQL will randomly sort them.
</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Active:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="form-check form-check-inline">
{html_radios id="status" name='status' values=$status_ids output=$status_names selected=$regex.status separator='</div><div class="form-check form-check-inline">'}
</div>
<small class="text-muted">Only active regex are used during the collection matching process.</small>
</div>
</div>
</form>
</div>
<div class="card-footer">
<div class="d-flex justify-content-between">
<a href="{{url("/admin/collection_regexes-list")}}" class="btn btn-outline-secondary">
<i class="fa fa-times me-2"></i>Cancel
</a>
<button type="submit" form="regexForm" class="btn btn-success">
<i class="fa fa-save me-2"></i>Save Regex
</button>
</div>
</div>
</div>
<style>
{literal}
/* Textarea styling */
textarea {
min-height: 80px;
}
#regex {
font-family: monospace;
}
/* Form styling improvements */
.form-label {
margin-bottom: 0.5rem;
}
/* Number input styling */
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Improve input focus states */
.form-control:focus,
.form-select:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
{/literal}
</style>
@@ -1,50 +1,173 @@
<h1>{$title}</h1>
<div class="card card-body">
<p>This page lists regex used for grouping usenet collections.</p>
<div id="message"></div>
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h4 class="mb-0">{$title}</h4>
<a href="{{url("/admin/collection_regexes-edit")}}" class="btn btn-primary">
<i class="fa fa-plus me-2"></i>Add New Regex
</a>
</div>
</div>
<form name="groupsearch" action="" style="margin-bottom:5px;">
{{csrf_field()}}
<label for="group">Search a group:</label>
<input id="group" type="text" name="group" value="{$group}" size="15"/>
&nbsp;&nbsp;
<input class="btn btn-success" type="submit" value="Go"/>
</form>
{if $regex}
<div>{$regex->onEachSide(5)->links()}</div>
<table style="margin-top:10px;" class="data table table-striped responsive-utilities jambo-table Sortable">
<tr>
<th style="width:20px;">id</th>
<th>group</th>
<th style="width:25px;">edit</th>
<th>description</th>
<th style="width:40px;">delete</th>
<th>ordinal</th>
<th>status</th>
<th>regex</th>
</tr>
{foreach from=$regex item=row}
<tr id="row-{$row.id}" class="{cycle values=",alt"}">
<td>{$row.id}</td>
<td>{$row.group_regex}</td>
<td title="Edit this regex"><a
href="{{url("/admin/collection_regexes-edit?id={$row.id}")}}">Edit</a></td>
<td>{$row.description|truncate:50:"...":true}</td>
<td title="Delete this regex"><a href="javascript:ajax_collection_regex_delete({$row.id})"
onclick="return confirm('Are you sure? This will delete the regex from this list.');">Delete</a>
</td>
<td>{$row.ordinal}</td>
{if $row.status==1}
<td style="color:#00CC66">Active</td>
{else}
<td style="color:#FF0000">Disabled</td>
{/if}
<td title="Edit this regex"><a
href="{{url("/admin/collection_regexes-edit?id={$row.id}")}}">{$row.regex|escape:html|truncate:50:"...":true}</a>
</td>
</tr>
{/foreach}
</table>
<div style="margin-top: 15px">{$regex->onEachSide(5)->links()}</div>
{/if}
</div>
<div class="card-body">
<p class="mb-4">This page lists regex used for grouping usenet collections.</p>
<div id="message"></div>
<form name="groupsearch" action="" class="mb-4">
{{csrf_field()}}
<div class="row">
<div class="col-md-6 col-lg-4">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-search"></i></span>
<input id="group" type="text" name="group" class="form-control" value="{$group}" placeholder="Search a group...">
<button class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</div>
</form>
{if $regex}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th style="width:60px;">ID</th>
<th>Group</th>
<th>Description</th>
<th>Ordinal</th>
<th>Status</th>
<th>Regex</th>
<th style="width:120px;">Actions</th>
</tr>
</thead>
<tbody>
{foreach from=$regex item=row}
<tr id="row-{$row.id}">
<td>{$row.id}</td>
<td>{$row.group_regex}</td>
<td>{$row.description|truncate:50:"...":true}</td>
<td>{$row.ordinal}</td>
<td>
{if $row.status==1}
<span class="badge bg-success">Active</span>
{else}
<span class="badge bg-danger">Disabled</span>
{/if}
</td>
<td><code>{$row.regex|escape:html|truncate:50:"...":true}</code></td>
<td>
<div class="btn-group btn-group-sm">
<a href="{{url("/admin/collection_regexes-edit?id={$row.id}")}}" class="btn btn-outline-primary" title="Edit this regex">
<i class="fa fa-edit"></i>
</a>
<a href="javascript:ajax_collection_regex_delete({$row.id})"
onclick="return confirm('Are you sure? This will delete the regex from this list.');"
class="btn btn-outline-danger"
title="Delete this regex">
<i class="fa fa-trash"></i>
</a>
</div>
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
<div class="d-flex justify-content-center mt-4">
{$regex->onEachSide(5)->links()}
</div>
{else}
<div class="alert alert-info">
<i class="fa fa-info-circle me-2"></i>No regex rules found matching your criteria.
</div>
{/if}
</div>
<div class="card-footer">
<div class="d-flex justify-content-end">
<a href="{{url("/admin/collection_regexes-edit")}}" class="btn btn-primary">
<i class="fa fa-plus me-2"></i>Add New Regex
</a>
</div>
</div>
</div>
<style>
{literal}
/* Improve table appearance */
.table {
margin-bottom: 0;
}
.table th {
background-color: #f8f9fa;
border-top: none;
}
.table code {
font-size: 0.875rem;
background-color: rgba(0,0,0,0.05);
padding: 0.2em 0.4em;
}
/* Pagination styling */
.pagination {
margin-bottom: 0;
}
/* Responsive adjustments */
@media (max-width: 767.98px) {
.btn-group-sm .btn {
padding: 0.25rem 0.5rem;
}
}
/* Badge styling */
.badge {
font-weight: 500;
font-size: 0.75rem;
padding: 0.35em 0.65em;
}
{/literal}
</style>
<script>
{literal}
function ajax_collection_regex_delete(id) {
if (confirm('Are you sure you want to delete this regex?')) {
fetch(`/admin/collection_regexes-delete?id=${id}`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('input[name="_token"]').value,
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById(`row-${id}`).remove();
document.getElementById('message').innerHTML =
`<div class="alert alert-success alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<strong>Success!</strong> Regex deleted successfully.
</div>`;
} else {
document.getElementById('message').innerHTML =
`<div class="alert alert-danger alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<strong>Error!</strong> ${data.error || 'Failed to delete regex.'}
</div>`;
}
})
.catch(error => {
document.getElementById('message').innerHTML =
`<div class="alert alert-danger alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<strong>Error!</strong> An unexpected error occurred.
</div>`;
});
}
return false;
}
{/literal}
</script>
@@ -1,45 +1,156 @@
<h1>{$title}</h1>
<div class="card card-body">
<p>This page is used for testing regex for grouping usenet collections.<br/>Enter the group name to test and a
regex. Limit is how many collections to show max on the page, 0 for no limit(slow).</p>
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h4 class="mb-0">{$title}</h4>
<a href="{{url("/admin/collection_regexes-list")}}" class="btn btn-outline-secondary">
<i class="fa fa-arrow-left me-2"></i>Back to Regexes List
</a>
</div>
</div>
<form name="search" action="" method="post" style="margin-bottom:5px;">
{{csrf_field()}}
<label for="group" style="padding-right:1px">Group:</label>
<input id="group" type="text" name="group" value="{$group|htmlentities}" size="20"/><br/>
<label for="regex" style="padding-right:1px">Regex:</label>
<input id="regex" type="text" name="regex" value="{$regex|htmlentities}" size="100"/><br/>
<label for="limit" style="padding-right:7px">Limit:</label>
<input id="limit" type="text" name="limit" value="{$limit}" size="8"/><br/>
<input class="btn btn-success" type="submit" value="Test"/>
</form>
{if isset($data)}
<div class="card-body">
<p class="mb-4">This page is used for testing regex for grouping usenet collections. Enter the group name to test and a regex. Limit is how many collections to show max on the page, 0 for no limit (slow).</p>
{foreach from=$data key=hash item=collection}
<table style="margin-top:10px;" class="data table table-striped responsive-utilities jambo-table">
<tr>
<th>{$hash}<br/>Current Files: {count($collection)}</th>
</tr>
</table>
<table style="margin-top:10px;" class="data table table-striped responsive-utilities jambo-table Sortable">
<tr>
<th>name</th>
<th>current parts</th>
<th>total parts</th>
<th>poster</th>
<th>old hash</th>
</tr>
{foreach from=$collection item=row}
<tr id="row-{$row.new_collection_hash}" class="{cycle values=",alt"}">
<td>{$row.file_name}</td>
<td>{$row.file_current_parts}</td>
<td>{$row.file_total_parts}</td>
<td>{$row.collection_poster}</td>
<td>{$row.old_collection_hash}</td>
</tr>
{/foreach}
</table>
{/foreach}
{/if}
<form name="search" action="" method="post" id="regexTestForm">
{{csrf_field()}}
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="group" class="form-label fw-bold">Group:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-layer-group"></i></span>
<input id="group" type="text" name="group" class="form-control" value="{$group|htmlentities}"/>
</div>
<small class="text-muted">The newsgroup to test against (e.g., alt.binaries.example)</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="regex" class="form-label fw-bold">Regex:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-code"></i></span>
<input id="regex" type="text" name="regex" class="form-control" value="{$regex|htmlentities}"/>
</div>
<small class="text-muted">The regex pattern to test (include delimiters and modifiers)</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="limit" class="form-label fw-bold">Limit:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-list-ol"></i></span>
<input id="limit" type="number" name="limit" class="form-control" value="{$limit}"/>
</div>
<small class="text-muted">Maximum number of collections to display (use 0 for no limit, but this may be slow)</small>
</div>
</div>
</form>
</div>
<div class="card-footer">
<div class="d-flex justify-content-end">
<button type="submit" form="regexTestForm" class="btn btn-success">
<i class="fa fa-vial me-2"></i>Test Regex
</button>
</div>
</div>
</div>
{if isset($data)}
<div class="mt-4">
{foreach from=$data key=hash item=collection}
<div class="card mb-4">
<div class="card-header bg-light">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">Collection Hash: {$hash}</h5>
<span class="badge bg-primary">{count($collection)} Files</span>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped table-hover mb-0">
<thead>
<tr>
<th>Name</th>
<th>Current Parts</th>
<th>Total Parts</th>
<th>Poster</th>
<th>Old Hash</th>
</tr>
</thead>
<tbody>
{foreach from=$collection item=row}
<tr id="row-{$row.new_collection_hash}">
<td>{$row.file_name}</td>
<td>{$row.file_current_parts}</td>
<td>{$row.file_total_parts}</td>
<td>{$row.collection_poster}</td>
<td><code>{$row.old_collection_hash}</code></td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
</div>
{/foreach}
</div>
{/if}
<style>
{literal}
/* Form styling improvements */
.form-label {
margin-bottom: 0.5rem;
}
/* Number input styling */
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Improve table appearance */
.table {
margin-bottom: 0;
}
.table th {
background-color: #f8f9fa;
border-top: none;
}
.table code {
font-size: 0.875rem;
background-color: rgba(0,0,0,0.05);
padding: 0.2em 0.4em;
}
/* Responsive adjustments */
@media (max-width: 767.98px) {
.card-footer .btn {
padding: 0.375rem 0.75rem;
}
}
/* Improve input focus states */
.form-control:focus,
.form-select:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
{/literal}
</style>