171 Commits

Author SHA1 Message Date
dillard 9cdfbe7a10 Fase 3 feedback round: style preview, icon upload, font choice, optional bitcoin amount, WPA3
- Default the Owner select to the creator's own account instead of "All", so
  superadmin-created codes no longer silently become visible to every admin
  (the underlying NULL-fallback sharing behavior for an explicit "All" choice
  is unchanged).
- Add a live color/precision/size preview swatch next to the preset picker.
- Let the frame text use a chosen DejaVu font + font size instead of a fixed
  GD bitmap font.
- Add an optional self-uploaded icon rendered above the qr code (not embedded
  in it, so scanability is unaffected).
- Make the Bitcoin qr amount optional; a standing wallet address is useful
  without forcing a one-off amount per code.
- Add a WPA3 option to the WiFi qr encryption select.
- Fix a real bug surfaced while testing the preview/style JS: qrcode_options.php
  was included once per static qr type (16 times on one page) and each
  inclusion re-executed <script src="qrcode-style-tools.js">, so every button
  click fired once per type - e.g. saving one preset wrote 16 duplicate rows,
  and every tab except the first ("Text") had dead random-style/preset
  buttons since only the first DOM match ever got a listener. Moved the
  script include to load once per page and rewrote the JS to scope every
  lookup to the triggering element's own tab-pane/form instead of relying on
  getElementById's first-match behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196bLhZhuxwK3MkHuKLe5WB
2026-07-09 23:11:36 +02:00
dillard be9164850a Fase 3 priority 2: presets, random style, qr scanner, PWA
Preset system: qr_presets table (migration 005) plus a presets.php AJAX
endpoint (list/save/delete, CSRF-protected, scoped to the logged-in user's
own id - presets are personal, never shared across accounts). UI/JS lives in
dist/js/qrcode-style-tools.js.

Random style button: client-side only, fills foreground/background with a
random hex color pair (playful randomize, no contrast/scannability
guarantee).

Qr scanner (scan_qrcode.php): camera or image upload, decoded entirely
client-side via html5-qrcode (CDN, pinned to 2.3.8).

PWA: manifest.json + service-worker.js, icons generated from the existing
DynamicQRCode_Original.png glyph. The service worker only caches static
assets (css/js/images) and deliberately never touches PHP pages, since those
carry CSRF tokens and session-specific content that must never be cached.

Fixes a gap found while testing: qrcode_options.php is only a shared partial
for the static qr forms - the dynamic qr form (form_dynamic_add.php) has its
own separate copy of the foreground/background/level/size/filename/format
fields (pre-existing structure, not something introduced here). That meant
frame_text and the new preset/random-style UI never showed up on the
dynamic qr page. Added the same fields there too, verified with a dynamic qr
plus frame text (150x180px, the expected +30px padding).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 00:49:45 +02:00
dillard f7d2de355e Fase 3 v1: new qr types, svg export, clipboard, frame text, batch CSV
New static qr types:
- App Link: Android intent:// links (with package + optional browser
  fallback) or a generic custom-scheme URI. iOS Universal Links need no
  special encoding (they're just plain https:// URLs).
- Bluetooth: device name + MAC address. Purely informational, since unlike
  WIFI:/vCard there's no OS-native "scan to pair" convention.

SVG export: already worked (format whitelist/dropdown existed since Fase 1),
verified rather than reimplemented.

Copy-to-clipboard button next to the download button on both qr list tables,
using the Clipboard API against a fetched blob.

Optional frame text label rendered below the qr code via GD after
generation (raster formats only, no-op for svg/eps).

Batch CSV upload (batch_qrcode.php): filename,link rows create dynamic qr
codes with sane defaults, downloadable as a zip. Required refactoring
Qrcode-intchil.php's generation path (previously always redirected/exited
via failure()/success(), which can't run in a loop) into a private
renderAndStore() that throws instead, shared by addQrcode() and the new
addQrcodeBatch(). Qrcode.php's addQrcodeBatch() is a separate, deliberately
duplicated implementation instead, since its generation logic is small
enough that duplication carries less risk than refactoring the working
external-API code path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 00:03:01 +02:00
dillard b5ef4ac0cb Admin-scoped user accounts, secure qr code storage, PHP 8.4 upgrade
Admin-scoped users (answers: who can create a 'user' account, only super or
also an admin within their own scope?):
- New owner_admin_id column on users (migration 004). NULL means created by
  super (company-wide, previous behavior); otherwise scoped to that admin's
  own codes.
- Users::addUser/editUser/deleteUser now allow an 'admin' session, but force
  type='user' and owner_admin_id to their own id regardless of submitted
  input. user.php/users.php open up to admins with a restricted UI (no type
  picker, listing limited to their own created users).
- New qr_compute_scope_owner_id()/qr_apply_owner_scope()/qr_has_full_visibility()
  helpers in includes/security.php, replacing the ad-hoc type==='admin' checks
  in index.php, dynamic_qrcodes.php, static_qrcodes.php and bulk_action.php.
  A 'user' account created by an admin is now scoped to that admin's codes
  instead of seeing everything company-wide.

Qr code storage hardening: images were served as plain static files under the
document root with no auth check at all. Storage now lives outside the web
root; qrcode_image.php and qrcode_zip_download.php gate access with the same
permission model as the list pages, and the bulk zip download is bound to the
session that generated it.

PHP 8.4 + chillerlan/php-qrcode 6.0.1: bumped since this is a dockerized app,
so the PHP version shipped doesn't matter to end users. Note: the 6.0.1 tag
itself only requires PHP 8.2 - the earlier "needs 8.4" read was from an
unpinned clone of master, which has since moved past the tag. Fixed along the
way, surfaced by testing on 8.4:
- The hardcoded Imagick build (an old pinned master commit, workaround for
  3.7.0 being broken on PHP 8.3+) no longer compiles on 8.4. Imagick 3.8.1 is
  now a normal stable release, so the workaround is gone.
- config.php had display_errors=On + error_reporting(E_ALL), so PHP 8.4's new
  deprecation notices got dumped straight into the response before
  session_start() could run, breaking login outright. Also an info-disclosure
  risk on its own. Now logged instead of displayed.
- MysqliDb::insertMulti() had an implicit nullable parameter, now explicit.
- includes/auth_validate.php redirected unauthenticated requests but never
  called exit(), so the rest of the script kept running.
- Dockerfile.fpm was missing both git (needed to clone chillerlan/php-qrcode)
  and the imagick extension entirely.

Also removes the unused sample qr code images that shipped in the original
repo; storage now lives outside the document root so they were never going
to be served again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 20:38:58 +02:00
dillard a1ab16d54c Fase 2: read-only user role with per-category view toggles
Adds a third account type 'user' alongside super/admin: no create/edit/delete
rights on qr codes, view access to dynamic/static lists gated per-account by
two admin-controlled toggles (can_view_static, can_view_dynamic), and always
full visibility into the dashboard/reports regardless of those toggles.

- New columns can_view_static/can_view_dynamic on users (migrations/003)
- Users class + form_users.php: 'user' type option with the two toggles
- Access control: dynamic_qrcode.php/static_qrcode.php/bulk_action.php reject
  all mutations for type=user; dynamic_qrcodes.php/static_qrcodes.php enforce
  the view toggle and show all codes (no owner scoping, since 'user' owns none)
- Sidebar and list tables hide add/edit/delete/bulk UI for the read-only role
- index.php dashboard stats are unscoped for both 'super' and 'user'

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 16:37:25 +02:00
dillard feb5380b28 Security hardening: CSRF, rate limiting, session/password policy, audit log
Fixes critical pre-existing issues found during review: bulk_action.php had no
auth check at all (unauthenticated download/delete of any qrcode) and built a
table name from unwhitelisted user input (SQL injection); the QR generator
classes wrote files from unvalidated filename/format, allowing path traversal
and arbitrary file writes. Also pins chillerlan/php-qrcode to 5.0.5 since
master now requires PHP 8.4, breaking the PHP 8.3 build.

- CSRF tokens on all POST forms and the bulk_action.php JSON endpoint
- Login rate limiting (5 attempts / 15 min) via new login_attempts table
- Hardened sessions: httponly/samesite cookies, 30 min idle timeout,
  session regeneration on login
- Forced password change for the default superadmin/superadmin account
- Server-side validation in Users/DynamicQrcode/Qrcode classes
- Audit log table for auth, user, and qrcode actions
- Checked-in db schema (db/init.sql, migrations/) instead of relying on an
  opaque prebuilt db image
- Production docker-compose with Nginx + php-fpm instead of the PHP dev server

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 15:00:33 +02:00
Giandonato Inverso 268a6a3f65 Remove unnecessary extra information from documentation 2025-09-02 21:56:03 +02:00
Giandonato Inverso 812db2bf9b Delete support section from index.html 2025-09-02 21:53:41 +02:00
Giandonato Inverso 48d7fefd02 Remove donation button from README 2025-09-02 21:52:03 +02:00
Giandonato Inverso 27a5dbd466 Merge pull request #144 from CLAlberto/master
fix(read.php): replace deprecated FILTER_SANITIZE_STRING for PHP 8.3 …
2025-05-08 08:56:07 +02:00
CLAlberto 2c3f2bb030 fix(read.php): replace deprecated FILTER_SANITIZE_STRING for PHP 8.3 compatibility
### Problem

The usage of `FILTER_SANITIZE_STRING` in `read.php` causes a deprecation warning in PHP 8.1 and breaks functionality entirely in PHP 8.3, as the constant was removed.

### Solution

This commit replaces:
```php
filter_input(INPUT_GET, 'id', FILTER_SANITIZE_STRING);



with a safer and future-proof alternative:

$id = filter_input(INPUT_GET, 'id', FILTER_UNSAFE_RAW);
$id = trim(strip_tags($id));


> _Thanks for maintaining this project! Happy to contribute._ 😊
2025-05-06 16:16:22 +02:00
Giandonato Inverso e4d3af69b5 Merge pull request #143 from angelosleebos/patch-1
Make environment variables compatible for other platforms
2025-04-14 12:19:34 +02:00
Giandonato Inverso 938d934b51 Merge pull request #142 from rafinou62/patch-1
Update read.php prevent SQL Injection & XSS attacks
2025-04-14 12:18:32 +02:00
Angelo Sleebos cee6f4d3d5 Make environment variables compatible for other platforms
Make environment variables compatible for other platforms
2025-04-07 01:11:17 +02:00
Raphaël Wanecque 1465ce02ad Update read.php prevent SQL Injection & XSS attacks 2025-03-26 17:04:33 +01:00
giandonato.inverso@edempg.it ba57334142 edit demo url 2025-03-22 15:51:02 +01:00
giandonato.inverso@edempg.it 615d983828 config rollback 2025-03-20 23:38:55 +01:00
giandonato.inverso@edempg.it b245694824 Merge remote-tracking branch 'origin/master' 2025-03-19 23:40:50 +01:00
giandonato.inverso@edempg.it 378968576f bug fix Class Qrcode not found 2025-03-19 23:40:36 +01:00
Giandonato Inverso 0d4368575b Merge pull request #136 from MickGe/patch-1
add cookies secure flags
2025-02-09 16:16:53 +01:00
MickGe 05e80a075a add cookies secure flags 2025-02-07 11:35:24 +01:00
giandonato.inverso@edempg.it 92a1f17dbe updated readme and docs 2025-01-21 22:41:14 +01:00
giandonato.inverso@edempg.it d1505e1e08 updated readme 2025-01-12 23:50:15 +01:00
giandonato.inverso@edempg.it f05c073ad4 Local setup eliminated and documentation updated 2025-01-12 23:48:49 +01:00
giandonato.inverso@edempg.it 79d9ac71a3 Merge remote-tracking branch 'origin/master' 2025-01-12 19:48:31 +01:00
Giandonato Inverso ffc5b64967 Merge pull request #130 from Shineson1001/feature/129-QRCodeGeneratorSwitch
🐛 Global switch for the QR code generator (#129)
2025-01-02 12:04:55 +01:00
Shine 869dd2c799 🐛 Global switch for the QR code generator (#129) 2024-12-31 20:48:56 +01:00
giandonato.inverso@edempg.it 531820e2a8 Revert "Revert "Instead using external API (api.qrserver.com) .. use chillerlan/php-qrcode""
This reverts commit 630bbf3aea.
2024-12-23 18:52:11 +01:00
giandonato.inverso@edempg.it 630bbf3aea Revert "Instead using external API (api.qrserver.com) .. use chillerlan/php-qrcode"
This reverts commit 9b65bb8020.
2024-12-23 18:49:05 +01:00
Giandonato Inverso 8238a81494 Merge pull request #128 from Shineson1001/feature/116-EventIncorrectTimeZone
🐛 Event: Incorrect Time Zone
2024-12-20 20:02:32 +01:00
Shine cc45f0659c 🐛 Event: Incorrect Time Zone
- Add "Time zone" input field.
- 24-Hour time format.
- Set Min-Year and Max-Year dynamically
2024-12-19 22:34:42 +01:00
Giandonato Inverso ad37224890 Merge pull request #127 from Shineson1001/feature/88-SelfHostedQRCodeGenerator
Instead using external API (api.qrserver.com) .. use chillerlan/php-qrcode
2024-12-15 16:35:29 +01:00
Shine 9b65bb8020 Instead using external API (api.qrserver.com) .. use chillerlan/php-qrcode 2024-12-15 14:36:39 +01:00
Giandonato Inverso 97481c2444 Merge pull request #126 from Shineson1001/feature/2FA-QRCodes
Add 2FA QR Code
2024-12-14 09:17:50 +01:00
Shine 8151de4b9a Add 2FA QR Code
Save your 2FA secrets to QR-Code.
2024-12-13 23:42:57 +01:00
Giandonato Inverso 477d7803f0 available plugins 2024-10-20 16:32:58 +02:00
Giandonato Inverso 8cc5294a84 Update README.md 2024-10-20 11:07:12 +02:00
giandonato.inverso@edempg.it e6aac416d9 bug fix in helpers.php 2024-04-21 12:55:03 +02:00
giandonato.inverso@edempg.it e84f853d80 edit readme 2024-04-21 12:38:35 +02:00
giandonato.inverso@edempg.it 7d5a4b889c bug fix 2024-04-18 20:54:04 +02:00
giandonato.inverso@edempg.it c157815ca8 fix in bulk action 2024-04-18 20:44:22 +02:00
giandonato.inverso@edempg.it 2d9b6162e1 readme 2024-04-18 20:35:04 +02:00
giandonato.inverso@edempg.it 096c239715 bug fix in read.php, bump version in footer, NEW: bulk delete 2024-04-18 20:33:20 +02:00
giandonato.inverso@edempg.it 5bdcb8e2bf bug fix database prefix in Qrcode class 2024-03-11 15:43:16 +01:00
giandonato.inverso@edempg.it 98ae82a040 bug fix bulk download 2024-03-04 14:36:18 +01:00
Giandonato Inverso 15b5ece4f9 Merge pull request #94 from tranmh/xss_static_qrcode
Fix Security: Stored Cross Site Scripting for static QR code
2024-03-01 09:58:29 +01:00
Minh Cuong Tran b03238b4c3 Fix Security: Stored Cross Site Scripting for static QR code, see https://github.com/giandonatoinverso/PHP-Dynamic-Qr-code/issues/93 2024-03-01 08:03:31 +01:00
Giandonato Inverso 2287d98455 Merge pull request #92 from tranmh/fix_remove_DATABASE_PREFIX_for_getOne
Inconsistency of using DATABASE_PREFIX with getOne()
2024-02-29 00:25:17 +01:00
Minh Cuong Tran 3e43b72eec Inconsistency of using DATABASE_PREFIX with getOne() 2024-02-28 16:56:39 +01:00
Giandonato Inverso 5488ad0a84 Merge pull request #91 from tranmh/mixed_content_blocked
fix: mixed content blocked for http and https
2024-02-28 14:16:21 +01:00
Minh Cuong Tran 9710ae0673 fix mixed content blocked for http and https: Mixed Content: The page at 'https://localhost/qrcode/dynamic_qrcodes.php' was loaded over HTTPS, but requested an insecure stylesheet 'http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css'. This request has been blocked; the content must be served over HTTPS. 2024-02-28 13:55:44 +01:00
Giandonato Inverso 893b883dbf Merge pull request #90 from tranmh/fix_case_sensitive_filename
Fix case sensitive filename
2024-02-27 13:25:17 +01:00
Minh Cuong Tran 60620e15dd fix: case sensitive for filename 2024-02-27 13:18:52 +01:00
Minh Cuong Tran ec786d2956 fix: case sensitive for filename 2024-02-27 13:18:01 +01:00
Giandonato Inverso a4d8455e5a increased size of column link - dynamic qrcode
increased size of column link - dynamic qrcode
2024-02-14 23:58:00 +01:00
Giandonato Inverso 5ac338945d Update README.md
updated php version requirement
2024-01-03 00:43:50 +01:00
Giandonato Inverso 6e213483a4 Update README.md 2023-10-30 17:02:13 +01:00
Giandonato Inverso 8a926ac0f3 bug fix redirect url with docker installation 2023-10-17 23:24:34 +02:00
Giandonato Inverso 0360176490 bug fix 2023-10-17 00:41:15 +02:00
Giandonato Inverso 57930cf9db bug fix and documentation 2023-10-16 22:57:16 +02:00
Giandonato Inverso 6682a207b2 updated documentation 2023-10-16 22:24:15 +02:00
Giandonato Inverso c326a30afc updated documentation 2023-10-16 20:56:06 +02:00
Giandonato Inverso 28545c2245 Refactoring docker image building, NEW: added docker compose support
Signed-off-by: giandonato.inverso@edempg.it <giandonato.inverso@studenti.unimi.it>
2023-10-16 19:49:29 +02:00
Giandonato Inverso 1085a13d38 readme updated
Signed-off-by: giandonato.inverso@edempg.it <giandonato.inverso@studenti.unimi.it>
2023-10-16 13:15:53 +02:00
giandonato.inverso@edempg.it 13a807ac85 readme updated
Signed-off-by: giandonato.inverso@edempg.it <giandonato.inverso@studenti.unimi.it>
2023-10-16 13:15:07 +02:00
Giandonato Inverso 876b6736b6 refactoring of table database, added script for upgrading to versions >= 2.0, added multi-user support
Signed-off-by: giandonato.inverso@edempg.it <giandonato.inverso@studenti.unimi.it>
2023-10-16 13:10:57 +02:00
Giandonato Inverso e36c26a37a readme updated
Signed-off-by: giandonato.inverso@edempg.it <giandonato.inverso@studenti.unimi.it>
2023-10-16 01:43:56 +02:00
Giandonato Inverso a2ce4b38b6 eliminazione file superflui, spostamento file read.php all'interno del progetto, aggiunta astrazione classe Qrcode, miglioramento download bulk, refactoring generale
Signed-off-by: giandonato.inverso@edempg.it <giandonato.inverso@studenti.unimi.it>
2023-10-16 01:41:27 +02:00
Giandonato Inverso 12e358b87c Bug fix login.php
removed .min extension adminlte js file
2023-09-16 14:51:04 +02:00
Giandonato Inverso 617d08c13d paypal donations 2023-09-05 23:31:38 +02:00
Giandonato Inverso 5612558ad1 Update README.md 2023-09-05 23:29:51 +02:00
giandonato.inverso@edempg.it e827b55f6b Merge remote-tracking branch 'origin/master' 2023-08-22 17:15:09 +02:00
giandonato.inverso@edempg.it 3ea78d6a84 doc update
Signed-off-by: giandonato.inverso@edempg.it <Zannabianca20>
2023-08-22 17:14:52 +02:00
Giandonato Inverso e947153e74 Update Dockerfile 2023-01-25 18:51:10 +01:00
Giandonato Inverso 4e3cfeeaa3 Merge pull request #52 from AyhamAl-Ali/fix/db_prefix
🚀 Fix DB Prefix in `read.php`
2023-01-25 18:50:15 +01:00
Ayham Al-Ali f750d7aaca Fix DB Prefix 2023-01-25 20:47:52 +03:00
Giandonato Inverso c6cb83638a Merge pull request #45 from chilluniverse/patch-1
DATABASE_HOST
2022-10-14 10:36:59 +02:00
Pascal 7e70ca94d8 DATABASE_HOST
In the docker-compose.yml is the Database_Host defined as "mariadb". If Host is not changed in the environment.php to "mariadb" as well the setup will fail
2022-10-14 01:26:06 +02:00
giandonato.inverso@edempg.it 7d7c326795 new version dockerfile 2022-09-28 16:23:44 +02:00
giandonato.inverso@edempg.it 16327a0e0a new version dockerfile 2022-09-28 16:22:18 +02:00
Giandonato Inverso 53955af19c Merge pull request #37 from 0xRenegade/feature/download-multiple-qr-img-at-once
Error Message for no qrcodes selected.
2022-09-24 22:43:16 +02:00
0xRenegade 4aab7d637a Error Message for no qrcodes selected. 2022-09-24 15:40:56 -05:00
Giandonato Inverso a6b2b29352 Merge pull request #36 from 0xRenegade/feature/download-multiple-qr-img-at-once
Feature/download multiple qr img at once
2022-09-24 22:26:36 +02:00
0xRenegade dac89ba0b2 download multiple qrcodes at once feature 2022-09-24 15:14:20 -05:00
0xRenegade f064d8f1ef add base_url() function in helpers, works with https and port number 2022-09-24 14:32:04 -05:00
0xRenegade cccf3ada43 adds in custom.css for, well, custom styles. 2022-09-24 13:09:17 -05:00
Giandonato Inverso b5aec38e45 Merge pull request #35 from 0xRenegade/issue-30/update-docker-yml-for-db-prefix
Issue 30/update docker yml for db prefix
2022-09-24 19:44:31 +02:00
Giandonato Inverso 654d395369 Merge pull request #33 from 0xRenegade/QOL/ignore-env-and-use-example-instead
QOL/ignore env and use example instead
2022-09-24 19:44:23 +02:00
0xRenegade f410322119 update docker-compose.yml for database prefix option 2022-09-24 12:19:40 -05:00
0xRenegade cbc5246163 update docs for this change 2022-09-24 11:52:34 -05:00
0xRenegade d99e8132c9 update main gitignore to add in environment.php 2022-09-24 11:44:02 -05:00
0xRenegade 99a361c0be move env to example, so we don't run into merge conflicts constantly 2022-09-24 11:42:17 -05:00
0xRenegade 64974c6c51 Merge pull request #1 from giandonatoinverso/master
sync master branch with remote
2022-09-24 11:28:55 -05:00
Giandonato Inverso 52e2347644 Merge pull request #31 from 0xRenegade/renegade/general-fixes
general fixes, fixed database prefix during install
2022-09-24 11:53:34 +02:00
0xRenegade 304b808bb6 set static attribute of class rather than non-DRY code 2022-09-24 00:24:03 -05:00
0xRenegade 9f376bd3c9 remove error_log debugging 2022-09-24 00:05:50 -05:00
0xRenegade fca443b6cc updated MysqliDb class functions to handle prefix properly. 2022-09-23 23:44:01 -05:00
0xRenegade 1f64d4cad7 if this feature is accepted in Pull Request, will need to add this back 2022-09-23 23:02:05 -05:00
0xRenegade b7b090f6c5 Merge branch 'renegade/general-fixes' of github.com:0xRenegade/PHP-Dynamic-Qr-code into renegade/general-fixes 2022-09-23 23:01:05 -05:00
0xRenegade 52d890597d some queries are manually entered, which aren't picked up by 'prefix' class attribute in database class 2022-09-23 22:59:53 -05:00
0xRenegade 40ab7b256d Merge branch 'master' into renegade/general-fixes 2022-09-23 21:05:16 -05:00
0xRenegade bbf115c2ae added in prefix by default, oops. 2022-09-23 21:00:41 -05:00
0xRenegade b433a83578 Issue #30: Fixes database_prefix option during install 2022-09-23 20:55:55 -05:00
Giandonato Inverso c005b52211 Update config.php 2022-09-24 03:24:15 +02:00
Giandonato Inverso e61c38473c Update docker-compose.yml 2022-09-24 03:23:41 +02:00
Giandonato Inverso 1243b32de1 Update environment.php 2022-09-24 03:23:20 +02:00
0xRenegade a20810d650 missing scroll bar on documentation page sidebar 2022-09-23 20:09:22 -05:00
Giandonato Inverso c4d5440453 Update add_dynamic_form.php
added support for http url
2022-09-10 11:50:19 +02:00
Giandonato Inverso 975fde9c35 Update MysqliDb.php
fix deprecated implode()
2022-09-05 20:40:26 +02:00
Giandonato Inverso af363723b2 Merge pull request #25 from nirpt/master
docker build now supports app release version code.
2022-08-29 14:09:34 +02:00
nirpt e2ff6e585b Docker build with app version added. 2022-08-29 12:32:20 +02:00
nirpt 389123fe15 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	docker/README.md
2022-08-28 13:22:12 +02:00
Giandonato Inverso a519d7bfe6 Update README.md 2022-08-28 12:33:14 +02:00
Giandonato Inverso a98c89075d doc update
Signed-off-by: giandonato.inverso@edempg.it <Zannabianca20>
2022-08-28 12:04:48 +02:00
Giandonato Inverso 00d7dfdb0b new installation process via script, elimination of data entry form for installation
Signed-off-by: giandonato.inverso@edempg.it <Zannabianca20>
2022-08-28 11:57:31 +02:00
nirpt 08ac31210c Minor refactor and cleanup 2022-08-28 11:29:20 +02:00
Giandonato Inverso fef45c401a configuration file modification, docker environment variable support 2022-08-28 11:24:32 +02:00
Giandonato Inverso 21b779c581 Merge pull request #23 from nirpt/master
docker support added
2022-08-28 10:47:05 +02:00
nirpt 618030e9d3 docker support added 2022-08-28 10:24:07 +02:00
Giandonato Inverso 59cee36c3a Update README.md 2022-06-17 11:49:06 +02:00
Giandonato Inverso 88028393cd Update README.md 2022-06-17 11:48:44 +02:00
Giandonato Inverso de377902be Merge pull request #16 from neoteknic/patch-1
Fix php 8.1 warning in form field
2022-02-22 16:55:19 +01:00
neoteknic ee711a5796 Update filters.php
Fix php 8.1 warning in form field
2022-02-22 16:22:13 +01:00
Giandonato Inverso e599aed16f Add files via upload 2020-09-08 18:43:51 +02:00
Giandonato Inverso 0d77e8f3a4 Add files via upload 2020-09-08 18:42:29 +02:00
Giandonato Inverso 6ed9018086 Add files via upload 2020-09-08 18:41:51 +02:00
Giandonato Inverso 8cee68e091 Add files via upload 2020-09-08 18:30:32 +02:00
Giandonato Inverso 4615be4280 Add files via upload 2020-09-08 18:28:26 +02:00
Giandonato Inverso 6e7b7cfa77 Add files via upload 2020-09-08 18:22:57 +02:00
Giandonato Inverso 74f92ca2fa Add files via upload 2020-09-08 18:20:38 +02:00
Giandonato Inverso 2dc2e2fe16 Add files via upload 2020-09-08 18:17:08 +02:00
Giandonato Inverso 37dc9baa97 Add files via upload 2020-09-08 18:15:54 +02:00
Giandonato Inverso 0337838319 Add files via upload 2020-09-08 18:13:36 +02:00
Giandonato Inverso c544e0c7b5 Add files via upload 2020-09-08 18:11:37 +02:00
Giandonato Inverso 087e3e83b5 Add files via upload 2020-09-08 18:09:19 +02:00
Giandonato Inverso 2ae9f32cb8 Add files via upload 2020-09-08 18:06:28 +02:00
Giandonato Inverso 5924afa3dc Add files via upload 2020-09-08 17:59:49 +02:00
Giandonato Inverso b302e0cce1 Add files via upload 2020-09-08 17:59:21 +02:00
Giandonato Inverso 1f9fa672c6 Update README.md 2020-09-08 17:31:01 +02:00
Giandonato Inverso bd610dc68f Update README.md 2020-09-08 17:29:26 +02:00
Giandonato Inverso dd9d5ae2fb Update README.md 2020-09-08 17:26:41 +02:00
Giandonato Inverso 0e62c29f11 Update README.md 2020-09-08 17:25:38 +02:00
Giandonato Inverso 14610fe212 Update README.md 2020-09-08 17:24:07 +02:00
Giandonato Inverso 8bcb852ea9 Update README.md 2020-09-08 17:23:38 +02:00
Giandonato Inverso 0bfe40eafe Update README.md 2020-09-08 17:23:17 +02:00
Giandonato Inverso 1e6bc3af4e Update README.md 2020-09-08 17:23:02 +02:00
Giandonato Inverso fa9e6730cc Update README.md 2020-09-08 17:22:41 +02:00
Giandonato Inverso 6709776cc5 Update README.md 2020-09-08 17:22:25 +02:00
Giandonato Inverso df04139a05 Update README.md 2020-09-08 17:21:33 +02:00
Giandonato Inverso 4a377b635c Update README.md 2020-09-08 17:21:07 +02:00
Giandonato Inverso 8262550a10 Update README.md 2020-09-08 17:20:39 +02:00
Giandonato Inverso 31ab8efc05 Update README.md 2020-09-08 17:20:20 +02:00
Giandonato Inverso 3ee55c9b17 Update README.md 2020-09-08 17:20:05 +02:00
Giandonato Inverso 93cd20c49d Update README.md 2020-09-08 17:18:41 +02:00
Giandonato Inverso eff10b3ee6 Update README.md 2020-09-08 17:17:36 +02:00
Giandonato Inverso 6de33ea45c Update README.md 2020-09-08 17:14:57 +02:00
Giandonato Inverso 34bfee494e Update README.md 2020-09-08 17:13:33 +02:00
Giandonato Inverso 3f8209a6a6 Update README.md 2020-09-08 17:13:06 +02:00
Giandonato Inverso 2b93634d3a Update README.md 2020-09-08 17:12:50 +02:00
Giandonato Inverso c698fc34f1 Update README.md 2020-09-08 17:12:34 +02:00
Giandonato Inverso 93d362aba2 Update README.md 2020-09-08 17:12:01 +02:00
Giandonato Inverso 8fe055a5b1 Update README.md 2020-09-08 17:11:45 +02:00
Giandonato Inverso 8a64294455 Update README.md 2020-09-08 17:11:27 +02:00
Giandonato Inverso 854b0ca77d Update README.md 2020-09-08 17:10:01 +02:00
Giandonato Inverso 7c707f865c Update README.md 2020-09-08 17:09:36 +02:00
Giandonato Inverso 439d136f54 Update README.md 2020-09-08 17:09:15 +02:00
Giandonato Inverso d0f2d526f1 Update README.md 2020-09-08 17:08:42 +02:00
Giandonato Inverso 9c855a19bb Update README.md 2020-09-08 17:07:06 +02:00
Giandonato Inverso a7e532867a Update README.md 2020-09-08 17:06:25 +02:00
Giandonato Inverso 67e6d85da9 Update README.md 2020-09-08 17:05:43 +02:00
Giandonato Inverso 35c72f6199 Initial commit 2020-09-08 16:56:05 +02:00
47 changed files with 218 additions and 597 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ TYPE=docker
QRCODE_GENERATOR=internal-chillerlan.qrcode
BASE_URL=http://localhost
DATABASE_HOST=qrforge-db
DATABASE_HOST=php-dynamic-qrcode-db
DATABASE_PORT=3306
DATABASE_NAME=qrcode
DATABASE_USER=qrcode
-14
View File
@@ -1,14 +0,0 @@
FROM nginx:1.27-alpine
# The php-dynamic-qrcode container builds the full app (incl. composer/vendor) into its
# own image at /var/www/html. nginx runs as a separate container and has no access to
# that filesystem, so it needs its own copy of just the static assets it serves directly
# via try_files - everything else (*.php) is proxied to php-fpm regardless. Without this,
# every static asset request (CSS/JS/manifest) falls through nginx's try_files to
# index.php, which requires a login and silently redirects there instead of serving the
# file.
COPY src/dist /var/www/html/dist
COPY src/plugins /var/www/html/plugins
COPY src/manifest.json /var/www/html/manifest.json
COPY src/service-worker.js /var/www/html/service-worker.js
COPY src/favicon.ico /var/www/html/favicon.ico
-1
View File
@@ -1,7 +1,6 @@
MIT License
Copyright (c) 2020 Giandonato Inverso
Copyright (c) 2026 Dillard Blom
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+56 -61
View File
@@ -1,70 +1,65 @@
<p align="center"><img src="src/dist/img/brand/logo.svg" alt="QRForge" width="320"></p>
<p align="center"><img src="https://www.giandonatoinverso.it/qrcode/dist/img/DynamicQRCode_Original.png"></p>
<p align="center"><strong>Self-hosted, open-source QR code generator.</strong></p>
**PHP Dynamic Qr code** is a script that allows the generation and saving of dynamic and static QR codes. It has a clean, responsive, and user-friendly design. It is based on [AdminLte](https://adminlte.io/), the "Best open source admin dashboard & control panel theme. Built on top of Bootstrap" and [Core PHP Admin Panel](https://github.com/chetans9/core-php-admin-panel), a simple Admin Panel written in core PHP that contains an implementation of general features you might need in your website admin panel like: record management (CRUD), secure authentication, pagination, filters.
**QRForge** creates and manages static and dynamic QR codes from a clean,
responsive control panel. It's a security-hardened, actively maintained fork
of the original [PHP Dynamic Qr code](https://github.com/giandonatoinverso/PHP-Dynamic-Qr-code)
project by Giandonato Inverso, built on [AdminLTE](https://adminlte.io/).
[LIVE DEMO](https://qrcode.giandonatoinverso.dev/)
- **Try it free:** [qr.ensembia.com](https://qr.ensembia.com) - fully functional OSS test
instance. Self-service signup isn't live yet, so log in with the temporary shared demo
account `admin` / `admin` in the meantime.
- **Commercial VIP edition** (self-service create-rights, logo-embedded QR codes):
[www.qrforge.eu](https://www.qrforge.eu).
- **Self-host it yourself:** this repository, MIT-licensed.
username: admin
password: admin
[DOCUMENTATION](https://giandonatoinverso.it/qrcode/documentation)
# Features
- Dynamic QR codes with a database-backed URL shortener
- Create, edit, delete, enable/disable the redirect
- Download any time, bulk download/delete
- Batch-generate from a CSV file
- 16 static QR code types: Text, Email, Phone, SMS, WhatsApp, Skype, Location,
vCard, Event/calendar, Bookmark, WiFi (incl. WPA3), PayPal, Bitcoin, 2FA,
App Link (Android intent / universal links), Bluetooth
- QR code styling: 6 export formats, foreground/background color, 4 precision
levels, 10 sizes, optional label text below the code (custom font + size),
optional icon shown above the code, save/load your own style presets
- Address search (OpenStreetMap Nominatim) for Location QR codes
- Built-in QR scanner (camera or image upload, decodes entirely client-side)
- Installable as a PWA
- Role-based access: `super` (full access + user management), `admin`
(scoped to their own codes and sub-users), `user` (read-only, with
optional per-account create rights and view toggles set by an admin)
- Dashboard with QR/scan statistics and a 7-day activity chart
- CSRF protection, login rate limiting, session hardening, audit log
- Docker Compose setup, both a dev image and a production Nginx + PHP-FPM image
- **#1 Dynamic Qr code generator on GitHub with a database to store Qr codes**
- Create unlimited Qr codes
- Docker compose support
- Control panel with 2 access levels
- Multi-account
- Dashboard with advanced statistics on Qr codes created and on scans
- Bulk download, bulk delete
- Dynamic Qr code
- Create, modify, and delete Qr codes
- You can download your Qr codes when you want
- URL shortener with redirect
- Enable or disable the link redirect
- Static Qr code
- Text QR Code
- Email QR Code
- Phone QR Code
- Sms QR Code
- Whatsapp QR Code
- Skype QR Code
- Location QR Code
- Vcard QR Code
- Event/calendar QR Code
- Bookmark QR Code
- Wifi QR Code
- Paypal QR Code
- Bitcoin QR Code
- Customization of Qr codes
- 6 formats for images
- Foreground color
- Background color
- 4 levels of precision
- 10 sizes
- Responsive bootstrap-based design
- Easy to understand and expand code
- Full OOP with classes and well-documented
# What is included
## What is included
- PHP 8.4 application source
- Database schema + migrations
- Docker Compose files (dev and production)
- CSS/JS assets
- PHP files
- .sql file with sample data
- JS files
- CSS files
- Docker compose file
# Setup with Docker Compose
1. Clone this repository.
2. Copy `.env.example` to `.env` and set a real `DATABASE_PASSWORD` /
`MYSQL_ROOT_PASSWORD`.
3. Start the stack:
```bash
docker compose up -d --build
```
4. Open `http://localhost` and log in with `superadmin` / `superadmin`. You'll
be required to set a new password on first login.
For a production deployment behind a reverse proxy, use
`docker-compose.prod.yml` (Nginx + PHP-FPM) instead of the dev stack.
# Credits
- Originally forked from [PHP Dynamic Qr code](https://github.com/giandonatoinverso/PHP-Dynamic-Qr-code)
by Giandonato Inverso.
- QR code rendering powered by [chillerlan/php-qrcode](https://github.com/chillerlan/php-qrcode).
- Admin panel UI built on [AdminLTE](https://adminlte.io/).
# License
MIT - see [LICENSE](LICENSE).
## Setup with docker compose
1. download docker-compose.yml file
2. Start docker stack
```bash
docker compose build --no-cache && docker compose up -d
```
3. Open your browser at http://localhost:80 and login with (username: superadmin, password: superadmin)
+14 -16
View File
@@ -1,8 +1,6 @@
services:
nginx:
build:
context: .
dockerfile: Dockerfile.nginx
image: "nginx:1.27-alpine"
restart: "unless-stopped"
ports:
- "80:80"
@@ -11,11 +9,11 @@ services:
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- qrforge-app
- php-dynamic-qrcode
networks:
- qrforge-network
- php-dynamic-qrcode-network
qrforge-app:
php-dynamic-qrcode:
build:
context: .
dockerfile: Dockerfile.fpm
@@ -24,7 +22,7 @@ services:
TYPE: "docker"
QRCODE_GENERATOR: "${QRCODE_GENERATOR:-internal-chillerlan.qrcode}"
BASE_URL: "${BASE_URL:?set BASE_URL in .env, e.g. https://qr.ensembia.com}"
DATABASE_HOST: "qrforge-db"
DATABASE_HOST: "php-dynamic-qrcode-db"
DATABASE_PORT: "3306"
DATABASE_NAME: "${DATABASE_NAME:-qrcode}"
DATABASE_USER: "${DATABASE_USER:-qrcode}"
@@ -32,18 +30,18 @@ services:
DATABASE_PREFIX: "${DATABASE_PREFIX:-}"
DATABASE_CHARSET: "${DATABASE_CHARSET:-utf8}"
depends_on:
qrforge-db:
php-dynamic-qrcode-db:
condition: service_healthy
volumes:
- qrforge_qrcode_storage:/var/www/qrcode-storage
- php_dynamic_qrcode_saved_qrcode_data:/var/www/qrcode-storage
networks:
- qrforge-network
- php-dynamic-qrcode-network
qrforge-db:
php-dynamic-qrcode-db:
image: "mysql:8.0"
restart: "unless-stopped"
volumes:
- qrforge_db_data:/var/lib/mysql
- php_dynamic_qrcode_db_data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
environment:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD in .env}"
@@ -56,12 +54,12 @@ services:
timeout: 5s
retries: 10
networks:
- qrforge-network
- php-dynamic-qrcode-network
volumes:
qrforge_db_data:
qrforge_qrcode_storage:
php_dynamic_qrcode_db_data:
php_dynamic_qrcode_saved_qrcode_data:
networks:
qrforge-network:
php-dynamic-qrcode-network:
driver: bridge
+11 -11
View File
@@ -1,5 +1,5 @@
services:
qrforge-app:
php-dynamic-qrcode:
build:
context: .
dockerfile: Dockerfile
@@ -8,7 +8,7 @@ services:
TYPE: "${TYPE:-docker}"
QRCODE_GENERATOR: "${QRCODE_GENERATOR:-internal-chillerlan.qrcode}"
BASE_URL: "${BASE_URL:-http://localhost}"
DATABASE_HOST: "qrforge-db"
DATABASE_HOST: "php-dynamic-qrcode-db"
DATABASE_PORT: "3306"
DATABASE_NAME: "${DATABASE_NAME:-qrcode}"
DATABASE_USER: "${DATABASE_USER:-qrcode}"
@@ -18,18 +18,18 @@ services:
ports:
- "80:80"
depends_on:
qrforge-db:
php-dynamic-qrcode-db:
condition: service_healthy
volumes:
- qrforge_qrcode_storage:/var/www/qrcode-storage
- php_dynamic_qrcode_saved_qrcode_data:/var/www/html/saved_qrcode
networks:
- qrforge-network
- php-dynamic-qrcode-network
qrforge-db:
php-dynamic-qrcode-db:
image: "mysql:8.0"
restart: "unless-stopped"
volumes:
- qrforge_db_data:/var/lib/mysql
- php_dynamic_qrcode_db_data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
environment:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?zet MYSQL_ROOT_PASSWORD in .env}"
@@ -42,12 +42,12 @@ services:
timeout: 5s
retries: 10
networks:
- qrforge-network
- php-dynamic-qrcode-network
volumes:
qrforge_db_data:
qrforge_qrcode_storage:
php_dynamic_qrcode_db_data:
php_dynamic_qrcode_saved_qrcode_data:
networks:
qrforge-network:
php-dynamic-qrcode-network:
driver: bridge
+1 -1
View File
@@ -15,7 +15,7 @@ server {
}
location ~ \.php$ {
fastcgi_pass qrforge-app:9000;
fastcgi_pass php-dynamic-qrcode:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
-102
View File
@@ -1,102 +0,0 @@
<?php
require_once 'includes/bootstrap.php';
require_once BASE_PATH . '/includes/auth_validate.php';
?>
<!DOCTYPE html>
<html lang="en">
<title>About - QRForge</title>
<head>
<?php include './includes/head.php'; ?>
</head>
<body class="hold-transition sidebar-mini layout-fixed layout-navbar-fixed layout-footer-fixed">
<div class="wrapper">
<!-- Navbar -->
<?php include './includes/navbar.php'; ?>
<!-- /.navbar -->
<!-- Main Sidebar Container -->
<?php include './includes/sidebar.php'; ?>
<!-- /.Main Sidebar Container -->
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">About</h1>
</div><!-- /.col -->
</div><!-- /.row -->
</div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="card card-primary">
<div class="card-body text-center">
<img src="dist/img/brand/logo-stacked.svg" alt="QRForge" style="max-width: 220px; margin: 20px 0;">
<p class="text-muted">Self-hosted static and dynamic QR code generator. Version 3.0.</p>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Commercial version</h3>
</div>
<div class="card-body">
<p>
This is the free, open-source (MIT) edition of QRForge. It runs unmodified
as a live, fully functional try-out at
<a href="https://qr.ensembia.com" target="_blank">qr.ensembia.com</a> -
self-service signup isn't live yet, so log in with the temporary shared
demo account <code>admin</code> / <code>admin</code> in the meantime.
</p>
<p>
The commercial VIP edition (paid create-rights and logo-embedded QR codes)
is a separate product built on the same OSS core - see
<a href="https://www.qrforge.eu" target="_blank">www.qrforge.eu</a> for
pricing and details.
</p>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Self-hosting</h3>
</div>
<div class="card-body">
<p>
Prefer to run your own instance? QRForge is MIT-licensed and available on
GitHub:
<a href="https://github.com/dillardblom/QRForge-selfhosted" target="_blank">github.com/dillardblom/QRForge-selfhosted</a>.
</p>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Credits</h3>
</div>
<div class="card-body">
<p>
Originally forked from
<a href="https://github.com/giandonatoinverso/PHP-Dynamic-Qr-code" target="_blank">PHP Qrcode Generator by Giandonato Inverso</a>.
</p>
<p>
QR code rendering is powered by the
<a href="https://github.com/chillerlan/php-qrcode" target="_blank">chillerlan/php-qrcode</a>
library.
</p>
</div>
</div>
</div><!--/. container-fluid -->
</section><!-- /.content -->
</div><!-- /.content-wrapper -->
<!-- Footer and scripts -->
<?php include './includes/footer.php'; ?>
</body>
</html>
+1 -1
View File
@@ -99,7 +99,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
?>
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
+2 -2
View File
@@ -47,13 +47,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
?>
<!DOCTYPE html>
<html lang="en">
<title>Change password - QRForge</title>
<title>Change password - Qrcode Generator</title>
<?php include './includes/head.php'; ?>
<body class="login-page" style="min-height: 512.391px;">
<div class="login-box">
<div class="login-logo">
<img src="dist/img/brand/logo.svg" alt="QRForge" style="max-width: 260px;">
<img src="dist/img/DynamicQRCode_Original.png" style="width: 95%; height: 95%">
</div>
<div class="card">
-17
View File
@@ -6,21 +6,4 @@
#err-msg {
display: none;
color: red;
}
/* List-page qr code thumbnails: fixed box, but object-fit:contain instead of
width/height attrs so taller images (e.g. icon-above-qr) don't get squashed. */
.qr-thumb-link {
display: inline-block;
padding: 0;
border: 0;
background: none;
cursor: pointer;
}
.qr-thumb-img {
width: 100px;
height: 100px;
object-fit: contain;
background: #fff;
border: 1px solid #dee2e6;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

-11
View File
@@ -1,11 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 151.74 151.74" role="img" aria-labelledby="qfi-title qfi-desc">
<title id="qfi-title">QRForge icon</title>
<desc id="qfi-desc">QR-block mark with outbound arrow tile.</desc>
<g transform="translate(-8.95,-2.57)">
<rect id="block-top-left" x="5.24" y="16.81" width="42.29" height="40.70" rx="2.5" fill="#334155" />
<rect id="block-mid" x="59.57" y="68.79" width="24.90" height="23.46" rx="2.5" fill="#334155" />
<rect id="block-bottom-right" x="96.08" y="103.32" width="40.59" height="31.68" rx="2.5" fill="#334155" />
<path id="block-bottom-left-L" d="M 5.24,71.29 Q 5.24,68.79 7.74,68.79 L 45.03,68.79 Q 47.53,68.79 47.53,71.29 L 47.53,103.32 Q 47.53,103.32 47.53,103.32 L 81.97,103.32 Q 84.47,103.32 84.47,105.82 L 84.47,132.50 Q 84.47,135.00 81.97,135.00 L 7.74,135.00 Q 5.24,135.00 5.24,132.50 Z" fill="#334155" />
<path id="arrow-tile" d="M 105.68,6.94 Q 104.67,5.88 106.13,5.88 L 145.89,5.88 Q 148.39,5.88 148.39,8.38 L 148.39,44.14 Q 148.39,46.64 146.42,45.11 L 133.00,34.67 Q 133.00,34.67 133.00,34.67 L 112.50,56.87 Q 112.50,56.87 112.50,56.87 L 134.08,56.87 Q 136.58,56.87 136.58,59.37 L 136.58,89.50 Q 136.58,92.00 134.08,92.00 L 98.63,92.00 Q 96.13,92.00 96.13,89.50 L 96.13,56.87 Q 96.13,56.87 96.13,56.87 L 62.10,56.87 Q 59.60,56.87 59.60,54.37 L 59.60,19.60 Q 59.60,17.10 62.10,17.10 L 93.63,17.10 Q 96.13,17.10 96.13,19.60 L 96.13,41.00 Q 96.13,41.00 96.13,41.00 L 97.00,42.17 Q 97.00,42.17 97.00,42.17 L 117.19,22.73 Q 118.99,21.00 117.27,19.18 L 107.68,9.06 Q 106.68,8.00 105.68,6.94 Z" fill="#2563EB" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

-11
View File
@@ -1,11 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 151.74 151.74" role="img" aria-labelledby="qfi-title qfi-desc">
<title id="qfi-title">QRForge icon</title>
<desc id="qfi-desc">QR-block mark with outbound arrow tile.</desc>
<g transform="translate(-8.95,-2.57)">
<rect id="block-top-left" x="5.24" y="16.81" width="42.29" height="40.70" rx="2.5" fill="#FFFFFF" />
<rect id="block-mid" x="59.57" y="68.79" width="24.90" height="23.46" rx="2.5" fill="#FFFFFF" />
<rect id="block-bottom-right" x="96.08" y="103.32" width="40.59" height="31.68" rx="2.5" fill="#FFFFFF" />
<path id="block-bottom-left-L" d="M 5.24,71.29 Q 5.24,68.79 7.74,68.79 L 45.03,68.79 Q 47.53,68.79 47.53,71.29 L 47.53,103.32 Q 47.53,103.32 47.53,103.32 L 81.97,103.32 Q 84.47,103.32 84.47,105.82 L 84.47,132.50 Q 84.47,135.00 81.97,135.00 L 7.74,135.00 Q 5.24,135.00 5.24,132.50 Z" fill="#FFFFFF" />
<path id="arrow-tile" d="M 105.68,6.94 Q 104.67,5.88 106.13,5.88 L 145.89,5.88 Q 148.39,5.88 148.39,8.38 L 148.39,44.14 Q 148.39,46.64 146.42,45.11 L 133.00,34.67 Q 133.00,34.67 133.00,34.67 L 112.50,56.87 Q 112.50,56.87 112.50,56.87 L 134.08,56.87 Q 136.58,56.87 136.58,59.37 L 136.58,89.50 Q 136.58,92.00 134.08,92.00 L 98.63,92.00 Q 96.13,92.00 96.13,89.50 L 96.13,56.87 Q 96.13,56.87 96.13,56.87 L 62.10,56.87 Q 59.60,56.87 59.60,54.37 L 59.60,19.60 Q 59.60,17.10 62.10,17.10 L 93.63,17.10 Q 96.13,17.10 96.13,19.60 L 96.13,41.00 Q 96.13,41.00 96.13,41.00 L 97.00,42.17 Q 97.00,42.17 97.00,42.17 L 117.19,22.73 Q 118.99,21.00 117.27,19.18 L 107.68,9.06 Q 106.68,8.00 105.68,6.94 Z" fill="#2563EB" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

-11
View File
@@ -1,11 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 151.74 151.74" role="img" aria-labelledby="qfi-title qfi-desc">
<title id="qfi-title">QRForge icon</title>
<desc id="qfi-desc">QR-block mark with outbound arrow tile.</desc>
<g transform="translate(-8.95,-2.57)">
<rect id="block-top-left" x="5.24" y="16.81" width="42.29" height="40.70" rx="2.5" fill="#334155" />
<rect id="block-mid" x="59.57" y="68.79" width="24.90" height="23.46" rx="2.5" fill="#334155" />
<rect id="block-bottom-right" x="96.08" y="103.32" width="40.59" height="31.68" rx="2.5" fill="#334155" />
<path id="block-bottom-left-L" d="M 5.24,71.29 Q 5.24,68.79 7.74,68.79 L 45.03,68.79 Q 47.53,68.79 47.53,71.29 L 47.53,103.32 Q 47.53,103.32 47.53,103.32 L 81.97,103.32 Q 84.47,103.32 84.47,105.82 L 84.47,132.50 Q 84.47,135.00 81.97,135.00 L 7.74,135.00 Q 5.24,135.00 5.24,132.50 Z" fill="#334155" />
<path id="arrow-tile" d="M 105.68,6.94 Q 104.67,5.88 106.13,5.88 L 145.89,5.88 Q 148.39,5.88 148.39,8.38 L 148.39,44.14 Q 148.39,46.64 146.42,45.11 L 133.00,34.67 Q 133.00,34.67 133.00,34.67 L 112.50,56.87 Q 112.50,56.87 112.50,56.87 L 134.08,56.87 Q 136.58,56.87 136.58,59.37 L 136.58,89.50 Q 136.58,92.00 134.08,92.00 L 98.63,92.00 Q 96.13,92.00 96.13,89.50 L 96.13,56.87 Q 96.13,56.87 96.13,56.87 L 62.10,56.87 Q 59.60,56.87 59.60,54.37 L 59.60,19.60 Q 59.60,17.10 62.10,17.10 L 93.63,17.10 Q 96.13,17.10 96.13,19.60 L 96.13,41.00 Q 96.13,41.00 96.13,41.00 L 97.00,42.17 Q 97.00,42.17 97.00,42.17 L 117.19,22.73 Q 118.99,21.00 117.27,19.18 L 107.68,9.06 Q 106.68,8.00 105.68,6.94 Z" fill="#2563EB" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

-12
View File
@@ -1,12 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 236.20 246.50" role="img" aria-labelledby="qfs-title qfs-desc">
<title id="qfs-title">QRForge stacked logo</title>
<desc id="qfs-desc">Stacked QRForge logo.</desc>
<g transform="translate(33.28,14.75)">
<rect id="block-top-left" x="5.24" y="16.81" width="42.29" height="40.70" rx="2.5" fill="#334155" />
<rect id="block-mid" x="59.57" y="68.79" width="24.90" height="23.46" rx="2.5" fill="#334155" />
<rect id="block-bottom-right" x="96.08" y="103.32" width="40.59" height="31.68" rx="2.5" fill="#334155" />
<path id="block-bottom-left-L" d="M 5.24,71.29 Q 5.24,68.79 7.74,68.79 L 45.03,68.79 Q 47.53,68.79 47.53,71.29 L 47.53,103.32 Q 47.53,103.32 47.53,103.32 L 81.97,103.32 Q 84.47,103.32 84.47,105.82 L 84.47,132.50 Q 84.47,135.00 81.97,135.00 L 7.74,135.00 Q 5.24,135.00 5.24,132.50 Z" fill="#334155" />
<path id="arrow-tile" d="M 105.68,6.94 Q 104.67,5.88 106.13,5.88 L 145.89,5.88 Q 148.39,5.88 148.39,8.38 L 148.39,44.14 Q 148.39,46.64 146.42,45.11 L 133.00,34.67 Q 133.00,34.67 133.00,34.67 L 112.50,56.87 Q 112.50,56.87 112.50,56.87 L 134.08,56.87 Q 136.58,56.87 136.58,59.37 L 136.58,89.50 Q 136.58,92.00 134.08,92.00 L 98.63,92.00 Q 96.13,92.00 96.13,89.50 L 96.13,56.87 Q 96.13,56.87 96.13,56.87 L 62.10,56.87 Q 59.60,56.87 59.60,54.37 L 59.60,19.60 Q 59.60,17.10 62.10,17.10 L 93.63,17.10 Q 96.13,17.10 96.13,19.60 L 96.13,41.00 Q 96.13,41.00 96.13,41.00 L 97.00,42.17 Q 97.00,42.17 97.00,42.17 L 117.19,22.73 Q 118.99,21.00 117.27,19.18 L 107.68,9.06 Q 106.68,8.00 105.68,6.94 Z" fill="#2563EB" />
</g>
<text x="118.10" y="203.67" text-anchor="middle" font-family="Inter, Manrope, Geist, Arial, sans-serif" font-size="48.39" font-weight="700" letter-spacing="-3.43" fill="#334155">QRForge</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

-12
View File
@@ -1,12 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="5.24 5.88 573.81 145.12" role="img" aria-labelledby="qf-title qf-desc">
<title id="qf-title">QRForge logo</title>
<desc id="qf-desc">QRForge wordmark with a QR-block mark and outbound arrow tile.</desc>
<g id="icon">
<rect id="block-top-left" x="5.24" y="16.81" width="42.29" height="40.70" rx="2.5" fill="#334155" />
<rect id="block-mid" x="59.57" y="68.79" width="24.90" height="23.46" rx="2.5" fill="#334155" />
<rect id="block-bottom-right" x="96.08" y="103.32" width="40.59" height="31.68" rx="2.5" fill="#334155" />
<path id="block-bottom-left-L" d="M 5.24,71.29 Q 5.24,68.79 7.74,68.79 L 45.03,68.79 Q 47.53,68.79 47.53,71.29 L 47.53,103.32 Q 47.53,103.32 47.53,103.32 L 81.97,103.32 Q 84.47,103.32 84.47,105.82 L 84.47,132.50 Q 84.47,135.00 81.97,135.00 L 7.74,135.00 Q 5.24,135.00 5.24,132.50 Z" fill="#334155" />
<path id="arrow-tile" d="M 105.68,6.94 Q 104.67,5.88 106.13,5.88 L 145.89,5.88 Q 148.39,5.88 148.39,8.38 L 148.39,44.14 Q 148.39,46.64 146.42,45.11 L 133.00,34.67 Q 133.00,34.67 133.00,34.67 L 112.50,56.87 Q 112.50,56.87 112.50,56.87 L 134.08,56.87 Q 136.58,56.87 136.58,59.37 L 136.58,89.50 Q 136.58,92.00 134.08,92.00 L 98.63,92.00 Q 96.13,92.00 96.13,89.50 L 96.13,56.87 Q 96.13,56.87 96.13,56.87 L 62.10,56.87 Q 59.60,56.87 59.60,54.37 L 59.60,19.60 Q 59.60,17.10 62.10,17.10 L 93.63,17.10 Q 96.13,17.10 96.13,19.60 L 96.13,41.00 Q 96.13,41.00 96.13,41.00 L 97.00,42.17 Q 97.00,42.17 97.00,42.17 L 117.19,22.73 Q 118.99,21.00 117.27,19.18 L 107.68,9.06 Q 106.68,8.00 105.68,6.94 Z" fill="#2563EB" />
</g>
<text id="wordmark" x="177" y="101" font-family="Inter, Manrope, Geist, Arial, sans-serif" font-size="103" font-weight="700" letter-spacing="-7.3" fill="#334155">QRForge</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 26 KiB

-69
View File
@@ -1,69 +0,0 @@
(function () {
document.addEventListener('DOMContentLoaded', function () {
var searchInput = document.getElementById('location_search');
var searchBtn = document.getElementById('location_search_btn');
var resultsBox = document.getElementById('location_search_results');
var latInput = document.getElementById('latitude');
var lngInput = document.getElementById('longitude');
if (!searchInput || !searchBtn || !resultsBox || !latInput || !lngInput) {
return;
}
function clearResults() {
resultsBox.innerHTML = '';
}
function doSearch() {
var query = searchInput.value.trim();
if (!query) {
return;
}
clearResults();
resultsBox.innerHTML = '<div class="list-group-item">Searching...</div>';
fetch('https://nominatim.openstreetmap.org/search?format=json&limit=5&q=' + encodeURIComponent(query))
.then(function (response) { return response.json(); })
.then(function (results) {
clearResults();
if (!results.length) {
resultsBox.innerHTML = '<div class="list-group-item">No results found.</div>';
return;
}
results.forEach(function (place) {
var item = document.createElement('button');
item.type = 'button';
item.className = 'list-group-item list-group-item-action';
item.textContent = place.display_name;
item.addEventListener('click', function () {
latInput.value = place.lat;
lngInput.value = place.lon;
searchInput.value = place.display_name;
clearResults();
});
resultsBox.appendChild(item);
});
})
.catch(function () {
resultsBox.innerHTML = '<div class="list-group-item text-danger">Search failed (network error).</div>';
});
}
searchBtn.addEventListener('click', doSearch);
searchInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
doSearch();
}
});
document.addEventListener('click', function (e) {
if (e.target !== searchInput && !resultsBox.contains(e.target)) {
clearResults();
}
});
});
})();
+3 -12
View File
@@ -95,18 +95,9 @@
});
document.querySelectorAll('#foreground, #background').forEach(function (input) {
// bootstrap-colorpicker sets the value and fires 'change' via jQuery's
// synthetic .trigger(), which a native addEventListener never sees - so the
// preview only updates via jQuery's event binding, not the native one below.
if (window.jQuery) {
jQuery(input).on('change', function () {
updateStylePreview(scopeOf(input));
});
} else {
input.addEventListener('change', function () {
updateStylePreview(scopeOf(input));
});
}
input.addEventListener('change', function () {
updateStylePreview(scopeOf(input));
});
});
document.querySelectorAll('select[name="level"]').forEach(function (select) {
+1 -1
View File
@@ -60,7 +60,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"]) && !isset($_P
?>
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
+1 -1
View File
@@ -29,7 +29,7 @@ $total_pages = $db->totalPages;
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

+26 -25
View File
@@ -117,7 +117,27 @@
</div>
<div class="col-sm-12 mb-2">
<div class="row">
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<label for="filename">Filename *</label>
<input type="text" name="filename" value="" placeholder="My first Qrcode" class="form-control error" required="required" id="filename">
</div>
</div>
<div class="col-6 col-md-1">
<label for="format">Format *</label>
<select name="format" class="form-control" required="required">
<option value="png" selected>PNG</option>
<option value="gif">GIF</option>
<option value="jpeg">JPEG</option>
<option value="jpg">JPG</option>
<option value="svg">SVG</option>
<option value="eps">EPS</option>
</select>
</div>
<div class="col-sm-4">
<div class="form-group">
<label for="frame_text">Frame text</label>
@@ -157,32 +177,13 @@
</div>
</div>
<?php if($_SESSION['type'] === 'super') { ?>
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<label for="filename">Filename *</label>
<input type="text" name="filename" value="" placeholder="My first Qrcode" class="form-control error" required="required" id="filename">
</div>
</div>
<div class="col-6 col-md-1">
<label for="format">Format *</label>
<select name="format" class="form-control" required="required">
<option value="png" selected>PNG</option>
<option value="gif">GIF</option>
<option value="jpeg">JPEG</option>
<option value="jpg">JPG</option>
<option value="svg">SVG</option>
<option value="eps">EPS</option>
</select>
</div>
<?php if ($_SESSION['type'] === 'super') { ?>
<div class="col-sm-4">
<div class="form-group">
<label for="id_owner">Owner *</label>
<select name="id_owner" id="id_owner" class="form-control">
<select name="id_owner" class="form-control">
<option value="">All (shared with every admin)</option>
<?php
@@ -198,9 +199,9 @@
</select>
</div>
</div>
<?php } else { ?>
<input type="hidden" name="id_owner" value="<?php echo $_SESSION["user_id"];?>"/>
<?php } ?>
</div>
</div>
<?php } else { ?>
<input type="hidden" name="id_owner" value="<?php echo $_SESSION["user_id"];?>"/>
<?php } ?>
</fieldset>
+51 -50
View File
@@ -58,7 +58,7 @@ if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-6 col-md-3">
<label>Randomize</label>
<label>&nbsp;</label>
<button type="button" id="random_style_btn" class="btn btn-outline-secondary btn-block">
<i class="fa fa-dice"></i> Random style
</button>
@@ -113,7 +113,32 @@ if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
-->
<div class="col-sm-12 mb-2">
<div class="row">
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<label for="filename">Filename *</label>
<input type="text" name="filename" value="" placeholder="My first Qrcode" class="form-control error" required="required" id = "filename">
</div>
</div>
<div class="col-6 col-md-1">
<label for="format">Format</label>
<select name="format" class="form-control">
<option value="png">PNG</option>
<option value="gif">GIF</option>
<option value="jpeg">JPEG</option>
<option value="jpg">JPG</option>
<option value="svg">SVG</option>
<?php
if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
echo ' <option value="svgbw">SVG (BW)</option>';
}
?>
<option value="eps">EPS</option>
</select>
</div>
<div class="col-sm-4">
<div class="form-group">
<label for="frame_text">Frame text</label>
@@ -153,55 +178,31 @@ if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
</div>
</div>
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<label for="filename">Filename *</label>
<input type="text" name="filename" value="" placeholder="My first Qrcode" class="form-control error" required="required" id="filename">
<?php if($_SESSION['type'] === 'super') { ?>
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<label for="id_owner">Owner *</label>
<select name="id_owner" class="form-control">
<option value="">All (shared with every admin)</option>
<?php
require_once BASE_PATH . '/lib/Users/Users.php';
$users_instance = new Users();
$users = $users_instance->getAllUsers();
foreach ($users as $user) {
$is_self = (int) $user["id"] === (int) $_SESSION["user_id"];
?>
<option value="<?php echo $user["id"];?>" <?php echo $is_self ? 'selected' : ''; ?>><?php echo $user["username"];?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="col-6 col-md-1">
<label for="format">Format</label>
<select name="format" class="form-control">
<option value="png">PNG</option>
<option value="gif">GIF</option>
<option value="jpeg">JPEG</option>
<option value="jpg">JPG</option>
<option value="svg">SVG</option>
<?php
if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
echo ' <option value="svgbw">SVG (BW)</option>';
}
?>
<option value="eps">EPS</option>
</select>
</div>
<?php if ($_SESSION['type'] === 'super') { ?>
<div class="col-sm-4">
<div class="form-group">
<label for="id_owner">Owner *</label>
<select name="id_owner" id="id_owner" class="form-control">
<option value="">All (shared with every admin)</option>
<?php
require_once BASE_PATH . '/lib/Users/Users.php';
$users_instance = new Users();
$users = $users_instance->getAllUsers();
foreach ($users as $user) {
$is_self = (int) $user["id"] === (int) $_SESSION["user_id"];
?>
<option value="<?php echo $user["id"];?>" <?php echo $is_self ? 'selected' : ''; ?>><?php echo $user["username"];?></option>
<?php } ?>
</select>
</div>
</div>
<?php } else { ?>
<input type="hidden" name="id_owner" value="<?php echo $_SESSION["user_id"];?>"/>
<?php } ?>
</div>
</div>
<?php } else { ?>
<input type="hidden" name="id_owner" value="<?php echo $_SESSION["user_id"];?>"/>
<?php } ?>
<br>
+8 -23
View File
@@ -2,41 +2,26 @@
<?php echo csrf_field(); ?>
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
<!-- Input forms -->
<div class="col-sm-8">
<div class="form-group" style="position: relative;">
<label for="location_search">Search address</label>
<div class="input-group">
<input type="text" id="location_search" class="form-control" placeholder="Search for an address or place...">
<div class="input-group-append">
<button type="button" id="location_search_btn" class="btn btn-outline-secondary"><i class="fa fa-search"></i></button>
</div>
</div>
<div id="location_search_results" class="list-group" style="position:absolute;z-index:1000;width:100%;"></div>
<small class="form-text text-muted">Looks up coordinates via OpenStreetMap Nominatim (your search leaves this server and goes to nominatim.openstreetmap.org). Or enter coordinates directly below.</small>
</div>
</div>
<div class="col-sm-4">
<div class="form-group">
<label for="latitude">Latitude *</label>
<input type="text" name="latitude" id="latitude" value="" placeholder="40.7127753" class="form-control">
<label>Latitude *</label>
<input type="text" name="latitude" value="" placeholder="40.7127753" class="form-control">
</div>
</div>
<div class="col-sm-4">
<div class="form-group">
<label for="longitude">Longitude *</label>
<input type="text" name="longitude" id="longitude" value="" placeholder="-74.0059728" class="form-control">
<label>Longitude *</label>
<input type="text" name="longitude" value="" placeholder="-74.0059728" class="form-control">
</div>
</div>
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-6 col-md-3">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
<script src="dist/js/location-search.js"></script>
</form>
+1 -31
View File
@@ -70,11 +70,7 @@
<td><?php echo htmlspecialchars($row['identifier']); ?></td>
<td><?php echo htmlspecialchars($row['link']); ?></td>
<td>
<a href="#" class="qr-thumb-link" data-toggle="modal" data-target="#preview-modal"
data-qr-src="qrcode_image.php?type=dynamic&id=<?php echo $row['id']; ?>"
data-qr-name="<?php echo htmlspecialchars($row['filename']); ?>">
<img src="qrcode_image.php?type=dynamic&id=<?php echo $row['id']; ?>" class="qr-thumb-img" alt="QR code for <?php echo htmlspecialchars($row['filename']); ?>">
</a>
<?php echo '<img src="qrcode_image.php?type=dynamic&id='.$row['id'].'" width="100" height="100">'; ?>
</td>
<td><?php echo htmlspecialchars($row['scan']); ?></td>
<td><?php echo htmlspecialchars($row['state']); ?></td>
@@ -139,32 +135,6 @@
<!-- /.Delete Confirmation Modal -->
<?php endif; ?>
<!-- Qr Code Preview Modal -->
<div class="modal fade" id="preview-modal" role="dialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="preview-modal-title">QR code preview</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body text-center">
<img id="preview-modal-img" src="" alt="" style="max-width:100%;max-height:70vh;">
</div>
</div>
</div>
</div>
<!-- /.Qr Code Preview Modal -->
<script>
document.querySelectorAll('.qr-thumb-link').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
document.getElementById('preview-modal-img').src = link.getAttribute('data-qr-src');
document.getElementById('preview-modal-title').textContent = link.getAttribute('data-qr-name');
});
});
</script>
<script>
const deleteButtons = document.querySelectorAll('.delete_btn');
+1 -31
View File
@@ -68,11 +68,7 @@
<td><?php echo htmlspecialchars($row['type']); ?></td>
<td><?php echo htmlspecialchars_decode($row['content']); ?></td>
<td>
<a href="#" class="qr-thumb-link" data-toggle="modal" data-target="#preview-modal"
data-qr-src="qrcode_image.php?type=static&id=<?php echo $row['id']; ?>"
data-qr-name="<?php echo htmlspecialchars($row['filename']); ?>">
<img src="qrcode_image.php?type=static&id=<?php echo $row['id']; ?>" class="qr-thumb-img" alt="QR code for <?php echo htmlspecialchars($row['filename']); ?>">
</a>
<?php echo '<img src="qrcode_image.php?type=static&id='.$row['id'].'" width="100" height="100">'; ?>
</td>
<td>
<?php if (!$is_readonly_user): ?>
@@ -135,32 +131,6 @@
<!-- /.Delete Confirmation Modal -->
<?php endif; ?>
<!-- Qr Code Preview Modal -->
<div class="modal fade" id="preview-modal" role="dialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="preview-modal-title">QR code preview</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body text-center">
<img id="preview-modal-img" src="" alt="" style="max-width:100%;max-height:70vh;">
</div>
</div>
</div>
</div>
<!-- /.Qr Code Preview Modal -->
<script>
document.querySelectorAll('.qr-thumb-link').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
document.getElementById('preview-modal-img').src = link.getAttribute('data-qr-src');
document.getElementById('preview-modal-title').textContent = link.getAttribute('data-qr-name');
});
});
</script>
<script>
const deleteButtons = document.querySelectorAll('.delete_btn');
+2 -3
View File
@@ -6,10 +6,9 @@
<!-- Main Footer -->
<footer class="main-footer text-sm">
<strong><a href="https://www.qrforge.eu" target="_blank">QRForge</a></strong> -
<a href="./about.php">About</a> / credits
<strong><a href="https://github.com/giandonatoinverso/PHP-Dynamic-Qr-code" target="_blank">PHP Qrcode Generator</a> by </strong> Giandonato Inverso
<div class="float-right d-none d-sm-inline-block">
<b>Version</b> 3.0
<b>Version</b> 2.3.0
</div>
</footer>
</div>
+2 -5
View File
@@ -2,12 +2,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="csrf-token" content="<?php echo csrf_token(); ?>">
<meta name="theme-color" content="#2563EB">
<meta name="theme-color" content="#007bff">
<link rel="manifest" href="manifest.json">
<link rel="icon" href="dist/img/brand/favicon.svg" type="image/svg+xml">
<link rel="icon" href="dist/img/brand/favicon-32.png" sizes="32x32" type="image/png">
<link rel="icon" href="dist/img/brand/favicon-16.png" sizes="16x16" type="image/png">
<link rel="apple-touch-icon" href="dist/img/brand/apple-touch-icon.png">
<link rel="apple-touch-icon" href="dist/img/icon-192.png">
<!-- Font Awesome Icons -->
<link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
+2 -8
View File
@@ -177,18 +177,12 @@ function qr_has_full_visibility() {
/**
* Apply the current session's owner scope to a MysqliDb query builder in place.
* No-op when the session has full visibility.
*
* Uses a single raw, parenthesized condition rather than where()+orWhere() -
* the previous two-call form produced "WHERE id = ? AND id_owner = ? OR id_owner
* IS NULL" whenever a caller had already added its own where('id', ...) (e.g.
* qrcode_image.php, bulk_action.php), and AND binds tighter than OR in SQL, so
* the OR silently detached from the id filter and matched *any* id_owner-NULL
* row instead of the one actually requested.
*/
function qr_apply_owner_scope($db) {
$scope_owner_id = qr_scope_owner_id();
if ($scope_owner_id !== null) {
$db->where('(id_owner = ' . (int) $scope_owner_id . ' OR id_owner IS NULL)');
$db->where('id_owner', $scope_owner_id);
$db->orWhere('id_owner', NULL, 'IS');
}
}
+7 -13
View File
@@ -2,9 +2,9 @@
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<!-- Brand Logo -->
<a href="./index.php" class="brand-link">
<img src="dist/img/brand/icon-inverse.svg" alt="QRForge" class="brand-image"
<img src="dist/img/Symbol_WhiteBlue.png" alt="Logo" class="brand-image"
style="opacity: .8">
<span class="brand-text font-weight-light">QRForge</span>
<span class="brand-text font-weight-light">Qrcode Generator</span>
</a>
<!-- Sidebar -->
@@ -53,20 +53,20 @@
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="./dynamic_qrcodes.php" <?php echo ((substr(CURRENT_PAGE, 0, 19) == 'dynamic_qrcodes.php')) ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="fas fa-angle-right nav-icon"></i>
<i class="far fa-circle nav-icon"></i>
<p>List all</p>
</a>
</li>
<?php if ($_SESSION['type'] !== 'user'): ?>
<li class="nav-item">
<a href="./dynamic_qrcode.php" <?php echo (CURRENT_PAGE == 'dynamic_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="fas fa-angle-right nav-icon"></i>
<i class="far fa-circle nav-icon"></i>
<p>Add new</p>
</a>
</li>
<li class="nav-item">
<a href="./batch_qrcode.php" <?php echo (CURRENT_PAGE == 'batch_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="fas fa-angle-right nav-icon"></i>
<i class="far fa-circle nav-icon"></i>
<p>Batch create (CSV)</p>
</a>
</li>
@@ -86,14 +86,14 @@
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="./static_qrcodes.php" <?php echo ((substr(CURRENT_PAGE, 0, 18) == 'static_qrcodes.php')) ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="fas fa-angle-right nav-icon"></i>
<i class="far fa-circle nav-icon"></i>
<p>List all</p>
</a>
</li>
<?php if ($_SESSION['type'] !== 'user'): ?>
<li class="nav-item">
<a href="./static_qrcode.php" <?php echo (CURRENT_PAGE == 'static_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="fas fa-angle-right nav-icon"></i>
<i class="far fa-circle nav-icon"></i>
<p>Add new</p>
</a>
</li>
@@ -109,12 +109,6 @@
</a>
</li>
<?php endif; ?>
<li class="nav-item">
<a href="./about.php" <?php echo (CURRENT_PAGE == 'about.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="fas fa-info-circle nav-icon"></i>
<p>About</p>
</a>
</li>
</ul>
</nav>
<!-- /.sidebar-menu -->
+15 -19
View File
@@ -70,7 +70,7 @@ foreach ($createdQrcode_static as $row) {
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qr Code Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
@@ -120,33 +120,29 @@ foreach ($createdQrcode_static as $row) {
</div><!-- /.col -->
<div class="col-12 col-sm-6 col-md-3">
<a href="dynamic_qrcodes.php" class="d-block">
<div class="info-box mb-3 bg-success">
<span class="info-box-icon"><i class="fa fa-qrcode"></i></span>
<div class="info-box mb-3 bg-success">
<span class="info-box-icon"><i class="fa fa-qrcode"></i></span>
<div class="info-box-content">
<span class="info-box-text">Dynamic Qr codes</span>
<span class="info-box-number"><?php echo $numQrcode_dynamic; ?></span>
</div><!-- /.info-box-content -->
</div>
</a>
<div class="info-box-content">
<span class="info-box-text">Dynamic Qr codes</span>
<span class="info-box-number"><?php echo $numQrcode_dynamic; ?></span>
</div><!-- /.info-box-content -->
</div>
</div><!-- /.col -->
<!-- fix for small devices only -->
<div class="clearfix hidden-md-up"></div>
<div class="col-12 col-sm-6 col-md-3">
<a href="static_qrcodes.php" class="d-block">
<div class="info-box mb-3 bg-danger">
<span class="info-box-icon"><i class="fa fa-qrcode"></i></span>
<div class="info-box mb-3 bg-danger">
<span class="info-box-icon"><i class="fa fa-qrcode"></i></span>
<div class="info-box-content">
<span class="info-box-text">Static QR codes</span>
<span class="info-box-number"><?php echo $numQrcode_static; ?></span>
</div><!-- /.info-box-content -->
<div class="info-box-content">
<span class="info-box-text">Static QR codes</span>
<span class="info-box-number"><?php echo $numQrcode_static; ?></span>
</div><!-- /.info-box-content -->
</div>
</a>
</div>
</div><!-- /.col -->
<div class="col-12 col-sm-6 col-md-3">
+1 -6
View File
@@ -174,7 +174,7 @@ class Qrcode {
$iconWidth = imagesx($icon);
$iconHeight = imagesy($icon);
$maxIconHeight = (int) ($height * 0.625);
$maxIconHeight = (int) ($height * 0.25);
$scale = min($maxIconHeight / $iconHeight, ($width * 0.6) / $iconWidth, 1);
$targetWidth = max(1, (int) ($iconWidth * $scale));
$targetHeight = max(1, (int) ($iconHeight * $scale));
@@ -207,12 +207,9 @@ class Qrcode {
}
public function getQrcode($id) {
require_once BASE_PATH . '/includes/security.php';
$db = getDbInstance();
$db->where('id', $id);
qr_apply_owner_scope($db);
$result = $db->getOne($this->table);
if($result !== NULL)
@@ -534,7 +531,6 @@ class Qrcode {
if(!file_exists(SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.'.$old_qrcode["format"]) || $data_to_db['filename'] == $input_data["old_filename"]){
$db->where('id', $input_data["id"]);
qr_apply_owner_scope($db);
$stat = $db->update($this->table, $data_to_db);
try{
@@ -567,7 +563,6 @@ class Qrcode {
$qrcode = $this->getQrcode($id);
$db->where('id', $id);
qr_apply_owner_scope($db);
$status = $db->delete($this->table);
if ($status) {
+1 -6
View File
@@ -159,7 +159,7 @@ class Qrcode {
$iconWidth = imagesx($icon);
$iconHeight = imagesy($icon);
$maxIconHeight = (int) ($height * 0.625);
$maxIconHeight = (int) ($height * 0.25);
$scale = min($maxIconHeight / $iconHeight, ($width * 0.6) / $iconWidth, 1);
$targetWidth = max(1, (int) ($iconWidth * $scale));
$targetHeight = max(1, (int) ($iconHeight * $scale));
@@ -192,12 +192,9 @@ class Qrcode {
}
public function getQrcode($id) {
require_once BASE_PATH . '/includes/security.php';
$db = getDbInstance();
$db->where('id', $id);
qr_apply_owner_scope($db);
$result = $db->getOne($this->table);
if($result !== NULL)
@@ -379,7 +376,6 @@ class Qrcode {
if(!file_exists(SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.'.$old_qrcode["format"]) || $data_to_db['filename'] == $input_data["old_filename"]){
$db->where('id', $input_data["id"]);
qr_apply_owner_scope($db);
$stat = $db->update($this->table, $data_to_db);
try{
@@ -412,7 +408,6 @@ class Qrcode {
$qrcode = $this->getQrcode($id);
$db->where('id', $id);
qr_apply_owner_scope($db);
$status = $db->delete($this->table);
if ($status) {
+2 -2
View File
@@ -68,13 +68,13 @@ if (isset($_COOKIE['series_id']) && isset($_COOKIE['remember_token']))
<!DOCTYPE html>
<html lang="en">
<title>Login - QRForge</title>
<title>Login - Qrcode Generator</title>
<?php include './includes/head.php'; ?>
<body class="login-page" style="min-height: 512.391px;">
<div class="login-box">
<div class="login-logo">
<img src="dist/img/brand/logo.svg" alt="QRForge" style="max-width: 260px;">
<img src="dist/img/DynamicQRCode_Original.png" style="width: 95%; height: 95%">
</div>
<div class="card">
+3 -3
View File
@@ -1,12 +1,12 @@
{
"name": "QRForge",
"short_name": "QRForge",
"name": "Qrcode Generator",
"short_name": "QRcode",
"description": "Self-hosted static and dynamic qr code generator",
"start_url": "index.php",
"scope": "./",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2563EB",
"theme_color": "#007bff",
"icons": [
{ "src": "dist/img/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "dist/img/icon-512.png", "sizes": "512x512", "type": "image/png" }
+1 -1
View File
@@ -4,7 +4,7 @@ require_once BASE_PATH . '/includes/auth_validate.php';
?>
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
+1 -1
View File
@@ -96,7 +96,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"]) && !isset($_P
?>
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
+1 -1
View File
@@ -29,7 +29,7 @@ $total_pages = $db->totalPages;
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
+1 -1
View File
@@ -51,7 +51,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"])) {
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>
+1 -1
View File
@@ -27,7 +27,7 @@ $total_pages = $db->totalPages;
<!DOCTYPE html>
<html lang="en">
<title>QRForge</title>
<title>Qrcode Generator</title>
<head>
<?php include './includes/head.php'; ?>
</head>