Merge branch 'develop'

This commit is contained in:
Brady Wetherington 2021-04-06 20:10:22 -07:00
commit 792a31cc7f
61 changed files with 23361 additions and 5963 deletions

View file

@ -2146,6 +2146,78 @@
"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"
]
}
]
}

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-243-orange.svg?style=flat-square)](#contributors)
## Snipe-IT - Open Source Asset Management System
@ -122,7 +122,8 @@ 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") |
<!-- 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

@ -51,6 +51,9 @@ 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;
try {
$ldapconn = Ldap::connectToLdap();
@ -175,6 +178,9 @@ 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] : "";
$user = User::where('username', $item["username"])->first();
if ($user) {
@ -193,6 +199,9 @@ 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"];
// 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

@ -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

@ -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,9 @@ 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');
}

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

@ -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

@ -359,7 +359,7 @@ class CustomField extends Model
"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))

View file

@ -169,7 +169,9 @@ 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;
// Get LDAP user data
$item = array();
$item["username"] = isset($ldapatttibutes[$ldap_result_username][0]) ? $ldapatttibutes[$ldap_result_username][0] : "";
@ -177,7 +179,9 @@ class Ldap extends Model
$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["telephone"] = isset($ldapatttibutes[$ldap_result_phone][0]) ?$ldapatttibutes[$ldap_result_phone][0] : "";
$item["jobtitle"] = isset($ldapatttibutes[$ldap_result_jobtitle][0]) ? $ldapatttibutes[$ldap_result_jobtitle][0] : "";
$item["country"] = isset($ldapatttibutes[$ldap_result_country][0]) ? $ldapatttibutes[$ldap_result_country][0] : "";
return $item;

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

@ -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

@ -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 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()
{
//
}
}

27707
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.

View file

@ -19,8 +19,8 @@
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=b3d2ebfcb2541fa8e9ec",
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=39eb843599c275696aed",
"/css/dist/all.css": "/css/dist/all.css?id=199fdf677ce0dce6cef8",
"/css/blue.png": "/css/blue.png?id=4c85d6a97173123bd14a",
"/css/blue@2x.png": "/css/blue@2x.png?id=62c67c6a822439e8a4ac",
"/css/blue.png": "/css/blue.png?id=e83a6c29e04fe851f212",
"/css/blue@2x.png": "/css/blue@2x.png?id=51135dd4d24f88f5de0b",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=0d9935d834b39386a82e",
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=97ec6f217441ac2f79ed",
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=8f7aed152883df9c6f5e",
@ -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,9 @@ 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_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', Input::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

@ -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

@ -28,13 +28,13 @@
</script>
{{-- stylesheets --}}
<link rel="stylesheet" href="{{ url(mix('css/dist/all.css')) }}">
<link rel="stylesheet" href="{{ mix('css/dist/all.css') }}">
@if (($snipeSettings) && ($snipeSettings->allow_user_skin==1) && Auth::check() && Auth::user()->present()->skin != '')
<link rel="stylesheet" href="{{ mix('css/skins/skin-'.Auth::user()->present()->skin.'.min.css') }}">
@elseif (($snipeSettings) && ($snipeSettings->skin!=''))
<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')
@ -158,18 +154,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', ['location' => $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', ['location' => $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

@ -355,7 +355,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

@ -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
@ -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)
@ -389,6 +427,7 @@ case $distro in
create_virtualhost
log "a2enmod rewrite"
log "a2ensite $APP_NAME.conf"
rename_default_vhost
set_hosts
@ -422,6 +461,7 @@ case $distro in
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

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'