nodebb-plugin-pdf-secure 1.2.20 → 1.2.21

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.2.20",
3
+ "version": "1.2.21",
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": {
Binary file
@@ -1573,47 +1573,46 @@
1573
1573
  }
1574
1574
  }
1575
1575
 
1576
- /* Premium Lock Overlay */
1577
- #premiumLockOverlay {
1576
+ /* Per-page lock overlay - her kilitli sayfa icin */
1577
+ .page-lock-overlay {
1578
+ position: absolute;
1579
+ top: 0; left: 0; right: 0; bottom: 0;
1578
1580
  display: flex;
1579
1581
  flex-direction: column;
1580
1582
  align-items: center;
1581
1583
  justify-content: center;
1582
- padding: 60px 20px;
1583
- text-align: center;
1584
1584
  background: linear-gradient(180deg, rgba(31,31,31,0.95) 0%, rgba(20,20,20,0.98) 100%);
1585
- border-top: 2px solid rgba(255, 215, 0, 0.3);
1586
- min-height: 400px;
1587
- margin: 0 auto;
1588
- max-width: 100%;
1585
+ z-index: 5;
1586
+ text-align: center;
1587
+ padding: 20px;
1589
1588
  }
1590
1589
 
1591
- .premium-lock-icon {
1592
- margin-bottom: 20px;
1590
+ .page-lock-icon {
1591
+ margin-bottom: 16px;
1593
1592
  opacity: 0.9;
1594
1593
  }
1595
1594
 
1596
- .premium-lock-pages {
1597
- font-size: 22px;
1595
+ .page-lock-title {
1596
+ font-size: 20px;
1598
1597
  font-weight: 600;
1599
1598
  color: #ffd700;
1600
- margin-bottom: 12px;
1599
+ margin-bottom: 10px;
1601
1600
  }
1602
1601
 
1603
- .premium-lock-message {
1604
- font-size: 16px;
1602
+ .page-lock-message {
1603
+ font-size: 14px;
1605
1604
  color: #a0a0a0;
1606
- margin-bottom: 28px;
1607
- max-width: 400px;
1605
+ margin-bottom: 24px;
1606
+ max-width: 320px;
1608
1607
  line-height: 1.5;
1609
1608
  }
1610
1609
 
1611
- .premium-lock-button {
1610
+ .page-lock-button {
1612
1611
  display: inline-block;
1613
- padding: 14px 40px;
1612
+ padding: 12px 36px;
1614
1613
  background: linear-gradient(135deg, #ffd700 0%, #ffaa00 100%);
1615
1614
  color: #1a1a1a;
1616
- font-size: 16px;
1615
+ font-size: 15px;
1617
1616
  font-weight: 700;
1618
1617
  border-radius: 8px;
1619
1618
  text-decoration: none;
@@ -1621,22 +1620,29 @@
1621
1620
  box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);
1622
1621
  }
1623
1622
 
1624
- .premium-lock-button:hover {
1623
+ .page-lock-button:hover {
1625
1624
  transform: translateY(-2px);
1626
1625
  box-shadow: 0 6px 20px rgba(255, 215, 0, 0.4);
1627
1626
  }
1628
1627
 
1629
- .premium-lock-secondary {
1630
- margin-top: 20px;
1631
- font-size: 14px;
1628
+ .page-lock-secondary {
1629
+ margin-top: 16px;
1630
+ font-size: 13px;
1632
1631
  color: #888;
1633
1632
  font-style: italic;
1634
1633
  }
1635
1634
 
1635
+ /* Blur locked page content - even if overlay removed, content unreadable */
1636
+ .page-locked-blur canvas,
1637
+ .page-locked-blur .textLayer,
1638
+ .page-locked-blur .annotationLayer {
1639
+ filter: blur(8px) !important;
1640
+ -webkit-filter: blur(8px) !important;
1641
+ }
1642
+
1636
1643
  /* Locked Thumbnails */
1637
1644
  .thumbnail.locked {
1638
- opacity: 0.4;
1639
- cursor: not-allowed;
1645
+ opacity: 0.5;
1640
1646
  position: relative;
1641
1647
  }
1642
1648
 
@@ -1654,22 +1660,17 @@
1654
1660
  }
1655
1661
 
1656
1662
  @media (max-width: 768px) {
1657
- #premiumLockOverlay {
1658
- padding: 40px 16px;
1659
- min-height: 300px;
1660
- }
1661
-
1662
- .premium-lock-pages {
1663
- font-size: 18px;
1663
+ .page-lock-title {
1664
+ font-size: 17px;
1664
1665
  }
1665
1666
 
1666
- .premium-lock-message {
1667
- font-size: 14px;
1667
+ .page-lock-message {
1668
+ font-size: 13px;
1668
1669
  }
1669
1670
 
1670
- .premium-lock-button {
1671
- padding: 12px 32px;
1672
- font-size: 14px;
1671
+ .page-lock-button {
1672
+ padding: 10px 28px;
1673
+ font-size: 13px;
1673
1674
  }
1674
1675
  }
1675
1676
  </style>
@@ -2224,24 +2225,53 @@
2224
2225
  return data.buffer;
2225
2226
  }
2226
2227
 
2227
- function showPremiumLockOverlay(totalPages) {
2228
- var viewerEl = document.getElementById('viewer');
2229
- if (!viewerEl) return;
2230
-
2228
+ function injectPageLock(pageEl) {
2229
+ if (!pageEl) return;
2230
+ // Add blur class to page content
2231
+ if (!pageEl.classList.contains('page-locked-blur')) {
2232
+ pageEl.classList.add('page-locked-blur');
2233
+ }
2234
+ // Don't duplicate overlay
2235
+ if (pageEl.querySelector('.page-lock-overlay')) return;
2231
2236
  var overlay = document.createElement('div');
2232
- overlay.id = 'premiumLockOverlay';
2237
+ overlay.className = 'page-lock-overlay';
2233
2238
  overlay.innerHTML = '\
2234
- <div class="premium-lock-icon">\
2235
- <svg viewBox="0 0 24 24" width="64" height="64" fill="#ffd700">\
2239
+ <div class="page-lock-icon">\
2240
+ <svg viewBox="0 0 24 24" width="48" height="48" fill="#ffd700">\
2236
2241
  <path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2z"/>\
2237
2242
  </svg>\
2238
2243
  </div>\
2239
- <div class="premium-lock-pages">' + (totalPages - 1) + ' sayfa daha kilitli</div>\
2240
- <div class="premium-lock-message">Bu icerigi goruntulemeye devam etmek icin Premium uyelik gereklidir.</div>\
2241
- <a href="https://forumtest.ieu.app/premium" target="_blank" class="premium-lock-button">Premium Satin Al</a>\
2242
- <div class="premium-lock-secondary">Materyal yukleyerek de Premium olabilirsiniz!</div>';
2244
+ <div class="page-lock-title">Kilitli</div>\
2245
+ <div class="page-lock-message">Bu sayfayi goruntulemeye devam etmek icin Premium uyelik gereklidir.</div>\
2246
+ <a href="https://forumtest.ieu.app/premium" target="_blank" class="page-lock-button">Premium Satin Al</a>\
2247
+ <div class="page-lock-secondary">Materyal yukleyerek de Premium olabilirsiniz!</div>';
2248
+ pageEl.style.position = 'relative';
2249
+ pageEl.appendChild(overlay);
2250
+ }
2251
+
2252
+ function resetOverlayCSS(overlay) {
2253
+ overlay.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;display:flex;z-index:5;opacity:1;visibility:visible;pointer-events:auto;';
2254
+ }
2243
2255
 
2244
- viewerEl.appendChild(overlay);
2256
+ function applyPageLocks() {
2257
+ var pages = document.querySelectorAll('#viewer .page');
2258
+ pages.forEach(function (page) {
2259
+ var pageNum = parseInt(page.dataset.pageNumber || '0', 10);
2260
+ if (pageNum > 1) {
2261
+ // Ensure page is visible (undo any old display:none)
2262
+ if (page.style.display === 'none') {
2263
+ page.style.display = '';
2264
+ }
2265
+ injectPageLock(page);
2266
+ // CSS integrity: reset overlay styles in case of tampering
2267
+ var existing = page.querySelector('.page-lock-overlay');
2268
+ if (existing) resetOverlayCSS(existing);
2269
+ // Ensure blur class is present
2270
+ if (!page.classList.contains('page-locked-blur')) {
2271
+ page.classList.add('page-locked-blur');
2272
+ }
2273
+ }
2274
+ });
2245
2275
  }
2246
2276
 
2247
2277
  // ============================================
@@ -2250,18 +2280,7 @@
2250
2280
  function startPeriodicCheck() {
2251
2281
  setInterval(function () {
2252
2282
  if (!premiumInfo || premiumInfo.isPremium) return;
2253
- var pages = document.querySelectorAll('#viewer .page');
2254
- pages.forEach(function (page, idx) {
2255
- if (idx > 0 && page.style.display !== 'none') {
2256
- page.style.display = 'none';
2257
- }
2258
- });
2259
- if (!document.getElementById('premiumLockOverlay')) {
2260
- showPremiumLockOverlay(premiumInfo.totalPages);
2261
- }
2262
- if (pdfViewer && pdfViewer.currentPageNumber > 1) {
2263
- pdfViewer.currentPageNumber = 1;
2264
- }
2283
+ applyPageLocks();
2265
2284
  }, 2000);
2266
2285
  }
2267
2286
 
@@ -2272,32 +2291,41 @@
2272
2291
  var viewerEl = document.getElementById('viewer');
2273
2292
  if (!viewerEl) return;
2274
2293
 
2294
+ // Observer 1: Watch for lock overlay removal from any page
2275
2295
  new MutationObserver(function (mutations) {
2296
+ if (!premiumInfo || premiumInfo.isPremium) return;
2276
2297
  for (var i = 0; i < mutations.length; i++) {
2277
2298
  var removed = mutations[i].removedNodes;
2278
2299
  for (var j = 0; j < removed.length; j++) {
2279
- if (removed[j].id === 'premiumLockOverlay') {
2280
- showPremiumLockOverlay(premiumInfo.totalPages);
2281
- return;
2300
+ if (removed[j].classList && removed[j].classList.contains('page-lock-overlay')) {
2301
+ var pageEl = mutations[i].target;
2302
+ if (pageEl && pageEl.classList && pageEl.classList.contains('page')) {
2303
+ injectPageLock(pageEl);
2304
+ }
2282
2305
  }
2283
2306
  }
2284
2307
  }
2285
- }).observe(viewerEl, { childList: true });
2308
+ }).observe(viewerEl, { childList: true, subtree: true });
2286
2309
 
2310
+ // Observer 2: Watch for style/class tampering on overlays and pages
2287
2311
  new MutationObserver(function (mutations) {
2312
+ if (!premiumInfo || premiumInfo.isPremium) return;
2288
2313
  for (var i = 0; i < mutations.length; i++) {
2289
- var m = mutations[i];
2290
- if (m.type === 'attributes' && m.attributeName === 'style') {
2291
- var target = m.target;
2292
- if (target.classList && target.classList.contains('page')) {
2293
- var pageNum = parseInt(target.dataset.pageNumber || '0', 10);
2294
- if (pageNum > 1 && target.style.display !== 'none') {
2295
- target.style.display = 'none';
2296
- }
2314
+ var target = mutations[i].target;
2315
+ if (!target || !target.classList) continue;
2316
+ // Overlay CSS tampered - reset it
2317
+ if (target.classList.contains('page-lock-overlay')) {
2318
+ resetOverlayCSS(target);
2319
+ }
2320
+ // Blur class removed from locked page - re-add it
2321
+ if (target.classList.contains('page') && !target.classList.contains('page-locked-blur')) {
2322
+ var pageNum = parseInt(target.dataset.pageNumber || '0', 10);
2323
+ if (pageNum > 1) {
2324
+ target.classList.add('page-locked-blur');
2297
2325
  }
2298
2326
  }
2299
2327
  }
2300
- }).observe(viewerEl, { childList: true, subtree: true, attributes: true, attributeFilter: ['style'] });
2328
+ }).observe(viewerEl, { subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
2301
2329
  }
2302
2330
 
2303
2331
  // Auto-load PDF if config is present (injected by NodeBB plugin)
@@ -2399,7 +2427,7 @@
2399
2427
  // Premium Gate: Client-side page restriction for non-premium users
2400
2428
  if (config.isPremium === false && pdfDoc && pdfDoc.numPages > 1) {
2401
2429
  premiumInfo = Object.freeze({ isPremium: false, totalPages: pdfDoc.numPages });
2402
- showPremiumLockOverlay(pdfDoc.numPages);
2430
+ applyPageLocks();
2403
2431
  startPeriodicCheck();
2404
2432
  setupAntiTampering();
2405
2433
  } else {
@@ -2477,9 +2505,11 @@
2477
2505
  }).promise;
2478
2506
 
2479
2507
  const thumb = document.createElement('div');
2480
- thumb.className = 'thumbnail' + (i === 1 ? ' active' : '');
2508
+ const isLocked = premiumInfo && !premiumInfo.isPremium && i > 1;
2509
+ thumb.className = 'thumbnail' + (i === 1 ? ' active' : '') + (isLocked ? ' locked' : '');
2481
2510
  thumb.dataset.page = i;
2482
- thumb.innerHTML = `<div class="thumbnailNum">${i}</div>`;
2511
+ thumb.innerHTML = `<div class="thumbnailNum">${i}</div>` +
2512
+ (isLocked ? '<div class="thumbnail-lock">\u{1F512}</div>' : '');
2483
2513
  thumb.insertBefore(canvas, thumb.firstChild);
2484
2514
 
2485
2515
  thumb.onclick = () => {
@@ -2525,6 +2555,12 @@
2525
2555
  eventBus.on('pagerendered', (evt) => {
2526
2556
  injectAnnotationLayer(evt.pageNumber);
2527
2557
 
2558
+ // Re-inject lock overlay if this is a locked page (PDF.js re-render removes it)
2559
+ if (premiumInfo && !premiumInfo.isPremium && evt.pageNumber > 1) {
2560
+ var pageEl = document.querySelector('#viewer .page[data-page-number="' + evt.pageNumber + '"]');
2561
+ if (pageEl) injectPageLock(pageEl);
2562
+ }
2563
+
2528
2564
  // Rotation is handled natively by PDF.js via pagesRotation
2529
2565
  });
2530
2566