Исходный код вики Инструкция по работе с разделом Трудозатраты
Версия 1.1 от Alexey Semenihin на 10.10.2025
Скрыть последних авторов
| author | version | line-number | content |
|---|---|---|---|
| |
1.1 | 1 | {{velocity}} |
| 2 | ## Макрос для отображения подстраниц с отображаемыми именами в алфавитном порядке | ||
| 3 | #macro(displayDirectChildrenClean) | ||
| 4 | #set ($currentPage = $doc.fullName) | ||
| 5 | #set ($children = $xwiki.getDocument($currentPage).getChildren()) | ||
| 6 | #if ($children.size() > 0) | ||
| 7 | ## Создаем список для хранения пар (отображаемое имя, полное имя) | ||
| 8 | #set ($childList = []) | ||
| 9 | #foreach ($child in $children) | ||
| 10 | #set ($childDoc = $xwiki.getDocument($child)) | ||
| 11 | ## Получаем отображаемое имя (title), если оно не пустое, иначе используем имя документа | ||
| 12 | #set ($displayName = $childDoc.getPlainTitle()) | ||
| 13 | #if ($displayName == '') | ||
| 14 | #set ($displayName = $childDoc.getName()) | ||
| 15 | #end | ||
| 16 | ## Добавляем пару в список | ||
| 17 | #set ($discard = $childList.add({"displayName": $displayName, "fullName": $child})) | ||
| 18 | #end | ||
| 19 | ## Сортируем список по displayName | ||
| 20 | #set ($sortedChildren = $sorttool.sort($childList, "displayName:asc")) | ||
| 21 | ## Выводим отсортированный список | ||
| 22 | #foreach ($item in $sortedChildren) | ||
| 23 | [[$item.displayName>>$item.fullName]] | ||
| 24 | #end | ||
| 25 | #end | ||
| 26 | #end | ||
| 27 | ## Выводим список | ||
| 28 | #displayDirectChildrenClean() | ||
| 29 | {{/velocity}} | ||
| 30 | |||
| 31 | |||
| 32 | {{velocity}} | ||
| 33 | |||
| 34 | ## Подключаем необходимые JS/CSS для работы вкладок | ||
| 35 | $xwiki.jsfx.use('uicomponents/widgets/tabView.js', {'forceSkinAction': true, 'language': 'xwiki/2.1'}) | ||
| 36 | $xwiki.ssfx.use('uicomponents/widgets/tabView.css', true) | ||
| 37 | {{html}} | ||
| 38 | <script> | ||
| 39 | // Ждём загрузки DOM | ||
| 40 | document.observe("dom:loaded", function() { | ||
| 41 | // Находим ссылку на вкладку "Вложения" (по ID или тексту) | ||
| 42 | var attachmentsTab = $('Attachmentslink'); | ||
| 43 | |||
| 44 | // Если вкладка найдена, имитируем клик | ||
| 45 | if (attachmentsTab) { | ||
| 46 | attachmentsTab.click(); | ||
| 47 | } | ||
| 48 | // Альтернативный вариант (если не сработал ID): | ||
| 49 | }); | ||
| 50 | </script> | ||
| 51 | {{/html}} | ||
| 52 | {{/velocity}} | ||
| 53 | |||
| 54 | {{velocity}} | ||
| 55 | === Прикрепите вложения и используйте кнопки для навигации === | ||
| 56 | #set($spaceRef = $doc.documentReference.lastSpaceReference.name) | ||
| 57 | #set($pageName = $doc.documentReference.name) | ||
| 58 | #set($docFullName = "${spaceRef}.${pageName}") | ||
| 59 | #set($attachments = $doc.getAttachmentList()) | ||
| 60 | #set($itemsPerPage = 1) | ||
| 61 | #set($totalPages = ($attachments.size() + $itemsPerPage - 1) / $itemsPerPage) | ||
| 62 | #set($currentPage = 1) | ||
| 63 | #if("$!request.page" != "") | ||
| 64 | #set($currentPage = $mathtool.toInteger($request.page)) | ||
| 65 | #end | ||
| 66 | #set($startIndex = ($currentPage - 1) * $itemsPerPage) | ||
| 67 | #set($endIndex = $startIndex + $itemsPerPage - 1) | ||
| 68 | #if($endIndex >= $attachments.size()) | ||
| 69 | #set($endIndex = $attachments.size() - 1) | ||
| 70 | #end | ||
| 71 | |||
| 72 | {{html}} | ||
| 73 | <div class="navigation-controls"> | ||
| 74 | #if($attachments && !$attachments.isEmpty()) | ||
| 75 | <!-- Выпадающий список для выбора вложения --> | ||
| 76 | <div class="attachment-selector"> | ||
| 77 | <label for="attachment-select">Выберите вложение:</label> | ||
| 78 | <select id="attachment-select"> | ||
| 79 | #foreach($attachment in $attachments) | ||
| 80 | <option value="$attachment.filename" #if($velocityCount == $currentPage)selected#end> | ||
| 81 | $attachment.filename | ||
| 82 | </option> | ||
| 83 | #end | ||
| 84 | </select> | ||
| 85 | </div> | ||
| 86 | |||
| 87 | <!-- Пагинация --> | ||
| 88 | <div class="pagination-controls"> | ||
| 89 | #if($currentPage > 1) | ||
| 90 | #set($prevPage = $currentPage - 1) | ||
| 91 | <a href="$doc.getURL('view', "page=${prevPage}")" class="btn btn-primary">Назад</a> | ||
| 92 | #else | ||
| 93 | <span class="btn btn-disabled">Назад</span> | ||
| 94 | #end | ||
| 95 | |||
| 96 | <span class="page-info">Страница $currentPage из $totalPages</span> | ||
| 97 | |||
| 98 | #if($currentPage < $totalPages) | ||
| 99 | #set($nextPage = $currentPage + 1) | ||
| 100 | <a href="$doc.getURL('view', "page=${nextPage}")" class="btn btn-primary">Далее</a> | ||
| 101 | #else | ||
| 102 | <span class="btn btn-disabled">Далее</span> | ||
| 103 | #end | ||
| 104 | </div> | ||
| 105 | #else | ||
| 106 | <div class="no-attachments-message"> | ||
| 107 | Нет прикрепленных вложений. Пожалуйста, добавьте файлы для просмотра. | ||
| 108 | </div> | ||
| 109 | #end | ||
| 110 | </div> | ||
| 111 | {{/html}} | ||
| 112 | |||
| 113 | #if($attachments && !$attachments.isEmpty()) | ||
| 114 | #foreach($i in [$startIndex..$endIndex]) | ||
| 115 | #set($attachment = $attachments.get($i)) | ||
| 116 | #set($filename = $attachment.filename) | ||
| 117 | #set($fileurl = $doc.getAttachmentURL($filename, "download")) | ||
| 118 | #set($fileExtension = $filename.substring($filename.lastIndexOf('.') + 1).toLowerCase()) | ||
| 119 | |||
| 120 | === Документ: $filename === | ||
| 121 | |||
| 122 | {{html}} | ||
| 123 | <div class="document-viewer-container" id="document-viewer"> | ||
| 124 | #if($fileExtension == 'pdf') | ||
| 125 | <!-- PDF Viewer --> | ||
| 126 | <iframe | ||
| 127 | id="document-frame" | ||
| 128 | src="/resources/pdfjs/web/viewer.html?file=${escapetool.url($fileurl)}" | ||
| 129 | width="100%" | ||
| 130 | height="600px"> | ||
| 131 | </iframe> | ||
| 132 | #elseif($fileExtension == 'doc' || $fileExtension == 'docx' || $fileExtension == 'ppt' || $fileExtension == 'pptx' || $fileExtension == 'xls' || $fileExtension == 'xlsx' || $fileExtension == 'mp4') | ||
| 133 | #if($fileExtension == 'doc' || $fileExtension == 'docx' || $fileExtension == 'ppt' || $fileExtension == 'pptx' || $fileExtension == 'xls' || $fileExtension == 'xlsx') | ||
| 134 | <!-- Office Viewer --> | ||
| 135 | #set($officeViewerUrl = "${doc.getURL('view')}?xpage=office&attachment=${escapetool.url($filename)}") | ||
| 136 | <iframe | ||
| 137 | id="document-frame" | ||
| 138 | src="$officeViewerUrl" | ||
| 139 | width="100%" | ||
| 140 | height="600px"> | ||
| 141 | </iframe> | ||
| 142 | #end | ||
| 143 | #if($fileExtension == 'mp4') | ||
| 144 | ==ВИДЕО== | ||
| 145 | <video id="myvideo" width="920" controls> | ||
| 146 | <source src="${fileurl}" type="video/$fileExtension"> | ||
| 147 | Ваш браузер не поддерживает видео. | ||
| 148 | </video> | ||
| 149 | #end | ||
| 150 | #else | ||
| 151 | ==Конец== | ||
| 152 | <div class="unsupported-format"> | ||
| 153 | Формат файла (${fileExtension}) не поддерживается для просмотра. | ||
| 154 | <a href="${fileurl}">Скачать файл</a> | ||
| 155 | </div> | ||
| 156 | #end | ||
| 157 | </div> | ||
| 158 | {{/html}} | ||
| 159 | #end | ||
| 160 | #else | ||
| 161 | {{html}} | ||
| 162 | <div class="no-attachments-message"> | ||
| 163 | Нет доступных вложений для отображения. | ||
| 164 | </div> | ||
| 165 | {{/html}} | ||
| 166 | #end | ||
| 167 | |||
| 168 | {{html}} | ||
| 169 | <style> | ||
| 170 | .navigation-controls { | ||
| 171 | display: flex; | ||
| 172 | flex-direction: column; | ||
| 173 | gap: 15px; | ||
| 174 | margin-bottom: 20px; | ||
| 175 | } | ||
| 176 | .attachment-selector { | ||
| 177 | display: flex; | ||
| 178 | align-items: center; | ||
| 179 | gap: 10px; | ||
| 180 | } | ||
| 181 | .attachment-selector select { | ||
| 182 | padding: 8px; | ||
| 183 | border-radius: 4px; | ||
| 184 | min-width: 300px; | ||
| 185 | } | ||
| 186 | .document-viewer-container { | ||
| 187 | margin: 20px 0; | ||
| 188 | border: 1px solid #eee; | ||
| 189 | padding: 10px; | ||
| 190 | background: #f9f9f9; | ||
| 191 | } | ||
| 192 | .pagination-controls { | ||
| 193 | margin: 20px 0; | ||
| 194 | display: flex; | ||
| 195 | justify-content: center; | ||
| 196 | align-items: center; | ||
| 197 | gap: 20px; | ||
| 198 | } | ||
| 199 | .btn { | ||
| 200 | padding: 8px 16px; | ||
| 201 | border-radius: 4px; | ||
| 202 | text-decoration: none; | ||
| 203 | } | ||
| 204 | .btn-primary { | ||
| 205 | background-color: #3366cc; | ||
| 206 | color: white; | ||
| 207 | } | ||
| 208 | .btn-disabled { | ||
| 209 | background-color: #eee; | ||
| 210 | color: #999; | ||
| 211 | cursor: not-allowed; | ||
| 212 | } | ||
| 213 | .page-info { | ||
| 214 | font-weight: bold; | ||
| 215 | } | ||
| 216 | .unsupported-format { | ||
| 217 | padding: 20px; | ||
| 218 | text-align: center; | ||
| 219 | color: #666; | ||
| 220 | } | ||
| 221 | .no-attachments-message { | ||
| 222 | padding: 20px; | ||
| 223 | text-align: center; | ||
| 224 | color: #666; | ||
| 225 | font-style: italic; | ||
| 226 | border: 1px dashed #ccc; | ||
| 227 | background-color: #f5f5f5; | ||
| 228 | } | ||
| 229 | </style> | ||
| 230 | |||
| 231 | <script> | ||
| 232 | //<![CDATA[ | ||
| 233 | document.addEventListener('DOMContentLoaded', function() { | ||
| 234 | const select = document.getElementById('attachment-select'); | ||
| 235 | const viewerContainer = document.getElementById('document-viewer'); | ||
| 236 | |||
| 237 | if (select && viewerContainer) { | ||
| 238 | select.addEventListener('change', function() { | ||
| 239 | const filename = this.value; | ||
| 240 | const fileExtension = filename.split('.').pop().toLowerCase(); | ||
| 241 | const fileurl = "${doc.getURL('download')}" + "/" + encodeURIComponent(filename); | ||
| 242 | const pdfViewerUrl = "/resources/pdfjs/web/viewer.html?file=" + encodeURIComponent(fileurl); | ||
| 243 | const baseUrl = "${doc.getURL('view')}"; | ||
| 244 | |||
| 245 | let viewerHtml = ''; | ||
| 246 | |||
| 247 | if (fileExtension === 'pdf') { | ||
| 248 | viewerHtml = '<iframe id="document-frame" ' + | ||
| 249 | 'src="' + pdfViewerUrl + '" ' + | ||
| 250 | 'width="100%" height="600px"></iframe>'; | ||
| 251 | } else if (['doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx'].indexOf(fileExtension) >= 0) { | ||
| 252 | viewerHtml = '<iframe id="document-frame" ' + | ||
| 253 | 'src="' + baseUrl + '?xpage=office&attachment=' + encodeURIComponent(filename) + '" ' + | ||
| 254 | 'width="100%" height="600px"></iframe>'; | ||
| 255 | } else if (fileExtension === 'mp4'||fileExtension === 'mov') { | ||
| 256 | viewerHtml = '<video width="920" controls>' + | ||
| 257 | '<source src="' + fileurl + '" type="video/' + fileExtension + '">' + | ||
| 258 | 'Ваш браузер не поддерживает видео.</video>'; | ||
| 259 | } else { | ||
| 260 | viewerHtml = '<div class="unsupported-format">' + | ||
| 261 | 'Формат файла (' + fileExtension + ') не поддерживается для просмотра. ' + | ||
| 262 | '<a href="' + fileurl + '">Скачать файл</a></div>'; | ||
| 263 | } | ||
| 264 | |||
| 265 | viewerContainer.innerHTML = viewerHtml; | ||
| 266 | |||
| 267 | // Обновляем заголовок документа | ||
| 268 | const titleElement = viewerContainer.previousElementSibling; | ||
| 269 | if (titleElement && titleElement.tagName === 'H3') { | ||
| 270 | titleElement.textContent = 'Документ: ' + filename; | ||
| 271 | } | ||
| 272 | }); | ||
| 273 | } | ||
| 274 | }); | ||
| 275 | //]]> | ||
| 276 | </script> | ||
| 277 | {{/html}} | ||
| 278 | {{/velocity}} |