Merge remote-tracking branch 'origin/develop'

This commit is contained in:
snipe 2021-04-20 14:55:48 -07:00
commit 83200d3cbd
76 changed files with 8309 additions and 6146 deletions

View file

@ -2146,6 +2146,157 @@
"contributions": [
"code"
]
},
{
"login": "raelldottin",
"name": "Raell Dottin",
"avatar_url": "https://avatars.githubusercontent.com/u/317015?v=4",
"profile": "https://github.com/raelldottin",
"contributions": [
"code"
]
},
{
"login": "misilot",
"name": "Tom Misilo",
"avatar_url": "https://avatars.githubusercontent.com/u/1446856?v=4",
"profile": "https://github.com/misilot",
"contributions": [
"code"
]
},
{
"login": "JuustoMestari",
"name": "David Davenne",
"avatar_url": "https://avatars.githubusercontent.com/u/4496300?v=4",
"profile": "http://david.davenne.be",
"contributions": [
"code"
]
},
{
"login": "ocelotsloth",
"name": "Mark Stenglein",
"avatar_url": "https://avatars.githubusercontent.com/u/9255772?v=4",
"profile": "https://markstenglein.com",
"contributions": [
"code"
]
},
{
"login": "ajsy",
"name": "ajsy",
"avatar_url": "https://avatars.githubusercontent.com/u/35658596?v=4",
"profile": "https://github.com/ajsy",
"contributions": [
"code"
]
},
{
"login": "t3easy",
"name": "Jan Kiesewetter",
"avatar_url": "https://avatars.githubusercontent.com/u/3628035?v=4",
"profile": "https://github.com/t3easy",
"contributions": [
"code"
]
},
{
"login": "Tetrachloromethane250",
"name": "Tetrachloromethane250",
"avatar_url": "https://avatars.githubusercontent.com/u/79449630?v=4",
"profile": "https://github.com/Tetrachloromethane250",
"contributions": [
"code"
]
},
{
"login": "kajes",
"name": "Lars Kajes",
"avatar_url": "https://avatars.githubusercontent.com/u/22004482?v=4",
"profile": "https://www.kajes.se/",
"contributions": [
"code"
]
},
{
"login": "Joly0",
"name": "Joly0",
"avatar_url": "https://avatars.githubusercontent.com/u/13993216?v=4",
"profile": "https://github.com/Joly0",
"contributions": [
"code"
]
},
{
"login": "limeless",
"name": "theburger",
"avatar_url": "https://avatars.githubusercontent.com/u/1501022?v=4",
"profile": "https://github.com/limeless",
"contributions": [
"code"
]
},
{
"login": "deivishome",
"name": "David Valin Alonso",
"avatar_url": "https://avatars.githubusercontent.com/u/36065681?v=4",
"profile": "https://github.com/deivishome",
"contributions": [
"code"
]
},
{
"login": "andreaci",
"name": "andreaci",
"avatar_url": "https://avatars.githubusercontent.com/u/8290389?v=4",
"profile": "https://github.com/andreaci",
"contributions": [
"code"
]
},
{
"login": "Jelle-S",
"name": "Jelle Sebreghts",
"avatar_url": "https://avatars.githubusercontent.com/u/1828542?v=4",
"profile": "http://www.jellesebreghts.be",
"contributions": [
"code"
]
},
{
"login": "Skywalker-11",
"name": "Michael Pietsch",
"avatar_url": "https://avatars.githubusercontent.com/u/11180862?v=4",
"profile": "https://github.com/Skywalker-11",
"contributions": []
},
{
"login": "sh1hab",
"name": "Masudul Haque Shihab",
"avatar_url": "https://avatars.githubusercontent.com/u/22068886?v=4",
"profile": "https://github.com/sh1hab",
"contributions": [
"code"
]
},
{
"login": "zybersup",
"name": "Supapong Areeprasertkul",
"avatar_url": "https://avatars.githubusercontent.com/u/16099942?v=4",
"profile": "http://www.freedomdive.com/",
"contributions": [
"code"
]
},
{
"login": "psarossy",
"name": "Peter Sarossy",
"avatar_url": "https://avatars.githubusercontent.com/u/207358?v=4",
"profile": "https://github.com/psarossy",
"contributions": [
"code"
]
}
]
}

View file

@ -144,6 +144,11 @@ APP_LOG=single
APP_LOG_MAX_FILES=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC
APP_FORCE_TLS=false
GOOGLE_MAPS_API=
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600
IMPORT_TIME_LIMIT=600
IMPORT_MEMORY_LIMIT=500M
REPORT_TIME_LIMIT=12000

2
.gitignore vendored
View file

@ -52,6 +52,8 @@ tests/_support/_generated/*
*.cache
.vagrant
*.log
*.retry
\.php_cs\.dist

View file

@ -57,6 +57,7 @@ RUN \
&& mkdir -p "/var/lib/snipeit/dumps" && rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
&& mkdir -p "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
&& ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
&& chown -h "/var/www/html/storage/*.key"
&& chown -R apache "/var/lib/snipeit"
# Install composer

View file

@ -1,5 +1,5 @@
![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-235-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-252-orange.svg?style=flat-square)](#contributors)
## Snipe-IT - Open Source Asset Management System
@ -122,7 +122,9 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars0.githubusercontent.com/u/1255375?v=4" width="110px;"/><br /><sub>Peter Upfold</sub>](https://peter.upfold.org.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterUpfold "Code") | [<img src="https://avatars2.githubusercontent.com/u/961717?v=4" width="110px;"/><br /><sub>Jared Biel</sub>](https://github.com/jbiel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbiel "Code") | [<img src="https://avatars1.githubusercontent.com/u/1733625?v=4" width="110px;"/><br /><sub>Dampfklon</sub>](https://github.com/dampfklon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dampfklon "Code") | [<img src="https://avatars2.githubusercontent.com/u/52973156?v=4" width="110px;"/><br /><sub>Charles Hamilton</sub>](https://communityclosing.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chamilton-ccn "Code") | [<img src="https://avatars.githubusercontent.com/u/551789?v=4" width="110px;"/><br /><sub>Giuseppe Iannello</sub>](https://github.com/giannello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=giannello "Code") | [<img src="https://avatars.githubusercontent.com/u/3691490?v=4" width="110px;"/><br /><sub>Peter Dave Hello</sub>](https://www.peterdavehello.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterDaveHello "Code") | [<img src="https://avatars.githubusercontent.com/u/6106332?v=4" width="110px;"/><br /><sub>sigmoidal</sub>](https://github.com/sigmoidal)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sigmoidal "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2082554?v=4" width="110px;"/><br /><sub>Vincent Lainé</sub>](https://github.com/phenixdotnet)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phenixdotnet "Code") | [<img src="https://avatars.githubusercontent.com/u/1943040?v=4" width="110px;"/><br /><sub>Lucas Pleß</sub>](http://www.lucas-pless.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derlucas "Code") | [<img src="https://avatars.githubusercontent.com/u/472804?v=4" width="110px;"/><br /><sub>Ian Littman</sub>](http://twitter.com/iansltx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=iansltx "Code") | [<img src="https://avatars.githubusercontent.com/u/3519029?v=4" width="110px;"/><br /><sub>João Paulo</sub>](https://github.com/PauloLuna)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PauloLuna "Code") | [<img src="https://avatars.githubusercontent.com/u/70443365?v=4" width="110px;"/><br /><sub>ThoBur</sub>](https://github.com/ThoBur)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ThoBur "Code") | [<img src="https://avatars.githubusercontent.com/u/1972329?v=4" width="110px;"/><br /><sub>Alexander Chibrikin</sub>](http://phpprofi.ru/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") | [<img src="https://avatars.githubusercontent.com/u/438332?v=4" width="110px;"/><br /><sub>Anthony Winstanley</sub>](https://github.com/winstan)<br />[💻](https://github.com/snipe/snipe-it/commits?author=winstan "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3075214?v=4" width="110px;"/><br /><sub>Folke</sub>](https://github.com/fashberg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fashberg "Code") | [<img src="https://avatars.githubusercontent.com/u/1351571?v=4" width="110px;"/><br /><sub>Bennett Blodinger</sub>](https://github.com/benwa)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benwa "Code") | [<img src="https://avatars.githubusercontent.com/u/2974631?v=4" width="110px;"/><br /><sub>NMC</sub>](https://nmc.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ncareau "Code") | [<img src="https://avatars.githubusercontent.com/u/52182449?v=4" width="110px;"/><br /><sub>andres-baller</sub>](https://github.com/andres-baller)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andres-baller "Code") | [<img src="https://avatars.githubusercontent.com/u/67109348?v=4" width="110px;"/><br /><sub>sean-borg</sub>](https://github.com/sean-borg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sean-borg "Code") | [<img src="https://avatars.githubusercontent.com/u/32170051?v=4" width="110px;"/><br /><sub>EDVLeer</sub>](https://github.com/EDVLeer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EDVLeer "Code") | [<img src="https://avatars.githubusercontent.com/u/23075196?v=4" width="110px;"/><br /><sub>Kurokat</sub>](https://github.com/Kurokat)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Kurokat "Code") |
| [<img src="https://avatars.githubusercontent.com/u/915514?v=4" width="110px;"/><br /><sub>Kevin Köllmann</sub>](https://www.kevinkoellmann.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koelle25 "Code") | [<img src="https://avatars.githubusercontent.com/u/49025941?v=4" width="110px;"/><br /><sub>sw-mreyes</sub>](https://github.com/sw-mreyes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sw-mreyes "Code") | [<img src="https://avatars.githubusercontent.com/u/70129?v=4" width="110px;"/><br /><sub>Joel Pittet</sub>](https://pittet.ca)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joelpittet "Code") | [<img src="https://avatars.githubusercontent.com/u/792695?v=4" width="110px;"/><br /><sub>Eli Young</sub>](https://elyscape.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=elyscape "Code") |
| [<img src="https://avatars.githubusercontent.com/u/915514?v=4" width="110px;"/><br /><sub>Kevin Köllmann</sub>](https://www.kevinkoellmann.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koelle25 "Code") | [<img src="https://avatars.githubusercontent.com/u/49025941?v=4" width="110px;"/><br /><sub>sw-mreyes</sub>](https://github.com/sw-mreyes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sw-mreyes "Code") | [<img src="https://avatars.githubusercontent.com/u/70129?v=4" width="110px;"/><br /><sub>Joel Pittet</sub>](https://pittet.ca)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joelpittet "Code") | [<img src="https://avatars.githubusercontent.com/u/792695?v=4" width="110px;"/><br /><sub>Eli Young</sub>](https://elyscape.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=elyscape "Code") | [<img src="https://avatars.githubusercontent.com/u/317015?v=4" width="110px;"/><br /><sub>Raell Dottin</sub>](https://github.com/raelldottin)<br />[💻](https://github.com/snipe/snipe-it/commits?author=raelldottin "Code") | [<img src="https://avatars.githubusercontent.com/u/1446856?v=4" width="110px;"/><br /><sub>Tom Misilo</sub>](https://github.com/misilot)<br />[💻](https://github.com/snipe/snipe-it/commits?author=misilot "Code") | [<img src="https://avatars.githubusercontent.com/u/4496300?v=4" width="110px;"/><br /><sub>David Davenne</sub>](http://david.davenne.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JuustoMestari "Code") |
| [<img src="https://avatars.githubusercontent.com/u/9255772?v=4" width="110px;"/><br /><sub>Mark Stenglein</sub>](https://markstenglein.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ocelotsloth "Code") | [<img src="https://avatars.githubusercontent.com/u/35658596?v=4" width="110px;"/><br /><sub>ajsy</sub>](https://github.com/ajsy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ajsy "Code") | [<img src="https://avatars.githubusercontent.com/u/3628035?v=4" width="110px;"/><br /><sub>Jan Kiesewetter</sub>](https://github.com/t3easy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=t3easy "Code") | [<img src="https://avatars.githubusercontent.com/u/79449630?v=4" width="110px;"/><br /><sub>Tetrachloromethane250</sub>](https://github.com/Tetrachloromethane250)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tetrachloromethane250 "Code") | [<img src="https://avatars.githubusercontent.com/u/22004482?v=4" width="110px;"/><br /><sub>Lars Kajes</sub>](https://www.kajes.se/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kajes "Code") | [<img src="https://avatars.githubusercontent.com/u/13993216?v=4" width="110px;"/><br /><sub>Joly0</sub>](https://github.com/Joly0)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Joly0 "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>theburger</sub>](https://github.com/limeless)<br />[💻](https://github.com/snipe/snipe-it/commits?author=limeless "Code") |
| [<img src="https://avatars.githubusercontent.com/u/36065681?v=4" width="110px;"/><br /><sub>David Valin Alonso</sub>](https://github.com/deivishome)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deivishome "Code") | [<img src="https://avatars.githubusercontent.com/u/8290389?v=4" width="110px;"/><br /><sub>andreaci</sub>](https://github.com/andreaci)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreaci "Code") | [<img src="https://avatars.githubusercontent.com/u/1828542?v=4" width="110px;"/><br /><sub>Jelle Sebreghts</sub>](http://www.jellesebreghts.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jelle-S "Code") | [<img src="https://avatars.githubusercontent.com/u/11180862?v=4" width="110px;"/><br /><sub>Michael Pietsch</sub>](https://github.com/Skywalker-11)<br /> | [<img src="https://avatars.githubusercontent.com/u/22068886?v=4" width="110px;"/><br /><sub>Masudul Haque Shihab</sub>](https://github.com/sh1hab)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sh1hab "Code") | [<img src="https://avatars.githubusercontent.com/u/16099942?v=4" width="110px;"/><br /><sub>Supapong Areeprasertkul</sub>](http://www.freedomdive.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zybersup "Code") | [<img src="https://avatars.githubusercontent.com/u/207358?v=4" width="110px;"/><br /><sub>Peter Sarossy</sub>](https://github.com/psarossy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=psarossy "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

27
Vagrantfile vendored
View file

@ -8,25 +8,34 @@ Vagrant.configure("2") do |config|
config.vm.define "bionic" do |bionic|
bionic.vm.box = "ubuntu/bionic64"
bionic.vm.hostname = 'bionic'
bionic.vm.network "public_network", bridge: NETWORK_BRIDGE
bionic.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
bionic.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
bionic.vm.network "forwarded_port", guest: 80, host: 8080
bionic.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
bionic.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "xenial" do |xenial|
xenial.vm.box = "ubuntu/xenial64"
xenial.vm.hostname = 'xenial'
xenial.vm.network "public_network", bridge: NETWORK_BRIDGE
xenial.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
xenial.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
xenial.vm.network "forwarded_port", guest: 80, host: 8080
xenial.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
xenial.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "trusty" do |trusty|
trusty.vm.box = "ubuntu/trusty32"
trusty.vm.hostname = 'trusty'
trusty.vm.network "public_network", bridge: NETWORK_BRIDGE
trusty.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
trusty.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
trusty.vm.network "forwarded_port", guest: 80, host: 8080
trusty.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
trusty.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "centos7" do |centos7|

View file

@ -0,0 +1,10 @@
<VirtualHost *:80>
<Directory {{ app_path }}/public>
Allow From All
AllowOverride All
Options -Indexes
</Directory>
DocumentRoot {{ app_path }}/public
ServerName {{ fqdn }}
</VirtualHost>

View file

@ -0,0 +1,226 @@
---
- name: Set up local server
hosts: all
remote_user: vagrant
become_user: root
become_method: sudo
vars:
app_path: "/var/www/snipeit"
fqdn: "localhost"
tasks:
- name: Update and upgrade existing apt packages
become: true
apt:
upgrade: yes
update_cache: yes
- name: Install Utilities
become: true
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- nano
- vim
- name: Installing Apache httpd, PHP, MariaDB and other requirements.
become: true
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- mariadb-client
- php
- php-curl
- php-mysql
- php-gd
- php-ldap
- php-zip
- php-mbstring
- php-xml
- php-bcmath
- curl
- git
- unzip
- python-pymysql
#
# Install the lastest version of composer
#
- name: Composer check
stat:
path: /usr/local/bin/composer
register: composer_exits
- name: Install Composer
shell: |
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
mv composer.phar /usr/local/bin/composer
exit $RESULT
when: not composer_exits.stat.exists
args:
creates: /usr/local/bin/composer
become: true
#
# Install and Configure MariaDB
#
- name: Install MariaDB
become: true
apt:
name: mariadb-server
state: present
register: sql_server
- name: Start and Enable MySQL server
become: true
systemd:
state: started
enabled: yes
name: mariadb
- name: Create Vagrant mysql password
become: true
mysql_user:
name: vagrant
password: vagrant
login_unix_socket: /var/run/mysqld/mysqld.sock
priv: "*.*:ALL"
state: present
- name: Enable remote mysql
replace:
path: /etc/mysql/mariadb.conf.d/50-server.cnf
regexp: "127.0.0.1"
replace: "0.0.0.0"
become: true
notify:
- restart mysql
- name: Create snipeit database
become: true
mysql_db:
name: snipeit
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
#
# Install Apache Web Server
#
- name: Install Apache 2.4
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- apache2
- libapache2-mod-php
become: true
register: apache2_server
- name: Start and Enable Apache2 Server
become: true
systemd:
name: apache2
state: started
enabled: yes
#- name: Disable Apache modules
# become: true
# apache2_module:
# state: absent
# name: "{{ item }}"
# with_items:
# #- mpm_prefork
# notify:
# - restart apache2
- name: Enable Apache modules
become: true
apache2_module:
state: present
name: "{{ item }}"
with_items:
- rewrite
- vhost_alias
- deflate
- expires
- proxy_fcgi
- proxy
notify:
- restart apache2
- name: Install Apache VirtualHost File
become: true
template:
src: apachevirtualhost.conf.j2
dest: "/etc/apache2/sites-available/snipeit.conf"
- name: Enable VirtualHost
become: true
command: a2ensite snipeit
args:
creates: /etc/apache2/sites-enabled/snipeit.conf
notify:
- restart apache2
- name: Map apache dir to local folder
become: true
file:
src: /vagrant
dest: "{{ app_path }}"
state: link
notify:
- restart apache2
#
# Install dependencies from composer
#
- name: Install dependencies from composer
composer:
command: install
working_dir: "{{ app_path }}"
notify:
- restart apache2
#
# Configure .env file
#
- name: Copy .env file
copy:
src: "{{ app_path }}/.env.example"
dest: "{{ app_path }}/.env"
- name: Configure .env file
lineinfile:
path: "{{ app_path }}/.env"
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: '^DB_HOST=', line: 'DB_HOST=127.0.0.1'}
- { regexp: '^DB_DATABASE=', line: 'DB_DATABASE=snipeit' }
- { regexp: '^DB_USERNAME=', line: 'DB_USERNAME=vagrant' }
- { regexp: '^DB_PASSWORD=', line: 'DB_PASSWORD=vagrant' }
- { regexp: '^APP_URL=', line: "APP_URL=http://{{ fqdn }}" }
- { regexp: '^APP_ENV=', line: "APP_ENV=development" }
- { regexp: '^APP_DEBUG=', line: "APP_ENV=true" }
- name: Generate application key
shell: "php {{ app_path }}/artisan key:generate --force"
- name: Artisan Migrate
shell: "php {{ app_path }}/artisan migrate --force"
#
# Create Cron Job
#
- name: Create scheduler cron job
become: true
cron:
name: "Snipe-IT Artisan Scheduler"
job: "/usr/bin/php {{ app_path }}/artisan schedule:run"
handlers:
- name: restart apache2
become: true
systemd:
name: apache2
state: restarted
- name: restart mysql
become: true
systemd:
name: mysql
state: restarted

View file

@ -2,6 +2,7 @@
namespace App\Console\Commands;
use App\Models\Department;
use Illuminate\Console\Command;
use App\Models\Setting;
use App\Models\Ldap;
@ -51,6 +52,10 @@ class LdapSync extends Command
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag_field;
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
$ldap_result_email = Setting::getSettings()->ldap_email;
$ldap_result_phone = Setting::getSettings()->ldap_phone_field;
$ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle;
$ldap_result_country = Setting::getSettings()->ldap_country;
$ldap_result_dept = Setting::getSettings()->ldap_dept;
try {
$ldapconn = Ldap::connectToLdap();
@ -175,6 +180,16 @@ class LdapSync extends Command
$item["email"] = isset($results[$i][$ldap_result_email][0]) ? $results[$i][$ldap_result_email][0] : "" ;
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
$item["telephone"] = isset($results[$i][$ldap_result_phone][0]) ? $results[$i][$ldap_result_phone][0] : "";
$item["jobtitle"] = isset($results[$i][$ldap_result_jobtitle][0]) ? $results[$i][$ldap_result_jobtitle][0] : "";
$item["country"] = isset($results[$i][$ldap_result_country][0]) ? $results[$i][$ldap_result_country][0] : "";
$item["department"] = isset($results[$i][$ldap_result_dept][0]) ? $results[$i][$ldap_result_dept][0] : "";
$department = Department::firstOrCreate([
'name' => $item["department"],
]);
$user = User::where('username', $item["username"])->first();
if ($user) {
@ -193,6 +208,10 @@ class LdapSync extends Command
$user->username = $item["username"];
$user->email = $item["email"];
$user->employee_num = e($item["employee_number"]);
$user->phone = $item["telephone"];
$user->jobtitle = $item["jobtitle"];
$user->country = $item["country"];
$user->department_id = $department->id;
// Sync activated state for Active Directory.
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {

View file

@ -5,8 +5,8 @@ use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
ini_set('max_execution_time', env('IMPORT_TIME_LIM', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEM_LIM', '500M'));
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
/**
* Class ObjectImportCommand

View file

@ -1002,38 +1002,38 @@ class Helper
* @return string path to uploaded image or false if something went wrong
*/
public static function processUploadedImage(String $image_data, String $save_path) {
if ($image_data != null && $save_path != null) {
// After modification, the image is prefixed by mime info like the following:
// data:image/jpeg;base64,; This causes the image library to be unhappy, so we need to remove it.
$header = explode(';', $image_data, 2)[0];
// Grab the image type from the header while we're at it.
$extension = substr($header, strpos($header, '/')+1);
// Start reading the image after the first comma, postceding the base64.
$image = substr($image_data, strpos($image_data, ',')+1);
$file_name = str_random(25).".".$extension;
$directory= public_path($save_path);
// Check if the uploads directory exists. If not, try to create it.
if (!file_exists($directory)) {
mkdir($directory, 0755, true);
}
$path = public_path($save_path.$file_name);
try {
Image::make($image)->resize(500, 500, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
} catch (\Exception $e) {
return false;
}
return $file_name;
if ($image_data == null || $save_path == null) {
return false;
}
return false;
// After modification, the image is prefixed by mime info like the following:
// data:image/jpeg;base64,; This causes the image library to be unhappy, so we need to remove it.
$header = explode(';', $image_data, 2)[0];
// Grab the image type from the header while we're at it.
$extension = substr($header, strpos($header, '/')+1);
// Start reading the image after the first comma, postceding the base64.
$image = substr($image_data, strpos($image_data, ',')+1);
$file_name = str_random(25).".".$extension;
$directory= public_path($save_path);
// Check if the uploads directory exists. If not, try to create it.
if (!file_exists($directory)) {
mkdir($directory, 0755, true);
}
$path = public_path($save_path.$file_name);
try {
Image::make($image)->resize(500, 500, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
} catch (\Exception $e) {
return false;
}
return $file_name;
}
}

View file

@ -254,7 +254,8 @@ class AccessoriesController extends Controller
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $request->get('assigned_to')
'assigned_to' => $request->get('assigned_to'),
'note' => $request->get('note')
]);
$accessory->logCheckout($request->input('note'), $user);

View file

@ -478,13 +478,36 @@ class AssetsController extends Controller
$model = AssetModel::find($request->get('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted=='1') {
if (Gate::allows('admin')) {
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
} else {
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
// Set the field value based on what was sent in the request
$field_val = $request->input($field->convertUnicodeDbSlug(), null);
// If input value is null, use custom field's default value
if ($field_val == null) {
\Log::debug('Field value for '.$field->convertUnicodeDbSlug().' is null');
$field_val = $field->defaultValue($request->get('model_id'));
\Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
}
// if the field is set to encrypted, make sure we encrypt the value
if ($field->field_encrypted == '1') {
\Log::debug('This model field is encrypted in this fieldset.');
if (Gate::allows('admin')) {
// If input value is null, use custom field's default value
if (($field_val == null) && ($request->has('model_id')!='')){
$field_val = \Crypt::encrypt($field->defaultValue($request->get('model_id')));
} else {
$field_val = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
}
}
$asset->{$field->convertUnicodeDbSlug()} = $field_val;
}
}

View file

@ -95,7 +95,14 @@ class CustomFieldsController extends Controller
$field = new CustomField;
$data = $request->all();
$validator = Validator::make($data, $field->validationRules());
$regex_format = null;
if (str_contains($data["format"], "regex:")){
$regex_format = $data["format"];
}
$validator = Validator::make($data, $field->validationRules($regex_format));
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}

View file

@ -0,0 +1,138 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\LicenseSeatsTransformer;
use App\Models\Asset;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\User;
use Auth;
use Illuminate\Http\Request;
class LicenseSeatsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request, $licenseId)
{
//
if ($license = License::find($licenseId)) {
$this->authorize('view', $license);
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
->where('license_seats.license_id', $licenseId);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
if ($request->input('sort')=='department') {
$seats->OrderDepartments($order);
} else {
$seats->orderBy('id', $order);
}
$total = $seats->count();
$offset = (($seats) && (request('offset') > $total)) ? 0 : request('offset', 0);
$limit = request('limit', 50);
$seats = $seats->skip($offset)->take($limit)->get();
if ($seats) {
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($licenseId, $seatId)
{
//
$this->authorize('view', License::class);
// sanity checks:
// 1. does the license seat exist?
if (!$licenseSeat = LicenseSeat::find($seatId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
}
// 2. does the seat belong to the specified license?
if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
}
return (new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $licenseId
* @param int $seatId
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $licenseId, $seatId)
{
$this->authorize('checkout', License::class);
// sanity checks:
// 1. does the license seat exist?
if (!$licenseSeat = LicenseSeat::find($seatId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
}
// 2. does the seat belong to the specified license?
if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
}
$oldUser = $licenseSeat->user()->first();
$oldAsset = $licenseSeat->asset()->first();
// attempt to update the license seat
$licenseSeat->fill($request->all());
$licenseSeat->user_id = Auth::user()->id;
// check if this update is a checkin operation
// 1. are relevant fields touched at all?
$touched = $licenseSeat->isDirty('assigned_to') || $licenseSeat->isDirty('asset_id');
// 2. are they cleared? if yes then this is a checkin operation
$is_checkin = ($touched && $licenseSeat->assigned_to === null && $licenseSeat->asset_id === null);
if (!$touched) {
// nothing to update
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
}
if ($licenseSeat->save()) {
// the logging functions expect only one "target". if both asset and user are present in the request,
// we simply let assets take precedence over users...
$changes = $licenseSeat->getChanges();
if (array_key_exists('assigned_to', $changes)) {
$target = $is_checkin ? $oldUser : User::find($changes['assigned_to']);
}
if (array_key_exists('asset_id', $changes)) {
$target = $is_checkin ? $oldAsset : Asset::find($changes['asset_id']);
}
if ($is_checkin) {
$licenseSeat->logCheckin($target, $request->input('note'));
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
}
// in this case, relevant fields are touched but it's not a checkin operation. so it must be a checkout operation.
$licenseSeat->logCheckout($request->input('note'), $target);
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
}
return Helper::formatStandardApiResponse('error', null, $licenseSeat->getErrors());
}
}

View file

@ -237,50 +237,6 @@ class LicensesController extends Controller
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users')));
}
/**
* Get license seat listing
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $licenseId
* @return \Illuminate\Contracts\View\View
*/
public function seats(Request $request, $licenseId)
{
if ($license = License::find($licenseId)) {
$this->authorize('view', $license);
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
->where('license_seats.license_id', $licenseId);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
if ($request->input('sort')=='department') {
$seats->OrderDepartments($order);
} else {
$seats->orderBy('id', $order);
}
$offset = (($seats) && (request('offset') > $seats->count())) ? 0 : request('offset', 0);
$limit = request('limit', 50);
$total = $seats->count();
$seats = $seats->skip($offset)->take($limit)->get();
if ($seats) {
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
}
/**
* Gets a paginated collection for the select2 menus

View file

@ -484,7 +484,7 @@ class AssetsController extends Controller
return response()->file($barcode_file, $header);
} else {
// Calculate barcode width in pixel based on label width (inch)
$barcode_width = ($settings->labels_width - $settings->labels_display_sgutter) * 96.000000000001;
$barcode_width = ($settings->labels_width - $settings->labels_display_sgutter) * 200.000000000001;
$barcode = new \Com\Tecnick\Barcode\Barcode();
try {

View file

@ -58,12 +58,12 @@ class LoginController extends Controller
*
* @return void
*/
public function __construct(LdapAd $ldap, Saml $saml)
public function __construct(/*LdapAd $ldap, */ Saml $saml)
{
parent::__construct();
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
Session::put('backUrl', \URL::previous());
$this->ldap = $ldap;
// $this->ldap = $ldap;
$this->saml = $saml;
}
@ -140,10 +140,10 @@ class LoginController extends Controller
*
* @throws \Exception
*/
private function loginViaLdap(Request $request): User
private function loginViaLdap(LdapAd $ldap, Request $request): User
{
try {
return $this->ldap->ldapLogin($request->input('username'), $request->input('password'));
return $ldap->ldapLogin($request->input('username'), $request->input('password'));
} catch (\Exception $ex) {
LOG::debug("LDAP user login: " . $ex->getMessage());
throw new \Exception($ex->getMessage());
@ -217,7 +217,7 @@ class LoginController extends Controller
$user = null;
// Should we even check for LDAP users?
if ($this->ldap->init()) {
if (Setting::getSettings()->ldap_enabled) { // avoid hitting the $this->ldap
LOG::debug("LDAP is enabled.");
try {
LOG::debug("Attempting to log user in by LDAP authentication.");

View file

@ -210,5 +210,29 @@ class LocationsController extends Controller
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
public function print_assigned($id)
{
}
$location = Location::where('id',$id)->first();
$parent = Location::where('id',$location->parent_id)->first();
$manager = User::where('id',$location->manager_id)->first();
$users = User::where('location_id', $id)->with('company', 'department', 'location')->get();
$assets = Asset::where('assigned_to', $id)->where('assigned_type', Location::class)->with('model', 'model.category')->get();
return view('locations/print')->with('assets', $assets)->with('users',$users)->with('location', $location)->with('parent', $parent)->with('manager', $manager);
}
public function print_all_assigned($id)
{
$location = Location::where('id',$id)->first();
$parent = Location::where('id',$location->parent_id)->first();
$manager = User::where('id',$location->manager_id)->first();
$users = User::where('location_id', $id)->with('company', 'department', 'location')->get();
$assets = Asset::where('location_id', $id)->with('model', 'model.category')->get();
return view('locations/print')->with('assets', $assets)->with('users',$users)->with('location', $location)->with('parent', $parent)->with('manager', $manager);
}
}

View file

@ -48,10 +48,9 @@ class ProfileController extends Controller
$user->last_name = $request->input('last_name');
$user->website = $request->input('website');
$user->gravatar = $request->input('gravatar');
$user->skin = $request->input('skin');
$user->phone = $request->input('phone');
if (!config('app.lock_passwords')) {
$user->locale = $request->input('locale', 'en');
}

View file

@ -403,9 +403,7 @@ class ReportsController extends Controller
*/
public function postCustom(Request $request)
{
ini_set('max_execution_time', env('REPORT_TIME_LIM', 12000)); //12000 seconds = 200 minutes
ini_set('max_execution_time', env('REPORT_TIME_LIMIT', 12000)); //12000 seconds = 200 minutes
$this->authorize('reports.view');

View file

@ -400,6 +400,7 @@ class SettingsController extends Controller
$setting->version_footer = $request->input('version_footer');
$setting->footer_text = $request->input('footer_text');
$setting->skin = $request->input('skin');
$setting->allow_user_skin = $request->input('allow_user_skin');
$setting->show_url_in_emails = $request->input('show_url_in_emails', '0');
$setting->logo_print_assets = $request->input('logo_print_assets', '0');
@ -942,6 +943,10 @@ class SettingsController extends Controller
$setting->ldap_tls = $request->input('ldap_tls', '0');
$setting->ldap_pw_sync = $request->input('ldap_pw_sync', '0');
$setting->custom_forgot_pass_url = $request->input('custom_forgot_pass_url');
$setting->ldap_phone_field = $request->input('ldap_phone');
$setting->ldap_jobtitle = $request->input('ldap_jobtitle');
$setting->ldap_country = $request->input('ldap_country');
$setting->ldap_dept = $request->input('ldap_dept');
}
@ -993,6 +998,11 @@ class SettingsController extends Controller
$setting->saml_sp_x509cert = $request->input('saml_sp_x509cert');
$setting->saml_sp_privatekey = $request->input('saml_sp_privatekey');
}
if (!empty($request->input('saml_sp_x509certNew'))) {
$setting->saml_sp_x509certNew = $request->input('saml_sp_x509certNew');
} else {
$setting->saml_sp_x509certNew = "";
}
$setting->saml_custom_settings = $request->input('saml_custom_settings');
if ($setting->save()) {
@ -1221,4 +1231,4 @@ class SettingsController extends Controller
{
return view('settings.logins');
}
}
}

View file

@ -32,8 +32,9 @@ class ItemImportRequest extends FormRequest
public function import(Import $import)
{
ini_set('max_execution_time', env('IMPORT_TIME_LIM', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEM_LIM', '500M'));
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
$filename = config('app.private_uploads') . '/imports/' . $import->file_path;
$import->import_type = $this->input('import-type');
$class = title_case($import->import_type);

View file

@ -5,11 +5,13 @@ namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use OneLogin\Saml2\IdPMetadataParser as OneLogin_Saml2_IdPMetadataParser;
use OneLogin\Saml2\Utils as OneLogin_Saml2_Utils;
use App\Models\Setting;
/**
* This handles validating and cleaning SAML settings provided by the user.
*
* @author Johnson Yi <jyi.dev@outlook.com>
* @author Michael Pietsch <skywalker-11@mi-pietsch.de>
*
* @since 5.0.0
*/
@ -55,7 +57,49 @@ class SettingsSamlRequest extends FormRequest
}
}
if ($this->input('saml_sp_regenerate_keypair') == '1' || !$this->has('saml_sp_x509cert')) {
$was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
$custom_x509cert='';
$custom_privateKey='';
$custom_x509certNew='';
if (!empty($this->input('saml_custom_settings'))) {
$req_custom_settings = preg_split('/\r\n|\r|\n/', $this->input('saml_custom_settings'));
$custom_settings = [];
foreach ($req_custom_settings as $custom_setting) {
$split = explode('=', $custom_setting, 2);
if (count($split) == 2) {
$split[0] = trim($split[0]);
$split[1] = trim($split[1]);
if (!empty($split[0])) {
$custom_settings[] = implode('=', $split);
}
if ($split[0] == 'sp_x509cert') {
$custom_x509cert = $split[1];
} elseif ($split[0] == 'sp_privateKey') {
$custom_privateKey = $split[1];
} elseif ($split[0] == 'sp_x509certNew') {
//to prepare for Key rollover
$custom_x509certNew = $split[1];
}
}
}
$this->merge(['saml_custom_settings' => implode(PHP_EOL, $custom_settings) . PHP_EOL]);
}
$cert_updated=false;
if (!empty($custom_x509cert) && !empty($custom_privateKey)) {
// custom certificate and private key are defined
$cert_updated=true;
$x509 = openssl_x509_read($custom_x509cert);
$pkey = openssl_pkey_get_private($custom_privateKey);
} elseif ($this->input('saml_sp_regenerate_keypair') == '1' || !$this->has('saml_sp_x509cert') || $was_custom_x509cert) {
// key regeneration requested, no certificate defined yet or previous custom certicate was removed
error_log("regen");
$cert_updated=true;
$dn = [
"countryName" => "US",
"stateOrProvinceName" => "N/A",
@ -94,24 +138,23 @@ class SettingsSamlRequest extends FormRequest
}
}
if (!empty($this->input('saml_custom_settings'))) {
$req_custom_settings = preg_split('/\r\n|\r|\n/', $this->input('saml_custom_settings'));
$custom_settings = [];
foreach ($req_custom_settings as $custom_setting) {
$split = explode('=', $custom_setting, 2);
if (count($split) == 2) {
$split[0] = trim($split[0]);
$split[1] = trim($split[1]);
if (!empty($split[0])) {
$custom_settings[] = implode('=', $split);
}
}
if ($custom_x509certNew) {
$x509New = openssl_x509_read($custom_x509certNew);
openssl_x509_export($x509New, $x509certNew);
while (($error = openssl_error_string() !== false)) {
$errors[] = $error;
}
$this->merge(['saml_custom_settings' => implode(PHP_EOL, $custom_settings) . PHP_EOL]);
if (!empty($x509certNew)) {
$this->merge([
'saml_sp_x509certNew' => $x509certNew
]);
}
} else {
$this->merge([
'saml_sp_x509certNew' => ""
]);
}
});
}

View file

@ -20,12 +20,11 @@ class LicenseSeatsTransformer
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformLicenseSeat (LicenseSeat $seat, $seat_count)
public function transformLicenseSeat (LicenseSeat $seat, $seat_count=0)
{
$array = [
'id' => (int) $seat->id,
'license_id' => (int) $seat->license->id,
'name' => 'Seat '.$seat_count,
'assigned_user' => ($seat->user) ? [
'id' => (int) $seat->user->id,
'name'=> e($seat->user->present()->fullName),
@ -49,6 +48,10 @@ class LicenseSeatsTransformer
'user_can_checkout' => (($seat->assigned_to=='') && ($seat->asset_id=='')),
];
if($seat_count != 0) {
$array['name'] = 'Seat '.$seat_count;
}
$permissions_array['available_actions'] = [
'checkout' => Gate::allows('checkout', License::class),
'checkin' => Gate::allows('checkin', License::class),

View file

@ -120,8 +120,10 @@ class AssetImporter extends ItemImporter
$this->log('Asset ' . $this->item["name"] . ' with serial number ' . $this->item['serial'] . ' was created');
// If we have a target to checkout to, lets do so.
//-- user_id is a property of the abstract class Importer, which this class inherits from and it's setted by
//-- the class that needs to use it (command importer or GUI importer inside the project).
if(isset($target)) {
$asset->fresh()->checkOut($target);
$asset->fresh()->checkOut($target, $this->user_id);
}
return;
}

View file

@ -313,8 +313,14 @@ class Asset extends Depreciable
}
if ($this->save()) {
event(new CheckoutableCheckedOut($this, $target, Auth::user(), $note));
if (is_integer($admin)){
$checkedOutBy = User::findOrFail($admin);
} elseif (get_class($admin) === 'App\Models\User') {
$checkedOutBy = $admin;
} else {
$checkedOutBy = Auth::user();
}
event(new CheckoutableCheckedOut($this, $target, $checkedOutBy, $note));
$this->increment('checkout_counter', 1);
return true;

View file

@ -353,16 +353,16 @@ class CustomField extends Model
* @since [v4.1.10]
* @return array
*/
public function validationRules()
public function validationRules($regex_format = null)
{
return [
"name" => "required|unique:custom_fields",
"element" => [
"required",
Rule::in(['text', 'listbox', 'textara', 'checkbox', 'radio'])
Rule::in(['text', 'listbox', 'textarea', 'checkbox', 'radio'])
],
'format' => [
Rule::in(array_merge(array_keys(CustomField::PREDEFINED_FORMATS), CustomField::PREDEFINED_FORMATS))
Rule::in(array_merge(array_keys(CustomField::PREDEFINED_FORMATS), CustomField::PREDEFINED_FORMATS, [$regex_format]))
],
'field_encrypted' => "nullable|boolean"
];

View file

@ -161,7 +161,7 @@ class Ldap extends Model
* @param $ldapatttibutes
* @return array|bool
*/
static function parseAndMapLdapAttributes($ldapatttibutes)
static function parseAndMapLdapAttributes($ldapattributes)
{
//Get LDAP attribute config
$ldap_result_username = Setting::getSettings()->ldap_username_field;
@ -169,15 +169,21 @@ class Ldap extends Model
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
$ldap_result_email = Setting::getSettings()->ldap_email;
$ldap_result_phone = Setting::getSettings()->ldap_phone;
$ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle;
$ldap_result_country = Setting::getSettings()->ldap_country;
$ldap_result_dept = Setting::getSettings()->ldap_dept;
// Get LDAP user data
$item = array();
$item["username"] = isset($ldapatttibutes[$ldap_result_username][0]) ? $ldapatttibutes[$ldap_result_username][0] : "";
$item["employee_number"] = isset($ldapatttibutes[$ldap_result_emp_num][0]) ? $ldapatttibutes[$ldap_result_emp_num][0] : "";
$item["lastname"] = isset($ldapatttibutes[$ldap_result_last_name][0]) ? $ldapatttibutes[$ldap_result_last_name][0] : "";
$item["firstname"] = isset($ldapatttibutes[$ldap_result_first_name][0]) ? $ldapatttibutes[$ldap_result_first_name][0] : "";
$item["email"] = isset($ldapatttibutes[$ldap_result_email][0]) ? $ldapatttibutes[$ldap_result_email][0] : "" ;
$item["username"] = isset($ldapattributes[$ldap_result_username][0]) ? $ldapattributes[$ldap_result_username][0] : "";
$item["employee_number"] = isset($ldapattributes[$ldap_result_emp_num][0]) ? $ldapattributes[$ldap_result_emp_num][0] : "";
$item["lastname"] = isset($ldapattributes[$ldap_result_last_name][0]) ? $ldapattributes[$ldap_result_last_name][0] : "";
$item["firstname"] = isset($ldapattributes[$ldap_result_first_name][0]) ? $ldapattributes[$ldap_result_first_name][0] : "";
$item["email"] = isset($ldapattributes[$ldap_result_email][0]) ? $ldapattributes[$ldap_result_email][0] : "" ;
$item["telephone"] = isset($ldapattributes[$ldap_result_phone][0]) ?$ldapattributes[$ldap_result_phone][0] : "";
$item["jobtitle"] = isset($ldapattributes[$ldap_result_jobtitle][0]) ? $ldapattributes[$ldap_result_jobtitle][0] : "";
$item["country"] = isset($ldapattributes[$ldap_result_country][0]) ? $ldapattributes[$ldap_result_country][0] : "";
$item["department"] = isset($ldapattributes[$ldap_result_dept][0]) ? $ldapattributes[$ldap_result_dept][0] : "";
return $item;
@ -263,7 +269,11 @@ class Ldap extends Model
if ($filter != '' && substr($filter, 0, 1) != '(') { // wrap parens around NON-EMPTY filters that DON'T have them, for back-compatibility with AdLdap2-based filters
$filter = "($filter)";
} elseif ($filter == '') {
$filter = "(cn=*)";
}
$search_results = ldap_search($ldapconn, $base_dn, $filter);
if (!$search_results) {

View file

@ -20,6 +20,16 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
protected $guarded = 'id';
protected $table = 'license_seats';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'assigned_to',
'asset_id'
];
use Acceptable;
public function getCompanyableParents()

View file

@ -114,20 +114,12 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
/**
* Check user permissions
* Internally check the user permission for the given section
*
* Parses the user and group permission masks to see if the user
* is authorized to do the thing
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v1.0]
* @return boolean
*/
public function hasAccess($section)
protected function checkPermissionSection($section)
{
if ($this->isSuperUser()) {
return true;
}
$user_groups = $this->groups;
@ -158,6 +150,24 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return false;
}
/**
* Check user permissions
*
* Parses the user and group permission masks to see if the user
* is authorized to do the thing
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v1.0]
* @return boolean
*/
public function hasAccess($section)
{
if ($this->isSuperUser()) {
return true;
}
return $this->checkPermissionSection($section);
}
/**
* Checks if the user is a SuperUser
*
@ -167,23 +177,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
*/
public function isSuperUser()
{
if (!$user_permissions = json_decode($this->permissions, true)) {
return false;
}
foreach ($this->groups as $user_group) {
$group_permissions = json_decode($user_group->permissions, true);
$group_array = (array)$group_permissions;
if ((array_key_exists('superuser', $group_array)) && ($group_permissions['superuser']=='1')) {
return true;
}
}
if ((array_key_exists('superuser', $user_permissions)) && ($user_permissions['superuser']=='1')) {
return true;
}
return false;
return $this->checkPermissionSection('superuser');
}

View file

@ -35,4 +35,14 @@ class LicensePolicy extends CheckoutablePermissionsPolicy
return false;
}
/**
* Determine whether the user can access files associated with licenses.
*
* @param \App\Models\User $user
* @return mixed
*/
public function files(User $user)
{
return $user->hasAccess($this->columnName().'.files');
}
}

View file

@ -115,6 +115,23 @@ class AssetModelPresenter extends Presenter
"title" => trans('general.notes'),
"visible" => false,
],
[
"field" => "created_at",
"searchable" => true,
"sortable" => true,
"visible" => false,
"title" => trans('general.created_at'),
"formatter" => "dateDisplayFormatter"
],
[
"field" => "updated_at",
"searchable" => true,
"sortable" => true,
"visible" => false,
"title" => trans('general.updated_at'),
"formatter" => "dateDisplayFormatter"
],
];

View file

@ -79,7 +79,23 @@ class CategoryPresenter extends Presenter
"title" => trans('table.actions'),
"visible" => true,
"formatter" => "categoriesActionsFormatter",
]
],
[
"field" => "created_at",
"searchable" => true,
"sortable" => true,
"visible" => false,
"title" => trans('general.created_at'),
"formatter" => "dateDisplayFormatter"
],
[
"field" => "updated_at",
"searchable" => true,
"sortable" => true,
"visible" => false,
"title" => trans('general.updated_at'),
"formatter" => "dateDisplayFormatter"
],
];
return json_encode($layout);

View file

@ -16,6 +16,7 @@ use App\Observers\LicenseObserver;
use App\Observers\SettingObserver;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Illuminate\Routing\UrlGenerator;
/**
* This service provider handles setting the observers on models
@ -33,8 +34,15 @@ class AppServiceProvider extends ServiceProvider
* @since [v3.0]
* @return void
*/
public function boot()
public function boot(UrlGenerator $url)
{
if (env('APP_FORCE_TLS')) {
if (strpos(env('APP_URL'), 'https') === 0) {
$url->forceScheme('https');
} else {
\Log::warning("'APP_FORCE_TLS' is set to true, but 'APP_URL' does not start with 'https://'. Will not force TLS on connections.");
}
}
Schema::defaultStringLength(191);
Asset::observe(AssetObserver::class);
Accessory::observe(AccessoryObserver::class);

View file

@ -2,8 +2,9 @@
use App\Services\LdapAd;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
class LdapServiceProvider extends ServiceProvider
class LdapServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
@ -26,4 +27,14 @@ class LdapServiceProvider extends ServiceProvider
{
$this->app->singleton(LdapAd::class, LdapAd::class);
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [LdapAd::class];
}
}

View file

@ -129,7 +129,13 @@ class LdapAd extends LdapAdConfiguration
$login_username = $username;
}
if ($this->ldap->auth()->attempt($login_username, $password, true) === false) {
if ($this->ldapConfig['username'] && $this->ldapConfig['password']) {
$bind_as_user = false;
} else {
$bind_as_user = true;
}
if ($this->ldap->auth()->attempt($login_username, $password, $bind_as_user) === false) {
throw new Exception('Unable to validate user credentials!');
}

View file

@ -141,6 +141,7 @@ class Saml
* Builds settings from Snipe-IT for OneLogin_Saml2_Auth.
*
* @author Johnson Yi <jyi.dev@outlook.com>
* @author Michael Pietsch <skywalker-11@mi-pietsch.de>
*
* @since 5.0.0
*
@ -162,6 +163,11 @@ class Saml
data_set($settings, 'sp.singleLogoutService.url', route('saml.sls'));
data_set($settings, 'sp.x509cert', $setting->saml_sp_x509cert);
data_set($settings, 'sp.privateKey', $setting->saml_sp_privatekey);
if(!empty($setting->saml_sp_x509certNew)) {
data_set($settings, 'sp.x509certNew', $setting->saml_sp_x509certNew);
} else {
data_set($settings, 'sp.x509certNew', "");
}
if (!empty(data_get($settings, 'sp.privateKey'))) {
data_set($settings, 'security.logoutRequestSigned', true);
@ -214,7 +220,6 @@ class Saml
}
}
}
$this->_settings = $settings;
}
}
@ -502,4 +507,4 @@ class Saml
return $username;
}
}
}

893
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -218,6 +218,12 @@ return array(
'note' => '',
'display' => true,
),
array(
'permission' => 'licenses.files',
'label' => 'View and Modify License Files',
'note' => '',
'display' => true,
),
),

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddUserSkinSetting extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// Update the users table
Schema::table('users', function ($table) {
$table->string('skin')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// Update the users table
Schema::table('users', function ($table) {
$table->dropColumn('skin');
});
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddSettingAllowUserSkin extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// Update the users table
Schema::table('settings', function ($table) {
$table->boolean('allow_user_skin')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// Update the users table
Schema::table('settings', function ($table) {
$table->dropColumn('allow_user_skin');
});
}
}

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddSamlKeyRollover extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function(Blueprint $table) {
$table->text('saml_sp_x509certNew')->nullable()->default(NULL);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function(Blueprint $table) {
$table->dropColumn('saml_sp_x509certNew');
});
}
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddsSeveralLdapFields extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->string('ldap_phone_field')->after('ldap_email')->nullable();
$table->string('ldap_jobtitle')->after('ldap_phone_field')->nullable();
$table->string('ldap_country')->after('ldap_jobtitle')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('ldap_phone_field');
$table->dropColumn('ldap_jobtitle');
$table->dropColumn('ldap_country');
});
}
}

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddLdapDept extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->string('ldap_dept')->after('ldap_active_flag')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('ldap_dept');
});
}
}

View file

@ -0,0 +1,54 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIdsToTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('migrations', function (Blueprint $table) {
// Add the id column to the migrations table if it doesn't yet have one
if (!Schema::hasColumn('migrations', 'id')) {
$table->increments('id');
}
});
Schema::table('password_resets', function (Blueprint $table) {
// Add the id column to the password_resets table if it doesn't yet have one
if (!Schema::hasColumn('password_resets', 'id')) {
$table->increments('id');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('migrations', function (Blueprint $table) {
if (Schema::hasColumn('migrations', 'id')) {
$table->dropColumn('id');
}
});
Schema::table('password_resets', function (Blueprint $table) {
if (Schema::hasColumn('password_resets', 'id')) {
$table->dropColumn('id');
}
});
}
}

11468
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,12 +2,12 @@
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run development -- --watch",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --disable-host-check --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"engines": {
"node": ">=0.12"
@ -15,12 +15,12 @@
"devDependencies": {
"axios": "^0.20.0",
"babel-preset-latest": "^6.24.1",
"cross-env": "^5.0.5",
"cross-env": "^7.0",
"jquery": "^3.5.1",
"laravel-mix": "2.1",
"laravel-mix": "^5.0.1",
"lodash": "^4.17.20",
"vue": "2.4.4",
"vue-loader": "^13.6.1",
"vue-loader": "^15.9.6",
"vue-template-compiler": "2.4.4"
},
"dependencies": {
@ -34,6 +34,7 @@
"bootstrap-less": "^3.3.8",
"bootstrap-table": "^1.18.0",
"chart.js": "^2.9.4",
"css-loader": "^3.6.0",
"ekko-lightbox": "^5.1.1",
"font-awesome": "^4.7.0",
"icheck": "^1.0.2",
@ -43,12 +44,13 @@
"jquery-ui": "^1.12.1",
"jquery-ui-bundle": "^1.12.1",
"jquery.iframe-transport": "^1.0.0",
"less": "github:less/less.js#efa6eb5306f28a7ef7e235d79ce854b780345591",
"less": "^3.13.1",
"less-loader": "^4.1.0",
"list.js": "^1.5.0",
"papaparse": "^4.3.3",
"phantomjs": "^2.1.7",
"select2": "4.0.13",
"sheetjs": "^2.0.0",
"tableexport.jquery.plugin": "^1.10.20",
"tether": "^1.4.0",
"vue-resource": "^1.3.3"

Binary file not shown.

BIN
public/css/dist/signature-pad.min.css vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -30,9 +30,9 @@
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=b3d2ebfcb2541fa8e9ec",
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=d7996d850e8bcdc4e167",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced",
"/css/build/signature-pad.min.css": "/css/build/signature-pad.min.css?id=d41d8cd98f00b204e980",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=1e77fde04b3f42432581",
"/js/build/vendor.js": "/js/build/vendor.js?id=b93877b4a88a76e1b18b",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=58d95c93430f2ae33392",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=fd6e727609678bf04984",
"/js/dist/all.js": "/js/dist/all.js?id=0efed60df4636f215a73"
}

View file

@ -11,6 +11,8 @@ return array(
'country' => 'Country',
'create' => 'Create Location',
'update' => 'Update Location',
'print_assigned' => 'Print Assigned',
'print_all_assigned' => 'Print All Assigned',
'name' => 'Location Name',
'address' => 'Address',
'zip' => 'Postal Code',

View file

@ -14,6 +14,8 @@ return array(
'alerts_enabled' => 'Email Alerts Enabled',
'alert_interval' => 'Expiring Alerts Threshold (in days)',
'alert_inv_threshold' => 'Inventory Alert Threshold',
'allow_user_skin' => 'Allow user skin',
'allow_user_skin_help_text' => 'Checking this box will allow a user to change the site skin for himself.' ,
'asset_ids' => 'Asset IDs',
'audit_interval' => 'Audit Interval',
'audit_interval_help' => 'If you are required to regularly physically audit your assets, enter the interval in months.',
@ -72,6 +74,10 @@ return array(
'ldap_tls' => 'Use TLS',
'ldap_tls_help' => 'This should be checked only if you are running STARTTLS on your LDAP server. ',
'ldap_uname' => 'LDAP Bind Username',
'ldap_dept' => 'LDAP Department',
'ldap_phone' => 'LDAP Telephone Number',
'ldap_jobtitle' => 'LDAP Job Title',
'ldap_country' => 'LDAP Country',
'ldap_pword' => 'LDAP Bind Password',
'ldap_basedn' => 'Base Bind DN',
'ldap_filter' => 'LDAP Filter',

View file

@ -582,3 +582,34 @@ Form::macro('skin', function ($name = "skin", $selected = null, $class = null) {
return $select;
});
Form::macro('user_skin', function ($name = "skin", $selected = null, $class = null) {
$formats = array(
'' => 'Site Default',
'blue' => 'Default Blue',
'blue-dark' => 'Blue (Dark Mode)',
'green' => 'Green Dark',
'green-dark' => 'Green (Dark Mode)',
'red' => 'Red Dark',
'red-dark' => 'Red (Dark Mode)',
'orange' => 'Orange Dark',
'orange-dark' => 'Orange (Dark Mode)',
'black' => 'Black',
'black-dark' => 'Black (Dark Mode)',
'purple' => 'Purple',
'purple-dark' => 'Purple (Dark Mode)',
'yellow' => 'Yellow',
'yellow-dark' => 'Yellow (Dark Mode)',
'contrast' => 'High Contrast',
);
$select = '<select name="'.$name.'" class="'.$class.'" style="width: 250px">';
foreach ($formats as $format => $label) {
$select .= '<option value="'.$format.'"'.($selected == $format ? ' selected="selected"' : '').'>'.$label.'</option> '."\n";
}
$select .= '</select>';
return $select;
});

View file

@ -11,7 +11,7 @@
@section('content')
<link rel="stylesheet" href="{{ url(mix('css/build/signature-pad.min.css')) }}">
<link rel="stylesheet" href="{{ url(mix('css/dist/signature-pad.min.css')) }}">
<style>
.form-horizontal .control-label, .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline {

View file

@ -58,6 +58,17 @@
</div>
</div>
@if ($snipeSettings->allow_user_skin=='1')
<!-- Skin -->
<div class="form-group {{ $errors->has('skin') ? 'error' : '' }}">
<label for="website" class="col-md-3 control-label">{{ Form::label('skin', trans('general.skin')) }}</label>
<div class="col-md-8">
{!! Form::user_skin('skin', old('skin', $user->skin), 'select2') !!}
{!! $errors->first('skin', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@endif
<!-- Phone -->
<div class="form-group {{ $errors->has('phone') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="phone">{{ trans('admin/users/table.phone') }}</label>
@ -67,8 +78,6 @@
</div>
</div>
<!-- Website URL -->
<div class="form-group {{ $errors->has('website') ? ' has-error' : '' }}">
<label for="website" class="col-md-3 control-label">{{ trans('general.website') }}</label>
@ -114,8 +123,6 @@
<!-- Two factor opt in -->
@if ($snipeSettings->two_factor_enabled=='1')
<div class="form-group {{ $errors->has('avatar') ? 'has-error' : '' }}">

View file

@ -11,7 +11,7 @@
$settings->labels_width = $settings->labels_width - $settings->labels_display_sgutter;
$settings->labels_height = $settings->labels_height - $settings->labels_display_bgutter;
// Leave space on bottom for 1D barcode if necessary
$qr_size = ($settings->alt_barcode_enabled=='1') && ($settings->alt_barcode!='') ? $settings->labels_height - .3 : $settings->labels_height - .3;
$qr_size = ($settings->alt_barcode_enabled=='1') && ($settings->alt_barcode!='') ? $settings->labels_height - .3 : $settings->labels_height - 0.1;
?>
<style>
@ -61,8 +61,8 @@ $qr_size = ($settings->alt_barcode_enabled=='1') && ($settings->alt_barcode!='')
height: {{ $settings->labels_height }}in;
padding-top: {{$settings->labels_display_bgutter}}in;
font-family: arial, helvetica, sans-serif;
font-size: {{$settings->labels_fontsize}};
padding-right: .01in;
font-size: {{$settings->labels_fontsize}}pt;
padding-right: .0001in;
overflow: hidden !important;
display: inline;
word-wrap: break-word;
@ -164,4 +164,4 @@ $qr_size = ($settings->alt_barcode_enabled=='1') && ($settings->alt_barcode!='')
</body>
</html>
</html>

View file

@ -16,6 +16,34 @@
}
</style>
<div class="col-md-6">
<div class="box box-default" id="audited-div" style="display: none">
<div class="box-header with-border">
<h2 class="box-title"> {{ trans('general.bulkaudit_status') }} (<span id="audit-counter">0</span> assets audited) </h2>
</div>
<div class="box-body">
<table id="audited" class="table table-striped snipe-table">
<thead>
<tr>
<th>{{ trans('general.asset_tag') }}</th>
<th>{{ trans('general.bulkaudit_status') }}</th>
<th></th>
</tr>
<tr id="audit-loader" style="display: none;">
<td colspan="3">
<i class="fa fa-spinner spin" aria-hidden="true"></i> Processing...
</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
<div>
<div class="row">
{{ Form::open(['method' => 'POST', 'class' => 'form-horizontal', 'role' => 'form', 'id' => 'audit-form' ]) }}

View file

@ -855,9 +855,8 @@
<a href="{{ route('components.show', $component->id) }}">{{ $component->name }}</a>
</td>
<td>{{ $component->pivot->assigned_qty }}</td>
<td>{{ $component->purchase_cost }}</td>
<?php $totalCost = $totalCost + $component->purchase_cost ;?>
<td>{{ $component->purchase_cost }} each</td>
<?php $totalCost = $totalCost + ($component->purchase_cost *$component->pivot->assigned_qty) ?>
</tr>
@endif
@endforeach

View file

@ -28,13 +28,13 @@
</script>
{{-- stylesheets --}}
<link rel="stylesheet" href="{{ url(mix('css/dist/all.css')) }}">
@if (($snipeSettings) && ($snipeSettings->allow_user_skin==1) && Auth::check() && Auth::user()->present()->skin != '')
<link rel="stylesheet" href="{{ mix('css/dist/skins/skin-'.Auth::user()->present()->skin.'.min.css') }}">
@else
<link rel="stylesheet" href="{{ url(mix('css/dist/skins/skin-'.($snipeSettings->skin!='' ? $snipeSettings->skin : 'blue').'.css')) }}">
{{-- page level css --}}
@endif
{{-- page level css --}}
@stack('css')

View file

@ -31,7 +31,9 @@
<ul class="nav nav-tabs">
<li class="active"><a href="#details" data-toggle="tab">Details</a></li>
<li><a href="#seats" data-toggle="tab">{{ trans('admin/licenses/form.seats') }}</a></li>
@can('files', $license)
<li><a href="#uploads" data-toggle="tab">{{ trans('general.file_uploads') }}</a></li>
@endcan
<li><a href="#history" data-toggle="tab">{{ trans('admin/licenses/general.checkout_history') }}</a></li>
<li class="pull-right"><a href="#" data-toggle="modal" data-target="#uploadFileModal"><i class="fa fa-paperclip" aria-hidden="true"></i> {{ trans('button.upload') }}</a></li>
</ul>
@ -350,7 +352,7 @@
data-sort-order="asc"
data-sort-name="name"
class="table table-striped snipe-table"
data-url="{{ route('api.license.seats', $license->id) }}"
data-url="{{ route('api.licenses.seats.index', $license->id) }}"
data-export-options='{
"fileName": "export-seats-{{ str_slug($license->name) }}-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
@ -364,6 +366,7 @@
</div> <!--/.row-->
</div> <!-- /.tab-pane -->
@can('files', $license)
<div class="tab-pane" id="uploads">
<div class="table-responsive">
<table
@ -447,6 +450,7 @@
</table>
</div>
</div> <!-- /.tab-pane -->
@endcan
<div class="tab-pane" id="history">
<div class="row">

View file

@ -0,0 +1,188 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Assigned to {{ $location->present()->fullName() }} Location</title>
<style>
body {
font-family: "Arial, Helvetica", sans-serif;
}
table.inventory {
border: solid #000;
border-width: 1px 1px 1px 1px;
width: 100%;
}
@page {
size: A4;
}
table.inventory th, table.inventory td {
border: solid #000;
border-width: 0 1px 1px 0;
padding: 3px;
font-size: 12px;
}
.print-logo {
max-height: 40px;
}
</style>
</head>
<body>
@if ($snipeSettings->logo_print_assets=='1')
@if ($snipeSettings->brand == '3')
<h3>
@if ($snipeSettings->logo!='')
<img class="print-logo" src="{{ url('/') }}/uploads/{{ $snipeSettings->logo }}">
@endif
{{ $snipeSettings->site_name }}
</h3>
@elseif ($snipeSettings->brand == '2')
@if ($snipeSettings->logo!='')
<img class="print-logo" src="{{ url('/') }}/uploads/{{ $snipeSettings->logo }}">
@endif
@else
<h3>{{ $snipeSettings->site_name }}</h3>
@endif
@endif
<h2>Asset Management System</h2>
<b>Assigned To:</b> {{ $location->present()->fullName() }}
@if ($parent)
{{ $parent->present()->fullName() }}
@endif
<br>
@if ($manager)
<b>Manager:</b> {{ $manager->present()->fullName() }}<br>
@endif
<b>Current Date:</b> {{ date("d/m/Y h:i:s A") }}<br><br>
@if ($users->count() > 0)
@php
$counter = 1;
@endphp
<table class="inventory">
<thead>
<tr>
<th colspan="6">{{ trans('general.users') }}</th>
</tr>
</thead>
<thead>
<tr>
<th style="width: 5px;"></th>
<th style="width: 25%;">Company</th>
<th style="width: 25%;">User Name</th>
<th style="width: 10%;">Employee No.</th>
<th style="width: 20%;">Department</th>
<th style="width: 20%;">Location</th>
</tr>
</thead>
@foreach ($users as $user)
<tr>
<td>{{ $counter }}</td>
<td>{{ $user->company->name }}</td>
<td>{{ $user->first_name }} {{ $user->last_name }}</td>
<td>{{ $user->employee_num }}</td>
<td>{{ $user->department->name }}</td>
<td>{{ $user->location->name }}</td>
</tr>
@php
$counter++
@endphp
@endforeach
</table>
@endif
@if ($assets->count() > 0)
<br><br>
<table class="inventory">
<thead>
<tr>
<th colspan="10">{{ trans('general.assets') }}</th>
</tr>
</thead>
<thead>
<tr>
<th style="width: 20px;"></th>
<th style="width: 10%;">Asset Tag</th>
<th style="width: 10%;">Name</th>
<th style="width: 10%;">Category</th>
<th style="width: 10%;">Manufacturer</th>
<th style="width: 15%;">Model</th>
<th style="width: 15%;">Serial</th>
<th style="width: 10%;">Location</th>
<th style="width: 10%;">Checked Out</th>
<th style="width: 10%;">Expected Checkin</th>
</tr>
</thead>
@php
$counter = 1;
@endphp
@foreach ($assets as $asset)
<tr>
<td>{{ $counter }}</td>
<td>{{ $asset->asset_tag }}</td>
<td>{{ $asset->name }}</td>
<td>{{ $asset->model->category->name }}</td>
<td>{{ $asset->model->manufacturer->name }}</td>
<td>{{ $asset->model->name }} {{ $asset->model->model_number }}</td>
<td>{{ $asset->serial }}</td>
<td>{{ $asset->location->name }}</td>
<td>{{ $asset->last_checkout }}</td>
<td>{{ $asset->expected_checkin }}</td>
</tr>
@php
$counter++
@endphp
@endforeach
</table>
@endif
<br>
<br>
<br>
<table>
<tr>
<td>Signed By (Asset Auditor):</td>
<td>___________________________</td>
<td></td>
<td>Date:</td>
<td>____________________</td>
</tr>
</table>
<br>
<br>
<br>
<table>
<tr>
<td>Signed By (Finance Asset Auditor):</td>
<td>____________________</td>
<td></td>
<td>Date:</td>
<td>____________________</td>
</tr>
</table>
<br>
<br>
<br>
<table>
<tr>
<td>Signed By (Location Manager):</td>
<td>_______________________</td>
<td></td>
<td>Date:</td>
<td>____________________</td>
</tr>
</table>
</body>
</html>

View file

@ -9,10 +9,6 @@
@parent
@stop
@section('header_right')
<a href="{{ route('locations.edit', ['location' => $location->id]) }}" class="btn btn-sm btn-primary pull-right">{{ trans('admin/locations/table.update') }} </a>
@stop
{{-- Page content --}}
@section('content')
@ -86,6 +82,71 @@
</div><!-- /.box-body -->
</div> <!--/.box-->
<div class="box box-default">
<div class="box-header with-border">
<div class="box-heading">
<h2 class="box-title">{{ trans('general.accessories') }}</h2>
</div>
</div>
<div class="box-body">
<div class="table table-responsive">
<table
data-columns="{{ \App\Presenters\AccessoryPresenter::dataTableLayout() }}"
data-cookie-id-table="accessoriesListingTable"
data-pagination="true"
data-id-table="accessoriesListingTable"
data-search="true"
data-side-pagination="server"
data-show-columns="true"
data-show-export="true"
data-show-refresh="true"
data-sort-order="asc"
id="accessoriesListingTable"
class="table table-striped snipe-table"
data-url="{{route('api.accessories.index', ['location_id' => $location->id]) }}"
data-export-options='{
"fileName": "export-locations-{{ str_slug($location->name) }}-accessories-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
}'>
</table>
</div><!-- /.table-responsive -->
</div><!-- /.box-body -->
</div> <!--/.box-->
<div class="box box-default">
<div class="box-header with-border">
<div class="box-heading">
<h2 class="box-title">{{ trans('general.consumables') }}</h2>
</div>
</div>
<div class="box-body">
<div class="table table-responsive">
<table
data-columns="{{ \App\Presenters\ConsumablePresenter::dataTableLayout() }}"
data-cookie-id-table="consumablesListingTable"
data-pagination="true"
data-id-table="consumablesListingTable"
data-search="true"
data-side-pagination="server"
data-show-columns="true"
data-show-export="true"
data-show-refresh="true"
data-sort-order="asc"
id="consumablesListingTable"
class="table table-striped snipe-table"
data-url="{{route('api.consumables.index', ['location_id' => $location->id]) }}"
data-export-options='{
"fileName": "export-locations-{{ str_slug($location->name) }}-consumables-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
}'>
</table>
</div><!-- /.table-responsive -->
</div><!-- /.box-body -->
</div> <!--/.box-->
<div class="box box-default">
<div class="box-header with-border">
@ -158,18 +219,23 @@
</div>
@endif
</div>
<div class="col-md-12">
<a href="{{ route('locations.edit', ['location' => $location->id]) }}" style="width: 50%;" class="btn btn-sm btn-primary pull-left">{{ trans('admin/locations/table.update') }} </a>
</div>
<div class="col-md-12" style="padding-top: 5px;">
<a href="{{ route('locations.print_assigned', ['locationId' => $location->id]) }}" style="width: 50%;" class="btn btn-sm btn-default pull-left">{{ trans('admin/locations/table.print_assigned') }} </a>
</div>
<div class="col-md-12" style="padding-top: 5px;">
<a href="{{ route('locations.print_all_assigned', ['locationId' => $location->id]) }}" style="width: 50%;" class="btn btn-sm btn-default pull-left">{{ trans('admin/locations/table.print_all_assigned') }} </a>
</div>
</div>
</div>
</div>
</div>
@stop
@section('moar_scripts')

View file

@ -41,7 +41,6 @@
iconsPrefix: 'fa',
cookie: true,
cookieExpire: '2y',
cookieIdTable: '{{ Route::currentRouteName() }}',
mobileResponsive: true,
maintainSelected: true,
trimOnSearch: false,

View file

@ -150,7 +150,7 @@
</div>
</div>
<!-- Email format -->
<!-- Skin -->
<div class="form-group {{ $errors->has('skin') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('skin', trans('general.skin')) }}
@ -161,6 +161,17 @@
</div>
</div>
<!-- Allow User Skin -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('allow_user_skin', trans('admin/settings/general.allow_user_skin')) }}
</div>
<div class="col-md-9">
{{ Form::checkbox('allow_user_skin', '1', old('allow_user_skin', $setting->allow_user_skin),array('class' => 'minimal')) }}
{{ trans('general.yes') }}
<p class="help-block">{{ trans('admin/settings/general.allow_user_skin_help_text') }}</p>
</div>
</div>
<!-- Custom css -->
<div class="form-group {{ $errors->has('custom_css') ? 'error' : '' }}">

View file

@ -333,13 +333,26 @@
{{ Form::label('ldap_emp_num', trans('admin/settings/general.ldap_emp_num')) }}
</div>
<div class="col-md-9">
{{ Form::text('ldap_emp_num', Request::old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => '', $setting->demoMode]) }}
{{ Form::text('ldap_emp_num', Request::old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => 'employeenumber/employeeid', $setting->demoMode]) }}
{!! $errors->first('ldap_emp_num', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@if (config('app.lock_passwords')===true)
<p class="text-warning"><i class="fa fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
@endif
</div>
</div>
<!-- LDAP department -->
<div class="form-group {{ $errors->has('ldap_dept') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('ldap_dept', trans('admin/settings/general.ldap_dept')) }}
</div>
<div class="col-md-9">
{{ Form::text('ldap_dept', Request::old('ldap_dept', $setting->ldap_dept), ['class' => 'form-control','placeholder' => 'department', $setting->demoMode]) }}
{!! $errors->first('ldap_dept', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@if (config('app.lock_passwords')===true)
<p class="text-warning"><i class="fa fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
@endif
</div>
</div>
<!-- LDAP email -->
<div class="form-group {{ $errors->has('ldap_email') ? 'error' : '' }}">
@ -347,7 +360,7 @@
{{ Form::label('ldap_email', trans('admin/settings/general.ldap_email')) }}
</div>
<div class="col-md-9">
{{ Form::text('ldap_email', Request::old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => '', $setting->demoMode]) }}
{{ Form::text('ldap_email', Request::old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => 'mail', $setting->demoMode]) }}
{!! $errors->first('ldap_email', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@if (config('app.lock_passwords')===true)
<p class="text-warning"><i class="fa fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
@ -355,7 +368,47 @@
</div>
</div>
<!-- LDAP Phone -->
<div class="form-group {{ $errors->has('ldap_phone') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('ldap_phone', trans('admin/settings/general.ldap_phone')) }}
</div>
<div class="col-md-9">
{{ Form::text('ldap_phone', Request::old('ldap_phone', $setting->ldap_phone_field), ['class' => 'form-control','placeholder' => 'telephonenumber', $setting->demoMode]) }}
{!! $errors->first('ldap_phone', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@if (config('app.lock_passwords')===true)
<p class="text-warning"><i class="fa fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
@endif
</div>
</div>
<!-- LDAP Job title -->
<div class="form-group {{ $errors->has('ldap_jobtitle') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('ldap_jobtitle', trans('admin/settings/general.ldap_jobtitle')) }}
</div>
<div class="col-md-9">
{{ Form::text('ldap_jobtitle', Request::old('ldap_jobtitle', $setting->ldap_jobtitle), ['class' => 'form-control','placeholder' => 'title', $setting->demoMode]) }}
{!! $errors->first('ldap_jobtitle', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@if (config('app.lock_passwords')===true)
<p class="text-warning"><i class="fa fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
@endif
</div>
</div>
<!-- LDAP Country -->
<div class="form-group {{ $errors->has('ldap_country') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('ldap_country', trans('admin/settings/general.ldap_country')) }}
</div>
<div class="col-md-9">
{{ Form::text('ldap_country', Request::old('ldap_country', $setting->ldap_country), ['class' => 'form-control','placeholder' => 'c', $setting->demoMode]) }}
{!! $errors->first('ldap_country', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@if (config('app.lock_passwords')===true)
<p class="text-warning"><i class="fa fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
@endif
</div>
</div>
@if ($setting->ldap_enabled)
<!-- LDAP test -->

View file

@ -157,7 +157,9 @@
{{ Form::label('saml_custom_settings', trans('admin/settings/general.saml_custom_settings')) }}
</div>
<div class="col-md-9">
{{ Form::textarea('saml_custom_settings', old('saml_custom_settings', $setting->saml_custom_settings), ['class' => 'form-control','placeholder' => 'example.option=false', 'wrap' => 'off', $setting->demoMode]) }}
{{ Form::textarea('saml_custom_settings', old('saml_custom_settings', $setting->saml_custom_settings), ['class' => 'form-control','placeholder' => 'example.option=false
sp_x509cert=file:///...
sp_private_key=file:///', 'wrap' => 'off', $setting->demoMode]) }}
<p class="help-block">{{ trans('admin/settings/general.saml_custom_settings_help') }}</p>
{!! $errors->first('saml_custom_settings', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>

View file

@ -106,7 +106,7 @@
<div class="form-group {{ $errors->has('username') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="username">{{ trans('admin/users/table.username') }}</label>
<div class="col-md-6{{ (\App\Helpers\Helper::checkIfRequired($user, 'username')) ? ' required' : '' }}">
@if ($user->ldap_import!='1')
@if ($user->ldap_import!='1' || str_contains(Route::currentRouteName(), 'clone'))
<input
class="form-control"
type="text"
@ -137,7 +137,7 @@
{{ trans('admin/users/table.password') }}
</label>
<div class="col-md-6{{ (\App\Helpers\Helper::checkIfRequired($user, 'password')) ? ' required' : '' }}">
@if ($user->ldap_import!='1')
@if ($user->ldap_import!='1' || str_contains(Route::currentRouteName(), 'clone') )
<input
type="password"
name="password"
@ -161,7 +161,7 @@
</div>
</div>
@if ($user->ldap_import!='1')
@if ($user->ldap_import!='1' || str_contains(Route::currentRouteName(), 'clone'))
<!-- Password Confirm -->
<div class="form-group {{ $errors->has('password_confirmation') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="password_confirmation">

View file

@ -85,7 +85,7 @@
<td>{{ $asset->serial }}</td>
<td>
{{ $asset->last_checkout }}</td>
<td><img height="20%" src="{{ asset('/') }}display-sig/{{ $asset->assetlog->first()->accept_signature }}"></img></td>
<td><div><img height="20%" src="{{ asset('/') }}display-sig/{{ $asset->assetlog->first()->accept_signature }}"></img></div></td>
</tr>
@if($settings->show_assigned_assets)
@php
@ -145,7 +145,7 @@
@can('viewKeys', $license)
{{ $license->serial }}
@else
------------
<i class="fa-lock" aria-hidden="true"></i> {{ str_repeat('x', 15) }}
@endcan
</td>
<td>{{ $license->assetlog->first()->created_at }}</td>

View file

@ -166,7 +166,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api']
/*--- Departments API ---*/
/*--- Suppliers API ---*/
Route::group(['prefix' => 'departments'], function () {
@ -496,11 +495,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api']
/*--- Licenses API ---*/
Route::group(['prefix' => 'licenses'], function () {
Route::get('{licenseId}/seats', [
'as' => 'api.license.seats',
'uses' => 'LicensesController@seats'
]);
Route::get('selectlist',
[
'as' => 'api.licenses.selectlist',
@ -525,7 +519,18 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api']
]
); // Licenses resource
Route::resource('licenses.seats', 'LicenseSeatsController',
[
'names' =>
[
'index' => 'api.licenses.seats.index',
'show' => 'api.licenses.seats.show',
'update' => 'api.licenses.seats.update'
],
'except' => ['create', 'edit', 'destroy', 'store'],
'parameters' => ['licenseseat' => 'licenseseat_id']
]
); // Licenseseats resource
/*--- Locations API ---*/

View file

@ -22,6 +22,16 @@ Route::group(['middleware' => 'auth'], function () {
Route::resource('locations', 'LocationsController', [
'parameters' => ['location' => 'location_id']
]);
Route::get(
'locations/{locationId}/printassigned',
[ 'as' => 'locations.print_assigned', 'uses' => 'LocationsController@print_assigned' ]
);
Route::get(
'locations/{locationId}/printallassigned',
[ 'as' => 'locations.print_all_assigned', 'uses' => 'LocationsController@print_all_assigned' ]
);
/*
* Manufacturers

View file

@ -156,8 +156,8 @@ create_virtualhost () {
create_user () {
echo "* Creating Snipe-IT user."
if [ "$distro" == "ubuntu" ] || [ "$distro" == "debian" ] || [ "$distro" == "raspbian" ] ; then
adduser --quiet --disabled-password --gecos '""' "$APP_USER"
if [[ "$distro" == "ubuntu" ]] || [[ "$distro" == "debian" ]] || [[ "$distro" == "raspbian" ]] ; then
adduser --quiet --disabled-password --gecos 'Snipe-IT User' "$APP_USER"
else
adduser "$APP_USER"
fi
@ -211,7 +211,7 @@ install_snipeit () {
sed -i "s|^\\(DB_HOST=\\).*|\\1localhost|" "$APP_PATH/.env"
sed -i "s|^\\(DB_DATABASE=\\).*|\\1snipeit|" "$APP_PATH/.env"
sed -i "s|^\\(DB_USERNAME=\\).*|\\1snipeit|" "$APP_PATH/.env"
sed -i "s|^\\(DB_PASSWORD=\\).*|\\1$mysqluserpw|" "$APP_PATH/.env"
sed -i "s|^\\(DB_PASSWORD=\\).*|\\1'$mysqluserpw'|" "$APP_PATH/.env"
sed -i "s|^\\(APP_URL=\\).*|\\1http://$fqdn|" "$APP_PATH/.env"
echo "* Installing composer."
@ -265,7 +265,13 @@ set_hosts () {
echo >> /etc/hosts "127.0.0.1 $(hostname) $fqdn"
}
if [[ -f /etc/lsb-release || -f /etc/debian_version ]]; then
rename_default_vhost () {
log "mv /etc/apache2/sites-enabled/000-default.conf /etc/apache2/sites-enabled/111-default.conf"
log "mv /etc/apache2/sites-enabled/snipeit.conf /etc/apache2/sites-enabled/000-snipeit.conf"
}
if [[ -f /etc/debian_version || -f /etc/lsb-release ]]; then
distro="$(lsb_release -is)"
version="$(lsb_release -rs)"
codename="$(lsb_release -cs)"
@ -311,7 +317,7 @@ case $distro in
apache_group=www-data
apachefile=/etc/apache2/sites-available/$APP_NAME.conf
;;
*debian*)
*Debian|debian*)
echo " The installer has detected $distro version $version codename $codename."
distro=debian
apache_group=www-data
@ -330,7 +336,7 @@ case $distro in
apachefile=/etc/httpd/conf.d/$APP_NAME.conf
;;
*)
echo " The installer was unable to determine your OS. Exiting for safety."
echo " The installer was unable to determine your OS. Exiting for safety. Exiting for safety."
exit 1
;;
esac
@ -368,7 +374,39 @@ done
case $distro in
debian)
if [[ "$version" =~ ^9 ]]; then
if [[ "$version" =~ ^10 ]]; then
# Install for Debian 10.x
tzone=$(cat /etc/timezone)
echo "* Adding PHP repository."
log "apt-get install -y apt-transport-https lsb-release ca-certificates"
log "wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg"
echo "deb https://packages.sury.org/php/ $codename main" > /etc/apt/sources.list.d/php.list
echo -n "* Updating installed packages."
log "apt-get update && apt-get -y upgrade" & pid=$!
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php7.3 php7.3 php7.3-mcrypt php7.3-curl php7.3-mysql php7.3-gd php7.3-ldap php7.3-zip php7.3-mbstring php7.3-xml php7.3-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
create_virtualhost
log "a2enmod rewrite"
log "a2ensite $APP_NAME.conf"
rename_default_vhost
set_hosts
echo "* Securing MariaDB."
/usr/bin/mysql_secure_installation
install_snipeit
echo "* Restarting Apache httpd."
log "service apache2 restart"
elif [[ "$version" =~ ^9 ]]; then
# Install for Debian 9.x
tzone=$(cat /etc/timezone)
@ -382,13 +420,14 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php7.1 php7.1 php7.1-mcrypt php7.1-curl php7.1-mysql php7.1-gd php7.1-ldap php7.1-zip php7.1-mbstring php7.1-xml php7.1-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php7.4 php7.4 php7.4-mcrypt php7.4-curl php7.4-mysql php7.4-gd php7.4-ldap php7.4-zip php7.4-mbstring php7.4-xml php7.4-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
create_virtualhost
log "a2enmod rewrite"
log "a2ensite $APP_NAME.conf"
rename_default_vhost
set_hosts
@ -415,13 +454,14 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client php7.1 php7.1-mcrypt php7.1-curl php7.1-mysql php7.1-gd php7.1-ldap php7.1-zip php7.1-mbstring php7.1-xml php7.1-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client php7.4 php7.4-mcrypt php7.4-curl php7.4-mysql php7.4-gd php7.4-ldap php7.4-zip php7.4-mbstring php7.4-xml php7.4-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
create_virtualhost
log "a2enmod rewrite"
log "a2ensite $APP_NAME.conf"
rename_default_vhost
set_hosts
@ -456,8 +496,7 @@ case $distro in
log "phpenmod mbstring"
log "a2enmod rewrite"
log "a2ensite $APP_NAME.conf"
log "mv /etc/apache2/sites-enabled/000-default.conf /etc/apache2/sites-enabled/111-default.conf"
log "mv /etc/apache2/sites-enabled/snipeit.conf /etc/apache2/sites-enabled/000-snipeit.conf"
rename_default_vhost
set_hosts
@ -486,7 +525,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php7.1 php7.1 php7.1-mcrypt php7.1-curl php7.1-mysql php7.1-gd php7.1-ldap php7.1-zip php7.1-mbstring php7.1-xml php7.1-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php7.4 php7.4 php7.4-mcrypt php7.4-curl php7.4-mysql php7.4-gd php7.4-ldap php7.4-zip php7.4-mbstring php7.4-xml php7.4-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@ -523,7 +562,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client php7.1 php7.1-mcrypt php7.1-curl php7.1-mysql php7.1-gd php7.1-ldap php7.1-zip php7.1-mbstring php7.1-xml php7.1-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client php7.4 php7.4-mcrypt php7.4-curl php7.4-mysql php7.4-gd php7.4-ldap php7.4-zip php7.4-mbstring php7.4-xml php7.4-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@ -551,7 +590,7 @@ case $distro in
fi
;;
raspbian)
if [[ "$version" =~ ^9 ]]; then
if [[ "$version" =~ ^10 ]]; then
# Install for Raspbian 9.x
tzone=$(cat /etc/timezone)
cat >/etc/apt/sources.list.d/10-buster.list <<EOL

View file

@ -0,0 +1,185 @@
<?php
use App\Http\Transformers\LicenseSeatsTransformer;
use App\Models\Asset;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\User;
class ApiLicenseSeatsCest
{
protected $license;
protected $timeFormat;
public function _before(ApiTester $I)
{
$this->user = \App\Models\User::find(1);
$I->haveHttpHeader('Accept', 'application/json');
$I->amBearerAuthenticated($I->getToken($this->user));
}
/** @test */
public function indexLicenseSeats(ApiTester $I)
{
$I->wantTo('Get a list of license seats for a specific license');
// call
$I->sendGET('/licenses/1/seats?limit=10&order=desc');
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
// sample verify
$licenseSeats = App\Models\LicenseSeat::where('license_id', 1)
->orderBy('id','desc')->take(10)->get();
// pick a random seat
$licenseSeat = $licenseSeats->random();
// need the index in the original list so that the "name" field is determined correctly
$licenseSeatNumber = 0;
foreach($licenseSeats as $index=>$seat) {
if ($licenseSeat === $seat) {
$licenseSeatNumber = $index+1;
}
}
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat, $licenseSeatNumber)));
}
/** @test */
public function showLicenseSeat(ApiTester $I)
{
$I->wantTo('Get a license seat');
// call
$I->sendGET('/licenses/1/seats/10');
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
// sample verify
$licenseSeat = App\Models\LicenseSeat::findOrFail(10);
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
}
/** @test */
public function checkoutLicenseSeatToUser(ApiTester $I)
{
$I->wantTo('Checkout a license seat to a user');
$user = App\Models\User::all()->random();
$licenseSeat = App\Models\LicenseSeat::all()->random();
$endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id;
$data = [
'assigned_to' => $user->id,
'note' => 'Test Checkout to User via API'
];
// update
$I->sendPATCH($endpoint, $data);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$response = json_decode($I->grabResponse());
$I->assertEquals('success', $response->status);
$I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages);
$I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change
$I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change
// verify
$licenseSeat = $licenseSeat->fresh();
$I->sendGET($endpoint);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
// verify that the last logged action is a checkout
$I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson([
"action_type" => "checkout"
]);
}
/** @test */
public function checkoutLicenseSeatToAsset(ApiTester $I)
{
$I->wantTo('Checkout a license seat to an asset');
$asset = App\Models\Asset::all()->random();
$licenseSeat = App\Models\LicenseSeat::all()->random();
$endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id;
$data = [
'asset_id' => $asset->id,
'note' => 'Test Checkout to Asset via API'
];
// update
$I->sendPATCH($endpoint, $data);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$response = json_decode($I->grabResponse());
$I->assertEquals('success', $response->status);
$I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages);
$I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change
$I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change
// verify
$licenseSeat = $licenseSeat->fresh();
$I->sendGET($endpoint);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
// verify that the last logged action is a checkout
$I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson([
"action_type" => "checkout"
]);
}
/** @test */
public function checkoutLicenseSeatToUserAndAsset(ApiTester $I)
{
$I->wantTo('Checkout a license seat to a user AND an asset');
$asset = App\Models\Asset::all()->random();
$user = App\Models\User::all()->random();
$licenseSeat = App\Models\LicenseSeat::all()->random();
$endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id;
$data = [
'asset_id' => $asset->id,
'assigned_to' => $user->id,
'note' => 'Test Checkout to User and Asset via API'
];
// update
$I->sendPATCH($endpoint, $data);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$response = json_decode($I->grabResponse());
$I->assertEquals('success', $response->status);
$I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages);
$I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change
$I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change
// verify
$licenseSeat = $licenseSeat->fresh();
$I->sendGET($endpoint);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
// verify that the last logged action is a checkout
$I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson([
"action_type" => "checkout"
]);
}
}

View file

@ -1,5 +1,4 @@
const { mix } = require('laravel-mix');
const mix = require('laravel-mix');
// This generates a file called app.css, which we use
// later on to build all.css
@ -58,7 +57,7 @@ mix
*/
mix
.copy('./resources/assets/css/signature-pad.css', './public/css/dist')
.minify('./public/css/build/signature-pad.css');
.minify('./public/css/dist/signature-pad.css');
// Combine main SnipeIT JS files
mix.js(
@ -94,8 +93,6 @@ mix.less('./resources/assets/less/skins/skin-orange.less', 'css/dist/skins', './
mix.combine(
[
'./node_modules/bootstrap-table/dist/bootstrap-table.css',
'./node_modules/bootstrap-table/dist/extentions/mobile/bootstrap-table-mobile.css',
'./node_modules/bootstrap-table/dist/extensions/export/bootstrap-table-export.css',
'./node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.css'
],
'public/css/dist/bootstrap-table.css'
@ -137,14 +134,14 @@ mix
.combine(
[
'./node_modules/bootstrap-table/dist/bootstrap-table.js',
'./node_modules/bootstrap-table/dist/extentions/mobile/bootstrap-table-mobile.js',
'./node_modules/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile.js',
'./node_modules/bootstrap-table/dist/extensions/export/bootstrap-table-export.js',
'./node_modules/bootstrap-table/dist/extensions/cookie/bootstrap-table-cookie.js',
'./resources/assets/js/extensions/jquery.base64.js',
'./node_modules/tableexport.jquery.plugin/tableExport.js',
'./node_modules/tableexport.jquery.plugin/libs/jsPDF/jspdf.min.js',
'./resources/js/FileSaver.min.js',
'./resources/js/xlsx.core.min.js',
'./resources/assets/js/FileSaver.min.js',
'./node_modules/xlsx/dist/xlsx.core.min.js',
'./node_modules/tableexport.jquery.plugin/libs/jsPDF-AutoTable/jspdf.plugin.autotable.js',
'./node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.js',
'./node_modules/bootstrap-table/dist/extensions/toolbar/bootstrap-table-toolbar.js'