diff --git a/assets/media-live-search.css b/assets/media-live-search.css new file mode 100644 index 0000000..9a4436e --- /dev/null +++ b/assets/media-live-search.css @@ -0,0 +1,611 @@ +/* Quick Navigation Media Live Search Styles für bestehendes Mediapool Suchfeld */ + +/* CSS Variables - Standard: Light Theme */ +:root { + --qn-dropdown-bg: #fff; + --qn-dropdown-border: #ddd; + --qn-dropdown-text: #333; + --qn-item-border: #f5f5f5; + --qn-item-hover-bg: #f8f9fa; + --qn-thumb-bg: #f8f9fa; + --qn-title-color: #333; + --qn-filename-color: #666; + --qn-meta-color: #999; + --qn-loading-color: #666; +} + +/* Dark Theme Variables */ +.rex-theme-dark, +.rex-has-theme.rex-theme-dark { + --qn-dropdown-bg: #151c22; + --qn-dropdown-border: #444; + --qn-dropdown-text: #e9ecef; + --qn-item-border: #404040; + --qn-item-hover-bg: #1e2a32; + --qn-thumb-bg: #1e2a32; + --qn-title-color: #e9ecef; + --qn-filename-color: #adb5bd; + --qn-meta-color: #6c757d; + --qn-loading-color: #adb5bd; +} + +/* Auto Theme (rex-has-theme ohne spezifische Theme-Klasse) */ +@media (prefers-color-scheme: dark) { + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) { + --qn-dropdown-bg: #151c22; + --qn-dropdown-border: #444; + --qn-dropdown-text: #e9ecef; + --qn-item-border: #404040; + --qn-item-hover-bg: #1e2a32; + --qn-thumb-bg: #1e2a32; + --qn-title-color: #e9ecef; + --qn-filename-color: #adb5bd; + --qn-meta-color: #6c757d; + --qn-loading-color: #adb5bd; + } + + /* Fallback für Browser ohne REDAXO Theme System */ + body:not([class*="rex-theme"]):not([class*="rex-has-theme"]) { + --qn-dropdown-bg: #151c22; + --qn-dropdown-border: #444; + --qn-dropdown-text: #e9ecef; + --qn-item-border: #404040; + --qn-item-hover-bg: #1e2a32; + --qn-thumb-bg: #1e2a32; + --qn-title-color: #e9ecef; + --qn-filename-color: #adb5bd; + --qn-meta-color: #6c757d; + --qn-loading-color: #adb5bd; + } +} + +.qn-media-live-search-results { + display: none; + position: absolute; + top: 100%; + right: 0; + min-width: 340px; + width: 340px; + background: var(--qn-dropdown-bg); + border: 1px solid var(--qn-dropdown-border); + border-radius: 4px; + box-shadow: 0 4px 8px rgba(0,0,0,0.1); + z-index: 9999; + max-height: 400px; + overflow-y: auto; + margin-top: 2px; + color: var(--qn-dropdown-text); +} + +/* Responsive Anpassung für schmale Bildschirme */ +@media screen and (max-width: 480px) { + .qn-media-live-search-results { + left: 0; + right: 0; + width: auto; + min-width: auto; + } +} + +.qn-live-search-item { + display: block; + padding: 12px 16px; + border-bottom: 1px solid var(--qn-item-border); + cursor: pointer; + transition: background-color 0.2s; +} + +.qn-live-search-item-content { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +.qn-live-search-item-content:last-child { + margin-bottom: 0; +} + +.qn-live-search-item:hover { + background-color: var(--qn-item-hover-bg); +} + +.qn-live-search-item:last-child { + border-bottom: none; +} + +.qn-live-search-thumb { + flex: 0 0 45px; + height: 35px; + margin-right: 14px; + display: flex; + align-items: center; + justify-content: center; + background: var(--qn-thumb-bg); + border-radius: 4px; + overflow: hidden; +} + +.qn-live-search-thumb img { + max-width: 100%; + max-height: 100%; + object-fit: cover; +} + +.qn-live-search-thumb i { + font-size: 16px; + color: #6c757d; +} + +.qn-live-search-info { + flex: 1; + min-width: 0; +} + +.qn-live-search-title { + font-weight: 500; + color: var(--qn-title-color); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 13px; + line-height: 1.3; +} + +.qn-live-search-filename { + color: var(--qn-filename-color); + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 2px; +} + +.qn-live-search-meta { + color: var(--qn-meta-color); + font-size: 11px; + margin-top: 2px; +} + +.qn-live-search-actions { + display: flex; + gap: 8px; + margin-top: 8px; + justify-content: flex-start; +} + +.qn-live-search-actions .btn { + margin: 0; + padding: 4px 12px; + font-size: 11px; +} + +.qn-live-search-actions .btn-select { + background: #28a745; + color: white; + border-color: #28a745; +} + +.qn-live-search-actions .btn-select:hover { + background: #218838; + border-color: #1e7e34; +} + +.qn-live-search-loading, +.qn-live-search-no-results { + padding: 16px; + text-align: center; + color: var(--qn-loading-color); + font-size: 13px; +} + +.qn-live-search-loading i { + margin-right: 5px; +} + +.qn-live-search-item { + display: block; + padding: 12px 16px; + border-bottom: 1px solid #f5f5f5; + cursor: pointer; + transition: background-color 0.2s; +} + +.qn-live-search-item-content { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +.qn-live-search-item-content:last-child { + margin-bottom: 0; +} + +.qn-live-search-item:hover { + background-color: #f8f9fa; +} + +.qn-live-search-item:last-child { + border-bottom: none; +} + +.qn-live-search-thumb { + flex: 0 0 45px; + height: 35px; + margin-right: 14px; + display: flex; + align-items: center; + justify-content: center; + background: #f8f9fa; + border-radius: 4px; + overflow: hidden; +} + +.qn-live-search-thumb img { + max-width: 100%; + max-height: 100%; + object-fit: cover; +} + +.qn-live-search-thumb i { + font-size: 16px; + color: #6c757d; +} + +.qn-live-search-info { + flex: 1; + min-width: 0; +} + +.qn-live-search-title { + font-weight: 500; + color: #333; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 13px; + line-height: 1.3; +} + +.qn-live-search-filename { + color: #666; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 2px; +} + +.qn-live-search-meta { + color: #999; + font-size: 11px; + margin-top: 2px; +} + +.qn-live-search-actions { + display: flex; + gap: 8px; + margin-top: 8px; + justify-content: flex-start; +} + +.qn-live-search-actions .btn { + margin: 0; + padding: 4px 12px; + font-size: 11px; +} + +.qn-live-search-actions .btn-select { + background: #28a745; + color: white; + border-color: #28a745; +} + +.qn-live-search-actions .btn-select:hover { + background: #218838; + border-color: #1e7e34; +} + +.qn-live-search-loading, +.qn-live-search-no-results { + padding: 16px; + text-align: center; + color: #666; + font-size: 13px; +} + +.qn-live-search-loading i { + margin-right: 5px; +} + +/* Light Theme Support - explizit hell, auch bei prefers-color-scheme: dark */ +body.rex-theme-light .qn-media-live-search-results, +html.rex-theme-light .qn-media-live-search-results, +.rex-theme-light .qn-media-live-search-results, +body.rex-has-theme.rex-theme-light .qn-media-live-search-results, +html.rex-has-theme.rex-theme-light .qn-media-live-search-results, +.rex-has-theme.rex-theme-light .qn-media-live-search-results { + background: #fff !important; + border-color: #ddd !important; + color: #333 !important; +} + +body.rex-theme-light .qn-live-search-item, +html.rex-theme-light .qn-live-search-item, +.rex-theme-light .qn-live-search-item, +body.rex-has-theme.rex-theme-light .qn-live-search-item, +html.rex-has-theme.rex-theme-light .qn-live-search-item, +.rex-has-theme.rex-theme-light .qn-live-search-item { + border-bottom-color: #f5f5f5 !important; +} + +body.rex-theme-light .qn-live-search-item:hover, +html.rex-theme-light .qn-live-search-item:hover, +.rex-theme-light .qn-live-search-item:hover, +body.rex-has-theme.rex-theme-light .qn-live-search-item:hover, +html.rex-has-theme.rex-theme-light .qn-live-search-item:hover, +.rex-has-theme.rex-theme-light .qn-live-search-item:hover { + background-color: #f8f9fa !important; +} + +body.rex-theme-light .qn-live-search-thumb, +html.rex-theme-light .qn-live-search-thumb, +.rex-theme-light .qn-live-search-thumb, +body.rex-has-theme.rex-theme-light .qn-live-search-thumb, +html.rex-has-theme.rex-theme-light .qn-live-search-thumb, +.rex-has-theme.rex-theme-light .qn-live-search-thumb { + background: #f8f9fa !important; +} + +body.rex-theme-light .qn-live-search-title, +html.rex-theme-light .qn-live-search-title, +.rex-theme-light .qn-live-search-title, +body.rex-has-theme.rex-theme-light .qn-live-search-title, +html.rex-has-theme.rex-theme-light .qn-live-search-title, +.rex-has-theme.rex-theme-light .qn-live-search-title { + color: #333 !important; +} + +body.rex-theme-light .qn-live-search-filename, +html.rex-theme-light .qn-live-search-filename, +.rex-theme-light .qn-live-search-filename, +body.rex-has-theme.rex-theme-light .qn-live-search-filename, +html.rex-has-theme.rex-theme-light .qn-live-search-filename, +.rex-has-theme.rex-theme-light .qn-live-search-filename { + color: #666 !important; +} + +body.rex-theme-light .qn-live-search-meta, +html.rex-theme-light .qn-live-search-meta, +.rex-theme-light .qn-live-search-meta, +body.rex-has-theme.rex-theme-light .qn-live-search-meta, +html.rex-has-theme.rex-theme-light .qn-live-search-meta, +.rex-has-theme.rex-theme-light .qn-live-search-meta { + color: #999 !important; +} + +body.rex-theme-light .qn-live-search-loading, +body.rex-theme-light .qn-live-search-no-results, +html.rex-theme-light .qn-live-search-loading, +html.rex-theme-light .qn-live-search-no-results, +.rex-theme-light .qn-live-search-loading, +.rex-theme-light .qn-live-search-no-results, +body.rex-has-theme.rex-theme-light .qn-live-search-loading, +body.rex-has-theme.rex-theme-light .qn-live-search-no-results, +html.rex-has-theme.rex-theme-light .qn-live-search-loading, +html.rex-has-theme.rex-theme-light .qn-live-search-no-results, +.rex-has-theme.rex-theme-light .qn-live-search-loading, +.rex-has-theme.rex-theme-light .qn-live-search-no-results { + color: #666 !important; +} + +/* Dark Theme Support - spezifische Selektoren */ +/* Explizites Dark Theme */ +body.rex-theme-dark .qn-media-live-search-results, +html.rex-theme-dark .qn-media-live-search-results, +.rex-theme-dark .qn-media-live-search-results, +body.rex-has-theme.rex-theme-dark .qn-media-live-search-results, +html.rex-has-theme.rex-theme-dark .qn-media-live-search-results, +.rex-has-theme.rex-theme-dark .qn-media-live-search-results { + background: #151c22 !important; + border-color: #444 !important; + color: #e9ecef !important; +} + +body.rex-theme-dark .qn-live-search-item, +html.rex-theme-dark .qn-live-search-item, +.rex-theme-dark .qn-live-search-item, +body.rex-has-theme.rex-theme-dark .qn-live-search-item, +html.rex-has-theme.rex-theme-dark .qn-live-search-item, +.rex-has-theme.rex-theme-dark .qn-live-search-item { + border-bottom-color: #404040 !important; +} + +body.rex-theme-dark .qn-live-search-item:hover, +html.rex-theme-dark .qn-live-search-item:hover, +.rex-theme-dark .qn-live-search-item:hover, +body.rex-has-theme.rex-theme-dark .qn-live-search-item:hover, +html.rex-has-theme.rex-theme-dark .qn-live-search-item:hover, +.rex-has-theme.rex-theme-dark .qn-live-search-item:hover { + background-color: #1e2a32 !important; +} + +body.rex-theme-dark .qn-live-search-thumb, +html.rex-theme-dark .qn-live-search-thumb, +.rex-theme-dark .qn-live-search-thumb, +body.rex-has-theme.rex-theme-dark .qn-live-search-thumb, +html.rex-has-theme.rex-theme-dark .qn-live-search-thumb, +.rex-has-theme.rex-theme-dark .qn-live-search-thumb { + background: #1e2a32 !important; +} + +body.rex-theme-dark .qn-live-search-title, +html.rex-theme-dark .qn-live-search-title, +.rex-theme-dark .qn-live-search-title, +body.rex-has-theme.rex-theme-dark .qn-live-search-title, +html.rex-has-theme.rex-theme-dark .qn-live-search-title, +.rex-has-theme.rex-theme-dark .qn-live-search-title { + color: #e9ecef !important; +} + +body.rex-theme-dark .qn-live-search-filename, +html.rex-theme-dark .qn-live-search-filename, +.rex-theme-dark .qn-live-search-filename, +body.rex-has-theme.rex-theme-dark .qn-live-search-filename, +html.rex-has-theme.rex-theme-dark .qn-live-search-filename, +.rex-has-theme.rex-theme-dark .qn-live-search-filename { + color: #adb5bd !important; +} + +body.rex-theme-dark .qn-live-search-meta, +html.rex-theme-dark .qn-live-search-meta, +.rex-theme-dark .qn-live-search-meta, +body.rex-has-theme.rex-theme-dark .qn-live-search-meta, +html.rex-has-theme.rex-theme-dark .qn-live-search-meta, +.rex-has-theme.rex-theme-dark .qn-live-search-meta { + color: #6c757d !important; +} + +body.rex-theme-dark .qn-live-search-loading, +body.rex-theme-dark .qn-live-search-no-results, +html.rex-theme-dark .qn-live-search-loading, +html.rex-theme-dark .qn-live-search-no-results, +.rex-theme-dark .qn-live-search-loading, +.rex-theme-dark .qn-live-search-no-results, +body.rex-has-theme.rex-theme-dark .qn-live-search-loading, +body.rex-has-theme.rex-theme-dark .qn-live-search-no-results, +html.rex-has-theme.rex-theme-dark .qn-live-search-loading, +html.rex-has-theme.rex-theme-dark .qn-live-search-no-results, +.rex-has-theme.rex-theme-dark .qn-live-search-loading, +.rex-has-theme.rex-theme-dark .qn-live-search-no-results { + color: #adb5bd !important; +} + +/* Direkter prefers-color-scheme Support für alle Fälle */ +@media (prefers-color-scheme: dark) { + .qn-media-live-search-results { + background: #151c22 !important; + border-color: #444 !important; + color: #e9ecef !important; + } + + .qn-live-search-item { + border-bottom-color: #404040 !important; + } + + .qn-live-search-item:hover { + background-color: #1e2a32 !important; + } + + .qn-live-search-thumb { + background: #1e2a32 !important; + } + + .qn-live-search-title { + color: #e9ecef !important; + } + + .qn-live-search-filename { + color: #adb5bd !important; + } + + .qn-live-search-meta { + color: #6c757d !important; + } + + .qn-live-search-loading, + .qn-live-search-no-results { + color: #adb5bd !important; + } +} + +/* Auto Theme Support (rex-has-theme ohne explizite Theme-Klasse hört auf prefers-color-scheme) */ +@media (prefers-color-scheme: dark) { + body.rex-theme-auto .qn-media-live-search-results, + html.rex-theme-auto .qn-media-live-search-results, + .rex-theme-auto .qn-media-live-search-results, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-media-live-search-results, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-media-live-search-results, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-media-live-search-results, + .qn-media-live-search-results { + background: #151c22 !important; + border-color: #444 !important; + color: #e9ecef !important; + } + + body.rex-theme-auto .qn-live-search-item, + html.rex-theme-auto .qn-live-search-item, + .rex-theme-auto .qn-live-search-item, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-item, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-item, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-item, + .qn-live-search-item { + border-bottom-color: #404040 !important; + } + + body.rex-theme-auto .qn-live-search-item:hover, + html.rex-theme-auto .qn-live-search-item:hover, + .rex-theme-auto .qn-live-search-item:hover, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-item:hover, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-item:hover, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-item:hover, + .qn-live-search-item:hover { + background-color: #1e2a32 !important; + } + + body.rex-theme-auto .qn-live-search-thumb, + html.rex-theme-auto .qn-live-search-thumb, + .rex-theme-auto .qn-live-search-thumb, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-thumb, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-thumb, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-thumb, + .qn-live-search-thumb { + background: #1e2a32 !important; + } + + body.rex-theme-auto .qn-live-search-title, + html.rex-theme-auto .qn-live-search-title, + .rex-theme-auto .qn-live-search-title, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-title, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-title, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-title, + .qn-live-search-title { + color: #e9ecef !important; + } + + body.rex-theme-auto .qn-live-search-filename, + html.rex-theme-auto .qn-live-search-filename, + .rex-theme-auto .qn-live-search-filename, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-filename, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-filename, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-filename, + .qn-live-search-filename { + color: #adb5bd !important; + } + + body.rex-theme-auto .qn-live-search-meta, + html.rex-theme-auto .qn-live-search-meta, + .rex-theme-auto .qn-live-search-meta, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-meta, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-meta, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-meta, + .qn-live-search-meta { + color: #6c757d !important; + } + + body.rex-theme-auto .qn-live-search-loading, + body.rex-theme-auto .qn-live-search-no-results, + html.rex-theme-auto .qn-live-search-loading, + html.rex-theme-auto .qn-live-search-no-results, + .rex-theme-auto .qn-live-search-loading, + .rex-theme-auto .qn-live-search-no-results, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-loading, + body.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-no-results, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-loading, + html.rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-no-results, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-loading, + .rex-has-theme:not(.rex-theme-light):not(.rex-theme-dark) .qn-live-search-no-results, + .qn-live-search-loading, + .qn-live-search-no-results { + color: #adb5bd !important; + } +} diff --git a/assets/media-live-search.js b/assets/media-live-search.js new file mode 100644 index 0000000..b49b3ae --- /dev/null +++ b/assets/media-live-search.js @@ -0,0 +1,210 @@ +/** + * Quick Navigation Media Live Search + * Erweitert das bestehende Mediapool-Suchfeld um Live-Suche + */ + +$(document).on('rex:ready', function() { + initQuickNavigationMediaLiveSearch(); +}); + +function initQuickNavigationMediaLiveSearch() { + // Prüfen ob Live-Search vom User aktiviert ist + if (typeof rex !== 'undefined' && rex.QUICKNAV_MEDIA_LIVESEARCH_ENABLED === false) { + return; + } + + // Prüfen ob wir im Mediapool sind + var isMediapool = $('body').hasClass('rex-page-mediapool') || + $('[data-page="mediapool"]').length > 0 || + window.location.href.indexOf('page=mediapool') !== -1; + + if (!isMediapool) { + return; + } + + var searchInput = $('#be_search-media-name'); + var searchResults = null; + var searchTimer = null; + var currentRequest = null; + + if (searchInput.length === 0) { + return; + } + + // Container für Live-Suche Ergebnisse relativ zum bestehenden Suchfeld erstellen + var resultsContainer = $('
'); + var searchForm = searchInput.closest('.form-group, .input-group, form'); + searchForm.css('position', 'relative'); + searchForm.append(resultsContainer); + + // Event Handler für Live-Suche (zusätzlich zur normalen Suchfunktion) + searchInput.on('input.quicknav', function() { + var searchTerm = $(this).val().trim(); + + // Timer zurücksetzen + if (searchTimer) { + clearTimeout(searchTimer); + } + + // Laufende Requests abbrechen + if (currentRequest) { + currentRequest.abort(); + currentRequest = null; + } + + if (searchTerm.length === 0) { + resultsContainer.hide(); + return; + } + + if (searchTerm.length < 2) { + return; // Erst ab 2 Zeichen Live-Suche + } + + // Kurz warten vor der Suche (Debouncing) + searchTimer = setTimeout(function() { + performQuickNavMediaSearch(searchTerm, resultsContainer); + }, 300); + }); + + // Klick außerhalb schließt die Ergebnisse + $(document).on('click.quicknav', function(e) { + if (!$(e.target).closest('.qn-media-live-search-results, #be_search-media-name').length) { + resultsContainer.hide(); + } + }); + + // ESC schließt die Ergebnisse + searchInput.on('keydown.quicknav', function(e) { + if (e.keyCode === 27) { // ESC + resultsContainer.hide(); + } + }); + + function performQuickNavMediaSearch(searchTerm, container) { + // Loading anzeigen + container.html('
Suche läuft...
') + .show(); + + // Parameter aus der aktuellen Seite holen + var categoryId = $('#rex_file_category').val() || 0; + var openerInputField = $('input[name="opener_input_field"]').val() || ''; + var types = $('input[name="args[types]"]').val() || ''; + + // AJAX Request zur Quick Navigation API + currentRequest = $.ajax({ + url: window.location.pathname + window.location.search, + method: 'GET', + data: { + 'rex-api-call': 'quicknavigation_media_search', + 'term': searchTerm, + 'category_id': categoryId, + 'opener_input_field': openerInputField, + 'types': types + }, + dataType: 'json', + success: function(response) { + currentRequest = null; + + if (response.success) { + displayQuickNavSearchResults(response.results, container); + } else { + container.html('
Fehler: ' + (response.error || 'Unbekannter Fehler') + '
'); + } + }, + error: function(xhr, status, error) { + currentRequest = null; + + if (status !== 'abort') { + container.html('
Fehler beim Laden der Suchergebnisse
'); + } + } + }); + } +} + + function displayQuickNavSearchResults(results, container) { + if (results.length === 0) { + container.html('
Keine Ergebnisse gefunden
'); + container.show(); + return; + } + + var html = ''; + + $.each(results, function(index, item) { + var thumbnail = ''; + + if (item.thumbnail.type === 'image') { + thumbnail = '' + item.thumbnail.alt + ''; + } else if (item.thumbnail.type === 'icon') { + thumbnail = ''; + } else if (item.thumbnail.type === 'error') { + thumbnail = ''; + } + + var detailUrl = ''; + if (item.actions.edit) { + detailUrl = item.actions.edit.url; + } + + if (item.actions.select) { + // Widget-Modus: Buttons untereinander anzeigen + html += '
' + + '
' + + '
' + thumbnail + '
' + + '
' + + '
' + item.title + '
' + + '
' + item.filename + '
' + + '
' + item.size + ' • ' + item.updatedate + '
' + + '
' + + '
' + + '
'; + + // Übernehmen-Button + html += ''; + + // Details-Button + if (detailUrl) { + html += 'Details'; + } + + html += '
'; + + } else if (detailUrl) { + // Normaler Modus: Komplettes Element als Link zur Detailansicht + html += '' + + '
' + + '
' + thumbnail + '
' + + '
' + + '
' + item.title + '
' + + '
' + item.filename + '
' + + '
' + item.size + ' • ' + item.updatedate + '
' + + '
' + + '
' + + '
'; + } else { + // Fallback ohne Link + html += '
' + + '
' + + '
' + thumbnail + '
' + + '
' + + '
' + item.title + '
' + + '
' + item.filename + '
' + + '
' + item.size + ' • ' + item.updatedate + '
' + + '
' + + '
' + + '
'; + } + }); + + container.html(html).show(); + } \ No newline at end of file diff --git a/boot.php b/boot.php index 95f2ae8..fa41486 100644 --- a/boot.php +++ b/boot.php @@ -12,6 +12,7 @@ namespace FriendsOfRedaxo\QuickNavigation; use FriendsOfRedaxo\QuickNavigation\ApiFunction\MenuRender; +use FriendsOfRedaxo\QuickNavigation\ApiFunction\MediaSearch; use FriendsOfRedaxo\QuickNavigation\Button\ArticleHistoryButton; use FriendsOfRedaxo\QuickNavigation\Button\ButtonRegistry; use FriendsOfRedaxo\QuickNavigation\Button\ArticleNavigationButton; @@ -38,8 +39,16 @@ if (rex::isBackend() && rex::getUser() && rex_backend_login::hasSession() && rex_be_controller::getCurrentPage() != '2factor_auth_verify') { if (rex::getUser()->hasPerm('quick_navigation[]')) { rex_api_function::register('quicknavigation_api', MenuRender::class); + rex_api_function::register('quicknavigation_media_search', 'rex_api_quicknavigation_media_search'); rex_view::addCssFile(rex_addon::get('quick_navigation')->getAssetsUrl('quick-navigation.css')); + rex_view::addCssFile(rex_addon::get('quick_navigation')->getAssetsUrl('media-live-search.css')); rex_view::addJsFile(rex_addon::get('quick_navigation')->getAssetsUrl('quick-navigation.js')); + rex_view::addJsFile(rex_addon::get('quick_navigation')->getAssetsUrl('media-live-search.js')); + + // Media Live-Search Einstellung für aktuellen User + $userId = rex::getUser()->getId(); + $mediaLiveSearchEnabled = rex_addon::get('quick_navigation')->getConfig('quick_navigation_media_livesearch' . $userId, 1); // Default: aktiviert + rex_view::setJsProperty('QUICKNAV_MEDIA_LIVESEARCH_ENABLED', (bool) $mediaLiveSearchEnabled); $userId = rex::getUser()->getId(); if (rex_addon::get('quick_navigation')->getConfig('quick_navigation_artdirections' . $userId) != '1') { diff --git a/lang/de_de.lang b/lang/de_de.lang index d4c42c9..0ac3f5f 100644 --- a/lang/de_de.lang +++ b/lang/de_de.lang @@ -28,3 +28,10 @@ quick_navigation_yform_add = Datensatz hinzufügen in: quick_navigation_media_sort_alpha = Nach Dateinamen sortieren (A-Z) quick_navigation_media_sort_date = Nach Datum sortieren (neueste zuerst) quick_navigation_media_sort_title = Nach Titel sortieren (A-Z) + +# Media Live-Search +quick_navigation_media_livesearch = Medienpool Live-Suche aktivieren? +quick_navigation_media_live_search_placeholder = Live-Suche... + +# Live Search +quick_navigation_media_live_search_placeholder = Live-Suche... diff --git a/lang/en_gb.lang b/lang/en_gb.lang index 7b06c46..76eac8c 100644 --- a/lang/en_gb.lang +++ b/lang/en_gb.lang @@ -23,3 +23,12 @@ quick_navigation_favorite_article_add = Add article to: quick_navigation_favorite_category_add = Add category to: quick_navigation_yform = YForm quick_navigation_yform_add = Add dataset to: + +# Media sorting +quick_navigation_media_sort_alpha = Sort by filename (A-Z) +quick_navigation_media_sort_date = Sort by date (newest first) +quick_navigation_media_sort_title = Sort by title (A-Z) + +# Media Live-Search +quick_navigation_media_livesearch = Enable media live search? +quick_navigation_media_live_search_placeholder = Live search... diff --git a/lib/QuickNavigation/ApiFunction/MediaSearch.php b/lib/QuickNavigation/ApiFunction/MediaSearch.php new file mode 100644 index 0000000..dc91dcd --- /dev/null +++ b/lib/QuickNavigation/ApiFunction/MediaSearch.php @@ -0,0 +1,271 @@ +hasPerm('media[read]')) { + $result = [ + 'success' => false, + 'error' => 'Keine Berechtigung' + ]; + echo json_encode($result); + exit; + } + + if (strlen($searchTerm) < 2) { + $result = [ + 'success' => false, + 'error' => 'Suchbegriff zu kurz' + ]; + echo json_encode($result); + exit; + } + + try { + // Debug: Log search parameters + error_log('MediaSearch API: term=' . $searchTerm . ', category=' . $categoryId); + + // Medien suchen - korrigierter Filter + $where = []; + + // Suchbegriff in Filename und Title + $searchWildcard = '%' . $searchTerm . '%'; + $where[] = '(m.filename LIKE ? OR m.title LIKE ?)'; + $params = [$searchWildcard, $searchWildcard]; + + // Kategorie-Filter + if ($categoryId > 0) { + $where[] = 'm.category_id = ?'; + $params[] = $categoryId; + } + + // Types-Filter (falls vorhanden) + if (!empty($types)) { + $typeArray = explode(',', $types); + $typePlaceholders = str_repeat('?,', count($typeArray) - 1) . '?'; + $where[] = 'm.filename REGEXP ?'; + $params[] = '\.(' . implode('|', array_map('preg_quote', $typeArray)) . ')$'; + } + + // SQL Query aufbauen + $sql = rex_sql::factory(); + $query = 'SELECT m.* FROM ' . rex::getTable('media') . ' m'; + if (!empty($where)) { + $query .= ' WHERE ' . implode(' AND ', $where); + } + $query .= ' ORDER BY m.updatedate DESC LIMIT 10'; + + error_log('MediaSearch SQL: ' . $query . ' | Params: ' . json_encode($params)); + + $sql->setQuery($query, $params); + + $results = []; + while ($sql->hasNext()) { + $media = rex_media::get($sql->getValue('filename')); + if ($media) { + $thumbnail = $this->generateThumbnail($media); + + // Dateigröße formatieren + $size = rex_formatter::bytes($media->getSize()); + + // Update-Datum formatieren + $updatedate = rex_formatter::intlDate($media->getValue('updatedate'), 'short'); + + // Actions basierend auf opener_input_field + $actions = $this->generateActions($media, $openerInputField); + + $results[] = [ + 'title' => $media->getTitle() ?: $media->getFilename(), + 'filename' => $media->getFilename(), + 'size' => $size, + 'updatedate' => $updatedate, + 'thumbnail' => $thumbnail, + 'actions' => $actions + ]; + } + $sql->next(); + } + + $result = [ + 'success' => true, + 'results' => array_slice($results, 0, 10) // Maximal 10 Ergebnisse + ]; + + echo json_encode($result); + exit; + + } catch (Exception $e) { + $result = [ + 'success' => false, + 'error' => 'Fehler bei der Suche: ' . $e->getMessage() + ]; + echo json_encode($result); + exit; + } + } + + private function generateThumbnail($media) + { + $isImage = in_array($media->getExtension(), ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']); + + if ($isImage) { + try { + $thumbnailUrl = rex_media_manager::getUrl('rex_mediabutton_preview', $media->getFileName()); + return [ + 'type' => 'image', + 'src' => $thumbnailUrl, + 'alt' => $media->getTitle() ?: $media->getFilename() + ]; + } catch (Exception $e) { + // Fallback zu Icon + } + } + + // Icon basierend auf Dateierweiterung (FontAwesome 6) + $icon = $this->getFileIcon($media->getExtension()); + + return [ + 'type' => 'icon', + 'icon' => $icon, + 'title' => strtoupper($media->getExtension()) . ' Datei' + ]; + } + + private function getFileIcon($extension) + { + $iconMap = [ + // Bilder + 'jpg' => 'fa-regular fa-image', + 'jpeg' => 'fa-regular fa-image', + 'png' => 'fa-regular fa-image', + 'gif' => 'fa-regular fa-image', + 'webp' => 'fa-regular fa-image', + 'svg' => 'fa-regular fa-image', + 'bmp' => 'fa-regular fa-image', + 'tiff' => 'fa-regular fa-image', + + // Dokumente + 'pdf' => 'fa-regular fa-file-pdf', + 'doc' => 'fa-regular fa-file-word', + 'docx' => 'fa-regular fa-file-word', + 'xls' => 'fa-regular fa-file-excel', + 'xlsx' => 'fa-regular fa-file-excel', + 'ppt' => 'fa-regular fa-file-powerpoint', + 'pptx' => 'fa-regular fa-file-powerpoint', + 'txt' => 'fa-regular fa-file-lines', + 'rtf' => 'fa-regular fa-file-lines', + + // Archive + 'zip' => 'fa-regular fa-file-zipper', + 'rar' => 'fa-regular fa-file-zipper', + '7z' => 'fa-regular fa-file-zipper', + 'tar' => 'fa-regular fa-file-zipper', + 'gz' => 'fa-regular fa-file-zipper', + + // Code + 'html' => 'fa-regular fa-file-code', + 'css' => 'fa-regular fa-file-code', + 'js' => 'fa-regular fa-file-code', + 'php' => 'fa-regular fa-file-code', + 'xml' => 'fa-regular fa-file-code', + 'json' => 'fa-regular fa-file-code', + + // Video + 'mp4' => 'fa-regular fa-file-video', + 'avi' => 'fa-regular fa-file-video', + 'mov' => 'fa-regular fa-file-video', + 'wmv' => 'fa-regular fa-file-video', + 'flv' => 'fa-regular fa-file-video', + 'webm' => 'fa-regular fa-file-video', + + // Audio + 'mp3' => 'fa-regular fa-file-audio', + 'wav' => 'fa-regular fa-file-audio', + 'flac' => 'fa-regular fa-file-audio', + 'aac' => 'fa-regular fa-file-audio', + 'ogg' => 'fa-regular fa-file-audio', + ]; + + return $iconMap[strtolower($extension)] ?? 'fa-regular fa-file'; + } + + private function generateActions($media, $openerInputField) + { + $actions = []; + + // Edit-Link (Details) + $actions['edit'] = [ + 'url' => rex_url::backendPage('mediapool/media', [ + 'opener_input_field' => $openerInputField, + 'file_id' => $media->getId(), + 'file_category_id' => $media->getCategoryId() + ]) + ]; + + // Wenn opener_input_field gesetzt ist, dann Auswahl-Modus + if (!empty($openerInputField)) { + if (strpos($openerInputField, '[]') !== false || strpos($openerInputField, 'list') !== false) { + // Medialist + $actions['select'] = [ + 'type' => 'medialist', + 'filename' => $media->getFilename(), + 'title' => $media->getTitle() ?: $media->getFilename(), + 'label' => 'Übernehmen' + ]; + } else { + // Einzelmedium + $actions['select'] = [ + 'type' => 'media', + 'filename' => $media->getFilename(), + 'title' => $media->getTitle() ?: $media->getFilename(), + 'label' => 'Übernehmen' + ]; + } + } + + return $actions; + } + + public function requiresCsrfProtection() + { + return false; + } +} diff --git a/lib/rex_api_quicknavigation_media_search.php b/lib/rex_api_quicknavigation_media_search.php new file mode 100644 index 0000000..f530a84 --- /dev/null +++ b/lib/rex_api_quicknavigation_media_search.php @@ -0,0 +1,10 @@ +getId(); // Einstellungen speichern if (rex_post('formsubmit', 'string') == '1') { - $package->setConfig(rex_post('config', [ - ['quick_navigation_favs' . $user, 'array[int]'], - ['quick_navigation_ignoreoffline' . $user, 'int'], - ['quick_navigation_artdirections' . $user, 'int'], - // Medienpool-Sortier-Button-Option entfernt - ])); + // Standard-Werte für Checkboxen setzen (falls nicht gecheckt) + $config = rex_post('config', 'array'); + + // Checkbox-Werte normalisieren + $config['quick_navigation_ignoreoffline' . $user] = isset($config['quick_navigation_ignoreoffline' . $user]) ? 1 : 0; + $config['quick_navigation_artdirections' . $user] = isset($config['quick_navigation_artdirections' . $user]) ? 1 : 0; + $config['quick_navigation_media_livesearch' . $user] = isset($config['quick_navigation_media_livesearch' . $user]) ? 1 : 0; + + $package->setConfig($config); echo rex_view::success($package->i18n('quick_navigation_config_saved')); } @@ -73,6 +76,19 @@ $fragment->setVar('elements', $formElements, false); $content .= $fragment->parse('core/form/checkbox.php'); +// Enable / Disable media live search +$formElements = []; +$n = []; +$n['label'] = ''; +// Standard ist aktiviert (1), nur wenn explizit deaktiviert (0) dann nicht checked +$mediaLiveSearchValue = $package->getConfig('quick_navigation_media_livesearch' . $user); +$isChecked = ($mediaLiveSearchValue === null || $mediaLiveSearchValue === '' || $mediaLiveSearchValue == '1'); +$n['field'] = ''; +$formElements[] = $n; +$fragment = new rex_fragment(); +$fragment->setVar('elements', $formElements, false); +$content .= $fragment->parse('core/form/checkbox.php'); + // Medienpool-Sortier-Button-Option entfernt // Save-Button