Added metadata status to item record view.
This commit is contained in:
25
composer.lock
generated
25
composer.lock
generated
@@ -3588,12 +3588,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "45b01f4e60c350f72a8697056674e449e053935a"
|
||||
"reference": "59be420c5cdc0c3c9cdae31804d32fefb515a918"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/45b01f4e60c350f72a8697056674e449e053935a",
|
||||
"reference": "45b01f4e60c350f72a8697056674e449e053935a",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/59be420c5cdc0c3c9cdae31804d32fefb515a918",
|
||||
"reference": "59be420c5cdc0c3c9cdae31804d32fefb515a918",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
@@ -3611,7 +3611,7 @@
|
||||
"airesvsg/acf-to-rest-api": "<=3.1",
|
||||
"akaunting/akaunting": "<2.1.13",
|
||||
"akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53",
|
||||
"alextselegidis/easyappointments": "<=1.5",
|
||||
"alextselegidis/easyappointments": "<=1.5.1",
|
||||
"alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1",
|
||||
"amazing/media2click": ">=1,<1.3.3",
|
||||
"ameos/ameos_tarteaucitron": "<1.2.23",
|
||||
@@ -3715,7 +3715,7 @@
|
||||
"contao/managed-edition": "<=1.5",
|
||||
"corveda/phpsandbox": "<1.3.5",
|
||||
"cosenary/instagram": "<=2.3",
|
||||
"craftcms/cms": "<=4.14.14|>=5,<=5.6.16",
|
||||
"craftcms/cms": "<4.15.3|>=5,<5.7.5",
|
||||
"croogo/croogo": "<4",
|
||||
"cuyz/valinor": "<0.12",
|
||||
"czim/file-handling": "<1.5|>=2,<2.3",
|
||||
@@ -3948,6 +3948,7 @@
|
||||
"klaviyo/magento2-extension": ">=1,<3",
|
||||
"knplabs/knp-snappy": "<=1.4.2",
|
||||
"kohana/core": "<3.3.3",
|
||||
"koillection/koillection": "<1.6.12",
|
||||
"krayin/laravel-crm": "<=1.3",
|
||||
"kreait/firebase-php": ">=3.2,<3.8.1",
|
||||
"kumbiaphp/kumbiapp": "<=1.1.1",
|
||||
@@ -3966,7 +3967,7 @@
|
||||
"latte/latte": "<2.10.8",
|
||||
"lavalite/cms": "<=9|==10.1",
|
||||
"lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5",
|
||||
"league/commonmark": "<2.6",
|
||||
"league/commonmark": "<2.7",
|
||||
"league/flysystem": "<1.1.4|>=2,<2.1.1",
|
||||
"league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3",
|
||||
"leantime/leantime": "<3.3",
|
||||
@@ -4066,9 +4067,9 @@
|
||||
"nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1",
|
||||
"october/backend": "<1.1.2",
|
||||
"october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1",
|
||||
"october/october": "<=3.6.4",
|
||||
"october/october": "<3.7.5",
|
||||
"october/rain": "<1.0.472|>=1.1,<1.1.2",
|
||||
"october/system": "<1.0.476|>=1.1,<1.1.12|>=2,<2.2.34|>=3,<3.5.15",
|
||||
"october/system": "<3.7.5",
|
||||
"oliverklee/phpunit": "<3.5.15",
|
||||
"omeka/omeka-s": "<4.0.3",
|
||||
"onelogin/php-saml": "<2.10.4",
|
||||
@@ -4196,8 +4197,8 @@
|
||||
"serluck/phpwhois": "<=4.2.6",
|
||||
"sfroemken/url_redirect": "<=1.2.1",
|
||||
"sheng/yiicms": "<1.2.1",
|
||||
"shopware/core": "<6.5.8.17-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
|
||||
"shopware/platform": "<6.5.8.17-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
|
||||
"shopware/core": "<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
|
||||
"shopware/platform": "<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
|
||||
"shopware/production": "<=6.3.5.2",
|
||||
"shopware/shopware": "<=5.7.17",
|
||||
"shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev",
|
||||
@@ -4240,7 +4241,7 @@
|
||||
"slim/slim": "<2.6",
|
||||
"slub/slub-events": "<3.0.3",
|
||||
"smarty/smarty": "<4.5.3|>=5,<5.1.1",
|
||||
"snipe/snipe-it": "<=7.0.13",
|
||||
"snipe/snipe-it": "<8.1",
|
||||
"socalnick/scn-social-auth": "<1.15.2",
|
||||
"socialiteproviders/steam": "<1.1",
|
||||
"spatie/browsershot": "<5.0.5",
|
||||
@@ -4510,7 +4511,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-01T20:05:59+00:00"
|
||||
"time": "2025-05-08T20:06:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
||||
@@ -320,9 +320,25 @@
|
||||
<span class="icon">
|
||||
<i class="fas" :class="{ 'fa-arrow-up': item?._toggle, 'fa-arrow-down': !item?._toggle }"/>
|
||||
</span>
|
||||
|
||||
<i class="fas" :class="{
|
||||
'fa-spinner fa-spin': undefined === item?.validated,
|
||||
'fa-check has-text-success': true === item?.validated,
|
||||
'fa-xmark has-text-danger': false === item?.validated,
|
||||
}"/>
|
||||
Metadata via
|
||||
</div>
|
||||
<div class="card-header-icon">
|
||||
<div class="field is-grouped">
|
||||
<div class="control" v-if="false === item?.validated">
|
||||
<NuxtLink @click="deleteMetadata(key)">
|
||||
<span class="icon-text has-text-danger">
|
||||
<span class="icon"><i class="fas fa-trash"/></span>
|
||||
<span>Delete</span>
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="control">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-server"/></span>
|
||||
<span>
|
||||
@@ -330,10 +346,14 @@
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="card-content" v-if="item?._toggle">
|
||||
<div class="columns is-multiline is-mobile">
|
||||
|
||||
<div class="column is-12" v-if="false === item?.validated && item.validated_message">
|
||||
<span class="has-text-danger">({{ item.validated_message }})</span>
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-passport"/></span>
|
||||
@@ -629,6 +649,8 @@ const loadContent = async (id) => {
|
||||
|
||||
useHead({title: `History : ${makeName(json) ?? id}`})
|
||||
await loadImage()
|
||||
await nextTick();
|
||||
await validateItem()
|
||||
}
|
||||
|
||||
watch(breakpoints.active(), async () => await loadImage())
|
||||
@@ -720,6 +742,48 @@ const toggleWatched = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const validateItem = async () => {
|
||||
try {
|
||||
const response = await request(`/history/${id}/validate`)
|
||||
|
||||
if (!response.ok) {
|
||||
return
|
||||
}
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
for (const [backend, item] of Object.entries(json)) {
|
||||
if (data.value.metadata[backend] === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
data.value.metadata[backend]['validated'] = item.status
|
||||
data.value.metadata[backend]['validated_message'] = item.message
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
const deleteMetadata = async backend => {
|
||||
if (!confirm(`Remove metadata from '${backend}'?`)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await request(`/history/${id}/metadata/${backend}`, {method: 'DELETE'})
|
||||
|
||||
if (200 !== response.status) {
|
||||
const json = await parse_api_response(response)
|
||||
notification('error', 'Error', `${json.error.code}: ${json.error.message}`)
|
||||
return
|
||||
}
|
||||
|
||||
notification('success', 'Success!', `Deleted '${backend}' metadata.`)
|
||||
await loadContent(id);
|
||||
} catch (e) {
|
||||
notification('error', 'Error', `Request error. ${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
const getMoment = (time) => time.toString().length < 13 ? moment.unix(time) : moment(time)
|
||||
const headerTitle = computed(() => isLoading.value ? id : makeName(data.value))
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use App\Libs\Attributes\Route\Get;
|
||||
use App\Libs\Attributes\Route\Route;
|
||||
use App\Libs\Container;
|
||||
use App\Libs\DataUtil;
|
||||
use App\Libs\Entity\StateEntity;
|
||||
use App\Libs\Entity\StateInterface as iState;
|
||||
use App\Libs\Enums\Http\Method;
|
||||
use App\Libs\Enums\Http\Status;
|
||||
@@ -19,12 +20,15 @@ use App\Libs\Exceptions\RuntimeException;
|
||||
use App\Libs\Guid;
|
||||
use App\Libs\Mappers\Import\DirectMapper;
|
||||
use App\Libs\Mappers\ImportInterface as iImport;
|
||||
use App\Libs\Options;
|
||||
use App\Libs\Traits\APITraits;
|
||||
use DateInterval;
|
||||
use JsonException;
|
||||
use PDO;
|
||||
use Psr\Http\Message\ResponseInterface as iResponse;
|
||||
use Psr\Http\Message\ServerRequestInterface as iRequest;
|
||||
use Psr\Log\LoggerInterface as iLogger;
|
||||
use Psr\SimpleCache\CacheInterface as iCache;
|
||||
use SplFileInfo;
|
||||
use Throwable;
|
||||
|
||||
@@ -689,6 +693,105 @@ final class Index
|
||||
return $this->read($request, $id);
|
||||
}
|
||||
|
||||
#[Get(self::URL . '/{id:\d+}/validate[/]', name: 'history.validate')]
|
||||
public function validate_item(iCache $cache, iRequest $request, string $id): iResponse
|
||||
{
|
||||
try {
|
||||
$userContext = $this->getUserContext(request: $request, mapper: $this->mapper, logger: $this->logger);
|
||||
} catch (RuntimeException $e) {
|
||||
return api_error($e->getMessage(), Status::NOT_FOUND);
|
||||
}
|
||||
|
||||
$entity = Container::get(iState::class)::fromArray([iState::COLUMN_ID => $id]);
|
||||
|
||||
if (null === ($item = $userContext->db->get($entity))) {
|
||||
return api_error('Item Not found', Status::NOT_FOUND);
|
||||
}
|
||||
|
||||
$cacheKey = r('validate_item_{id}_{user}_{backends}', [
|
||||
'id' => $item->id,
|
||||
'user' => $userContext->name,
|
||||
'backends' => md5(implode(',', array_keys($item->getMetadata()))),
|
||||
]);
|
||||
|
||||
try {
|
||||
if (null !== ($cached = $cache->get($cacheKey))) {
|
||||
return api_response(Status::OK, $cached, headers: ['X-Cache' => 'HIT']);
|
||||
}
|
||||
} catch (Throwable) {
|
||||
// Ignore cache errors.
|
||||
}
|
||||
|
||||
$validation = [];
|
||||
|
||||
foreach ($item->getMetadata() as $name => $metadata) {
|
||||
$id = ag($metadata, StateEntity::COLUMN_ID, null);
|
||||
$validation[$name] = [
|
||||
'id' => $id,
|
||||
'status' => false,
|
||||
'message' => 'Item not found.',
|
||||
];
|
||||
|
||||
if (null === $userContext->config->get($name)) {
|
||||
$validation[$name]['message'] = 'Backend not found.';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null === $id) {
|
||||
$validation[$name]['message'] = 'Item ID is missing.';
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->getClient(name: $name, userContext: $userContext);
|
||||
$item = $client->getMetadata($id, [Options::NO_LOGGING => true]);
|
||||
if (count($item) > 0) {
|
||||
$validation[$name]['status'] = true;
|
||||
$validation[$name]['message'] = 'Item found.';
|
||||
} else {
|
||||
$validation[$name]['status'] = false;
|
||||
$validation[$name]['message'] = 'Item not found.';
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$validation[$name]['message'] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$cache->set($cacheKey, $validation, new DateInterval('PT10M'));
|
||||
} catch (Throwable) {
|
||||
// Ignore cache errors.
|
||||
}
|
||||
|
||||
return api_response(Status::OK, $validation, headers: ['X-Cache' => 'MISS']);
|
||||
}
|
||||
|
||||
#[Delete(self::URL . '/{id:\d+}/metadata/{backend}[/]', name: 'history.metadata.delete')]
|
||||
public function delete_item_metadata(iCache $cache, iRequest $request, string $id, string $backend): iResponse
|
||||
{
|
||||
try {
|
||||
$userContext = $this->getUserContext(request: $request, mapper: $this->mapper, logger: $this->logger);
|
||||
} catch (RuntimeException $e) {
|
||||
return api_error($e->getMessage(), Status::NOT_FOUND);
|
||||
}
|
||||
|
||||
$entity = Container::get(iState::class)::fromArray([iState::COLUMN_ID => $id]);
|
||||
|
||||
if (null === ($item = $userContext->db->get($entity))) {
|
||||
return api_error('Item Not found', Status::NOT_FOUND);
|
||||
}
|
||||
|
||||
if (null === ($item->getMetadata($backend) ?? null)) {
|
||||
return api_error('Item metadata not found.', Status::NOT_FOUND);
|
||||
}
|
||||
|
||||
$item->metadata = ag_delete($item->getMetadata(), $backend);
|
||||
|
||||
$userContext->db->update($item);
|
||||
|
||||
return $this->read($request, $id);
|
||||
}
|
||||
|
||||
#[Get(self::URL . '/{id:\d+}/images/{type:poster|background}[/]', name: 'history.item.images')]
|
||||
public function images(iRequest $request, string $id, string $type): iResponse
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user