diff --git a/assets/js/backend.js b/assets/js/backend.js new file mode 100644 index 0000000..b79e19e --- /dev/null +++ b/assets/js/backend.js @@ -0,0 +1,84 @@ +/** + * URL Generator - Backend JavaScript + * Adds click-to-copy functionality for URL call snippets + */ + +(function($) { + 'use strict'; + + $(document).ready(function() { + // Add click-to-copy functionality to code elements with data-copy-target attribute + $('.url-code-copy').on('click', function(e) { + e.preventDefault(); + + var $this = $(this); + var targetId = $this.data('copy-target'); + var $textarea = $('#' + targetId); + + if ($textarea.length === 0) { + console.error('Copy target textarea not found:', targetId); + return; + } + + var textToCopy = $textarea.val(); + + // Use the Clipboard API if available, fallback to older method + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(textToCopy).then(function() { + showCopyFeedback($this); + }).catch(function(err) { + fallbackCopyToClipboard($textarea, $this); + }); + } else { + fallbackCopyToClipboard($textarea, $this); + } + }); + + // Add keyboard accessibility (Enter or Space to trigger copy) + $('.url-code-copy').on('keydown', function(e) { + // Enter or Space key + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + $(this).trigger('click'); + } + }); + }); + + /** + * Fallback copy method for older browsers or non-HTTPS contexts + */ + function fallbackCopyToClipboard($textarea, $element) { + $textarea[0].select(); + + try { + document.execCommand('copy'); + showCopyFeedback($element); + } catch (err) { + console.error('Failed to copy text: ', err); + } + } + + /** + * Show visual feedback when text is copied + */ + function showCopyFeedback($element) { + var $icon = $element.find('.rex-icon'); + + // Change icon to checkmark temporarily + if ($icon.length) { + $icon.removeClass('fa-copy').addClass('fa-check'); + } + + // Add success class + $element.addClass('url-copied'); + + // Reset after 2 seconds + setTimeout(function() { + if ($icon.length) { + $icon.removeClass('fa-check').addClass('fa-copy'); + } + $element.removeClass('url-copied'); + }, 2000); + } + +})(jQuery); diff --git a/assets/styles.css b/assets/styles.css index 3263a7c..87c097b 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -241,3 +241,30 @@ .addon-url-data-table .label-default { background: #ccc; } + +/* Click-to-copy styles */ +.url-code-copy { + cursor: pointer; + transition: all 0.2s ease; + position: relative; + padding: 2px 8px; + border-radius: 3px; +} + +.url-code-copy:hover { + background-color: rgba(0, 123, 255, 0.1); +} + +.url-code-copy:focus { + outline: 2px solid rgba(0, 123, 255, 0.5); + outline-offset: 2px; +} + +.url-code-copy.url-copied { + background-color: rgba(40, 167, 69, 0.2); +} + +.url-code-copy .rex-icon { + font-size: 0.9em; + margin-right: 4px; +} diff --git a/boot.php b/boot.php index 566ded5..50ad98d 100644 --- a/boot.php +++ b/boot.php @@ -95,6 +95,7 @@ if (rex::isBackend() && rex::getUser() !== null) { rex_view::addCssFile($addon->getAssetsUrl('styles.css')); + rex_view::addJsFile($addon->getAssetsUrl('js/backend.js')); } if (null !== Url::getRewriter() && Url::getRewriter()->getSeoTagsExtensionPoint() !== '') { diff --git a/fragments/url/profiles/card.php b/fragments/url/profiles/card.php index a59d655..d615560 100644 --- a/fragments/url/profiles/card.php +++ b/fragments/url/profiles/card.php @@ -96,9 +96,11 @@ class="btn btn-primary btn-xs pull-right" ?>
Aufruf via
- rex_getUrl('', '', ['= htmlspecialchars($profile['namespace'] ?? '') ?>' => {id}])
+ rex_getUrl('', '', ['= rex_escape($profile['namespace'] ?? '') ?>' => {id}])
+
oder via Artikel
- rex_article::get(= $article->getId() ?>)->getUrl(['= htmlspecialchars($profile['namespace'] ?? '') ?>' => {id}])
+ rex_article::get(= $article->getId() ?>)->getUrl(['= rex_escape($profile['namespace'] ?? '') ?>' => {id}])
+