Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f2d4c945af | |||
| 6ef56bcb58 | |||
| 7967c36a11 | |||
| 357aff618c | |||
| 8c7823949e |
@@ -75,6 +75,7 @@ RUN cd /opt \
|
|||||||
&& rm /opt/composer-setup.php /opt/composer-setup.sha384sum
|
&& rm /opt/composer-setup.php /opt/composer-setup.sha384sum
|
||||||
|
|
||||||
RUN docker-php-source extract
|
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 pdo_mysql zip exif pcntl gd
|
||||||
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
|
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
|
||||||
RUN docker-php-ext-install gettext && docker-php-ext-enable gettext
|
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
|
- **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
|
instance. Self-service signup isn't live yet, so log in with the temporary shared demo
|
||||||
account `admin` / `admin` in the meantime.
|
account `admin` / `admin` in the meantime.
|
||||||
- **Commercial VIP edition** (self-service create-rights, logo-embedded QR codes):
|
- **Commercial VIP edition:** the ability to give sub-users the ability to create
|
||||||
[www.qrforge.eu](https://www.qrforge.eu).
|
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.
|
- **Self-host it yourself:** this repository, MIT-licensed.
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
@@ -31,7 +34,8 @@ project by Giandonato Inverso, built on [AdminLTE](https://adminlte.io/).
|
|||||||
- Installable as a PWA
|
- Installable as a PWA
|
||||||
- Role-based access: `super` (full access + user management), `admin`
|
- Role-based access: `super` (full access + user management), `admin`
|
||||||
(scoped to their own codes and sub-users), `user` (read-only, with
|
(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
|
- Dashboard with QR/scan statistics and a 7-day activity chart
|
||||||
- CSRF protection, login rate limiting, session hardening, audit log
|
- CSRF protection, login rate limiting, session hardening, audit log
|
||||||
- Docker Compose setup, both a dev image and a production Nginx + PHP-FPM image
|
- Docker Compose setup, both a dev image and a production Nginx + PHP-FPM image
|
||||||
|
|||||||
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>
|
</div>
|
||||||
|
|
||||||
<?php if ($_SESSION['type'] === 'super'): ?>
|
<?php if ($_SESSION['type'] === 'super'): ?>
|
||||||
|
<?php $editing_self = $edit && (int) $user['id'] === (int) $_SESSION['user_id']; ?>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<label for="user-type">User type *</label>
|
<label for="user-type">User type *</label>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label 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>
|
||||||
|
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label 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>
|
||||||
|
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label 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>
|
||||||
</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>
|
||||||
|
|
||||||
<div class="col-sm-12 mt-2" id="user-view-toggles">
|
<div class="col-sm-12 mt-2" id="user-view-toggles">
|
||||||
|
|||||||
@@ -139,6 +139,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ class StaticQrcode {
|
|||||||
* create a qr code of type "vcard"
|
* 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){
|
if($fullname != NULL && $phone != NULL){
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ class StaticQrcode {
|
|||||||
$vcard->categories($categories);
|
$vcard->categories($categories);
|
||||||
$vcard->note($note);
|
$vcard->note($note);
|
||||||
$vcard->photo($photo);
|
$vcard->photo($photo);
|
||||||
$vcard->address($address, $city, $postcode, $state);
|
$vcard->address($address, $city, $state, $postcode, $country);
|
||||||
$vcard->create();
|
$vcard->create();
|
||||||
|
|
||||||
$this->sData = $vcard->get();
|
$this->sData = $vcard->get();
|
||||||
@@ -202,7 +202,7 @@ class StaticQrcode {
|
|||||||
|
|
||||||
$this->sContent .= '<div class="col-sm-4">';
|
$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>';
|
$this->sContent .= '</div>';
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,14 @@ class Users
|
|||||||
'edit' => "true",
|
'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);
|
$validation_error = $this->validateUsernameAndType($input_data['username'] ?? '', $requested_type);
|
||||||
if ($validation_error !== null) {
|
if ($validation_error !== null) {
|
||||||
|
|||||||
@@ -43,10 +43,11 @@ class vCard
|
|||||||
*
|
*
|
||||||
* @return self
|
* @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 .= 'ADR:;;'.$sAddress.';';
|
||||||
$this->sData .= $sCity.';'.$sPostcode.';'.$sState."\n";
|
$this->sData .= $sCity.';'.$sState.';'.$sPostcode.';'.$sCountry."\n";
|
||||||
return $this;
|
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']);
|
case 'location': $static_qrcode_instance->locationQrcode($_POST['latitude'], $_POST['longitude']);
|
||||||
break;
|
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;
|
break;
|
||||||
|
|
||||||
case 'event': $static_qrcode_instance->eventQrcode($_POST['title'], $_POST['start'], $_POST['end'], $_POST['timezone'], $_POST['location'], $_POST['description'], $_POST['url']);
|
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