nodebb-plugin-pdf-secure 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-pdf-secure",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Secure PDF viewer plugin for NodeBB - prevents downloading, enables canvas-only rendering with Premium group support",
5
5
  "main": "library.js",
6
6
  "repository": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  console.log('[PDF-Secure] main.js loaded');
4
4
 
5
- // Main plugin logic - PDF links open in fullscreen viewer
5
+ // Main plugin logic - PDF links become inline embedded viewers
6
6
  (async function () {
7
7
  try {
8
8
  var hooks = await app.require('hooks');
@@ -19,7 +19,7 @@ console.log('[PDF-Secure] main.js loaded');
19
19
  function interceptPdfLinks() {
20
20
  var postContents = document.querySelectorAll('[component="post/content"]');
21
21
 
22
- postContents.forEach(function (content, idx) {
22
+ postContents.forEach(function (content) {
23
23
  var pdfLinks = content.querySelectorAll('a[href$=".pdf"], a[href$=".PDF"]');
24
24
 
25
25
  pdfLinks.forEach(function (link) {
@@ -29,33 +29,102 @@ console.log('[PDF-Secure] main.js loaded');
29
29
  var href = link.getAttribute('href');
30
30
  var parts = href.split('/');
31
31
  var filename = parts[parts.length - 1];
32
- console.log('[PDF-Secure] Processing:', filename);
32
+ console.log('[PDF-Secure] Embedding:', filename);
33
33
 
34
- // Create PDF preview card with "View PDF" button
34
+ // Create inline viewer container
35
35
  var container = document.createElement('div');
36
- container.className = 'pdf-secure-card';
37
- container.innerHTML =
38
- '<div class="pdf-secure-card-icon">' +
36
+ container.className = 'pdf-secure-embed';
37
+
38
+ // Header with filename
39
+ var header = document.createElement('div');
40
+ header.className = 'pdf-secure-embed-header';
41
+ header.innerHTML =
42
+ '<div class="pdf-secure-embed-title">' +
39
43
  '<svg viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z" fill="currentColor"/></svg>' +
44
+ '<span>' + escapeHtml(decodeURIComponent(link.textContent || filename)) + '</span>' +
40
45
  '</div>' +
41
- '<div class="pdf-secure-card-info">' +
42
- '<span class="pdf-secure-card-name">' + escapeHtml(link.textContent || filename) + '</span>' +
43
- '<span class="pdf-secure-card-type">PDF Dosyası</span>' +
44
- '</div>' +
45
- '<button class="pdf-secure-card-btn" data-filename="' + escapeHtml(filename) + '">' +
46
- '<svg viewBox="0 0 24 24"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" fill="currentColor"/></svg>' +
47
- '<span>Görüntüle</span>' +
48
- '</button>';
49
-
50
- // Click handler - open viewer in new tab
51
- container.querySelector('.pdf-secure-card-btn').addEventListener('click', function (e) {
52
- e.preventDefault();
53
- var fname = this.dataset.filename;
54
- var viewerUrl = config.relative_path + '/plugins/pdf-secure/viewer?file=' + encodeURIComponent(fname);
55
- window.open(viewerUrl, '_blank', 'noopener,noreferrer');
46
+ '<div class="pdf-secure-embed-actions">' +
47
+ '<button class="pdf-secure-fullscreen-btn" title="Tam Ekran">' +
48
+ '<svg viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" fill="currentColor"/></svg>' +
49
+ '</button>' +
50
+ '</div>';
51
+ container.appendChild(header);
52
+
53
+ // Iframe for viewer
54
+ var iframeWrapper = document.createElement('div');
55
+ iframeWrapper.className = 'pdf-secure-embed-body';
56
+
57
+ var iframe = document.createElement('iframe');
58
+ iframe.className = 'pdf-secure-iframe';
59
+ iframe.src = config.relative_path + '/plugins/pdf-secure/viewer?file=' + encodeURIComponent(filename);
60
+ iframe.setAttribute('frameborder', '0');
61
+ iframe.setAttribute('allowfullscreen', 'true');
62
+ iframe.setAttribute('loading', 'lazy');
63
+
64
+ iframeWrapper.appendChild(iframe);
65
+ container.appendChild(iframeWrapper);
66
+
67
+ // Fullscreen button handler
68
+ header.querySelector('.pdf-secure-fullscreen-btn').addEventListener('click', function () {
69
+ if (iframe.requestFullscreen) {
70
+ iframe.requestFullscreen();
71
+ } else if (iframe.webkitRequestFullscreen) {
72
+ iframe.webkitRequestFullscreen();
73
+ } else if (iframe.msRequestFullscreen) {
74
+ iframe.msRequestFullscreen();
75
+ }
56
76
  });
57
77
 
58
- link.replaceWith(container);
78
+ // Check for NodeBB upload container and replace it instead of just the link
79
+ var uploadContainer = link.closest('.upload-container, .uploaded-file, .attachment, [data-upload], .img-responsive');
80
+ var nodebbPreview = link.closest('p, div');
81
+
82
+ // Also look for sibling image/icon that NodeBB might show
83
+ var prevSibling = link.previousElementSibling;
84
+ var parentParagraph = link.parentElement;
85
+
86
+ // If the link is inside a paragraph with just the link and maybe an icon, replace the whole paragraph
87
+ if (parentParagraph && parentParagraph.tagName === 'P') {
88
+ var textContent = parentParagraph.textContent.trim();
89
+ var linkText = link.textContent.trim();
90
+ // If paragraph only contains the link text (maybe with some whitespace), replace the whole paragraph
91
+ if (textContent === linkText || textContent === filename || textContent === decodeURIComponent(filename)) {
92
+ parentParagraph.replaceWith(container);
93
+ return;
94
+ }
95
+ }
96
+
97
+ // Hide any preceding SVG or image that looks like a file icon
98
+ if (prevSibling && (prevSibling.tagName === 'SVG' || prevSibling.tagName === 'IMG' || prevSibling.classList.contains('file-icon'))) {
99
+ prevSibling.style.display = 'none';
100
+ }
101
+
102
+ // If there's an upload container, replace it entirely
103
+ if (uploadContainer) {
104
+ uploadContainer.replaceWith(container);
105
+ } else {
106
+ link.replaceWith(container);
107
+ }
108
+ });
109
+ });
110
+
111
+ // Also hide any remaining large file preview icons that NodeBB might render
112
+ postContents.forEach(function (content) {
113
+ // Hide default NodeBB file preview containers for PDFs
114
+ var fileIcons = content.querySelectorAll('img[src*=".pdf"], svg.file-icon, .file-preview');
115
+ fileIcons.forEach(function (icon) {
116
+ // Check if this is near a pdf-secure-embed
117
+ var nearEmbed = icon.closest('.pdf-secure-embed');
118
+ if (!nearEmbed) {
119
+ // Check if there's a pdf-secure-embed nearby
120
+ var parent = icon.parentElement;
121
+ if (parent) {
122
+ var siblingEmbed = parent.querySelector('.pdf-secure-embed');
123
+ if (siblingEmbed) {
124
+ icon.style.display = 'none';
125
+ }
126
+ }
127
+ }
59
128
  });
60
129
  });
61
130
  }
package/static/style.less CHANGED
@@ -1,132 +1,149 @@
1
- /* PDF Secure Viewer — Card Styles for NodeBB */
1
+ /* PDF Secure Viewer — Inline Embed Styles for NodeBB */
2
+
3
+ /* Hide NodeBB default file preview icons when PDF is embedded */
4
+ [component="post/content"] {
5
+
6
+ /* Hide large SVG file icons that NodeBB shows for uploaded files */
7
+ >p>svg[viewBox],
8
+ >svg[viewBox],
9
+ >p>img[alt*=".pdf"],
10
+ >img[alt*=".pdf"],
11
+ .file-icon,
12
+ .attachment-icon {
13
+
14
+ &:has(+ .pdf-secure-embed),
15
+ &:has(~ .pdf-secure-embed) {
16
+ display: none !important;
17
+ }
18
+ }
19
+ }
2
20
 
3
- /* PDF Card - shown in posts */
4
- .pdf-secure-card {
5
- display: flex;
6
- align-items: center;
7
- gap: 12px;
8
- padding: 12px 16px;
9
- margin: 12px 0;
10
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
11
- border: 1px solid rgba(255, 255, 255, 0.1);
12
- border-radius: 12px;
13
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
14
- transition: transform 0.2s, box-shadow 0.2s;
21
+ /* Force hide any orphaned file icons near our embeds */
22
+ .pdf-secure-embed~svg,
23
+ .pdf-secure-embed~img[alt*=".pdf"],
24
+ svg:has(+ .pdf-secure-embed),
25
+ img[alt*=".pdf"]:has(+ .pdf-secure-embed) {
26
+ display: none !important;
15
27
  }
16
28
 
17
- .pdf-secure-card:hover {
18
- transform: translateY(-2px);
19
- box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25);
29
+ /* Embedded PDF Container */
30
+ .pdf-secure-embed {
31
+ margin: 16px 0;
32
+ border-radius: 12px;
33
+ overflow: hidden;
34
+ background: #1f1f1f;
35
+ border: 1px solid rgba(255, 255, 255, 0.1);
36
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
20
37
  }
21
38
 
22
- .pdf-secure-card-icon {
39
+ /* Header */
40
+ .pdf-secure-embed-header {
23
41
  display: flex;
24
42
  align-items: center;
25
- justify-content: center;
26
- width: 48px;
27
- height: 48px;
28
- background: linear-gradient(135deg, #e81224 0%, #c0392b 100%);
29
- border-radius: 10px;
30
- flex-shrink: 0;
43
+ justify-content: space-between;
44
+ padding: 10px 16px;
45
+ background: linear-gradient(135deg, #2d2d2d 0%, #252525 100%);
46
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
31
47
  }
32
48
 
33
- .pdf-secure-card-icon svg {
34
- width: 26px;
35
- height: 26px;
36
- fill: #fff;
49
+ .pdf-secure-embed-title {
50
+ display: flex;
51
+ align-items: center;
52
+ gap: 10px;
53
+ color: #fff;
54
+ font-size: 14px;
55
+ font-weight: 500;
37
56
  }
38
57
 
39
- .pdf-secure-card-info {
40
- display: flex;
41
- flex-direction: column;
42
- gap: 2px;
43
- flex: 1;
44
- min-width: 0;
58
+ .pdf-secure-embed-title svg {
59
+ width: 20px;
60
+ height: 20px;
61
+ fill: #e81224;
62
+ flex-shrink: 0;
45
63
  }
46
64
 
47
- .pdf-secure-card-name {
48
- font-size: 14px;
49
- font-weight: 600;
50
- color: #fff;
65
+ .pdf-secure-embed-title span {
51
66
  white-space: nowrap;
52
67
  overflow: hidden;
53
68
  text-overflow: ellipsis;
69
+ max-width: 400px;
54
70
  }
55
71
 
56
- .pdf-secure-card-type {
57
- font-size: 12px;
58
- color: rgba(255, 255, 255, 0.5);
72
+ .pdf-secure-embed-actions {
73
+ display: flex;
74
+ gap: 8px;
59
75
  }
60
76
 
61
- .pdf-secure-card-btn {
77
+ .pdf-secure-fullscreen-btn {
62
78
  display: flex;
63
79
  align-items: center;
64
- gap: 6px;
65
- padding: 10px 18px;
66
- background: linear-gradient(135deg, #0078d4 0%, #0066b8 100%);
80
+ justify-content: center;
81
+ width: 32px;
82
+ height: 32px;
83
+ background: rgba(255, 255, 255, 0.08);
67
84
  border: none;
68
- border-radius: 8px;
69
- color: #fff;
70
- font-size: 13px;
71
- font-weight: 600;
85
+ border-radius: 6px;
72
86
  cursor: pointer;
73
87
  transition: all 0.2s;
74
- flex-shrink: 0;
75
88
  }
76
89
 
77
- .pdf-secure-card-btn:hover {
78
- background: linear-gradient(135deg, #1a86dc 0%, #0078d4 100%);
79
- transform: scale(1.02);
90
+ .pdf-secure-fullscreen-btn:hover {
91
+ background: rgba(255, 255, 255, 0.15);
80
92
  }
81
93
 
82
- .pdf-secure-card-btn:active {
83
- transform: scale(0.98);
84
- }
85
-
86
- .pdf-secure-card-btn svg {
94
+ .pdf-secure-fullscreen-btn svg {
87
95
  width: 18px;
88
96
  height: 18px;
89
- fill: currentColor;
97
+ fill: #fff;
90
98
  }
91
99
 
92
- /* Mobile responsiveness */
93
- @media (max-width: 480px) {
94
- .pdf-secure-card {
95
- padding: 10px 12px;
96
- gap: 10px;
97
- }
100
+ /* Viewer Body */
101
+ .pdf-secure-embed-body {
102
+ position: relative;
103
+ width: 100%;
104
+ height: 600px;
105
+ background: #525659;
106
+ }
107
+
108
+ .pdf-secure-iframe {
109
+ width: 100%;
110
+ height: 100%;
111
+ border: none;
112
+ display: block;
113
+ }
98
114
 
99
- .pdf-secure-card-icon {
100
- width: 40px;
101
- height: 40px;
115
+ /* Responsive */
116
+ @media (max-width: 768px) {
117
+ .pdf-secure-embed-body {
118
+ height: 450px;
102
119
  }
103
120
 
104
- .pdf-secure-card-icon svg {
105
- width: 22px;
106
- height: 22px;
121
+ .pdf-secure-embed-title span {
122
+ max-width: 200px;
107
123
  }
124
+ }
108
125
 
109
- .pdf-secure-card-name {
110
- font-size: 13px;
126
+ @media (max-width: 480px) {
127
+ .pdf-secure-embed-body {
128
+ height: 350px;
111
129
  }
112
130
 
113
- .pdf-secure-card-btn {
114
- padding: 8px 14px;
115
- font-size: 12px;
131
+ .pdf-secure-embed-header {
132
+ padding: 8px 12px;
116
133
  }
117
134
 
118
- .pdf-secure-card-btn span {
119
- display: none;
135
+ .pdf-secure-embed-title {
136
+ font-size: 13px;
120
137
  }
121
138
 
122
- .pdf-secure-card-btn svg {
123
- margin: 0;
139
+ .pdf-secure-embed-title span {
140
+ max-width: 150px;
124
141
  }
125
142
  }
126
143
 
127
- /* Anti-print protection */
144
+ /* Anti-print */
128
145
  @media print {
129
- .pdf-secure-card {
146
+ .pdf-secure-embed {
130
147
  display: none !important;
131
148
  }
132
149
  }