mirror of
https://github.com/openvk/openvk
synced 2025-01-22 15:54:26 +03:00
add viewer and gallery
This commit is contained in:
parent
41b2947ba4
commit
6bb92aed3c
12 changed files with 309 additions and 24 deletions
|
@ -156,6 +156,11 @@ class Document extends Media
|
|||
return false;
|
||||
}
|
||||
|
||||
function isImage(): bool
|
||||
{
|
||||
return in_array($this->getVKAPIType(), [3, 4]);
|
||||
}
|
||||
|
||||
function isCopiedBy(User $user): bool
|
||||
{
|
||||
if($user->getId() === $this->getOwnerID())
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\{Documents, Clubs};
|
||||
use openvk\Web\Models\Entities\Document;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
|
||||
final class DocumentsPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
@ -10,6 +11,8 @@ final class DocumentsPresenter extends OpenVKPresenter
|
|||
|
||||
function renderList(?int $owner_id = NULL): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$this->template->_template = "Documents/List.xml";
|
||||
if($owner_id > 0)
|
||||
$this->notFound();
|
||||
|
@ -110,6 +113,10 @@ final class DocumentsPresenter extends OpenVKPresenter
|
|||
$document->save();
|
||||
} catch(\TypeError $e) {
|
||||
$this->flashFail("err", tr("forbidden"), $e->getMessage(), null, $isAjax);
|
||||
} catch(ISE $e) {
|
||||
$this->flashFail("err", tr("forbidden"), tr("error_file_preview"), null, $isAjax);
|
||||
} catch(\ValueError $e) {
|
||||
$this->flashFail("err", tr("forbidden"), $e->getMessage(), null, $isAjax);
|
||||
}
|
||||
|
||||
if(!$isAjax) {
|
||||
|
@ -121,4 +128,25 @@ final class DocumentsPresenter extends OpenVKPresenter
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderPage(int $virtual_id, int $real_id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$access_key = $this->queryParam("key");
|
||||
$doc = (new Documents)->getDocumentById((int)$virtual_id, (int)$real_id);
|
||||
if(!$doc || $doc->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
if(!$doc->checkAccessKey($access_key))
|
||||
$this->notFound();
|
||||
|
||||
$this->template->doc = $doc;
|
||||
$this->template->type = $doc->getVKAPIType();
|
||||
$this->template->is_image = $doc->isImage();
|
||||
$this->template->tags = $doc->getTags();
|
||||
$this->template->copied = $doc->isCopiedBy($this->user->identity);
|
||||
$this->template->copyImportance = true;
|
||||
$this->template->modifiable = $doc->canBeModifiedBy($this->user->identity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
{/block}
|
||||
|
||||
{block content}
|
||||
{var $is_gallery = $current_tab == 3 || $current_tab == 4}
|
||||
<div id="docs_page_wrapper">
|
||||
<div class="docs_page_search">
|
||||
<form action="/search" method="get">
|
||||
|
@ -36,7 +37,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="docs_page_content">
|
||||
<div n:class="docs_page_content, $is_gallery ? docs_page_gallery">
|
||||
<div class="summaryBar display_flex_row display_flex_space_between">
|
||||
<div class="summary">{tr($locale_string, $count)}.</div>
|
||||
|
||||
|
@ -49,16 +50,19 @@
|
|||
<div class="container_white scroll_container">
|
||||
{if $count > 0}
|
||||
{foreach $docs as $doc}
|
||||
<div class="scroll_node">
|
||||
{include "components/doc.xml", doc => $doc}
|
||||
</div>
|
||||
{if $is_gallery}
|
||||
{include "components/image.xml", doc => $doc, scroll_context => true}
|
||||
{else}
|
||||
{include "components/doc.xml", doc => $doc, scroll_context => true}
|
||||
{/if}
|
||||
{/foreach}
|
||||
{else}
|
||||
{include "../components/error.xml", description => tr("there_is_no_documents_alright")}
|
||||
{/if}
|
||||
|
||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||
</div>
|
||||
|
||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
74
Web/Presenters/templates/Documents/Page.xml
Normal file
74
Web/Presenters/templates/Documents/Page.xml
Normal file
|
@ -0,0 +1,74 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}
|
||||
{_document} "{ovk_proc_strtr($doc->getName(), 20)}"
|
||||
{/block}
|
||||
|
||||
{block header}
|
||||
{$doc->getName()}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<style>
|
||||
.sidebar, .page_header, .page_footer {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.page_body {
|
||||
margin-top: -45px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class='media-page-wrapper photo-page-wrapper'>
|
||||
<div class='photo-page-wrapper-photo'>
|
||||
{if $is_image}
|
||||
<img alt="doc image" src="{$doc->getURL()}" />
|
||||
{else}
|
||||
<a href="{$doc->getURL()}" download="{downloadable_name($doc->getName())}">
|
||||
<input class="button" type="button" value="{_download_file}">
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class='ovk-photo-details'>
|
||||
<div class='media-page-wrapper-description'>
|
||||
<p n:if='sizeof($tags) > 0'>
|
||||
{foreach $tags as $tag}
|
||||
<a href="/search?section=docs&tags={urlencode($tag)}">
|
||||
{$tag}{if $tag != $tags[sizeof($tags) - 1]},{/if}
|
||||
</a>
|
||||
{/foreach}
|
||||
</p>
|
||||
<div class='upload_time'>
|
||||
{_info_upload_date}: {$doc->getPublicationTime()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="media-page-wrapper-details">
|
||||
<div class='media-page-wrapper-comments'></div>
|
||||
<div class='media-page-wrapper-actions docMainItem' data-context="page" data-id="{$doc->getPrettiestId()}">
|
||||
{if !$doc->isOwnerHidden()}
|
||||
{var $owner = $doc->getOwner()}
|
||||
<a href="{$owner->getURL()}" class='media-page-author-block'>
|
||||
<img class='cCompactAvatars' src="{$owner->getAvatarURL('miniscule')}">
|
||||
|
||||
<div class='media-page-author-block-name'>
|
||||
<b>{$owner->getCanonicalName()}</b>
|
||||
</div>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
{if isset($thisUser)}
|
||||
<a n:if="$modifiable" class="profile_link" style="display:block;width:96%;" id="edit_icon">{_edit}</a>
|
||||
<a n:if="$modifiable" class="profile_link" style="display:block;width:96%;" id="report_icon">{_report}</a>
|
||||
<a n:if="!$copied || $copied && $copyImportance" class="profile_link" style="display:block;width:96%;" id="add_icon">{_add}</a>
|
||||
<a n:if="$copied && !$copyImportance" class="profile_link" style="display:block;width:96%;" id="remove_icon">{_remove}</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
|
@ -12,14 +12,13 @@
|
|||
»
|
||||
<a href="/docs">{_documents}</a>
|
||||
{/if}
|
||||
|
||||
»
|
||||
загрузка
|
||||
Non-AJAX Document upload
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<table>
|
||||
<table width="600">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="nobold">{_name}:</span></td>
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
{var $copied = $doc->isCopiedBy($thisUser)}
|
||||
{var $modifiable = $doc->canBeModifiedBy($thisUser)}
|
||||
|
||||
<div class="docListViewItem" data-id="{$doc->getPrettiestId()}">
|
||||
<a>
|
||||
<div n:class="docMainItem, docListViewItem, $scroll_context ? scroll_node" data-id="{$doc->getPrettiestId()}">
|
||||
<a class="viewerOpener" href="/doc{$doc->getPrettyId()}">
|
||||
{if $preview}
|
||||
<img class="doc_icon" alt="document_preview" src="{$preview->getURLBySizeId('tiny')}">
|
||||
{else}
|
||||
|
@ -14,7 +14,7 @@
|
|||
{/if}
|
||||
</a>
|
||||
<div class="doc_content noOverflow">
|
||||
<a><b class="noOverflow doc_name">{$doc->getName()}</b></a>
|
||||
<a class="viewerOpener" href="/doc{$doc->getPrettyId()}"><b class="noOverflow doc_name">{$doc->getName()}</b></a>
|
||||
|
||||
<div class="doc_content_info">
|
||||
<span>{$doc->getPublicationTime()}</span>,
|
||||
|
|
19
Web/Presenters/templates/Documents/components/image.xml
Normal file
19
Web/Presenters/templates/Documents/components/image.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
{var $preview = $doc->hasPreview() ? $doc->getPreview() : NULL}
|
||||
{var $copied = $doc->isCopiedBy($thisUser)}
|
||||
{var $modifiable = $doc->canBeModifiedBy($thisUser)}
|
||||
|
||||
<a href="/doc{$doc->getPrettyId()}" n:class="docMainItem, viewerOpener, docGalleryItem, $scroll_context ? scroll_node" data-id="{$doc->getPrettiestId()}">
|
||||
<img loading="lazy" src="{$preview->getURLBySizeId('medium')}" alt="gallery photo">
|
||||
|
||||
<div class="doc_top_panel doc_shown_by_hover">
|
||||
<div n:if="!$modifiable" id="report_icon"></div>
|
||||
<div n:if="$modifiable" id="edit_icon"></div>
|
||||
<div n:if="!$copied || $copied && $copyImportance" id="add_icon"></div>
|
||||
<div n:if="$copied && !$copyImportance" id="remove_icon"></div>
|
||||
</div>
|
||||
|
||||
<div class="doc_bottom_panel doc_shown_by_hover doc_content">
|
||||
<span class="doc_bottom_panel_name noOverflow doc_name">{$doc->getName()}</span>
|
||||
<span class="doc_bottom_panel_size">{readable_filesize($doc->getFilesize())}</span>
|
||||
</div>
|
||||
</a>
|
|
@ -317,6 +317,8 @@ routes:
|
|||
handler: "Documents->listGroup"
|
||||
- url: "/docs/upload"
|
||||
handler: "Documents->upload"
|
||||
- url: "/doc{num}_{num}"
|
||||
handler: "Documents->page"
|
||||
- url: "/admin"
|
||||
handler: "Admin->index"
|
||||
- url: "/admin/users"
|
||||
|
|
|
@ -4010,6 +4010,73 @@ hr {
|
|||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 10px;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container .docGalleryItem {
|
||||
height: 200px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
/*width: 200px;*/
|
||||
}
|
||||
|
||||
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container .docGalleryItem img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container .docGalleryItem .doc_bottom_panel {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
background: rgba(1, 1, 1, 0.7);
|
||||
padding: 6px 6px;
|
||||
width: calc(100% - 12px);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 0fr;
|
||||
}
|
||||
|
||||
.docGalleryItem .doc_shown_by_hover {
|
||||
transition: all 100ms ease-in;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container .docGalleryItem:hover .doc_shown_by_hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container .docGalleryItem .doc_bottom_panel span {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container .docGalleryItem .doc_bottom_panel .doc_bottom_panel_size {
|
||||
max-width: 49px;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.docGalleryItem .doc_top_panel {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
background: rgba(1, 1, 1, 0.7);
|
||||
padding: 6px 6px;
|
||||
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.docGalleryItem .doc_top_panel > div {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#docs_page_wrapper select {
|
||||
width: 150px;
|
||||
}
|
||||
|
@ -4023,7 +4090,7 @@ hr {
|
|||
border-bottom: 1px solid #EDEDED;
|
||||
}
|
||||
|
||||
.scroll_node:last-of-type .docListViewItem {
|
||||
.docs_page_content .docListViewItem:last-of-type {
|
||||
border-bottom: unset !important;
|
||||
}
|
||||
|
||||
|
@ -4111,3 +4178,7 @@ hr {
|
|||
.docListViewItem .doc_volume #mark_icon {
|
||||
background-position-x: -80px;
|
||||
}
|
||||
|
||||
.doc_viewer_wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ function showDocumentUploadDialog(target = null, append_to_url = null)
|
|||
<li>${tr("limitations_file_author_rights")}.</li>
|
||||
</ul>
|
||||
|
||||
<div style="text-align:center;margin: 10px 0px 2px 0px;">
|
||||
<div id="_document_upload_frame" style="text-align:center;margin: 10px 0px 2px 0px;">
|
||||
<input onclick="upload_btn.click()" class="button" type="button" value="${tr("select_file_fp")}">
|
||||
<input id="upload_btn" type="file" style="display:none;">
|
||||
</div>
|
||||
|
@ -92,9 +92,19 @@ function showDocumentUploadDialog(target = null, append_to_url = null)
|
|||
})
|
||||
}
|
||||
|
||||
u(document).on('click', '.docListViewItem #edit_icon', async (e) => {
|
||||
u(document).on("drop", "#_document_upload_frame", (e) => {
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
e.preventDefault()
|
||||
|
||||
u(`#_document_upload_frame #upload_btn`).nodes[0].files = e.dataTransfer.files
|
||||
u("#_document_upload_frame #upload_btn").trigger("change")
|
||||
})
|
||||
|
||||
u(document).on('click', '.docMainItem #edit_icon', async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const target = u(e.target).closest("#edit_icon")
|
||||
const item = target.closest('.docListViewItem')
|
||||
const item = target.closest('.docMainItem')
|
||||
const id = item.nodes[0].dataset.id
|
||||
|
||||
CMessageBox.toggleLoader()
|
||||
|
@ -109,7 +119,7 @@ u(document).on('click', '.docListViewItem #edit_icon', async (e) => {
|
|||
<input type="text" name="doc_name" value="${doc.title}" placeholder="...">
|
||||
|
||||
<label>
|
||||
<input maxlength="255" value="0" type="radio" name="doc_access" ${doc.folder_id == 0 ? "checked" : ''}>
|
||||
<input maxlength="255" value="0" type="radio" name="doc_access" ${doc.folder_id != 3 ? "checked" : ''}>
|
||||
${tr("private_document")}
|
||||
</label>
|
||||
<br>
|
||||
|
@ -161,9 +171,12 @@ u(document).on('change', "#docs_page_wrapper select[name='docs_sort']", (e) => {
|
|||
window.router.route(new_url.href)
|
||||
})
|
||||
|
||||
u(document).on('click', '.docListViewItem #remove_icon', async (e) => {
|
||||
const target = u(e.target).closest("#remove_icon")
|
||||
const item = target.closest('.docListViewItem')
|
||||
u(document).on('click', '.docMainItem #remove_icon', async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const target = u(e.target).closest("#remove_icon")
|
||||
const item = target.closest('.docMainItem')
|
||||
const context = item.attr('data-context')
|
||||
const id = item.nodes[0].dataset.id.split("_")
|
||||
|
||||
target.addClass('lagged')
|
||||
|
@ -172,13 +185,21 @@ u(document).on('click', '.docListViewItem #remove_icon', async (e) => {
|
|||
|
||||
if(res == 1) {
|
||||
target.attr('id', 'mark_icon')
|
||||
|
||||
if(context == "page") {
|
||||
target.html('✓')
|
||||
window.router.route('/docs')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
u(document).on('click', '.docListViewItem #add_icon', async (e) => {
|
||||
u(document).on('click', '.docMainItem #add_icon', async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const target = u(e.target).closest("#add_icon")
|
||||
const item = target.closest('.docListViewItem')
|
||||
const item = target.closest('.docMainItem')
|
||||
const id = item.nodes[0].dataset.id.split("_")
|
||||
const context = item.attr('data-context')
|
||||
|
||||
target.addClass('lagged')
|
||||
|
||||
|
@ -192,11 +213,17 @@ u(document).on('click', '.docListViewItem #add_icon', async (e) => {
|
|||
|
||||
target.removeClass('lagged')
|
||||
target.attr('id', 'mark_icon')
|
||||
|
||||
if(context == "page") {
|
||||
target.html('✓')
|
||||
}
|
||||
})
|
||||
|
||||
u(document).on('click', '.docListViewItem #report_icon', (e) => {
|
||||
u(document).on('click', '.docMainItem #report_icon', (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const target = u(e.target).closest("#report_icon")
|
||||
const item = target.closest('.docListViewItem')
|
||||
const item = target.closest('.docMainItem')
|
||||
const id = item.nodes[0].dataset.id.split("_")
|
||||
|
||||
MessageBox(tr("report_question"), `
|
||||
|
@ -218,3 +245,50 @@ u(document).on('click', '.docListViewItem #report_icon', (e) => {
|
|||
|
||||
Function.noop])
|
||||
})
|
||||
|
||||
u(document).on("click", ".docListViewItem a.viewerOpener, a.docGalleryItem", async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const target = u(e.target)
|
||||
const link = target.closest('a')
|
||||
|
||||
CMessageBox.toggleLoader()
|
||||
const url = link.nodes[0].href
|
||||
const request = await fetch(url)
|
||||
const body_html = await request.text()
|
||||
const parser = new DOMParser
|
||||
const body = parser.parseFromString(body_html, "text/html")
|
||||
|
||||
const preview = body.querySelector('.photo-page-wrapper-photo')
|
||||
const details = body.querySelector('.ovk-photo-details')
|
||||
|
||||
preview.querySelector('img').setAttribute('id', 'ovk-photo-img')
|
||||
|
||||
const photo_viewer = new CMessageBox({
|
||||
title: '',
|
||||
custom_template: u(`
|
||||
<div class="ovk-photo-view-dimmer">
|
||||
<div class="ovk-photo-view">
|
||||
<div class="photo_com_title">
|
||||
<text id="photo_com_title_photos">
|
||||
${tr("document")}
|
||||
</text>
|
||||
<div>
|
||||
<a id="ovk-photo-close">${tr("close")}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='photo_viewer_wrapper doc_viewer_wrapper'>
|
||||
${preview.innerHTML}
|
||||
</div>
|
||||
<div class="ovk-photo-details">
|
||||
${details.innerHTML}
|
||||
</div>
|
||||
</div>
|
||||
</div>`)
|
||||
})
|
||||
photo_viewer.getNode().find("#ovk-photo-close").on("click", function(e) {
|
||||
photo_viewer.close()
|
||||
});
|
||||
|
||||
CMessageBox.toggleLoader()
|
||||
})
|
||||
|
|
|
@ -387,6 +387,11 @@ function readable_filesize($bytes, $precision = 2): string
|
|||
return round($bytes, $precision) . $units[$power];
|
||||
}
|
||||
|
||||
function downloadable_name(string $text): string
|
||||
{
|
||||
return preg_replace('/[\\/:*?"<>|]/', '_', str_replace(' ', '_', $text));
|
||||
}
|
||||
|
||||
return (function() {
|
||||
_ovk_check_environment();
|
||||
require __DIR__ . "/vendor/autoload.php";
|
||||
|
|
|
@ -2222,7 +2222,10 @@
|
|||
"tags" = "Теги";
|
||||
"owner_is_hidden" = "Автор скрыт";
|
||||
"accessbility" = "Доступность";
|
||||
"download_file" = "Скачать файл";
|
||||
"remove" = "Удалить";
|
||||
|
||||
"document" = "Документ";
|
||||
"document_type_0" = "Все";
|
||||
"document_type_1" = "Текстовые";
|
||||
"document_type_2" = "Архивы";
|
||||
|
@ -2259,6 +2262,7 @@
|
|||
"error_file_too_big" = "Файл слишком большой.";
|
||||
"error_file_invalid_format" = "Формат файла не разрешён.";
|
||||
"error_file_adding_copied" = "Не удалось добавить файл; он уже добавлен.";
|
||||
"error_file_preview" = "Не удалось загрузить файл: изображение имеет странности.";
|
||||
|
||||
"private_document" = "Приватный (по ссылке)";
|
||||
"public_document" = "Публичный";
|
||||
|
|
Loading…
Reference in a new issue