Compare commits
6 Commits
v0.9
...
96e2e5f475
| Author | SHA1 | Date | |
|---|---|---|---|
| 96e2e5f475 | |||
| e154e92c1a | |||
| fba327f5d6 | |||
| 8ba999a301 | |||
| 62ac0c93a1 | |||
| 7d9ae6acd2 |
@@ -75,6 +75,7 @@ RUN cd /opt \
|
||||
&& rm /opt/composer-setup.php /opt/composer-setup.sha384sum
|
||||
|
||||
RUN docker-php-source extract
|
||||
RUN docker-php-ext-configure gd --with-freetype --with-jpeg
|
||||
RUN docker-php-ext-install pdo_mysql zip exif pcntl gd
|
||||
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
|
||||
RUN docker-php-ext-install gettext && docker-php-ext-enable gettext
|
||||
|
||||
@@ -10,8 +10,11 @@ project by Giandonato Inverso, built on [AdminLTE](https://adminlte.io/).
|
||||
- **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).
|
||||
- **Commercial VIP edition:** the ability to give sub-users the ability to create
|
||||
QR codes as well, from their own (sub)account. If you have a bigger organisation,
|
||||
having more users being able to create new QR codes delegates your workload. To
|
||||
fund our open-source project, a small fee (€49/year subscription per organisation
|
||||
("tenant")) is requested for this. [www.qrforge.eu](https://www.qrforge.eu).
|
||||
- **Self-host it yourself:** this repository, MIT-licensed.
|
||||
|
||||
# Features
|
||||
@@ -31,7 +34,8 @@ project by Giandonato Inverso, built on [AdminLTE](https://adminlte.io/).
|
||||
- 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)
|
||||
optional view toggles set by an admin; per-account create rights are
|
||||
a VIP-edition feature, not available in this OSS version)
|
||||
- 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
-1
@@ -11,7 +11,7 @@ server {
|
||||
add_header Referrer-Policy "same-origin" always;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
try_files $uri /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
|
||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.9 KiB |
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.9 KiB |
@@ -26,25 +26,29 @@
|
||||
</div>
|
||||
|
||||
<?php if ($_SESSION['type'] === 'super'): ?>
|
||||
<?php $editing_self = $edit && (int) $user['id'] === (int) $_SESSION['user_id']; ?>
|
||||
<div class="col-sm-4">
|
||||
<label for="user-type">User type *</label>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="radio">
|
||||
<label class="radio">
|
||||
<input type="radio" name="type" value="super" required="required" <?php echo ($edit && $user['type'] =='super') ? "checked": "" ; ?>/> Super admin</label>
|
||||
<input type="radio" name="type" value="super" required="required" <?php echo ($edit && $user['type'] =='super') ? "checked": "" ; ?> <?php echo $editing_self ? "disabled" : ""; ?>/> Super admin</label>
|
||||
</div>
|
||||
|
||||
<div class="radio">
|
||||
<label class="radio">
|
||||
<input type="radio" name="type" value="admin" required="required" <?php echo ($edit && $user['type'] =='admin') ? "checked": "" ; ?>/> Admin</label>
|
||||
<input type="radio" name="type" value="admin" required="required" <?php echo ($edit && $user['type'] =='admin') ? "checked": "" ; ?> <?php echo $editing_self ? "disabled" : ""; ?>/> Admin</label>
|
||||
</div>
|
||||
|
||||
<div class="radio">
|
||||
<label class="radio">
|
||||
<input type="radio" name="type" value="user" required="required" id="type-user" <?php echo ($edit && $user['type'] =='user') ? "checked": "" ; ?>/> User (read-only)</label>
|
||||
<input type="radio" name="type" value="user" required="required" id="type-user" <?php echo ($edit && $user['type'] =='user') ? "checked": "" ; ?> <?php echo $editing_self ? "disabled" : ""; ?>/> User (read-only)</label>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($editing_self): ?>
|
||||
<small class="form-text text-muted">You can't change your own access level.</small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 mt-2" id="user-view-toggles">
|
||||
|
||||
@@ -139,6 +139,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="form-group">
|
||||
<label>Country</label>
|
||||
<input type="text" name="country" value="" placeholder="" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ class StaticQrcode {
|
||||
* create a qr code of type "vcard"
|
||||
*
|
||||
*/
|
||||
public function vcardQrcode($fullname, $nickname, $email, $website, $phone, $home_phone, $work_phone, $company, $role, $categories, $note, $photo, $address, $city, $postcode, $state)
|
||||
public function vcardQrcode($fullname, $nickname, $email, $website, $phone, $home_phone, $work_phone, $company, $role, $categories, $note, $photo, $address, $city, $postcode, $state, $country)
|
||||
{
|
||||
if($fullname != NULL && $phone != NULL){
|
||||
|
||||
@@ -188,7 +188,7 @@ class StaticQrcode {
|
||||
$vcard->categories($categories);
|
||||
$vcard->note($note);
|
||||
$vcard->photo($photo);
|
||||
$vcard->address($address, $city, $postcode, $state);
|
||||
$vcard->address($address, $city, $state, $postcode, $country);
|
||||
$vcard->create();
|
||||
|
||||
$this->sData = $vcard->get();
|
||||
@@ -202,7 +202,7 @@ class StaticQrcode {
|
||||
|
||||
$this->sContent .= '<div class="col-sm-4">';
|
||||
|
||||
$this->sContent .= '<strong>Phone:</strong> '.$phone.'<br>'.'<strong>Home Phone:</strong> '.$home_phone.'<br>'.'<strong>Work phone:</strong> '.$work_phone.'<br>'.'<strong>Address:</strong> '.$address.' '.$city.' '.$postcode.' '.$state.'</div>';
|
||||
$this->sContent .= '<strong>Phone:</strong> '.$phone.'<br>'.'<strong>Home Phone:</strong> '.$home_phone.'<br>'.'<strong>Work phone:</strong> '.$work_phone.'<br>'.'<strong>Address:</strong> '.$address.' '.$city.' '.$postcode.' '.$state.' '.$country.'</div>';
|
||||
|
||||
$this->sContent .= '</div>';
|
||||
|
||||
|
||||
@@ -160,7 +160,14 @@ class Users
|
||||
'edit' => "true",
|
||||
));
|
||||
|
||||
$requested_type = $_SESSION['type'] === 'admin' ? 'user' : ($input_data['type'] ?? '');
|
||||
$is_self_edit = (int) $input_data['id'] === (int) $_SESSION['user_id'];
|
||||
|
||||
// A user editing their own account keeps their current type, even if a
|
||||
// different value was submitted - prevents accidentally (or deliberately)
|
||||
// locking yourself out by downgrading your own access level.
|
||||
$requested_type = $_SESSION['type'] === 'admin'
|
||||
? 'user'
|
||||
: ($is_self_edit ? $target['type'] : ($input_data['type'] ?? ''));
|
||||
|
||||
$validation_error = $this->validateUsernameAndType($input_data['username'] ?? '', $requested_type);
|
||||
if ($validation_error !== null) {
|
||||
|
||||
@@ -43,10 +43,11 @@ class vCard
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function address($sAddress, $sCity, $sPostcode, $sState)
|
||||
public function address($sAddress, $sCity, $sState, $sPostcode, $sCountry)
|
||||
{
|
||||
// Component order per vCard 4.0 ADR: pobox;ext;street;locality;region;code;country
|
||||
$this->sData .= 'ADR:;;'.$sAddress.';';
|
||||
$this->sData .= $sCity.';'.$sPostcode.';'.$sState."\n";
|
||||
$this->sData .= $sCity.';'.$sState.';'.$sPostcode.';'.$sCountry."\n";
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"]) && !isset($_P
|
||||
case 'location': $static_qrcode_instance->locationQrcode($_POST['latitude'], $_POST['longitude']);
|
||||
break;
|
||||
|
||||
case 'vcard': $static_qrcode_instance->vcardQrcode($_POST['full_name'], $_POST['nickname'], $_POST['email'], $_POST['website'], $_POST['phone'], $_POST['home_phone'], $_POST['work_phone'], $_POST['company'], $_POST['role'], $_POST['categories'], $_POST['note'], $_POST['photo'], $_POST['address'], $_POST['city'], $_POST['post_code'], $_POST['state']);
|
||||
case 'vcard': $static_qrcode_instance->vcardQrcode($_POST['full_name'], $_POST['nickname'], $_POST['email'], $_POST['website'], $_POST['phone'], $_POST['home_phone'], $_POST['work_phone'], $_POST['company'], $_POST['role'], $_POST['categories'], $_POST['note'], $_POST['photo'], $_POST['address'], $_POST['city'], $_POST['post_code'], $_POST['state'], $_POST['country']);
|
||||
break;
|
||||
|
||||
case 'event': $static_qrcode_instance->eventQrcode($_POST['title'], $_POST['start'], $_POST['end'], $_POST['timezone'], $_POST['location'], $_POST['description'], $_POST['url']);
|
||||
|
||||
Reference in New Issue
Block a user