Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add archival possibilities #4403

Merged
merged 4 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/classes/DisplayParams.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ class DisplayParams
public array $metadataValuePath = array();

public array $metadataValue = array();
// end metadata stuff

public bool $includeArchived = false;

private array $metadataHaving = array();
// end metadata stuff

public function __construct(Users $Users, private Request $Request, private string $entityType)
{
Expand All @@ -71,6 +73,8 @@ public function __construct(Users $Users, private Request $Request, private stri
$this->orderby = Orderby::tryFrom($Users->userData['orderby']) ?? $this->orderby;
$this->sort = Sort::tryFrom($Users->userData['sort']) ?? $this->sort;
$this->adjust();
// we don't care about the value, so it can be 'on' from a checkbox or 1 or anything really
$this->includeArchived = $this->Request->query->has('archived');
}

public function appendFilterSql(FilterableColumn $column, int $value): void
Expand Down
1 change: 1 addition & 0 deletions src/classes/EntitySqlBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public function getReadSqlBeforeWhere(bool $getTags = true, bool $fullSelect = f
entity.rating,
entity.userid,
entity.locked,
entity.state,
entity.canread,
entity.canwrite,
entity.modified_at,';
Expand Down
4 changes: 4 additions & 0 deletions src/controllers/AbstractEntityController.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public function __construct(protected App $App, protected AbstractEntity $Entity
$Templates = new Templates($this->Entity->Users);
$this->templatesArr = $Templates->Pins->readAllSimple();
}
if ($App->Request->query->has('archived') && $Entity instanceof AbstractConcreteEntity) {
$Entity->Uploads->includeArchived = true;
}

}

public function getResponse(): Response
Expand Down
22 changes: 19 additions & 3 deletions src/models/AbstractEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,12 @@ public function readShow(DisplayParams $displayParams, bool $extended = false):
$sql = $EntitySqlBuilder->getReadSqlBeforeWhere($extended, $extended, $displayParams->hasMetadataSearch);
$teamgroupsOfUser = array_column($this->TeamGroups->readGroupsFromUser(), 'id');

// first where is the state
$sql .= ' WHERE entity.state = :state';
// first WHERE is the state, possibly including archived
$stateSql = 'entity.state = :normal';
if ($displayParams->includeArchived) {
$stateSql = '(entity.state = :normal OR entity.state = :archived)';
}
$sql .= ' WHERE ' . $stateSql;

// add externally added filters
$sql .= $this->filterSql;
Expand Down Expand Up @@ -300,7 +304,10 @@ public function readShow(DisplayParams $displayParams, bool $extended = false):

$req = $this->Db->prepare($sql);
$req->bindParam(':userid', $this->Users->userData['userid'], PDO::PARAM_INT);
$req->bindValue(':state', State::Normal->value, PDO::PARAM_INT);
$req->bindValue(':normal', State::Normal->value, PDO::PARAM_INT);
if ($displayParams->includeArchived) {
$req->bindValue(':archived', State::Archived->value, PDO::PARAM_INT);
}
if ($displayParams->hasMetadataSearch) {
foreach ($displayParams->metadataKey as $i => $v) {
$req->bindParam(sprintf(':metadata_key_%d', $i), $displayParams->metadataKey[$i]);
Expand Down Expand Up @@ -345,6 +352,15 @@ public function patch(Action $action, array $params): array
}
match ($action) {
Action::AccessKey => (new AccessKeyHelper($this))->toggleAccessKey(),
Action::Archive => (
function () {
$targetState = State::Archived;
if ($this->entityData['state'] === $targetState->value) {
$targetState = State::Normal;
}
$this->update(new EntityParams('state', (string) $targetState->value));
}
)(),
Action::Lock => $this->toggleLock(),
Action::Pin => $this->Pins->togglePin(),
Action::UpdateMetadataField => (
Expand Down
47 changes: 27 additions & 20 deletions src/models/Uploads.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use Elabftw\Enums\Storage;
use Elabftw\Exceptions\IllegalActionException;
use Elabftw\Exceptions\ImproperActionException;
use Elabftw\Interfaces\ContentParamsInterface;
use Elabftw\Interfaces\CreateUploadParamsInterface;
use Elabftw\Interfaces\RestInterface;
use Elabftw\Make\MakeThumbnail;
Expand All @@ -45,6 +44,8 @@ class Uploads implements RestInterface

public array $uploadData = array();

public bool $includeArchived = false;

protected Db $Db;

private string $hashAlgorithm = 'sha256';
Expand Down Expand Up @@ -158,17 +159,6 @@ public function create(CreateUploadParamsInterface $params): int
return $this->Db->lastInsertId();
}

public function read(ContentParamsInterface $params): array
{
if ($params->getTarget() === 'all') {
return $this->readAll();
}
if ($params->getTarget() === 'uploadid') {
$this->id = $this->getIdFromLongname($params->getContent());
}
return $this->readOne();
}

/**
* Read from current id
*/
Expand Down Expand Up @@ -204,6 +194,9 @@ public function readBinary(): Response
*/
public function readAll(): array
{
if ($this->includeArchived) {
return $this->readNormalAndArchived();
}
$sql = 'SELECT uploads.*, CONCAT (users.firstname, " ", users.lastname) AS fullname
FROM uploads LEFT JOIN users ON (uploads.userid = users.userid) WHERE item_id = :id AND type = :type AND state = :state';
$req = $this->Db->prepare($sql);
Expand All @@ -218,6 +211,9 @@ public function readAll(): array
public function patch(Action $action, array $params): array
{
$this->canWriteOrExplode();
if ($action === Action::Archive) {
return $this->archive();
}
unset($params['action']);
foreach ($params as $key => $value) {
$this->update(new UploadParams($key, $value));
Expand Down Expand Up @@ -299,13 +295,19 @@ public function getStorageFromLongname(string $longname): int
return (int) $req->fetchColumn();
}

public function getIdFromLongname(string $longname): int
private function readNormalAndArchived(): array
{
$sql = 'SELECT id FROM uploads WHERE long_name = :long_name LIMIT 1';
$sql = 'SELECT uploads.*, CONCAT (users.firstname, " ", users.lastname) AS fullname
FROM uploads LEFT JOIN users ON (uploads.userid = users.userid) WHERE item_id = :id AND type = :type AND (state = :normal OR state = :archived)';
$req = $this->Db->prepare($sql);
$req->bindParam(':long_name', $longname, PDO::PARAM_STR);
$req->bindParam(':id', $this->Entity->id, PDO::PARAM_INT);
$req->bindParam(':type', $this->Entity->type);
$req->bindValue(':normal', State::Normal->value, PDO::PARAM_INT);
$req->bindValue(':archived', State::Archived->value, PDO::PARAM_INT);
$this->Db->execute($req);
return (int) $req->fetchColumn();

return $req->fetchAll();

}

/**
Expand All @@ -314,10 +316,8 @@ public function getIdFromLongname(string $longname): int
*/
private function replace(CreateUpload $params): int
{
$this->canWriteOrExplode();
// read the current one to get the comment
$upload = $this->readOne();
$this->update(new UploadParams('state', (string) State::Archived->value));
// read the current one to get the comment, and at the same time archive it
$upload = $this->archive();

return $this->create(new CreateUpload($params->getFilename(), $params->getFilePath(), $upload['comment']));
}
Expand Down Expand Up @@ -366,6 +366,13 @@ private function canWriteOrExplode(): void
$this->Entity->canOrExplode('write');
}

private function archive(): array
{
$this->canWriteOrExplode();
$this->update(new UploadParams('state', (string) State::Archived->value));
return $this->readOne();
}

/**
* This function will not remove the files but set them to "deleted" state
* A manual purge must be made by sysadmin if they wish to really remove them.
Expand Down
2 changes: 2 additions & 0 deletions src/scss/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ $lightred: #ffc1b7;
$dangerred: #e6614c;
$darkred: #dd1e00;

$warningbg: #fff3cd;

$green: #54aa08;

$lightgold: #eddec6;
Expand Down
5 changes: 4 additions & 1 deletion src/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,10 @@ label {
background-color: $lightred;
border-color: $darkred;
color: $darkred;
transition-duration: 500ms;
}

.hover-warning:hover {
background-color: $warningbg;
}

.hover-action:hover {
Expand Down
4 changes: 4 additions & 0 deletions src/templates/show-item.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
{% if item.timestamped %}
<i style='color:#{{ item.color }}' class='far fa-calendar-check fa-fw'></i>
{% endif %}
{# archived icon #}
{% if item.state == constant('Elabftw\\Enums\\State::Archived').value %}
<i class='fas fa-box-archive fa-fw'></i>
{% endif %}
{# category #}
<span class='item-category'><a style='color:#{{ item.color }}' href='?cat={{ item.category_id }}'>{{ item.category|raw }}</a></span>
</div>
Expand Down
10 changes: 10 additions & 0 deletions src/templates/show.html
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ <h1 id='pageTitle' {% if hideTitle %}hidden{%endif%}>{{ App.pageTitle }}</h1>
{% endfor %}
</select>
{# onBlur cannot work on bootstrap multiselect see https://stackoverflow.com/questions/42673800/bootstrap-multiselect-blur-event-not-triggering so add a button #}
{# ARCHIVED filter #}
<div class='input-group mr-1'>
<div class='input-group-prepend'>
<div class='input-group-text'>
<input id='showArchivedBox' type='checkbox' {{ App.Request.query.get('archived') == 'on' ? 'checked' }} name='archived' aria-label='{{ 'Show archived'|trans }}'>
</div>
</div>
<label for='showArchivedBox' class='input-group-text brl-none'>{{ 'Show archived'|trans }}</label>
</div>

<button class='btn btn-primary'>{{ 'Go'|trans }}</button>

</div>
Expand Down
6 changes: 5 additions & 1 deletion src/templates/upload-dropdown-menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,12 @@
<i class='fas fa-fw fa-info-circle mr-1'></i>{{ 'More information'|trans }}</a>

{% if not upload.immutable and not Entity.isReadOnly %}
<!-- DESTROY -->
{# ARCHIVE #}
<div class='dropdown-divider'></div>
<a class='dropdown-item hover-warning' data-action='archive-upload' data-uploadid='{{ upload.id }}'>
<i class='fas fa-fw fa-box-archive fa-fw mr-1' title='{{ 'Archive/Unarchive'|trans }}'></i>{{ 'Archive/Unarchive'|trans }}
</a>
{# DESTROY #}
<a class='dropdown-item hover-danger' data-action='destroy-upload' data-uploadid='{{ upload.id }}'>
<i class='fas fa-fw fa-trash-alt fa-fw mr-1' title='{{ 'Delete'|trans }}'></i>{{ 'Delete'|trans }}
</a>
Expand Down
17 changes: 13 additions & 4 deletions src/templates/uploads.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ <h5 class='modal-title' id='plainTextModalLabel'>{{ 'File content'|trans }}</h5>

<!-- UPLOADED FILES -->
<div id='filesdiv'>
<a name='filesDiv'></a>
{% if Entity.entityData.uploads %}
<div class='d-flex justify-content-between'>
<div><!-- necessary div for flex -->
Expand All @@ -32,9 +33,14 @@ <h3 title='{{ 'Toggle visibility'|trans }}' data-action='toggle-next' data-toggl
</div>

{# id is used with data-toggle-target-extra #}
<div title='{{ 'Switch layout'|trans }}' id='uploadsViewToggler' class='hl-hover-gray rounded p-1' data-action='toggle-uploads-layout' data-target-layout='{{ App.Users.userData.uploads_layout ? 0 : 1 }}'>
<i class='fas fa-fw fa-list fa-flip-horizontal'></i>
<i class='fas fa-fw fa-table-cells'></i>
<div>
<span title='{{ 'Switch layout'|trans }}' id='uploadsViewToggler' class='hl-hover-gray rounded p-1' data-action='toggle-uploads-layout' data-target-layout='{{ App.Users.userData.uploads_layout ? 0 : 1 }}'>
<i class='fas fa-fw fa-list fa-flip-horizontal'></i>
<i class='fas fa-fw fa-table-cells'></i>
</span>
<span title='{{ 'Show archived'|trans }}' class='hl-hover-gray rounded p-1' data-action='toggle-uploads-show-archived'>
<i class='fas fa-fw fa-box-archive'></i>
</span>
</div>
</div>

Expand All @@ -58,8 +64,11 @@ <h3 title='{{ 'Toggle visibility'|trans }}' data-action='toggle-next' data-toggl
<!-- DEFAULT LAYOUT (1) -->
{% else %}
<div class='col-md-4 col-sm-6'>
<div class='thumbnail box' data-type='{{ Entity.type }}' data-id='{{ Entity.id }}' style='overflow: visible'>
<div class='thumbnail box {{ upload.state == constant('Elabftw\\Enums\\State::Archived').value ? 'bg-light' }}' data-type='{{ Entity.type }}' data-id='{{ Entity.id }}' style='overflow: visible'>
{% include('upload-dropdown-menu.html') %}
{% if upload.state == constant('Elabftw\\Enums\\State::Archived').value %}
<p class='mb-0'><i class='fas fa-fw fa-box-archive mr-1'></i>{{ 'Archived'|trans }}</p>
{% endif %}

<!-- IMAGES -->
{% if ext matches '/(jpg|jpeg|png|gif|tif|tiff|pdf|eps|svg|heic)$/i' %}
Expand Down
3 changes: 3 additions & 0 deletions src/templates/view-edit-toolbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ <h5 class='modal-title' id='timestampModalLabel'>{{ 'Timestamp Experiment'|trans
<!-- CHANGELOG -->
<a class='dropdown-item' href='?id={{ Entity.id }}&mode=changelog'><i class='fas fa-list fa-fw'></i> {{ 'See changelog'|trans }}</a>

<a class='dropdown-item hover-warning' data-action='archive-entity'>
<i class='fas fa-fw fa-box-archive fa-fw mr-1' title='{{ 'Archive/Unarchive'|trans }}'></i>{{ 'Archive/Unarchive'|trans }}
</a>
{% if App.Config.configArr.deletable_xp == '1' %}
<div class='dropdown-divider'></div>
{# DELETE #}
Expand Down
6 changes: 6 additions & 0 deletions src/templates/view-edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ <h5 class='modal-title' id='ownerModalLabel'>{{ 'Transfer ownership'|trans }}</h
</div>
</div>
</div>

<div id='isArchivedDiv'>
{% if Entity.entityData.state == constant('Elabftw\\Enums\\State::Archived').value %}
{{ 'This entry is archived: it will not appear in the listings by default.'|trans|msg('ok', false) }}
{% endif %}
</div>
1 change: 1 addition & 0 deletions src/ts/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ enum Action {

AccessKey = 'accesskey',
Add = 'add',
Archive = 'archive',
Bloxberg = 'bloxberg',
Deduplicate = 'deduplicate',
Duplicate = 'duplicate',
Expand Down
3 changes: 3 additions & 0 deletions src/ts/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ document.addEventListener('DOMContentLoaded', () => {
.then(() => reloadElement('filesdiv'))
// remove overlay in all cases
.finally(() => document.getElementById('container').removeChild(document.getElementById('loadingOverlay')));
// ARCHIVE ENTITY
} else if (el.matches('[data-action="archive-entity"]')) {
ApiC.patch(`${entity.type}/${entity.id}`, {action: Action.Archive}).then(() => reloadElement('isArchivedDiv'));

// DESTROY ENTITY
} else if (el.matches('[data-action="destroy"]')) {
Expand Down
23 changes: 23 additions & 0 deletions src/ts/uploads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,24 @@ document.addEventListener('DOMContentLoaded', () => {
reloadElement('filesdiv');
});

// TOGGLE SHOW ARCHIVED
} else if (el.matches('[data-action="toggle-uploads-show-archived"]')) {
const url = new URL(window.location.href);
const queryParams = new URLSearchParams(url.search);

// toggle "archived" query parameter
if (queryParams.has('archived')) {
queryParams.delete('archived');
} else {
queryParams.set('archived', 'on');
}

// Update the query parameters in the URL
url.search = queryParams.toString();
url.hash = 'filesDiv';
const modifiedUrl = url.toString();
window.location.replace(modifiedUrl);

// REPLACE UPLOAD
} else if (el.matches('[data-action="replace-upload"]')) {
document.getElementById('replaceUploadForm_' + el.dataset.uploadid).hidden = false;
Expand All @@ -124,6 +142,11 @@ document.addEventListener('DOMContentLoaded', () => {
};
ApiC.post(`${entity.type}/${entity.id}/${Model.Upload}`, params).then(() => reloadElement('filesdiv'));

// ARCHIVE UPLOAD
} else if (el.matches('[data-action="archive-upload"]')) {
const uploadid = parseInt(el.dataset.uploadid, 10);
ApiC.patch(`${entity.type}/${entity.id}/${Model.Upload}/${uploadid}`, {action: Action.Archive}).then(() => reloadElement('filesdiv'));

// DESTROY UPLOAD
} else if (el.matches('[data-action="destroy-upload"]')) {
const uploadid = parseInt(el.dataset.uploadid, 10);
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/models/ExperimentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public function testCreateAndDestroy(): void
$this->assertTrue((bool) Check::id($new));
$this->Experiments->setId($new);
$this->Experiments->canOrExplode('write');
// test archive too
$this->assertIsArray($this->Experiments->patch(Action::Archive, array()));
// two times to test unarchive branch
$this->assertIsArray($this->Experiments->patch(Action::Archive, array()));
$this->Experiments->toggleLock();
$this->Experiments->destroy();
$Templates = new Templates($this->Users);
Expand Down