Repository: ldidry/lstu Branch: master Commit: d9ca70e87797 Files: 134 Total size: 787.2 KB Directory structure: gitextract_qiht013d/ ├── .dockerignore ├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .provision/ │ ├── README.md │ ├── ansible-role-lstu/ │ │ ├── README.md │ │ ├── handlers/ │ │ │ └── main.yml │ │ ├── tasks/ │ │ │ ├── apprun.yaml │ │ │ ├── dependencies.yaml │ │ │ ├── gitclone.yaml │ │ │ └── main.yml │ │ ├── templates/ │ │ │ ├── app.conf │ │ │ └── lstu.conf.j2 │ │ └── vars/ │ │ └── main.yml │ └── terraform-aws-lstu/ │ ├── README.md │ ├── lstu_startup.sh │ ├── main.tf │ ├── output.tf │ ├── provider.tf │ └── vars.tf ├── .weblate ├── AUTHORS.md ├── CHANGELOG ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cpanfile ├── cpanfile.snapshot ├── docker/ │ └── entrypoint.sh ├── docker-compose.dev.yml ├── docker-compose.yml ├── docker-stack.yml ├── hooks/ │ └── build ├── lib/ │ ├── Lstu/ │ │ ├── Command/ │ │ │ ├── ban.pm │ │ │ ├── safebrowsingcheck.pm │ │ │ ├── theme.pm │ │ │ └── url.pm │ │ ├── Controller/ │ │ │ ├── Admin.pm │ │ │ ├── Authent.pm │ │ │ ├── Stats.pm │ │ │ └── URL.pm │ │ ├── DB/ │ │ │ ├── Ban/ │ │ │ │ ├── MySQL.pm │ │ │ │ ├── Pg.pm │ │ │ │ └── SQLite.pm │ │ │ ├── Ban.pm │ │ │ ├── Session/ │ │ │ │ ├── MySQL.pm │ │ │ │ ├── Pg.pm │ │ │ │ └── SQLite.pm │ │ │ ├── Session.pm │ │ │ ├── URL/ │ │ │ │ ├── MySQL.pm │ │ │ │ ├── Pg.pm │ │ │ │ └── SQLite.pm │ │ │ └── URL.pm │ │ ├── DefaultConfig.pm │ │ └── Plugin/ │ │ ├── Headers.pm │ │ └── Helpers.pm │ ├── Lstu.pm │ └── Mounter.pm ├── lstu.conf.template ├── script/ │ ├── application │ └── lstu ├── t/ │ ├── lstu.passwd │ ├── mysql1.conf │ ├── mysql2.conf │ ├── mysql3.conf │ ├── postgresql1.conf │ ├── postgresql2.conf │ ├── postgresql3.conf │ ├── sqlite1.conf │ ├── sqlite2.conf │ ├── sqlite3.conf │ └── test.t ├── themes/ │ ├── default/ │ │ ├── lib/ │ │ │ └── Lstu/ │ │ │ ├── I18N/ │ │ │ │ ├── br.po │ │ │ │ ├── de.po │ │ │ │ ├── en.po │ │ │ │ ├── es.po │ │ │ │ ├── fr.po │ │ │ │ ├── fr_FR.po │ │ │ │ ├── hr.po │ │ │ │ ├── lstu.pot │ │ │ │ ├── oc.po │ │ │ │ ├── pt_BR.po │ │ │ │ └── sv.po │ │ │ └── I18N.pm │ │ ├── public/ │ │ │ ├── browserconfig.xml │ │ │ ├── css/ │ │ │ │ ├── animation.css │ │ │ │ ├── bootstrap-lstu.min.css │ │ │ │ ├── bootstrap.min.css │ │ │ │ ├── fontelico-codes.css │ │ │ │ ├── fontelico-embedded.css │ │ │ │ ├── fontelico-ie7-codes.css │ │ │ │ ├── fontelico-ie7.css │ │ │ │ ├── fontelico.css │ │ │ │ ├── fontelico.min.css │ │ │ │ └── lstu.css │ │ │ ├── font/ │ │ │ │ └── licenses/ │ │ │ │ ├── Apache License.txt │ │ │ │ └── SIL Open Font License.txt │ │ │ ├── fontello.json │ │ │ └── manifest.json │ │ └── templates/ │ │ ├── api.html.ep │ │ ├── index.html.ep │ │ ├── layouts/ │ │ │ └── default.html.ep │ │ ├── login.html.ep │ │ ├── logout.html.ep │ │ ├── partial/ │ │ │ └── lstu.js.ep │ │ └── stats.html.ep │ └── milligram/ │ ├── Makefile │ ├── lib/ │ │ └── Lstu/ │ │ ├── I18N/ │ │ │ ├── en.po │ │ │ ├── fr.po │ │ │ ├── milligram.pot │ │ │ └── oc.po │ │ └── I18N.pm │ ├── public/ │ │ └── css/ │ │ ├── lstu.css │ │ ├── lstu.min.css │ │ ├── milli-lstu.min.css │ │ ├── milligram.min.css │ │ └── milligram.min.css.map │ └── templates/ │ ├── index.html.ep │ ├── layouts/ │ │ └── default.html.ep │ └── stats.html.ep └── utilities/ ├── bootstrap.json ├── lstu-minion@.service ├── lstu.apache ├── lstu.default ├── lstu.init ├── lstu.nginx ├── lstu.service ├── lstu_upstart.conf ├── migrations/ │ ├── mysql.sql │ ├── postgresql.sql │ └── sqlite.sql └── read_conf.pl ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ .cpan .cpanm .git docker-compose.yml local log/* lstu.conf lstu.db ================================================ FILE: .gitattributes ================================================ zanata.xml merge=ours ================================================ FILE: .gitignore ================================================ .cpan .cpanm .*_history local/* cover_db/* safebrowsing_db/* .tx/* *.db *.db-shm *.db-wal *.db-journal lstu.conf *.swp script/hypnotoad.pid .zanata-cache/* themes/* themes/default/public/robots.txt themes/milligram/public/robots.txt !themes/default !themes/default/* !themes/milligram !themes/milligram/* ================================================ FILE: .gitlab-ci.yml ================================================ image: hatsoftwares/lstu-test-ci:latest stages: - tags - carton - carton_bdd - tests variables: POSTGRES_DB: lstu_db POSTGRES_USER: lstu POSTGRES_PASSWORD: lstu_pwd MYSQL_DATABASE: lstu_db MYSQL_USER: lstu MYSQL_PASSWORD: lstu_pwd MYSQL_ROOT_PASSWORD: root before_script: - if [[ $CI_JOB_NAME == "postgresql 3/3" ]]; then export PGPASSWORD=lstu_pwd; echo 'CREATE DATABASE lstu_minion WITH OWNER lstu;' | psql -h postgres -U lstu lstu_db; fi - if [[ $CI_JOB_NAME == "mysql 3/3" ]]; then echo "CREATE DATABASE lstu_minion; GRANT ALL ON lstu_minion.* TO lstu@'%'; FLUSH PRIVILEGES;" | mysql -h mariadb -u root -proot; fi ### Jobs templates ## # .retry: &retry retry: 2 except: - tags .carton_bdd_template: &carton_bdd_definition <<: *retry stage: carton_bdd needs: - carton cache: key: "$CI_COMMIT_REF_NAME" paths: - local/ policy: pull artifacts: paths: - local.tar expire_in: 3 hours after_script: - tar cf local.tar local/ .tests_template: &tests_definition <<: *retry stage: tests parallel: 3 script: - tar xf local.tar && rm local.tar - export CI_JOB_NAME=$(echo $CI_JOB_NAME | sed -e 's@ .*@@') - echo "Database $CI_JOB_NAME test $CI_NODE_INDEX on $CI_NODE_TOTAL" - MOJO_CONFIG="t/${CI_JOB_NAME}${CI_NODE_INDEX}.conf" make test - MOJO_CONFIG="t/${CI_JOB_NAME}${CI_NODE_INDEX}.conf" make cover - MOJO_CONFIG="t/${CI_JOB_NAME}${CI_NODE_INDEX}.conf" make test-junit-output coverage: '/Total.* (\d+\.\d+)$/' artifacts: reports: junit: tap.xml .sqlite_template: &sqlite_definition <<: *tests_definition needs: - carton_sqlite services: - name: rroemhild/test-openldap alias: rroemhild-test-openldap .pg_template: &pg_definition <<: *tests_definition needs: - carton_postgresql services: - name: rroemhild/test-openldap alias: rroemhild-test-openldap - name: postgres:9.6 alias: postgres .mysql_template: &mysql_definition <<: *tests_definition needs: - carton_mysql services: - name: rroemhild/test-openldap alias: rroemhild-test-openldap - name: mariadb:10.1 alias: mariadb ### Publish tag changelog ## # publish_changelog: image: hatsoftwares/curl-jq:latest stage: tags script: - export PROJECT_API_URL="https://framagit.org/api/v4/projects/${CI_PROJECT_ID}" - export DESCRIPTION_URL="${PROJECT_API_URL}/repository/tags/${CI_COMMIT_TAG}/release" - 'export HEADER="Private-Token: ${GITLAB_API_TOKEN}"' - sed -n '/^'$CI_COMMIT_TAG'[[:space:]]/,/^[^\t]/p' CHANGELOG | sed -e 's/^[^\t].*//' -e 's/\t//g' | sed '/^[[:space:]]*$/d' > /tmp/text - if [[ ! -z $GITLAB_API_TOKEN ]]; then curl -s --request POST --data-urlencode "description@/tmp/text" --header "${HEADER}" "${DESCRIPTION_URL}"; fi only: - tags ### Toot tag ## # pouet-it: image: hatsoftwares/pouet-it-from-ci:latest stage: tags script: - cd /opt/pouet-it-from-ci - export project="$(echo $CI_PROJECT_NAME | perl -p -e 's/(?:^(.)|-(.))/\U$1\U$2/g')" - export MESSAGE=$(echo -e "Je viens de sortir "'#'"${project} en version ${CI_COMMIT_TAG} !""\n${CI_PROJECT_URL}/tags/${CI_COMMIT_TAG}") - carton exec ./pouet-it-from-ci.pl only: - tags ### Podcheck ## # podcheck: <<: *retry stage: carton script: - make podcheck ### Install common dependencies ## # carton: <<: *retry stage: carton cache: key: "$CI_COMMIT_REF_NAME" paths: - local/ script: - carton install --deployment --without=sqlite --without=postgresql --without=mysql ### Install DB related dependencies ## # carton_sqlite: <<: *carton_bdd_definition script: - carton install --deployment --without=postgresql --without=mysql carton_postgresql: <<: *carton_bdd_definition script: - carton install --deployment --without=sqlite --without=mysql carton_mysql: <<: *carton_bdd_definition script: - carton install --deployment --without=sqlite --without=postgresql ### Tests ## # sqlite: <<: *sqlite_definition postgresql: <<: *pg_definition mysql: <<: *mysql_definition ================================================ FILE: .provision/README.md ================================================ ## ansible-role-lstu An ansible role deploy the application on host machine(Ubuntu 20.04) ## terraform-aws-lstu A terraform plan creates necessary AWS infrastructure and deploy the lstu. This terraform plan uses the `lstu_startup.sh` script to deploy application on AWS and also uses above ansible roles `ansible-role-lstu` to configure the application on AWS. ================================================ FILE: .provision/ansible-role-lstu/README.md ================================================ Ansible-Role-Lstu ========= This role installs the and configures lstu on Debian/Ubuntu servers with nginx web server configuration. Role Variables -------------- | Variable name | Value | Description | | ------------- | ----- | ----------- | | `app_dir` | /var/www/lstu | Set the application directory for the best practice | | `lstu_owner` | www-data | Set the application user for the best practice | | `lstu_group` | www-data | Set the application group for the best practice | | `_contact` | contact.example.com | contact option (mandatory), where you have to put some way for the users to contact you. | | `_secret` | IWIWojnokd | secret option (mandatory) array of random strings used to encrypt cookies | | `_project_version` | master | We can chose the project version either Master branch, Dev branch or tag based | | `_server_name` | IP address (or) CNAME/FQDN | Mention the Server Name for the Nginx configurations | Sample example of use in a playbook -------------- The following code has been tested with Ubuntu 20.04 ```yaml - name: "install lstu" hosts: enter your hosts file become: yes role: - ansible-role-lstu vars: lstu_owner: "www-data" lstu_group: "www-data" app_dir: "/var/www/lstu" _contact: "contact.example.com" _report: "report@example.com" _project_version: "master" _server_name: "IP address (or) CNAME/FQDN" ``` Contributing ------------ Don’t hesitate to create a pull request ================================================ FILE: .provision/ansible-role-lstu/handlers/main.yml ================================================ --- # handlers file for ansible-role-lstu - name: restart nginx service: name=nginx state=restarted ================================================ FILE: .provision/ansible-role-lstu/tasks/apprun.yaml ================================================ #apprun.yml --- - name: This command will install the postgress module ansible.builtin.shell: cmd: carton install --deployment --without=test --without=sqlite --without=mysql chdir: "{{ app_dir }}" - name: Upload application file template: src: ../templates/lstu.conf.j2 dest: "{{ app_dir }}/lstu.conf" - name: Run the command for app_executes ansible.builtin.shell: cmd: carton exec hypnotoad script/lstu chdir: "{{ app_dir }}" - name: Nginx configuration file add template: src: ../templates/app.conf dest: /etc/nginx/conf.d/ mode: '0644' notify: restart nginx ================================================ FILE: .provision/ansible-role-lstu/tasks/dependencies.yaml ================================================ #dependencies.yaml --- - name: Lstu | Update apt cache ansible.builtin.apt: update_cache=yes changed_when: no - name: Install Dependencies ansible.builtin.apt: name: - nginx - carton - build-essential - libpng-dev - libssl-dev - libpq-dev - zlib1g-dev - libmojo-sqlite-perl state: present ================================================ FILE: .provision/ansible-role-lstu/tasks/gitclone.yaml ================================================ #gitclone --- - name: Clone the repository ansible.builtin.git: repo: 'https://framagit.org/luc/lstu.git' dest: "{{ app_dir }}" clone: yes update: yes version: "{{ _project_version }}" - name: Change the owner ansible.builtin.file: path: "{{ app_dir }}" owner: "{{ lstu_owner }}" group: "{{ lstu_group }}" state: directory recurse: yes ================================================ FILE: .provision/ansible-role-lstu/tasks/main.yml ================================================ --- # tasks file for ansible-role-lstu - include: dependencies.yaml - include: gitclone.yaml - include: apprun.yaml ================================================ FILE: .provision/ansible-role-lstu/templates/app.conf ================================================ upstream lstu { server 127.0.0.1:8080; } server { listen 80; listen [::]:80; server_name {{ _server_name }}; access_log /var/log/nginx/lstu.access.log; error_log /var/log/nginx/lstu.error.log; location / { proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Remote-Port $remote_port; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://lstu; proxy_redirect http://lstu https://lstu.example.org; } } ================================================ FILE: .provision/ansible-role-lstu/templates/lstu.conf.j2 ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://0.0.0.0:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => '{{ _contact }}', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT secret => ['{{ _secret }}'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either #hashed_adminpwd => '94b2feede6ea5e2eec62f457ecb7d3f719b24d19c29d4e5466246a31908fc23b', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite #dbtype => 'sqlite', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype pgdb => { database => 'lstu', host => 'localhost', # optional, default is 5432 port => 5432, user => 'DBUSER', pwd => 'DBPASSWORD', # optional, default is 1 #max_connections => 1, }, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # user => 'DBUSER', # pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # port => 5432, # user => 'DBUSER', # pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # user => 'DBUSER', # pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: .provision/ansible-role-lstu/vars/main.yml ================================================ --- # vars file for ansible-role-lstu lstu_owner: "www-data" lstu_group: "www-data" app_dir: "/var/www/lstu" _contact: "" _secret: "" _project_version: "" _server_name: "" ================================================ FILE: .provision/terraform-aws-lstu/README.md ================================================ # Terraform-AWS-Deploy This terraform plan create the resourcess of EC2 instance ## Terraform Variables Edit the `vars.tf` file to add the variables as per your need. | Variable name | Value | Description | | ------------- | ----- | ----------- | | `aws_region` | us-east-1 | Set the region | | `vpc_cidr` | 10.0.0.0/16 | Set the cidr value for the vpc | | `public_subnet_cidr` | 10.0.2.0/24 | Set the cidr value for the public subnet | | `user` | ubuntu | Set the EC2 instance user name | | `public_key` | /home/user_name/.ssh/id_rsa_pub | Set the publickey value for the ec2 instance from the host machine | | `private_key` | /home/user_name/.ssh/id_rsa | Set the private key value for the ec2 instance from the hostmachine | | `aws_access_key` | AWSACCESSKEY | Enter your aws access key | | `aws_secrete_key` | AWSSECRETEKEY | Enter your aws secrete key | | `instance_name` | lstu_app_instance | Set the name for instance | | `app_dir` | /var/www/lstu | Set the application directory for the best practice | | `lstu_owner` | www-data | Set the application user for the best practice | | `lstu_group` | www-data | Set the application group for the best practice | | `contact` | contact.example.com | contact option (mandatory), where you have to put some way for the users to contact you. | | `secret` | IWIWojnokd | secret option (mandatory) array of random strings used to encrypt cookies | | `project_version` | master | We can chose the project version either Master branch, Dev branch or tag based | ## Usage of terraform plan with lstu deploy script ```sh git clone https://github.com/ldidry/lstu cd lstu/.provision/terraform-aws-lstu terraform init terraform plan terraform apply ``` ## Usage of terraform plan with ansible role - Comment out the below `data template` and `user_data` source in __main.tf__ file ```sh data "template_file" "init" { template = file("./lstu_startup.sh") vars = { user = var.lstu_owner group = var.lstu_group directory = var.app_dir git_branch = var.project_version contact_lstu = var.contact secret_lstu = var.secret } } ``` ```sh user_data = data.template_file.init.rendered ``` - Add the below provisioner data in __main.tf__ file at the `aws_instance` resource ```sh connection { agent = false type = "ssh" host = aws_instance.ec2_instance.public_dns private_key = "${file(var.private_key)}" user = "${var.user}" } provisioner "remote-exec" { inline = [ "sudo apt update -y", "sudo apt install python3.9 -y", ] } provisioner "local-exec" { command = < hosts && \ echo "[lstu]" | tee -a hosts && \ echo "${aws_instance.ec2_instance.public_ip} ansible_user=${var.user} ansible_ssh_private_key_file=${var.private_key}" | tee -a hosts && \ export ANSIBLE_HOST_KEY_CHECKING=False && \ ansible-playbook -u ${var.user} --private-key ${var.private_key} -i hosts site.yaml EOT } ``` ================================================ FILE: .provision/terraform-aws-lstu/lstu_startup.sh ================================================ #!/bin/bash echo "**********************************************************************" echo " *" echo "Install dependencies *" echo " *" echo "**********************************************************************" SUDO=sudo $SUDO apt update $SUDO apt install jq -y $SUDO apt install wget -y $SUDO apt install unzip -y $SUDO apt install carton -y $SUDO apt install build-essential -y $SUDO apt install nginx -y $SUDO apt install libssl-dev -y $SUDO apt install libpng-dev -y $SUDO apt install libio-socket-ssl-perl -y $SUDO apt install liblwp-protocol-https-perl -y $SUDO apt install zlib1g-dev -y $SUDO apt install libmojo-sqlite-perl -y $SUDO apt install libpq-dev -y echo "**********************************************************************" echo " *" echo "Configuring the Application *" echo " *" echo "**********************************************************************" sleep 10; version=$(curl -s https://framagit.org/api/v4/projects/5/releases | jq '.[]' | jq -r '.name' | head -1) echo $version pushd ${directory} $SUDO wget https://framagit.org/fiat-tux/hat-softwares/lstu/-/archive/$version/lstu-$version.zip $SUDO unzip lstu-$version.zip $SUDO chown ${user} lstu-$version $SUDO chgrp ${group} lstu-$version pushd lstu-$version echo "**********************************************************************" echo " *" echo "Install Carton Packages *" echo " *" echo "**********************************************************************" $SUDO carton install --deployment --without=test --without=sqlite --without=mysql sleep 10; $SUDO cp lstu.conf.template lstu.conf $SUDO sed -i 's/127.0.0.1/0.0.0.0/' lstu.conf $SUDO sed -i 's/#contact/contact/g' lstu.conf $SUDO sed -i "s/admin\[at]\example.com/${contact_lstu}/g" lstu.conf $SUDO sed -i 's/#secret/secret/' -i lstu.conf $SUDO sed -i "s/fdjsofjoihrei/${secret_lstu}/g" lstu.conf $SUDO sed -i '89 , 91 s/#/ /g' lstu.conf $SUDO sed -i '94 , 95 s/#/ /g' lstu.conf $SUDO sed -i '98 s/#/ /g' lstu.conf echo "**********************************************************************" echo " *" echo "Run the Application *" echo " *" echo "**********************************************************************" $SUDO carton exec hypnotoad script/lstu ================================================ FILE: .provision/terraform-aws-lstu/main.tf ================================================ locals { user_data_vars = { user = var.lstu_owner group = var.lstu_group directory = var.app_dir contact_lstu = var.contact secret_lstu = var.secret } } #Create the VPC resource "aws_vpc" "vpc" { cidr_block = "${var.vpc_cidr}" enable_dns_hostnames = true enable_dns_support = true instance_tenancy = "default" tags = { Name = "lstu-master-vpc" } } # Create InternetGateWay and attach to VPC resource "aws_internet_gateway" "IGW" { vpc_id = "${aws_vpc.vpc.id}" tags = { "Name" = "lstu-master-igw" } } # Create a public subnet resource "aws_subnet" "publicsubnet" { vpc_id = "${aws_vpc.vpc.id}" cidr_block = "${var.public_subnet_cidr}" map_public_ip_on_launch = true tags = { Name = "lstu-master-us-east-1-public" } } # Create routeTable resource "aws_route_table" "publicroute" { vpc_id = "${aws_vpc.vpc.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.IGW.id}" } tags = { Name = "lstu-master-us-east-1-public-rt" } } resource "aws_main_route_table_association" "mainRTB" { vpc_id = "${aws_vpc.vpc.id}" route_table_id = "${aws_route_table.publicroute.id}" } ## Create security group resource "aws_security_group" "security" { name = "lstu-master-sg" description = "allow all traffic" vpc_id = "${aws_vpc.vpc.id}" ingress { description = "allow all traffic" from_port = "0" to_port = "65535" protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "allow port SSH" from_port = "22" to_port = "22" protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # Add ubuntu AMI data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] } } #Create key_pair for the instance resource "aws_key_pair" "genkey" { key_name = "lstu.webapp" public_key = "${file(var.public_key)}" } # Craete ec2 instance resource "aws_instance" "ec2_instance" { ami = "${data.aws_ami.ubuntu.id}" instance_type = "t2.medium" associate_public_ip_address = "true" subnet_id = "${aws_subnet.publicsubnet.id}" vpc_security_group_ids = ["${aws_security_group.security.id}"] user_data = templatefile("${path.module}/lstu_startup.sh", local.user_data_vars) key_name = "lstu.webapp" tags = { Name = "${var.instance_name}" } } ================================================ FILE: .provision/terraform-aws-lstu/output.tf ================================================ output "public_ip" { value = "${aws_instance.ec2_instance.public_ip}" } output "App_running_at" { value = "http://${aws_instance.ec2_instance.public_ip}:8080" } ================================================ FILE: .provision/terraform-aws-lstu/provider.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.0" } } } provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "${var.aws_region}" } ================================================ FILE: .provision/terraform-aws-lstu/vars.tf ================================================ variable "aws_region" { default = "aws_region" } variable "vpc_cidr" { default = "cidr_value" } variable "public_subnet_cidr" { default = "cidr_value" } variable "public_subnet1_cidr" { default = "cidr_value" } variable "user" { default = "user_of_instance" } variable "public_key" { default = "$PWD_publickey" } variable "private_key" { default = "$PWD_privatekey" } variable "aws_access_key" { default = "aws_access_key" } variable "aws_secret_key" { default = "aws_secret_key" } variable "instance_name" { default = "lstu" } variable "lstu_owner" { default = "" } variable "lstu_group" { default = "" } variable "app_dir" { default = "" } variable "project_version" { default = "" } variable "contact" { default = "" } variable "secret" { default = "" } ================================================ FILE: .weblate ================================================ [weblate] url = https://weblate.framasoft.org/api/ translation = lstu ================================================ FILE: AUTHORS.md ================================================ # Lstu's authors ## Main developer * Luc Didry, aka Sky (), core developer, framasky@framasphere.org on [Diaspora*](https://framasphere.org/public/framasky) ## Contributors * Benjamin Bouvier * Armando Lüscher * Quentin Pagès (occitan translation) * Phyks (Lucas Verney) * Pierre Rudloff * Belvar (Gwenn M) (breton translation) * Ira W. Snyder * Arnaud de Mouhy (docker port) * Mathieu Aubin * frju365 (Julien Gomes Dias) * Arunodhayam Sam (IaC and ConfigManagement) ================================================ FILE: CHANGELOG ================================================ Revision history for Perl application Lstu 0.29-0 ????-??-?? 0.28-0 2023-12-17 - ⬆️ Update dependencies - 💥 BREAKING CHANGE: Use `?_format=json` instead of `?format=json` - 💥 BREAKING CHANGE: Use `?_format=json` instead of terminating the URL with `.json` 0.27-0 2023-01-11 - 🐛 Fix length of ip column for MySQL (#64) - 🔥 Remove everything about browser extensions (#98) - 🌐 Update translations 0.26-0 2022-05-23 - ✨ Add a config setting to change QR code size 0.25-0 2022-05-09 - ✨ Add a config flag to disable API - ⚡ Use a HEAD request instead of a GET when checking for redirections - 🐛 Handle disabled URLs in `url` CLI command 0.24-0 2022-04-26 - ✨ Allow to sort results in admin stats page (#91) - Fix disabled URLs still showing in admin interface (#79) - Make a distinction between inexistant and disabled URLs 0.23-0 2021-04-26 - Update translations - Improve tests - Improve CI - Upgrade some dependencies 0.22-0 2019-11-08 - Disable URLs instead of removing them (prevents spammers to reuse a deleted shortened URL) - Slugify custom URLs and add a suffix (-2, -3…) if the custom URL is already taken - Allow to search several IP addresses at once - New logo and update theme! - Now use https://weblate.framasoft.org/projects/lstu for translations 0.21-4 2018-12-17 - Add a lockfile to GSB database update to prevent concurrent updates 0.21-3 2018-11-19 - Fix Pg sessions and urls delete fonctions 0.21-2 2018-11-19 - Fix URL removal in memcached from safebrowsingcheck and url commands 0.21-1 2018-10-17 - Fix URL removal in memcached from safebrowsingcheck command 0.21-0 2018-10-17 - Allow to delete URLs and ban IPs from safebrowsingcheck - Add ban CLI tool for banning and unbanning IP addresses 0.20-2 2018-10-07 - Handle very long URLs 0.20-1 2018-09-20 - Fix safebrowsingcheck CLI help message 0.20-0 2018-09-20 - Docker port, thanks to Arnaud de Mouhy - Improve safebrowsingcheck CLI: - displays creators' IP addresses + other URLs from thoses IPs - allow to specify URLs to check - allow to check URLs created seconds ago 0.19-3 2018-09-08 - Update translations 0.19-2 2018-09-08 - Improve GSB object creation and GSB DB update 0.19-1 2018-09-08 - Update translations 0.19-0 2018-09-08 - Allow to use Google Safe Browsing database to check if the URLs are harmless It uses a local copy of the GSB DB, so Google won't see the URLs to shorten - safebrowsingcheck CLI to check all the URLs in the database against GSB DB. usage: carton exec script/lstu safebrowsingcheck - Fix bugs (#45) 0.18-1 2018-08-21 - Update url CLI help message 0.18-0 2018-08-20 - Add option to store IP address of URL creator - Add command to search URL with the IP address of its creator - Add option to blacklist IP address - Allow to remove multiple URLs at once with the CLI 0.17-3 2018-08-20 - Fix missing default db_path 0.17-2 2018-05-15 - Add ability to respond to /robots.txt either by sending the file if it exists or by answering 404 0.17-1 2018-04-28 - Fix bug in making LDAP and Htpasswd dependencies optional 0.17 2018-04-28 - Import URL into cookie by JSON upload - Add X-Content-Type-Options, X-XSS-Protection and X-Frame-Options headers - More dependencies become optional to install - Some refactoring - Add LDAP tests to test suite 0.16-1 2018-04-27 - Fix bug in LDAP authentication 0.16 2018-04-24 - Add Content-Security-Policy header - Use Memcached for cache system (previous cache was unstable) 0.15-2 2018-04-23 - Update Mojolicious::Plugin::GzipStatic 0.15-1 2018-04-23 - Fix url command help function 0.15 2018-04-23 - Add CLI command to search and delete url - Fix CI 0.14-1 2018-04-23 - Fix for PostgreSQL and MySQL connections 0.14 2018-04-22 - Use same DB backend for Minion than db_type 0.13-1 2018-04-22 - Better CSS minification 0.13 2018-04-22 - Gzip static assets with Mojolicious::Plugin::GzipStatic (increase page load speed) - Other page load speed improvements 0.12-1 2018-04-22 - Force Mojo::SQLite version 0.12 2018-04-22 - Improve LDAP User Authentication Support (beware of configuration changes!) - Allow users to configure maximum number of database connections - Use CHI for cache system - Move some tasks to recurring instead of being in after_dispatch hook - Allow to install only deps related to one DB instead of all supported DBs - Use Mojo::SQLite instead of ORLite - Better CI - Put documentation on the wiki (https://framagit.org/luc/lstu/wikis/home) - Update dependencies - Fix bugs 0.11-2 2018-03-22 - Update packages dependencies in README - Fix CI 0.11-1 2018-03-22 - Update dependencies 0.11 2018-03-22 - Add home link to stats page - Fix copy-to-clipboard button - Option to blacklist URLs whose path part matches the configured regex 0.10 2018-02-22 - Performance improvments (cache + assets minification) - Add /stats/:short API endpoint - Update API page - Zanata integration (https://trad.framasoft.org) 0.09-2 2017-09-05 - Handle redirections without host 0.09-1 2017-09-05 - Handle redirections without host 0.09 2017-09-05 - Add QRcode generation - Option to disable the spamhaus check (#29) - Option to blacklist and/or whitelist domains (#30) - Option to set the maximum number of redirections (#31) - Instance statistics available at /fullstats URL - Now return 404 status for non-existent URLs 0.08 2017-02-08 - Add Piwik tracking option - Add Minion option for async counters increasing - Add LDAP and Htpasswd authentication (#8) - Add Database abstraction layer - Add PostgreSQL (#21) - Allow to run several Lstu's instances from the same code but with different configuration files (#23) - Cache dependencies for the different stages of the CI - Allow whitelisted IPs to bypass anti-spam/anti-bruteforce systems (#24) - Add breton translation 0.07 2016-12-20 - Add logo - Add theme system - Better Spamhaus checking - Add ban system - Add occitan translation - Add Milligram theme - Add cache system - Add hashed_adminpwd option 0.06 2015-09-04 - Add Spamhaus checking before accepting the URL - Fix small bug 0.05 2015-07-31 - Add copy-to-clipboard button (#1) - Add test suite - Add Net::Domain::TLD >= 1.73 as dependancy 0.04 2015-06-10 - Change internationalization system (.po files now) - Fixed domain for shortened URLs system (#12) - Fix XSS vulnerability (#15) - Subdirectory mounting system (#16) - Show your URLs ("stats") page - Show all URLs page for admin (#11) - API description page - API change - Self-documented configuration template - add doc and templates for systemd and sysVinit - add template for nginx 0.03 2013-09-25 - FIX #5 again by using transactions 0.02 2013-09-24 - use of morbo or hypnotoad servers availables - use of config file - FIX #1 : GET parameters deleted when using /a/*url route - FIX #2 : Can't use morbo or hypnotoad - FIX #3 : Internationalization - FIX #5 : Scalability issues 0.01 2013-09-21 - original version; ================================================ FILE: CONTRIBUTING.md ================================================ # How to contribute? The official git repository is . You can create an account on Framagit with your Github/Gitlab/Bitbucket account. ## Issues Please, post any issue on . ## Merge requests Please, post any merge request on . For your merge request to be accepted, please: - use 4 spaces for indentation - add yourself to [AUTHORS.md](AUTHORS.md) - be sure that it passed the automated tests before submitting the MR - [squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) ================================================ FILE: Dockerfile ================================================ FROM alpine:3.19 ARG BUILD_DATE ARG VCS_REF ARG VERSION LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.name="Let's Shorten That URL" \ org.label-schema.url="https://lstu.fr/" \ org.label-schema.vcs-ref=$VCS_REF \ org.label-schema.vcs-url="https://framagit.org/fiat-tux/hat-softwares/lstu" \ org.label-schema.vendor="Luc Didry" \ org.label-schema.version=$VERSION \ org.label-schema.schema-version="1.0" RUN adduser -D lstu COPY --chown=lstu:lstu . /home/lstu WORKDIR /home/lstu RUN apk --update add ca-certificates perl perl-netaddr-ip perl-io-socket-ssl perl-dbd-pg mariadb-connector-c-dev libpng zlib openssl perl-dbd-mysql RUN apk add --virtual .build-deps build-base perl-utils perl-dev make sudo zlib-dev libpng-dev postgresql-dev mariadb-dev openssl-dev RUN cpan -T Carton RUN sudo -u lstu carton install --deployment --without=test --without=cache RUN perl -MCPAN -e 'install inc::latest' RUN perl -MCPAN -e 'install Config::FromHash' RUN apk del .build-deps RUN rm -rf /var/cache/apk/* USER lstu ENTRYPOINT ["/bin/sh", "/home/lstu/docker/entrypoint.sh"] ================================================ FILE: LICENSE ================================================ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 Copyright (C) 2004 Sam Hocevar Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. ================================================ FILE: Makefile ================================================ EXTRACTDIR=-D lib -D themes/default/templates POT=themes/default/lib/Lstu/I18N/lstu.pot XGETTEXT=carton exec local/bin/xgettext.pl CARTON=carton exec REAL_LSTU=script/application LSTU=script/lstu HARNESS_PERL_SWITCHES=-MDevel::Cover=+ignore,local minify: @echo "CSS concatenation" @cd ./themes/default/public/css/ && cat bootstrap.min.css lstu.css fontelico.css | csso > bootstrap-lstu.min.css @cd ./themes/milligram/public/css/ && cat milligram.min.css lstu.css ../../../default/public/css/fontelico.css | csso > milli-lstu.min.css locales: $(XGETTEXT) $(EXTRACTDIR) -o $(POT) 2>/dev/null cd ./themes/milligram && make locales podcheck: podchecker lib/Lstu/DB/*pm lib/Lstu/Command/*pm check-syntax: find lib/ themes/ -name \*.pm -exec carton exec perl -Ilib -c {} \; find t/ -name \*.t -exec carton exec perl -Ilib -c {} \; cover: PERL5OPT='-Ilib/' $(CARTON) cover --ignore_re '^local' test: @PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='$(HARNESS_PERL_SWITCHES)' $(CARTON) prove --comments --failures test-junit-output: @PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='$(HARNESS_PERL_SWITCHES)' $(CARTON) prove --comments --failures --formatter TAP::Formatter::JUnit > tap.xml test-sqlite: @rm -rf test1.db test1.db-journal cover_db/ @echo 'MOJO_CONFIG=t/sqlite1.conf' @PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='$(HARNESS_PERL_SWITCHES)' MOJO_CONFIG=t/sqlite1.conf $(CARTON) prove --comments --failures @PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='$(HARNESS_PERL_SWITCHES)' $(CARTON) cover --ignore_re '^local' run-ldap-container: podman run -d --name rroemhild-test-openldap -p 127.0.0.1:10389:10389 docker.io/rroemhild/test-openldap dev: minify $(CARTON) morbo $(LSTU) --listen http://0.0.0.0:3000 --watch lib/ --watch script/ --watch themes/ --watch lstu.conf devlog: multitail log/development.log minion: $(CARTON) $(REAL_LSTU) minion worker installdeps: carton install updatedeps: carton update ================================================ FILE: README.md ================================================ # Lstu ## What does Lstu mean? It means Let's Shorten That Url. ## License Lstu is licensed under the terms of the WTFPL. See the LICENSE file. ## Installation Please, see the [wiki](https://framagit.org/fiat-tux/hat-softwares/lstu/wikis/home). Or you can see [usage with Docker](https://framagit.org/fiat-tux/hat-softwares/lstu/wikis/usage-with-docker). ## How many URLs can it handle ? By default, there are 8 361 453 672 available combinations. I think the sqlite db will explode before you reach this limit, but you can use PostgreSQL or MySQL as database instead of SQLite. If you want more shortened URLs than that, open `lstu.conf` and change the `length` setting. Every time somebody uses Lstu, it will create 'waiting' shortened URLs codes in order to be quick to shorten the URLs. Accordingly to the `lstu.conf` configuration file, it will create `provisioning` waiting URLs, adding them `provis_step` by `provis_step`. ## Official instance You can see it working and use it at . ## API See . Your instance will provide the same page with your URL. ## Contributing See the [contributing guidelines](CONTRIBUTING.md). ## Other projects dependencies Lstu is written in Perl with the Mojolicious framework and uses [Milligram](https://milligram.io/) CSS framework to look not too ugly. ## Authors See the [AUTHORS.md](AUTHORS.md) file. ## Deploy Lstu An ansible role and a terraform plan reside under the `.provision` directory. An user could utilize the terraform plan if they chose to deploy lstu on AWS, if that's not the goal, they could simply execute the ansible role in part. Usage docs for both are present in their respective directories. ================================================ FILE: cpanfile ================================================ requires 'inc::Module::Install::DSL'; requires 'Mojolicious', '>= 8.09'; requires 'Data::Validate::URI'; requires 'Net::Domain::TLD', '>= 1.74'; requires 'Mojolicious::Plugin::I18N'; requires 'Mojolicious::Plugin::DebugDumperHelper'; requires 'Mojolicious::Plugin::Piwik'; requires 'Mojolicious::Plugin::StaticCache'; requires 'Mojolicious::Plugin::GzipStatic'; requires 'Mojolicious::Plugin::CSPHeader', '>= 0.03'; requires 'Minion'; requires 'Locale::Maketext'; requires 'Locale::Maketext::Extract'; requires 'Net::Abuse::Utils::Spamhaus'; requires 'Net::DNS', '>= 1.12'; requires 'Net::SSLeay', '>= 1.81'; requires 'IO::Socket::SSL'; requires 'Image::PNG::QRCode'; requires 'Cpanel::JSON::XS'; feature 'ldap', 'LDAP authentication support' => sub { requires 'Net::LDAP'; requires 'Mojolicious::Plugin::Authentication'; }; feature 'htpasswd', 'Htpasswd authentication support' => sub { requires 'Apache::Htpasswd'; requires 'Mojolicious::Plugin::Authentication'; }; feature 'cache', 'URL cache system' => sub { requires 'Mojolicious::Plugin::CHI'; requires 'CHI::Driver::Memcached'; requires 'Cache::Memcached'; }; feature 'test' => sub { requires 'Devel::Cover', '>= 1.36'; }; feature 'sqlite', 'SQLite support' => sub { requires 'Mojo::SQLite', '>= 3.000'; requires 'Minion::Backend::SQLite', '>= 4.001'; }; feature 'postgresql', 'PostgreSQL support' => sub { requires 'Mojo::Pg'; requires 'Mojolicious::Plugin::PgURLHelper'; }; feature 'mysql', 'MySQL support' => sub { requires 'Mojo::mysql', '>= 1.22'; requires 'Minion::Backend::mysql'; requires 'Mojolicious::Plugin::PgURLHelper'; }; feature 'safebrowsing', 'Check URLs against Google safebrowsing database' => sub { requires 'Net::Google::SafeBrowsing4', '>= 0.8'; requires 'Term::ProgressBar::Quiet'; }; ================================================ FILE: cpanfile.snapshot ================================================ # carton snapshot format: version 1.0 DISTRIBUTIONS Apache-Htpasswd-1.9 pathname: K/KM/KMELTZ/Apache-Htpasswd-1.9.tar.gz provides: Apache::Htpasswd 1.9 requirements: Crypt::PasswdMD5 0 Digest::SHA 2 ExtUtils::MakeMaker 0 MIME::Base64 0 Authen-SASL-2.1700 pathname: E/EH/EHUELS/Authen-SASL-2.1700.tar.gz provides: Authen::SASL 2.1700 Authen::SASL::CRAM_MD5 2.1700 Authen::SASL::EXTERNAL 2.1700 Authen::SASL::Perl 2.1700 Authen::SASL::Perl::ANONYMOUS 2.1700 Authen::SASL::Perl::CRAM_MD5 2.1700 Authen::SASL::Perl::DIGEST_MD5 2.1700 Authen::SASL::Perl::EXTERNAL 2.1700 Authen::SASL::Perl::GSSAPI 2.1700 Authen::SASL::Perl::LOGIN 2.1700 Authen::SASL::Perl::PLAIN 2.1700 requirements: Digest::HMAC_MD5 0 Digest::MD5 0 ExtUtils::MakeMaker 0 perl 5.006000 B-Hooks-EndOfScope-0.26 pathname: E/ET/ETHER/B-Hooks-EndOfScope-0.26.tar.gz provides: B::Hooks::EndOfScope 0.26 B::Hooks::EndOfScope::PP 0.26 B::Hooks::EndOfScope::XS 0.26 requirements: ExtUtils::MakeMaker 0 Hash::Util::FieldHash 0 Module::Implementation 0.05 Scalar::Util 0 Sub::Exporter::Progressive 0.001006 Text::ParseWords 0 Tie::Hash 0 Variable::Magic 0.48 perl 5.006001 strict 0 warnings 0 CHI-0.61 pathname: A/AS/ASB/CHI-0.61.tar.gz provides: CHI 0.61 CHI::CacheObject 0.61 CHI::Driver 0.61 CHI::Driver::Base::CacheContainer 0.61 CHI::Driver::CacheCache 0.61 CHI::Driver::FastMmap 0.61 CHI::Driver::File 0.61 CHI::Driver::Memory 0.61 CHI::Driver::Metacache 0.61 CHI::Driver::Null 0.61 CHI::Driver::RawMemory 0.61 CHI::Driver::Role::HasSubcaches 0.61 CHI::Driver::Role::IsSizeAware 0.61 CHI::Driver::Role::IsSubcache 0.61 CHI::Stats 0.61 requirements: Carp::Assert 0.20 Class::Load 0 Data::UUID 0 Digest::JHash 0 Digest::MD5 0 ExtUtils::MakeMaker 0 File::Spec 0.80 Hash::MoreUtils 0 JSON::MaybeXS 1.003003 List::MoreUtils 0.13 Log::Any 0.08 Moo 1.003 MooX::Types::MooseLike 0.23 MooX::Types::MooseLike::Base 0 MooX::Types::MooseLike::Numeric 0 Storable 0 String::RewritePrefix 0 Task::Weaken 0 Time::Duration 1.06 Time::Duration::Parse 0.03 Time::HiRes 1.30 Try::Tiny 0.05 CHI-Driver-Memcached-0.16 pathname: J/JS/JSWARTZ/CHI-Driver-Memcached-0.16.tar.gz provides: CHI::Driver::Memcached 0.16 CHI::Driver::Memcached::Base 0.16 CHI::Driver::Memcached::Fast 0.16 CHI::Driver::Memcached::libmemcached 0.16 requirements: CHI 0.33 ExtUtils::MakeMaker 0 Moose 0.66 Cache-Memcached-1.30 pathname: D/DO/DORMANDO/Cache-Memcached-1.30.tar.gz provides: Cache::Memcached 1.30 Cache::Memcached::GetParser undef requirements: Encode 0 ExtUtils::MakeMaker 0 Storable 0 String::CRC32 0 Time::HiRes 0 Canary-Stability-2013 pathname: M/ML/MLEHMANN/Canary-Stability-2013.tar.gz provides: Canary::Stability 2013 requirements: ExtUtils::MakeMaker 0 Capture-Tiny-0.48 pathname: D/DA/DAGOLDEN/Capture-Tiny-0.48.tar.gz provides: Capture::Tiny 0.48 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 6.17 File::Spec 0 File::Temp 0 IO::Handle 0 Scalar::Util 0 perl 5.006 strict 0 warnings 0 Carp-Assert-0.22 pathname: Y/YV/YVES/Carp-Assert-0.22.tar.gz provides: Carp::Assert 0.22 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 0 perl 5.006 strict 0 vars 0 warnings 0 Class-Load-0.25 pathname: E/ET/ETHER/Class-Load-0.25.tar.gz provides: Class::Load 0.25 Class::Load::PP 0.25 requirements: Carp 0 Data::OptList 0.110 Exporter 0 ExtUtils::MakeMaker 0 Module::Implementation 0.04 Module::Runtime 0.012 Package::Stash 0.14 Scalar::Util 0 Try::Tiny 0 base 0 perl 5.006 strict 0 warnings 0 Class-Load-XS-0.10 pathname: E/ET/ETHER/Class-Load-XS-0.10.tar.gz provides: Class::Load::XS 0.10 requirements: Class::Load 0.20 ExtUtils::MakeMaker 0 XSLoader 0 perl 5.006 strict 0 warnings 0 Class-Method-Modifiers-2.15 pathname: E/ET/ETHER/Class-Method-Modifiers-2.15.tar.gz provides: Class::Method::Modifiers 2.15 requirements: B 0 Carp 0 Exporter 0 ExtUtils::MakeMaker 0 base 0 perl 5.006 strict 0 warnings 0 Class-MethodMaker-2.24 pathname: S/SC/SCHWIGON/class-methodmaker/Class-MethodMaker-2.24.tar.gz provides: Class::MethodMaker 2.24 Class::MethodMaker::Constants undef Class::MethodMaker::Engine 2.24 Class::MethodMaker::OptExt undef Class::MethodMaker::V1Compat undef Generate undef requirements: ExtUtils::MakeMaker 0 perl 5.006 Clone-0.46 pathname: G/GA/GARU/Clone-0.46.tar.gz provides: Clone 0.46 requirements: ExtUtils::MakeMaker 0 Clone-Choose-0.010 pathname: H/HE/HERMES/Clone-Choose-0.010.tar.gz provides: Clone::Choose 0.010 requirements: ExtUtils::MakeMaker 0 Storable 0 perl 5.008001 Convert-ASN1-0.34 pathname: T/TI/TIMLEGGE/Convert-ASN1-0.34.tar.gz provides: Convert::ASN1 0.34 requirements: ExtUtils::MakeMaker 0 Cpanel-JSON-XS-4.37 pathname: R/RU/RURBAN/Cpanel-JSON-XS-4.37.tar.gz provides: Cpanel::JSON::XS 4.37 Cpanel::JSON::XS::Type undef requirements: Carp 0 Config 0 Encode 1.9801 Exporter 0 ExtUtils::MakeMaker 0 Pod::Text 2.08 XSLoader 0 overload 0 strict 0 warnings 0 Crypt-PasswdMD5-1.42 pathname: R/RS/RSAVAGE/Crypt-PasswdMD5-1.42.tgz provides: Crypt::PasswdMD5 1.42 requirements: Digest::MD5 2.53 ExtUtils::MakeMaker 0 strict 0 warnings 0 DBD-MariaDB-1.23 pathname: P/PA/PALI/DBD-MariaDB-1.23.tar.gz provides: DBD::MariaDB 1.23 requirements: Config 0 DBI 1.608 Data::Dumper 0 Devel::CheckLib 1.12 DynaLoader 0 ExtUtils::MakeMaker 0 File::Spec 0 Getopt::Long 0 perl 5.008001 strict 0 utf8 0 warnings 0 DBD-Pg-3.18.0 pathname: T/TU/TURNSTEP/DBD-Pg-3.18.0.tar.gz provides: Bundle::DBD::Pg v3.18.0 DBD::Pg v3.18.0 requirements: DBI 1.614 ExtUtils::MakeMaker 6.58 File::Temp 0 Test::More 0.88 Time::HiRes 0 version 0 DBD-SQLite-1.74 pathname: I/IS/ISHIGAKI/DBD-SQLite-1.74.tar.gz provides: DBD::SQLite 1.74 DBD::SQLite::Constants undef DBD::SQLite::GetInfo undef DBD::SQLite::VirtualTable 1.74 DBD::SQLite::VirtualTable::Cursor 1.74 DBD::SQLite::VirtualTable::FileContent undef DBD::SQLite::VirtualTable::FileContent::Cursor undef DBD::SQLite::VirtualTable::PerlData undef DBD::SQLite::VirtualTable::PerlData::Cursor undef requirements: DBI 1.57 ExtUtils::MakeMaker 0 File::Spec 0.82 Test::More 0.88 Tie::Hash 0 perl 5.006 DBI-1.643 pathname: T/TI/TIMB/DBI-1.643.tar.gz provides: Bundle::DBI 12.008696 DBD::DBM 0.08 DBD::DBM::Statement 0.08 DBD::DBM::Table 0.08 DBD::DBM::db 0.08 DBD::DBM::dr 0.08 DBD::DBM::st 0.08 DBD::ExampleP 12.014311 DBD::ExampleP::db 12.014311 DBD::ExampleP::dr 12.014311 DBD::ExampleP::st 12.014311 DBD::File 0.44 DBD::File::DataSource::File 0.44 DBD::File::DataSource::Stream 0.44 DBD::File::Statement 0.44 DBD::File::Table 0.44 DBD::File::TableSource::FileSystem 0.44 DBD::File::db 0.44 DBD::File::dr 0.44 DBD::File::st 0.44 DBD::Gofer 0.015327 DBD::Gofer::Policy::Base 0.010088 DBD::Gofer::Policy::classic 0.010088 DBD::Gofer::Policy::pedantic 0.010088 DBD::Gofer::Policy::rush 0.010088 DBD::Gofer::Transport::Base 0.014121 DBD::Gofer::Transport::corostream undef DBD::Gofer::Transport::null 0.010088 DBD::Gofer::Transport::pipeone 0.010088 DBD::Gofer::Transport::stream 0.014599 DBD::Gofer::db 0.015327 DBD::Gofer::dr 0.015327 DBD::Gofer::st 0.015327 DBD::Mem 0.001 DBD::Mem::DataSource 0.001 DBD::Mem::Statement 0.001 DBD::Mem::Table 0.001 DBD::Mem::db 0.001 DBD::Mem::dr 0.001 DBD::Mem::st 0.001 DBD::NullP 12.014715 DBD::NullP::db 12.014715 DBD::NullP::dr 12.014715 DBD::NullP::st 12.014715 DBD::Proxy 0.2004 DBD::Proxy::RPC::PlClient 0.2004 DBD::Proxy::db 0.2004 DBD::Proxy::dr 0.2004 DBD::Proxy::st 0.2004 DBD::Sponge 12.010003 DBD::Sponge::db 12.010003 DBD::Sponge::dr 12.010003 DBD::Sponge::st 12.010003 DBDI 12.015129 DBI 1.643 DBI::Const::GetInfo::ANSI 2.008697 DBI::Const::GetInfo::ODBC 2.011374 DBI::Const::GetInfoReturn 2.008697 DBI::Const::GetInfoType 2.008697 DBI::DBD 12.015129 DBI::DBD::Metadata 2.014214 DBI::DBD::SqlEngine 0.06 DBI::DBD::SqlEngine::DataSource 0.06 DBI::DBD::SqlEngine::Statement 0.06 DBI::DBD::SqlEngine::Table 0.06 DBI::DBD::SqlEngine::TableSource 0.06 DBI::DBD::SqlEngine::TieMeta 0.06 DBI::DBD::SqlEngine::TieTables 0.06 DBI::DBD::SqlEngine::db 0.06 DBI::DBD::SqlEngine::dr 0.06 DBI::DBD::SqlEngine::st 0.06 DBI::Gofer::Execute 0.014283 DBI::Gofer::Request 0.012537 DBI::Gofer::Response 0.011566 DBI::Gofer::Serializer::Base 0.009950 DBI::Gofer::Serializer::DataDumper 0.009950 DBI::Gofer::Serializer::Storable 0.015586 DBI::Gofer::Transport::Base 0.012537 DBI::Gofer::Transport::pipeone 0.012537 DBI::Gofer::Transport::stream 0.012537 DBI::Profile 2.015065 DBI::ProfileData 2.010008 DBI::ProfileDumper 2.015325 DBI::ProfileDumper::Apache 2.014121 DBI::ProfileSubs 0.009396 DBI::ProxyServer 0.3005 DBI::ProxyServer::db 0.3005 DBI::ProxyServer::dr 0.3005 DBI::ProxyServer::st 0.3005 DBI::SQL::Nano 1.015544 DBI::SQL::Nano::Statement_ 1.015544 DBI::SQL::Nano::Table_ 1.015544 DBI::Util::CacheMemory 0.010315 DBI::Util::_accessor 0.009479 DBI::common 1.643 requirements: ExtUtils::MakeMaker 6.48 Test::Simple 0.90 perl 5.008001 Data-OptList-0.114 pathname: R/RJ/RJBS/Data-OptList-0.114.tar.gz provides: Data::OptList 0.114 requirements: ExtUtils::MakeMaker 6.78 List::Util 0 Params::Util 0 Sub::Install 0.921 perl 5.012 strict 0 warnings 0 Data-UUID-1.226 pathname: R/RJ/RJBS/Data-UUID-1.226.tar.gz provides: Data::UUID 1.226 requirements: Digest::MD5 0 ExtUtils::MakeMaker 0 Data-Validate-Domain-0.15 pathname: D/DR/DROLSKY/Data-Validate-Domain-0.15.tar.gz provides: Data::Validate::Domain 0.15 requirements: Exporter 0 ExtUtils::MakeMaker 0 Net::Domain::TLD 1.74 strict 0 warnings 0 Data-Validate-IP-0.31 pathname: D/DR/DROLSKY/Data-Validate-IP-0.31.tar.gz provides: Data::Validate::IP 0.31 requirements: Exporter 0 ExtUtils::MakeMaker 0 NetAddr::IP 4 Scalar::Util 0 base 0 perl 5.008 strict 0 warnings 0 Data-Validate-URI-0.07 pathname: S/SO/SONNEN/Data-Validate-URI-0.07.tar.gz provides: Data::Validate::URI 0.07 requirements: Data::Validate::Domain 0 Data::Validate::IP 0 ExtUtils::MakeMaker 0 Devel-CheckLib-1.16 pathname: M/MA/MATTN/Devel-CheckLib-1.16.tar.gz provides: Devel::CheckLib 1.16 requirements: Exporter 0 ExtUtils::MakeMaker 0 File::Spec 0 File::Temp 0.16 perl 5.004050 Devel-Cover-1.40 pathname: P/PJ/PJCJ/Devel-Cover-1.40.tar.gz provides: Devel::Cover 1.40 Devel::Cover::Annotation::Git 1.40 Devel::Cover::Annotation::Random 1.40 Devel::Cover::Annotation::Svk 1.40 Devel::Cover::Branch 1.40 Devel::Cover::Collection 1.40 Devel::Cover::Collection::Template::Provider 1.40 Devel::Cover::Condition 1.40 Devel::Cover::Condition_and_2 1.40 Devel::Cover::Condition_and_3 1.40 Devel::Cover::Condition_or_2 1.40 Devel::Cover::Condition_or_3 1.40 Devel::Cover::Condition_xor_4 1.40 Devel::Cover::Criterion 1.40 Devel::Cover::DB 1.40 Devel::Cover::DB::Criterion 1.40 Devel::Cover::DB::Digests 1.40 Devel::Cover::DB::File 1.40 Devel::Cover::DB::IO 1.40 Devel::Cover::DB::IO::Base 1.40 Devel::Cover::DB::IO::JSON 1.40 Devel::Cover::DB::IO::Sereal 1.40 Devel::Cover::DB::IO::Storable 1.40 Devel::Cover::DB::Run 1.40 Devel::Cover::DB::Structure 1.40 Devel::Cover::Html_Common 1.40 Devel::Cover::Op 1.40 Devel::Cover::Pod 1.40 Devel::Cover::Report::Compilation 1.40 Devel::Cover::Report::Html 1.40 Devel::Cover::Report::Html_basic 1.40 Devel::Cover::Report::Html_basic::Template::Provider 1.40 Devel::Cover::Report::Html_minimal 1.40 Devel::Cover::Report::Html_subtle 1.40 Devel::Cover::Report::Html_subtle::Template::Provider 1.40 Devel::Cover::Report::Json 1.40 Devel::Cover::Report::Sort 1.40 Devel::Cover::Report::Text 1.40 Devel::Cover::Report::Text2 1.40 Devel::Cover::Report::Vim 1.40 Devel::Cover::Report::Vim::Template::Provider 1.40 Devel::Cover::Statement 1.40 Devel::Cover::Subroutine 1.40 Devel::Cover::Test 1.40 Devel::Cover::Time 1.40 Devel::Cover::Truth_Table 1.40 Devel::Cover::Truth_Table::Row 1.40 Devel::Cover::Util 1.40 Devel::Cover::Web 1.40 requirements: Digest::MD5 0 ExtUtils::MakeMaker 0 HTML::Entities 3.69 Storable 0 Test::More 0 Devel-Cycle-1.12 pathname: L/LD/LDS/Devel-Cycle-1.12.tar.gz provides: Devel::Cycle 1.12 requirements: ExtUtils::MakeMaker 0 Scalar::Util 0 Test::More 0 Devel-GlobalDestruction-0.14 pathname: H/HA/HAARG/Devel-GlobalDestruction-0.14.tar.gz provides: Devel::GlobalDestruction 0.14 requirements: ExtUtils::MakeMaker 0 Sub::Exporter::Progressive 0.001011 perl 5.006 Devel-OverloadInfo-0.007 pathname: I/IL/ILMARI/Devel-OverloadInfo-0.007.tar.gz provides: Devel::OverloadInfo 0.007 requirements: B 0 Exporter 5.57 ExtUtils::MakeMaker 0 MRO::Compat 0 Package::Stash 0.14 Scalar::Util 0 Sub::Util 1.40 Text::ParseWords 0 overload 0 perl 5.006 strict 0 warnings 0 Devel-StackTrace-2.04 pathname: D/DR/DROLSKY/Devel-StackTrace-2.04.tar.gz provides: Devel::StackTrace 2.04 Devel::StackTrace::Frame 2.04 requirements: ExtUtils::MakeMaker 0 File::Spec 0 Scalar::Util 0 overload 0 perl 5.006 strict 0 warnings 0 Devel-Symdump-2.18 pathname: A/AN/ANDK/Devel-Symdump-2.18.tar.gz provides: Devel::Symdump 2.18 Devel::Symdump::Export undef requirements: Compress::Zlib 0 ExtUtils::MakeMaker 0 Test::More 0 perl 5.004 Digest-HMAC-1.04 pathname: A/AR/ARODLAND/Digest-HMAC-1.04.tar.gz provides: Digest::HMAC 1.04 Digest::HMAC_MD5 1.04 Digest::HMAC_SHA1 1.04 requirements: Digest::MD5 2 Digest::SHA 1 ExtUtils::MakeMaker 0 perl 5.004 Digest-JHash-0.10 pathname: S/SH/SHLOMIF/Digest-JHash-0.10.tar.gz provides: Digest::JHash 0.10 requirements: DynaLoader 0 Exporter 0 ExtUtils::MakeMaker 0 perl 5.008 strict 0 vars 0 warnings 0 Dist-CheckConflicts-0.11 pathname: D/DO/DOY/Dist-CheckConflicts-0.11.tar.gz provides: Dist::CheckConflicts 0.11 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 6.30 Module::Runtime 0.009 base 0 strict 0 warnings 0 Encode-Locale-1.05 pathname: G/GA/GAAS/Encode-Locale-1.05.tar.gz provides: Encode::Locale 1.05 requirements: Encode 2 Encode::Alias 0 ExtUtils::MakeMaker 0 perl 5.008 Eval-Closure-0.14 pathname: D/DO/DOY/Eval-Closure-0.14.tar.gz provides: Eval::Closure 0.14 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 0 Scalar::Util 0 constant 0 overload 0 strict 0 warnings 0 Exporter-Tiny-1.006002 pathname: T/TO/TOBYINK/Exporter-Tiny-1.006002.tar.gz provides: Exporter::Shiny 1.006002 Exporter::Tiny 1.006002 requirements: ExtUtils::MakeMaker 6.17 perl 5.006001 ExtUtils-Config-0.008 pathname: L/LE/LEONT/ExtUtils-Config-0.008.tar.gz provides: ExtUtils::Config 0.008 requirements: Data::Dumper 0 ExtUtils::MakeMaker 6.30 strict 0 warnings 0 ExtUtils-Helpers-0.026 pathname: L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz provides: ExtUtils::Helpers 0.026 ExtUtils::Helpers::Unix 0.026 ExtUtils::Helpers::VMS 0.026 ExtUtils::Helpers::Windows 0.026 requirements: Carp 0 Exporter 5.57 ExtUtils::MakeMaker 0 File::Basename 0 File::Copy 0 File::Spec::Functions 0 Text::ParseWords 3.24 perl 5.006 strict 0 warnings 0 ExtUtils-InstallPaths-0.012 pathname: L/LE/LEONT/ExtUtils-InstallPaths-0.012.tar.gz provides: ExtUtils::InstallPaths 0.012 requirements: Carp 0 ExtUtils::Config 0.002 ExtUtils::MakeMaker 0 File::Spec 0 perl 5.006 strict 0 warnings 0 File-Listing-6.16 pathname: P/PL/PLICEASE/File-Listing-6.16.tar.gz provides: File::Listing 6.16 File::Listing::apache 6.16 File::Listing::dosftp 6.16 File::Listing::netware 6.16 File::Listing::unix 6.16 File::Listing::vms 6.16 requirements: Exporter 5.57 ExtUtils::MakeMaker 0 HTTP::Date 0 perl 5.006 File-Remove-1.61 pathname: S/SH/SHLOMIF/File-Remove-1.61.tar.gz provides: File::Remove 1.61 requirements: Cwd 3.29 ExtUtils::MakeMaker 0 File::Glob 0 File::Path 0 File::Spec 3.29 Module::Build 0.28 constant 0 perl 5.008 strict 0 vars 0 warnings 0 HTML-Parser-3.81 pathname: O/OA/OALDERS/HTML-Parser-3.81.tar.gz provides: HTML::Entities 3.81 HTML::Filter 3.81 HTML::HeadParser 3.81 HTML::LinkExtor 3.81 HTML::Parser 3.81 HTML::PullParser 3.81 HTML::TokeParser 3.81 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 6.52 HTML::Tagset 0 HTTP::Headers 0 IO::File 0 URI 0 URI::URL 0 XSLoader 0 strict 0 HTML-Tagset-3.20 pathname: P/PE/PETDANCE/HTML-Tagset-3.20.tar.gz provides: HTML::Tagset 3.20 requirements: ExtUtils::MakeMaker 0 HTTP-CookieJar-0.014 pathname: D/DA/DAGOLDEN/HTTP-CookieJar-0.014.tar.gz provides: HTTP::CookieJar 0.014 HTTP::CookieJar::LWP 0.014 requirements: Carp 0 ExtUtils::MakeMaker 6.17 HTTP::Date 0 Time::Local 1.1901 parent 0 perl 5.008001 strict 0 warnings 0 HTTP-Cookies-6.11 pathname: O/OA/OALDERS/HTTP-Cookies-6.11.tar.gz provides: HTTP::Cookies 6.11 HTTP::Cookies::Microsoft 6.11 HTTP::Cookies::Netscape 6.11 requirements: Carp 0 ExtUtils::MakeMaker 0 HTTP::Date 6 HTTP::Headers::Util 6 HTTP::Request 0 locale 0 perl 5.008001 strict 0 HTTP-Date-6.06 pathname: O/OA/OALDERS/HTTP-Date-6.06.tar.gz provides: HTTP::Date 6.06 requirements: Exporter 0 ExtUtils::MakeMaker 0 Time::Local 1.28 Time::Zone 0 perl 5.006002 strict 0 HTTP-Message-6.45 pathname: O/OA/OALDERS/HTTP-Message-6.45.tar.gz provides: HTTP::Config 6.45 HTTP::Headers 6.45 HTTP::Headers::Auth 6.45 HTTP::Headers::ETag 6.45 HTTP::Headers::Util 6.45 HTTP::Message 6.45 HTTP::Request 6.45 HTTP::Request::Common 6.45 HTTP::Response 6.45 HTTP::Status 6.45 requirements: Carp 0 Clone 0.46 Compress::Raw::Bzip2 0 Compress::Raw::Zlib 2.062 Encode 3.01 Encode::Locale 1 Exporter 5.57 ExtUtils::MakeMaker 0 File::Spec 0 HTTP::Date 6 IO::Compress::Bzip2 2.021 IO::Compress::Deflate 0 IO::Compress::Gzip 0 IO::HTML 0 IO::Uncompress::Inflate 0 IO::Uncompress::RawInflate 0 LWP::MediaTypes 6 MIME::Base64 2.1 MIME::QuotedPrint 0 URI 1.10 parent 0 perl 5.008001 strict 0 warnings 0 HTTP-Negotiate-6.01 pathname: G/GA/GAAS/HTTP-Negotiate-6.01.tar.gz provides: HTTP::Negotiate 6.01 requirements: ExtUtils::MakeMaker 0 HTTP::Headers 6 perl 5.008001 Hash-Merge-0.302 pathname: H/HE/HERMES/Hash-Merge-0.302.tar.gz provides: Hash::Merge 0.302 requirements: Clone::Choose 0.008 ExtUtils::MakeMaker 6.64 Scalar::Util 0 perl 5.008001 Hash-MoreUtils-0.06 pathname: R/RE/REHSACK/Hash-MoreUtils-0.06.tar.gz provides: Hash::MoreUtils 0.06 requirements: ExtUtils::MakeMaker 0 perl 5.008001 IO-HTML-1.004 pathname: C/CJ/CJM/IO-HTML-1.004.tar.gz provides: IO::HTML 1.004 requirements: Carp 0 Encode 2.10 Exporter 5.57 ExtUtils::MakeMaker 0 perl 5.008 IO-Interactive-1.025 pathname: B/BD/BDFOY/IO-Interactive-1.025.tar.gz provides: IO::Interactive 1.025 requirements: ExtUtils::MakeMaker 6.64 File::Spec::Functions 0 perl 5.008 IO-Socket-SSL-2.084 pathname: S/SU/SULLR/IO-Socket-SSL-2.084.tar.gz provides: IO::Socket::SSL 2.084 IO::Socket::SSL::Intercept 2.056 IO::Socket::SSL::OCSP_Cache 2.084 IO::Socket::SSL::OCSP_Resolver 2.084 IO::Socket::SSL::PublicSuffix undef IO::Socket::SSL::SSL_Context 2.084 IO::Socket::SSL::SSL_HANDLE 2.084 IO::Socket::SSL::Session_Cache 2.084 IO::Socket::SSL::Trace 2.084 IO::Socket::SSL::Utils 2.015 requirements: ExtUtils::MakeMaker 0 Mozilla::CA 0 Net::SSLeay 1.46 Scalar::Util 0 Image-PNG-QRCode-0.10 pathname: B/BK/BKB/Image-PNG-QRCode-0.10.tar.gz provides: Image::PNG::QRCode 0.10 requirements: ExtUtils::MakeMaker 0 perl 5.006001 JSON-4.10 pathname: I/IS/ISHIGAKI/JSON-4.10.tar.gz provides: JSON 4.10 JSON::Backend::PP 4.10 requirements: ExtUtils::MakeMaker 0 Test::More 0 JSON-MaybeXS-1.004005 pathname: E/ET/ETHER/JSON-MaybeXS-1.004005.tar.gz provides: JSON::MaybeXS 1.004005 requirements: Carp 0 Cpanel::JSON::XS 2.3310 ExtUtils::MakeMaker 0 JSON::PP 2.27300 Scalar::Util 0 perl 5.006 JSON-XS-4.03 pathname: M/ML/MLEHMANN/JSON-XS-4.03.tar.gz provides: JSON::XS 4.03 requirements: Canary::Stability 0 ExtUtils::MakeMaker 6.52 Types::Serialiser 0 common::sense 0 LWP-MediaTypes-6.04 pathname: O/OA/OALDERS/LWP-MediaTypes-6.04.tar.gz provides: LWP::MediaTypes 6.04 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 0 File::Basename 0 Scalar::Util 0 perl 5.006002 strict 0 List-BinarySearch-0.25 pathname: D/DA/DAVIDO/List-BinarySearch-0.25.tar.gz provides: List::BinarySearch 0.25 List::BinarySearch::PP 0.25 requirements: English 0 ExtUtils::MakeMaker 6.62 List::BinarySearch::XS 0 Scalar::Util 0 Test::More 0.98 perl 5.008000 List-BinarySearch-XS-0.09 pathname: D/DA/DAVIDO/List-BinarySearch-XS-0.09.tar.gz provides: List::BinarySearch::XS 0.09 requirements: ExtUtils::MakeMaker 6.62 Test::More 0.98 perl 5.008000 List-MoreUtils-0.430 pathname: R/RE/REHSACK/List-MoreUtils-0.430.tar.gz provides: List::MoreUtils 0.430 List::MoreUtils::PP 0.430 requirements: Exporter::Tiny 0.038 ExtUtils::MakeMaker 0 List::MoreUtils::XS 0.430 List-MoreUtils-XS-0.430 pathname: R/RE/REHSACK/List-MoreUtils-XS-0.430.tar.gz provides: List::MoreUtils::XS 0.430 requirements: Carp 0 ExtUtils::MakeMaker 0 File::Basename 0 File::Copy 0 File::Path 0 File::Spec 0 IPC::Cmd 0 XSLoader 0.22 base 0 Locale-Maketext-Lexicon-1.00 pathname: D/DR/DRTECH/Locale-Maketext-Lexicon-1.00.tar.gz provides: Locale::Maketext::Extract 1.00 Locale::Maketext::Extract::Plugin::Base 1.00 Locale::Maketext::Extract::Plugin::FormFu 1.00 Locale::Maketext::Extract::Plugin::FormFu::Extractor 1.00 Locale::Maketext::Extract::Plugin::Generic 1.00 Locale::Maketext::Extract::Plugin::Haml 1.00 Locale::Maketext::Extract::Plugin::Mason 1.00 Locale::Maketext::Extract::Plugin::PPI 1.00 Locale::Maketext::Extract::Plugin::Perl 1.00 Locale::Maketext::Extract::Plugin::TT2 1.00 Locale::Maketext::Extract::Plugin::TT2::Directive 1.00 Locale::Maketext::Extract::Plugin::TT2::Parser 1.00 Locale::Maketext::Extract::Plugin::TextTemplate 1.00 Locale::Maketext::Extract::Plugin::TextTemplate::Parser 1.00 Locale::Maketext::Extract::Plugin::YAML 1.00 Locale::Maketext::Extract::Plugin::YAML::Extractor 1.00 Locale::Maketext::Extract::Run 1.00 Locale::Maketext::Lexicon 1.00 Locale::Maketext::Lexicon::Auto 1.00 Locale::Maketext::Lexicon::Gettext 1.00 Locale::Maketext::Lexicon::Msgcat 1.00 Locale::Maketext::Lexicon::Tie 1.00 requirements: ExtUtils::MakeMaker 6.30 Locale::Maketext 1.17 Log-Any-1.717 pathname: P/PR/PREACTION/Log-Any-1.717.tar.gz provides: Log::Any 1.717 Log::Any::Adapter 1.717 Log::Any::Adapter::Base 1.717 Log::Any::Adapter::Capture 1.717 Log::Any::Adapter::File 1.717 Log::Any::Adapter::Multiplex 1.717 Log::Any::Adapter::Null 1.717 Log::Any::Adapter::Stderr 1.717 Log::Any::Adapter::Stdout 1.717 Log::Any::Adapter::Syslog 1.717 Log::Any::Adapter::Test 1.717 Log::Any::Adapter::Util 1.717 Log::Any::Manager 1.717 Log::Any::Proxy 1.717 Log::Any::Proxy::Null 1.717 Log::Any::Proxy::Test 1.717 Log::Any::Proxy::WithStackTrace 1.717 Log::Any::Test 1.717 requirements: ExtUtils::MakeMaker 0 MRO-Compat-0.15 pathname: H/HA/HAARG/MRO-Compat-0.15.tar.gz provides: MRO::Compat 0.15 requirements: ExtUtils::MakeMaker 0 perl 5.006 Minion-10.28 pathname: S/SR/SRI/Minion-10.28.tar.gz provides: LinkCheck undef LinkCheck::Controller::Links undef LinkCheck::Task::CheckLinks undef Minion 10.28 Minion::Backend undef Minion::Backend::Pg undef Minion::Command::minion undef Minion::Command::minion::job undef Minion::Command::minion::worker undef Minion::Iterator undef Minion::Job undef Minion::Worker undef Mojolicious::Plugin::Minion undef Mojolicious::Plugin::Minion::Admin undef requirements: ExtUtils::MakeMaker 0 Mojolicious 9.0 YAML::XS 0.67 perl 5.016 Minion-Backend-SQLite-v5.0.7 pathname: D/DB/DBOOK/Minion-Backend-SQLite-v5.0.7.tar.gz provides: Minion::Backend::SQLite v5.0.7 requirements: List::Util 0 Minion 10.13 Module::Build::Tiny 0.034 Mojo::SQLite 3.000 Mojolicious 7.49 Sys::Hostname 0 Time::HiRes 0 perl 5.010001 Minion-Backend-mysql-1.004 pathname: P/PR/PREACTION/Minion-Backend-mysql-1.004.tar.gz provides: Minion::Backend::mysql 1.004 requirements: ExtUtils::MakeMaker 0 Minion 10.13 Mojo::mysql 1.04 Mojolicious 9.0 perl 5.016 Module-Build-0.4234 pathname: L/LE/LEONT/Module-Build-0.4234.tar.gz provides: Module::Build 0.4234 Module::Build::Base 0.4234 Module::Build::Compat 0.4234 Module::Build::Config 0.4234 Module::Build::Cookbook 0.4234 Module::Build::Dumper 0.4234 Module::Build::Notes 0.4234 Module::Build::PPMMaker 0.4234 Module::Build::Platform::Default 0.4234 Module::Build::Platform::MacOS 0.4234 Module::Build::Platform::Unix 0.4234 Module::Build::Platform::VMS 0.4234 Module::Build::Platform::VOS 0.4234 Module::Build::Platform::Windows 0.4234 Module::Build::Platform::aix 0.4234 Module::Build::Platform::cygwin 0.4234 Module::Build::Platform::darwin 0.4234 Module::Build::Platform::os2 0.4234 Module::Build::PodParser 0.4234 requirements: CPAN::Meta 2.142060 Cwd 0 Data::Dumper 0 ExtUtils::CBuilder 0.27 ExtUtils::Install 0 ExtUtils::Manifest 0 ExtUtils::Mkbootstrap 0 ExtUtils::ParseXS 2.21 File::Basename 0 File::Compare 0 File::Copy 0 File::Find 0 File::Path 0 File::Spec 0.82 Getopt::Long 0 Module::Metadata 1.000002 Perl::OSType 1 TAP::Harness 3.29 Text::Abbrev 0 Text::ParseWords 0 perl 5.006001 version 0.87 Module-Build-Tiny-0.047 pathname: L/LE/LEONT/Module-Build-Tiny-0.047.tar.gz provides: Module::Build::Tiny 0.047 requirements: CPAN::Meta 0 DynaLoader 0 Exporter 5.57 ExtUtils::CBuilder 0 ExtUtils::Config 0.003 ExtUtils::Helpers 0.020 ExtUtils::Install 0 ExtUtils::InstallPaths 0.002 ExtUtils::ParseXS 0 File::Basename 0 File::Find 0 File::Path 0 File::Spec::Functions 0 Getopt::Long 2.36 JSON::PP 2 Pod::Man 0 TAP::Harness::Env 0 perl 5.006 strict 0 warnings 0 Module-Implementation-0.09 pathname: D/DR/DROLSKY/Module-Implementation-0.09.tar.gz provides: Module::Implementation 0.09 requirements: Carp 0 ExtUtils::MakeMaker 0 Module::Runtime 0.012 Try::Tiny 0 strict 0 warnings 0 Module-Install-1.20-withdate pathname: E/ET/ETHER/Module-Install-1.20-withdate.tar.gz provides: Module::AutoInstall 1.20 Module::Install 1.20 Module::Install::Admin 1.20 Module::Install::Admin::Bundle 1.20 Module::Install::Admin::Compiler 1.20 Module::Install::Admin::Find 1.20 Module::Install::Admin::Include 1.20 Module::Install::Admin::Makefile 1.20 Module::Install::Admin::Manifest 1.20 Module::Install::Admin::Metadata 1.20 Module::Install::Admin::ScanDeps 1.20 Module::Install::Admin::WriteAll 1.20 Module::Install::AutoInstall 1.20 Module::Install::Base 1.20 Module::Install::Base::FakeAdmin 1.20 Module::Install::Bundle 1.20 Module::Install::Can 1.20 Module::Install::Compiler 1.20 Module::Install::Deprecated 1.20 Module::Install::External 1.20 Module::Install::Fetch 1.20 Module::Install::Include 1.20 Module::Install::Inline 1.20 Module::Install::MakeMaker 1.20 Module::Install::Makefile 1.20 Module::Install::Metadata 1.20 Module::Install::PAR 1.20 Module::Install::Run 1.20 Module::Install::Scripts 1.20 Module::Install::Share 1.20 Module::Install::Win32 1.20 Module::Install::With 1.20 Module::Install::WriteAll 1.20 inc::Module::Install 1.20 inc::Module::Install::DSL 1.20 requirements: Devel::PPPort 3.16 ExtUtils::Install 1.52 ExtUtils::MakeMaker 6.59 ExtUtils::ParseXS 2.19 File::Path 0 File::Remove 1.42 File::Spec 3.28 Module::Build 0.29 Module::CoreList 2.17 Module::ScanDeps 1.09 Parse::CPAN::Meta 1.4413 Test::Harness 3.13 Test::More 0.86 YAML::Tiny 1.38 autodie 0 perl 5.006 Module-Runtime-0.016 pathname: Z/ZE/ZEFRAM/Module-Runtime-0.016.tar.gz provides: Module::Runtime 0.016 requirements: Module::Build 0 Test::More 0.41 perl 5.006 strict 0 warnings 0 Module-Runtime-Conflicts-0.003 pathname: E/ET/ETHER/Module-Runtime-Conflicts-0.003.tar.gz provides: Module::Runtime::Conflicts 0.003 requirements: Dist::CheckConflicts 0 ExtUtils::MakeMaker 0 Module::Runtime 0 perl 5.006 strict 0 warnings 0 Module-ScanDeps-1.35 pathname: R/RS/RSCHUPP/Module-ScanDeps-1.35.tar.gz provides: Module::ScanDeps 1.35 requirements: ExtUtils::MakeMaker 0 File::Spec 0 File::Temp 0 Getopt::Long 0 List::Util 1.33 Module::Metadata 0 Text::ParseWords 0 perl 5.008009 version 0 Mojo-Pg-4.27 pathname: S/SR/SRI/Mojo-Pg-4.27.tar.gz provides: Mojo::Pg 4.27 Mojo::Pg::Database undef Mojo::Pg::Migrations undef Mojo::Pg::PubSub undef Mojo::Pg::Results undef Mojo::Pg::Transaction undef requirements: DBD::Pg 3.007004 ExtUtils::MakeMaker 0 Mojolicious 8.50 SQL::Abstract::Pg 1.0 perl 5.016 Mojo-SQLite-3.009 pathname: D/DB/DBOOK/Mojo-SQLite-3.009.tar.gz provides: Mojo::SQLite 3.009 Mojo::SQLite::Database 3.009 Mojo::SQLite::Migrations 3.009 Mojo::SQLite::PubSub 3.009 Mojo::SQLite::Results 3.009 Mojo::SQLite::Transaction 3.009 requirements: Carp 0 DBD::SQLite 1.68 DBI 1.627 File::Spec::Functions 0 File::Temp 0 Module::Build::Tiny 0.034 Mojolicious 8.03 SQL::Abstract::Pg 1.0 Scalar::Util 0 URI 1.69 URI::db 0.15 URI::file 4.21 perl 5.010001 Mojo-mysql-1.27 pathname: J/JH/JHTHORSEN/Mojo-mysql-1.27.tar.gz provides: Mojo::mysql 1.27 Mojo::mysql::Database undef Mojo::mysql::Migrations undef Mojo::mysql::PubSub undef Mojo::mysql::Results undef Mojo::mysql::Transaction undef SQL::Abstract::mysql undef requirements: DBD::MariaDB 1.21 DBI 1.643 ExtUtils::MakeMaker 0 Mojolicious 8.03 SQL::Abstract 1.86 perl 5.016 Mojolicious-9.35 pathname: S/SR/SRI/Mojolicious-9.35.tar.gz provides: Mojo undef Mojo::Asset undef Mojo::Asset::File undef Mojo::Asset::Memory undef Mojo::Base undef Mojo::ByteStream undef Mojo::Cache undef Mojo::Collection undef Mojo::Content undef Mojo::Content::MultiPart undef Mojo::Content::Single undef Mojo::Cookie undef Mojo::Cookie::Request undef Mojo::Cookie::Response undef Mojo::DOM undef Mojo::DOM::CSS undef Mojo::DOM::HTML undef Mojo::Date undef Mojo::DynamicMethods undef Mojo::EventEmitter undef Mojo::Exception undef Mojo::File undef Mojo::Headers undef Mojo::HelloWorld undef Mojo::Home undef Mojo::IOLoop undef Mojo::IOLoop::Client undef Mojo::IOLoop::Server undef Mojo::IOLoop::Stream undef Mojo::IOLoop::Subprocess undef Mojo::IOLoop::TLS undef Mojo::JSON undef Mojo::JSON::Pointer undef Mojo::Loader undef Mojo::Log undef Mojo::Message undef Mojo::Message::Request undef Mojo::Message::Response undef Mojo::Parameters undef Mojo::Path undef Mojo::Promise undef Mojo::Reactor undef Mojo::Reactor::EV undef Mojo::Reactor::Poll undef Mojo::Server undef Mojo::Server::CGI undef Mojo::Server::Daemon undef Mojo::Server::Hypnotoad undef Mojo::Server::Morbo undef Mojo::Server::Morbo::Backend undef Mojo::Server::Morbo::Backend::Poll undef Mojo::Server::PSGI undef Mojo::Server::Prefork undef Mojo::Template undef Mojo::Transaction undef Mojo::Transaction::HTTP undef Mojo::Transaction::WebSocket undef Mojo::URL undef Mojo::Upload undef Mojo::UserAgent undef Mojo::UserAgent::CookieJar undef Mojo::UserAgent::Proxy undef Mojo::UserAgent::Server undef Mojo::UserAgent::Transactor undef Mojo::Util undef Mojo::WebSocket undef Mojolicious 9.35 Mojolicious::Command undef Mojolicious::Command::Author::cpanify undef Mojolicious::Command::Author::generate undef Mojolicious::Command::Author::generate::app undef Mojolicious::Command::Author::generate::dockerfile undef Mojolicious::Command::Author::generate::lite_app undef Mojolicious::Command::Author::generate::makefile undef Mojolicious::Command::Author::generate::plugin undef Mojolicious::Command::Author::inflate undef Mojolicious::Command::cgi undef Mojolicious::Command::daemon undef Mojolicious::Command::eval undef Mojolicious::Command::get undef Mojolicious::Command::prefork undef Mojolicious::Command::psgi undef Mojolicious::Command::routes undef Mojolicious::Command::version undef Mojolicious::Commands undef Mojolicious::Controller undef Mojolicious::Lite undef Mojolicious::Plugin undef Mojolicious::Plugin::Config undef Mojolicious::Plugin::DefaultHelpers undef Mojolicious::Plugin::EPLRenderer undef Mojolicious::Plugin::EPRenderer undef Mojolicious::Plugin::HeaderCondition undef Mojolicious::Plugin::JSONConfig undef Mojolicious::Plugin::Mount undef Mojolicious::Plugin::NotYAMLConfig undef Mojolicious::Plugin::TagHelpers undef Mojolicious::Plugins undef Mojolicious::Renderer undef Mojolicious::Routes undef Mojolicious::Routes::Match undef Mojolicious::Routes::Pattern undef Mojolicious::Routes::Route undef Mojolicious::Sessions undef Mojolicious::Static undef Mojolicious::Types undef Mojolicious::Validator undef Mojolicious::Validator::Validation undef Test::Mojo undef ojo undef requirements: ExtUtils::MakeMaker 0 IO::Socket::IP 0.37 Sub::Util 1.41 perl 5.016 Mojolicious-Plugin-Authentication-1.39 pathname: J/JJ/JJATRIA/Mojolicious-Plugin-Authentication-1.39.tar.gz provides: Mojolicious::Plugin::Authentication 1.39 requirements: Exporter 0 ExtUtils::MakeMaker 0 Mojolicious 8.0 perl 5.016 Mojolicious-Plugin-CHI-0.20 pathname: A/AK/AKRON/Mojolicious-Plugin-CHI-0.20.tar.gz provides: Mojolicious::Plugin::CHI 0.20 Mojolicious::Plugin::CHI::chi undef requirements: CHI 0.58 Digest::JHash 0.05 ExtUtils::MakeMaker 0 Mojolicious 4.77 Test::Memory::Cycle 1.06 Test::More 0 Test::Output 1 perl 5.010001 Mojolicious-Plugin-CSPHeader-0.06 pathname: L/LD/LDIDRY/Mojolicious-Plugin-CSPHeader-0.06.tar.gz provides: Mojolicious::Plugin::CSPHeader 0.06 requirements: ExtUtils::MakeMaker 0 Mojolicious 7.75 Mojolicious-Plugin-DebugDumperHelper-0.03 pathname: L/LD/LDIDRY/Mojolicious-Plugin-DebugDumperHelper-0.03.tar.gz provides: Mojolicious::Plugin::DebugDumperHelper 0.03 requirements: ExtUtils::MakeMaker 0 Mojolicious 6.11 Mojolicious-Plugin-GzipStatic-0.04 pathname: L/LD/LDIDRY/Mojolicious-Plugin-GzipStatic-0.04.tar.gz provides: Mojolicious::Plugin::GzipStatic 0.04 requirements: ExtUtils::MakeMaker 0 IO::Compress::Gzip 0 Mojolicious 7.75 Mojolicious-Plugin-I18N-1.6 pathname: S/SH/SHARIFULN/Mojolicious-Plugin-I18N-1.6.tar.gz provides: Mojolicious::Plugin::I18N 1.6 requirements: I18N::LangTags 0.35 Module::Build 0.42 Mojolicious 5 Test::More 0 perl 5.010001 Mojolicious-Plugin-PgURLHelper-0.03 pathname: L/LD/LDIDRY/Mojolicious-Plugin-PgURLHelper-0.03.tar.gz provides: Mojolicious::Plugin::PgURLHelper 0.03 requirements: ExtUtils::MakeMaker 0 Mojolicious 7.23 Mojolicious-Plugin-Piwik-2.00 pathname: A/AK/AKRON/Mojolicious-Plugin-Piwik-2.00.tar.gz provides: Mojolicious::Plugin::Piwik 2.00 requirements: ExtUtils::MakeMaker 0 Mojolicious 8.02 Test::More 0 perl 5.010001 Mojolicious-Plugin-StaticCache-0.02 pathname: L/LD/LDIDRY/Mojolicious-Plugin-StaticCache-0.02.tar.gz provides: Mojolicious::Plugin::StaticCache 0.02 requirements: ExtUtils::MakeMaker 0 Mojolicious 7.33 Moo-2.005005 pathname: H/HA/HAARG/Moo-2.005005.tar.gz provides: Method::Generate::Accessor undef Method::Generate::BuildAll undef Method::Generate::Constructor undef Method::Generate::DemolishAll undef Moo 2.005005 Moo::HandleMoose undef Moo::HandleMoose::FakeConstructor undef Moo::HandleMoose::FakeMetaClass undef Moo::HandleMoose::_TypeMap undef Moo::Object undef Moo::Role 2.005005 Moo::_Utils undef Moo::sification undef oo undef requirements: Carp 0 Class::Method::Modifiers 1.10 Exporter 0 ExtUtils::MakeMaker 0 Role::Tiny 2.002003 Scalar::Util 1.00 Sub::Defer 2.006006 Sub::Quote 2.006006 perl 5.006 MooX-Types-MooseLike-0.29 pathname: M/MA/MATEU/MooX-Types-MooseLike-0.29.tar.gz provides: MooX::Types::MooseLike 0.29 MooX::Types::MooseLike::Base 0.29 requirements: ExtUtils::MakeMaker 0 Module::Runtime 0.014 MooX-Types-MooseLike-Numeric-1.03 pathname: M/MA/MATEU/MooX-Types-MooseLike-Numeric-1.03.tar.gz provides: MooX::Types::MooseLike::Numeric 1.03 requirements: ExtUtils::MakeMaker 0 Moo 1.004002 MooX::Types::MooseLike 0.23 Test::Fatal 0.003 Test::More 0.96 Moose-2.2206 pathname: E/ET/ETHER/Moose-2.2206.tar.gz provides: Class::MOP 2.2206 Class::MOP::Attribute 2.2206 Class::MOP::Class 2.2206 Class::MOP::Class::Immutable::Trait 2.2206 Class::MOP::Deprecated 2.2206 Class::MOP::Instance 2.2206 Class::MOP::Method 2.2206 Class::MOP::Method::Accessor 2.2206 Class::MOP::Method::Constructor 2.2206 Class::MOP::Method::Generated 2.2206 Class::MOP::Method::Inlined 2.2206 Class::MOP::Method::Meta 2.2206 Class::MOP::Method::Wrapped 2.2206 Class::MOP::MiniTrait 2.2206 Class::MOP::Mixin 2.2206 Class::MOP::Mixin::AttributeCore 2.2206 Class::MOP::Mixin::HasAttributes 2.2206 Class::MOP::Mixin::HasMethods 2.2206 Class::MOP::Mixin::HasOverloads 2.2206 Class::MOP::Module 2.2206 Class::MOP::Object 2.2206 Class::MOP::Overload 2.2206 Class::MOP::Package 2.2206 Moose 2.2206 Moose::Cookbook 2.2206 Moose::Cookbook::Basics::BankAccount_MethodModifiersAndSubclassing 2.2206 Moose::Cookbook::Basics::BinaryTree_AttributeFeatures 2.2206 Moose::Cookbook::Basics::BinaryTree_BuilderAndLazyBuild 2.2206 Moose::Cookbook::Basics::Company_Subtypes 2.2206 Moose::Cookbook::Basics::DateTime_ExtendingNonMooseParent 2.2206 Moose::Cookbook::Basics::Document_AugmentAndInner 2.2206 Moose::Cookbook::Basics::Genome_OverloadingSubtypesAndCoercion 2.2206 Moose::Cookbook::Basics::HTTP_SubtypesAndCoercion 2.2206 Moose::Cookbook::Basics::Immutable 2.2206 Moose::Cookbook::Basics::Person_BUILDARGSAndBUILD 2.2206 Moose::Cookbook::Basics::Point_AttributesAndSubclassing 2.2206 Moose::Cookbook::Extending::Debugging_BaseClassRole 2.2206 Moose::Cookbook::Extending::ExtensionOverview 2.2206 Moose::Cookbook::Extending::Mooseish_MooseSugar 2.2206 Moose::Cookbook::Legacy::Debugging_BaseClassReplacement 2.2206 Moose::Cookbook::Legacy::Labeled_AttributeMetaclass 2.2206 Moose::Cookbook::Legacy::Table_ClassMetaclass 2.2206 Moose::Cookbook::Meta::GlobRef_InstanceMetaclass 2.2206 Moose::Cookbook::Meta::Labeled_AttributeTrait 2.2206 Moose::Cookbook::Meta::PrivateOrPublic_MethodMetaclass 2.2206 Moose::Cookbook::Meta::Table_MetaclassTrait 2.2206 Moose::Cookbook::Meta::WhyMeta 2.2206 Moose::Cookbook::Roles::ApplicationToInstance 2.2206 Moose::Cookbook::Roles::Comparable_CodeReuse 2.2206 Moose::Cookbook::Roles::Restartable_AdvancedComposition 2.2206 Moose::Cookbook::Snack::Keywords 2.2206 Moose::Cookbook::Snack::Types 2.2206 Moose::Cookbook::Style 2.2206 Moose::Deprecated 2.2206 Moose::Exception 2.2206 Moose::Exception::AccessorMustReadWrite 2.2206 Moose::Exception::AddParameterizableTypeTakesParameterizableType 2.2206 Moose::Exception::AddRoleTakesAMooseMetaRoleInstance 2.2206 Moose::Exception::AddRoleToARoleTakesAMooseMetaRole 2.2206 Moose::Exception::ApplyTakesABlessedInstance 2.2206 Moose::Exception::AttachToClassNeedsAClassMOPClassInstanceOrASubclass 2.2206 Moose::Exception::AttributeConflictInRoles 2.2206 Moose::Exception::AttributeConflictInSummation 2.2206 Moose::Exception::AttributeExtensionIsNotSupportedInRoles 2.2206 Moose::Exception::AttributeIsRequired 2.2206 Moose::Exception::AttributeMustBeAnClassMOPMixinAttributeCoreOrSubclass 2.2206 Moose::Exception::AttributeNamesDoNotMatch 2.2206 Moose::Exception::AttributeValueIsNotAnObject 2.2206 Moose::Exception::AttributeValueIsNotDefined 2.2206 Moose::Exception::AutoDeRefNeedsArrayRefOrHashRef 2.2206 Moose::Exception::BadOptionFormat 2.2206 Moose::Exception::BothBuilderAndDefaultAreNotAllowed 2.2206 Moose::Exception::BuilderDoesNotExist 2.2206 Moose::Exception::BuilderMethodNotSupportedForAttribute 2.2206 Moose::Exception::BuilderMethodNotSupportedForInlineAttribute 2.2206 Moose::Exception::BuilderMustBeAMethodName 2.2206 Moose::Exception::CallingMethodOnAnImmutableInstance 2.2206 Moose::Exception::CallingReadOnlyMethodOnAnImmutableInstance 2.2206 Moose::Exception::CanExtendOnlyClasses 2.2206 Moose::Exception::CanOnlyConsumeRole 2.2206 Moose::Exception::CanOnlyWrapBlessedCode 2.2206 Moose::Exception::CanReblessOnlyIntoASubclass 2.2206 Moose::Exception::CanReblessOnlyIntoASuperclass 2.2206 Moose::Exception::CannotAddAdditionalTypeCoercionsToUnion 2.2206 Moose::Exception::CannotAddAsAnAttributeToARole 2.2206 Moose::Exception::CannotApplyBaseClassRolesToRole 2.2206 Moose::Exception::CannotAssignValueToReadOnlyAccessor 2.2206 Moose::Exception::CannotAugmentIfLocalMethodPresent 2.2206 Moose::Exception::CannotAugmentNoSuperMethod 2.2206 Moose::Exception::CannotAutoDerefWithoutIsa 2.2206 Moose::Exception::CannotAutoDereferenceTypeConstraint 2.2206 Moose::Exception::CannotCalculateNativeType 2.2206 Moose::Exception::CannotCallAnAbstractBaseMethod 2.2206 Moose::Exception::CannotCallAnAbstractMethod 2.2206 Moose::Exception::CannotCoerceAWeakRef 2.2206 Moose::Exception::CannotCoerceAttributeWhichHasNoCoercion 2.2206 Moose::Exception::CannotCreateHigherOrderTypeWithoutATypeParameter 2.2206 Moose::Exception::CannotCreateMethodAliasLocalMethodIsPresent 2.2206 Moose::Exception::CannotCreateMethodAliasLocalMethodIsPresentInClass 2.2206 Moose::Exception::CannotDelegateLocalMethodIsPresent 2.2206 Moose::Exception::CannotDelegateWithoutIsa 2.2206 Moose::Exception::CannotFindDelegateMetaclass 2.2206 Moose::Exception::CannotFindType 2.2206 Moose::Exception::CannotFindTypeGivenToMatchOnType 2.2206 Moose::Exception::CannotFixMetaclassCompatibility 2.2206 Moose::Exception::CannotGenerateInlineConstraint 2.2206 Moose::Exception::CannotInitializeMooseMetaRoleComposite 2.2206 Moose::Exception::CannotInlineTypeConstraintCheck 2.2206 Moose::Exception::CannotLocatePackageInINC 2.2206 Moose::Exception::CannotMakeMetaclassCompatible 2.2206 Moose::Exception::CannotOverrideALocalMethod 2.2206 Moose::Exception::CannotOverrideBodyOfMetaMethods 2.2206 Moose::Exception::CannotOverrideLocalMethodIsPresent 2.2206 Moose::Exception::CannotOverrideNoSuperMethod 2.2206 Moose::Exception::CannotRegisterUnnamedTypeConstraint 2.2206 Moose::Exception::CannotUseLazyBuildAndDefaultSimultaneously 2.2206 Moose::Exception::CircularReferenceInAlso 2.2206 Moose::Exception::ClassDoesNotHaveInitMeta 2.2206 Moose::Exception::ClassDoesTheExcludedRole 2.2206 Moose::Exception::ClassNamesDoNotMatch 2.2206 Moose::Exception::CloneObjectExpectsAnInstanceOfMetaclass 2.2206 Moose::Exception::CodeBlockMustBeACodeRef 2.2206 Moose::Exception::CoercingWithoutCoercions 2.2206 Moose::Exception::CoercionAlreadyExists 2.2206 Moose::Exception::CoercionNeedsTypeConstraint 2.2206 Moose::Exception::ConflictDetectedInCheckRoleExclusions 2.2206 Moose::Exception::ConflictDetectedInCheckRoleExclusionsInToClass 2.2206 Moose::Exception::ConstructClassInstanceTakesPackageName 2.2206 Moose::Exception::CouldNotCreateMethod 2.2206 Moose::Exception::CouldNotCreateWriter 2.2206 Moose::Exception::CouldNotEvalConstructor 2.2206 Moose::Exception::CouldNotEvalDestructor 2.2206 Moose::Exception::CouldNotFindTypeConstraintToCoerceFrom 2.2206 Moose::Exception::CouldNotGenerateInlineAttributeMethod 2.2206 Moose::Exception::CouldNotLocateTypeConstraintForUnion 2.2206 Moose::Exception::CouldNotParseType 2.2206 Moose::Exception::CreateMOPClassTakesArrayRefOfAttributes 2.2206 Moose::Exception::CreateMOPClassTakesArrayRefOfSuperclasses 2.2206 Moose::Exception::CreateMOPClassTakesHashRefOfMethods 2.2206 Moose::Exception::CreateTakesArrayRefOfRoles 2.2206 Moose::Exception::CreateTakesHashRefOfAttributes 2.2206 Moose::Exception::CreateTakesHashRefOfMethods 2.2206 Moose::Exception::DefaultToMatchOnTypeMustBeCodeRef 2.2206 Moose::Exception::DelegationToAClassWhichIsNotLoaded 2.2206 Moose::Exception::DelegationToARoleWhichIsNotLoaded 2.2206 Moose::Exception::DelegationToATypeWhichIsNotAClass 2.2206 Moose::Exception::DoesRequiresRoleName 2.2206 Moose::Exception::EnumCalledWithAnArrayRefAndAdditionalArgs 2.2206 Moose::Exception::EnumValuesMustBeString 2.2206 Moose::Exception::ExtendsMissingArgs 2.2206 Moose::Exception::HandlesMustBeAHashRef 2.2206 Moose::Exception::IllegalInheritedOptions 2.2206 Moose::Exception::IllegalMethodTypeToAddMethodModifier 2.2206 Moose::Exception::IncompatibleMetaclassOfSuperclass 2.2206 Moose::Exception::InitMetaRequiresClass 2.2206 Moose::Exception::InitializeTakesUnBlessedPackageName 2.2206 Moose::Exception::InstanceBlessedIntoWrongClass 2.2206 Moose::Exception::InstanceMustBeABlessedReference 2.2206 Moose::Exception::InvalidArgPassedToMooseUtilMetaRole 2.2206 Moose::Exception::InvalidArgumentToMethod 2.2206 Moose::Exception::InvalidArgumentsToTraitAliases 2.2206 Moose::Exception::InvalidBaseTypeGivenToCreateParameterizedTypeConstraint 2.2206 Moose::Exception::InvalidHandleValue 2.2206 Moose::Exception::InvalidHasProvidedInARole 2.2206 Moose::Exception::InvalidNameForType 2.2206 Moose::Exception::InvalidOverloadOperator 2.2206 Moose::Exception::InvalidRoleApplication 2.2206 Moose::Exception::InvalidTypeConstraint 2.2206 Moose::Exception::InvalidTypeGivenToCreateParameterizedTypeConstraint 2.2206 Moose::Exception::InvalidValueForIs 2.2206 Moose::Exception::IsaDoesNotDoTheRole 2.2206 Moose::Exception::IsaLacksDoesMethod 2.2206 Moose::Exception::LazyAttributeNeedsADefault 2.2206 Moose::Exception::Legacy 2.2206 Moose::Exception::MOPAttributeNewNeedsAttributeName 2.2206 Moose::Exception::MatchActionMustBeACodeRef 2.2206 Moose::Exception::MessageParameterMustBeCodeRef 2.2206 Moose::Exception::MetaclassIsAClassNotASubclassOfGivenMetaclass 2.2206 Moose::Exception::MetaclassIsARoleNotASubclassOfGivenMetaclass 2.2206 Moose::Exception::MetaclassIsNotASubclassOfGivenMetaclass 2.2206 Moose::Exception::MetaclassMustBeASubclassOfMooseMetaClass 2.2206 Moose::Exception::MetaclassMustBeASubclassOfMooseMetaRole 2.2206 Moose::Exception::MetaclassMustBeDerivedFromClassMOPClass 2.2206 Moose::Exception::MetaclassNotLoaded 2.2206 Moose::Exception::MetaclassTypeIncompatible 2.2206 Moose::Exception::MethodExpectedAMetaclassObject 2.2206 Moose::Exception::MethodExpectsFewerArgs 2.2206 Moose::Exception::MethodExpectsMoreArgs 2.2206 Moose::Exception::MethodModifierNeedsMethodName 2.2206 Moose::Exception::MethodNameConflictInRoles 2.2206 Moose::Exception::MethodNameNotFoundInInheritanceHierarchy 2.2206 Moose::Exception::MethodNameNotGiven 2.2206 Moose::Exception::MustDefineAMethodName 2.2206 Moose::Exception::MustDefineAnAttributeName 2.2206 Moose::Exception::MustDefineAnOverloadOperator 2.2206 Moose::Exception::MustHaveAtLeastOneValueToEnumerate 2.2206 Moose::Exception::MustPassAHashOfOptions 2.2206 Moose::Exception::MustPassAMooseMetaRoleInstanceOrSubclass 2.2206 Moose::Exception::MustPassAPackageNameOrAnExistingClassMOPPackageInstance 2.2206 Moose::Exception::MustPassEvenNumberOfArguments 2.2206 Moose::Exception::MustPassEvenNumberOfAttributeOptions 2.2206 Moose::Exception::MustProvideANameForTheAttribute 2.2206 Moose::Exception::MustSpecifyAtleastOneMethod 2.2206 Moose::Exception::MustSpecifyAtleastOneRole 2.2206 Moose::Exception::MustSpecifyAtleastOneRoleToApplicant 2.2206 Moose::Exception::MustSupplyAClassMOPAttributeInstance 2.2206 Moose::Exception::MustSupplyADelegateToMethod 2.2206 Moose::Exception::MustSupplyAMetaclass 2.2206 Moose::Exception::MustSupplyAMooseMetaAttributeInstance 2.2206 Moose::Exception::MustSupplyAnAccessorTypeToConstructWith 2.2206 Moose::Exception::MustSupplyAnAttributeToConstructWith 2.2206 Moose::Exception::MustSupplyArrayRefAsCurriedArguments 2.2206 Moose::Exception::MustSupplyPackageNameAndName 2.2206 Moose::Exception::NeedsTypeConstraintUnionForTypeCoercionUnion 2.2206 Moose::Exception::NeitherAttributeNorAttributeNameIsGiven 2.2206 Moose::Exception::NeitherClassNorClassNameIsGiven 2.2206 Moose::Exception::NeitherRoleNorRoleNameIsGiven 2.2206 Moose::Exception::NeitherTypeNorTypeNameIsGiven 2.2206 Moose::Exception::NoAttributeFoundInSuperClass 2.2206 Moose::Exception::NoBodyToInitializeInAnAbstractBaseClass 2.2206 Moose::Exception::NoCasesMatched 2.2206 Moose::Exception::NoConstraintCheckForTypeConstraint 2.2206 Moose::Exception::NoDestructorClassSpecified 2.2206 Moose::Exception::NoImmutableTraitSpecifiedForClass 2.2206 Moose::Exception::NoParentGivenToSubtype 2.2206 Moose::Exception::OnlyInstancesCanBeCloned 2.2206 Moose::Exception::OperatorIsRequired 2.2206 Moose::Exception::OverloadConflictInSummation 2.2206 Moose::Exception::OverloadRequiresAMetaClass 2.2206 Moose::Exception::OverloadRequiresAMetaMethod 2.2206 Moose::Exception::OverloadRequiresAMetaOverload 2.2206 Moose::Exception::OverloadRequiresAMethodNameOrCoderef 2.2206 Moose::Exception::OverloadRequiresAnOperator 2.2206 Moose::Exception::OverloadRequiresNamesForCoderef 2.2206 Moose::Exception::OverrideConflictInComposition 2.2206 Moose::Exception::OverrideConflictInSummation 2.2206 Moose::Exception::PackageDoesNotUseMooseExporter 2.2206 Moose::Exception::PackageNameAndNameParamsNotGivenToWrap 2.2206 Moose::Exception::PackagesAndModulesAreNotCachable 2.2206 Moose::Exception::ParameterIsNotSubtypeOfParent 2.2206 Moose::Exception::ReferencesAreNotAllowedAsDefault 2.2206 Moose::Exception::RequiredAttributeLacksInitialization 2.2206 Moose::Exception::RequiredAttributeNeedsADefault 2.2206 Moose::Exception::RequiredMethodsImportedByClass 2.2206 Moose::Exception::RequiredMethodsNotImplementedByClass 2.2206 Moose::Exception::Role::Attribute 2.2206 Moose::Exception::Role::AttributeName 2.2206 Moose::Exception::Role::Class 2.2206 Moose::Exception::Role::EitherAttributeOrAttributeName 2.2206 Moose::Exception::Role::Instance 2.2206 Moose::Exception::Role::InstanceClass 2.2206 Moose::Exception::Role::InvalidAttributeOptions 2.2206 Moose::Exception::Role::Method 2.2206 Moose::Exception::Role::ParamsHash 2.2206 Moose::Exception::Role::Role 2.2206 Moose::Exception::Role::RoleForCreate 2.2206 Moose::Exception::Role::RoleForCreateMOPClass 2.2206 Moose::Exception::Role::TypeConstraint 2.2206 Moose::Exception::RoleDoesTheExcludedRole 2.2206 Moose::Exception::RoleExclusionConflict 2.2206 Moose::Exception::RoleNameRequired 2.2206 Moose::Exception::RoleNameRequiredForMooseMetaRole 2.2206 Moose::Exception::RolesDoNotSupportAugment 2.2206 Moose::Exception::RolesDoNotSupportExtends 2.2206 Moose::Exception::RolesDoNotSupportInner 2.2206 Moose::Exception::RolesDoNotSupportRegexReferencesForMethodModifiers 2.2206 Moose::Exception::RolesInCreateTakesAnArrayRef 2.2206 Moose::Exception::RolesListMustBeInstancesOfMooseMetaRole 2.2206 Moose::Exception::SingleParamsToNewMustBeHashRef 2.2206 Moose::Exception::TriggerMustBeACodeRef 2.2206 Moose::Exception::TypeConstraintCannotBeUsedForAParameterizableType 2.2206 Moose::Exception::TypeConstraintIsAlreadyCreated 2.2206 Moose::Exception::TypeParameterMustBeMooseMetaType 2.2206 Moose::Exception::UnableToCanonicalizeHandles 2.2206 Moose::Exception::UnableToCanonicalizeNonRolePackage 2.2206 Moose::Exception::UnableToRecognizeDelegateMetaclass 2.2206 Moose::Exception::UndefinedHashKeysPassedToMethod 2.2206 Moose::Exception::UnionCalledWithAnArrayRefAndAdditionalArgs 2.2206 Moose::Exception::UnionTakesAtleastTwoTypeNames 2.2206 Moose::Exception::ValidationFailedForInlineTypeConstraint 2.2206 Moose::Exception::ValidationFailedForTypeConstraint 2.2206 Moose::Exception::WrapTakesACodeRefToBless 2.2206 Moose::Exception::WrongTypeConstraintGiven 2.2206 Moose::Exporter 2.2206 Moose::Intro 2.2206 Moose::Manual 2.2206 Moose::Manual::Attributes 2.2206 Moose::Manual::BestPractices 2.2206 Moose::Manual::Classes 2.2206 Moose::Manual::Concepts 2.2206 Moose::Manual::Construction 2.2206 Moose::Manual::Contributing 2.2206 Moose::Manual::Delegation 2.2206 Moose::Manual::Delta 2.2206 Moose::Manual::Exceptions 2.2206 Moose::Manual::Exceptions::Manifest 2.2206 Moose::Manual::FAQ 2.2206 Moose::Manual::MOP 2.2206 Moose::Manual::MethodModifiers 2.2206 Moose::Manual::MooseX 2.2206 Moose::Manual::Resources 2.2206 Moose::Manual::Roles 2.2206 Moose::Manual::Support 2.2206 Moose::Manual::Types 2.2206 Moose::Manual::Unsweetened 2.2206 Moose::Meta::Attribute 2.2206 Moose::Meta::Attribute::Native 2.2206 Moose::Meta::Attribute::Native::Trait 2.2206 Moose::Meta::Attribute::Native::Trait::Array 2.2206 Moose::Meta::Attribute::Native::Trait::Bool 2.2206 Moose::Meta::Attribute::Native::Trait::Code 2.2206 Moose::Meta::Attribute::Native::Trait::Counter 2.2206 Moose::Meta::Attribute::Native::Trait::Hash 2.2206 Moose::Meta::Attribute::Native::Trait::Number 2.2206 Moose::Meta::Attribute::Native::Trait::String 2.2206 Moose::Meta::Class 2.2206 Moose::Meta::Class::Immutable::Trait 2.2206 Moose::Meta::Instance 2.2206 Moose::Meta::Method 2.2206 Moose::Meta::Method::Accessor 2.2206 Moose::Meta::Method::Accessor::Native 2.2206 Moose::Meta::Method::Accessor::Native::Array 2.2206 Moose::Meta::Method::Accessor::Native::Array::Writer 2.2206 Moose::Meta::Method::Accessor::Native::Array::accessor 2.2206 Moose::Meta::Method::Accessor::Native::Array::clear 2.2206 Moose::Meta::Method::Accessor::Native::Array::count 2.2206 Moose::Meta::Method::Accessor::Native::Array::delete 2.2206 Moose::Meta::Method::Accessor::Native::Array::elements 2.2206 Moose::Meta::Method::Accessor::Native::Array::first 2.2206 Moose::Meta::Method::Accessor::Native::Array::first_index 2.2206 Moose::Meta::Method::Accessor::Native::Array::get 2.2206 Moose::Meta::Method::Accessor::Native::Array::grep 2.2206 Moose::Meta::Method::Accessor::Native::Array::insert 2.2206 Moose::Meta::Method::Accessor::Native::Array::is_empty 2.2206 Moose::Meta::Method::Accessor::Native::Array::join 2.2206 Moose::Meta::Method::Accessor::Native::Array::map 2.2206 Moose::Meta::Method::Accessor::Native::Array::natatime 2.2206 Moose::Meta::Method::Accessor::Native::Array::pop 2.2206 Moose::Meta::Method::Accessor::Native::Array::push 2.2206 Moose::Meta::Method::Accessor::Native::Array::reduce 2.2206 Moose::Meta::Method::Accessor::Native::Array::set 2.2206 Moose::Meta::Method::Accessor::Native::Array::shallow_clone 2.2206 Moose::Meta::Method::Accessor::Native::Array::shift 2.2206 Moose::Meta::Method::Accessor::Native::Array::shuffle 2.2206 Moose::Meta::Method::Accessor::Native::Array::sort 2.2206 Moose::Meta::Method::Accessor::Native::Array::sort_in_place 2.2206 Moose::Meta::Method::Accessor::Native::Array::splice 2.2206 Moose::Meta::Method::Accessor::Native::Array::uniq 2.2206 Moose::Meta::Method::Accessor::Native::Array::unshift 2.2206 Moose::Meta::Method::Accessor::Native::Bool::not 2.2206 Moose::Meta::Method::Accessor::Native::Bool::set 2.2206 Moose::Meta::Method::Accessor::Native::Bool::toggle 2.2206 Moose::Meta::Method::Accessor::Native::Bool::unset 2.2206 Moose::Meta::Method::Accessor::Native::Code::execute 2.2206 Moose::Meta::Method::Accessor::Native::Code::execute_method 2.2206 Moose::Meta::Method::Accessor::Native::Collection 2.2206 Moose::Meta::Method::Accessor::Native::Counter::Writer 2.2206 Moose::Meta::Method::Accessor::Native::Counter::dec 2.2206 Moose::Meta::Method::Accessor::Native::Counter::inc 2.2206 Moose::Meta::Method::Accessor::Native::Counter::reset 2.2206 Moose::Meta::Method::Accessor::Native::Counter::set 2.2206 Moose::Meta::Method::Accessor::Native::Hash 2.2206 Moose::Meta::Method::Accessor::Native::Hash::Writer 2.2206 Moose::Meta::Method::Accessor::Native::Hash::accessor 2.2206 Moose::Meta::Method::Accessor::Native::Hash::clear 2.2206 Moose::Meta::Method::Accessor::Native::Hash::count 2.2206 Moose::Meta::Method::Accessor::Native::Hash::defined 2.2206 Moose::Meta::Method::Accessor::Native::Hash::delete 2.2206 Moose::Meta::Method::Accessor::Native::Hash::elements 2.2206 Moose::Meta::Method::Accessor::Native::Hash::exists 2.2206 Moose::Meta::Method::Accessor::Native::Hash::get 2.2206 Moose::Meta::Method::Accessor::Native::Hash::is_empty 2.2206 Moose::Meta::Method::Accessor::Native::Hash::keys 2.2206 Moose::Meta::Method::Accessor::Native::Hash::kv 2.2206 Moose::Meta::Method::Accessor::Native::Hash::set 2.2206 Moose::Meta::Method::Accessor::Native::Hash::shallow_clone 2.2206 Moose::Meta::Method::Accessor::Native::Hash::values 2.2206 Moose::Meta::Method::Accessor::Native::Number::abs 2.2206 Moose::Meta::Method::Accessor::Native::Number::add 2.2206 Moose::Meta::Method::Accessor::Native::Number::div 2.2206 Moose::Meta::Method::Accessor::Native::Number::mod 2.2206 Moose::Meta::Method::Accessor::Native::Number::mul 2.2206 Moose::Meta::Method::Accessor::Native::Number::set 2.2206 Moose::Meta::Method::Accessor::Native::Number::sub 2.2206 Moose::Meta::Method::Accessor::Native::Reader 2.2206 Moose::Meta::Method::Accessor::Native::String::append 2.2206 Moose::Meta::Method::Accessor::Native::String::chomp 2.2206 Moose::Meta::Method::Accessor::Native::String::chop 2.2206 Moose::Meta::Method::Accessor::Native::String::clear 2.2206 Moose::Meta::Method::Accessor::Native::String::inc 2.2206 Moose::Meta::Method::Accessor::Native::String::length 2.2206 Moose::Meta::Method::Accessor::Native::String::match 2.2206 Moose::Meta::Method::Accessor::Native::String::prepend 2.2206 Moose::Meta::Method::Accessor::Native::String::replace 2.2206 Moose::Meta::Method::Accessor::Native::String::substr 2.2206 Moose::Meta::Method::Accessor::Native::Writer 2.2206 Moose::Meta::Method::Augmented 2.2206 Moose::Meta::Method::Constructor 2.2206 Moose::Meta::Method::Delegation 2.2206 Moose::Meta::Method::Destructor 2.2206 Moose::Meta::Method::Meta 2.2206 Moose::Meta::Method::Overridden 2.2206 Moose::Meta::Mixin::AttributeCore 2.2206 Moose::Meta::Object::Trait 2.2206 Moose::Meta::Role 2.2206 Moose::Meta::Role::Application 2.2206 Moose::Meta::Role::Application::RoleSummation 2.2206 Moose::Meta::Role::Application::ToClass 2.2206 Moose::Meta::Role::Application::ToInstance 2.2206 Moose::Meta::Role::Application::ToRole 2.2206 Moose::Meta::Role::Attribute 2.2206 Moose::Meta::Role::Composite 2.2206 Moose::Meta::Role::Method 2.2206 Moose::Meta::Role::Method::Conflicting 2.2206 Moose::Meta::Role::Method::Required 2.2206 Moose::Meta::TypeCoercion 2.2206 Moose::Meta::TypeCoercion::Union 2.2206 Moose::Meta::TypeConstraint 2.2206 Moose::Meta::TypeConstraint::Class 2.2206 Moose::Meta::TypeConstraint::DuckType 2.2206 Moose::Meta::TypeConstraint::Enum 2.2206 Moose::Meta::TypeConstraint::Parameterizable 2.2206 Moose::Meta::TypeConstraint::Parameterized 2.2206 Moose::Meta::TypeConstraint::Registry 2.2206 Moose::Meta::TypeConstraint::Role 2.2206 Moose::Meta::TypeConstraint::Union 2.2206 Moose::Object 2.2206 Moose::Role 2.2206 Moose::Spec::Role 2.2206 Moose::Unsweetened 2.2206 Moose::Util 2.2206 Moose::Util::MetaRole 2.2206 Moose::Util::TypeConstraints 2.2206 Moose::Util::TypeConstraints::Builtins 2.2206 Test::Moose 2.2206 metaclass 2.2206 oose 2.2206 requirements: Carp 1.22 Class::Load 0.09 Class::Load::XS 0.01 Data::OptList 0.107 Devel::GlobalDestruction 0 Devel::OverloadInfo 0.005 Devel::StackTrace 2.03 Dist::CheckConflicts 0.02 Eval::Closure 0.04 ExtUtils::MakeMaker 0 List::Util 1.56 MRO::Compat 0.05 Module::Runtime 0.014 Module::Runtime::Conflicts 0.002 Package::DeprecationManager 0.11 Package::Stash 0.32 Package::Stash::XS 0.24 Params::Util 1.00 Scalar::Util 1.19 Sub::Exporter 0.980 Sub::Util 1.40 Try::Tiny 0.17 parent 0.223 strict 1.03 warnings 1.03 Mozilla-CA-20231213 pathname: L/LW/LWP/Mozilla-CA-20231213.tar.gz provides: Mozilla::CA 20231213 requirements: ExtUtils::MakeMaker 0 Net-Abuse-Utils-Spamhaus-0.09 pathname: S/SA/SAXJAZMAN/net/Net-Abuse-Utils-Spamhaus-0.09.tar.gz provides: Net::Abuse::Utils::Spamhaus 0.09 requirements: ExtUtils::MakeMaker 6.59 Net::DNS 0.74 perl 5.008008 Net-DNS-1.41 pathname: N/NL/NLNETLABS/Net-DNS-1.41.tar.gz provides: Net::DNS 1.41 Net::DNS::Domain 1913 Net::DNS::DomainName 1898 Net::DNS::DomainName1035 1898 Net::DNS::DomainName2535 1898 Net::DNS::Header 1910 Net::DNS::Mailbox 1910 Net::DNS::Mailbox1035 1910 Net::DNS::Mailbox2535 1910 Net::DNS::Nameserver 1949 Net::DNS::Packet 1947 Net::DNS::Parameters 1945 Net::DNS::Question 1895 Net::DNS::RR 1910 Net::DNS::RR::A 1896 Net::DNS::RR::AAAA 1896 Net::DNS::RR::AFSDB 1945 Net::DNS::RR::AMTRELAY 1896 Net::DNS::RR::APL 1896 Net::DNS::RR::APL::Item 1896 Net::DNS::RR::CAA 1910 Net::DNS::RR::CDNSKEY 1909 Net::DNS::RR::CDS 1909 Net::DNS::RR::CERT 1896 Net::DNS::RR::CNAME 1896 Net::DNS::RR::CSYNC 1910 Net::DNS::RR::DHCID 1896 Net::DNS::RR::DNAME 1896 Net::DNS::RR::DNSKEY 1910 Net::DNS::RR::DS 1909 Net::DNS::RR::EUI48 1896 Net::DNS::RR::EUI64 1896 Net::DNS::RR::GPOS 1910 Net::DNS::RR::HINFO 1896 Net::DNS::RR::HIP 1896 Net::DNS::RR::HTTPS 1945 Net::DNS::RR::IPSECKEY 1909 Net::DNS::RR::ISDN 1896 Net::DNS::RR::KEY 1896 Net::DNS::RR::KX 1945 Net::DNS::RR::L32 1896 Net::DNS::RR::L64 1896 Net::DNS::RR::LOC 1896 Net::DNS::RR::LP 1896 Net::DNS::RR::MB 1910 Net::DNS::RR::MG 1910 Net::DNS::RR::MINFO 1896 Net::DNS::RR::MR 1910 Net::DNS::RR::MX 1945 Net::DNS::RR::NAPTR 1898 Net::DNS::RR::NID 1896 Net::DNS::RR::NS 1896 Net::DNS::RR::NSEC 1945 Net::DNS::RR::NSEC3 1910 Net::DNS::RR::NSEC3PARAM 1896 Net::DNS::RR::NULL 1896 Net::DNS::RR::OPENPGPKEY 1896 Net::DNS::RR::OPT 1934 Net::DNS::RR::OPT::CHAIN 1934 Net::DNS::RR::OPT::CLIENT_SUBNET 1934 Net::DNS::RR::OPT::COOKIE 1934 Net::DNS::RR::OPT::DAU 1934 Net::DNS::RR::OPT::DHU 1934 Net::DNS::RR::OPT::EXPIRE 1934 Net::DNS::RR::OPT::EXTENDED_ERROR 1934 Net::DNS::RR::OPT::KEY_TAG 1934 Net::DNS::RR::OPT::N3U 1934 Net::DNS::RR::OPT::NSID 1934 Net::DNS::RR::OPT::PADDING 1934 Net::DNS::RR::OPT::REPORT_CHANNEL 1934 Net::DNS::RR::OPT::TCP_KEEPALIVE 1934 Net::DNS::RR::PTR 1896 Net::DNS::RR::PX 1945 Net::DNS::RR::RP 1945 Net::DNS::RR::RRSIG 1896 Net::DNS::RR::RT 1945 Net::DNS::RR::SIG 1908 Net::DNS::RR::SMIMEA 1896 Net::DNS::RR::SOA 1945 Net::DNS::RR::SPF 1896 Net::DNS::RR::SRV 1945 Net::DNS::RR::SSHFP 1896 Net::DNS::RR::SVCB 1945 Net::DNS::RR::TKEY 1908 Net::DNS::RR::TLSA 1896 Net::DNS::RR::TSIG 1909 Net::DNS::RR::TXT 1911 Net::DNS::RR::URI 1896 Net::DNS::RR::X25 1896 Net::DNS::RR::ZONEMD 1896 Net::DNS::Resolver 1895 Net::DNS::Resolver::Base 1947 Net::DNS::Resolver::MSWin32 1856 Net::DNS::Resolver::Recurse 1930 Net::DNS::Resolver::UNIX 1856 Net::DNS::Resolver::android 1856 Net::DNS::Resolver::cygwin 1856 Net::DNS::Resolver::os2 1856 Net::DNS::Resolver::os390 1856 Net::DNS::Text 1894 Net::DNS::Update 1895 Net::DNS::ZoneFile 1910 Net::DNS::ZoneFile::Generator 1910 Net::DNS::ZoneFile::Text 1910 requirements: Carp 1.1 Digest::HMAC 1.03 Digest::MD5 2.13 Digest::SHA 5.23 Encode 2.26 Exporter 5.63 ExtUtils::MakeMaker 6.48 File::Spec 3.29 Getopt::Long 2.43 IO::File 1.14 IO::Select 1.17 IO::Socket 1.3 IO::Socket::IP 0.38 MIME::Base64 2.13 PerlIO 1.05 Scalar::Util 1.19 Time::Local 1.19 perl 5.008009 Net-Domain-TLD-1.75 pathname: A/AL/ALEXP/Net-Domain-TLD-1.75.tar.gz provides: Net::Domain::TLD 1.75 requirements: Carp 0 ExtUtils::MakeMaker 0 Storable 0 Net-Google-SafeBrowsing4-0.8 pathname: J/JS/JSOBRIER/Net-Google-SafeBrowsing4-0.8.tar.gz provides: Net::Google::SafeBrowsing4 0.8 Net::Google::SafeBrowsing4::Storage 0.3 Net::Google::SafeBrowsing4::Storage::File 0.4 Net::Google::SafeBrowsing4::URI undef requirements: Carp 0 Digest::SHA 0 Exporter 0 ExtUtils::MakeMaker 0 HTTP::Message 0 JSON::XS 0 LWP::UserAgent 0 List::BinarySearch 0 List::Util 0 MIME::Base64 0 Net::IP::Lite 0 Path::Tiny 0 Storable 0 Test::LWP::UserAgent 0 Test::More 0.92 Test::Pod::Coverage 0 Text::Trim 0 Time::HiRes 0 URI 1.61 constant 0 Net-HTTP-6.23 pathname: O/OA/OALDERS/Net-HTTP-6.23.tar.gz provides: Net::HTTP 6.23 Net::HTTP::Methods 6.23 Net::HTTP::NB 6.23 Net::HTTPS 6.23 requirements: Carp 0 Compress::Raw::Zlib 0 ExtUtils::MakeMaker 0 IO::Socket::INET 0 IO::Uncompress::Gunzip 0 URI 0 base 0 perl 5.006002 strict 0 warnings 0 Net-IP-Lite-0.03 pathname: A/AL/ALEXKOM/Net-IP-Lite-0.03.tar.gz provides: Net::IP::Lite 0.03 Net::IP::Lite::Net 0.03 requirements: ExtUtils::MakeMaker 0 Test::Exception 0 Test::More 0 Net-SSLeay-1.92 pathname: C/CH/CHRISN/Net-SSLeay-1.92.tar.gz provides: Net::SSLeay 1.92 Net::SSLeay::Handle 1.92 requirements: English 0 ExtUtils::MakeMaker 0 File::Spec::Functions 0 MIME::Base64 0 Text::Wrap 0 constant 0 perl 5.008001 NetAddr-IP-4.079 pathname: M/MI/MIKER/NetAddr-IP-4.079.tar.gz provides: NetAddr::IP 4.079 NetAddr::IP::InetBase 0.08 NetAddr::IP::Lite 1.57 NetAddr::IP::Util 1.53 NetAddr::IP::UtilPP 1.09 NetAddr::IP::UtilPolluted 1.53 NetAddr::IP::Util_IS 1 requirements: ExtUtils::MakeMaker 0 Test::More 0 Package-DeprecationManager-0.18 pathname: D/DR/DROLSKY/Package-DeprecationManager-0.18.tar.gz provides: Package::DeprecationManager 0.18 requirements: Carp 0 ExtUtils::MakeMaker 0 List::Util 1.33 Package::Stash 0 Params::Util 0 Sub::Install 0 Sub::Util 0 strict 0 warnings 0 Package-Stash-0.40 pathname: E/ET/ETHER/Package-Stash-0.40.tar.gz provides: Package::Stash 0.40 Package::Stash::PP 0.40 requirements: B 0 Carp 0 Dist::CheckConflicts 0.02 ExtUtils::MakeMaker 0 Getopt::Long 0 Module::Implementation 0.06 Package::Stash::XS 0.26 Scalar::Util 0 Symbol 0 Text::ParseWords 0 constant 0 perl 5.008001 strict 0 warnings 0 Package-Stash-XS-0.30 pathname: E/ET/ETHER/Package-Stash-XS-0.30.tar.gz provides: Package::Stash::XS 0.30 requirements: ExtUtils::MakeMaker 0 XSLoader 0 perl 5.008001 strict 0 warnings 0 PadWalker-2.5 pathname: R/RO/ROBIN/PadWalker-2.5.tar.gz provides: PadWalker 2.5 requirements: ExtUtils::MakeMaker 0 perl 5.008001 Params-Util-1.102 pathname: R/RE/REHSACK/Params-Util-1.102.tar.gz provides: Params::Util 1.102 Params::Util::PP 1.102 requirements: Carp 0 ExtUtils::MakeMaker 0 File::Basename 0 File::Copy 0 File::Path 0 File::Spec 0 IPC::Cmd 0 Scalar::Util 1.18 XSLoader 0.22 parent 0 Path-Tiny-0.144 pathname: D/DA/DAGOLDEN/Path-Tiny-0.144.tar.gz provides: Path::Tiny 0.144 Path::Tiny::Error 0.144 requirements: Carp 0 Cwd 0 Digest 1.03 Digest::SHA 5.45 Encode 0 Exporter 5.57 ExtUtils::MakeMaker 6.17 Fcntl 0 File::Compare 0 File::Copy 0 File::Glob 0 File::Path 2.07 File::Spec 0.86 File::Temp 0.19 File::stat 0 constant 0 overload 0 perl 5.008001 strict 0 warnings 0 warnings::register 0 Pod-Coverage-0.23 pathname: R/RC/RCLAMP/Pod-Coverage-0.23.tar.gz provides: Pod::Coverage 0.23 Pod::Coverage::CountParents undef Pod::Coverage::ExportOnly undef Pod::Coverage::Extractor 0.23 Pod::Coverage::Overloader undef requirements: Devel::Symdump 2.01 ExtUtils::MakeMaker 0 Pod::Find 0.21 Pod::Parser 1.13 Test::More 0 Pod-Parser-1.66 pathname: M/MA/MAREKR/Pod-Parser-1.66.tar.gz provides: Pod::Cache 1.66 Pod::Cache::Item 1.66 Pod::Find 1.66 Pod::Hyperlink 1.66 Pod::InputObjects 1.66 Pod::InputSource 1.66 Pod::InteriorSequence 1.66 Pod::List 1.66 Pod::Paragraph 1.66 Pod::ParseTree 1.66 Pod::ParseUtils 1.66 Pod::Parser 1.66 Pod::PlainText 2.07 Pod::Select 1.66 requirements: Cwd 0 ExtUtils::MakeMaker 0 File::Basename 0 Test::More 0.6 Role-Tiny-2.002004 pathname: H/HA/HAARG/Role-Tiny-2.002004.tar.gz provides: Role::Tiny 2.002004 Role::Tiny::With 2.002004 requirements: Exporter 5.57 perl 5.006 SQL-Abstract-2.000001 pathname: M/MS/MSTROUT/SQL-Abstract-2.000001.tar.gz provides: Chunkstrumenter undef DBIx::Class::SQLMaker::Role::SQLA2Passthrough undef SQL::Abstract 2.000001 SQL::Abstract::Formatter undef SQL::Abstract::Parts undef SQL::Abstract::Plugin::BangOverrides undef SQL::Abstract::Plugin::ExtraClauses undef SQL::Abstract::Reference undef SQL::Abstract::Role::Plugin undef SQL::Abstract::Test undef SQL::Abstract::Tree undef requirements: Exporter 5.57 ExtUtils::MakeMaker 0 Hash::Merge 0.12 List::Util 0 MRO::Compat 0.12 Moo 2.000001 Scalar::Util 0 Sub::Quote 2.000001 Test::Builder::Module 0.84 Test::Deep 0.101 Text::Balanced 2.00 perl 5.006 SQL-Abstract-Pg-1.0 pathname: S/SR/SRI/SQL-Abstract-Pg-1.0.tar.gz provides: SQL::Abstract::Pg 1.0 requirements: ExtUtils::MakeMaker 0 SQL::Abstract 2.0 perl 5.016 Safe-Isa-1.000010 pathname: E/ET/ETHER/Safe-Isa-1.000010.tar.gz provides: Safe::Isa 1.000010 requirements: Exporter 5.57 ExtUtils::MakeMaker 0 Scalar::Util 0 perl 5.006 String-CRC32-2.100 pathname: L/LE/LEEJO/String-CRC32-2.100.tar.gz provides: String::CRC32 2.100 requirements: ExtUtils::MakeMaker 0 String-RewritePrefix-0.009 pathname: R/RJ/RJBS/String-RewritePrefix-0.009.tar.gz provides: String::RewritePrefix 0.009 requirements: Carp 0 ExtUtils::MakeMaker 6.78 Sub::Exporter 0.972 perl 5.012 strict 0 warnings 0 Sub-Exporter-0.991 pathname: R/RJ/RJBS/Sub-Exporter-0.991.tar.gz provides: Sub::Exporter 0.991 Sub::Exporter::Util 0.991 requirements: Carp 0 Data::OptList 0.100 ExtUtils::MakeMaker 6.78 Params::Util 0.14 Sub::Install 0.92 perl 5.012 strict 0 warnings 0 Sub-Exporter-Progressive-0.001013 pathname: F/FR/FREW/Sub-Exporter-Progressive-0.001013.tar.gz provides: Sub::Exporter::Progressive 0.001013 requirements: ExtUtils::MakeMaker 0 Sub-Install-0.929 pathname: R/RJ/RJBS/Sub-Install-0.929.tar.gz provides: Sub::Install 0.929 requirements: B 0 Carp 0 ExtUtils::MakeMaker 6.78 Scalar::Util 0 perl 5.008000 strict 0 warnings 0 Sub-Quote-2.006008 pathname: H/HA/HAARG/Sub-Quote-2.006008.tar.gz provides: Sub::Defer 2.006008 Sub::Quote 2.006008 requirements: ExtUtils::MakeMaker 0 Scalar::Util 0 perl 5.006 Sub-Uplevel-0.2800 pathname: D/DA/DAGOLDEN/Sub-Uplevel-0.2800.tar.gz provides: Sub::Uplevel 0.2800 requirements: Carp 0 ExtUtils::MakeMaker 6.17 constant 0 perl 5.006 strict 0 warnings 0 Task-Weaken-1.06 pathname: E/ET/ETHER/Task-Weaken-1.06.tar.gz provides: Task::Weaken 1.06 requirements: Config 0 ExtUtils::MakeMaker 0 File::Spec 0 Scalar::Util 1.14 perl 5.006 strict 0 Term-ProgressBar-2.23 pathname: M/MA/MANWAR/Term-ProgressBar-2.23.tar.gz provides: Term::ProgressBar 2.23 Term::ProgressBar::IO 2.23 requirements: Capture::Tiny 0.13 Carp 0 Class::MethodMaker 1.02 ExtUtils::MakeMaker 0 Fatal 0 File::Temp 0 POSIX 0 Term::ReadKey 2.14 Test::Exception 0.31 Test::More 0.80 Test::Warnings 0 perl 5.006 Term-ProgressBar-Quiet-0.31 pathname: L/LB/LBROCARD/Term-ProgressBar-Quiet-0.31.tar.gz provides: Term::ProgressBar::Quiet 0.31 requirements: ExtUtils::MakeMaker 0 IO::Interactive 0 Term::ProgressBar 0 Test::MockObject 0 Test::More 0 TermReadKey-2.38 pathname: J/JS/JSTOWE/TermReadKey-2.38.tar.gz provides: Term::ReadKey 2.38 requirements: ExtUtils::MakeMaker 6.58 Test-Deep-1.204 pathname: R/RJ/RJBS/Test-Deep-1.204.tar.gz provides: Test::Deep 1.204 Test::Deep::All 1.204 Test::Deep::Any 1.204 Test::Deep::Array 1.204 Test::Deep::ArrayEach 1.204 Test::Deep::ArrayElementsOnly 1.204 Test::Deep::ArrayLength 1.204 Test::Deep::ArrayLengthOnly 1.204 Test::Deep::Blessed 1.204 Test::Deep::Boolean 1.204 Test::Deep::Cache 1.204 Test::Deep::Cache::Simple 1.204 Test::Deep::Class 1.204 Test::Deep::Cmp 1.204 Test::Deep::Code 1.204 Test::Deep::Hash 1.204 Test::Deep::HashEach 1.204 Test::Deep::HashElements 1.204 Test::Deep::HashKeys 1.204 Test::Deep::HashKeysOnly 1.204 Test::Deep::Ignore 1.204 Test::Deep::Isa 1.204 Test::Deep::ListMethods 1.204 Test::Deep::MM 1.204 Test::Deep::Methods 1.204 Test::Deep::NoTest 1.204 Test::Deep::None 1.204 Test::Deep::Number 1.204 Test::Deep::Obj 1.204 Test::Deep::Ref 1.204 Test::Deep::RefType 1.204 Test::Deep::Regexp 1.204 Test::Deep::RegexpMatches 1.204 Test::Deep::RegexpOnly 1.204 Test::Deep::RegexpRef 1.204 Test::Deep::RegexpRefOnly 1.204 Test::Deep::RegexpVersion 1.204 Test::Deep::ScalarRef 1.204 Test::Deep::ScalarRefOnly 1.204 Test::Deep::Set 1.204 Test::Deep::Shallow 1.204 Test::Deep::Stack 1.204 Test::Deep::String 1.204 Test::Deep::SubHash 1.204 Test::Deep::SubHashElements 1.204 Test::Deep::SubHashKeys 1.204 Test::Deep::SubHashKeysOnly 1.204 Test::Deep::SuperHash 1.204 Test::Deep::SuperHashElements 1.204 Test::Deep::SuperHashKeys 1.204 Test::Deep::SuperHashKeysOnly 1.204 requirements: ExtUtils::MakeMaker 6.78 List::Util 1.09 Scalar::Util 1.09 Test::Builder 0 Test::More 0.96 perl 5.012 Test-Exception-0.43 pathname: E/EX/EXODIST/Test-Exception-0.43.tar.gz provides: Test::Exception 0.43 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 0 Sub::Uplevel 0.18 Test::Builder 0.7 Test::Builder::Tester 1.07 Test::Harness 2.03 base 0 perl 5.006001 strict 0 warnings 0 Test-Fatal-0.017 pathname: R/RJ/RJBS/Test-Fatal-0.017.tar.gz provides: Test::Fatal 0.017 requirements: Carp 0 Exporter 5.57 ExtUtils::MakeMaker 6.78 Test::Builder 0 Try::Tiny 0.07 strict 0 warnings 0 Test-LWP-UserAgent-0.036 pathname: E/ET/ETHER/Test-LWP-UserAgent-0.036.tar.gz provides: Test::LWP::UserAgent 0.036 requirements: CPAN::Meta::Requirements 2.120620 Carp 0 ExtUtils::MakeMaker 0 HTTP::Date 0 HTTP::Request 0 HTTP::Response 0 HTTP::Status 0 LWP::UserAgent 0 Module::Metadata 0 Safe::Isa 0 Scalar::Util 0 Storable 0 Try::Tiny 0 URI 1.62 namespace::clean 0.19 parent 0 perl 5.006 strict 0 warnings 0 Test-Memory-Cycle-1.06 pathname: P/PE/PETDANCE/Test-Memory-Cycle-1.06.tar.gz provides: Test::Memory::Cycle 1.06 requirements: Devel::Cycle 1.07 ExtUtils::MakeMaker 0 Getopt::Long 0 PadWalker 0 Test::Builder 0 Test::Builder::Tester 0 Test::More 0 Test::Simple 0.62 Test-MockObject-1.20200122 pathname: C/CH/CHROMATIC/Test-MockObject-1.20200122.tar.gz provides: Test::MockObject 1.20200122 Test::MockObject::Extends 1.20200122 requirements: Carp 0 Devel::Peek 0 ExtUtils::MakeMaker 0 Scalar::Util 0 Test::Builder 0 UNIVERSAL::can 1.20110617 UNIVERSAL::isa 1.20110614 constant 0 perl 5.008 strict 0 warnings 0 Test-Output-1.034 pathname: B/BD/BDFOY/Test-Output-1.034.tar.gz provides: Test::Output 1.034 requirements: Capture::Tiny 0.17 ExtUtils::MakeMaker 6.64 File::Spec::Functions 0 File::Temp 0.17 perl 5.008 Test-Pod-Coverage-1.10 pathname: N/NE/NEILB/Test-Pod-Coverage-1.10.tar.gz provides: Test::Pod::Coverage 1.10 requirements: ExtUtils::MakeMaker 0 Pod::Coverage 0 Test::Builder 0 perl 5.006 strict 0 warnings 0 Test-Warnings-0.032 pathname: E/ET/ETHER/Test-Warnings-0.032.tar.gz provides: Test::Warnings 0.032 requirements: Carp 0 Exporter 0 ExtUtils::MakeMaker 0 Test::Builder 0 parent 0 perl 5.006 strict 0 warnings 0 Text-Soundex-3.05 pathname: R/RJ/RJBS/Text-Soundex-3.05.tar.gz provides: Text::Soundex 3.05 requirements: ExtUtils::MakeMaker 0 if 0 Text-Trim-1.04 pathname: R/RJ/RJT/Text-Trim-1.04.tar.gz provides: Text::Trim 1.04 requirements: ExtUtils::MakeMaker 0 perl 5.006 Time-Duration-1.21 pathname: N/NE/NEILB/Time-Duration-1.21.tar.gz provides: Time::Duration 1.21 requirements: Exporter 0 ExtUtils::MakeMaker 0 constant 0 perl 5.006 strict 0 warnings 0 Time-Duration-Parse-0.16 pathname: N/NE/NEILB/Time-Duration-Parse-0.16.tar.gz provides: Time::Duration::Parse 0.16 requirements: Carp 0 Exporter 5.57 ExtUtils::MakeMaker 0 perl 5.006 strict 0 warnings 0 TimeDate-2.33 pathname: A/AT/ATOOMIC/TimeDate-2.33.tar.gz provides: Date::Format 2.24 Date::Format::Generic 2.24 Date::Language 1.10 Date::Language::Afar 0.99 Date::Language::Amharic 1.00 Date::Language::Austrian 1.01 Date::Language::Brazilian 1.01 Date::Language::Bulgarian 1.01 Date::Language::Chinese 1.00 Date::Language::Chinese_GB 1.01 Date::Language::Czech 1.01 Date::Language::Danish 1.01 Date::Language::Dutch 1.02 Date::Language::English 1.01 Date::Language::Finnish 1.01 Date::Language::French 1.04 Date::Language::Gedeo 0.99 Date::Language::German 1.02 Date::Language::Greek 1.00 Date::Language::Hungarian 1.01 Date::Language::Icelandic 1.01 Date::Language::Italian 1.01 Date::Language::Norwegian 1.01 Date::Language::Occitan 1.04 Date::Language::Oromo 0.99 Date::Language::Romanian 1.01 Date::Language::Russian 1.01 Date::Language::Russian_cp1251 1.01 Date::Language::Russian_koi8r 1.01 Date::Language::Sidama 0.99 Date::Language::Somali 0.99 Date::Language::Spanish 1.00 Date::Language::Swedish 1.01 Date::Language::Tigrinya 1.00 Date::Language::TigrinyaEritrean 1.00 Date::Language::TigrinyaEthiopian 1.00 Date::Language::Turkish 1.0 Date::Parse 2.33 Time::Zone 2.24 TimeDate 1.21 requirements: ExtUtils::MakeMaker 0 Try-Tiny-0.31 pathname: E/ET/ETHER/Try-Tiny-0.31.tar.gz provides: Try::Tiny 0.31 requirements: Carp 0 Exporter 5.57 ExtUtils::MakeMaker 0 constant 0 perl 5.006 strict 0 warnings 0 Types-Serialiser-1.01 pathname: M/ML/MLEHMANN/Types-Serialiser-1.01.tar.gz provides: JSON::PP::Boolean 1.01 Types::Serialiser 1.01 Types::Serialiser::BooleanBase 1.01 Types::Serialiser::Error 1.01 requirements: ExtUtils::MakeMaker 0 common::sense 0 UNIVERSAL-can-1.20140328 pathname: C/CH/CHROMATIC/UNIVERSAL-can-1.20140328.tar.gz provides: UNIVERSAL::can 1.20140328 requirements: ExtUtils::MakeMaker 6.30 Scalar::Util 0 strict 0 vars 0 warnings 0 warnings::register 0 UNIVERSAL-isa-1.20171012 pathname: E/ET/ETHER/UNIVERSAL-isa-1.20171012.tar.gz provides: UNIVERSAL::isa 1.20171012 requirements: ExtUtils::MakeMaker 0 Scalar::Util 0 perl 5.006002 strict 0 warnings 0 warnings::register 0 URI-5.21 pathname: O/OA/OALDERS/URI-5.21.tar.gz provides: URI 5.21 URI::Escape 5.21 URI::Heuristic 5.21 URI::IRI 5.21 URI::QueryParam 5.21 URI::Split 5.21 URI::URL 5.21 URI::WithBase 5.21 URI::data 5.21 URI::file 5.21 URI::file::Base 5.21 URI::file::FAT 5.21 URI::file::Mac 5.21 URI::file::OS2 5.21 URI::file::QNX 5.21 URI::file::Unix 5.21 URI::file::Win32 5.21 URI::ftp 5.21 URI::gopher 5.21 URI::http 5.21 URI::https 5.21 URI::icap 5.21 URI::icaps 5.21 URI::ldap 5.21 URI::ldapi 5.21 URI::ldaps 5.21 URI::mailto 5.21 URI::mms 5.21 URI::news 5.21 URI::nntp 5.21 URI::nntps 5.21 URI::pop 5.21 URI::rlogin 5.21 URI::rsync 5.21 URI::rtsp 5.21 URI::rtspu 5.21 URI::sftp 5.21 URI::sip 5.21 URI::sips 5.21 URI::snews 5.21 URI::ssh 5.21 URI::telnet 5.21 URI::tn3270 5.21 URI::urn 5.21 URI::urn::isbn 5.21 URI::urn::oid 5.21 requirements: Carp 0 Cwd 0 Data::Dumper 0 Encode 0 Exporter 5.57 ExtUtils::MakeMaker 0 MIME::Base64 2 Net::Domain 0 Scalar::Util 0 constant 0 integer 0 overload 0 parent 0 perl 5.008001 strict 0 utf8 0 warnings 0 URI-Nested-0.10 pathname: D/DW/DWHEELER/URI-Nested-0.10.tar.gz provides: URI::Nested 0.10 requirements: Module::Build 0.30 Test::More 0.88 URI 1.40 perl 5.008001 URI-db-0.21 pathname: D/DW/DWHEELER/URI-db-0.21.tar.gz provides: URI::cassandra 0.21 URI::cockroach 0.21 URI::cockroachdb 0.21 URI::couch 0.21 URI::couchdb 0.21 URI::cubrid 0.21 URI::db 0.21 URI::db2 0.21 URI::derby 0.21 URI::exasol 0.21 URI::firebird 0.21 URI::hive 0.21 URI::impala 0.21 URI::informix 0.21 URI::ingres 0.21 URI::interbase 0.21 URI::ldapdb 0.21 URI::maria 0.21 URI::mariadb 0.21 URI::max 0.21 URI::maxdb 0.21 URI::monet 0.21 URI::monetdb 0.21 URI::mongo 0.21 URI::mongodb 0.21 URI::mssql 0.21 URI::mysql 0.21 URI::oracle 0.21 URI::pg 0.21 URI::pgsql 0.21 URI::pgxc 0.21 URI::postgres 0.21 URI::postgresql 0.21 URI::postgresxc 0.21 URI::redshift 0.21 URI::snowflake 0.21 URI::sqlite 0.21 URI::sqlite3 0.21 URI::sqlserver 0.21 URI::sybase 0.21 URI::teradata 0.21 URI::unify 0.21 URI::vertica 0.21 URI::yugabyte 0.21 URI::yugabytedb 0.21 requirements: Module::Build 0.30 Test::More 0.88 URI 1.40 URI::Nested 0.10 perl 5.008001 Variable-Magic-0.63 pathname: V/VP/VPIT/Variable-Magic-0.63.tar.gz provides: Variable::Magic 0.63 requirements: Carp 0 Config 0 Exporter 0 ExtUtils::MakeMaker 0 IO::Handle 0 IO::Select 0 IPC::Open3 0 POSIX 0 Socket 0 Test::More 0 XSLoader 0 base 0 lib 0 perl 5.008 WWW-RobotRules-6.02 pathname: G/GA/GAAS/WWW-RobotRules-6.02.tar.gz provides: WWW::RobotRules 6.02 WWW::RobotRules::AnyDBM_File 6.00 WWW::RobotRules::InCore 6.02 requirements: AnyDBM_File 0 ExtUtils::MakeMaker 0 Fcntl 0 URI 1.10 perl 5.008001 YAML-LibYAML-0.88 pathname: I/IN/INGY/YAML-LibYAML-0.88.tar.gz provides: YAML::LibYAML 0.88 YAML::XS 0.88 YAML::XS::LibYAML undef requirements: ExtUtils::MakeMaker 0 perl 5.008001 YAML-Tiny-1.74 pathname: E/ET/ETHER/YAML-Tiny-1.74.tar.gz provides: YAML::Tiny 1.74 requirements: B 0 Carp 0 Exporter 0 ExtUtils::MakeMaker 0 Fcntl 0 Scalar::Util 0 perl 5.008001 strict 0 warnings 0 common-sense-3.75 pathname: M/ML/MLEHMANN/common-sense-3.75.tar.gz provides: common::sense 3.75 requirements: ExtUtils::MakeMaker 0 libwww-perl-6.72 pathname: O/OA/OALDERS/libwww-perl-6.72.tar.gz provides: LWP 6.72 LWP::Authen::Basic 6.72 LWP::Authen::Digest 6.72 LWP::Authen::Ntlm 6.72 LWP::ConnCache 6.72 LWP::Debug 6.72 LWP::Debug::TraceHTTP 6.72 LWP::DebugFile 6.72 LWP::MemberMixin 6.72 LWP::Protocol 6.72 LWP::Protocol::cpan 6.72 LWP::Protocol::data 6.72 LWP::Protocol::file 6.72 LWP::Protocol::ftp 6.72 LWP::Protocol::gopher 6.72 LWP::Protocol::http 6.72 LWP::Protocol::loopback 6.72 LWP::Protocol::mailto 6.72 LWP::Protocol::nntp 6.72 LWP::Protocol::nogo 6.72 LWP::RobotUA 6.72 LWP::Simple 6.72 LWP::UserAgent 6.72 requirements: Digest::MD5 0 Encode 2.12 Encode::Locale 0 ExtUtils::MakeMaker 0 File::Copy 0 File::Listing 6 File::Temp 0 Getopt::Long 0 HTML::Entities 0 HTML::HeadParser 3.71 HTTP::CookieJar::LWP 0 HTTP::Cookies 6 HTTP::Date 6 HTTP::Negotiate 6 HTTP::Request 6.18 HTTP::Request::Common 6.18 HTTP::Response 6.18 HTTP::Status 6.18 IO::Select 0 IO::Socket 0 LWP::MediaTypes 6 MIME::Base64 2.1 Module::Load 0 Net::FTP 2.58 Net::HTTP 6.18 Scalar::Util 0 Try::Tiny 0 URI 1.10 URI::Escape 0 WWW::RobotRules 6 parent 0.217 perl 5.008001 strict 0 warnings 0 namespace-clean-0.27 pathname: R/RI/RIBASUSHI/namespace-clean-0.27.tar.gz provides: namespace::clean 0.27 requirements: B::Hooks::EndOfScope 0.12 ExtUtils::MakeMaker 0 Package::Stash 0.23 perl 5.008001 perl-ldap-0.68 pathname: M/MA/MARSCHAP/perl-ldap-0.68.tar.gz provides: Bundle::Net::LDAP 0.03 LWP::Protocol::ldap 1.25 LWP::Protocol::ldapi undef LWP::Protocol::ldaps undef Net::LDAP 0.68 Net::LDAP::ASN 0.13 Net::LDAP::Bind 1.05 Net::LDAP::Constant 0.24 Net::LDAP::Control 0.20 Net::LDAP::Control::Assertion 0.02 Net::LDAP::Control::DontUseCopy 0.02 Net::LDAP::Control::EntryChange 0.02 Net::LDAP::Control::ManageDsaIT 0.04 Net::LDAP::Control::MatchedValues 0.02 Net::LDAP::Control::NoOp 0.01 Net::LDAP::Control::Paged 0.05 Net::LDAP::Control::PasswordPolicy 0.04 Net::LDAP::Control::PersistentSearch 0.04 Net::LDAP::Control::PostRead 0.03 Net::LDAP::Control::PreRead 0.04 Net::LDAP::Control::ProxyAuth 1.09 Net::LDAP::Control::Relax 0.03 Net::LDAP::Control::Sort 0.04 Net::LDAP::Control::SortResult 0.03 Net::LDAP::Control::Subentries 0.01 Net::LDAP::Control::SyncDone 0.03 Net::LDAP::Control::SyncRequest 0.03 Net::LDAP::Control::SyncState 0.04 Net::LDAP::Control::TreeDelete 0.01 Net::LDAP::Control::VLV 0.07 Net::LDAP::Control::VLVResponse 0.04 Net::LDAP::DSML 0.17 Net::LDAP::DSML::output 0.17 Net::LDAP::DSML::pp 0.17 Net::LDAP::Entry 0.29 Net::LDAP::Extension 1.04 Net::LDAP::Extension::Cancel 0.02 Net::LDAP::Extension::Refresh 0.04 Net::LDAP::Extension::SetPassword 0.06 Net::LDAP::Extension::WhoAmI 0.02 Net::LDAP::Extra 0.02 Net::LDAP::Extra::AD 0.05 Net::LDAP::Extra::eDirectory 0.03 Net::LDAP::Filter 0.20 Net::LDAP::FilterList 0.02 Net::LDAP::FilterMatch 0.27 Net::LDAP::Intermediate 0.04 Net::LDAP::Intermediate::SyncInfo 0.03 Net::LDAP::LDIF 0.27 Net::LDAP::Message 1.12 Net::LDAP::Message::Dummy 1.12 Net::LDAP::Reference 0.14 Net::LDAP::RootDSE 0.02 Net::LDAP::Schema 0.9908 Net::LDAP::Search 0.14 Net::LDAP::Util 0.20 Net::LDAPI 0.04 Net::LDAPS 0.06 requirements: Authen::SASL 2.00 Convert::ASN1 0.2 Digest::HMAC_MD5 0 Digest::MD5 0 ExtUtils::MakeMaker 6.59 File::Basename 0 File::Compare 0 File::Path 0 HTTP::Negotiate 0 HTTP::Response 0 HTTP::Status 0 IO::File 0 IO::Socket::SSL 1.26 JSON 0 LWP 0 LWP::MediaTypes 0 LWP::Protocol 0 MIME::Base64 0 Test::More 0 Text::Soundex 0 Time::Local 0 URI::ldap 1.1 perl 5.008001 ================================================ FILE: docker/entrypoint.sh ================================================ #!/bin/sh set -eu cd ~lstu if [ "${1:-}" == "dev" ] then echo "" echo "" echo "Container started in dev mode. Connect to the container with the following command:" echo " docker-compose -f docker-compose.dev.yml exec -u root app_dev sh" echo "" echo "" echo "You can then install the build dependencies with this command" echo " apk --update add vim build-base perl-utils perl-dev make sudo zlib-dev libpng-dev postgresql-dev mariadb-dev" tail -f /dev/null exit 0 fi # If MySQL/PostgreSQL, wait for database to be up DB_TYPE=$(perl utilities/read_conf.pl dbtype) DB_HOST= DB_PORT= if [ "$DB_TYPE" == "mysql" ] then DB_HOST=$(perl utilities/read_conf.pl mysqldb/host db) DB_PORT=$(perl utilities/read_conf.pl mysqldb/port 3306) fi if [ "$DB_TYPE" == "postgresql" ] then DB_HOST=$(perl utilities/read_conf.pl pgdb/host db) DB_PORT=$(perl utilities/read_conf.pl pgdb/port 5432) fi if [ -n "$DB_HOST" -a -n "$DB_PORT" ] then while ! nc -vz "${DB_HOST}" "${DB_PORT}"; do echo "Waiting for database..." sleep 1; done fi if [ "${1:-}" == "minion" ] then exec carton exec script/application minion worker fi exec carton exec hypnotoad -f script/lstu ================================================ FILE: docker-compose.dev.yml ================================================ version: '3.3' services: app_dev: build: . ports: - 8080:8080 volumes: - .:/home/lstu command: dev mariadb_dev: image: mariadb:10.2 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: lstu postgres_dev: image: postgres:10.3-alpine environment: POSTGRES_PASSWORD: password POSTGRES_USER: lstu POSTGRES_DB: lstu memcached: image: memcached:1.5-alpine adminer: image: dehy/adminer ports: - 8081:80 ================================================ FILE: docker-compose.yml ================================================ version: '3.3' services: app: build: . ports: - 8080:8080 volumes: - ./lstu.conf:/home/lstu/lstu.conf:ro db: image: mariadb:10.2 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: lstu cache: image: memcached:1.5-alpine minion: build: . command: minion volumes: - ./lstu.conf:/home/lstu/lstu.conf:ro minion_db: image: mariadb:10.2 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: lstu_minion ================================================ FILE: docker-stack.yml ================================================ version: '3.3' services: app: image: aquinum/lstu configs: - source: lstu.conf target: /home/lstu/lstu.conf uid: '1000' gid: '1000' mode: 0440 deploy: replicas: 1 db: image: mariadb:10.2 environment: MYSQL_ROOT_PASSWORD: MYSQL_DATABASE: lstu cache: image: memcached:1.5-alpine minion: image: aquinum/lstu command: minion configs: - source: lstu.conf target: /home/lstu/lstu.conf uid: '1000' gid: '1000' mode: 0440 minion_db: image: mariadb:10.2 environment: MYSQL_ROOT_PASSWORD: MYSQL_DATABASE: lstu_minion configs: lstu.conf: file: ./lstu.conf ================================================ FILE: hooks/build ================================================ #!/bin/bash # https://medium.com/microscaling-systems/labelling-automated-builds-on-docker-hub-f3d073fb8e1#.yldbwesu7 if [ -z "${IMAGE_NAME}" ]; then echo ! "IMAGE_NAME" variable missing. echo eg. IMAGE_NAME="aquinum/lstu:local" sh $0 exit 1 fi function evil_git_dirty { [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "-dirty" } function last_tag { git describe --abbrev=0 } function version_number_plus { [[ $(git rev-list --count ${1}..HEAD) -gt 0 ]] && echo "+" } VERSION=$(last_tag) VERSION=${VERSION}$(version_number_plus ${VERSION}) VCS_REF=$(git rev-parse --short HEAD)$(evil_git_dirty) BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") docker build --build-arg VCS_REF=${VCS_REF} \ --build-arg BUILD_DATE=${BUILD_DATE} \ --build-arg VERSION=${VERSION} \ -t $IMAGE_NAME . ================================================ FILE: lib/Lstu/Command/ban.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Command::ban; use Mojo::Base 'Mojolicious::Command'; use FindBin qw($Bin); use File::Spec qw(catfile); use Mojo::Util qw(getopt); use Lstu::DB::Ban; has description => 'Ban IPs addresses for ten years, or unban them'; has usage => sub { shift->extract_usage }; sub run { my $c = shift; my @args = @_; getopt \@args, 'b|ban=s{1,}' => \my @ban_ips, 'u|unban=s{1,}' => \my @unban_ips; for my $ip (@ban_ips) { Lstu::DB::Ban->new( app => $c->app, ip => $ip )->ban_ten_years; } say sprintf("%d banned IP addresses", scalar(@ban_ips)) if (@ban_ips); for my $ip (@unban_ips) { Lstu::DB::Ban->new( app => $c->app, ip => $ip )->unban; } say sprintf("%d unbanned IP addresses", scalar(@unban_ips)) if (@unban_ips); say $c->extract_usage unless (scalar(@ban_ips) || scalar(@unban_ips)); } =encoding utf8 =head1 NAME Lstu::Command::ban - Ban IPs addresses for ten years, or unban them =head1 SYNOPSIS Usage: carton exec script/lstu ban -b|--ban Ban the space separated IP addresses for ten years carton exec script/lstu ban -u|--unban Unban the space separated IP addresses Please note that you can pass the --ban and --unban options at the same time. =cut 1; ================================================ FILE: lib/Lstu/Command/safebrowsingcheck.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Command::safebrowsingcheck; use Mojo::Base 'Mojolicious::Command'; use FindBin qw($Bin); use File::Spec qw(catfile); use Term::ProgressBar::Quiet; use Mojo::Util qw(getopt); use Mojo::Collection 'c'; use Lstu::DB::URL; use Lstu::DB::Ban; has description => 'Checks all URLs in database against Google Safe Browsing database (local copy)'; has usage => sub { shift->extract_usage }; sub run { my $c = shift; my @args = @_; getopt \@args, 'u|url=s{1,}' => \my @urls_to_check, 't|test=s{1,}' => \my @urls_to_test, 's|seconds=i' => \my $delay, 'r|remove' => \my $remove, 'a|all' => \my $all, 'b|ban' => \my $ban; if ($c->app->gsb) { my $urls; if (@urls_to_check) { $urls = c(get_shorts($c, @urls_to_check)); } elsif ($delay) { $urls = Lstu::DB::URL->new(app => $c->app)->get_all_urls_created_ago($delay); } elsif (@urls_to_test) { $urls = c(@urls_to_test); } else { $urls = Lstu::DB::URL->new(app => $c->app)->get_all_urls; } unless ($urls->size) { say 'No URLs to check.'; exit; } my $progress = Term::ProgressBar::Quiet->new( { name => 'Scanning '.$urls->size.' URLs', count => $urls->size, ETA => 'linear' } ); my (@bad, %bad_ips, @bad_from_ips); my $gsb = $c->app->gsb; my $disabled = 0; my @testing_results; $urls->each(sub { my ($e, $num) = @_; my $u = (@urls_to_test) ? $e : $e->{url}; $progress->update($num); my @matches = $gsb->lookup(url => $u); if (@matches) { if (@urls_to_test) { push @testing_results, sprintf('%s is in GSB base!', $e); } else { push @bad, $e->{short}; $bad_ips{$e->{created_by}} = 1 if $e->{created_by}; $disabled += Lstu::DB::URL->new( app => $c->app, short => $e->{short} )->remove if $remove; } } elsif (@urls_to_test) { push @testing_results, sprintf('%s is safe.', $e); } }); if (@urls_to_test) { map {say $_;} @testing_results; exit; } say sprintf('All URLs (%d) have been scanned.', $urls->size); say sprintf('%d bad URLs detected.', scalar(@bad)); if ($remove) { say sprintf('%d bad URLs disabled.', $disabled) if $disabled; } else { say sprintf("If you want to disable the detected bad URLs, please do:\n carton exec script/lstu url --remove %s", join(' ', @bad)) if @bad; } $disabled = 0; for my $ip (keys %bad_ips) { my $u = Lstu::DB::URL->new(app => $c->app)->search_creator($ip); $u->each(sub { my ($e, $num) = @_; push @bad_from_ips, $e->{short}; $disabled += Lstu::DB::URL->new( app => $c->app, short => $e->{short} )->remove if ($remove && $all); }); } my @ips = keys %bad_ips; say sprintf("Bad URLs creators' IP addresses: \n %s", join(", ", @ips)) if (@ips); if ($ban) { for my $ip (@ips) { Lstu::DB::Ban->new( app => $c->app, ip => $ip )->ban_ten_years; } say sprintf("%d banned IP addresses", scalar(@ips)) if (@ips); } if ($remove && $all) { say sprintf('%d URLs from same IPs disabled.', $disabled) if $disabled; } else { say sprintf("If you want to disable the URLs created by the same IPs than the detected bad URLs, please do:\n carton exec script/lstu url --remove %s", join(' ', @bad_from_ips)) if @bad_from_ips; } } else { say 'It seems that safebrowsing_api_key isn\'t set. Please, check your configuration'; } } sub get_shorts { my $c = shift; my @shorts = @_; my @results; for my $short (@shorts) { my $u = Lstu::DB::URL->new(app => $c->app, short => $short); if ($u->url && !$u->disabled) { push @results, $u->to_hash; } else { say sprintf('Sorry, unable to find an URL with short = %s', $short); } } return @results; } =encoding utf8 =head1 NAME Lstu::Command::safebrowsing - Checks all URLs in database against Google Safe Browsing database (local copy) =head1 SYNOPSIS Usage: carton exec script/lstu safebrowsingcheck Checks all URLs in database against Google Safe Browsing database carton exec script/lstu safebrowsingcheck -u|--url Checks the space-separated URLs against Google Safe Browsing database carton exec script/lstu safebrowsingcheck -s|--seconds Checks URLs created the last xxx seconds against Google Safe Browsing database carton exec script/lstu safebrowsingcheck -t|--test Checks URLs against Google Safe Browsing database Options (available with all commands except --test): -r|--remove Remove bad URLs that have been found -a|--all Remove all URLs created by the same IP addresses that created bad URLs (only in combination with the `-r|--remove` option) -b|--ban Ban IP addresses that created bad URLs =cut 1; ================================================ FILE: lib/Lstu/Command/theme.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Command::theme; use Mojo::Base 'Mojolicious::Commands'; use FindBin qw($Bin); use File::Spec qw(catfile cat dir); use File::Path qw(make_path); has description => 'Create new theme skeleton.'; has usage => sub { shift->extract_usage }; has message => sub { shift->extract_usage . "\nCreate new theme skeleton:\n" }; has namespaces => sub { ['Lstu::Command::theme'] }; sub run { my $c = shift; my $name = shift; unless (defined $name) { say $c->extract_usage; exit 1; } my $home = File::Spec->catdir($Bin, '..', 'themes', $name); unless (-d $home) { # Create skeleton mkdir $home; mkdir File::Spec->catdir($home, 'public'); make_path(File::Spec->catdir($home, 'templates', 'layouts')); make_path(File::Spec->catdir($home, 'lib', 'Lstu', 'I18N')); my $i18n = < 1, _decode => 1, _style => 'gettext', '*' => [ Gettext => dirname(__FILE__) . '/I18N/*.po', Gettext => $app_dir . 'themes/default/lib/Lstu/I18N/*.po', ] }; use vars qw($app_dir); BEGIN { use Cwd; my $app_dir = getcwd; } 1; EOF open my $f, '>', File::Spec->catfile($home, 'lib', 'Lstu', 'I18N.pm') or die "Unable to open $home/lib/Lstu/I18N.pm: $!"; print $f $i18n; close $f; my $makefile = </dev/null sed $(SEDOPTS) -i $(POT) sed $(SEDOPTS2) -i $(POT) EOF open $f, '>', File::Spec->catfile($home, 'Makefile') or die "Unable to open $home/Makefile: $!"; print $f $makefile; close $f; } else { say "$name theme already exists. Aborting."; exit 1; } } =encoding utf8 =head1 NAME Lstu::Command::theme - Create new theme skeleton. =head1 SYNOPSIS Usage: script/lstu theme THEME_NAME Your new theme will be available in the themes directory. =cut 1; ================================================ FILE: lib/Lstu/Command/url.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Command::url; use Mojo::Base 'Mojolicious::Command'; use Mojo::Util qw(getopt); use Mojo::Collection 'c'; use Lstu::DB::URL; use FindBin qw($Bin); use File::Spec qw(catfile); has description => 'Manage stored URL'; has usage => sub { shift->extract_usage }; sub run { my $c = shift; my @args = @_; getopt \@args, 'info=s{1,}' => \my @info, 'r|remove=s{1,}' => \my @remove, 's|search=s' => \my $search, 'ip=s{1,}' => \my @ips, 'y|yes' => \my $yes, 'e|even-if-disabled' => \my $even_disabled; if (scalar @info) { c(@info)->each( sub { my ($e, $num) = @_; my $u = get_short($c, $e, $even_disabled); print_infos($u->to_hash) if $u; } ); } if (scalar @remove) { my @r_ips; c(@remove)->each( sub { my ($e, $num) = @_; my $u = get_short($c, $e, 1); if ($u) { push @r_ips, $u->created_by if $u->created_by; print_infos($u->to_hash); if ($u->disabled) { say sprintf('%s URL is already disabled', $e); } else { my $confirm = ($yes) ? 'yes' : undef; unless (defined $confirm) { printf('Are you sure you want to remove this URL (%s)? [N/y] ', $e); $confirm = ; chomp $confirm; } if ($confirm =~ m/^y(es)?$/i) { if ($u->remove) { say sprintf('Success: %s URL has been removed', $e); } else { say sprintf('Failure: %s URL has not been removed', $e); } } else { say 'Answer was not "y" or "yes". Aborting deletion.'; } } } } ); say sprintf("If you want to ban the uploaders' IPs, please do:\n carton exec script/lstu ban --ban %s", join(' ', @r_ips)) if @r_ips; } if ($search) { my $u = Lstu::DB::URL->new(app => $c->app)->search_url($search); my @shorts; my @s_ips; $u->each(sub { my ($e, $num) = @_; push @shorts, $e->{short}; push @s_ips, $e->{created_by} if $e->{created_by}; print_infos($e); }); say sprintf('%d matching URLs', $u->size); say sprintf("If you want to delete those URLs, please do:\n carton exec script/lstu url --yes --remove %s", join(' ', @shorts)) if @shorts; say sprintf("If you want to ban those IPs, please do:\n carton exec script/lstu ban --ban %s", join(' ', @s_ips)) if @s_ips; } if (scalar(@ips)) { my @recap; c(@ips)->each(sub { my ($ip, $num) = @_; my $u = Lstu::DB::URL->new(app => $c->app)->search_creator($ip); my @shorts; $u->each(sub { my ($e, $num) = @_; push @shorts, $e->{short}; print_infos($e); }); say sprintf('[%s] %d matching URLs', $ip, $u->size); if (@shorts) { say sprintf("[%s] If you want to delete those URLs, please do:\n carton exec script/lstu url --yes --remove %s", $ip, join(' ', @shorts)); push @recap, @shorts; } }); say sprintf("If you want to delete all those URLs, please do:\n carton exec script/lstu url --yes --remove %s", join(' ', @recap)) if @recap; say sprintf("If you want to ban those IPs, please do:\n carton exec script/lstu ban --ban %s", join(' ', @ips)) if @ips; } } sub get_short { my $c = shift; my $short = shift; my $even_if_disabled = shift; my $u = Lstu::DB::URL->new(app => $c->app, short => $short); if ($u->url) { return $u if !$u->disabled; return $u if $even_if_disabled; } say sprintf('Sorry, unable to find an URL with short = %s', $short); return undef; } sub print_infos { my $u = shift; if ($u) { my $msg = <{timestamp}); my $timestamp = sprintf('%d-%d-%d %d:%d:%d GMT', $year + 1900, ++$mon, $mday, $hour, $min, $sec); if ($u->{created_by}) { $msg .= ' created_by : %s'; say sprintf($msg, $u->{short}, $u->{url}, $u->{disabled}, $u->{counter}, $timestamp, $u->{timestamp}, $u->{created_by}); } else { say sprintf($msg, $u->{short}, $u->{url}, $u->{disabled}, $u->{counter}, $timestamp, $u->{timestamp}); } } } =encoding utf8 =head1 NAME Lstu::Command::url - Manage URL in Lstu's database =head1 SYNOPSIS Usage: carton exec script/lstu url --info Print infos about the space-separated URLs carton exec script/lstu url --remove [--yes] Remove the space-separated URLs (ask for confirmation unless --yes is given) Will print infos about URL before confirmation carton exec script/lstu url --search Search URLs by its true URL (LIKE match) carton exec script/lstu url --ip Search URLs by the IP address of its creator (exact match) =cut 1; ================================================ FILE: lib/Lstu/Controller/Admin.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Controller::Admin; use Mojo::Base 'Mojolicious::Controller'; use Digest::SHA qw(sha256_hex); use Lstu::DB::URL; use Lstu::DB::Ban; use Lstu::DB::Session; sub login { my $c = shift; my $pwd = $c->param('adminpwd'); my $act = $c->param('action'); $c->cleaning; my $ip = $c->ip; my $banned = Lstu::DB::Ban->new( app => $c, ip => $ip )->is_banned($c->config('ban_min_strike')); if (defined $banned) { my $penalty = 3600; if ($banned->strike >= 2 * $c->config('ban_min_strike')) { $penalty = 3600 * 24 * 30; # 30 days of banishing } $banned->increment_ban_delay($penalty); $c->flash('msg' => $c->l('Too many bad passwords. You\'re banned.')); $c->flash('banned' => 1); $c->redirect_to('stats'); } else { if ( (defined($c->config('adminpwd')) && defined($pwd) && $pwd eq $c->config('adminpwd')) || (defined($c->config('hashed_adminpwd')) && defined($pwd) && sha256_hex($pwd) eq $c->config('hashed_adminpwd')) ) { my $token = $c->shortener(32); Lstu::DB::Session->new( app => $c, token => $token, until => time + 3600 )->write; $c->session('token' => $token); $c->respond_to( json => sub { my $c = shift; $c->render( json => { success => Mojo::JSON->true, msg => $c->l('You have been successfully logged in.') } ); }, any => sub { my $c = shift; $c->redirect_to('stats'); } ); } elsif (defined($act) && $act eq 'logout') { Lstu::DB::Session->new( app => $c, token => $c->session('token') )->remove; delete $c->session->{token}; $c->respond_to( json => sub { my $c = shift; $c->render( json => { success => Mojo::JSON->true, msg => $c->l('You have been successfully logged out.') } ); }, any => sub { shift->redirect_to('stats'); } ); } else { Lstu::DB::Ban->new( app => $c, ip => $ip )->increment_ban_delay(3600); my $msg = $c->l('Bad password'); $c->respond_to( json => sub { my $c = shift; $c->render( json => { success => Mojo::JSON->false, msg => $msg } ); }, any => sub { my $c = shift; $c->flash('msg' => $msg); $c->redirect_to('stats'); } ); } } } sub delete { my $c = shift; my $short = $c->param('short'); my $db_session = Lstu::DB::Session->new( app => $c, token => $c->session('token') ); if (defined($c->session('token')) && $db_session->is_valid) { my $db_url = Lstu::DB::URL->new( app => $c, short => $short ); if ($db_url->url) { my $deleted = $db_url->remove; $c->respond_to( json => { json => { success => Mojo::JSON->true, deleted => $deleted } }, any => sub { my $c = shift; $c->redirect_to('stats'); } ); } else { my $msg = $c->l('The shortened URL %1 doesn\'t exist.', $c->url_for('/')->to_abs.$short); $c->respond_to( json => { json => { success => Mojo::JSON->false, msg => $msg } }, any => sub { my $c = shift; $c->flash('msg' => $msg); $c->redirect_to('stats'); } ); } } else { $c->flash('msg' => $c->l('You\'re not authenticated as the admin')); $c->redirect_to('stats'); } } 1; ================================================ FILE: lib/Lstu/Controller/Authent.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Controller::Authent; use Mojo::Base 'Mojolicious::Controller'; sub index { my $c = shift; if ($c->is_user_authenticated) { $c->redirect_to('index'); } else { $c->render(template => 'login'); } } sub login { my $c = shift; my $login = $c->param('login'); my $pwd = $c->param('password'); if($c->authenticate($login, $pwd)) { $c->respond_to( json => sub { my $c = shift; $c->render( json => { success => Mojo::JSON->true, msg => $c->l('You have been successfully logged in.') } ); }, any => sub { $c->redirect_to('index'); } ); } else { my $msg = $c->l('Please, check your credentials: unable to authenticate.'); $c->respond_to( json => sub { my $c = shift; $c->render( json => { success => Mojo::JSON->false, msg => $msg } ); }, any => sub { $c->stash(msg => $msg); $c->render(template => 'login') } ); } } sub log_out { my $c = shift; if ($c->is_user_authenticated) { $c->logout; } $c->respond_to( json => sub { my $c = shift; $c->render( json => { success => Mojo::JSON->true, msg => $c->l('You have been successfully logged out.') } ); }, any => sub { $c->render(template => 'logout'); } ); } 1; ================================================ FILE: lib/Lstu/Controller/Stats.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Controller::Stats; use Mojo::Base 'Mojolicious::Controller'; use Lstu::DB::URL; use Lstu::DB::Session; use Mojo::JSON qw(to_json decode_json); use Mojo::Util qw(b64_encode); use Image::PNG::QRCode 'qrpng'; sub export_cookie { my $c = shift; my $u = (defined($c->cookie('url'))) ? decode_json $c->cookie('url') : []; $c->res->headers->add('Content-Disposition' => 'attachment;filename=lstu_export.json'); return $c->render(json => $u); } sub import_cookie { my $c = shift; my $file = $c->param('file'); my $json = decode_json($file->slurp); if (ref($json) eq 'ARRAY') { # Get URLs from cookie my $u = (defined($c->cookie('url'))) ? decode_json $c->cookie('url') : []; # Add the new URL push @{$u}, @{$json}; # Make the array contain only unique URLs my %k = map { $_, 1 } @{$u}; @{$u} = keys %k; # And set the cookie my $cookie = to_json($u); $c->cookie( 'url' => $cookie, { path => $c->config('prefix'), expires => time + 142560000 } ); # expires in 10 years $c->flash(success_msg => $c->l('File imported')); } else { $c->flash(msg => $c->l('Sorry, unable to parse the provided file')); } return $c->redirect_to('stats'); } sub fullstats { my $c = shift; my $url = Lstu::DB::URL->new(app => $c); return $c->render( json => { empty => $url->count_empty, urls => $url->total, timestamp => time, } ); } sub stats { my $c = shift; my $order = $c->param('order') // 'counter'; my $dir = $c->param('dir') // '-desc'; my %orders = ( 'short' => 1, 'url' => 1, 'counter' => 1, 'created_by' => 1, ); my %dirs = (-desc => 1, -asc => 1); $order = 'counter' unless $orders{$order}; $dir = '-desc' unless $dirs{$dir}; if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) { my $db_session = Lstu::DB::Session->new( app => $c, token => $c->session('token') ); if (defined($c->session('token')) && $db_session->is_valid) { my $total = Lstu::DB::URL->new(app => $c)->total; my $page = $c->param('page') || 0; $page = 0 if ($page < 0); $page = $page - 1 if ($page * $c->config('page_offset') > $total); my ($first, $last) = (!$page, ($page * $c->config('page_offset') <= $total && $total < ($page + 1) * $c->config('page_offset'))); my @urls = Lstu::DB::URL->new( app => $c, )->paginate($page, $c->config('page_offset'), $order, $dir); $c->respond_to( json => sub { my $c = shift; $c->render( json => { prefix => $c->prefix, urls => \@urls, first => $first, last => $last, page => $page, admin => 1, total => $total } ); }, any => sub { my $c = shift; $c->render( template => 'stats', prefix => $c->prefix, urls => \@urls, first => $first, last => $last, page => $page, admin => 1, total => $total ) } ); } else { my $u = (defined($c->cookie('url'))) ? decode_json $c->cookie('url') : []; my @urls = Lstu::DB::URL->new( app => $c )->get_a_lot($u); my $prefix = $c->prefix; $c->respond_to( json => sub { my @struct; for my $url (@urls) { push @struct, { short => $prefix.$url->{short}, url => $url->{url}, counter => $url->{counter}, created_at => $url->{timestamp}, qrcode => b64_encode(qrpng(text => $prefix.$url->{short})) }; } $c->render( json => \@struct ); }, any => sub { my @struct; for my $url (@urls) { push @struct, { short => $url->{short}, url => $url->{url}, counter => $url->{counter}, timestamp => $url->{timestamp}, qrcode => b64_encode(qrpng(text => $prefix.$url->{short})) }; } $c->render( template => 'stats', prefix => $prefix, urls => \@struct ) } ) } } else { $c->redirect_to('login'); } } sub stat_for_one_short { my $c = shift; my $short = $c->param('short'); my $url = Lstu::DB::URL->new( app => $c, short => $short ); if ($url->{url}) { my $prefix = $c->prefix; $c->render( json => { success => Mojo::JSON->true, short => $prefix.$url->{short}, url => $url->{url}, counter => $url->{counter}, created_at => $url->{timestamp}, timestamp => time } ); } else { $c->render( json => { success => Mojo::JSON->false, msg => $c->l('The shortened URL %1 doesn\'t exist.', $c->url_for('/')->to_abs.$short) } ); } } 1; ================================================ FILE: lib/Lstu/Controller/URL.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::Controller::URL; use Mojo::Base 'Mojolicious::Controller'; use Lstu::DB::URL; use Lstu::DB::Ban; use Data::Validate::URI qw(is_http_uri is_https_uri); use Mojo::JSON qw(to_json decode_json); use Mojo::URL; use Mojo::Util qw(b64_encode slugify); use Image::PNG::QRCode 'qrpng'; sub add { my $c = shift; $c->cleaning; # Is the user allowed to create a short URL? if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) { my $ip = $c->ip; # Check banning my $banned = Lstu::DB::Ban->new( app => $c, ip => $ip )->is_banned($c->config('ban_min_strike')); my $disabled_api = 0; if ($c->config('disable_api')) { $disabled_api = 1 if $c->validation->csrf_protect->has_error('csrf_token'); $disabled_api = 1 if (!defined($c->req->headers->referrer) || Mojo::URL->new($c->req->headers->referrer)->host ne Mojo::URL->new('https://'.$c->req->headers->host)->host) } if (defined $banned) { # Increase ban delay if necessary my $penalty = 3600; if ($banned->strike >= 2 * $c->config('ban_min_strike')) { $penalty = 3600 * 24 * 30; # 30 days of banishing } $banned->increment_ban_delay($penalty); my $msg = $c->l('You asked to shorten too many URLs too quickly. You\'re banned for %1 hour(s).', $penalty/3600); $c->respond_to( json => { json => { success => Mojo::JSON->false, msg => $msg } }, any => sub { shift->render( template => 'index', msg => $msg, banned => 1 ); } ); } elsif ($disabled_api) { my $msg = $c->l('Sorry, the API is disabled.'); $c->app->log->info('Blocked API call for '.$ip); $c->respond_to( json => { json => { success => Mojo::JSON->false, msg => $msg } }, any => sub { shift->render( template => 'index', msg => $msg, ); } ); } else { my $lsturl = $c->param('lsturl'); $lsturl =~ s/^\s+|\s+$//g; my $url = Mojo::URL->new($lsturl); my $custom_url = $c->param('lsturl-custom'); my $format = $c->param('format'); $custom_url = undef if (defined($custom_url) && $custom_url eq ''); my ($msg, $short); if (defined($custom_url) && ($custom_url =~ m#^(a|d|cookie|stats|fullstats|login|logout|api)$# || $custom_url =~ m/\.json$/) ) { $msg = $c->l('The shortened text can\'t be "a", "api", "d", "cookie", "stats", "fullstats", "login" or "logout" or end with ".json". Your URL to shorten: %1', $url); } elsif (is_http_uri($url->to_string) || is_https_uri($url->to_string) || (defined($url->host) && $url->host =~ m/\.onion$/)) { my $res = ($url->host =~ m/\.onion$/) ? {} : $c->is_spam($url, 0); # Check if spam if ($res->{is_spam}) { $msg = $res->{msg}; } else { # Not spam, let's go Lstu::DB::Ban->new( app => $c, ip => $ip )->increment_ban_delay(1); my $db_url = Lstu::DB::URL->new( app => $c, url => $url ); if ($db_url->short && !defined($custom_url)) { # Already got this URL $short = $db_url->short; } else { if (defined($custom_url)) { $custom_url = slugify $custom_url; my $suffix = 2; my $original_custom_url = $custom_url; while (Lstu::DB::URL->new(app => $c)->exist($custom_url) > 0) { $custom_url = $original_custom_url.'-'.$suffix; $suffix++; } Lstu::DB::URL->new( app => $c, short => $custom_url, url => $url, timestamp => time(), created_by => ($c->config('log_creator_ip')) ? $ip : undef )->write; $short = $custom_url; } else { $db_url = Lstu::DB::URL->new(app => $c)->choose_empty; if (defined $db_url) { $db_url->url($url)->timestamp(time); $db_url->created_by($ip) if $c->config('log_creator_ip'); $db_url->write; $short = $db_url->short; } else { # Houston, we have a problem $msg = $c->l('No shortened URL available. Please retry or contact the administrator at %1. Your URL to shorten: [_2]', $c->config('contact'), $url); } } } } } else { $msg = $c->l('%1 is not a valid URL.', $url); } if ($msg) { $c->respond_to( json => { json => { success => Mojo::JSON->false, msg => $msg } }, any => sub { shift->render( template => 'index', msg => $msg ); } ); } else { # Get URLs from cookie my $u = (defined($c->cookie('url'))) ? decode_json $c->cookie('url') : []; # Add the new URL push @{$u}, $short; # Make the array contain only unique URLs my %k = map { $_, 1 } @{$u}; @{$u} = keys %k; # And set the cookie my $cookie = to_json($u); $c->cookie( 'url' => $cookie, { path => $c->config('prefix'), expires => time + 142560000 } ); # expires in 10 years my $prefix = $c->prefix; my $qrcode = b64_encode(qrpng(text => $prefix.$short, scale => $c->config('qrcode_size'))); $c->respond_to( json => { json => { success => Mojo::JSON->true, url => $url, short => $prefix.$short, qrcode => $qrcode } }, any => sub { shift->render( template => 'index', url => $url, short => $prefix.$short, qrcode => $qrcode ); } ); } } } else { # Not authorized $c->redirect_to('login'); } } sub get { my $c = shift; my $short = $c->param('short'); if (defined($c->stash('format')) && $short eq 'robots' && $c->stash('format') eq 'txt') { if ($c->app->static->file('robots.txt')) { $c->res->headers->content_type('text/plain'); return $c->reply->static('robots.txt'); } else { return $c->reply->not_found; } } my $url; my $disabled_url = 0; if (scalar(@{$c->config('memcached_servers')})) { $url = $c->chi('lstu_urls_cache')->compute($short, undef, sub { my $db_url = Lstu::DB::URL->new(app => $c, short => $short); if ($db_url->disabled) { $disabled_url++; return undef } else { return $db_url->url; } }); } else { my $db_url = Lstu::DB::URL->new(app => $c, short => $short); if ($db_url->disabled) { $disabled_url++; } else { $url = $db_url->url; } } if ($url) { $c->respond_to( json => { json => { success => Mojo::JSON->true, url => $url } }, any => sub { my $c = shift; $c->res->code(301); $c->redirect_to($url); } ); # Update counter $c->on(finish => sub { if ($c->config('minion')->{enabled} && $c->config('minion')->{db_path}) { $c->app->minion->enqueue(increase_counter => [$short, $c->{url}]); } else { Lstu::DB::URL->new(app => $c, short => $short)->increment_counter; my $piwik = $c->config('piwik'); if (defined($piwik) && $piwik->{idsite} && $piwik->{url}) { $c->piwik_api( 'Track' => { idSite => $piwik->{idsite}, action_url => $c->{url}, url => $piwik->{url} } ); } } }); } else { my $msg; if ($disabled_url) { $msg = $c->l('The shortened URL %1 no longer exists.', $c->url_for('/')->to_abs.$short); } else { $msg = $c->l('The shortened URL %1 doesn\'t exist.', $c->url_for('/')->to_abs.$short); } $c->res->code(404); $c->respond_to( json => { json => { success => Mojo::JSON->false, msg => $msg } }, any => sub { my $c = shift; $c->render( template => 'index', msg => $msg ); } ); } } 1; ================================================ FILE: lib/Lstu/DB/Ban/MySQL.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Ban::MySQL; use Mojo::Base 'Lstu::DB::Ban'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->ip); return $c; } 1; ================================================ FILE: lib/Lstu/DB/Ban/Pg.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Ban::Pg; use Mojo::Base 'Lstu::DB::Ban'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->ip); return $c; } sub increment_ban_delay { my $c = shift; my $penalty = shift; my $until = time + $penalty; my $h = { strike => 1 }; if ($c->record) { $h = $c->app->dbi->db->query('UPDATE ban SET until = ?, strike = strike + 1 WHERE ip = ? RETURNING strike', $until, $c->ip)->hashes->first; } else { $c->app->dbi->db->query('INSERT INTO ban (ip, until, strike) VALUES (?, ?, 1)', $c->ip, $until); $c->record(1); } $c->strike($h->{strike}); $c->until($until); return $c; } sub ban_ten_years { my $c = shift; my $until = time + 315360000; my $h = { strike => time }; if ($c->record) { $h = $c->app->dbi->db->query('UPDATE ban SET until = ?, strike = ? WHERE ip = ? RETURNING strike', $until, time, $c->ip)->hashes->first; } else { $c->app->dbi->db->query('INSERT INTO ban (ip, until, strike) VALUES (?, ?, ?)', $c->ip, $until, time); $c->record(1); } $c->strike($h->{strike}); $c->until($until); return $c; } 1; ================================================ FILE: lib/Lstu/DB/Ban/SQLite.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Ban::SQLite; use Mojo::Base 'Lstu::DB::Ban'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->ip); return $c; } 1; ================================================ FILE: lib/Lstu/DB/Ban.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Ban; use Mojo::Base -base; use Mojo::Collection 'c'; has 'ip'; has 'until'; has 'strike' => 0; has 'record' => 0; has 'app'; =head1 NAME Lstu::DB::Ban - Abstraction layer for Lstu ban system =head1 Contributing When creating a new database accessor, make sure that it provides the following subroutines. After that, modify this file and modify the C subroutine to allow to use your accessor. Have a look at Lstu::DB::Ban::SQLite's code: it's simple and may be more understandable that this doc. =head1 Attributes =over 1 =item B : string, an IP address =item B : unix timestamp =item B : integer =item B : a mojolicious object =back =head1 Sub routines =head2 new =over 1 =item B : C<$c = Lstu::DB::Ban-Enew(app =E $self);> =item B : any of the attribute above =item B : construct a new Lstu::DB::Ban object. If the C attribute is provided, it have to load the informations from the database =item B : the Lstu::DB::Ban object =item B : the app argument is used by Lstu::DB::Ban to choose which db accessor will be used, you don't need to use it in new(), but you can use it to access helpers or configuration settings in the other subroutines =back =cut sub new { my $c = shift; $c = $c->SUPER::new(@_); if (ref($c) eq 'Lstu::DB::Ban') { my $dbtype = $c->app->config('dbtype'); if ($dbtype eq 'sqlite') { require Lstu::DB::Ban::SQLite; $c = Lstu::DB::Ban::SQLite->new(@_); } elsif ($dbtype eq 'postgresql') { require Lstu::DB::Ban::Pg; $c = Lstu::DB::Ban::Pg->new(@_); } elsif ($dbtype eq 'mysql') { require Lstu::DB::Ban::MySQL; $c = Lstu::DB::Ban::MySQL->new(@_); } } return $c; } sub to_hash { my $c = shift; return { ip => $c->ip, until => $c->until, strike => $c->strike }; } =head2 is_whitelisted =over 1 =item B : C<$c-Eis_whitelisted> =item B : none =item B : tells you if the current object is in the configured whitelisted IPs =item B : boolean =back =cut sub is_whitelisted { my $c = shift; my $ip = $c->ip; return c(@{$c->app->config('ban_whitelist')})->grep(sub { $_ eq $ip })->size; } =head2 is_blacklisted =over 1 =item B : C<$c-Eis_blacklisted> =item B : none =item B : tells you if the current object is in the configured blacklisted IPs =item B : boolean =back =cut sub is_blacklisted { my $c = shift; my $ip = $c->ip; return c(@{$c->app->config('ban_blacklist')})->grep(sub { $_ eq $ip })->size; } =head2 is_banned =over 1 =item B : C<$c-Eis_banned(3)> =item B : an integer. Will be config('ban_min_strike'), which is the number of strike before being banned (default: 3) =item B : check the db record with C equal to the object's ip attribute if C is superior to current time and if C is superior or equal to the argument. eg: C ? AND strike E= ?', $c->ip, time, $argument> =item B : the Lstu::DB::Ban object if the ip is banned, undef otherwise =item B : if the IP is whitelisted (see C above), it must return undef =back =cut sub is_banned { my $c = shift; my $ban_min_strike = shift; return undef if $c->is_whitelisted; if ($c->is_blacklisted) { $c->until(time + 3600); $c->strike(1 + $c->app->config('ban_min_strike')); return $c; } my $h = $c->app->dbi->db->query('SELECT * FROM ban WHERE ip = ? AND until > ? AND strike >= ?', $c->ip, time, $ban_min_strike)->hashes; if ($h->size) { $c->until($h->first->{until}); $c->strike($h->first->{strike}); $c->record(1); return $c; } else { return undef; } } =head2 increment_ban_delay =over 1 =item B : C<$c-Eincrement_ban_delay(3600)> =item B : an integer. This number is a penalty (in second) that will be added to the C attribute of the Lstu::DB::Ban object =item B : add penalty to the C attribute of the Lstu::DB::Ban object, increment the C attribute by one and write the Lstu::DB::Ban object's attribute to the database. Update the database record if one already exists, create one otherwise. =item B : the Lstu::DB::Ban object =back =cut sub increment_ban_delay { my $c = shift; my $penalty = shift; my $until = time + $penalty; my $h = { strike => 1 }; if ($c->record) { $c->app->dbi->db->query('UPDATE ban SET until = ?, strike = strike + 1 WHERE ip = ?', $until, $c->ip); $h = $c->app->dbi->db->query('SELECT strike FROM ban WHERE ip = ?', $c->ip)->hashes->first; } else { $c->app->dbi->db->query('INSERT INTO ban (ip, until, strike) VALUES (?, ?, 1)', $c->ip, $until); $c->record(1); } $c->strike($h->{strike}); $c->until($until); return $c; } =head2 clear =over 1 =item B : C<$c-Eclear> =item B : none =item B : delete all ban records from database where until < time eg: C ?, time> =item B : nothing is expected =back =cut sub clear { my $c = shift; $c->app->dbi->db->query('DELETE FROM ban WHERE until < ?', time); } =head2 unban =over 1 =item B : C<$c-Eunban> =item B : none =item B : unban IP address =item B : the Lstu::DB::Ban object =back =cut sub unban { my $c = shift; $c->app->dbi->db->query('DELETE from ban WHERE ip = ?', $c->ip); return $c; } =head2 delete_all =over 1 =item B : C<$c-Edelete_all> =item B : none =item B : delete all ban records from database unconditionnally =item B : nothing is expected =back =cut sub delete_all { my $c = shift; $c->app->dbi->db->query('DELETE FROM ban'); } =head2 ban_ten_years =over 1 =item B : C<$c-Eban_ten_years> =item B : none =item B : ban an IP address forever =item B : nothing is expected =back =cut sub ban_ten_years { my $c = shift; my $until = time + 315360000; my $h = { strike => time }; if ($c->record) { $c->app->dbi->db->query('UPDATE ban SET until = ?, strike = ? WHERE ip = ?', $until, time, $c->ip); $h = $c->app->dbi->db->query('SELECT strike FROM ban WHERE ip = ?', $c->ip)->hashes->first; } else { $c->app->dbi->db->query('INSERT INTO ban (ip, until, strike) VALUES (?, ?, 1)', $c->ip, $until); $c->record(1); } $c->strike($h->{strike}); $c->until($until); return $c; } =head2 _slurp =over 1 =item B : C<$c-E_slurp> =item B : none =item B : put a database record's columns into the Lstu::DB::Ban object's attributes =item B : the Lstu::DB::Ban object =back =cut sub _slurp { my $c = shift; my $h = $c->app->dbi->db->query('SELECT * FROM ban WHERE ip = ?', $c->ip)->hashes; if ($h->size) { $c->until($h->first->{until}); $c->strike($h->first->{strike}); $c->record(1); } return $c; } 1; ================================================ FILE: lib/Lstu/DB/Session/MySQL.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Session::MySQL; use Mojo::Base 'Lstu::DB::Session'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->token); return $c; } 1; ================================================ FILE: lib/Lstu/DB/Session/Pg.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Session::Pg; use Mojo::Base 'Lstu::DB::Session'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->token); return $c; } sub remove { my $c = shift; my $h = $c->app->dbi->db->query('DELETE FROM sessions WHERE token = ? RETURNING *', $c->token)->hashes; if ($h->size) { $c = Lstu::DB::Session->new(app => $c->app); } return $h->size; } 1; ================================================ FILE: lib/Lstu/DB/Session/SQLite.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Session::SQLite; use Mojo::Base 'Lstu::DB::Session'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->token); return $c; } 1; ================================================ FILE: lib/Lstu/DB/Session.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::Session; use Mojo::Base -base; has 'token'; has 'until'; has 'record' => 0; has 'app'; =head1 NAME Lstu::DB::Session - Abstraction layer for Lstu session system =head1 Contributing When creating a new database accessor, make sure that it provides the following subroutines. After that, modify this file and modify the C subroutine to allow to use your accessor. Have a look at Lstu::DB::Session::SQLite's code: it's simple and may be more understandable that this doc. =head1 Attributes =over 1 =item B : random string =item B : unix timestamp =item B : a mojolicious object =back =head1 Sub routines =head2 new =over 1 =item B : C<$c = Lstu::DB::Session-Enew(app =E $self);> =item B : any of the attribute above =item B : construct a new Lstu::DB::Session object. If the C attribute is provided, it have to load the informations from the database =item B : the Lstu::DB::Session object =item B : the app argument is used by Lstu::DB::Session to choose which db accessor will be used, you don't need to use it in new(), but you can use it to access helpers or configuration settings in the other subroutines =back =cut sub new { my $c = shift; $c = $c->SUPER::new(@_); if (ref($c) eq 'Lstu::DB::Session') { my $dbtype = $c->app->config('dbtype'); if ($dbtype eq 'sqlite') { require Lstu::DB::Session::SQLite; $c = Lstu::DB::Session::SQLite->new(@_); } elsif ($dbtype eq 'postgresql') { require Lstu::DB::Session::Pg; $c = Lstu::DB::Session::Pg->new(@_); } elsif ($dbtype eq 'mysql') { require Lstu::DB::Session::MySQL; $c = Lstu::DB::Session::MySQL->new(@_); } } return $c; } sub is_valid { my $c = shift; return 0 unless defined $c->until; return ($c->until > time); } sub to_hash { my $c = shift; return { token => $c->token, until => $c->until } } =head2 remove =over 1 =item B : C<$c-Eremove> =item B : none =item B : remove the session record from the database =item B : the Lstu::DB::Session object =back =cut sub remove { my $c = shift; $c->app->dbi->db->query('DELETE FROM sessions WHERE token = ?', $c->token); my $h = $c->app->dbi->db->query('SELECT * FROM sessions WHERE token = ?', $c->token)->hashes; if ($h->size) { # We found the session, it hasn't been removed return 0; } else { $c = Lstu::DB::Session->new(app => $c->app); # We didn't found the session, it has been removed return 1; } } =head2 write =over 1 =item B : C<$c-Ewrite> =item B : none =item B : create or update the object in the database =item B : the Lstu::DB::Session object =back =cut sub write { my $c = shift; if ($c->record) { $c->app->dbi->db->query('UPDATE sessions SET until = ? WHERE token = ?', $c->until, $c->token); } else { $c->app->dbi->db->query('INSERT INTO sessions (token, until) VALUES (?, ?)', $c->token, $c->until); $c->record(1); } return $c; } =head2 clear =over 1 =item B : C<$c-Eclear> =item B : none =item B : delete all session records from database where until < time eg: C ?, time> =item B : nothing is expected =back =cut sub clear { my $c = shift; $c->app->dbi->db->query('DELETE FROM sessions WHERE until < ?', time); } =head2 delete_all =over 1 =item B : C<$c-Edelete_all> =item B : none =item B : delete all session records from database unconditionnally =item B : nothing is expected =back =cut sub delete_all { my $c = shift; $c->app->dbi->db->query('DELETE FROM sessions'); } =head2 _slurp =over 1 =item B : C<$c-E_slurp> =item B : none =item B : put a database record's columns into the Lstu::DB::Session object's attributes =item B : the Lstu::DB::Session object =back =cut sub _slurp { my $c = shift; my $h = $c->app->dbi->db->query('SELECT * FROM sessions WHERE token = ?', $c->token)->hashes; if ($h->size) { $c->token($h->first->{token}); $c->until($h->first->{until}); $c->record(1); } return $c; } 1; ================================================ FILE: lib/Lstu/DB/URL/MySQL.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::URL::MySQL; use Mojo::Base 'Lstu::DB::URL'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->short || $c->url); return $c; } 1; ================================================ FILE: lib/Lstu/DB/URL/Pg.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::URL::Pg; use Mojo::Base 'Lstu::DB::URL'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->short || $c->url); return $c; } sub increment_counter { my $c = shift; my $h = $c->app->dbi->db->query('UPDATE lstu SET counter = counter + 1 WHERE short = ? RETURNING counter', $c->short)->hashes->first; $c->counter($h->{counter}); return $c; } sub remove { my $c = shift; my $removed = 0; if ($c->app->config('really_delete_urls')) { $removed = $c->app->dbi->db->query('DELETE FROM lstu WHERE short = ? RETURNING disabled', $c->short)->hashes->size; } else { $removed = $c->app->dbi->db->query('UPDATE lstu SET disabled = 1 WHERE short = ? RETURNING disabled', $c->short)->hashes->first->{disabled}; } if ($removed) { if (scalar(@{$c->app->config('memcached_servers')})) { $c->app->chi('lstu_urls_cache')->remove($c->short); } $c = Lstu::DB::URL->new(app => $c->app); } return $removed; } 1; ================================================ FILE: lib/Lstu/DB/URL/SQLite.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::URL::SQLite; use Mojo::Base 'Lstu::DB::URL'; sub new { my $c = shift; $c = $c->SUPER::new(@_); $c = $c->_slurp if ($c->short || $c->url); return $c; } sub paginate { my $c = shift; my $page = shift; my $page_offset = shift; my $order = shift // 'counter'; my $dir = shift // '-desc'; $dir =~ s/^-//; return @{$c->app->dbi->db->query("SELECT * FROM lstu WHERE url IS NOT NULL AND disabled = 0 ORDER BY $order $dir LIMIT ? OFFSET ?", $page_offset, $page * $page_offset)->hashes->to_array}; } 1; ================================================ FILE: lib/Lstu/DB/URL.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DB::URL; use Mojo::Base -base; has 'short'; has 'url'; has 'counter' => 0; has 'timestamp'; has 'created_by'; has 'disabled' => 0; has 'record' => 0; has 'app'; =head1 NAME Lstu::DB::URL - Abstraction layer for Lstu session system =head1 Contributing When creating a new database accessor, make sure that it provides the following subroutines. After that, modify this file and modify the C subroutine to allow to use your accessor. Have a look at Lstu::DB::URL::SQLite's code: it's simple and may be more understandable that this doc. =head1 Attributes =over 1 =item B : random string =item B : string, valid URL =item B : integer =item B : unix timestamp =item B : the IP address of the creator =item B : boolean (0 or 1), is the URL active? =item B : a mojolicious object =back =head1 Sub routines =head2 new =over 1 =item B : C<$c = Lstu::DB::URL-Enew(app =E $self);> =item B : any of the attribute above =item B : construct a new Lstu::DB::URL object. If the C or the C attribute is provided, it have to load the informations from the database. In the case of multiple records for the same C, choose the first. =item B : the Lstu::DB::URL object =item B : the app argument is used by Lstu::DB::URL to choose which db accessor will be used, you don't need to use it in new(), but you can use it to access helpers or configuration settings in the other subroutines =back =cut sub new { my $c = shift; $c = $c->SUPER::new(@_); if (ref($c) eq 'Lstu::DB::URL') { my $dbtype = $c->app->config('dbtype'); if ($dbtype eq 'sqlite') { require Lstu::DB::URL::SQLite; $c = Lstu::DB::URL::SQLite->new(@_); } elsif ($dbtype eq 'postgresql') { require Lstu::DB::URL::Pg; $c = Lstu::DB::URL::Pg->new(@_); } elsif ($dbtype eq 'mysql') { require Lstu::DB::URL::MySQL; $c = Lstu::DB::URL::MySQL->new(@_); } } return $c; } sub to_hash { my $c = shift; return { short => $c->short, url => $c->url, disabled => $c->disabled, counter => $c->counter, timestamp => $c->timestamp, created_by => $c->created_by }; } =head2 increment_counter =over 1 =item B : C<$c-Eincrement_counter> =item B : none =item B : increment the C attribute of the Lstu::DB::URL object and update the database record =item B : the Lstu::DB::URL object =back =cut sub increment_counter { my $c = shift; $c->app->dbi->db->query('UPDATE lstu SET counter = counter + 1 WHERE short = ?', $c->short); my $h = $c->app->dbi->db->query('SELECT counter FROM lstu WHERE short = ?', $c->short)->hashes->first; $c->counter($h->{counter}); return $c; } =head2 write =over 1 =item B : C<$c-Ewrite> =item B : none =item B : create or update the object in the database =item B : the Lstu::DB::URL object =back =cut sub write { my $c = shift; if ($c->record) { $c->app->dbi->db->query('UPDATE lstu SET url = ?, counter = ?, timestamp = ?, created_by = ?, disabled = ? WHERE short = ?', $c->url, $c->counter, $c->timestamp, $c->created_by, $c->disabled, $c->short); } else { $c->app->dbi->db->query('INSERT INTO lstu (short, url, counter, timestamp, created_by, disabled) VALUES (?, ?, ?, ?, ?, ?)', $c->short, $c->url, $c->counter, $c->timestamp, $c->created_by, $c->disabled); $c->record(1); } return $c; } =head2 remove =over 1 =item B : C<$c-Eremove> =item B : none =item B : remove the URL record from the database =item B : 1 for success, 0 for failure =back =cut sub remove { my $c = shift; my $removed = 0; if ($c->app->config('really_delete_urls')) { $c->app->dbi->db->query('DELETE FROM lstu WHERE short = ?', $c->short); my $count = $c->app->dbi->db->query('SELECT count(*) FROM lstu WHERE short = ?', $c->short)->hashes->first->{count}; $removed = ($count == 0) ? 1 : 0; } else { $c->app->dbi->db->query('UPDATE lstu SET disabled = 1 WHERE short = ?', $c->short); $removed = $c->app->dbi->db->query('SELECT disabled FROM lstu WHERE short = ?', $c->short)->hashes->first->{disabled}; } if ($removed) { if (scalar(@{$c->app->config('memcached_servers')})) { $c->app->chi('lstu_urls_cache')->remove($c->short); } $c = Lstu::DB::URL->new(app => $c->app); } return $removed; } =head2 exist =over 1 =item B : C<$c-Eexist('short')> =item B : string =item B : count how many database record there is with C equal to the argument. eg: COUNT(short) WHERE short = ?, $argument =item B : integer. Should be 0 or 1 =back =cut sub exist { my $c = shift; my $short = shift; return undef unless $short; return $c->app->dbi->db->query('SELECT count(short) AS count FROM lstu WHERE short = ?', $short)->hashes->first->{count}; } =head2 choose_empty =over 1 =item B : C<$c-Echoose_empty> =item B : none =item B : choose an unassigned short string in the database =item B : string, an unassigned short string =back =cut sub choose_empty { my $c = shift; my $h = $c->app->dbi->db->query('SELECT * FROM lstu WHERE url IS NULL')->hashes->shuffle; if ($h->size) { $c->short($h->first->{short}); $c->record(1); return $c; } else { return undef; } } =head2 count_empty =over 1 =item B : C<$c-Ecount_empty> =item B : none =item B : count how many unassigned short string there is in the database eg: C =item B : integer =back =cut sub count_empty { my $c = shift; return $c->app->dbi->db->query('SELECT count(short) AS count FROM lstu WHERE url IS NULL')->hashes->first->{count}; } =head2 paginate =over 1 =item B : C<$c-Epaginate($page, $page_offset)> =item B : two integers. =over 2 =item B<$page> : the number of the page you want =item B<$page_offset> : the number of records per page. =back =item B : returns all the URL records, page per page, ordered by the C attribute eg: SELECT * WHERE url IS NOT NULL ORDER BY counter DESC LIMIT ? OFFSET ?, $page_offset, $page * $page_offset =item B : an array of hash references, containing all the Lstu::DB::URL attributes, except C =back =cut sub paginate { my $c = shift; my $page = shift; my $page_offset = shift; my $order = shift // 'counter'; my $dir = shift // '-desc'; return @{$c->app->dbi->db->select('lstu', undef, { url => { '!=', undef }, disabled => 0 }, { order_by => { $dir => $order }, limit => $page_offset, offset => $page * $page_offset })->hashes->to_array}; } =head2 get_a_lot =over 1 =item B : C<$c-Eget_a_lot(['short1', 'short2'])> =item B : an array reference of strings, which are C attributes =item B : returns all the URL records which C attribute are in the array ref =item B : an array of hash references, containing all the Lstu::DB::URL attributes, except C =back =cut sub get_a_lot { my $c = shift; my $u = shift; if (scalar @{$u}) { my $p = join ",", (('?') x @{$u}); return @{$c->app->dbi->db->query('SELECT * FROM lstu WHERE short IN ('.$p.') ORDER BY counter DESC', @{$u})->hashes->to_array}; } else { return (); } } =head2 total =over 1 =item B : C<$c-Etotal> =item B : none =item B : count how many shorten links there is in the database. eg: C =item B : integer =back =cut sub total { my $c = shift; return $c->app->dbi->db->query('SELECT count(short) AS count FROM lstu WHERE url IS NOT NULL AND disabled = 0')->hashes->first->{count}; } =head2 delete_all =over 1 =item B : C<$c-Edelete_all> =item B : none =item B : delete all URL records from database unconditionnally =item B : nothing is expected =back =cut sub delete_all { my $c = shift; $c->app->dbi->db->query('DELETE FROM lstu'); } =head2 search_url =over 1 =item B : C<$c-Esearch_url($string)> =item B : string, part of URL to search =item B : search records which real url matches the given string =item B : a Mojo::Collection containing hashes of the matching records =back =cut sub search_url { my $c = shift; my $s = shift; $c->app->dbi->db->select('lstu', undef, { url => {-like => '%'.$s.'%'}})->hashes; } =head2 search_creator =over 1 =item B : C<$c-Esearch_creator($string)> =item B : string, IP address to search =item B : search records which creator's IP address matches the given string =item B : a Mojo::Collection containing hashes of the matching records =back =cut sub search_creator { my $c = shift; my $s = shift; $c->app->dbi->db->select('lstu', undef, { created_by => $s })->hashes; } =head2 get_all_urls =over 1 =item B : C<$c-Eget_all_urls()> =item B : none =item B : return all non-empty records =item B : a Mojo::Collection containing hashes of all non-empty records =back =cut sub get_all_urls { my $c = shift; $c->app->dbi->db->select('lstu', undef, { url => { '!=', undef } })->hashes; } =head2 get_all_urls_created_ago =over 1 =item B : C<$c-Eget_all_urls_created_ago($seconds)> =item B : integer, number of seconds =item B : return all non-empty records created less than $seconds agog =item B : a Mojo::Collection containing hashes of matching records =back =cut sub get_all_urls_created_ago { my $c = shift; my $delay = shift; $c->app->dbi->db->select('lstu', undef, { url => { '!=', undef }, timestamp => { '>=', time - $delay } })->hashes; } =head2 _slurp =over 1 =item B : C<$c-E_slurp> =item B : none =item B : put a database record's columns into the Lstu::DB::URL object's attributes =item B : the Lstu::DB::URL object =back =cut sub _slurp { my $c = shift; my $h; if ($c->short) { $h = $c->app->dbi->db->query('SELECT * FROM lstu WHERE short = ?', $c->short)->hashes; } else { $h = $c->app->dbi->db->query('SELECT * FROM lstu WHERE url = ?', $c->url)->hashes; } if ($h->size) { $c->url($h->first->{url}); $c->short($h->first->{short}); $c->counter($h->first->{counter}); $c->timestamp($h->first->{timestamp}); $c->created_by($h->first->{created_by}); $c->disabled($h->first->{disabled}); $c->record(1); } return $c; } 1; ================================================ FILE: lib/Lstu/DefaultConfig.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::DefaultConfig; require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw($default_config); our $default_config = { prefix => '/', provisioning => 100, provis_step => 5, length => 8, secret => ['hfudsifdsih'], really_delete_urls => 0, page_offset => 10, theme => 'default', ban_min_strike => 3, ban_whitelist => [], ban_blacklist => [], minion => { enabled => 0, db_path => 'minion.db' }, session_duration => 3600, disable_api => 0, dbtype => 'sqlite', db_path => 'lstu.db', max_redir => 2, skip_spamhaus => 0, safebrowsing_api_key => '', memcached_servers => [], x_frame_options => 'DENY', x_content_type_options => 'nosniff', x_xss_protection => '1; mode=block', log_creator_ip => 0, qrcode_size => 3, }; 1; ================================================ FILE: lib/Lstu/Plugin/Headers.pm ================================================ package Lstu::Plugin::Headers; use Mojo::Base 'Mojolicious::Plugin'; sub register { my ($self, $app) = @_; # Assets Cache headers $app->plugin('StaticCache' => { even_in_dev => 1, max_age => 2592000 }); # Add CSP Header if (!defined($app->config('csp')) || (defined($app->config('csp')) && $app->config('csp') ne '')) { my $directives = { 'default-src' => "'none'", 'script-src' => "'self'", 'style-src' => "'self'", 'img-src' => "'self' data:", 'font-src' => "'self'", 'form-action' => "'self'", 'base-uri' => "'self'", }; my $frame_ancestors = ''; $frame_ancestors = "'none'" if $app->config('x_frame_options') eq 'DENY'; $frame_ancestors = "'self'" if $app->config('x_frame_options') eq 'SAMEORIGIN'; if ($app->config('x_frame_options') =~ m#^ALLOW-FROM#) { $frame_ancestors = $app->config('x_frame_options'); $frame_ancestors =~ s#ALLOW-FROM +##; } $directives->{'frame-ancestors'} = $frame_ancestors if $frame_ancestors; $app->plugin('CSPHeader', csp => $app->config('csp'), directives => $directives ); } # Add other headers $app->hook( before_dispatch => sub { my $c = shift; $c->res->headers->header('X-Frame-Options' => $app->config('x_frame_options')) if $app->config('x_frame_options'); $c->res->headers->header('X-Content-Type-Options' => $app->config('x_content_type_options')) if $app->config('x_content_type_options'); $c->res->headers->header('X-XSS-Protection' => $app->config('x_xss_protection')) if $app->config('x_xss_protection'); } ); } 1; ================================================ FILE: lib/Lstu/Plugin/Helpers.pm ================================================ package Lstu::Plugin::Helpers; use Mojo::Base 'Mojolicious::Plugin'; use Mojo::URL; use Net::Abuse::Utils::Spamhaus qw(check_fqdn); use Lstu::DB::URL; use Lstu::DB::Ban; use Lstu::DB::Session; use FindBin qw($Bin); sub register { my ($self, $app) = @_; # PgURL helper if ($app->config('dbtype') eq 'postgresql' || $app->config('dbtype') eq 'mysql') { $app->plugin('PgURLHelper'); } # DB migrations if ($app->config('dbtype') eq 'sqlite') { require Mojo::SQLite; $app->helper(dbi => \&_sqlite); # Database migration # Have to create $sql before using its migrations attribute, otherwise, it won't work my $sql = Mojo::SQLite->new('sqlite:'.$app->config('db_path')); my $migrations = $sql->migrations; if ($app->mode eq 'development' && $ENV{LSTU_DEBUG}) { $migrations->from_file('utilities/migrations/sqlite.sql')->migrate(0)->migrate($migrations->latest); } else { $migrations->from_file('utilities/migrations/sqlite.sql')->migrate($migrations->latest); } } elsif ($app->config('dbtype') eq 'postgresql') { require Mojo::Pg; $app->helper(dbi => \&_pg); # Database migration my $migrations = Mojo::Pg::Migrations->new(pg => $app->dbi); if ($app->mode eq 'development' && $ENV{LSTU_DEBUG}) { $migrations->from_file('utilities/migrations/postgresql.sql')->migrate(0)->migrate($migrations->latest); } else { $migrations->from_file('utilities/migrations/postgresql.sql')->migrate($migrations->latest); } } elsif ($app->config('dbtype') eq 'mysql') { require Mojo::mysql; $app->helper(dbi => \&_mysql); # Database migration my $migrations = Mojo::mysql::Migrations->new(mysql => $app->dbi); if ($app->mode eq 'development' && $ENV{LSTU_DEBUG}) { $migrations->from_file('utilities/migrations/mysql.sql')->migrate(0)->migrate($migrations->latest); } else { $migrations->from_file('utilities/migrations/mysql.sql')->migrate($migrations->latest); } } # Helpers $app->helper(ip => \&_ip); $app->helper(provisioning => \&_provisioning); $app->helper(prefix => \&_prefix); $app->helper(shortener => \&_shortener); $app->helper(is_spam => \&_is_spam); $app->helper(cleaning => \&_cleaning); $app->helper(gsb => \&_gsb); $app->helper(gsb_update => \&_gsb_update); } sub _sqlite { my $c = shift; state $sqlite = Mojo::SQLite->new('sqlite:'.$c->app->config('db_path')); return $sqlite; } sub _pg { my $c = shift; my $pgdb = $c->config('pgdb'); my $port = (defined $pgdb->{port}) ? $pgdb->{port}: 5432; my $addr = $c->pg_url({ host => $pgdb->{host}, port => $port, database => $pgdb->{database}, user => $pgdb->{user}, pwd => $pgdb->{pwd} }); state $pg = Mojo::Pg->new($addr); $pg->max_connections($pgdb->{max_connections}) if defined $pgdb->{max_connections}; return $pg; } sub _mysql { my $c = shift; my $mysqldb = $c->config('mysqldb'); my $port = (defined $mysqldb->{port}) ? $mysqldb->{port}: 3306; my $addr = $c->pg_url({ host => $mysqldb->{host}, port => $port, database => $mysqldb->{database}, user => $mysqldb->{user}, pwd => $mysqldb->{pwd} }); $addr =~ s/postgresql/mysql/; state $mysql = Mojo::mysql->new($addr); $mysql->max_connections($mysqldb->{max_connections}) if defined $mysqldb->{max_connections}; return $mysql; } sub _ip { my $c = shift; my $proxy = $c->req->headers->header('X-Forwarded-For'); my $ip = ($proxy) ? $proxy : $c->tx->remote_address; return $ip; } sub _provisioning { my $c = shift; # Create some short patterns for provisioning my $db_url = Lstu::DB::URL->new(app => $c); if ($db_url->count_empty < $c->config('provisioning')) { for (my $i = 0; $i < $c->config('provis_step'); $i++) { my $short; do { $short = $c->shortener($c->config('length')); } while ($db_url->exist($short) || $short =~ m#^(a|d|cookie|stats|fullstats|login|logout|api)$#); $db_url->short($short)->write; } } } sub _prefix { my $c = shift; my $prefix = $c->url_for('index')->to_abs; # Forced domain $prefix->host($c->config('fixed_domain')) if (defined($c->config('fixed_domain')) && $c->config('fixed_domain') ne ''); # Hack for prefix (subdir) handling $prefix .= '/' unless ($prefix =~ m#/$#); return $prefix; } sub _shortener { my $c = shift; my $length = shift; my @chars = ('a'..'h', 'j', 'k', 'm'..'z','A'..'H', 'J'..'N', 'P'..'Z','0'..'9', '-', '_'); my $result = ''; foreach (1..$length) { $result .= $chars[rand scalar(@chars)]; } return $result; } sub _is_spam { my $c = shift; my $url = shift; my $nb_redir = shift; my $ip = $c->ip; return { is_spam => 0 } if scalar(grep {/$ip/} @{$c->config('ban_whitelist')}); my $wl = $c->config('spam_whitelist_regex'); return { is_spam => 0 } if (defined($wl) && $url->host =~ m/$wl/); my $bl = $c->config('spam_blacklist_regex'); my $path_bl = $c->config('spam_path_blacklist_regex'); return { is_spam => 1, msg => $c->l('The URL you want to shorten comes from a domain (%1) that is blacklisted on this server (usually because of spammers that use this domain).', $url->host) } if ((defined($bl) && $url->host =~ m/$bl/) || (defined($path_bl) && $url->path =~ m/$path_bl/)); if ($nb_redir++ <= $c->config('max_redir')) { my $res = ($c->config('skip_spamhaus')) ? undef : check_fqdn($url->host); if (defined $res) { return { is_spam => 1, msg => $c->l('The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I refuse to shorten it.', $url->host) } } else { if ($c->config('safebrowsing_api_key') && scalar($c->gsb->lookup(url => $url->to_string))) { return { is_spam => 1, msg => $c->l('The URL or one of its redirection(s) (%1) is blacklisted in Google Safe Browsing database. I refuse to shorten it.', $url) } } my $res = $c->ua->head($url)->res; if (defined($res->code) && $res->code >= 300 && $res->code < 400) { my $new_url = Mojo::URL->new($res->headers->location); $new_url->host($url->host) unless $new_url->host; $new_url->scheme($url->scheme) unless $new_url->scheme; return $c->is_spam($new_url, $nb_redir); } else { return { is_spam => 0 }; } } } else { return { is_spam => 1, msg => $c->l('The URL redirects %1 times or most. It\'s most likely a dangerous URL (spam, phishing, etc.). I refuse to shorten it.', $c->config('max_redir')) } } } sub _cleaning { my $c = shift; # Delete old sessions Lstu::DB::Session->new(app => $c)->clear(); # Delete old bans Lstu::DB::Ban->new(app => $c)->clear(); } sub _gsb { my $c = shift; my $check = shift; # Google safebrowsing (if configured) if ($c->config('safebrowsing_api_key')) { use Net::Google::SafeBrowsing4; use Net::Google::SafeBrowsing4::Storage::File; my $force_update = (!-e Mojo::File->new($Bin, '..' , 'safebrowsing_db')); state $gsb = Net::Google::SafeBrowsing4->new( key => $c->config('safebrowsing_api_key'), storage => Net::Google::SafeBrowsing4::Storage::File->new(path => Mojo::File->new($Bin, '..' , 'safebrowsing_db')), ); my $lock_file = Mojo::File->new($Bin, '..', 'gsb.lock'); if ($force_update) { $lock_file->touch; $gsb->update(); $lock_file->remove_tree; } elsif ($check) { if (! -e $lock_file->to_string) { $lock_file->touch; my $update = Mojo::File->new($Bin, '..' , 'safebrowsing_db')->to_string; my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($update); $gsb->update() if $mtime < time - 86400; $lock_file->remove_tree; } } return $gsb; } else { return undef; } } 1; ================================================ FILE: lib/Lstu.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu; use Mojo::Base 'Mojolicious'; use Mojo::JSON; use Lstu::DB::URL; use Lstu::DefaultConfig qw($default_config); $ENV{MOJO_REVERSE_PROXY} = 1; # This method will run once at server start sub startup { my $self = shift; my $config = $self->plugin('Config' => { default => $default_config }); $config->{provisioning} = $config->{provisionning} if (defined($config->{provisionning})); die "You need to provide a contact information in lstu.conf!" unless (defined($config->{contact})); # qrcode_size correction if needed; $self->config('qrcode_size', 100) if $self->config('qrcode_size') > 100; $self->config('qrcode_size', 1 ) if $self->config('qrcode_size') < 1; $self->config('qrcode_size', sprintf('%d', $self->config('qrcode_size'))); # Themes handling shift @{$self->renderer->paths}; shift @{$self->static->paths}; if ($config->{theme} ne 'default') { my $theme = $self->home->rel_file('themes/'.$config->{theme}); push @{$self->renderer->paths}, $theme.'/templates' if -d $theme.'/templates'; push @{$self->static->paths}, $theme.'/public' if -d $theme.'/public'; } push @{$self->renderer->paths}, $self->home->rel_file('themes/default/templates'); push @{$self->static->paths}, $self->home->rel_file('themes/default/public'); # Internationalization my $lib = $self->home->rel_file('themes/'.$config->{theme}.'/lib'); eval qq(use lib "$lib"); $self->plugin('I18N'); # Debug $self->plugin('DebugDumperHelper'); # Piwik $self->plugin('Piwik'); # Static assets gzipping $self->plugin('GzipStatic'); # URL cache if (scalar(@{$config->{memcached_servers}})) { $self->plugin(CHI => { lstu_urls_cache => { driver => 'Memcached', servers => $config->{memcached_servers}, expires_in => '1 day', expires_on_backend => 1, } }); } # Headers $self->plugin('Lstu::Plugin::Headers'); # Lstu Helpers $self->plugin('Lstu::Plugin::Helpers'); # Authentication (if configured) if (defined($self->config('ldap')) || defined($self->config('htpasswd'))) { if (defined($self->config('ldap'))) { require Net::LDAP; } if (defined($self->config('htpasswd'))) { require Apache::Htpasswd; } die 'Unable to read '.$self->config('htpasswd') if (defined($self->config('htpasswd')) && !-r $self->config('htpasswd')); $self->plugin('Authentication' => { autoload_user => 1, session_key => 'Lstu', load_user => sub { my ($c, $username) = @_; return $username; }, validate_user => sub { my ($c, $username, $password, $extradata) = @_; if (defined($c->config('ldap'))) { my $ldap = Net::LDAP->new($c->config->{ldap}->{uri}); my $mesg; if (defined($c->config->{ldap}->{bind_dn}) && defined($c->config->{ldap}->{bind_pwd})) { # connect to the ldap server using the bind credentials $mesg = $ldap->bind( $c->config->{ldap}->{bind_dn}, password => $c->config->{ldap}->{bind_pwd} ); } else { # anonymous bind $mesg = $ldap->bind } if ($mesg->code) { $c->app->log->info('[LDAP INFO] Authenticated bind failed - Login: '.$c->config->{ldap}->{bind_dn}) if defined($c->config->{ldap}->{bind_dn}); $c->app->log->error('[LDAP ERROR] Error on bind: '.$mesg->error); return undef; } my $ldap_user_attr = $c->config->{ldap}->{user_attr}; my $ldap_user_filter = $c->config->{ldap}->{user_filter}; # search the ldap database for the user who is trying to login $mesg = $ldap->search( base => $c->config->{ldap}->{user_tree}, filter => "(&($ldap_user_attr=$username)$ldap_user_filter)" ); if ($mesg->code) { $c->app->log->error('[LDAP ERROR] Error on search: '.$mesg->error); return undef; } # check to make sure that the ldap search returned at least one entry my @entries = $mesg->entries; my $entry = $entries[0]; unless (defined $entry) { $c->app->log->info("[LDAP INFO] Authentication failed - User $username filtered out, IP: ".$c->ip); return undef; } # retrieve the first user returned by the search $c->app->log->debug("[LDAP DEBUG] Found user dn: ".$entry->dn); # Now we know that the user exists $mesg = $ldap->bind($entry->dn, password => $password ); if ($mesg->code) { $c->app->log->info("[LDAP INFO] Authentication failed - Login: $username, IP: ".$c->ip); $c->app->log->error('[LDAP ERROR] Authentication failed '.$mesg->error); return undef; } $c->app->log->info("[LDAP INFO] Authentication successful - Login: $username, IP: ".$c->ip); } elsif (defined($c->config('htpasswd'))) { my $htpasswd = new Apache::Htpasswd( { passwdFile => $c->config('htpasswd'), ReadOnly => 1 } ); if (!$htpasswd->htCheckPassword($username, $password)) { return undef; } $c->app->log->info("[Simple authentication successful] login: $username, IP: ".$c->ip); } return $username; } } ); $self->app->sessions->default_expiration($self->config('session_duration')); } # Minion if ($config->{minion}->{enabled}) { if ($config->{dbtype} eq 'sqlite') { $self->plugin('Minion' => { SQLite => 'sqlite:'.$config->{minion}->{db_path} }) if $config->{minion}->{db_path}; } elsif ($config->{dbtype} eq 'postgresql') { my $pgdb = $config->{minion}->{pgdb}; my $port = (defined $pgdb->{port}) ? $pgdb->{port}: 5432; my $addr = $self->pg_url({ host => $pgdb->{host}, port => $port, database => $pgdb->{database}, user => $pgdb->{user}, pwd => $pgdb->{pwd} }); $self->plugin('Minion' => { Pg => $addr }); } elsif ($config->{dbtype} eq 'mysql') { my $mysqldb = $config->{minion}->{mysqldb}; my $port = (defined $mysqldb->{port}) ? $mysqldb->{port}: 3306; my $addr = $self->pg_url({ host => $mysqldb->{host}, port => $port, database => $mysqldb->{database}, user => $mysqldb->{user}, pwd => $mysqldb->{pwd} }); $addr =~ s/postgresql/mysql/; $self->plugin('Minion' => { mysql => $addr }); } } # Secrets $self->secrets($config->{secret}); # Hooks $self->hook( before_dispatch => sub { my $c = shift; # API allowed domains if (defined($c->config('allowed_domains')) && scalar @{$c->config('allowed_domains')}) { if ($c->config('allowed_domains')->[0] eq '*') { $c->res->headers->header('Access-Control-Allow-Origin' => '*'); } elsif (my $origin = $c->req->headers->origin) { for my $domain (@{$c->config('allowed_domains')}) { if ($domain eq $origin) { $c->res->headers->header('Access-Control-Allow-Origin' => $origin); last; } } } } } ); # Recurrent tasks Mojo::IOLoop->recurring(2 => sub { my $loop = shift; $self->provisioning(); }); if ($self->config('safebrowsing_api_key')) { $self->gsb(1); Mojo::IOLoop->recurring(86400 => sub { my $loop = shift; $self->gsb(1); }); } # Minion if ($config->{minion}->{enabled}) { $self->app->minion->add_task( increase_counter => sub { my $job = shift; my $short = shift; my $url = shift; my $db_url = Lstu::DB::URL->new( app => $job->app, short => $short )->increment_counter; my $piwik = $job->app->config('piwik'); if (defined($piwik) && $piwik->{idsite} && $piwik->{url}) { $job->app->piwik_api( 'Track' => { idSite => $piwik->{idsite}, action_url => $url, url => $piwik->{url} } ); } } ); } # For the first launch (after, this isn't really useful) $self->provisioning(); # Default layout $self->defaults(layout => 'default'); # Router my $r = $self->routes; # Normal route to controller $r->get('/' => sub { my $c = shift; if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) { $c->render(template => 'index'); } else { $c->redirect_to('login'); } })->name('index'); if (defined $self->config('ldap') || defined $self->config('htpasswd')) { # Login page $r->get('/login') ->to('Authent#index') ->name('login'); # Authentication $r->post('/login') ->to('Authent#login'); # Logout page $r->get('/logout') ->to('Authent#log_out') ->name('logout'); } $r->get('/api' => sub { shift->render(template => 'api'); })->name('api'); $r->get('/partial/lstu.js' => sub { my $c = shift; $c->render( template => 'partial/lstu', format => 'js' ); })->name('lstu.js'); $r->post('/stats') ->to('Admin#login'); $r->get('/d/:short') ->to('Admin#delete') ->name('delete'); $r->post('/a') ->to('URL#add') ->name('add'); $r->get('/cookie') ->to('Stats#export_cookie') ->name('export_cookie'); $r->post('/cookie') ->to('Stats#import_cookie') ->name('import_cookie'); $r->get('/stats') ->to('Stats#stats') ->name('stats'); $r->get('/stats/:short') ->to('Stats#stat_for_one_short') ->name('stat_for_one_short'); $r->get('/fullstats') ->to('Stats#fullstats') ->name('fullstats'); $r->get('/:short') ->to('URL#get') ->name('short'); } 1; ================================================ FILE: lib/Mounter.pm ================================================ package Mounter; use Mojo::Base 'Mojolicious'; use Mojo::File; use FindBin qw($Bin); use File::Spec qw(catfile); use Lstu::DefaultConfig qw($default_config); # This method will run once at server start sub startup { my $self = shift; push @{$self->commands->namespaces}, 'Lstu::Command'; my $cfile = Mojo::File->new($Bin, '..' , 'lstu.conf'); if (defined $ENV{MOJO_CONFIG}) { $cfile = Mojo::File->new($ENV{MOJO_CONFIG}); unless (-e $cfile->to_abs) { $cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG}); } } my $config = $self->plugin('Config' => { file => $cfile, default => $default_config } ); # Themes handling shift @{$self->static->paths}; if ($config->{theme} ne 'default') { my $theme = $self->home->rel_file('themes/'.$config->{theme}); push @{$self->static->paths}, $theme.'/public' if -d $theme.'/public'; } push @{$self->static->paths}, $self->home->rel_file('themes/default/public'); # Static assets gzipping $self->plugin('GzipStatic'); # Headers $self->plugin('Lstu::Plugin::Headers'); # Helpers $self->plugin('Lstu::Plugin::Helpers'); # URL cache if (scalar(@{$config->{memcached_servers}})) { $self->plugin(CHI => { lstu_urls_cache => { driver => 'Memcached', servers => $config->{memcached_servers}, expires_in => '1 day', expires_on_backend => 1, } }); } $self->plugin('Mount' => {$config->{prefix} => File::Spec->catfile($Bin, '..', 'script', 'application')}); } 1; ================================================ FILE: lstu.conf.template ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY #contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either #hashed_adminpwd => '94b2feede6ea5e2eec62f457ecb7d3f719b24d19c29d4e5466246a31908fc23b', # indicates if you want to really delete URLs from admin page (/stats) # or just want to deactivate the shorten URL (won’t redirect anymore, can’t be used anymore) # optional, default to 0 (false) #really_delete_urls => 0, # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # if set to 1, Lstu will try to prevent its use without using the web interface # optional, default is 0 #disable_api => 0, # choose what database you want to use # valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite #dbtype => 'sqlite', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lstu', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # user => 'DBUSER', # pwd => 'DBPASSWORD', # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # user => 'DBUSER', # pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # user => 'DBUSER', # pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # user => 'DBUSER', # pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, # Positive integer which specifies how many pixels one "module" (one block of the QR code) occupies. # You can't use fractional values. An arbitrary upper limit of 100 is imposed by Image::PNG::QRCode module. # optional, default is 3 #qrcode_size => 3, }; ================================================ FILE: script/application ================================================ #!/usr/bin/env perl use strict; use warnings; use FindBin; BEGIN { unshift @INC, "$FindBin::Bin/../lib" } # Start command line interface for application require Mojolicious::Commands; Mojolicious::Commands->start_app('Lstu'); ================================================ FILE: script/lstu ================================================ #!/usr/bin/env perl use strict; use warnings; use FindBin; BEGIN { unshift @INC, "$FindBin::Bin/../lib" } # Start command line interface for application require Mojolicious::Commands; Mojolicious::Commands->start_app('Mounter'); ================================================ FILE: t/lstu.passwd ================================================ luc:$apr1$zG4UAKGa$FqSi4widrkVH/pT3qPawd. ================================================ FILE: t/mysql1.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either adminpwd => 'toto', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either #hashed_adminpwd => '94b2feede6ea5e2eec62f457ecb7d3f719b24d19c29d4e5466246a31908fc23b', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite and postgresql (all lowercase) # optional, default is sqlite dbtype => 'mysql', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lstu', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype mysqldb => { database => 'lstu_db', host => 'mariadb', # # optional, default is 3306 # #port => 3306, user => 'lstu', pwd => 'lstu_pwd' # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, }, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/mysql2.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either hashed_adminpwd => '31f7a65e315586ac198bd798b6629ce4903d0899476d5741a9f32e2e521b6a66', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite dbtype => 'mysql', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lstu', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype mysqldb => { database => 'lstu_db', host => 'mariadb', # # optional, default is 3306 # #port => 3306, user => 'lstu', pwd => 'lstu_pwd' # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, }, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/mysql3.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either hashed_adminpwd => '31f7a65e315586ac198bd798b6629ce4903d0899476d5741a9f32e2e521b6a66', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite dbtype => 'mysql', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lstu', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype mysqldb => { database => 'lstu_db', host => 'mariadb', # # optional, default is 3306 # #port => 3306, user => 'lstu', pwd => 'lstu_pwd' # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, }, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. minion => { enabled => 1, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default mysqldb => { database => 'lstu_minion', host => 'mariadb', # optional, default is 3306 #port => 3306, user => 'lstu', pwd => 'lstu_pwd', }, }, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/postgresql1.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either adminpwd => 'toto', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either #hashed_adminpwd => '94b2feede6ea5e2eec62f457ecb7d3f719b24d19c29d4e5466246a31908fc23b', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite dbtype => 'postgresql', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype pgdb => { database => 'lstu_db', host => 'postgres', # # optional, default is 5432 # #port => 5432, user => 'lstu', pwd => 'lstu_pwd' # # optional, default is 1 # #max_connections => 1, }, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/postgresql2.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either hashed_adminpwd => '31f7a65e315586ac198bd798b6629ce4903d0899476d5741a9f32e2e521b6a66', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite dbtype => 'postgresql', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype pgdb => { database => 'lstu_db', host => 'postgres', # host => 'localhost', # # optional, default is 5432 user => 'lstu', pwd => 'lstu_pwd' # #max_connections => 1, #}, }, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/postgresql3.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either hashed_adminpwd => '31f7a65e315586ac198bd798b6629ce4903d0899476d5741a9f32e2e521b6a66', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite, postgresql and mysql (all lowercase) # optional, default is sqlite dbtype => 'postgresql', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db #db_path => 'lstu.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype pgdb => { database => 'lstu_db', host => 'postgres', # # optional, default is 5432 # #port => 5432, user => 'lstu', pwd => 'lstu_pwd' # # optional, default is 1 # #max_connections => 1, }, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. minion => { enabled => 1, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default pgdb => { database => 'lstu_minion', host => 'postgres', # # optional, default is 5432 # #port => 5432, user => 'lstu', pwd => 'lstu_pwd' }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # }, }, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/sqlite1.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either adminpwd => 'toto', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either #hashed_adminpwd => '94b2feede6ea5e2eec62f457ecb7d3f719b24d19c29d4e5466246a31908fc23b', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite and postgresql (all lowercase) # optional, default is sqlite #dbtype => 'sqlite', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db db_path => 'test1.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lstu', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/sqlite2.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either hashed_adminpwd => '31f7a65e315586ac198bd798b6629ce4903d0899476d5741a9f32e2e521b6a66', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite and postgresql (all lowercase) # optional, default is sqlite #dbtype => 'sqlite', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db db_path => 'test2.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lstu', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default # It will use the same DB type as Lstu: sqlite if you choose sqlite for `dbtype`, # postgresql for postgresql, etc. #minion => { # enabled => 0, # # SQLite ONLY - only used if if you choose sqlite as DB type, # # define the path to the minion database # # you can define it relative to lstu directory or set an absolute path # # remember that it has to be in a directory writable by Lutim user # # optional, default is minion.db # db_path => 'minion.db', # # PostgreSQL ONLY - only used if you choose postgresql as DB type # # these are the credentials to access the Minion's PostgreSQL database # # mandatory if you choosed postgresql as DB type, no default # pgdb => { # database => 'lstu_minion', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD' # }, # # MySQL ONLY - only used if you choose mysql as DB type # # these are the credentials to access the Minion's MySQL database # # mandatory if you choosed mysql as DB type, no default # mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # }, #}, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/sqlite3.conf ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: { #################### # Hypnotoad settings #################### # see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings hypnotoad => { # array of IP addresses and ports you want to listen to listen => ['http://127.0.0.1:8080'], # if you use Lstu behind a reverse proxy like Nginx, you want to set proxy to 1 # if you use Lstu directly, let it commented #proxy => 1, }, # put a way to contact you here and uncomment it # MANDATORY contact => 'admin[at]example.com', # array of random strings used to encrypt cookies # optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT #secret => ['fdjsofjoihrei'], # secret passphrase to access some admin features # If you don't want to have a plain text password in configuration, # use hashed_adminpwd instead # optional, but you won't have access to admin /stats if not set and if hashed_adminpwd is not set either #adminpwd => 's3cr3T', # secret hashed passphrase to access some admin features # Hash your password by issuing `echo -n s3cr3T | sha256sum` on your terminal # optional, but you won't have access to admin /stats if not set and if adminpwd is not set either hashed_adminpwd => '31f7a65e315586ac198bd798b6629ce4903d0899476d5741a9f32e2e521b6a66', # choose a theme. See the available themes in `themes` directory # optional, default is 'default' #theme => 'default', # number of URLs to be displayed per page in /stats # optional, default is 10 #page_offset => 10, # length of the random URL # optional, default is 8 #length => 8, # how many URLs will be provisioned in a batch ? # optional, default is 5 #provis_step => 5, # max number of URLs to be provisioned # optional, default is 100 #provisioning => 100, # URL sub-directory in which you want Lstu to be accessible # example: you want to have Lstu under https://example.org/lstu/ # => set prefix to '/lstu' or to '/lstu/', it doesn't matter # optional, defaut is / #prefix => '/', # array of authorized domains for API calls. # if you want to authorize everyone to use the API: ['*'] # optional, no domains allowed by default #allowed_domains => ['http://1.example.com', 'http://2.example.com'], # if set, the shortened URLs will use this domain # optional #fixed_domain => 'example.org', # choose what database you want to use # valid choices are sqlite and postgresql (all lowercase) # optional, default is sqlite #dbtype => 'sqlite', # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lstu directory or set an absolute path # remember that it has to be in a directory writable by Lstu user # optional, default is lstu.db db_path => 'test3.db', # PostgreSQL ONLY - only used if dbtype is set to postgresql # these are the credentials to access the PostgreSQL database # mandatory if you choosed postgresql as dbtype #pgdb => { # database => 'lstu', # host => 'localhost', # # optional, default is 5432 # #port => 5432, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 1 # #max_connections => 1, #}, # MySQL ONLY - only used if dbtype is set to mysql # these are the credentials to access the MySQL database # mandatory if you choosed mysql as dbtype #mysqldb => { # database => 'lstu', # host => 'localhost', # # optional, default is 3306 # #port => 3306, # #user => 'DBUSER', # #pwd => 'DBPASSWORD', # # optional, default is 5 (set to 0 to disable persistent connections) # #max_connections => 5, #}, # Rate-limiting for the API # After ban_min_strike requests in a second, the IP address will be # banned for one hour. # If it continues to query the API during this ban time at least # ban_min_strike times, it will be banned for a month. # optional, default is 3 #ban_min_strike => 3, # Ban whitelist # You can whitelist IP addresses to prevent you from being banned # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_whitelist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_whitelist => [], # Ban blacklist # You can blacklist IP addresses to always ban those IP addresses # Be careful, the IP addresses are compared as string, not as IP addresses # a network range will not work # Example of valid input: ban_blacklist => ['198.51.100.42', '2001:0DB8::42'],¬ # optional, default is an empty array #ban_blacklist => [], # define an URL to the Piwik instance and the ID of a website to track # set if you want to track views in Piwik # optional, Piwik tracking is disabled by default #piwik => { # url => 'http://piwik.example.com', # idsite => '1', #}, # use Minion instead of directly increase counters # need to launch a minion worker service if enabled # optional, Minion is disabled by default minion => { enabled => 1, db_path => 'minion.db' }, # set `ldap` if you want that only authenticated users can shorten URLs # please note that everybody can still use shortend URLs # optional, no default #ldap => { uri => 'ldap://rroemhild-test-openldap:10389', user_tree => 'ou=people,dc=planetexpress,dc=com', bind_dn => 'cn=admin,dc=planetexpress,dc=com', bind_pwd => 'GoodNewsEveryone', user_attr => 'uid', user_filter => '(!(uid=admin))', }, #ldap => { # uri => 'ldaps://ldap.example.org', # server URI # user_tree => 'ou=users,dc=example,dc=org', # search base DN # bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN # bind_pwd => 'secr3t', # search bind password # user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.) # user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.) #}, # set `htpasswd` if you want to use an htpasswd file instead of ldap # create the file with `htpasswd -c lstu.passwd user`, update it with `htpasswd lstu.passwd user2` # make sure that lstu can read the file! # optional, no default #htpasswd => 'lstu.passwd', # if you've set ldap or htpasswd above, the session will last `session_duration` seconds before # the user needs to reauthenticate # optional, default is 3600 #session_duration => 3600, # how many redirections are allowed for the shortened URL before considering it as a spam? # optional, default is 2. Set to -1 to allow infinite redirections (not recommended) #max_redir => 2, # spam blacklist regex. All URLs (or redirection) whose host part matches this regex are considered as spam # optional, no default #spam_blacklist_regex => 'foo|bar', # spam path blacklist regex. All URLs (or redirection) whose path part matches this regex are considered as spam # optional, no default #spam_path_blacklist_regex => 'foo|bar', # spam whitelist regex. All URLs (or redirection) whose host part matches this regex will never be considered as spam # optional, no default #spam_whitelist_regex => 'foo|bar', # set to 1 to skip SpamHaus check (not recommended) # optional, default is 0 #skip_spamhaus => 0, # put your Google API key to enable Google safebrowsing check # This will allow Lstu to download the Google safebrowsing database and use a local copy to check the URLs. # Google does not get the URLs that are checked. # Instructions to get a key: https://developers.google.com/safe-browsing/v4/get-started # TL;DR: https://console.developers.google.com/projectselector/apis/library # optional, no default #safebrowsing_api_key => '', # array of memcached servers to cache URL in order to accelerate responses to often-viewed URL. # If set to [], the cache is disabled # optional, default is [] #memcached_servers => [], # Content-Security-Policy header that will be sent by Lstu # Set to '' to disable CSP header # https://content-security-policy.com/ provides a good documentation about CSP. # https://report-uri.com/home/generate provides a tool to generate a CSP header. # optional, default is "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'" # the default value is good for `default` and `milligram` themes #csp => "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; form-action 'self'; base-uri 'self'", # X-Frame-Options header that will be sent by Lstu # Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/' # Set to '' to disable X-Frame-Options header # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly # to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) # optional, default is 'DENY' #x_frame_options => 'DENY', # X-Content-Type-Options that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # Set to '' to disable X-Content-Type-Options header # optional, default is 'nosniff' #x_content_type_options => 'nosniff', # X-XSS-Protection that will be sent by Lstu # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # Set to '' to disable X-XSS-Protection header # optional, default is '1; mode=block' #x_xss_protection => '1; mode=block', # Log creator's IP address # Set to 1 if you want to register the IP addresses of URL creators # optional, default is 0 #log_creator_ip => 0, }; ================================================ FILE: t/test.t ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: use Mojo::Base -strict; use Mojo::JSON qw(true false); use Mojo::File; use Mojo::URL; use Mojolicious; use Test::More; use Test::Mojo; use Lstu::DB::URL; use Lstu::DB::Ban; use Lstu::DB::Session; use Lstu::DefaultConfig qw($default_config); use FindBin qw($Bin); use File::Spec::Functions; my ($m, $cfile); BEGIN { use lib 'lib'; $m = Mojolicious->new; $cfile = Mojo::File->new($Bin, '..' , 'lstu.conf'); if (defined $ENV{MOJO_CONFIG}) { $cfile = Mojo::File->new($ENV{MOJO_CONFIG}); unless (-e $cfile->to_abs) { $cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG}); } } my $config = $m->plugin('Config' => { file => $cfile->to_abs->to_string, default => $default_config } ); $m->plugin('Lstu::Plugin::Helpers'); $m->plugin('DebugDumperHelper'); } Lstu::DB::URL->new(app => $m)->delete_all; Lstu::DB::Ban->new(app => $m)->delete_all; my $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; # Home page $t->get_ok('/') ->status_is(200) ->header_is('X-Frame-Options' => 'DENY') ->header_is('X-XSS-Protection' => '1; mode=block') ->header_is('X-Content-Type-Options' => 'nosniff') ->header_is('Content-Security-Policy' => "base-uri 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' data:; script-src 'self'; style-src 'self'") ->content_like(qr/Lstu/i); # Test robots.txt $t->get_ok('/robots.txt') ->status_is(404); $t->get_ok('/robots') ->status_is(404); # Create short URL $t->post_ok('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://lstu.fr') ->json_like('/short' => qr#http://127\.0\.0\.1:\d+/[-_a-zA-Z0-9]{8}#); # Create short URL with a .onion URL $t->post_ok('/a' => form => { lsturl => 'http://lstupiioqgxmq66f.onion', 'lsturl-custom' => 'onion', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://lstupiioqgxmq66f.onion', '/short' => 'http://127.0.0.1/onion'); Lstu::DB::Ban->new(app => $m)->delete_all; # prevents ban # Create short URL with robots custom short $t->post_ok('/a' => form => { lsturl => 'http://robots-txt.com/', 'lsturl-custom' => 'robots', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://lstu.fr', '/short' => 'http://127.0.0.1/robots'); # Test robots.txt even after creating a robots short URL $t->get_ok('/robots.txt') ->status_is(404); $t->get_ok('/robots') ->status_is(301); # Create short URL, with invalid argument # Create short URL, with invalid argument $t->post_ok('/a' => form => { lsturl => 'truc', _format => 'json' }) ->status_is(200) ->json_has('/msg', '/success') ->json_is({success => false, msg => 'truc is not a valid URL.'}); Lstu::DB::Ban->new(app => $m)->delete_all; # prevents ban # Give time to provision some short URLs sleep 5; # Create short URL my $a = $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' })->res->json('/short'); # Test redirection $t->get_ok($a) ->status_is(301); # Test JSON answer $t->get_ok($a.'?_format=json') ->status_is(200) ->json_is({success => true, url => 'https://lstu.fr'}); # Extract the path of the short URL my $short = Mojo::URL->new($a)->path(); # Get stats about the short URL $t->get_ok('/stats'.$short) ->status_is(200) ->json_has('/success', '/short', '/url', '/counter', '/created_at', '/timestamp') ->json_is('/success' => true, '/url' => 'https://lstu.fr', '/short' => $a, '/counter' => 1) ->json_like('/created_at' => qr#[0-9]{10}#, '/timestamp' => qr#[0-9]{10}#); # Test JSON answer on invalid short URL $t->get_ok($a.'i?_format=json') ->status_is(404) ->json_is({success => false, msg => 'The shortened URL '.$a.'i doesn\'t exist.'}); # Get stats about an invalid short URL $t->get_ok('/stats'.$short.'i') ->status_is(200) ->json_is({success => false, msg => 'The shortened URL '.$a.'i doesn\'t exist.'}); # Test full stats $t->get_ok('/fullstats') ->status_is(200) ->json_has('/urls', '/empty', '/timestamp') ->json_is('/urls' => 3) ->json_like('/empty' => qr#\d+#, '/timestamp' => qr#[0-9]{10}#); # Needed if we use Minion for increasing counters $t->app->minion->perform_jobs if ($t->app->config('minion') && $t->app->config('minion')->{enabled}); # Get stats in JSON format $t->get_ok('/stats?_format=json') ->status_is(200) ->json_has('/0/created_at', '/0/counter', '/0/short', '/0/url', '/0/qrcode') ->json_is('/0/url' => 'https://lstu.fr', '/0/short' => $a) ->json_is('/0/counter' => 2) ->json_like('/0/created_at' => qr#\d+#); # Extract the path of the short URL my $b = $a; $b = Mojo::URL->new($a)->path(); $t->ua->max_redirects(1); # Try to delete short URL while not admin $t->get_ok('/d'.$b) ->status_is(200) ->content_like(qr/You're not authenticated as the admin/); # Login as admin $t->post_ok('/stats' => form => { adminpwd => 'toto', page => 0 }) ->status_is(200) ->content_like(qr/$a/); # Delete short URL while admin $t->get_ok('/d'.$b) ->status_is(200) ->content_like(qr/WTFPL/); # Verify that short URL does not exists anymore $t->get_ok($a.'?_format=json') ->status_is(404) ->json_is({success => false, msg => 'The shortened URL '.$a.' no longer exists.'}); # Create short URL $a = $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' })->res->json('/short'); # Extract the path of the short URL $a = Mojo::URL->new($a)->path(); # Delete short URL while admin, JSON format $t->get_ok('/d'.$a.'?_format=json') ->status_is(200) ->json_is({success => true, deleted => 1}); # Logout $t->post_ok('/stats' => form => { adminpwd => 'toto', action => 'logout' }) ->status_is(200); # Test admin ban Lstu::DB::Ban->new(app => $m)->delete_all; for my $i (1..3) { # Login three times with a bad password $t->post_ok('/stats' => form => { adminpwd => 'totoi' }) ->status_is(200) ->content_like(qr/Bad password/); } # Login with a bad password, should be banned now $t->post_ok('/stats' => form => { adminpwd => 'totoi' }) ->status_is(200) ->content_like(qr/Too many bad passwords\./); # Test user ban Lstu::DB::Ban->new(app => $m)->delete_all; # reset ban Lstu::DB::URL->new(app => $m)->delete_all; $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->post_ok('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }) ->status_is(200) ->json_has('/msg', '/success') ->json_is('/success' => false) ->json_like('/msg' => qr#You asked to shorten too many URLs too quickly\. You're banned for \d+ hour\(s\)\.#); Lstu::DB::Ban->new(app => $m)->delete_all; # reset ban # Give time to provision some short URLs sleep 3; # Create short URL, JSON format $t->post_ok('/a' => form => { lsturl => ' https://fiat-tux.fr', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://fiat-tux.fr') ->json_like('/short' => qr#http://127\.0\.0\.1:\d+/[-_a-zA-Z0-9]{8}#); # Test htpasswd my $config_file = Mojo::File->new($cfile->to_abs->to_string); my $config_content = $config_file->slurp; my $config_orig = $config_content; $config_content =~ s/#?htpasswd.*/htpasswd => 't\/lstu.passwd',/gm; $config_file->spew($config_content); Lstu::DB::Ban->new(app => $m)->delete_all; # reset ban Lstu::DB::URL->new(app => $m)->delete_all; $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; $t->get_ok('/') ->status_is(302); $t->get_ok('/login') ->status_is(200) ->content_like(qr/Login/); $t->post_ok('/login' => form => { login => 'luc', password => 'titi' }) ->status_is(200) ->content_like(qr/Please, check your credentials: unable to authenticate\./); $t->post_ok('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }) ->status_is(302); $t->post_ok('/login' => form => { login => 'luc', password => 'toto' }) ->status_is(302); $t->post_ok('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://lstu.fr') ->json_like('/short' => qr#http://127\.0\.0\.1:\d+/[-_a-zA-Z0-9]{8}#); $t->get_ok('/logout') ->status_is(200) ->content_like(qr/You have been successfully logged out\./); # Test IP whitelisting $config_content = $config_orig; $config_content =~ s/^( +)#?ban_whitelist.*/$1ban_whitelist => ['::1', '127.0.0.1'],/gm; $config_file->spew($config_content); $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->post_ok('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://lstu.fr') ->json_like('/short' => qr#http://127\.0\.0\.1:\d+/[-_a-zA-Z0-9]{8}#); $config_file->spew($config_orig); # Test IP blacklisting $config_content = $config_orig; $config_content =~ s/^( +)#?ban_blacklist.*/$1ban_blacklist => ['::1', '127.0.0.1'],/gm; $config_file->spew($config_content); $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }); $t->post_ok('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }) ->status_is(200) ->json_has('/msg', '/success') ->json_is('/success' => false) ->json_like('/msg' => qr#You asked to shorten too many URLs too quickly\. You're banned for \d+ hour\(s\)\.#); $config_file->spew($config_orig); # Test domain blacklisting Lstu::DB::Ban->new(app => $m)->delete_all; $config_content = $config_orig; $config_content =~ s/^( +)#?spam_blacklist_regex.*/$1spam_blacklist_regex => 'google\\.(fr|com)',/gm; $config_file->spew($config_content); $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; $t->post_ok('/a' => form => { lsturl => 'https://google.fr', _format => 'json' }) ->status_is(200) ->json_has('/msg', '/success') ->json_is({success => false, msg => 'The URL you want to shorten comes from a domain (google.fr) that is blacklisted on this server (usually because of spammers that use this domain).'}); $t->post_ok('/a' => form => { lsturl => 'https://google.com', _format => 'json' }) ->status_is(200) ->json_has('/msg', '/success') ->json_is({success => false, msg => 'The URL you want to shorten comes from a domain (google.com) that is blacklisted on this server (usually because of spammers that use this domain).'}); $t->post_ok('/a' => form => { lsturl => 'https://google.de', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://google.de') ->json_like('/short' => qr#http://127\.0\.0\.1:\d+/[-_a-zA-Z0-9]{8}#); # Test domain whitelisting Lstu::DB::Ban->new(app => $m)->delete_all; $config_content =~ s/^( +)#?spam_whitelist_regex.*/$1spam_whitelist_regex => 'google\.fr',/gm; $config_file->spew($config_content); $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; $t->post_ok('/a' => form => { lsturl => 'https://google.fr', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://google.fr') ->json_like('/short' => qr#http://127\.0\.0\.1:\d+/[-_a-zA-Z0-9]{8}#); $t->post_ok('/a' => form => { lsturl => 'https://google.com', _format => 'json' }) ->status_is(200) ->json_has('/msg', '/success') ->json_is({success => false, msg => 'The URL you want to shorten comes from a domain (google.com) that is blacklisted on this server (usually because of spammers that use this domain).'}); # Test Cache system $config_content =~ s/^( +)#?memcached_servers => \[\],/memcached_servers => ['127.0.0.1:11211'],/gm; $config_file->spew($config_content); $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; $a = $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' })->res->json('/short'); $t->get_ok($a) ->status_is(301); # Test command my $help = `carton exec script/lstu help url`; like($help, qr/Print infos about the space-separated URLs/m, 'Test help url command'); # Create short URL $a = $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' })->res->json('/short'); # Extract the path of the short URL $a =~ s#.*/##; my $info = `MOJO_CONFIG=$cfile carton exec script/lstu url --info $a`; like($info, qr/lstu\.fr/m, 'Test url --info command'); my $search = `MOJO_CONFIG=$cfile carton exec script/lstu url --search lstu.fr`; like($search, qr/$a/m, 'Test url --search command'); my $remove = `MOJO_CONFIG=$cfile carton exec script/lstu url --remove $a --yes`; like($remove, qr/Success/m, 'Test url --remove command'); # Restore configuration $config_file->spew($config_orig); ## Test LDAP $config_content = $config_orig; $config_content =~ s/^( +)#?ldap => \{ uri/$1ldap => { uri/gm; $config_file->spew($config_content); Lstu::DB::URL->new(app => $m)->delete_all; $t = Test::Mojo->new('Lstu'); # Give time to provision some short URLs sleep 3; $t->get_ok('/') ->status_is(302); $t->get_ok('/login') ->status_is(200) ->content_like(qr/Signin/i); $t->post_ok('/a' => form => { lsturl => 'https://google.com', _format => 'json' }) ->status_is(302); $t->post_ok('/login' => form => { login => 'zoidberg', password => 'zoidberg' }) ->status_is(302); # # Create short URL $t->post_ok('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' }) ->status_is(200) ->json_has('/url', '/short', '/success', '/qrcode') ->json_is('/success' => true, '/url' => 'https://lstu.fr') ->json_like('/short' => qr#http://127\.0\.0\.1:\d+/[-_a-zA-Z0-9]{8}#); $a = $t->ua->post('/a' => form => { lsturl => 'https://lstu.fr', _format => 'json' })->res->json('/short'); # Test redirection $t->get_ok($a) ->status_is(301); $t->get_ok('/logout') ->status_is(200) ->content_like(qr/You have been successfully logged out\./); $t->get_ok('/') ->status_is(302); $t->get_ok('/login') ->status_is(200) ->content_like(qr/Signin/i); # Test redirection $t->get_ok($a) ->status_is(301); # Restore configuration $config_file->spew($config_orig); ## Test headers modifications $config_content = $config_orig; $config_content =~ s/^( +)#?x_frame_options => 'DENY',/$1x_frame_options => 'SAMEORIGIN',/gm; $config_content =~ s/^( +)#?x_xss_protection => '1; mode=block',/$1x_xss_protection => '1',/gm; $config_content =~ s/^( +)#?x_content_type_options => 'nosniff',/$1x_content_type_options => '',/gm; $config_file->spew($config_content); $t = Test::Mojo->new('Lstu'); # Home page $t->get_ok('/') ->status_is(200) ->header_is('X-Frame-Options' => 'SAMEORIGIN') ->header_is('X-XSS-Protection' => '1') ->header_isnt('X-Content-Type-Options' => 'nosniff') ->header_is('Content-Security-Policy' => "base-uri 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; script-src 'self'; style-src 'self'"); # Restore configuration $config_file->spew($config_orig); done_testing(); ================================================ FILE: themes/default/lib/Lstu/I18N/br.po ================================================ # Lstu # Copyright (C) 2013 - 2015 Luc Didry # This file is distributed under the same license as the Lstu package. # # Translators: # Translators: # Gwenn M , 2016 # Luc Didry , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2023-11-23 10:03+0000\n" "Last-Translator: Florent Grouin \n" "Language-Team: Breton \n" "Language: br\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.2\n" "X-Poedit-SourceCharset: UTF-8\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 n'eo ket un URL talvoudek." #: themes/default/templates/api.html.ep:96 msgid "" "A page with a table containing the same informations that the JSON response" msgstr "" #: themes/default/templates/layouts/default.html.ep:38 msgid "About" msgstr "A-zivout" #: themes/default/templates/stats.html.ep:13 themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Ger-tremen ar merour" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Ger-tremen didalvoudek" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Eilañ er golver" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "Konter" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Krouet" #: themes/default/templates/api.html.ep:44 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Testenn verr personelaet" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Dilemel" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "Ezporzhiañ hoc’h URLoù" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Abeg ar c'hwitadenn" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Restr bet emporzhiet" #: themes/default/templates/api.html.ep:101 msgid "" "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" #: themes/default/templates/index.html.ep:12 themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "Deomp !" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Pouezit war Enankañ, ha Reol+C evit eilañ an ere berr" #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Degemer" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "" #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "Ma 'z eo termenet \"adminpwd\" :" #: themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:88 msgid "" "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "M'eo kevatal da \"json\" e vo ar respont er mentrezh JSON, e mod all e vo e " "HTML" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "" "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), " "it will give you statistics for all URLs, sorted by the most visited first, " "paginated with pages containing %1 records." msgstr "" #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" #: themes/default/templates/stats.html.ep:32 themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "Enporzhiañ URLoù" #: themes/default/templates/api.html.ep:79 msgid "" "It will use Lstu's cookies to know which shortened URL it will return " "statistics for." msgstr "" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:18 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON : c'hwitadenn" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:144 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:31 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON : berzh" #: themes/default/templates/layouts/default.html.ep:37 msgid "License:" msgstr "Lañvaz :" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Kevreañ" #: themes/default/templates/layouts/default.html.ep:43 msgid "Logout" msgstr "Digevreañ" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Digennaskañ eus stadegoù an ardoer" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Da-heul" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "" "No shortened URL available. Please retry or contact the administrator at %1. " "Your URL to shorten: %2" msgstr "" "N'eus URL berr ebet hegerz. Klaskit en-dro pe kit e darempred gant an ardoer " "e %1. Hoc'h URL da verraat : %2" #: themes/default/templates/api.html.ep:111 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:41 themes/default/templates/api.html.ep:83 themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Arventennoù :" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Ger-tremen" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "" #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Kent" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "Kod QR" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:32 themes/default/templates/api.html.ep:54 themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Respont evit ar mentrezh HTML" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Respont evit ar furmad JSON" #: themes/default/templates/api.html.ep:102 themes/default/templates/api.html.ep:117 themes/default/templates/api.html.ep:142 themes/default/templates/api.html.ep:15 themes/default/templates/api.html.ep:29 themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:59 themes/default/templates/api.html.ep:68 themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Respont :" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "URL berraet" #: themes/default/templates/api.html.ep:127 themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Rankout a rafe kas en-dro un disoc'h gant berzh" #: themes/default/templates/layouts/default.html.ep:45 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Lugañ" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "" #: themes/default/templates/layouts/default.html.ep:42 themes/default/templates/layouts/default.html.ep:48 themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Stadegoù" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "" "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I " "refuse to shorten it." msgstr "" "Ostiz an URL pe unan eus e adheñchadennoù (%1) a zo e roll du Spamhaus. Ne " "fell ket din e verraat." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "" "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe " "Browsing database. I refuse to shorten it." msgstr "" #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "" "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, " "phishing, etc.). I refuse to shorten it." msgstr "" "Adheñchet eo an URL %1 gwech pe ouzhpenn. Moarvat eo un URL arvarus (spam, " "phishing...). Ne fell ket din e verraat." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "" "The URL you want to shorten comes from a domain (%1) that is blacklisted on " "this server (usually because of spammers that use this domain)." msgstr "" #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 lib/Lstu/Controller/URL.pm:226 msgid "The shortened URL %1 doesn't exist." msgstr "N'eus ket eus an URL berraet %1." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "" "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", " "\"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to " "shorten: %1" msgstr "" #: themes/default/templates/api.html.ep:113 msgid "" "To do an admin login, set it to the password defined in the settings " "(\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "" #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Re a ger-tremen didalvoudek. Forbannet oc'h." #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:43 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "URL da verraat" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "" "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." "" msgstr "" "Goulennet hoc'h eus da verraat re a URLoù en un amzer re verr. Forbannet " "hoc'h e-pad %1 eur." #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "" #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:31 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "" #: themes/default/templates/api.html.ep:137 msgid "" "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) " "to use it." msgstr "" #: themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "" #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "" #: themes/default/templates/api.html.ep:146 msgid "" "You will be redirected to Lstu statistics page with a message in case of " "failure" msgstr "" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "" #: themes/default/templates/api.html.ep:19 msgid "" "You will be redirected to the Lstu classic interface where you will be able " "to shorten URLs" msgstr "" #: themes/default/templates/api.html.ep:54 msgid "" "You will be redirected to the Lstu classic interface with a message giving " "the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:62 msgid "" "You will be redirected to the targeted URL or to the Lstu interface with a " "message giving the failure reason" msgstr "" "Adheñchet e viot davet an URL bukenn pe ketal Ltsu gant ur gemennadenn a " "roio abeg ar c'hwitadenn" #: themes/default/templates/api.html.ep:123 msgid "" "You will have the statistics page with the admin stats if you're succesfully " "logged in, or your stats and a failure message otherwise" msgstr "" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:11 themes/default/templates/api.html.ep:12 themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "ret" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:114 themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:44 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:86 themes/default/templates/api.html.ep:88 msgid "optional" msgstr "diret" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:57 themes/default/templates/api.html.ep:66 themes/default/templates/api.html.ep:92 themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "url_berraet" ================================================ FILE: themes/default/lib/Lstu/I18N/de.po ================================================ # Nico Domino , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2021-04-28 09:40+0000\n" "Last-Translator: Luc Didry \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.6\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 ist keine gültige URL." #: themes/default/templates/api.html.ep:96 msgid "" "A page with a table containing the same informations that the JSON response" msgstr "" "Eine Seite mit einer Tabelle mit derselben Info als in der JSON Antwort" #: themes/default/templates/layouts/default.html.ep:38 msgid "About" msgstr "Über" #: themes/default/templates/stats.html.ep:13 #: themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Admin Passwort" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Falsches Passwort" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Zu Zwischenablage Speichern" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "Zähler" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Erstellt" #: themes/default/templates/api.html.ep:44 #: themes/default/templates/index.html.ep:10 #: themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Benutzerdefinierter verkürzter Text" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Löschen" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "URLs exportieren" #: themes/default/templates/api.html.ep:105 #: themes/default/templates/api.html.ep:145 #: themes/default/templates/api.html.ep:53 #: themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Fehlergrund" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Datei wurde importiert" #: themes/default/templates/api.html.ep:101 msgid "" "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" "Ermitteln Sie die Details (Besuchszähler, Erstellungsdatum usw.) einer " "verkürzten URL." #: themes/default/templates/index.html.ep:12 #: themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "Go!" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Enter drücken, dann Strg+C um den kurzen Link zu kopieren." #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Heim" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "Wenn \"aktion\" als \"logout\" definiert ist." #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "Wenn \"adminpwd\" definiert ist:" #: themes/default/templates/api.html.ep:115 #: themes/default/templates/api.html.ep:13 #: themes/default/templates/api.html.ep:140 #: themes/default/templates/api.html.ep:27 #: themes/default/templates/api.html.ep:45 #: themes/default/templates/api.html.ep:88 msgid "" "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "" "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), " "it will give you statistics for all URLs, sorted by the most visited first, " "paginated with pages containing %1 records." msgstr "" #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" #: themes/default/templates/stats.html.ep:32 #: themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "URLs importieren" #: themes/default/templates/api.html.ep:79 msgid "" "It will use Lstu's cookies to know which shortened URL it will return " "statistics for." msgstr "" #: themes/default/templates/api.html.ep:105 #: themes/default/templates/api.html.ep:122 #: themes/default/templates/api.html.ep:145 #: themes/default/templates/api.html.ep:18 #: themes/default/templates/api.html.ep:53 #: themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON: Fehler" #: themes/default/templates/api.html.ep:104 #: themes/default/templates/api.html.ep:121 #: themes/default/templates/api.html.ep:129 #: themes/default/templates/api.html.ep:144 #: themes/default/templates/api.html.ep:17 #: themes/default/templates/api.html.ep:31 #: themes/default/templates/api.html.ep:52 #: themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON: Erfolgreich" #: themes/default/templates/layouts/default.html.ep:37 msgid "License:" msgstr "Lizenz:" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Login" #: themes/default/templates/layouts/default.html.ep:43 msgid "Logout" msgstr "Logout" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Von Admin Statistik Ausloggen" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Nächst" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "" "No shortened URL available. Please retry or contact the administrator at %1. " "Your URL to shorten: %2" msgstr "" #: themes/default/templates/api.html.ep:111 #: themes/default/templates/api.html.ep:138 #: themes/default/templates/api.html.ep:25 #: themes/default/templates/api.html.ep:41 #: themes/default/templates/api.html.ep:83 #: themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Parameter" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Passwort" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "Bitte überprüfen Sie Ihre Logindaten: konnte nicht Authentifizieren." #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Bisherige" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "QR-Code" #: themes/default/templates/api.html.ep:123 #: themes/default/templates/api.html.ep:130 #: themes/default/templates/api.html.ep:146 #: themes/default/templates/api.html.ep:19 #: themes/default/templates/api.html.ep:32 #: themes/default/templates/api.html.ep:54 #: themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Antwort zu HTML Format" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Antwort zu JSON Format" #: themes/default/templates/api.html.ep:102 #: themes/default/templates/api.html.ep:117 #: themes/default/templates/api.html.ep:142 #: themes/default/templates/api.html.ep:15 #: themes/default/templates/api.html.ep:29 #: themes/default/templates/api.html.ep:47 #: themes/default/templates/api.html.ep:59 #: themes/default/templates/api.html.ep:68 #: themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Antwort:" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "Verkürzte URL" #: themes/default/templates/api.html.ep:127 #: themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Sollte immer erfolgreich sein" #: themes/default/templates/layouts/default.html.ep:45 #: themes/default/templates/login.html.ep:16 #: themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Einloggen" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "Entschuldigen Sie, das Lesen der Datei schlug fehl" #: themes/default/templates/layouts/default.html.ep:42 #: themes/default/templates/layouts/default.html.ep:48 #: themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Statistiken" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "" "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I " "refuse to shorten it." msgstr "" #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "" "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe " "Browsing database. I refuse to shorten it." msgstr "" #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "" "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, " "phishing, etc.). I refuse to shorten it." msgstr "" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "" "The URL you want to shorten comes from a domain (%1) that is blacklisted on " "this server (usually because of spammers that use this domain)." msgstr "" #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 #: lib/Lstu/Controller/URL.pm:226 msgid "The shortened URL %1 doesn't exist." msgstr "Die verkürzte URL %1 existiert nicht." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "" "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", " "\"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to " "shorten: %1" msgstr "" #: themes/default/templates/api.html.ep:113 msgid "" "To do an admin login, set it to the password defined in the settings " "(\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "" #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Zu viele fehlerhafte Passworte. Sie sind nun geblockt." #: themes/default/templates/api.html.ep:104 #: themes/default/templates/api.html.ep:95 #: themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:43 #: themes/default/templates/index.html.ep:5 #: themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "" "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." msgstr "" #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 #: themes/default/templates/api.html.ep:121 #: themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "" #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 #: themes/default/templates/api.html.ep:129 #: themes/default/templates/api.html.ep:31 #: themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "" #: themes/default/templates/api.html.ep:137 msgid "" "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) " "to use it." msgstr "" #: themes/default/templates/api.html.ep:39 #: themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "" #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "" #: themes/default/templates/api.html.ep:146 msgid "" "You will be redirected to Lstu statistics page with a message in case of " "failure" msgstr "" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "" #: themes/default/templates/api.html.ep:19 msgid "" "You will be redirected to the Lstu classic interface where you will be able " "to shorten URLs" msgstr "" #: themes/default/templates/api.html.ep:54 msgid "" "You will be redirected to the Lstu classic interface with a message giving " "the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:62 msgid "" "You will be redirected to the targeted URL or to the Lstu interface with a " "message giving the failure reason" msgstr "" #: themes/default/templates/api.html.ep:123 msgid "" "You will have the statistics page with the admin stats if you're succesfully " "logged in, or your stats and a failure message otherwise" msgstr "" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "" #: themes/default/templates/api.html.ep:104 #: themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:11 #: themes/default/templates/api.html.ep:12 #: themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "" #: themes/default/templates/api.html.ep:113 #: themes/default/templates/api.html.ep:114 #: themes/default/templates/api.html.ep:115 #: themes/default/templates/api.html.ep:13 #: themes/default/templates/api.html.ep:140 #: themes/default/templates/api.html.ep:27 #: themes/default/templates/api.html.ep:44 #: themes/default/templates/api.html.ep:45 #: themes/default/templates/api.html.ep:86 #: themes/default/templates/api.html.ep:88 msgid "optional" msgstr "" #: themes/default/templates/api.html.ep:135 #: themes/default/templates/api.html.ep:57 #: themes/default/templates/api.html.ep:66 #: themes/default/templates/api.html.ep:92 #: themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "" #: themes/default/templates/layouts/default.html.ep:50 msgid "Browser extensions" msgstr "Browser Erweiterung" #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:240 msgid "The shortened URL %1 no longer exists." msgstr "Die verkürzte URL %1 existiert nicht mehr." ================================================ FILE: themes/default/lib/Lstu/I18N/en.po ================================================ # Luc Didry , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-11-22 02:48+0000\n" "Last-Translator: Luc Didry \n" "Language-Team: English\n" "Language: en\n" "X-Generator: Zanata 4.6.2\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 is not a valid URL." #: themes/default/templates/api.html.ep:96 msgid "" "A page with a table containing the same informations that the JSON response" msgstr "" "A page with a table containing the same informations that the JSON response" #: themes/default/templates/layouts/default.html.ep:38 msgid "About" msgstr "About" #: themes/default/templates/stats.html.ep:13 themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Admin password" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Bad password" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Copy to clipboard" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "Counter" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Created" #: themes/default/templates/api.html.ep:44 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Custom shortened text" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Delete" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "Export your URLs" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Failure reason" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "File imported" #: themes/default/templates/api.html.ep:101 msgid "" "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" "Get the details (visit counter, creation date, etc.) of a shortened URL." #: themes/default/templates/index.html.ep:12 themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "Go!" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Hit Enter, then Ctrl+C to copy the short link" #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Home" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "If \"action\" is defined to \"logout\":" #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "If \"adminpwd\" is defined:" #: themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:88 msgid "" "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "If equal to \"json\", response will be in JSON format, HTML format otherwise" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "" "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), " "it will give you statistics for all URLs, sorted by the most visited first, " "paginated with pages containing %1 records." msgstr "" "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), " "it will give you statistics for all URLs, sorted by the most visited first, " "paginated with pages containing %1 records." #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "If you are logged in as admin, you can provide a \"page\" parameter" #: themes/default/templates/stats.html.ep:32 themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "Import URLs" #: themes/default/templates/api.html.ep:79 msgid "" "It will use Lstu's cookies to know which shortened URL it will return " "statistics for." msgstr "" "It will use Lstu's cookies to know which shortened URL it will return " "statistics for." #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:18 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON: failure" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:144 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:31 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON: success" #: themes/default/templates/layouts/default.html.ep:37 msgid "License:" msgstr "License:" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Login" #: themes/default/templates/layouts/default.html.ep:43 msgid "Logout" msgstr "Logout" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Logout from admin stats" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Next" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "" "No shortened URL available. Please retry or contact the administrator at %1. " "Your URL to shorten: %2" msgstr "" "No shortened URL available. Please retry or contact the administrator at %1. " "Your URL to shorten: %2" #: themes/default/templates/api.html.ep:111 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:41 themes/default/templates/api.html.ep:83 themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Parameters:" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Password" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "Please, check your credentials: unable to authenticate." #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Previous" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "QRCode" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:32 themes/default/templates/api.html.ep:54 themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Response for HTML format" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Response for JSON format" #: themes/default/templates/api.html.ep:102 themes/default/templates/api.html.ep:117 themes/default/templates/api.html.ep:142 themes/default/templates/api.html.ep:15 themes/default/templates/api.html.ep:29 themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:59 themes/default/templates/api.html.ep:68 themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Response:" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "Shortened URL" #: themes/default/templates/api.html.ep:127 themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Should always be successful" #: themes/default/templates/layouts/default.html.ep:45 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Signin" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "Sorry, unable to parse the provided file" #: themes/default/templates/layouts/default.html.ep:42 themes/default/templates/layouts/default.html.ep:48 themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Statistics" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "" "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I " "refuse to shorten it." msgstr "" "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I " "refuse to shorten it." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "" "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe " "Browsing database. I refuse to shorten it." msgstr "" "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe " "Browsing database. I refuse to shorten it." #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "" "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, " "phishing, etc.). I refuse to shorten it." msgstr "" "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, " "phishing, etc.). I refuse to shorten it." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "" "The URL you want to shorten comes from a domain (%1) that is blacklisted on " "this server (usually because of spammers that use this domain)." msgstr "" "The URL you want to shorten comes from a domain (%1) that is blacklisted on " "this server (usually because of spammers that use this domain)." #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 lib/Lstu/Controller/URL.pm:226 msgid "The shortened URL %1 doesn't exist." msgstr "The shortened URL %1 doesn't exist." #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:240 msgid "The shortened URL %1 no longer exists." msgstr "The shortened URL %1 no longer exists." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "" "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", " "\"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to " "shorten: %1" msgstr "" "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", " "\"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to " "shorten: %1" #: themes/default/templates/api.html.ep:113 msgid "" "To do an admin login, set it to the password defined in the settings " "(\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" "To do an admin login, set it to the password defined in the settings " "(\"adminpwd\" or \"hashed_adminpwd\")" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "To do an admin logout, set it to \"logout\"." #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Too many bad passwords. You're banned." #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:43 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "URL to shorten" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "" "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." "" msgstr "" "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." "" #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "You have been successfully logged in." #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:31 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "You have been successfully logged out." #: themes/default/templates/api.html.ep:137 msgid "" "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) " "to use it." msgstr "" "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) " "to use it." #: themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "You must be logged in to use it." #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "You will be redirected to Lstu statistics page" #: themes/default/templates/api.html.ep:146 msgid "" "You will be redirected to Lstu statistics page with a message in case of " "failure" msgstr "" "You will be redirected to Lstu statistics page with a message in case of " "failure" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "You will be redirected to Lstu successfully logged out interface" #: themes/default/templates/api.html.ep:19 msgid "" "You will be redirected to the Lstu classic interface where you will be able " "to shorten URLs" msgstr "" "You will be redirected to the Lstu classic interface where you will be able " "to shorten URLs" #: themes/default/templates/api.html.ep:54 msgid "" "You will be redirected to the Lstu classic interface with a message giving " "the shortened URL" msgstr "" "You will be redirected to the Lstu classic interface with a message giving " "the shortened URL" #: themes/default/templates/api.html.ep:62 msgid "" "You will be redirected to the targeted URL or to the Lstu interface with a " "message giving the failure reason" msgstr "" "You will be redirected to the targeted URL or to the Lstu interface with a " "message giving the failure reason" #: themes/default/templates/api.html.ep:123 msgid "" "You will have the statistics page with the admin stats if you're succesfully " "logged in, or your stats and a failure message otherwise" msgstr "" "You will have the statistics page with the admin stats if you're succesfully " "logged in, or your stats and a failure message otherwise" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "You're not authenticated as the admin" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "Your login" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "Your password" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "integer, how many unique visits of the shortened URL" #: themes/default/templates/api.html.ep:11 themes/default/templates/api.html.ep:12 themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "mandatory" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:114 themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:44 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:86 themes/default/templates/api.html.ep:88 msgid "optional" msgstr "optional" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:57 themes/default/templates/api.html.ep:66 themes/default/templates/api.html.ep:92 themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "shortened_url" ================================================ FILE: themes/default/lib/Lstu/I18N/es.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2021-05-01 10:42+0000\n" "Last-Translator: Berto Te \n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.6\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 no es una URL válida." #: themes/default/templates/api.html.ep:96 msgid "A page with a table containing the same informations that the JSON response" msgstr "" "Una página con una tabla que contiene la misma información que la respuesta " "JSON" #: themes/default/templates/layouts/default.html.ep:54 msgid "About" msgstr "Acerca de" #: themes/default/templates/stats.html.ep:13 themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Contraseña de administrador" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Contraseña incorrecta" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Copiar al portapapeles" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "contador" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Creado en" #: themes/default/templates/api.html.ep:44 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "xto de acceso directo personalizado" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Eliminar" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "Exporta tus URL" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Motivo del fallo" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Archivo importado" #: themes/default/templates/api.html.ep:101 msgid "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" "Obtenga los detalles (contador de visitas, fecha de creación, etc.) de una " "URL acortada." #: themes/default/templates/index.html.ep:12 themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "¡Vamos!" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Presiona Enter, luego Ctrl C para copiar el enlace corto" #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Inicio" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "Si \"action\" se establece en \"logout\":" #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "Si se define \"adminpwd\":" #: themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:88 msgid "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "Si es igual a \"json\", la respuesta estará en formato JSON, formato HTML de " "lo contrario" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), it will give you statistics for all URLs, sorted by the most visited first, paginated with pages containing %1 records." msgstr "" "Si ha iniciado sesión como administrador (configuración \"adminpwd\" o \"" "hashed_adminpwd\"), le proporcionará estadísticas para todas las URL, " "ordenadas primero por las más visitadas, paginadas con páginas que contienen%" " 1 registros." #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" "Si ha iniciado sesión como administrador, puede proporcionar un parámetro de " "\"page\"" #: themes/default/templates/stats.html.ep:32 themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "Importar URL" #: themes/default/templates/api.html.ep:79 msgid "It will use Lstu's cookies to know which shortened URL it will return statistics for." msgstr "" "Utilizará las cookies de Lstu para saber para qué URL devolverá estadísticas." #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:18 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON: fallo" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:144 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:31 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON: éxito" #: themes/default/templates/layouts/default.html.ep:53 msgid "License:" msgstr "Licencia:" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Iniciar sesión" #: themes/default/templates/layouts/default.html.ep:59 msgid "Logout" msgstr "Cerrar sesión" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Cerrar sesión en las estadísticas de administrador" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Siguiente" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "No shortened URL available. Please retry or contact the administrator at %1. Your URL to shorten: %2" msgstr "" "No hay URL abreviada disponible. Vuelva a intentarlo o póngase en contacto " "con el administrador en %1. Su URL para acortar: %2" #: themes/default/templates/api.html.ep:111 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:41 themes/default/templates/api.html.ep:83 themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Parámetros:" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Contraseña" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "Por favor, verifique sus credenciales: no se puede autenticar." #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Anterior" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "Código QR" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:32 themes/default/templates/api.html.ep:54 themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Respuesta para formato HTML" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Respuesta para formato JSON" #: themes/default/templates/api.html.ep:102 themes/default/templates/api.html.ep:117 themes/default/templates/api.html.ep:142 themes/default/templates/api.html.ep:15 themes/default/templates/api.html.ep:29 themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:59 themes/default/templates/api.html.ep:68 themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Respuesta:" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "Acceso directo a URL" #: themes/default/templates/api.html.ep:127 themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Siempre debe ser exitoso" #: themes/default/templates/layouts/default.html.ep:61 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Registrarse" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "Lo sentimos, no se puede analizar el archivo proporcionado" #: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Estadísticas" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I refuse to shorten it." msgstr "" "El host de URL o una de sus redirecciones (%1) está en la lista negra de " "Spamhaus. Me niego a acortarlo." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe Browsing database. I refuse to shorten it." msgstr "" "La URL o una de sus redirecciones (%1) está en la lista negra de la base de " "datos de Navegación segura de Google. Me niego a acortarlo." #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, phishing, etc.). I refuse to shorten it." msgstr "" "La URL redirige %1 veces o la mayoría. Es muy probable que sea una URL " "peligrosa (spam, phishing, etc.). Me niego a acortarlo." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "The URL you want to shorten comes from a domain (%1) that is blacklisted on this server (usually because of spammers that use this domain)." msgstr "" "La URL que desea acortar proviene de un dominio (%1) que está en la lista " "negra de este servidor (generalmente debido a los spammers que usan este " "dominio)." #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 lib/Lstu/Controller/URL.pm:229 msgid "The shortened URL %1 doesn't exist." msgstr "La URL acortada %1 no existe." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", \"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to shorten: %1" msgstr "" "El texto abreviado no puede ser \"a\", \"api\", \"d\", \"cookie\", \"stats\"" ", \"fullstats\", \"login\" o \"logout\" o finalizar con \".json\". Su URL " "para acortar: %1" #: themes/default/templates/api.html.ep:113 msgid "To do an admin login, set it to the password defined in the settings (\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" "Para hacer un inicio de sesión de administrador, configúrelo con la " "contraseña definida en la configuración (\"adminpwd\" o \"hashed_adminpwd\")" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "" "Para hacer un cierre de sesión de administrador, configúrelo en \"cerrar " "sesión\"." #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Demasiadas contraseñas malas. Estás prohibido." #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:43 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "URL para acortar" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." msgstr "" "Solicitó acortar demasiadas URL demasiado rápido. Estás prohibido por %1 " "hora (s)." #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "Has ingresado exitosamente." #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:31 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "Ha sido desconectado exitosamente." #: themes/default/templates/api.html.ep:137 msgid "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) to use it." msgstr "" "Debe iniciar sesión como administrador (configuración \"adminpwd\" o \"" "hashed_adminpwd\") para usarlo." #: themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "Debe iniciar sesión para usarlo." #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "Serás redirigido a la página de estadísticas de Lstu" #: themes/default/templates/api.html.ep:146 msgid "You will be redirected to Lstu statistics page with a message in case of failure" msgstr "" "Será redirigido a la página de estadísticas de Lstu con un mensaje en caso " "de falla" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "Será redirigido a la interfaz de Lstu que cerró la sesión correctamente" #: themes/default/templates/api.html.ep:19 msgid "You will be redirected to the Lstu classic interface where you will be able to shorten URLs" msgstr "" "Será redirigido a la interfaz clásica de Lstu, donde podrá acortar las URL" #: themes/default/templates/api.html.ep:54 msgid "You will be redirected to the Lstu classic interface with a message giving the shortened URL" msgstr "" "Será redirigido a la interfaz clásica de Lstu con un mensaje con la URL " "acortada" #: themes/default/templates/api.html.ep:62 msgid "You will be redirected to the targeted URL or to the Lstu interface with a message giving the failure reason" msgstr "" "Será redirigido a la URL de destino o a la interfaz Lstu con un mensaje que " "indique el motivo del erro" #: themes/default/templates/api.html.ep:123 msgid "You will have the statistics page with the admin stats if you're succesfully logged in, or your stats and a failure message otherwise" msgstr "" "Tendrá la página de estadísticas con las estadísticas de administrador si ha " "iniciado sesión correctamente, o sus estadísticas y un mensaje de error de " "lo contrario" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "No estás autenticado como administrado" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "Tu nombre de usuario" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "Tu contraseña" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "entero, cuántas visitas únicas de la URL acortada" #: themes/default/templates/api.html.ep:11 themes/default/templates/api.html.ep:12 themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "obligatorio" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:114 themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:44 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:86 themes/default/templates/api.html.ep:88 msgid "optional" msgstr "Opcional" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:57 themes/default/templates/api.html.ep:66 themes/default/templates/api.html.ep:92 themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "URL_acceso-directo" #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:240 msgid "The shortened URL %1 no longer exists." msgstr "La URL abreviada %1 ya no existe." ================================================ FILE: themes/default/lib/Lstu/I18N/fr.po ================================================ # Lstu # Copyright (C) 2013 Luc Didry # This file is distributed under the same license as the Lstu package. # # Translators: # Luc Didry , 2015 # Luc Didry , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2020-10-26 03:40+0000\n" "Last-Translator: Gerold Rupprecht \n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.1\n" "X-POOTLE-MTIME: 1441357092.000000\n" "X-Poedit-SourceCharset: UTF-8\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 n’est pas une URL valide." #: themes/default/templates/api.html.ep:96 msgid "" "A page with a table containing the same informations that the JSON response" msgstr "" "Une page avec un tableau contenant les mêmes informations que le message JSON" #: themes/default/templates/layouts/default.html.ep:38 msgid "About" msgstr "À propos" #: themes/default/templates/stats.html.ep:13 themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Mot de passe administrateur" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Mot de passe incorrect" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Copier dans le presse-papier" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "Compteur" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Créé le" #: themes/default/templates/api.html.ep:44 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Texte du raccourci personnalisé" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Supprimer" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "Exporter vos URL" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Raison de l’échec" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Fichier importé" #: themes/default/templates/api.html.ep:101 msgid "" "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" "Obtenez les détails (compteur de visites, date de création, etc.) d’une URL " "raccourcie." #: themes/default/templates/index.html.ep:12 themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "Allons-y !" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Appuyez sur Entrée puis faites Ctrl+C pour copier le lien" #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Accueil" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "Si \"action\" est définie à \"logout\" :" #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "Si \"adminpwd\" est défini :" #: themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:88 msgid "" "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "Si égal à \"json\", la réponse sera au format JSON, sinon ce sera une " "réponse en HTML" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "" "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), " "it will give you statistics for all URLs, sorted by the most visited first, " "paginated with pages containing %1 records." msgstr "" "Si vous êtes connecté·e en tant qu’admin (paramètre \"adminpwd\" ou " "\"hashed_adminpwd\"), cela vous donnera les statistiques pour toutes les " "URL, triées selon le nombre de visites en ordre décroissant, de façon " "paginée avec des pages contenant %1 enregistrements." #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" "Si vous êtes connecté·e en tant qu’admin, vous pouvez fournir un paramètre " "\"page\"" #: themes/default/templates/stats.html.ep:32 themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "Importer des URL" #: themes/default/templates/api.html.ep:79 msgid "" "It will use Lstu's cookies to know which shortened URL it will return " "statistics for." msgstr "" "Cela utilisera les cookies de Lstu pour savoir quelles sont les URL dont il " "faut fournir les statistiques." #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:18 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON : échec" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:144 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:31 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON : succès" #: themes/default/templates/layouts/default.html.ep:37 msgid "License:" msgstr "Licence :" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Connexion" #: themes/default/templates/layouts/default.html.ep:43 msgid "Logout" msgstr "Déconnexion" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Déconnexion des stats admin" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Suivant" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "" "No shortened URL available. Please retry or contact the administrator at %1. " "Your URL to shorten: %2" msgstr "" "Il n’y a plus d’URL raccourcie disponible. Veuillez réessayer ou contactez " "l’administrateur sur %1. Rappel de l’URL à raccourcir : %2" #: themes/default/templates/api.html.ep:111 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:41 themes/default/templates/api.html.ep:83 themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Paramètres :" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Mot de passe" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "Impossible de s’authentifier, veuillez vérifier vos identifiants." #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Précédent" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "QRCode" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:32 themes/default/templates/api.html.ep:54 themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Réponse pour le format HTML" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Réponse pour le format JSON" #: themes/default/templates/api.html.ep:102 themes/default/templates/api.html.ep:117 themes/default/templates/api.html.ep:142 themes/default/templates/api.html.ep:15 themes/default/templates/api.html.ep:29 themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:59 themes/default/templates/api.html.ep:68 themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Réponse :" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "URL raccourcie" #: themes/default/templates/api.html.ep:127 themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Devrait toujours retourner un succès" #: themes/default/templates/layouts/default.html.ep:45 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Connexion" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "Désolé, impossible d’analyser le fichier fourni" #: themes/default/templates/layouts/default.html.ep:42 themes/default/templates/layouts/default.html.ep:48 themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Statistiques" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "" "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I " "refuse to shorten it." msgstr "" "L'hôte de l'URL ou d'une de ses redirections (%1) est considéré comme nocif " "par Spamhaus. Je refuse de la raccourcir." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "" "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe " "Browsing database. I refuse to shorten it." msgstr "" "L’URL ou l’une de ses redirections (%1) est blacklistée dans la base de " "données Google Safe Browsing. Je refuse de la raccourcir." #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "" "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, " "phishing, etc.). I refuse to shorten it." msgstr "" "L'URL redirige %1 fois ou plus vers un autre site. Il est probable qu'il " "s'agisse d'une URL dangereuse (pourriel, hameçonnage, etc.). Je refuse de la " "raccourcir." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "" "The URL you want to shorten comes from a domain (%1) that is blacklisted on " "this server (usually because of spammers that use this domain)." msgstr "" "L’URL que vous souhaitez raccourcir provient d’un domaine (%1) qui est " "interdit sur ce serveur (généralement à cause de spammeurs qui utilisent ce " "domaine)." #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 lib/Lstu/Controller/URL.pm:226 msgid "The shortened URL %1 doesn't exist." msgstr "L’URL raccourcie %1 n’existe pas." #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:240 msgid "The shortened URL %1 no longer exists." msgstr "L’URL raccourcie %1 n’existe plus." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "" "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", " "\"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to " "shorten: %1" msgstr "" "Le texte du raccourci ne peut être \"a\", \"api\", \"d\", \"cookie\", " "\"stats\", \"fullstats\", \"login\" ou \"logout\" ou se terminer par \"." "json\". Rappel de l’URL à raccourcir : %1" #: themes/default/templates/api.html.ep:113 msgid "" "To do an admin login, set it to the password defined in the settings " "(\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" "Pour se connecter en tant qu’admin, mettez le mot de passe défini dans les " "paramètres de configuration (\"adminpwd\" ou \"hashed_adminpwd\")" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "" "Pour se déconnecter de l’administration, définissez ce paramètre à " "\"logout\"." #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Trop de mauvais mots de passe. Vous êtes banni." #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:43 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "URL à raccourcir" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "" "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." "" msgstr "" "Vous avez demandé à réduire trop d’URLs en trop peu de temps. Vous êtes " "banni pour %1 heure(s)." #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "Vous avez été connecté·e avec succès." #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:31 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "Vous avez été déconnecté·e avec succès." #: themes/default/templates/api.html.ep:137 msgid "" "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) " "to use it." msgstr "" "Vous devez être connecté·e en tant qu’admin (paramètre \"adminpwd\" ou " "\"hashed_adminpwd\") pour l'utiliser." #: themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "Vous devez être connecté·e pour l’utiliser." #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "Vous serez redirigé·e vers la page de statistiques de Lstu" #: themes/default/templates/api.html.ep:146 msgid "" "You will be redirected to Lstu statistics page with a message in case of " "failure" msgstr "" "Vous serez redirigé·e vers la page de statistiques de Lstu avec un message " "en cas d’échec" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "Vous serez redirigé vers l’interface de déconnexion réussie de Lstu" #: themes/default/templates/api.html.ep:19 msgid "" "You will be redirected to the Lstu classic interface where you will be able " "to shorten URLs" msgstr "" "Vous serez redirigé vers l’interface normale de Lstu où vous pourrez " "raccourcirs des URL" #: themes/default/templates/api.html.ep:54 msgid "" "You will be redirected to the Lstu classic interface with a message giving " "the shortened URL" msgstr "" "Vous aurez l’interface normale de Lstu avec un message indiquant l’URL " "raccourcie" #: themes/default/templates/api.html.ep:62 msgid "" "You will be redirected to the targeted URL or to the Lstu interface with a " "message giving the failure reason" msgstr "" "Vous serez redirigé vers l’URL cible ou vers la page d’accueil de Lstu avec " "un message indiquant la raison de l’échec" #: themes/default/templates/api.html.ep:123 msgid "" "You will have the statistics page with the admin stats if you're succesfully " "logged in, or your stats and a failure message otherwise" msgstr "" "Vous aurez la page de statistiques avec les statistiques admin si vous vous " "êtes connecté·e avec succès, ou sinon vos statistiques avec un message " "d’échec" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "Vous n’êtes pas authentifié·e en tant qu’admin" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "Votre identifiant" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "Votre mot de passe" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "entier, combien de visites uniques a reçu l'URL raccourcie" #: themes/default/templates/api.html.ep:11 themes/default/templates/api.html.ep:12 themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "obligatoire" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:114 themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:44 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:86 themes/default/templates/api.html.ep:88 msgid "optional" msgstr "facultatif" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:57 themes/default/templates/api.html.ep:66 themes/default/templates/api.html.ep:92 themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "url_raccourcie" ================================================ FILE: themes/default/lib/Lstu/I18N/fr_FR.po ================================================ # Lstu # Copyright (C) 2013 Luc Didry # This file is distributed under the same license as the Lstu package. # # Translators: # Luc Didry , 2015 # Luc Didry , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2019-11-08 11:07+0000\n" "Last-Translator: Framasoft \n" "Language-Team: French (France) \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 3.9.1\n" "X-POOTLE-MTIME: 1441357092.000000\n" "X-Poedit-SourceCharset: UTF-8\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 n’est pas une URL valide." #: themes/default/templates/api.html.ep:96 msgid "" "A page with a table containing the same informations that the JSON response" msgstr "" "Une page avec un tableau contenant les mêmes informations que le message JSON" #: themes/default/templates/layouts/default.html.ep:38 msgid "About" msgstr "À propos" #: themes/default/templates/stats.html.ep:13 themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Mot de passe administrateur" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Mot de passe incorrect" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Copier dans le presse-papier" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "Compteur" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Créé le" #: themes/default/templates/api.html.ep:44 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Texte du raccourci personnalisé" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Supprimer" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "Exporter vos URL" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Raison de l’échec" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Fichier importé" #: themes/default/templates/api.html.ep:101 msgid "" "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" "Obtenez les détails (compteur de visites, date de création, etc.) d’une URL " "raccourcie." #: themes/default/templates/index.html.ep:12 themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "Allons-y !" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Appuyez sur Entrée puis faites Ctrl+C pour copier le lien" #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Accueil" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "Si \"action\" est définie à \"logout\" :" #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "Si \"adminpwd\" est défini :" #: themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:88 msgid "" "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "Si égal à \"json\", la réponse sera au format JSON, sinon ce sera une " "réponse en HTML" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "" "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), " "it will give you statistics for all URLs, sorted by the most visited first, " "paginated with pages containing %1 records." msgstr "" "Si vous êtes connecté·e en tant qu’admin (paramètre \"adminpwd\" ou " "\"hashed_adminpwd\"), cela vous donnera les statistiques pour toutes les " "URL, triées selon le nombre de visites en ordre décroissant, de façon " "paginée avec des pages contenant %1 enregistrements." #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" "Si vous êtes connecté·e en tant qu’admin, vous pouvez fournir un paramètre " "\"page\"" #: themes/default/templates/stats.html.ep:32 themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "Importer des URL" #: themes/default/templates/api.html.ep:79 msgid "" "It will use Lstu's cookies to know which shortened URL it will return " "statistics for." msgstr "" "Cela utilisera les cookies de Lstu pour savoir quelles sont les URL dont il " "faut fournir les statistiques." #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:18 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON : échec" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:144 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:31 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON : succès" #: themes/default/templates/layouts/default.html.ep:37 msgid "License:" msgstr "Licence :" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Connexion" #: themes/default/templates/layouts/default.html.ep:43 msgid "Logout" msgstr "Déconnexion" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Déconnexion des stats admin" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Suivant" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "" "No shortened URL available. Please retry or contact the administrator at %1. " "Your URL to shorten: %2" msgstr "" "Il n’y a plus d’URL raccourcie disponible. Veuillez réessayer ou contactez " "l’administrateur sur %1. Rappel de l’URL à raccourcir : %2" #: themes/default/templates/api.html.ep:111 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:41 themes/default/templates/api.html.ep:83 themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Paramètres :" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Mot de passe" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "Impossible de s’authentifier, veuillez vérifier vos identifiants." #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Précédent" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "QRCode" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:32 themes/default/templates/api.html.ep:54 themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Réponse pour le format HTML" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Réponse pour le format JSON" #: themes/default/templates/api.html.ep:102 themes/default/templates/api.html.ep:117 themes/default/templates/api.html.ep:142 themes/default/templates/api.html.ep:15 themes/default/templates/api.html.ep:29 themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:59 themes/default/templates/api.html.ep:68 themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Réponse :" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "URL raccourcie" #: themes/default/templates/api.html.ep:127 themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Devrait toujours retourner un succès" #: themes/default/templates/layouts/default.html.ep:45 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Connexion" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "Désolé, impossible d’analyser le fichier fourni" #: themes/default/templates/layouts/default.html.ep:42 themes/default/templates/layouts/default.html.ep:48 themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Statistiques" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "" "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I " "refuse to shorten it." msgstr "" "L'hôte de l'URL ou d'une de ses redirections (%1) est considéré comme nocif " "par Spamhaus. Je refuse de la raccourcir." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "" "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe " "Browsing database. I refuse to shorten it." msgstr "" "L’URL ou l’une de ses redirections (%1) est blacklistée dans la base de " "données Google Safe Browsing. Je refuse de la raccourcir." #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "" "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, " "phishing, etc.). I refuse to shorten it." msgstr "" "L'URL redirige %1 fois ou plus vers un autre site. Il est probable qu'il " "s'agisse d'une URL dangereuse (pourriel, hameçonnage, etc.). Je refuse de la " "raccourcir." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "" "The URL you want to shorten comes from a domain (%1) that is blacklisted on " "this server (usually because of spammers that use this domain)." msgstr "" "L’URL que vous souhaitez raccourcir provient d’un domaine (%1) qui est " "interdit sur ce serveur (généralement à cause de spammeurs qui utilisent ce " "domaine)." #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 lib/Lstu/Controller/URL.pm:226 msgid "The shortened URL %1 doesn't exist." msgstr "L’URL raccourcie %1 n’existe pas." #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:240 msgid "The shortened URL %1 no longer exists." msgstr "L’URL raccourcie %1 n’existe plus." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "" "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", " "\"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to " "shorten: %1" msgstr "" "Le texte du raccourci ne peut être \"a\", \"api\", \"d\", \"cookie\", " "\"stats\", \"fullstats\", \"login\" ou \"logout\" ou se terminer par \"." "json\". Rappel de l’URL à raccourcir : %1" #: themes/default/templates/api.html.ep:113 msgid "" "To do an admin login, set it to the password defined in the settings " "(\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" "Pour se connecter en tant qu’admin, mettez le mot de passe défini dans les " "paramètres de configuration (\"adminpwd\" ou \"hashed_adminpwd\")" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "" "Pour se déconnecter de l’administration, définissez ce paramètre à " "\"logout\"." #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Trop de mauvais mots de passe. Vous êtes banni." #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:43 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "URL à raccourcir" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "" "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." "" msgstr "" "Vous avez demandé à réduire trop d’URLs en trop peu de temps. Vous êtes " "banni pour %1 heure(s)." #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "Vous avez été connecté·e avec succès." #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:31 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "Vous avez été déconnecté·e avec succès." #: themes/default/templates/api.html.ep:137 msgid "" "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) " "to use it." msgstr "" "Vous devez être connecté·e en tant qu’admin (paramètre \"adminpwd\" ou " "\"hashed_adminpwd\") pour l'utiliser." #: themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "Vous devez être connecté·e pour l’utiliser." #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "Vous serez redirigé·e vers la page de statistiques de Lstu" #: themes/default/templates/api.html.ep:146 msgid "" "You will be redirected to Lstu statistics page with a message in case of " "failure" msgstr "" "Vous serez redirigé·e vers la page de statistiques de Lstu avec un message " "en cas d’échec" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "Vous serez redirigé vers l’interface de déconnexion réussie de Lstu" #: themes/default/templates/api.html.ep:19 msgid "" "You will be redirected to the Lstu classic interface where you will be able " "to shorten URLs" msgstr "" "Vous serez redirigé vers l’interface normale de Lstu où vous pourrez " "raccourcirs des URL" #: themes/default/templates/api.html.ep:54 msgid "" "You will be redirected to the Lstu classic interface with a message giving " "the shortened URL" msgstr "" "Vous aurez l’interface normale de Lstu avec un message indiquant l’URL " "raccourcie" #: themes/default/templates/api.html.ep:62 msgid "" "You will be redirected to the targeted URL or to the Lstu interface with a " "message giving the failure reason" msgstr "" "Vous serez redirigé vers l’URL cible ou vers la page d’accueil de Lstu avec " "un message indiquant la raison de l’échec" #: themes/default/templates/api.html.ep:123 msgid "" "You will have the statistics page with the admin stats if you're succesfully " "logged in, or your stats and a failure message otherwise" msgstr "" "Vous aurez la page de statistiques avec les statistiques admin si vous vous " "êtes connecté·e avec succès, ou sinon vos statistiques avec un message " "d’échec" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "Vous n’êtes pas authentifié·e en tant qu’admin" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "Votre identifiant" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "Votre mot de passe" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "entier, combien de visites uniques a reçu l'URL raccourcie" #: themes/default/templates/api.html.ep:11 themes/default/templates/api.html.ep:12 themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "obligatoire" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:114 themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:44 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:86 themes/default/templates/api.html.ep:88 msgid "optional" msgstr "facultatif" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:57 themes/default/templates/api.html.ep:66 themes/default/templates/api.html.ep:92 themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "url_raccourcie" ================================================ FILE: themes/default/lib/Lstu/I18N/hr.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2022-07-26 23:36+0000\n" "Last-Translator: Milo Ivir \n" "Language-Team: Croatian \n" "Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.13.1\n" #. ($url) #: lib/Lstu/Controller/URL.pm:136 msgid "%1 is not a valid URL." msgstr "%1 nije valjani URL." #: themes/default/templates/api.html.ep:104 msgid "A page with a table containing the same informations that the JSON response" msgstr "Stranica s tablicom koja sadrži iste informacije kao JSON odgovor" #: themes/default/templates/layouts/default.html.ep:56 msgid "About" msgstr "Podaci" #: themes/default/templates/stats.html.ep:18 themes/default/templates/stats.html.ep:19 msgid "Admin password" msgstr "Administratorska lozinka" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:130 msgid "Bad password" msgstr "Kriva lozinka" #: themes/default/templates/layouts/default.html.ep:68 msgid "Browser extensions" msgstr "Proširenja preglednika" #: themes/default/templates/index.html.ep:22 msgid "Copy to clipboard" msgstr "Kopiraj u međuspremnik" #: themes/default/templates/stats.html.ep:62 themes/default/templates/stats.html.ep:78 msgid "Counter" msgstr "Brojač" #: themes/default/templates/stats.html.ep:67 themes/default/templates/stats.html.ep:81 msgid "Created" msgstr "Izrađeno" #: themes/default/templates/api.html.ep:52 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Prilagođen skraćeni tekst" #: themes/default/templates/stats.html.ep:88 msgid "Delete" msgstr "Brisanje" #: themes/default/templates/stats.html.ep:41 msgid "Export your URLs" msgstr "Izvezi svoje URL-ove" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:153 themes/default/templates/api.html.ep:61 themes/default/templates/api.html.ep:79 msgid "Failure reason" msgstr "Razlog neuspjeha" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Datoteka je uvezena" #: themes/default/templates/api.html.ep:109 msgid "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "Dobij detalje (brojač posjeta, datum stvaranja itd.) skraćenog URL-a." #: themes/default/templates/index.html.ep:13 themes/default/templates/stats.html.ep:22 msgid "Go!" msgstr "Kreni!" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Pritisni Enter, zatim Ctrl+C za kopiranje kratke poveznice" #: themes/default/templates/stats.html.ep:125 msgid "Home" msgstr "Doma" #: themes/default/templates/api.html.ep:134 msgid "If \"action\" is defined to \"logout\":" msgstr "Ako je „action” (radnja) definirana kao „logout” (odjava):" #: themes/default/templates/api.html.ep:127 msgid "If \"adminpwd\" is defined:" msgstr "Ako je „adminpwd” (administratorska lozinka) definirana:" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:148 themes/default/templates/api.html.ep:21 themes/default/templates/api.html.ep:35 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:96 msgid "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "Ako je jednako „json”, odgovor će biti u JSON formatu, inače u HTML formatu" #. (config('page_offset') #: themes/default/templates/api.html.ep:89 msgid "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), it will give you statistics for all URLs, sorted by the most visited first, paginated with pages containing %1 records." msgstr "" "Ako si prijavljen/a kao administrator (postavka „adminpwd” ili " "„hashed_adminpwd”), dobit ćeš statistiku za sve URL-ove, razvrstane po " "najposjećenijima, paginirane sa stranicama koje sadrže %1 zapisa." #: themes/default/templates/api.html.ep:94 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" "Ako si prijavljen/a kao administrator, možeš zadati parametar „page” " "(stranica)" #: themes/default/templates/stats.html.ep:37 themes/default/templates/stats.html.ep:40 msgid "Import URLs" msgstr "Uvezi URL-ove" #: themes/default/templates/api.html.ep:87 msgid "It will use Lstu's cookies to know which shortened URL it will return statistics for." msgstr "" "Koristit će Lstu-ove kolačiće za određivanje statisktike skraćenih URL-ova." #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:153 themes/default/templates/api.html.ep:26 themes/default/templates/api.html.ep:61 themes/default/templates/api.html.ep:79 msgid "JSON: failure" msgstr "JSON: neuspjeh" #: themes/default/templates/api.html.ep:112 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:137 themes/default/templates/api.html.ep:152 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:60 themes/default/templates/api.html.ep:78 msgid "JSON: success" msgstr "JSON: uspjeh" #: themes/default/templates/layouts/default.html.ep:55 msgid "License:" msgstr "Licenca:" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Prijava" #: themes/default/templates/layouts/default.html.ep:61 msgid "Logout" msgstr "Odjava" #: themes/default/templates/stats.html.ep:15 msgid "Logout from admin stats" msgstr "Odjavi se iz statistike administratora" #: themes/default/templates/stats.html.ep:120 msgid "Next" msgstr "Pored" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:130 msgid "No shortened URL available. Please retry or contact the administrator at %1. Your URL to shorten: %2" msgstr "" "Skraćeni URL nije dostupan. Pokušaj ponovo ili kontaktiraj administratora na " "%1. Tvoj URL za skraćivanje: %2" #: themes/default/templates/api.html.ep:119 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:33 themes/default/templates/api.html.ep:49 themes/default/templates/api.html.ep:91 msgid "Parameters:" msgstr "Parametri:" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Lozinka" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:26 msgid "Please, check your credentials: unable to authenticate." msgstr "Provjeri svoje podatke autentifikacije: autentifikacija je neuspjela." #: themes/default/templates/stats.html.ep:118 msgid "Previous" msgstr "Prijašnja" #: themes/default/templates/stats.html.ep:86 msgid "QRCode" msgstr "QR kod" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:131 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:154 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:40 themes/default/templates/api.html.ep:62 msgid "Response for HTML format" msgstr "Odgovor za HTML format" #: themes/default/templates/api.html.ep:103 msgid "Response for JSON format" msgstr "Odgovor za JSON format" #: themes/default/templates/api.html.ep:110 themes/default/templates/api.html.ep:125 themes/default/templates/api.html.ep:150 themes/default/templates/api.html.ep:23 themes/default/templates/api.html.ep:37 themes/default/templates/api.html.ep:55 themes/default/templates/api.html.ep:67 themes/default/templates/api.html.ep:76 themes/default/templates/api.html.ep:98 msgid "Response:" msgstr "Odgovor:" #: themes/default/templates/stats.html.ep:57 themes/default/templates/stats.html.ep:75 msgid "Shortened URL" msgstr "Skraćeni URL" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:32 msgid "Should always be successful" msgstr "Trebalo bi uvijek uspjeti" #: themes/default/templates/layouts/default.html.ep:63 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Registracija" #: lib/Lstu/Controller/URL.pm:53 themes/default/templates/api.html.ep:8 msgid "Sorry, the API is disabled." msgstr "" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "Žao nam je, obrada zadane datoteke nije uspjela" #: themes/default/templates/layouts/default.html.ep:60 themes/default/templates/layouts/default.html.ep:66 themes/default/templates/stats.html.ep:9 msgid "Statistics" msgstr "Statistika" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I refuse to shorten it." msgstr "" "URL host ili jedno od njegovih preusmjeravanja (%1) je na popisu nepoželjnih " "u Spamhausu. Odbijam ga skratiti." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe Browsing database. I refuse to shorten it." msgstr "" "URL ili jedno od njegovih preusmjeravanja (%1) nalazi se na popisu " "nepoželjnih u bazi podataka Googleovog sigurnog pregledavanja. Odbijam ga " "skratiti." #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, phishing, etc.). I refuse to shorten it." msgstr "" "URL preusmjerava barem %1 puta. Najvjerojatnije se radi o opasnom URL-u (" "neželjena pošta, krađa podataka itd.). Odbijam ga skratiti." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "The URL you want to shorten comes from a domain (%1) that is blacklisted on this server (usually because of spammers that use this domain)." msgstr "" "URL koji želiš skratiti dolazi s domene (%1) koja je na popisu nepoželjnih " "na ovom poslužitelju (obično zbog pošiljatelja neželjene pošte koji koriste " "ovu domenu)." #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:200 lib/Lstu/Controller/URL.pm:260 msgid "The shortened URL %1 doesn't exist." msgstr "Skraćeni URL %1 ne postoji." #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:258 msgid "The shortened URL %1 no longer exists." msgstr "Skraćeni URL %1 više ne postoji." #. ($url) #: lib/Lstu/Controller/URL.pm:77 msgid "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", \"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to shorten: %1" msgstr "" "Skraćeni tekst ne može biti „a”, „api”, „d”, „cookie”, „stats”, „fullstats”, " "„login” ili „logout” niti završiti s „.json”. Tvoj URL za skraćivanje: %1" #: themes/default/templates/api.html.ep:9 msgid "This page is informational only." msgstr "" #: themes/default/templates/api.html.ep:121 msgid "To do an admin login, set it to the password defined in the settings (\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" "Za administratorsku prijavu, postavi je na lozinku definiranu u postavkama " "(„adminpwd” ili „hashed_adminpwd”)" #: themes/default/templates/api.html.ep:122 msgid "To do an admin logout, set it to \"logout\"." msgstr "Za administratorsku odjavu postavi je na „logout” (odjava)." #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Previše neispravnih lozinki. Isključen/a si." #: themes/default/templates/api.html.ep:103 themes/default/templates/api.html.ep:112 themes/default/templates/stats.html.ep:52 themes/default/templates/stats.html.ep:72 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:51 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "URL za skraćivanje" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:41 msgid "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." msgstr "" "Prebrzo si zatražio/la skraćivanje previše URL-ova. Isključen/a si za %1 h." #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:25 msgid "You have been successfully logged in." msgstr "Uspješno si prijavljan/a." #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:137 themes/default/templates/api.html.ep:39 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "Uspješno si odjevljen/a." #: themes/default/templates/api.html.ep:145 msgid "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) to use it." msgstr "" "Za korištnje moraš biti prijavljen kao administrator (postavka „adminpwd” " "ili „hashed_adminpwd”)." #: themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:85 msgid "You must be logged in to use it." msgstr "Za korištenje se moraš prijaviti." #: themes/default/templates/api.html.ep:138 msgid "You will be redirected to Lstu statistics page" msgstr "Preusmjerit ćemo te na Lstu stranicu statistike" #: themes/default/templates/api.html.ep:154 msgid "You will be redirected to Lstu statistics page with a message in case of failure" msgstr "" "Preusmjerit ćemo te na Lstu stranicu statistike s porukom u slučaju neuspjeha" #: themes/default/templates/api.html.ep:40 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "Preusmjerit ćemo te na uspješno odjavljeno Lstu sučelje" #: themes/default/templates/api.html.ep:27 msgid "You will be redirected to the Lstu classic interface where you will be able to shorten URLs" msgstr "" "Preusmjerit ćemo te na klasično Lstu sučelje gdje ćeš moći skratiti URL-ove" #: themes/default/templates/api.html.ep:62 msgid "You will be redirected to the Lstu classic interface with a message giving the shortened URL" msgstr "" "Preusmjerit ćemo te na klasično Lstu sučelje s porukom o skraćenim URL-om" #: themes/default/templates/api.html.ep:70 msgid "You will be redirected to the targeted URL or to the Lstu interface with a message giving the failure reason" msgstr "" "Preusmjerit ćemo te na ciljani URL ili na klasično Lstu sučelje s porukom " "razloga neuspjeha" #: themes/default/templates/api.html.ep:131 msgid "You will have the statistics page with the admin stats if you're succesfully logged in, or your stats and a failure message otherwise" msgstr "" "Dobit ćeš stranicu statistike sa statistikom administratora ako si uspješno " "prijavljen/a. U suprotnom ćeš dobiti svoju statistiku i poruku o neuspjehu" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "Nisi autentificiran/a kao administrator" #: themes/default/templates/api.html.ep:19 msgid "Your login" msgstr "Tvoja prijava" #: themes/default/templates/api.html.ep:20 msgid "Your password" msgstr "Tvoja lozinka" #: themes/default/templates/api.html.ep:103 themes/default/templates/api.html.ep:112 msgid "integer, how many unique visits of the shortened URL" msgstr "cijeli broj, broj jedinstvenih posjeta skraćenog URL-a" #: themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:20 themes/default/templates/api.html.ep:51 msgid "mandatory" msgstr "obavezno" #: themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:148 themes/default/templates/api.html.ep:21 themes/default/templates/api.html.ep:35 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:94 themes/default/templates/api.html.ep:96 msgid "optional" msgstr "opcionalno" #: themes/default/templates/api.html.ep:100 themes/default/templates/api.html.ep:107 themes/default/templates/api.html.ep:143 themes/default/templates/api.html.ep:65 themes/default/templates/api.html.ep:74 msgid "shortened_url" msgstr "skraćeni_url" ================================================ FILE: themes/default/lib/Lstu/I18N/lstu.pot ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #. ($url) #: lib/Lstu/Controller/URL.pm:136 msgid "%1 is not a valid URL." msgstr "" #: themes/default/templates/api.html.ep:104 msgid "A page with a table containing the same informations that the JSON response" msgstr "" #: themes/default/templates/layouts/default.html.ep:56 msgid "About" msgstr "" #: themes/default/templates/stats.html.ep:18 themes/default/templates/stats.html.ep:19 msgid "Admin password" msgstr "" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:130 msgid "Bad password" msgstr "" #: themes/default/templates/index.html.ep:22 msgid "Copy to clipboard" msgstr "" #: themes/default/templates/stats.html.ep:62 themes/default/templates/stats.html.ep:78 msgid "Counter" msgstr "" #: themes/default/templates/stats.html.ep:67 themes/default/templates/stats.html.ep:81 msgid "Created" msgstr "" #: themes/default/templates/api.html.ep:52 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "" #: themes/default/templates/stats.html.ep:88 msgid "Delete" msgstr "" #: themes/default/templates/stats.html.ep:41 msgid "Export your URLs" msgstr "" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:153 themes/default/templates/api.html.ep:61 themes/default/templates/api.html.ep:79 msgid "Failure reason" msgstr "" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "" #: themes/default/templates/api.html.ep:109 msgid "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" #: themes/default/templates/index.html.ep:13 themes/default/templates/stats.html.ep:22 msgid "Go!" msgstr "" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "" #: themes/default/templates/stats.html.ep:125 msgid "Home" msgstr "" #: themes/default/templates/api.html.ep:134 msgid "If \"action\" is defined to \"logout\":" msgstr "" #: themes/default/templates/api.html.ep:127 msgid "If \"adminpwd\" is defined:" msgstr "" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:148 themes/default/templates/api.html.ep:21 themes/default/templates/api.html.ep:35 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:96 msgid "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" #. (config('page_offset') #: themes/default/templates/api.html.ep:89 msgid "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), it will give you statistics for all URLs, sorted by the most visited first, paginated with pages containing %1 records." msgstr "" #: themes/default/templates/api.html.ep:94 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" #: themes/default/templates/stats.html.ep:37 themes/default/templates/stats.html.ep:40 msgid "Import URLs" msgstr "" #: themes/default/templates/api.html.ep:87 msgid "It will use Lstu's cookies to know which shortened URL it will return statistics for." msgstr "" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:153 themes/default/templates/api.html.ep:26 themes/default/templates/api.html.ep:61 themes/default/templates/api.html.ep:79 msgid "JSON: failure" msgstr "" #: themes/default/templates/api.html.ep:112 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:137 themes/default/templates/api.html.ep:152 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:60 themes/default/templates/api.html.ep:78 msgid "JSON: success" msgstr "" #: themes/default/templates/layouts/default.html.ep:55 msgid "License:" msgstr "" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "" #: themes/default/templates/layouts/default.html.ep:61 msgid "Logout" msgstr "" #: themes/default/templates/stats.html.ep:15 msgid "Logout from admin stats" msgstr "" #: themes/default/templates/stats.html.ep:120 msgid "Next" msgstr "" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:130 msgid "No shortened URL available. Please retry or contact the administrator at %1. Your URL to shorten: %2" msgstr "" #: themes/default/templates/api.html.ep:119 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:33 themes/default/templates/api.html.ep:49 themes/default/templates/api.html.ep:91 msgid "Parameters:" msgstr "" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:26 msgid "Please, check your credentials: unable to authenticate." msgstr "" #: themes/default/templates/stats.html.ep:118 msgid "Previous" msgstr "" #: themes/default/templates/stats.html.ep:86 msgid "QRCode" msgstr "" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:131 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:154 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:40 themes/default/templates/api.html.ep:62 msgid "Response for HTML format" msgstr "" #: themes/default/templates/api.html.ep:103 msgid "Response for JSON format" msgstr "" #: themes/default/templates/api.html.ep:110 themes/default/templates/api.html.ep:125 themes/default/templates/api.html.ep:150 themes/default/templates/api.html.ep:23 themes/default/templates/api.html.ep:37 themes/default/templates/api.html.ep:55 themes/default/templates/api.html.ep:67 themes/default/templates/api.html.ep:76 themes/default/templates/api.html.ep:98 msgid "Response:" msgstr "" #: themes/default/templates/stats.html.ep:57 themes/default/templates/stats.html.ep:75 msgid "Shortened URL" msgstr "" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:32 msgid "Should always be successful" msgstr "" #: themes/default/templates/layouts/default.html.ep:63 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "" #: lib/Lstu/Controller/URL.pm:53 themes/default/templates/api.html.ep:8 msgid "Sorry, the API is disabled." msgstr "" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "" #: themes/default/templates/layouts/default.html.ep:60 themes/default/templates/layouts/default.html.ep:66 themes/default/templates/stats.html.ep:9 msgid "Statistics" msgstr "" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I refuse to shorten it." msgstr "" #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe Browsing database. I refuse to shorten it." msgstr "" #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, phishing, etc.). I refuse to shorten it." msgstr "" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "The URL you want to shorten comes from a domain (%1) that is blacklisted on this server (usually because of spammers that use this domain)." msgstr "" #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:200 lib/Lstu/Controller/URL.pm:260 msgid "The shortened URL %1 doesn't exist." msgstr "" #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:258 msgid "The shortened URL %1 no longer exists." msgstr "" #. ($url) #: lib/Lstu/Controller/URL.pm:77 msgid "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", \"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to shorten: %1" msgstr "" #: themes/default/templates/api.html.ep:9 msgid "This page is informational only." msgstr "" #: themes/default/templates/api.html.ep:121 msgid "To do an admin login, set it to the password defined in the settings (\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" #: themes/default/templates/api.html.ep:122 msgid "To do an admin logout, set it to \"logout\"." msgstr "" #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "" #: themes/default/templates/api.html.ep:103 themes/default/templates/api.html.ep:112 themes/default/templates/stats.html.ep:52 themes/default/templates/stats.html.ep:72 msgid "URL" msgstr "" #: themes/default/templates/api.html.ep:51 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:41 msgid "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." msgstr "" #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:25 msgid "You have been successfully logged in." msgstr "" #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:137 themes/default/templates/api.html.ep:39 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "" #: themes/default/templates/api.html.ep:145 msgid "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) to use it." msgstr "" #: themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:85 msgid "You must be logged in to use it." msgstr "" #: themes/default/templates/api.html.ep:138 msgid "You will be redirected to Lstu statistics page" msgstr "" #: themes/default/templates/api.html.ep:154 msgid "You will be redirected to Lstu statistics page with a message in case of failure" msgstr "" #: themes/default/templates/api.html.ep:40 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "" #: themes/default/templates/api.html.ep:27 msgid "You will be redirected to the Lstu classic interface where you will be able to shorten URLs" msgstr "" #: themes/default/templates/api.html.ep:62 msgid "You will be redirected to the Lstu classic interface with a message giving the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:70 msgid "You will be redirected to the targeted URL or to the Lstu interface with a message giving the failure reason" msgstr "" #: themes/default/templates/api.html.ep:131 msgid "You will have the statistics page with the admin stats if you're succesfully logged in, or your stats and a failure message otherwise" msgstr "" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "" #: themes/default/templates/api.html.ep:19 msgid "Your login" msgstr "" #: themes/default/templates/api.html.ep:20 msgid "Your password" msgstr "" #: themes/default/templates/api.html.ep:103 themes/default/templates/api.html.ep:112 msgid "integer, how many unique visits of the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:20 themes/default/templates/api.html.ep:51 msgid "mandatory" msgstr "" #: themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:148 themes/default/templates/api.html.ep:21 themes/default/templates/api.html.ep:35 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:94 themes/default/templates/api.html.ep:96 msgid "optional" msgstr "" #: themes/default/templates/api.html.ep:100 themes/default/templates/api.html.ep:107 themes/default/templates/api.html.ep:143 themes/default/templates/api.html.ep:65 themes/default/templates/api.html.ep:74 msgid "shortened_url" msgstr "" ================================================ FILE: themes/default/lib/Lstu/I18N/oc.po ================================================ # Lstu # Copyright (C) 2013 Luc Didry # This file is distributed under the same license as the Lstu package. # # Translators: # Lo Quentin # Luc Didry , 2015 # Cédric Valmary (totenoc.eu) , 2016. # Luc Didry , 2018. #zanata # Quentí, 2018. #zanata msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2021-04-28 09:40+0000\n" "Last-Translator: Quentin PAGÈS \n" "Language-Team: Occitan \n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.6\n" "X-POOTLE-MTIME: 1441357092.000000\n" "X-Poedit-SourceCharset: UTF-8\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 es pas una URL valida." #: themes/default/templates/api.html.ep:96 msgid "" "A page with a table containing the same informations that the JSON response" msgstr "" "Una pagina amb un tablèu que conten las meteissas informacions que lo " "messatge JSON" #: themes/default/templates/layouts/default.html.ep:38 msgid "About" msgstr "A prepaus" #: themes/default/templates/stats.html.ep:13 themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Senhal administrator" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Senhal incorrècte" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Copiar al quichapapièrs" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "Comptador" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Creat lo" #: themes/default/templates/api.html.ep:44 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Tèxte de l’acorchi personalizat" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Suprimir" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "Exportar vòstras URL" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Rason del fracàs" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Fichièr importat" #: themes/default/templates/api.html.ep:101 msgid "" "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" "Obténer los detalhs (comptador de visitas, data de creacion, etc.) d’una URL " "acorchida." #: themes/default/templates/index.html.ep:12 themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "Zo !" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Picatz sus Entrada puèi fasètz Ctrl+C per copiar lo ligam" #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Acuèlh" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "Se \"action\" es definit coma \"logout\" :" #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "Se \"adminpwd\" es definit :" #: themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:88 msgid "" "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "Se es egal a \"json\", la responsa serà al format JSON, autrament aquò serà " "una responsa en HTML" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "" "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), " "it will give you statistics for all URLs, sorted by the most visited first, " "paginated with pages containing %1 records." msgstr "" "Se sètz connectat·ada coma admin (\"adminpwd\" o \"hashed_adminpwd\" " "paramètre) aquò donarà totas las estatisticas per totas las URL, triadas per " "nombre de visitas en primièr las mai visitas, amb una paginacion amb %1 " "enregistraments per pagina." #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "Se sètz connectat·ada coma admin, podètz fornir un paramètre \"page\"" #: themes/default/templates/stats.html.ep:32 themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "Importar vòstras URL" #: themes/default/templates/api.html.ep:79 msgid "" "It will use Lstu's cookies to know which shortened URL it will return " "statistics for." msgstr "" "Aquò utilizarà los cookies de Lstu per saber quinas estatisticas tornar." #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:18 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON : fracàs" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:144 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:31 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON : succès" #: themes/default/templates/layouts/default.html.ep:37 msgid "License:" msgstr "Licéncia :" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Connexion" #: themes/default/templates/layouts/default.html.ep:43 msgid "Logout" msgstr "Desconnexion" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Desconnexion de las estatisticas admin" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Seguent" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "" "No shortened URL available. Please retry or contact the administrator at %1. " "Your URL to shorten: %2" msgstr "" "I a pas mai d’URL acorchadas disponiblas. Mercés de tornar ensajar o de " "contactar l’administrator sus %1. Recòrd de l’URL per acorchar : %2" #: themes/default/templates/api.html.ep:111 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:41 themes/default/templates/api.html.ep:83 themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Paramètres :" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Senhal" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "" "Mercés de verificar vòstres identificants : fracàs de l’autentificacion." #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Precedent" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "QRCode" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:32 themes/default/templates/api.html.ep:54 themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Responsa pel format HTML" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Responsa pel format JSON" #: themes/default/templates/api.html.ep:102 themes/default/templates/api.html.ep:117 themes/default/templates/api.html.ep:142 themes/default/templates/api.html.ep:15 themes/default/templates/api.html.ep:29 themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:59 themes/default/templates/api.html.ep:68 themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Responsa :" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "URL acorchada" #: themes/default/templates/api.html.ep:127 themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Deu totjorn tornar una capitada" #: themes/default/templates/layouts/default.html.ep:45 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Connexion" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "O planhèm èra pas possible de percórrer lo fichièr provesit" #: themes/default/templates/layouts/default.html.ep:42 themes/default/templates/layouts/default.html.ep:48 themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Estatisticas" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "" "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I " "refuse to shorten it." msgstr "" "L’òste de l’URL o d’una de sas redireccions (%1) es considerat coma nociu " "per Spamhaus. Refusi de l’acorchar." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "" "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe " "Browsing database. I refuse to shorten it." msgstr "" "L’URL o una de sas redireccions (%1) es considerat coma nociva per Google " "Safe Browsing. Refusi de l’acorchar." #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "" "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, " "phishing, etc.). I refuse to shorten it." msgstr "" "L’URL endralha %1 còps o mai cap a un a autre site. Pòt èsser una URL " "dangerossa (porrièl, pesca electronica, etc). Refusi de l’acorchar." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "" "The URL you want to shorten comes from a domain (%1) that is blacklisted on " "this server (usually because of spammers that use this domain)." msgstr "" "L’UR que volètz acorchar ven d’un domeni (%1) qu’es lista negra en aqueste " "servidor (sovent a causa de spammers qu’utilizan aqueste domeni)." #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 lib/Lstu/Controller/URL.pm:226 msgid "The shortened URL %1 doesn't exist." msgstr "L’URL acorchada %1 existís pas." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "" "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", " "\"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to " "shorten: %1" msgstr "" "Lo tèxte de l’acorchi deu pas èsser \"a\", \"api\", \"d\", \"cookie\", " "\"stats\", \"fullstats\", \"login\" o \"logout\" o acabar per \".json\". " "Recòrd de l’URL per acorchar : %1" #: themes/default/templates/api.html.ep:113 msgid "" "To do an admin login, set it to the password defined in the settings " "(\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" "Per vos connectar coma admin, botatz lo senhal definit dins los paramètres " "(\"adminpwd\" or \"hashed_adminpwd\")" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "" "Per vos desconnectar de l’administracion, definissètz aqueste paramètre coma " "\"logout\"." #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "Tròp de marrits senhals. Sètz bandit·ida." #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "URL" #: themes/default/templates/api.html.ep:43 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "URL per acorchar" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "" "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." "" msgstr "" "Avètz demandat d’acorchar tròp d’URL en pauc de temps. Sètz bandit per %1 " "ora(s)." #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "Sètz ben estat·ada connectat·ada." #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:31 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "Sètz ben estat·ada desconnectat·ada." #: themes/default/templates/api.html.ep:137 msgid "" "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) " "to use it." msgstr "" "Vos cal èsser connectat·ada coma admin (\"adminpwd\" o \"hashed_adminpwd\" " "paramètres) per utilizar aquò." #: themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "Vos cal èsser connectat·ada per utilizar aquò." #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "Seretz dirigit·ida cap a la pagina de las estatisticas de Lstu" #: themes/default/templates/api.html.ep:146 msgid "" "You will be redirected to Lstu statistics page with a message in case of " "failure" msgstr "" "Seretz dirigit·ida cap a la pagina de las estatisticas de Lstu amb un " "messatge se i a agut un fracàs" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "Seretz dirigit·ida cap a l’interfàcia de Lstu per la desconnexion" #: themes/default/templates/api.html.ep:19 msgid "" "You will be redirected to the Lstu classic interface where you will be able " "to shorten URLs" msgstr "" "Seretz dirigit·ida cap a l’interfàcia classica de Lstu ont poiretz acorchir " "d’URLs" #: themes/default/templates/api.html.ep:54 msgid "" "You will be redirected to the Lstu classic interface with a message giving " "the shortened URL" msgstr "" "Seretz dirigit·ida cap a l’interfàcia classica de Lstu amb un message donant " "l’URL acorchida" #: themes/default/templates/api.html.ep:62 msgid "" "You will be redirected to the targeted URL or to the Lstu interface with a " "message giving the failure reason" msgstr "" "Seretz dirigit·ida cap a l’URL finala o la pagina d’acuèlh de Lstu amb un " "messatge qu’indica la rason del fracàs" #: themes/default/templates/api.html.ep:123 msgid "" "You will have the statistics page with the admin stats if you're succesfully " "logged in, or your stats and a failure message otherwise" msgstr "" "Auretz la pagina d’estatisticas amb las statisticas admin se sètz ben " "connectat·ada, autrament vòstres estatisticas amb un messatge de fracàs" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "Sètz pas connectat·ada coma admin" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "Vòstre identificant" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "Vòstre senhal" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "entièr, quantas visitas unicas per l’URL acorchida" #: themes/default/templates/api.html.ep:11 themes/default/templates/api.html.ep:12 themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "obligatòri" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:114 themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:44 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:86 themes/default/templates/api.html.ep:88 msgid "optional" msgstr "facultatiu" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:57 themes/default/templates/api.html.ep:66 themes/default/templates/api.html.ep:92 themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "url_acorchida" #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:240 msgid "The shortened URL %1 no longer exists." msgstr "L’URL acorchida %1 existís pas pus." ================================================ FILE: themes/default/lib/Lstu/I18N/pt_BR.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. ($url) #: lib/Lstu/Controller/URL.pm:136 msgid "%1 is not a valid URL." msgstr "" #: themes/default/templates/api.html.ep:104 msgid "A page with a table containing the same informations that the JSON response" msgstr "" #: themes/default/templates/layouts/default.html.ep:56 msgid "About" msgstr "" #: themes/default/templates/stats.html.ep:18 themes/default/templates/stats.html.ep:19 msgid "Admin password" msgstr "" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:130 msgid "Bad password" msgstr "" #: themes/default/templates/layouts/default.html.ep:68 msgid "Browser extensions" msgstr "" #: themes/default/templates/index.html.ep:22 msgid "Copy to clipboard" msgstr "" #: themes/default/templates/stats.html.ep:62 themes/default/templates/stats.html.ep:78 msgid "Counter" msgstr "" #: themes/default/templates/stats.html.ep:67 themes/default/templates/stats.html.ep:81 msgid "Created" msgstr "" #: themes/default/templates/api.html.ep:52 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "" #: themes/default/templates/stats.html.ep:88 msgid "Delete" msgstr "" #: themes/default/templates/stats.html.ep:41 msgid "Export your URLs" msgstr "" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:153 themes/default/templates/api.html.ep:61 themes/default/templates/api.html.ep:79 msgid "Failure reason" msgstr "" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "" #: themes/default/templates/api.html.ep:109 msgid "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" #: themes/default/templates/index.html.ep:13 themes/default/templates/stats.html.ep:22 msgid "Go!" msgstr "" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "" #: themes/default/templates/stats.html.ep:125 msgid "Home" msgstr "" #: themes/default/templates/api.html.ep:134 msgid "If \"action\" is defined to \"logout\":" msgstr "" #: themes/default/templates/api.html.ep:127 msgid "If \"adminpwd\" is defined:" msgstr "" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:148 themes/default/templates/api.html.ep:21 themes/default/templates/api.html.ep:35 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:96 msgid "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" #. (config('page_offset') #: themes/default/templates/api.html.ep:89 msgid "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), it will give you statistics for all URLs, sorted by the most visited first, paginated with pages containing %1 records." msgstr "" #: themes/default/templates/api.html.ep:94 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "" #: themes/default/templates/stats.html.ep:37 themes/default/templates/stats.html.ep:40 msgid "Import URLs" msgstr "" #: themes/default/templates/api.html.ep:87 msgid "It will use Lstu's cookies to know which shortened URL it will return statistics for." msgstr "" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:153 themes/default/templates/api.html.ep:26 themes/default/templates/api.html.ep:61 themes/default/templates/api.html.ep:79 msgid "JSON: failure" msgstr "" #: themes/default/templates/api.html.ep:112 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:137 themes/default/templates/api.html.ep:152 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:60 themes/default/templates/api.html.ep:78 msgid "JSON: success" msgstr "" #: themes/default/templates/layouts/default.html.ep:55 msgid "License:" msgstr "" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "" #: themes/default/templates/layouts/default.html.ep:61 msgid "Logout" msgstr "" #: themes/default/templates/stats.html.ep:15 msgid "Logout from admin stats" msgstr "" #: themes/default/templates/stats.html.ep:120 msgid "Next" msgstr "" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:130 msgid "No shortened URL available. Please retry or contact the administrator at %1. Your URL to shorten: %2" msgstr "" #: themes/default/templates/api.html.ep:119 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:33 themes/default/templates/api.html.ep:49 themes/default/templates/api.html.ep:91 msgid "Parameters:" msgstr "" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:26 msgid "Please, check your credentials: unable to authenticate." msgstr "" #: themes/default/templates/stats.html.ep:118 msgid "Previous" msgstr "" #: themes/default/templates/stats.html.ep:86 msgid "QRCode" msgstr "" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:131 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:154 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:40 themes/default/templates/api.html.ep:62 msgid "Response for HTML format" msgstr "" #: themes/default/templates/api.html.ep:103 msgid "Response for JSON format" msgstr "" #: themes/default/templates/api.html.ep:110 themes/default/templates/api.html.ep:125 themes/default/templates/api.html.ep:150 themes/default/templates/api.html.ep:23 themes/default/templates/api.html.ep:37 themes/default/templates/api.html.ep:55 themes/default/templates/api.html.ep:67 themes/default/templates/api.html.ep:76 themes/default/templates/api.html.ep:98 msgid "Response:" msgstr "" #: themes/default/templates/stats.html.ep:57 themes/default/templates/stats.html.ep:75 msgid "Shortened URL" msgstr "" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:32 msgid "Should always be successful" msgstr "" #: themes/default/templates/layouts/default.html.ep:63 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "" #: lib/Lstu/Controller/URL.pm:53 themes/default/templates/api.html.ep:8 msgid "Sorry, the API is disabled." msgstr "" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "" #: themes/default/templates/layouts/default.html.ep:60 themes/default/templates/layouts/default.html.ep:66 themes/default/templates/stats.html.ep:9 msgid "Statistics" msgstr "" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I refuse to shorten it." msgstr "" #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe Browsing database. I refuse to shorten it." msgstr "" #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, phishing, etc.). I refuse to shorten it." msgstr "" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "The URL you want to shorten comes from a domain (%1) that is blacklisted on this server (usually because of spammers that use this domain)." msgstr "" #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:200 lib/Lstu/Controller/URL.pm:260 msgid "The shortened URL %1 doesn't exist." msgstr "" #. ($c->url_for('/') #: lib/Lstu/Controller/URL.pm:258 msgid "The shortened URL %1 no longer exists." msgstr "" #. ($url) #: lib/Lstu/Controller/URL.pm:77 msgid "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", \"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to shorten: %1" msgstr "" #: themes/default/templates/api.html.ep:9 msgid "This page is informational only." msgstr "" #: themes/default/templates/api.html.ep:121 msgid "To do an admin login, set it to the password defined in the settings (\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" #: themes/default/templates/api.html.ep:122 msgid "To do an admin logout, set it to \"logout\"." msgstr "" #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "" #: themes/default/templates/api.html.ep:103 themes/default/templates/api.html.ep:112 themes/default/templates/stats.html.ep:52 themes/default/templates/stats.html.ep:72 msgid "URL" msgstr "" #: themes/default/templates/api.html.ep:51 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:41 msgid "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." msgstr "" #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:25 msgid "You have been successfully logged in." msgstr "" #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:137 themes/default/templates/api.html.ep:39 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "" #: themes/default/templates/api.html.ep:145 msgid "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) to use it." msgstr "" #: themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:85 msgid "You must be logged in to use it." msgstr "" #: themes/default/templates/api.html.ep:138 msgid "You will be redirected to Lstu statistics page" msgstr "" #: themes/default/templates/api.html.ep:154 msgid "You will be redirected to Lstu statistics page with a message in case of failure" msgstr "" #: themes/default/templates/api.html.ep:40 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "" #: themes/default/templates/api.html.ep:27 msgid "You will be redirected to the Lstu classic interface where you will be able to shorten URLs" msgstr "" #: themes/default/templates/api.html.ep:62 msgid "You will be redirected to the Lstu classic interface with a message giving the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:70 msgid "You will be redirected to the targeted URL or to the Lstu interface with a message giving the failure reason" msgstr "" #: themes/default/templates/api.html.ep:131 msgid "You will have the statistics page with the admin stats if you're succesfully logged in, or your stats and a failure message otherwise" msgstr "" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "" #: themes/default/templates/api.html.ep:19 msgid "Your login" msgstr "" #: themes/default/templates/api.html.ep:20 msgid "Your password" msgstr "" #: themes/default/templates/api.html.ep:103 themes/default/templates/api.html.ep:112 msgid "integer, how many unique visits of the shortened URL" msgstr "" #: themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:20 themes/default/templates/api.html.ep:51 msgid "mandatory" msgstr "" #: themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:148 themes/default/templates/api.html.ep:21 themes/default/templates/api.html.ep:35 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:94 themes/default/templates/api.html.ep:96 msgid "optional" msgstr "" #: themes/default/templates/api.html.ep:100 themes/default/templates/api.html.ep:107 themes/default/templates/api.html.ep:143 themes/default/templates/api.html.ep:65 themes/default/templates/api.html.ep:74 msgid "shortened_url" msgstr "" ================================================ FILE: themes/default/lib/Lstu/I18N/sv.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2020-01-03 20:14+0000\n" "Last-Translator: Filip Bengtsson \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.9.1\n" #. ($url) #: lib/Lstu/Controller/URL.pm:118 msgid "%1 is not a valid URL." msgstr "%1 är inte en giltig länk." #: themes/default/templates/api.html.ep:96 msgid "A page with a table containing the same informations that the JSON response" msgstr "En sida med en tabell innehållande samma information som JSON-responsen" #: themes/default/templates/layouts/default.html.ep:54 msgid "About" msgstr "Om" #: themes/default/templates/stats.html.ep:13 themes/default/templates/stats.html.ep:14 msgid "Admin password" msgstr "Administratörslösenord" #: lib/Lstu/Controller/Admin.pm:87 themes/default/templates/api.html.ep:122 msgid "Bad password" msgstr "Ogiltigt lösenord" #: themes/default/templates/index.html.ep:21 msgid "Copy to clipboard" msgstr "Kopiera till urklipp" #: themes/default/templates/stats.html.ep:46 msgid "Counter" msgstr "Räknare" #: themes/default/templates/stats.html.ep:47 msgid "Created" msgstr "Skapad" #: themes/default/templates/api.html.ep:44 themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:9 msgid "Custom shortened text" msgstr "Anpassad förkortad text" #: themes/default/templates/stats.html.ep:52 msgid "Delete" msgstr "Radera" #: themes/default/templates/stats.html.ep:36 msgid "Export your URLs" msgstr "Exportera dina länkar" #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "Failure reason" msgstr "Orsak för haveri" #: lib/Lstu/Controller/Stats.pm:43 msgid "File imported" msgstr "Filen har importerats" #: themes/default/templates/api.html.ep:101 msgid "Get the details (visit counter, creation date, etc.) of a shortened URL." msgstr "" "Visa information (som besöksräknare och datum för skapande) för en förkortad " "länk." #: themes/default/templates/index.html.ep:12 themes/default/templates/stats.html.ep:17 msgid "Go!" msgstr "Kör!" #: themes/default/templates/partial/lstu.js.ep:30 msgid "Hit Enter, then Ctrl+C to copy the short link" msgstr "Tryck Enter och sedan Crtl+C för att kopiera den nerkortade länken" #: themes/default/templates/stats.html.ep:89 msgid "Home" msgstr "Hem" #: themes/default/templates/api.html.ep:126 msgid "If \"action\" is defined to \"logout\":" msgstr "Om ”action” har satts till ”logout”:" #: themes/default/templates/api.html.ep:119 msgid "If \"adminpwd\" is defined:" msgstr "Om ”adminpwd” har definierats:" #: themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:88 msgid "If equal to \"json\", response will be in JSON format, HTML format otherwise" msgstr "" "Om det är lika med ”json” kommer svaret ges i JSON-format, i annat fall som " "HTML" #. (config('page_offset') #: themes/default/templates/api.html.ep:81 msgid "If you are logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting), it will give you statistics for all URLs, sorted by the most visited first, paginated with pages containing %1 records." msgstr "" "Om du är inloggad som administratör (inställningen ”adminpwd” eller " "”hashed_adminpwd”) kommer du få statistiken för alla länkar, sorterade efter " "antal besökare i fallande ordning och %1 poster per sida." #: themes/default/templates/api.html.ep:86 msgid "If you are logged in as admin, you can provide a \"page\" parameter" msgstr "Om du är inloggad som administratör kan du ange parametern ”sida”" #: themes/default/templates/stats.html.ep:32 themes/default/templates/stats.html.ep:35 msgid "Import URLs" msgstr "Importera länkar" #: themes/default/templates/api.html.ep:79 msgid "It will use Lstu's cookies to know which shortened URL it will return statistics for." msgstr "" "Detta kommer använda LSTUs kakor för att avgöra vilken förkortad länks " "statistik som ska skickas." #: themes/default/templates/api.html.ep:105 themes/default/templates/api.html.ep:122 themes/default/templates/api.html.ep:145 themes/default/templates/api.html.ep:18 themes/default/templates/api.html.ep:53 themes/default/templates/api.html.ep:71 msgid "JSON: failure" msgstr "JSON: misslyckades" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:144 themes/default/templates/api.html.ep:17 themes/default/templates/api.html.ep:31 themes/default/templates/api.html.ep:52 themes/default/templates/api.html.ep:70 msgid "JSON: success" msgstr "JSON: lyckades" #: themes/default/templates/layouts/default.html.ep:53 msgid "License:" msgstr "Licens:" #: themes/default/templates/login.html.ep:8 msgid "Login" msgstr "Logga in" #: themes/default/templates/layouts/default.html.ep:59 msgid "Logout" msgstr "Logga ut" #: themes/default/templates/stats.html.ep:10 msgid "Logout from admin stats" msgstr "Logga ut från administrationsstatistiken" #: themes/default/templates/stats.html.ep:84 msgid "Next" msgstr "Nästa" #. ($c->config('contact') #: lib/Lstu/Controller/URL.pm:112 msgid "No shortened URL available. Please retry or contact the administrator at %1. Your URL to shorten: %2" msgstr "" "Ingen förkortad länk tillgänglig. Försök kontakta administratören på %1. Din " "länk som skulle kortas: %2" #: themes/default/templates/api.html.ep:111 themes/default/templates/api.html.ep:138 themes/default/templates/api.html.ep:25 themes/default/templates/api.html.ep:41 themes/default/templates/api.html.ep:83 themes/default/templates/api.html.ep:9 msgid "Parameters:" msgstr "Parameterar:" #: themes/default/templates/login.html.ep:12 msgid "Password" msgstr "Lösenord" #: lib/Lstu/Controller/Authent.pm:35 themes/default/templates/api.html.ep:18 msgid "Please, check your credentials: unable to authenticate." msgstr "Kontrollera gärna dina uppgifter: kunde inte bekräfta identitet." #: themes/default/templates/stats.html.ep:82 msgid "Previous" msgstr "Föregående" #: themes/default/templates/stats.html.ep:50 msgid "QRCode" msgstr "QR-kod" #: themes/default/templates/api.html.ep:123 themes/default/templates/api.html.ep:130 themes/default/templates/api.html.ep:146 themes/default/templates/api.html.ep:19 themes/default/templates/api.html.ep:32 themes/default/templates/api.html.ep:54 themes/default/templates/api.html.ep:96 msgid "Response for HTML format" msgstr "Svar för HTML-format" #: themes/default/templates/api.html.ep:95 msgid "Response for JSON format" msgstr "Svar för JSON-format" #: themes/default/templates/api.html.ep:102 themes/default/templates/api.html.ep:117 themes/default/templates/api.html.ep:142 themes/default/templates/api.html.ep:15 themes/default/templates/api.html.ep:29 themes/default/templates/api.html.ep:47 themes/default/templates/api.html.ep:59 themes/default/templates/api.html.ep:68 themes/default/templates/api.html.ep:90 msgid "Response:" msgstr "Svar:" #: themes/default/templates/stats.html.ep:45 msgid "Shortened URL" msgstr "Förkortad länk" #: themes/default/templates/api.html.ep:127 themes/default/templates/api.html.ep:24 msgid "Should always be successful" msgstr "Borde alltid lyckas" #: themes/default/templates/layouts/default.html.ep:61 themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 msgid "Signin" msgstr "Logga in" #: lib/Lstu/Controller/Stats.pm:45 msgid "Sorry, unable to parse the provided file" msgstr "Kunde tyvärr inte tolka filen" #: themes/default/templates/layouts/default.html.ep:58 themes/default/templates/layouts/default.html.ep:64 themes/default/templates/stats.html.ep:4 msgid "Statistics" msgstr "Statistik" #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:172 msgid "The URL host or one of its redirection(s) (%1) is blacklisted at Spamhaus. I refuse to shorten it." msgstr "" "Länkens värd eller en av dess omdirigeringar (%1) har svartlistats hos " "Spamhaus. Jag vägrar förkorta den." #. ($url) #: lib/Lstu/Plugin/Helpers.pm:178 msgid "The URL or one of its redirection(s) (%1) is blacklisted in Google Safe Browsing database. I refuse to shorten it." msgstr "" "Länkens värd eller en av dess omdirigeringar (%1) har svartlistats hos " "Google Safe Browsing. Jag vägrar förkorta den." #. ($c->config('max_redir') #: lib/Lstu/Plugin/Helpers.pm:194 msgid "The URL redirects %1 times or most. It's most likely a dangerous URL (spam, phishing, etc.). I refuse to shorten it." msgstr "" "Länken omdirigerar minst %1 gånger. Det är troligen en osäker länk (till " "exempel spam eller phishing). Jag vägrar förkorta den." #. ($url->host) #: lib/Lstu/Plugin/Helpers.pm:164 msgid "The URL you want to shorten comes from a domain (%1) that is blacklisted on this server (usually because of spammers that use this domain)." msgstr "" "Länken du vill förkorta kommer från en domän (%1) har på den här servern (" "troligen på grund av att spammare använder den här domänen). Jag vägrar " "förkorta den." #. ($c->url_for('/') #: lib/Lstu/Controller/Admin.pm:131 lib/Lstu/Controller/Stats.pm:184 lib/Lstu/Controller/URL.pm:229 msgid "The shortened URL %1 doesn't exist." msgstr "Den förkortade länken %1 finns inte." #. ($url) #: lib/Lstu/Controller/URL.pm:59 msgid "The shortened text can't be \"a\", \"api\", \"d\", \"cookie\", \"stats\", \"fullstats\", \"login\" or \"logout\" or end with \".json\". Your URL to shorten: %1" msgstr "" "Den nerkortade texten får inte vara ”a”, ”api”, ”cookie”, ”d”, ”stats”, " "”fullstats”, ”login”, ”logout” eller sluta med ”.json”. Din länk att " "förkorta: %1" #: themes/default/templates/api.html.ep:113 msgid "To do an admin login, set it to the password defined in the settings (\"adminpwd\" or \"hashed_adminpwd\")" msgstr "" "För att logga in som administratör, ändra det till lösenordet som anges i " "inställningen ”adminpwd” eller ”hashed_adminpwd”" #: themes/default/templates/api.html.ep:114 msgid "To do an admin logout, set it to \"logout\"." msgstr "För att logga ut som administratör, ändra det till ”logout”." #: lib/Lstu/Controller/Admin.pm:29 msgid "Too many bad passwords. You're banned." msgstr "För många ogiltiga lösenord. Du har blockerats." #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 themes/default/templates/stats.html.ep:44 msgid "URL" msgstr "Länk" #: themes/default/templates/api.html.ep:43 themes/default/templates/index.html.ep:5 themes/default/templates/index.html.ep:6 msgid "URL to shorten" msgstr "Länk att förkorta" #. ($penalty/3600) #: lib/Lstu/Controller/URL.pm:35 msgid "You asked to shorten too many URLs too quickly. You're banned for %1 hour(s)." msgstr "" "Nu har du bett om för många länkförkortningar under för kort tid. Du har " "blockerats i %1 timmar." #: lib/Lstu/Controller/Admin.pm:52 lib/Lstu/Controller/Authent.pm:26 themes/default/templates/api.html.ep:121 themes/default/templates/api.html.ep:17 msgid "You have been successfully logged in." msgstr "Du har loggats in." #: lib/Lstu/Controller/Admin.pm:73 lib/Lstu/Controller/Authent.pm:65 themes/default/templates/api.html.ep:129 themes/default/templates/api.html.ep:31 themes/default/templates/logout.html.ep:3 msgid "You have been successfully logged out." msgstr "Du har loggats ut." #: themes/default/templates/api.html.ep:137 msgid "You must be logged in as admin (\"adminpwd\" or \"hashed_adminpwd\" setting) to use it." msgstr "" "Du måste vara inloggad som administratör (inställningen ”adminpwd” eller " "”hashed_adminpwd”) för att använda den." #: themes/default/templates/api.html.ep:39 themes/default/templates/api.html.ep:77 msgid "You must be logged in to use it." msgstr "Du måste vara inloggad för att använda den." #: themes/default/templates/api.html.ep:130 msgid "You will be redirected to Lstu statistics page" msgstr "Du kommer skickas vidare till sidan med statistik för LSTU" #: themes/default/templates/api.html.ep:146 msgid "You will be redirected to Lstu statistics page with a message in case of failure" msgstr "" "Du kommer skickas vidare till sidan med statistik för LSTU med ett " "meddelande om det inte lyckas" #: themes/default/templates/api.html.ep:32 msgid "You will be redirected to Lstu successfully logged out interface" msgstr "Du kommer skickas vidare till sidan för lyckad utloggning från LSTU" #: themes/default/templates/api.html.ep:19 msgid "You will be redirected to the Lstu classic interface where you will be able to shorten URLs" msgstr "" "Du kommer skickas vidare till LSTUs klassiska gränssnitt där du kan förkorta " "länkar" #: themes/default/templates/api.html.ep:54 msgid "You will be redirected to the Lstu classic interface with a message giving the shortened URL" msgstr "" "Du kommer skickas vidare till LSTUs klassiska gränssnitt med ett meddelande " "innehållande den förkortade länken" #: themes/default/templates/api.html.ep:62 msgid "You will be redirected to the targeted URL or to the Lstu interface with a message giving the failure reason" msgstr "" "Du kommer skickas vidare till mållänken eller en LTSU-sida med en förklaring " "av vad som gått snett" #: themes/default/templates/api.html.ep:123 msgid "You will have the statistics page with the admin stats if you're succesfully logged in, or your stats and a failure message otherwise" msgstr "" "Du kommer få statistiksidan med administrationsstatistiken om du har loggats " "in, och annars statistiken och ett felmeddelande" #: lib/Lstu/Controller/Admin.pm:142 msgid "You're not authenticated as the admin" msgstr "Du är inte inloggad som administratör" #: themes/default/templates/api.html.ep:11 msgid "Your login" msgstr "Dina inloggningsuppgifter" #: themes/default/templates/api.html.ep:12 msgid "Your password" msgstr "Ditt lösenord" #: themes/default/templates/api.html.ep:104 themes/default/templates/api.html.ep:95 msgid "integer, how many unique visits of the shortened URL" msgstr "heltal, hur många enskilda besökare den förkortade länken har haft" #: themes/default/templates/api.html.ep:11 themes/default/templates/api.html.ep:12 themes/default/templates/api.html.ep:43 msgid "mandatory" msgstr "obligatoriskt" #: themes/default/templates/api.html.ep:113 themes/default/templates/api.html.ep:114 themes/default/templates/api.html.ep:115 themes/default/templates/api.html.ep:13 themes/default/templates/api.html.ep:140 themes/default/templates/api.html.ep:27 themes/default/templates/api.html.ep:44 themes/default/templates/api.html.ep:45 themes/default/templates/api.html.ep:86 themes/default/templates/api.html.ep:88 msgid "optional" msgstr "frivilligt" #: themes/default/templates/api.html.ep:135 themes/default/templates/api.html.ep:57 themes/default/templates/api.html.ep:66 themes/default/templates/api.html.ep:92 themes/default/templates/api.html.ep:99 msgid "shortened_url" msgstr "forkortad_lank" ================================================ FILE: themes/default/lib/Lstu/I18N.pm ================================================ package Lstu::I18N; use base 'Locale::Maketext'; use File::Basename qw/dirname/; use Locale::Maketext::Lexicon { _auto => 1, _decode => 1, _style => 'gettext', '*' => [Gettext => dirname(__FILE__) . '/I18N/*.po'] }; 1; ================================================ FILE: themes/default/public/browserconfig.xml ================================================ #ffc40d ================================================ FILE: themes/default/public/css/animation.css ================================================ /* Animation example, for spinners */ .animate-spin { -moz-animation: spin 2s infinite linear; -o-animation: spin 2s infinite linear; -webkit-animation: spin 2s infinite linear; animation: spin 2s infinite linear; display: inline-block; } @-moz-keyframes spin { 0% { -moz-transform: rotate(0deg); -o-transform: rotate(0deg); -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -moz-transform: rotate(359deg); -o-transform: rotate(359deg); -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @-webkit-keyframes spin { 0% { -moz-transform: rotate(0deg); -o-transform: rotate(0deg); -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -moz-transform: rotate(359deg); -o-transform: rotate(359deg); -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @-o-keyframes spin { 0% { -moz-transform: rotate(0deg); -o-transform: rotate(0deg); -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -moz-transform: rotate(359deg); -o-transform: rotate(359deg); -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @-ms-keyframes spin { 0% { -moz-transform: rotate(0deg); -o-transform: rotate(0deg); -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -moz-transform: rotate(359deg); -o-transform: rotate(359deg); -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes spin { 0% { -moz-transform: rotate(0deg); -o-transform: rotate(0deg); -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -moz-transform: rotate(359deg); -o-transform: rotate(359deg); -webkit-transform: rotate(359deg); transform: rotate(359deg); } } ================================================ FILE: themes/default/public/css/bootstrap-lstu.min.css ================================================ /*! * Bootstrap v3.3.4 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=8d56756b325f9f22f4eb) * Config saved to config.json and https://gist.github.com/8d56756b325f9f22f4eb */ /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}legend,td,th{padding:0} /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ @media print{*,:after,:before{background:0 0!important;color:#000!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999}thead{display:table-header-group}blockquote,img,pre,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}*,:after,:before,input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff;padding-top:40px;padding-bottom:40px}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus,input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,dl,h4,h5,h6,ol,ul{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{background-color:#fcf8e3;padding:.2em}.text-left,th{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}dl,ol,ul{margin-top:0}.alert>p,.alert>ul,ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline{margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th{padding:8px;line-height:1.42857143;border-top:1px solid #ddd}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered,.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset,legend{padding:0;border:0}fieldset{margin:0;min-width:0}legend{width:100%;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file],legend{display:block}input[type=range]{display:block;width:100%}select[multiple],select[multiple].input-sm,select[size],textarea.form-control,textarea.input-sm{height:auto}.form-control,output{display:block;font-size:14px;line-height:1.42857143;color:#555}output{padding-top:7px}.form-control{width:100%;height:34px;padding:6px 12px;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date],input[type=datetime-local],input[type=month],input[type=time]{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.checkbox-inline,.radio label,.radio-inline{padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox label,.radio label{min-height:20px}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-left:-20px;margin-top:4px \9}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;vertical-align:middle}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}.checkbox-inline.disabled,.checkbox.disabled label,.radio-inline.disabled,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio label,fieldset[disabled] .radio-inline,fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.form-group-sm .form-control,.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.form-group-sm .form-control,select.input-sm{height:30px;line-height:30px}select[multiple].form-group-sm .form-control,select[multiple].input-lg,textarea.form-group-sm .form-control,textarea.input-lg{height:auto}.form-group-sm .form-control-static{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;min-height:32px}.form-group-lg .form-control,.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.form-group-lg .form-control,select.input-lg{height:46px;line-height:46px}select[multiple].form-group-lg .form-control,textarea.form-group-lg .form-control{height:auto}.form-group-lg .form-control-static{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;min-height:38px}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.333333px}.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.active,.btn-default.focus,.btn-default:active,.btn-default:focus,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.active,.btn-primary.focus,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.active,.btn-success.focus,.btn-success:active,.btn-success:focus,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.active,.btn-info.focus,.btn-info:active,.btn-info:focus,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.active,.btn-warning.focus,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.active,.btn-danger.focus,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:400;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-xs{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px}.btn-block{display:block;width:100%}.alert>p+p,.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pager li,.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#337ab7;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7;cursor:default}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.pager:after,.pager:before,.row:after,.row:before{content:" ";display:table}.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.pager:after,.row:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs,.visible-xs-block{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}.visible-xs-inline{display:inline!important}.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm,.visible-sm-block{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}.visible-sm-inline{display:inline!important}.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md,.visible-md-block{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}.visible-md-inline{display:inline!important}.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg,.visible-lg-block{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}.visible-lg-inline{display:inline!important}.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}.hidden-print{display:none!important}}.container{padding:15px;margin:0 auto}@font-face{font-family:'Henny_Penny';font-style:normal;font-weight:400;src:local('Henny Penny'),local('HennyPenny-Regular'),url(../font/hennypenny.woff2) format('woff2')}@font-face{font-family:'Oswald';font-style:bold;font-weight:400;src:local('Oswald'),url(../font/Oswald-Bold.ttf)}@font-face{font-family:'OpenSans';font-style:normal;font-weight:200;src:local('OpenSans'),url(../font/OpenSans-Regular.ttf)}.hennypenny,.oswald{font-family:'Oswald';font-size:42px}.hennypenny{font-family:'Henny_Penny',cursive}.opensans{font-family:'OpenSans';font-size:25px}.link_nocol,.link_nocol:hover{color:#000;text-decoration:none}dd>strong{padding-left:15px}.logo{height:auto!important;padding-right:20px}.logo-img{height:200px;width:100%}.allons-y{border-style:none;background:linear-gradient(to right,rgba(255,255,255,.3) 0%,rgba(255,255,255,.5) 100%,transparent 25px) no-repeat,#b24b04;background-repeat:no-repeat,no-repeat;border-radius:5px;box-shadow:2px 2px 1px 1px rgba(0,0,0,.3);padding:8px;margin-right:9px;color:#fff;font-family:'OpenSans';font-size:14px;background-size:0 100%,100% 100%;transition:background-size .3s}.allons-y:hover{background-size:101% 100%}.allons-y:active{box-shadow:none;transform:translate(2px,2px);transition:all .1s}@font-face{font-family:'fontelico';src:url(../font/fontelico.eot?21941692);src:url(../font/fontelico.eot?21941692#iefix) format('embedded-opentype'),url(../font/fontelico.woff2?21941692) format('woff2'),url(../font/fontelico.woff?21941692) format('woff'),url(../font/fontelico.ttf?21941692) format('truetype'),url(../font/fontelico.svg?21941692#fontelico) format('svg');font-weight:400;font-style:normal}[class*=" icon-"]:before,[class^=icon-]:before{font-family:"fontelico";font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-clipboard:before{content:'\e800'}.icon-trash:before{content:'\e804'} ================================================ FILE: themes/default/public/css/bootstrap.min.css ================================================ /*! * Bootstrap v3.3.4 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=8d56756b325f9f22f4eb) * Config saved to config.json and https://gist.github.com/8d56756b325f9f22f4eb *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{line-height:34px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.form-group-sm .form-control{height:30px;line-height:30px}textarea.form-group-sm .form-control,select[multiple].form-group-sm .form-control{height:auto}.form-group-sm .form-control-static{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;min-height:32px}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.form-group-lg .form-control{height:46px;line-height:46px}textarea.form-group-lg .form-control,select[multiple].form-group-lg .form-control{height:auto}.form-group-lg .form-control-static{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;min-height:38px}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.333333px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#337ab7;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.pager:before,.pager:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.pager:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}} ================================================ FILE: themes/default/public/css/fontelico-codes.css ================================================ .icon-clipboard:before { content: '\e800'; } /* '' */ .icon-trash:before { content: '\e804'; } /* '' */ ================================================ FILE: themes/default/public/css/fontelico-embedded.css ================================================ @font-face { font-family: 'fontelico'; src: url('../font/fontelico.eot?87465342'); src: url('../font/fontelico.eot?87465342#iefix') format('embedded-opentype'), url('../font/fontelico.svg?87465342#fontelico') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontelico'; src: url('data:application/octet-stream;base64,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') format('woff'), url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ /* @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontelico'; src: url('../font/fontelico.svg?87465342#fontelico') format('svg'); } } */ [class^="icon-"]:before, [class*=" icon-"]:before { font-family: "fontelico"; font-style: normal; font-weight: normal; speak: none; display: inline-block; text-decoration: inherit; width: 1em; margin-right: .2em; text-align: center; /* opacity: .8; */ /* For safety - reset parent styles, that can break glyph codes*/ font-variant: normal; text-transform: none; /* fix buttons height, for twitter bootstrap */ line-height: 1em; /* Animation center compensation - margins should be symmetric */ /* remove if not needed */ margin-left: .2em; /* you can be more comfortable with increased icons size */ /* font-size: 120%; */ /* Uncomment for 3D effect */ /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } .icon-clipboard:before { content: '\e800'; } /* '' */ .icon-trash:before { content: '\e804'; } /* '' */ ================================================ FILE: themes/default/public/css/fontelico-ie7-codes.css ================================================ .icon-clipboard { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-trash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } ================================================ FILE: themes/default/public/css/fontelico-ie7.css ================================================ [class^="icon-"], [class*=" icon-"] { font-family: 'fontelico'; font-style: normal; font-weight: normal; /* fix buttons height */ line-height: 1em; /* you can be more comfortable with increased icons size */ /* font-size: 120%; */ } .icon-clipboard { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-trash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } ================================================ FILE: themes/default/public/css/fontelico.css ================================================ @font-face { font-family: 'fontelico'; src: url('../font/fontelico.eot?21941692'); src: url('../font/fontelico.eot?21941692#iefix') format('embedded-opentype'), url('../font/fontelico.woff2?21941692') format('woff2'), url('../font/fontelico.woff?21941692') format('woff'), url('../font/fontelico.ttf?21941692') format('truetype'), url('../font/fontelico.svg?21941692#fontelico') format('svg'); font-weight: normal; font-style: normal; } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ /* @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontelico'; src: url('../font/fontelico.svg?21941692#fontelico') format('svg'); } } */ [class^="icon-"]:before, [class*=" icon-"]:before { font-family: "fontelico"; font-style: normal; font-weight: normal; speak: none; display: inline-block; text-decoration: inherit; width: 1em; margin-right: .2em; text-align: center; /* opacity: .8; */ /* For safety - reset parent styles, that can break glyph codes*/ font-variant: normal; text-transform: none; /* fix buttons height, for twitter bootstrap */ line-height: 1em; /* Animation center compensation - margins should be symmetric */ /* remove if not needed */ margin-left: .2em; /* you can be more comfortable with increased icons size */ /* font-size: 120%; */ /* Font smoothing. That was taken from TWBS */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; /* Uncomment for 3D effect */ /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } .icon-clipboard:before { content: '\e800'; } /* '' */ .icon-trash:before { content: '\e804'; } /* '' */ ================================================ FILE: themes/default/public/css/fontelico.min.css ================================================ @font-face{font-family:fontelico;src:url(data:application/vnd.ms-fontobject;base64,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sgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=);src:url(data:application/vnd.ms-fontobject;base64,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sgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=) format('embedded-opentype'),url(data:application/font-woff2;base64,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) format('woff2'),url(data:application/x-font-woff;base64,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) format('woff'),url(data:application/x-font-ttf;base64,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) format('truetype'),url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxtZXRhZGF0YT5Db3B5cmlnaHQgKEMpIDIwMTYgYnkgb3JpZ2luYWwgYXV0aG9ycyBAIGZvbnRlbGxvLmNvbTwvbWV0YWRhdGE+CjxkZWZzPgo8Zm9udCBpZD0iZm9udGVsaWNvIiBob3Jpei1hZHYteD0iMTAwMCIgPgo8Zm9udC1mYWNlIGZvbnQtZmFtaWx5PSJmb250ZWxpY28iIGZvbnQtd2VpZ2h0PSI0MDAiIGZvbnQtc3RyZXRjaD0ibm9ybWFsIiB1bml0cy1wZXItZW09IjEwMDAiIGFzY2VudD0iODUwIiBkZXNjZW50PSItMTUwIiAvPgo8bWlzc2luZy1nbHlwaCBob3Jpei1hZHYteD0iMTAwMCIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImNsaXBib2FyZCIgdW5pY29kZT0iJiN4ZTgwMDsiIGQ9Ik0wLTE1MGwwIDkwNCAyMjUgMCAwLTY0LTE2MSAwIDAtNzc0IDU3OSAwIDAgNzc0LTE2MSAwIDAgNjQgMjI1IDAgMC05MDQtNzA3IDB6IG0xMjkgMTI5bDAgMzEgMzEgMCAwLTMxLTMxIDB6IG0wIDEyMWwwIDMxIDMxIDAgMC0zMS0zMSAweiBtMCAxMjFsMCAzMSAzMSAwIDAtMzEtMzEgMHogbTAgMTIxbDAgMzIgMzEgMCAwLTMyLTMxIDB6IG0wIDEyMWwwIDMyIDMxIDAgMC0zMi0zMSAweiBtMCA5NmwwIDk0IDEyOSAwIDAgOTdxMCA0MSAyNyA3MXQ2OSAyOSA2OS0zMCAyOC03MHEwLTU2LTItOTdsMTI5IDAgMC05NC00NDkgMHogbTk2LTU4MmwwIDMzIDM1MyAwIDAtMzMtMzUzIDB6IG0wIDEyMWwwIDMzIDM1MyAwIDAtMzMtMzUzIDB6IG0wIDEyMWwwIDMzIDM1MyAwIDAtMzMtMzUzIDB6IG0wIDEyMWwwIDM0IDM1MyAwIDAtMzQtMzUzIDB6IG0wIDEyMWwwIDM0IDM1MyAwIDAtMzQtMzUzIDB6IG05NyAyNjBxMC0xNCA5LTIydDIzLTkgMjIgOSA5IDIyLTkgMjQtMjIgOS0yMy05LTktMjR6IiBob3Jpei1hZHYteD0iNzA3IiAvPgoKPGdseXBoIGdseXBoLW5hbWU9InRyYXNoIiB1bmljb2RlPSImI3hlODA0OyIgZD0iTTI4NiA0Mzl2LTMyMXEwLTgtNS0xM3QtMTMtNWgtMzZxLTggMC0xMyA1dC01IDEzdjMyMXEwIDggNSAxM3QxMyA1aDM2cTggMCAxMy01dDUtMTN6IG0xNDMgMHYtMzIxcTAtOC01LTEzdC0xMy01aC0zNnEtOCAwLTEzIDV0LTUgMTN2MzIxcTAgOCA1IDEzdDEzIDVoMzZxOCAwIDEzLTV0NS0xM3ogbTE0MiAwdi0zMjFxMC04LTUtMTN0LTEyLTVoLTM2cS04IDAtMTMgNXQtNSAxM3YzMjFxMCA4IDUgMTN0MTMgNWgzNnE3IDAgMTItNXQ1LTEzeiBtNzItNDA0djUyOWgtNTAwdi01MjlxMC0xMiA0LTIydDgtMTUgNi01aDQ2NHEyIDAgNiA1dDggMTUgNCAyMnogbS0zNzUgNjAxaDI1MGwtMjcgNjVxLTQgNS05IDZoLTE3N3EtNi0xLTEwLTZ6IG01MTgtMTh2LTM2cTAtOC01LTEzdC0xMy01aC01NHYtNTI5cTAtNDYtMjYtODB0LTYzLTM0aC00NjRxLTM3IDAtNjMgMzN0LTI3IDc5djUzMWgtNTNxLTggMC0xMyA1dC01IDEzdjM2cTAgOCA1IDEzdDEzIDVoMTcybDM5IDkzcTkgMjEgMzEgMzV0NDQgMTVoMTc4cTIzIDAgNDQtMTV0MzAtMzVsMzktOTNoMTczcTggMCAxMy01dDUtMTN6IiBob3Jpei1hZHYteD0iNzg1LjciIC8+CjwvZm9udD4KPC9kZWZzPgo8L3N2Zz4=) format('svg');font-weight:400;font-style:normal}[class*=" icon-"]:before,[class^=icon-]:before{font-family:fontelico;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-clipboard:before{content:'\e800'}.icon-trash:before{content:'\e804'} ================================================ FILE: themes/default/public/css/lstu.css ================================================ body { padding-top: 40px; padding-bottom: 40px; } .container { padding: 15px; margin: 0 auto; } @font-face { font-family: 'Henny_Penny'; font-style: normal; font-weight: 400; src: local('Henny Penny'), local('HennyPenny-Regular'), url('../font/hennypenny.woff2') format('woff2'); } @font-face { font-family: 'Oswald'; font-style: bold; font-weight: 400; src: local('Oswald'), url('../font/Oswald-Bold.ttf') } @font-face { font-family: 'OpenSans'; font-style: normal; font-weight: 200; src: local('OpenSans'), url('../font/OpenSans-Regular.ttf') } .oswald { font-family: 'Oswald'; font-size: 42px; } .hennypenny { font-family: 'Henny_Penny', cursive; font-size: 42px; } .opensans { font-family: 'OpenSans'; font-size: 25px; } .link_nocol, .link_nocol:hover { color: #000; text-decoration: none; } dd > strong { padding-left: 15px; } .logo { height: auto!important; padding-right: 20px; } .logo-img { height: 200px; width: 100%; } .allons-y { border-style: none; background: linear-gradient(to right, rgba(255, 255, 255, 0.3) 0%, rgba(255,255,255, 0.5) 100%, transparent 25px) no-repeat, #B24B04; background-repeat: no-repeat, no-repeat; border-radius: 5px; box-shadow: 2px 2px 1px 1px rgba(0,0,0,0.3); padding: 8px; margin-right: 9px; color: white; font-family: 'OpenSans'; font-size: 14px; background-size: 0 100%, 100% 100%; transition: background-size 0.3s; } .allons-y:hover { background-size: 101% 100%; } .allons-y:active { box-shadow: none; transform: translate(2px, 2px); transition: all 0.1s; } ================================================ FILE: themes/default/public/font/licenses/Apache License.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: themes/default/public/font/licenses/SIL Open Font License.txt ================================================ Copyright (c) 2012, Vernon Adams (vern@newtypography.co.uk), with Reserved Font Name Oswald This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: themes/default/public/fontello.json ================================================ { "name": "fontelico", "css_prefix_text": "icon-", "css_use_suffix": false, "hinting": true, "units_per_em": 1000, "ascent": 850, "glyphs": [ { "uid": "c9bef3dc67fea47e94c4a5030ea64dad", "css": "clipboard", "code": 59392, "src": "elusive" }, { "uid": "f48ae54adfb27d8ada53d0fd9e34ee10", "css": "trash", "code": 59396, "src": "fontawesome" } ] } ================================================ FILE: themes/default/public/manifest.json ================================================ { "name": "Let's Shorten That Url", "short_name": "lstu", "icons": [ { "src": "/img/lstu36.png", "sizes": "36x36", "type": "image/png" }, { "src": "/img/lstu48.png", "sizes": "48x48", "type": "image/png" }, { "src": "/img/lstu72.png", "sizes": "72x72", "type": "image/png" }, { "src": "/img/lstu96.png", "sizes": "96x96", "type": "image/png" }, { "src": "/img/lstu144.png", "sizes": "144x144", "type": "image/png" }, { "src": "/img/lstu192.png", "sizes": "192x192", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" } ================================================ FILE: themes/default/templates/api.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % title 'Lstu API'; % use Mojo::JSON qw(to_json);

Lstu API

% if ($self->config('disable_api')) {

%= l('Sorry, the API is disabled.') %= l('This page is informational only.')

% }
% if (defined $self->config('ldap') || defined $self->config('htpasswd')) {
POST <%= url_for('login')->to_abs %>
<%= l('Parameters:') %>
  • login <%= l('mandatory') %>
    <%= l('Your login') %>
  • password <%= l('mandatory') %>
    <%= l('Your password') %>
  • _format <%= l('optional') %>
    <%= l('If equal to "json", response will be in JSON format, HTML format otherwise') %>
<%= l('Response:') %>
  • <%= l('JSON: success') %>
    {"msg":"<%= l('You have been successfully logged in.')%>","success":true}
  • <%= l('JSON: failure') %>
    {"msg":"<%= l('Please, check your credentials: unable to authenticate.') %>","success":false}
  • <%= l('Response for HTML format') %>
    <%= l('You will be redirected to the Lstu classic interface where you will be able to shorten URLs') %>
GET <%= url_for('logout')->to_abs %>

<%= l('Should always be successful') %>

<%= l('Parameters:') %>
  • _format <%= l('optional') %>
    <%= l('If equal to "json", response will be in JSON format, HTML format otherwise') %>
<%= l('Response:') %>
  • <%= l('JSON: success') %>
    {"msg":"<%= l('You have been successfully logged out.')%>","success":true}
  • <%= l('Response for HTML format') %>
    <%= l('You will be redirected to Lstu successfully logged out interface') %>
% }
POST <%= url_for('add')->to_abs %>
% if (defined $self->config('ldap') || defined $self->config('htpasswd')) {

<%= l('You must be logged in to use it.') %>

% } <%= l('Parameters:') %>
  • lsturl <%= l('mandatory') %>
    <%= l('URL to shorten') %>
  • lsturl-custom <%= l('optional') %>
    <%= l('Custom shortened text')%>
  • _format <%= l('optional') %>
    <%= l('If equal to "json", response will be in JSON format, HTML format otherwise') %>
<%= l('Response:') %>
    % my $u = prefix().url_for('short', short => 'XzQBd6eFa'); % $u =~ s#//#/#g; % $u = to_json $u;
  • <%= l('JSON: success') %>
    {"url":"https:\/\/fiat-tux.fr","short":<%= $u %>,"success":true}
  • <%= l('JSON: failure') %>
    {"msg":"<%= l('Failure reason') %>","success":false}
  • <%= l('Response for HTML format') %>
    <%= l('You will be redirected to the Lstu classic interface with a message giving the shortened URL') %>
GET <%= url_for('short', short => l('shortened_url'))->to_abs %>
<%= l('Response:') %>
  • <%= l('You will be redirected to the targeted URL or to the Lstu interface with a message giving the failure reason') %>
GET <%= url_for('short', short => l('shortened_url'))->to_abs %>?_format=json
<%= l('Response:') %>
  • <%= l('JSON: success') %>
    {"url":"https:\/\/fiat-tux.fr","success":true}
  • <%= l('JSON: failure') %>
    {"msg":"<%= l('Failure reason') %>","success":false}
GET <%= url_for('stats')->to_abs %>
% if (defined $self->config('ldap') || defined $self->config('htpasswd')) {

<%= l('You must be logged in to use it.') %>

% }

<%= l('It will use Lstu\'s cookies to know which shortened URL it will return statistics for.') %>

% if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) {

<%= l('If you are logged in as admin ("adminpwd" or "hashed_adminpwd" setting), it will give you statistics for all URLs, sorted by the most visited first, paginated with pages containing %1 records.', config('page_offset')) %>

% } <%= l('Parameters:') %>
    % if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) {
  • page <%= l('optional') %>
    <%= l('If you are logged in as admin, you can provide a "page" parameter') %> % }
  • _format <%= l('optional') %>
    <%= l('If equal to "json", response will be in JSON format, HTML format otherwise') %>
<%= l('Response:') %>
    % $u = prefix().url_for('short', short => l('shortened_url')); % $u =~ s#//#/#g; % $u = to_json $u;
  • <%= l('Response for JSON format') %>
    [{"short":<%= $u %>,"url":"<%= l('URL') %>","counter":<%= l('integer, how many unique visits of the shortened URL') %>}]
  • <%= l('Response for HTML format') %>
    <%= l('A page with a table containing the same informations that the JSON response') %>
GET <%= url_for('stats/'.l('shortened_url'))->to_abs %>

<%= l('Get the details (visit counter, creation date, etc.) of a shortened URL.') %>

<%= l('Response:') %>
  • <%= l('JSON: success') %>
    {"url":"https:\/\/fiat-tux.fr","short":<%= $u %>,"success":true,"counter":<%= l('integer, how many unique visits of the shortened URL') %>,"created_at":1519296674,"short":"<%= l('URL') %>","timestamp":1519296680}
  • <%= l('JSON: failure') %>
    {"msg":"<%= l('Failure reason') %>","success":false}
% if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) {
POST <%= url_for('stats')->to_abs %>
<%= l('Parameters:') %>
  • adminpwd
  • <%= l('optional') %>
    <%= l('To do an admin login, set it to the password defined in the settings ("adminpwd" or "hashed_adminpwd")') %>
  • action
  • <%= l('optional') %>
    <%= l('To do an admin logout, set it to "logout".') %>
  • _format <%= l('optional') %>
    <%= l('If equal to "json", response will be in JSON format, HTML format otherwise') %>
<%= l('Response:') %>
  • <%= l('If "adminpwd" is defined:') %>
    • <%= l('JSON: success') %>
      {"msg":"<%= l('You have been successfully logged in.')%>","success":true}
    • <%= l('JSON: failure') %>
      {"msg":"<%= l('Bad password') %>","success":false}
    • <%= l('Response for HTML format') %>
      <%= l('You will have the statistics page with the admin stats if you\'re succesfully logged in, or your stats and a failure message otherwise') %>
  • <%= l('If "action" is defined to "logout":') %>
    <%= l('Should always be successful') %>

    • <%= l('JSON: success') %>
      {"msg":"<%= l('You have been successfully logged out.')%>","success":true}
    • <%= l('Response for HTML format') %>
      <%= l('You will be redirected to Lstu statistics page') %>
GET <%= url_for('delete', short => l('shortened_url'))->to_abs %>

<%= l('You must be logged in as admin ("adminpwd" or "hashed_adminpwd" setting) to use it.') %>

<%= l('Parameters:') %>
  • _format <%= l('optional') %>
    <%= l('If equal to "json", response will be in JSON format, HTML format otherwise') %>
<%= l('Response:') %>
  • <%= l('JSON: success') %>
    {"url":"https:\/\/fiat-tux.fr","short":<%= $u %>,"success":true}
  • <%= l('JSON: failure') %>
    {"msg":"<%= l('Failure reason') %>","success":false}
  • <%= l('Response for HTML format') %>
    <%= l('You will be redirected to Lstu statistics page with a message in case of failure') %>
% }
================================================ FILE: themes/default/templates/index.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % title 'Lstu';
%= csrf_field <%= submit_button l('Go!'), class => 'allons-y' %>
% if (defined(stash('short')) && defined(stash('url'))) {

QRCode

=>

% } % if (defined(flash('msg'))) {

<%= flash('msg') %>

% } % if (defined(stash('msg'))) {

<%= stash('msg') %>

% } ================================================ FILE: themes/default/templates/layouts/default.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); % $year += 1900; % my $lang = $self->languages; % $lang =~ s/-(.*)/_\U$1/; <%= title %> % if ($self->app->mode eq 'production') { %= stylesheet '/css/bootstrap-lstu.min.css' % } else { %= stylesheet '/css/bootstrap.min.css' %= stylesheet '/css/lstu.css' %= stylesheet '/css/fontelico.css' % }

Let's Shorten That URL

© 2013 — <%= $year %> <%= link_to 'Luc Didry' => 'https://fiat-tux.fr' %> — <%= l('License:') %> <%= link_to 'WTFPL' => 'http://www.wtfpl.net/' %> — <%= link_to l('About') => 'https://framagit.org/fiat-tux/hat-softwares/lstu/blob/master/README.md' %> — <%= link_to 'API' => url_for('api') %> — % if (defined(config('ldap')) || defined(config('htpasswd'))) { % if (is_user_authenticated()) { <%= link_to l('Statistics') => url_for('stats') %> — <%= link_to l('Logout') => url_for('/logout')%> % } else { <%= link_to l('Signin') => url_for('/login') %> % } % } else { <%= link_to l('Statistics') => url_for('stats') %> % }

<%= content %>
% if (defined(stash('short')) && defined(stash('url'))) { %= javascript url_for('lstu.js') % } ================================================ FILE: themes/default/templates/login.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % title 'Lstu login'; % if (defined stash('msg')) { <%= stash('msg') %> % }
================================================ FILE: themes/default/templates/logout.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % title 'Lstu logout';

<%= l('You have been successfully logged out.') %>

<%= l('Signin') %>

'); } document.body.removeChild(textArea); } var copyBtn = document.getElementById('clipboard'); copyBtn.addEventListener('click', function(event) { event.preventDefault(); copyTextToClipboard(this.getAttribute('data-short')); }); ================================================ FILE: themes/default/templates/stats.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % title 'Lstu stats'; % use Mojo::Date; % my $order = $self->param('order') // 'counter'; % my $dir = $self->param('dir') // '-desc'; % my $arrow = ($dir eq '-desc') ? '↓' : '↑'; % my $adir = sprintf('&dir=%s', ($dir eq '-desc') ? '-asc' : '-desc'); % $dir = sprintf('&dir=%s', $dir);

<%= l('Statistics') %>

% if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % if (!flash('banned')) {
% if (defined(stash('admin'))) { <%= submit_button l('Logout from admin stats'), class => 'btn btn-default btn-primary' %> % } else {
<%= submit_button l('Go!'), class => 'btn btn-default btn-primary' %> % }
% } % if (defined(flash('msg'))) {

<%= flash('msg') %>

% }
% } % if (defined(flash('success_msg'))) {

<%= flash('success_msg') %>

% }
<%= submit_button l('Import URLs'), class => 'btn btn-default btn-primary' %> <%= l('Export your URLs') %>

% if ((defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) && defined(stash('admin'))) { % } else { % } % if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % unless (defined(stash('admin'))) { % } else { % } % } % my $i = 1; % if (defined(stash('page')) && stash('page') >= 0) { % $i = 1 + config('page_offset') * stash('page'); % } % my $j = $i; % for my $url (@{$urls}) { % if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % unless (defined(stash('admin'))) { % } else { % } % } % }
# <%= l('URL') %><%= $arrow if ($order eq 'url') %> <%= l('Shortened URL') %><%= $arrow if ($order eq 'short') %> <%= l('Counter') %><%= $arrow if ($order eq 'counter') %> <%= l('Created') %><%= $arrow if ($order eq 'created_by') %> <%= l('URL') %> <%= l('Shortened URL') %> <%= l('Counter') %> <%= l('Created') %> <%= l('QRCode') %><%= l('Delete') %>
<%= $i++ %> <%= link_to $url->{url} => $url->{url} %> <%= link_to $prefix.$url->{short} => $prefix.$url->{short} %> <%= $url->{counter} %> <%= Mojo::Date->new($url->{timestamp})->to_string %>QRCode
% if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % if (defined(stash('admin'))) { % } % } %= link_to l('Home') => '/' ================================================ FILE: themes/milligram/Makefile ================================================ POT=lib/Lstu/I18N/milligram.pot SEDOPTS=-e "s@SOME DESCRIPTIVE TITLE@Lstu language file@" \ -e "s@YEAR THE PACKAGE'S COPYRIGHT HOLDER@2015 Luc Didry@" \ -e "s@CHARSET@utf8@" \ -e "s@the PACKAGE package@the Lstu package@" \ -e '/^\#\. (/{N;/\n\#\. (/{N;/\n.*\.\.\/default\//{s/\#\..*\n.*\#\./\#. (/g}}}' \ -e '/^\#\. (/{N;/\n.*\.\.\/default\//{s/\n/ /}}' SEDOPTS2=-e '/^\#.*\.\.\/default\//,+3d' XGETTEXT=carton exec ../../local/bin/xgettext.pl CARTON=carton exec locales: $(XGETTEXT) -D templates -D ../default/templates -o $(POT) 2>/dev/null sed $(SEDOPTS) -i $(POT) sed $(SEDOPTS2) -i $(POT) ================================================ FILE: themes/milligram/lib/Lstu/I18N/en.po ================================================ # Lstu language file. # Copyright (C) 2013 Luc Didry # This file is distributed under the same license as the Lstu package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Lstu\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Luc Didry \n" "Language-Team: English (http://www.transifex.com/projects/p/lstu/language/en/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en\n" ================================================ FILE: themes/milligram/lib/Lstu/I18N/fr.po ================================================ # Lstu language file. # Copyright (C) 2013 Luc Didry # This file is distributed under the same license as the Lstu package. # Luc Didry , 2015 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Lstu\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Luc \n" "Language-Team: French (http://www.transifex.com/projects/p/lstu/language/fr/)" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf8\n" "Content-Transfer-Encoding: 8bit\n" ================================================ FILE: themes/milligram/lib/Lstu/I18N/milligram.pot ================================================ # Lstu language file. # Copyright (C) 2015 Luc Didry # This file is distributed under the same license as the Lstu package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/index.html.ep:14 msgid "The shortened URL" msgstr "" ================================================ FILE: themes/milligram/lib/Lstu/I18N/oc.po ================================================ # Lstu language file. # Copyright (C) 2013 Luc Didry # This file is distributed under the same license as the Lstu package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf8\n" "Content-Transfer-Encoding: 8bit\n" ================================================ FILE: themes/milligram/lib/Lstu/I18N.pm ================================================ # vim:set sw=4 ts=4 sts=4 ft=perl expandtab: package Lstu::I18N; use base 'Locale::Maketext'; use File::Basename qw/dirname/; use Locale::Maketext::Lexicon { _auto => 1, _decode => 1, _style => 'gettext', '*' => [ Gettext => dirname(__FILE__) . '/I18N/*.po', Gettext => $app_dir . 'themes/default/lib/Lstu/I18N/*.po', ] }; use vars qw($app_dir); BEGIN { use Cwd; my $app_dir = getcwd; } 1; ================================================ FILE: themes/milligram/public/css/lstu.css ================================================ @font-face { font-family: 'Henny_Penny'; font-style: normal; font-weight: 400; src: local('Henny Penny'), local('HennyPenny-Regular'), url('../font/hennypenny.woff2') format('woff2'); } @font-face { font-family: 'Oswald'; font-style: bold; font-weight: 400; src: local('Oswald'), url('../font/Oswald-Bold.ttf') } @font-face { font-family: 'OpenSans'; font-style: normal; font-weight: 200; src: local('OpenSans'), url('../font/OpenSans-Regular.ttf') } body { padding-top: 40px; padding-bottom: 40px; } .container { padding: 15px; margin: 0 auto; } .oswald { font-family: 'Oswald'; font-size: 42px; } .hennypenny { font-family: 'Henny_Penny', cursive; font-size: 42px; } .link_nocol, .link_nocol:hover { color: #000; text-decoration: none; } .result { border-color: #d6e9c6; padding: 15px; margin-bottom: 20px; border: 1px solid; border-radius: 4px; } .result a.button { padding: 0; } ul.pager { list-style: none outside; text-align: center; } ul.pager li { display: inline; } .disabled { display: none !important; } @media (max-width: 767px) { .hidden-xs { display: none !important; } } .qrcode-result { float: right; } dd > strong { padding-left: 15px; } .allons-y { border-style: none; background: linear-gradient(to right, rgba(255, 255, 255, 0.3) 0%, rgba(255,255,255, 0.5) 100%, transparent 25px) no-repeat, #9B4DCA; background-repeat: no-repeat, no-repeat; border-radius: 5px; box-shadow: 2px 2px 1px 1px rgba(0,0,0,0.3); padding: 8px; margin-right: 9px; color: white; font-family: 'OpenSans'; font-size: 14px; background-size: 0 100%, 100% 100%; transition: background-size 0.3s; } .allons-y:hover { background-size: 101% 100%; background-color: #9B4DCA !important; } .allons-y:active { box-shadow: none; transform: translate(2px, 2px); transition: all 0.1s; } @media (max-width:767px) { .hidden-xs{ display:none !important } } @media (min-width:768px) and (max-width:991px) { .hidden-sm{ display:none !important } } @media (min-width:992px) and (max-width:1199px) { .hidden-md{ display:none !important } } @media (min-width:1200px) { .hidden-lg{display:none !important } } ================================================ FILE: themes/milligram/public/css/lstu.min.css ================================================ @font-face{font-family:Henny_Penny;font-style:normal;font-weight:400;src:local('Henny Penny'),local('HennyPenny-Regular'),url(../font/hennypenny.woff2) format('woff2')}body{padding-top:40px;padding-bottom:40px}.container{padding:15px;margin:0 auto}.hennypenny{font-family:Henny_Penny,cursive;font-size:42px}.link_nocol,.link_nocol:hover{color:#000;text-decoration:none}.result{border-color:#d6e9c6;padding:15px;margin-bottom:20px;border:1px solid;border-radius:4px}.result a.button{padding:0}ul.pager{list-style:none outside;text-align:center}ul.pager li{display:inline}.disabled{display:none!important}@media (max-width:767px){.hidden-xs{display:none!important}}.qrcode-result{float:right} ================================================ FILE: themes/milligram/public/css/milli-lstu.min.css ================================================ /*! * Milligram v1.2.0 * http://milligram.github.io * * Copyright (c) 2016 CJ Patoilo * Licensed under the MIT license */ html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto','Helvetica Neue','Helvetica','Arial',sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6;padding-top:40px;padding-bottom:40px}*,:after,:before{box-sizing:inherit}blockquote{border-left:.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote :last-child{margin-bottom:0}.button,button,input[type=button],input[type=reset],input[type=submit]{background-color:#9b4dca;border:.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type=button][disabled]:focus,input[type=button][disabled]:hover,input[type=reset][disabled]:focus,input[type=reset][disabled]:hover,input[type=submit][disabled]:focus,input[type=submit][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type=button].button-outline,input[type=reset].button-outline,input[type=submit].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type=button].button-outline:focus,input[type=button].button-outline:hover,input[type=reset].button-outline:focus,input[type=reset].button-outline:hover,input[type=submit].button-outline:focus,input[type=submit].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type=button].button-outline[disabled]:focus,input[type=button].button-outline[disabled]:hover,input[type=reset].button-outline[disabled]:focus,input[type=reset].button-outline[disabled]:hover,input[type=submit].button-outline[disabled]:focus,input[type=submit].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type=button].button-clear,input[type=reset].button-clear,input[type=submit].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type=button].button-clear:focus,input[type=button].button-clear:hover,input[type=reset].button-clear:focus,input[type=reset].button-clear:hover,input[type=submit].button-clear:focus,input[type=submit].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type=button].button-clear[disabled]:focus,input[type=button].button-clear[disabled]:hover,input[type=reset].button-clear[disabled]:focus,input[type=reset].button-clear[disabled]:hover,input[type=submit].button-clear[disabled]:focus,input[type=submit].button-clear[disabled]:hover{color:#9b4dca}code,pre{background:#f4f5f6}code{border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{border-left:.3rem solid #9b4dca}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:.1rem solid #f4f5f6;margin:3rem 0}input[type=email],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=url],select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1rem;width:100%}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=url]:focus,select:focus,textarea:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type=checkbox],input[type=radio],ul.pager li{display:inline}.label-inline{display:inline-block;font-weight:400;margin-left:.5rem}.container{max-width:112rem;padding:0 2rem;position:relative;width:100%}.row{display:flex;flex-direction:column;width:100%}.result a.button,.row,.row.row-no-padding,.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width:40rem){.row{flex-direction:row;margin-left:-1rem;width:calc(100% + 2rem)}.row .column{margin-bottom:inherit;padding:0 1rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl{list-style:none}dl,ol,ul{margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{width:100%}td,th{border-bottom:.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}p{margin-top:0}h1,h2,h3,h6{letter-spacing:-.1rem}h1,h2,h3,h4,h5,h6{font-weight:300;margin-bottom:2rem;margin-top:0}h1{font-size:4rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:3rem;line-height:1.3}h4{font-size:2.4rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}@media (min-width:40rem){h1{font-size:5rem}h2{font-size:4.2rem}h3{font-size:3.6rem}h4{font-size:3rem}h5{font-size:2.4rem}h6{font-size:1.5rem}}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}@font-face{font-family:'Henny_Penny';font-style:normal;font-weight:400;src:local('Henny Penny'),local('HennyPenny-Regular'),url(../font/hennypenny.woff2) format('woff2')}@font-face{font-family:'Oswald';font-style:bold;font-weight:400;src:local('Oswald'),url(../font/Oswald-Bold.ttf)}@font-face{font-family:'OpenSans';font-style:normal;font-weight:200;src:local('OpenSans'),url(../font/OpenSans-Regular.ttf)}.container{padding:15px;margin:0 auto}.hennypenny,.oswald{font-family:'Oswald';font-size:42px}.hennypenny{font-family:'Henny_Penny',cursive}.link_nocol,.link_nocol:hover{color:#000;text-decoration:none}.result{padding:15px;margin-bottom:20px;border:1px solid;border-radius:4px}ul.pager{list-style:none outside;text-align:center}.disabled{display:none!important}@media (max-width:767px){.hidden-xs{display:none!important}}.qrcode-result{float:right}dd>strong{padding-left:15px}.allons-y{border-style:none;background:linear-gradient(to right,rgba(255,255,255,.3) 0%,rgba(255,255,255,.5) 100%,transparent 25px) no-repeat,#9b4dca;background-repeat:no-repeat,no-repeat;border-radius:5px;box-shadow:2px 2px 1px 1px rgba(0,0,0,.3);padding:8px;margin-right:9px;color:#fff;font-family:'OpenSans';font-size:14px;background-size:0 100%,100% 100%;transition:background-size .3s}.allons-y:hover{background-size:101% 100%;background-color:#9b4dca!important}.allons-y:active{box-shadow:none;transform:translate(2px,2px);transition:all .1s}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}@font-face{font-family:'fontelico';src:url(../font/fontelico.eot?21941692);src:url(../font/fontelico.eot?21941692#iefix) format('embedded-opentype'),url(../font/fontelico.woff2?21941692) format('woff2'),url(../font/fontelico.woff?21941692) format('woff'),url(../font/fontelico.ttf?21941692) format('truetype'),url(../font/fontelico.svg?21941692#fontelico) format('svg');font-weight:400;font-style:normal}[class*=" icon-"]:before,[class^=icon-]:before{font-family:"fontelico";font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-clipboard:before{content:'\e800'}.icon-trash:before{content:'\e804'} ================================================ FILE: themes/milligram/public/css/milligram.min.css ================================================ /*! * Milligram v1.2.0 * http://milligram.github.io * * Copyright (c) 2016 CJ Patoilo * Licensed under the MIT license */ html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}*,*:after,*:before{box-sizing:inherit}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.0rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:3.0rem;line-height:1.3}h4{font-size:2.4rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}@media (min-width: 40rem){h1{font-size:5.0rem}h2{font-size:4.2rem}h3{font-size:3.6rem}h4{font-size:3.0rem}h5{font-size:2.4rem}h6{font-size:1.5rem}}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right} /*# sourceMappingURL=milligram.min.css.map */ ================================================ FILE: themes/milligram/public/css/milligram.min.css.map ================================================ {"version":3,"sources":["milligram.min.css"],"names":[],"mappings":"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","file":"milligram.min.css","sourcesContent":["html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}*,*:after,*:before{box-sizing:inherit}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.0rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:3.0rem;line-height:1.3}h4{font-size:2.4rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}@media (min-width: 40rem){h1{font-size:5.0rem}h2{font-size:4.2rem}h3{font-size:3.6rem}h4{font-size:3.0rem}h5{font-size:2.4rem}h6{font-size:1.5rem}}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}\n"]} ================================================ FILE: themes/milligram/templates/index.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % title 'Lstu';
<%= submit_button l('Go!'), class => 'button allons-y' %>
% if (defined(stash('short')) && defined(stash('url'))) {
QRCode
% } % if (defined(flash('msg'))) {

<%= flash('msg') %>

% } % if (defined(stash('msg'))) {

<%= stash('msg') %>

% } ================================================ FILE: themes/milligram/templates/layouts/default.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); % $year += 1900; % my $lang = $self->languages; % $lang =~ s/-(.*)/_\U$1/; <%= title %>

Let's Shorten That URL

© 2013 — <%= $year %> <%= link_to 'Luc Didry' => 'https://fiat-tux.fr' %> — <%= l('License:') %> <%= link_to 'WTFPL' => 'http://www.wtfpl.net/' %> — <%= link_to l('About') => 'https://framagit.org/fiat-tux/hat-softwares/lstu/blob/master/README.md' %> — <%= link_to 'API' => url_for('api') %> — % if (defined(config('ldap')) || defined(config('htpasswd'))) { % if (is_user_authenticated()) { <%= link_to l('Statistics') => url_for('stats') %> — <%= link_to l('Logout') => url_for('/logout')%> % } else { <%= link_to l('Signin') => url_for('/login') %> % } % } else { <%= link_to l('Statistics') => url_for('stats') %> % }

<%= content %>
% if ($self->app->mode eq 'production') { %= stylesheet '/css/milli-lstu.min.css' % } else { %= stylesheet '/css/milligram.min.css' %= stylesheet '/css/lstu.css' %= stylesheet '/css/fontelico.css' % } % if (defined(stash('short')) && defined(stash('url'))) { %= javascript url_for('lstu.js') % } ================================================ FILE: themes/milligram/templates/stats.html.ep ================================================ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: % title 'Lstu stats'; % use Mojo::Date;

<%= l('Statistics') %>

% if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % if (!flash('banned')) {
% if (defined(stash('admin'))) { <%= submit_button l('Logout from admin stats'), class => 'button' %> % } else { <%= submit_button l('Go!'), class => 'button' %> % }
% } % if (defined(flash('msg'))) {

<%= flash('msg') %>

% } % } % if (defined(flash('success_msg'))) {

<%= flash('success_msg') %>

% }
<%= submit_button l('Import URLs'), class => 'button' %> <%= l('Export your URLs') %>
% if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % unless (defined(stash('admin'))) { % } else { % } % } % my $i = 1; % if (defined(stash('page')) && stash('page') >= 0) { % $i = 1 + config('page_offset') * stash('page'); % } % my $j = $i; % for my $url (@{$urls}) { % if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % unless (defined(stash('admin'))) { % } else { % } % } % }
# <%= l('URL') %> <%= l('Shortened URL') %> <%= l('Counter') %> <%= l('Created') %><%= l('QRCode') %><%= l('Delete') %>
<%= $i++ %> <%= link_to $url->{url} => $url->{url} %> <%= link_to $prefix.$url->{short} => $prefix.$url->{short} %> <%= $url->{counter} %> <%= Mojo::Date->new($url->{timestamp})->to_string %>QRCode
% if (defined(config('adminpwd')) || defined(config('hashed_adminpwd'))) { % if (defined(stash('admin'))) { % } % } %= link_to l('Home') => '/' => (class => 'button') ================================================ FILE: utilities/bootstrap.json ================================================ { "vars": { "@gray-base": "#000", "@gray-darker": "lighten(@gray-base, 13.5%)", "@gray-dark": "lighten(@gray-base, 20%)", "@gray": "lighten(@gray-base, 33.5%)", "@gray-light": "lighten(@gray-base, 46.7%)", "@gray-lighter": "lighten(@gray-base, 93.5%)", "@brand-primary": "darken(#428bca, 6.5%)", "@brand-success": "#5cb85c", "@brand-info": "#5bc0de", "@brand-warning": "#f0ad4e", "@brand-danger": "#d9534f", "@body-bg": "#fff", "@text-color": "@gray-dark", "@link-color": "@brand-primary", "@link-hover-color": "darken(@link-color, 15%)", "@link-hover-decoration": "underline", "@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif", "@font-family-serif": "Georgia, \"Times New Roman\", Times, serif", "@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace", "@font-family-base": "@font-family-sans-serif", "@font-size-base": "14px", "@font-size-large": "ceil((@font-size-base * 1.25))", "@font-size-small": "ceil((@font-size-base * 0.85))", "@font-size-h1": "floor((@font-size-base * 2.6))", "@font-size-h2": "floor((@font-size-base * 2.15))", "@font-size-h3": "ceil((@font-size-base * 1.7))", "@font-size-h4": "ceil((@font-size-base * 1.25))", "@font-size-h5": "@font-size-base", "@font-size-h6": "ceil((@font-size-base * 0.85))", "@line-height-base": "1.428571429", "@line-height-computed": "floor((@font-size-base * @line-height-base))", "@headings-font-family": "inherit", "@headings-font-weight": "500", "@headings-line-height": "1.1", "@headings-color": "inherit", "@icon-font-path": "\"../fonts/\"", "@icon-font-name": "\"glyphicons-halflings-regular\"", "@icon-font-svg-id": "\"glyphicons_halflingsregular\"", "@padding-base-vertical": "6px", "@padding-base-horizontal": "12px", "@padding-large-vertical": "10px", "@padding-large-horizontal": "16px", "@padding-small-vertical": "5px", "@padding-small-horizontal": "10px", "@padding-xs-vertical": "1px", "@padding-xs-horizontal": "5px", "@line-height-large": "1.3333333", "@line-height-small": "1.5", "@border-radius-base": "4px", "@border-radius-large": "6px", "@border-radius-small": "3px", "@component-active-color": "#fff", "@component-active-bg": "@brand-primary", "@caret-width-base": "4px", "@caret-width-large": "5px", "@table-cell-padding": "8px", "@table-condensed-cell-padding": "5px", "@table-bg": "transparent", "@table-bg-accent": "#f9f9f9", "@table-bg-hover": "#f5f5f5", "@table-bg-active": "@table-bg-hover", "@table-border-color": "#ddd", "@btn-font-weight": "normal", "@btn-default-color": "#333", "@btn-default-bg": "#fff", "@btn-default-border": "#ccc", "@btn-primary-color": "#fff", "@btn-primary-bg": "@brand-primary", "@btn-primary-border": "darken(@btn-primary-bg, 5%)", "@btn-success-color": "#fff", "@btn-success-bg": "@brand-success", "@btn-success-border": "darken(@btn-success-bg, 5%)", "@btn-info-color": "#fff", "@btn-info-bg": "@brand-info", "@btn-info-border": "darken(@btn-info-bg, 5%)", "@btn-warning-color": "#fff", "@btn-warning-bg": "@brand-warning", "@btn-warning-border": "darken(@btn-warning-bg, 5%)", "@btn-danger-color": "#fff", "@btn-danger-bg": "@brand-danger", "@btn-danger-border": "darken(@btn-danger-bg, 5%)", "@btn-link-disabled-color": "@gray-light", "@input-bg": "#fff", "@input-bg-disabled": "@gray-lighter", "@input-color": "@gray", "@input-border": "#ccc", "@input-border-radius": "@border-radius-base", "@input-border-radius-large": "@border-radius-large", "@input-border-radius-small": "@border-radius-small", "@input-border-focus": "#66afe9", "@input-color-placeholder": "#999", "@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)", "@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)", "@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)", "@form-group-margin-bottom": "15px", "@legend-color": "@gray-dark", "@legend-border-color": "#e5e5e5", "@input-group-addon-bg": "@gray-lighter", "@input-group-addon-border-color": "@input-border", "@cursor-disabled": "not-allowed", "@dropdown-bg": "#fff", "@dropdown-border": "rgba(0,0,0,.15)", "@dropdown-fallback-border": "#ccc", "@dropdown-divider-bg": "#e5e5e5", "@dropdown-link-color": "@gray-dark", "@dropdown-link-hover-color": "darken(@gray-dark, 5%)", "@dropdown-link-hover-bg": "#f5f5f5", "@dropdown-link-active-color": "@component-active-color", "@dropdown-link-active-bg": "@component-active-bg", "@dropdown-link-disabled-color": "@gray-light", "@dropdown-header-color": "@gray-light", "@dropdown-caret-color": "#000", "@screen-xs": "480px", "@screen-xs-min": "@screen-xs", "@screen-phone": "@screen-xs-min", "@screen-sm": "768px", "@screen-sm-min": "@screen-sm", "@screen-tablet": "@screen-sm-min", "@screen-md": "992px", "@screen-md-min": "@screen-md", "@screen-desktop": "@screen-md-min", "@screen-lg": "1200px", "@screen-lg-min": "@screen-lg", "@screen-lg-desktop": "@screen-lg-min", "@screen-xs-max": "(@screen-sm-min - 1)", "@screen-sm-max": "(@screen-md-min - 1)", "@screen-md-max": "(@screen-lg-min - 1)", "@grid-columns": "12", "@grid-gutter-width": "30px", "@grid-float-breakpoint": "@screen-sm-min", "@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)", "@container-tablet": "(720px + @grid-gutter-width)", "@container-sm": "@container-tablet", "@container-desktop": "(940px + @grid-gutter-width)", "@container-md": "@container-desktop", "@container-large-desktop": "(1140px + @grid-gutter-width)", "@container-lg": "@container-large-desktop", "@navbar-height": "50px", "@navbar-margin-bottom": "@line-height-computed", "@navbar-border-radius": "@border-radius-base", "@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))", "@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)", "@navbar-collapse-max-height": "340px", "@navbar-default-color": "#777", "@navbar-default-bg": "#f8f8f8", "@navbar-default-border": "darken(@navbar-default-bg, 6.5%)", "@navbar-default-link-color": "#777", "@navbar-default-link-hover-color": "#333", "@navbar-default-link-hover-bg": "transparent", "@navbar-default-link-active-color": "#555", "@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)", "@navbar-default-link-disabled-color": "#ccc", "@navbar-default-link-disabled-bg": "transparent", "@navbar-default-brand-color": "@navbar-default-link-color", "@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)", "@navbar-default-brand-hover-bg": "transparent", "@navbar-default-toggle-hover-bg": "#ddd", "@navbar-default-toggle-icon-bar-bg": "#888", "@navbar-default-toggle-border-color": "#ddd", "@navbar-inverse-color": "lighten(@gray-light, 15%)", "@navbar-inverse-bg": "#222", "@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)", "@navbar-inverse-link-color": "lighten(@gray-light, 15%)", "@navbar-inverse-link-hover-color": "#fff", "@navbar-inverse-link-hover-bg": "transparent", "@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color", "@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)", "@navbar-inverse-link-disabled-color": "#444", "@navbar-inverse-link-disabled-bg": "transparent", "@navbar-inverse-brand-color": "@navbar-inverse-link-color", "@navbar-inverse-brand-hover-color": "#fff", "@navbar-inverse-brand-hover-bg": "transparent", "@navbar-inverse-toggle-hover-bg": "#333", "@navbar-inverse-toggle-icon-bar-bg": "#fff", "@navbar-inverse-toggle-border-color": "#333", "@nav-link-padding": "10px 15px", "@nav-link-hover-bg": "@gray-lighter", "@nav-disabled-link-color": "@gray-light", "@nav-disabled-link-hover-color": "@gray-light", "@nav-tabs-border-color": "#ddd", "@nav-tabs-link-hover-border-color": "@gray-lighter", "@nav-tabs-active-link-hover-bg": "@body-bg", "@nav-tabs-active-link-hover-color": "@gray", "@nav-tabs-active-link-hover-border-color": "#ddd", "@nav-tabs-justified-link-border-color": "#ddd", "@nav-tabs-justified-active-link-border-color": "@body-bg", "@nav-pills-border-radius": "@border-radius-base", "@nav-pills-active-link-hover-bg": "@component-active-bg", "@nav-pills-active-link-hover-color": "@component-active-color", "@pagination-color": "@link-color", "@pagination-bg": "#fff", "@pagination-border": "#ddd", "@pagination-hover-color": "@link-hover-color", "@pagination-hover-bg": "@gray-lighter", "@pagination-hover-border": "#ddd", "@pagination-active-color": "#fff", "@pagination-active-bg": "@brand-primary", "@pagination-active-border": "@brand-primary", "@pagination-disabled-color": "@gray-light", "@pagination-disabled-bg": "#fff", "@pagination-disabled-border": "#ddd", "@pager-bg": "@pagination-bg", "@pager-border": "@pagination-border", "@pager-border-radius": "15px", "@pager-hover-bg": "@pagination-hover-bg", "@pager-active-bg": "@pagination-active-bg", "@pager-active-color": "@pagination-active-color", "@pager-disabled-color": "@pagination-disabled-color", "@jumbotron-padding": "30px", "@jumbotron-color": "inherit", "@jumbotron-bg": "@gray-lighter", "@jumbotron-heading-color": "inherit", "@jumbotron-font-size": "ceil((@font-size-base * 1.5))", "@state-success-text": "#3c763d", "@state-success-bg": "#dff0d8", "@state-success-border": "darken(spin(@state-success-bg, -10), 5%)", "@state-info-text": "#31708f", "@state-info-bg": "#d9edf7", "@state-info-border": "darken(spin(@state-info-bg, -10), 7%)", "@state-warning-text": "#8a6d3b", "@state-warning-bg": "#fcf8e3", "@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)", "@state-danger-text": "#a94442", "@state-danger-bg": "#f2dede", "@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)", "@tooltip-max-width": "200px", "@tooltip-color": "#fff", "@tooltip-bg": "#000", "@tooltip-opacity": ".9", "@tooltip-arrow-width": "5px", "@tooltip-arrow-color": "@tooltip-bg", "@popover-bg": "#fff", "@popover-max-width": "276px", "@popover-border-color": "rgba(0,0,0,.2)", "@popover-fallback-border-color": "#ccc", "@popover-title-bg": "darken(@popover-bg, 3%)", "@popover-arrow-width": "10px", "@popover-arrow-color": "@popover-bg", "@popover-arrow-outer-width": "(@popover-arrow-width + 1)", "@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)", "@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)", "@label-default-bg": "@gray-light", "@label-primary-bg": "@brand-primary", "@label-success-bg": "@brand-success", "@label-info-bg": "@brand-info", "@label-warning-bg": "@brand-warning", "@label-danger-bg": "@brand-danger", "@label-color": "#fff", "@label-link-hover-color": "#fff", "@modal-inner-padding": "15px", "@modal-title-padding": "15px", "@modal-title-line-height": "@line-height-base", "@modal-content-bg": "#fff", "@modal-content-border-color": "rgba(0,0,0,.2)", "@modal-content-fallback-border-color": "#999", "@modal-backdrop-bg": "#000", "@modal-backdrop-opacity": ".5", "@modal-header-border-color": "#e5e5e5", "@modal-footer-border-color": "@modal-header-border-color", "@modal-lg": "900px", "@modal-md": "600px", "@modal-sm": "300px", "@alert-padding": "15px", "@alert-border-radius": "@border-radius-base", "@alert-link-font-weight": "bold", "@alert-success-bg": "@state-success-bg", "@alert-success-text": "@state-success-text", "@alert-success-border": "@state-success-border", "@alert-info-bg": "@state-info-bg", "@alert-info-text": "@state-info-text", "@alert-info-border": "@state-info-border", "@alert-warning-bg": "@state-warning-bg", "@alert-warning-text": "@state-warning-text", "@alert-warning-border": "@state-warning-border", "@alert-danger-bg": "@state-danger-bg", "@alert-danger-text": "@state-danger-text", "@alert-danger-border": "@state-danger-border", "@progress-bg": "#f5f5f5", "@progress-bar-color": "#fff", "@progress-border-radius": "@border-radius-base", "@progress-bar-bg": "@brand-primary", "@progress-bar-success-bg": "@brand-success", "@progress-bar-warning-bg": "@brand-warning", "@progress-bar-danger-bg": "@brand-danger", "@progress-bar-info-bg": "@brand-info", "@list-group-bg": "#fff", "@list-group-border": "#ddd", "@list-group-border-radius": "@border-radius-base", "@list-group-hover-bg": "#f5f5f5", "@list-group-active-color": "@component-active-color", "@list-group-active-bg": "@component-active-bg", "@list-group-active-border": "@list-group-active-bg", "@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)", "@list-group-disabled-color": "@gray-light", "@list-group-disabled-bg": "@gray-lighter", "@list-group-disabled-text-color": "@list-group-disabled-color", "@list-group-link-color": "#555", "@list-group-link-hover-color": "@list-group-link-color", "@list-group-link-heading-color": "#333", "@panel-bg": "#fff", "@panel-body-padding": "15px", "@panel-heading-padding": "10px 15px", "@panel-footer-padding": "@panel-heading-padding", "@panel-border-radius": "@border-radius-base", "@panel-inner-border": "#ddd", "@panel-footer-bg": "#f5f5f5", "@panel-default-text": "@gray-dark", "@panel-default-border": "#ddd", "@panel-default-heading-bg": "#f5f5f5", "@panel-primary-text": "#fff", "@panel-primary-border": "@brand-primary", "@panel-primary-heading-bg": "@brand-primary", "@panel-success-text": "@state-success-text", "@panel-success-border": "@state-success-border", "@panel-success-heading-bg": "@state-success-bg", "@panel-info-text": "@state-info-text", "@panel-info-border": "@state-info-border", "@panel-info-heading-bg": "@state-info-bg", "@panel-warning-text": "@state-warning-text", "@panel-warning-border": "@state-warning-border", "@panel-warning-heading-bg": "@state-warning-bg", "@panel-danger-text": "@state-danger-text", "@panel-danger-border": "@state-danger-border", "@panel-danger-heading-bg": "@state-danger-bg", "@thumbnail-padding": "4px", "@thumbnail-bg": "@body-bg", "@thumbnail-border": "#ddd", "@thumbnail-border-radius": "@border-radius-base", "@thumbnail-caption-color": "@text-color", "@thumbnail-caption-padding": "9px", "@well-bg": "#f5f5f5", "@well-border": "darken(@well-bg, 7%)", "@badge-color": "#fff", "@badge-link-hover-color": "#fff", "@badge-bg": "@gray-light", "@badge-active-color": "@link-color", "@badge-active-bg": "#fff", "@badge-font-weight": "bold", "@badge-line-height": "1", "@badge-border-radius": "10px", "@breadcrumb-padding-vertical": "8px", "@breadcrumb-padding-horizontal": "15px", "@breadcrumb-bg": "#f5f5f5", "@breadcrumb-color": "#ccc", "@breadcrumb-active-color": "@gray-light", "@breadcrumb-separator": "\"/\"", "@carousel-text-shadow": "0 1px 2px rgba(0,0,0,.6)", "@carousel-control-color": "#fff", "@carousel-control-width": "15%", "@carousel-control-opacity": ".5", "@carousel-control-font-size": "20px", "@carousel-indicator-active-bg": "#fff", "@carousel-indicator-border-color": "#fff", "@carousel-caption-color": "#fff", "@close-font-weight": "bold", "@close-color": "#000", "@close-text-shadow": "0 1px 0 #fff", "@code-color": "#c7254e", "@code-bg": "#f9f2f4", "@kbd-color": "#fff", "@kbd-bg": "#333", "@pre-bg": "#f5f5f5", "@pre-color": "@gray-dark", "@pre-border-color": "#ccc", "@pre-scrollable-max-height": "340px", "@component-offset-horizontal": "180px", "@text-muted": "@gray-light", "@abbr-border-color": "@gray-light", "@headings-small-color": "@gray-light", "@blockquote-small-color": "@gray-light", "@blockquote-font-size": "(@font-size-base * 1.25)", "@blockquote-border-color": "@gray-lighter", "@page-header-border-color": "@gray-lighter", "@dl-horizontal-offset": "@component-offset-horizontal", "@hr-border": "@gray-lighter" }, "css": [ "print.less", "type.less", "grid.less", "tables.less", "forms.less", "buttons.less", "responsive-utilities.less", "pagination.less", "pager.less", "alerts.less" ], "js": [], "customizerUrl": "http://getbootstrap.com/customize/?id=8d56756b325f9f22f4eb" } ================================================ FILE: utilities/lstu-minion@.service ================================================ [Unit] Description=Shortened URLs service job queue Documentation=https://framagit.org/fiat-tux/hat-softwares/lstu [Service] Type=simple User=www-data WorkingDirectory=/var/www/lstu/ ExecStart=/usr/local/bin/carton exec script/application minion worker [Install] WantedBy=multi-user.target ================================================ FILE: utilities/lstu.apache ================================================ ServerName http://lstu.example.org ServerAdmin contact@lstu.example.org ProxyRequests Off ProxyPreserveHost On ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://lstu.example.org/ LogLevel info ErrorLog ${APACHE_LOG_DIR}/lstu.error.log CustomLog ${APACHE_LOG_DIR}/lstu.access.log combined # Local reverse proxy authorization override # Most unix distribution deny proxy by default ; # If apache 2.4: Require all granted # If apache 2.2: #Order deny,allow #Allow from all ================================================ FILE: utilities/lstu.default ================================================ # LDIR is the path where you installed Lstu # It has to end with a final / LDIR=/var/www/lstu/ # USER is the user who will launch Lstu USER=www-data ================================================ FILE: utilities/lstu.init ================================================ #!/bin/sh # vim: set ts=4 sw=4 sts=4 tw=0: # vim: set expandtab: ### BEGIN INIT INFO # Provides: lstu # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts lstu with hypnotoad # Description: starts lstu with hypnotoad ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=script/lstu NAME=lstu DESC=lstu if [ -f "/etc/default/lstu" ] then . /etc/default/lstu if [ -z $LDIR ] then echo "LDIR variable is empty, please fill it in /etc/default/lstu" exit 0 fi if [ -z $USER ] then echo "USER variable is empty, please fill it in /etc/default/lstu" exit 0 fi else echo "Missing /etc/default/lstu file" exit 0 fi if [ ! -f "$LDIR$DAEMON" ] then echo "Missing $LDIR$DAEMON file" exit 0 fi set -e . /lib/lsb/init-functions do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started cd $LDIR su $USER -c "carton exec hypnotoad $DAEMON >/dev/null 2>&1" return "$?" } do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred cd $LDIR su $USER -c "carton exec hypnotoad -s $DAEMON >/dev/null 2>&1" return "$?" } do_status() { cd $LDIR if [ -f "script/hypnotoad.pid" ] then pgrep -lf $DAEMON >/dev/null 2>&1 if [ "$?" = "0" ]; then log_progress_msg "$NAME is running" else log_failure_msg "$NAME is NOT running but PID file exists" fi else log_failure_msg "$NAME is NOT running" fi } case "$1" in start) log_daemon_msg "Starting $NAME" cd $LDIR if [ -f "script/hypnotoad.pid" ] then pgrep -lf $DAEMON >/dev/null 2>&1 if [ "$?" = "0" ] then log_progress_msg "$NAME is already running. Unable to start." log_end_msg 1; else do_start case "$?" in 0|1) log_progress_msg "done" log_end_msg 0 ;; 2) log_failure_msg "failed" log_end_msg 1 ;; esac fi else do_start case "$?" in 0|1) log_progress_msg "done" log_end_msg 0 ;; 2) log_failure_msg "failed" log_end_msg 1 ;; esac fi ;; stop) log_daemon_msg "Stopping $NAME" cd $LDIR if [ -f "script/hypnotoad.pid" ] then pgrep -lf $DAEMON >/dev/null 2>&1 if [ "$?" = "0" ]; then do_stop case "$?" in 0|1) log_progress_msg "done" log_end_msg 0 ;; *) log_failure_msg "failed" log_end_msg 1 ;; esac else log_failure_msg "$NAME is NOT running. Unable to stop" log_end_msg 1 fi else log_failure_msg "$NAME is NOT running. Unable to stop" log_end_msg 1 fi ;; status) log_daemon_msg "Checking $NAME status" do_status log_end_msg 0 ;; reload) log_daemon_msg "Reloading $NAME" do_start sleep 1 case "$?" in 0|1) log_progress_msg "done" log_end_msg 0 ;; 2) log_failure_msg "failed" log_end_msg 1 ;; esac ;; restart) log_daemon_msg "Restarting $NAME" do_stop sleep 6 do_start case "$?" in 0|1) log_progress_msg "done" log_end_msg 0 ;; 2) log_failure_msg "failed"; log_end_msg 1 ;; esac ;; *) echo "Usage: $0 {start|stop|status|reload|restart}" >&2 exit 3 ;; esac exit 0 ================================================ FILE: utilities/lstu.nginx ================================================ upstream lstu { server 127.0.0.1:8442; } server { listen 80; listen [::]:80; server_name lstu.example.org; access_log /var/log/nginx/lstu.access.log; error_log /var/log/nginx/lstu.error.log; location / { proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Remote-Port $remote_port; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://lstu; proxy_redirect http://lstu https://lstu.example.org; } } ================================================ FILE: utilities/lstu.service ================================================ [Unit] Description=Shortened URLs service Documentation=https://framagit.org/fiat-tux/hat-softwares/lstu Requires=network.target After=network.target [Service] Type=forking User=www-data RemainAfterExit=yes WorkingDirectory=/var/www/lstu/ PIDFile=/var/www/lstu/script/hypnotoad.pid ExecStart=/usr/local/bin/carton exec hypnotoad script/lstu ExecStop=/usr/local/bin/carton exec hypnotoad -s script/lstu ExecReload=/usr/local/bin/carton exec hypnotoad script/lstu [Install] WantedBy=multi-user.target ================================================ FILE: utilities/lstu_upstart.conf ================================================ # -*- upstart -*- description "lstu" author "Luc Didry " start on (networking) stop on runlevel [!2345] script if [ -f "/etc/default/lstu" ] then . /etc/default/lstu if [ -z $LDIR ] then echo "LDIR variable is empty, please fill it in /etc/default/lstu" exit 0 fi if [ -z $USER ] then echo "USER variable is empty, please fill it in /etc/default/lstu" exit 0 fi else echo "Missing /etc/default/lstu file" exit 0 fi cd $LDIR su $USER -c "carton exec hypnotoad -f script/lstu" end script ================================================ FILE: utilities/migrations/mysql.sql ================================================ -- 1 up CREATE TABLE IF NOT EXISTS lstu ( short varchar(255) PRIMARY KEY, url text, counter integer default 0, timestamp integer ); CREATE TABLE IF NOT EXISTS sessions ( token varchar(255) PRIMARY KEY, until integer ); CREATE TABLE IF NOT EXISTS ban ( ip varbinary(16) PRIMARY KEY, until integer, strike integer ); -- 1 down DROP TABLE ban; DROP TABLE sessions; DROP TABLE lstu; -- 2 up ALTER TABLE lstu ADD created_by text; -- 2 down ALTER TABLE lstu DROP COLUMN created_by; -- 3 up ALTER TABLE lstu ADD disabled integer DEFAULT 0; -- 3 down ALTER TABLE lstu DROP COLUMN disabled; -- 4 up ALTER TABLE ban MODIFY ip varbinary(39); -- 4 down ALTER TABLE ban MODIFY ip varbinary(16); ================================================ FILE: utilities/migrations/postgresql.sql ================================================ -- 1 up CREATE TABLE IF NOT EXISTS lstu ( short text PRIMARY KEY, url text, counter integer default 0, timestamp integer ); CREATE TABLE IF NOT EXISTS sessions ( token text PRIMARY KEY, until integer ); CREATE TABLE IF NOT EXISTS ban ( ip text PRIMARY KEY, until integer, strike integer ); -- 1 down DROP TABLE ban; DROP TABLE sessions; DROP TABLE lstu; -- 2 up CREATE INDEX IF NOT EXISTS empty_short_idx ON lstu (short) WHERE url IS NULL; -- 2 down DROP INDEX empty_short_idx; -- 3 up ALTER TABLE lstu ADD COLUMN created_by text; -- 3 down ALTER TABLE lstu DROP COLUMN created_by; -- 4 up ALTER TABLE lstu ADD disabled integer default 0; -- 4 down ALTER TABLE lstu DROP COLUMN disabled; ================================================ FILE: utilities/migrations/sqlite.sql ================================================ -- 1 up CREATE TABLE IF NOT EXISTS lstu ( short TEXT PRIMARY KEY, url TEXT, counter INTEGER, timestamp INTEGER ); CREATE TABLE IF NOT EXISTS sessions ( token TEXT PRIMARY KEY, until INTEGER ); CREATE TABLE IF NOT EXISTS ban ( ip TEXT PRIMARY KEY, until INTEGER, strike INTEGER ); CREATE INDEX IF NOT EXISTS empty_short_idx ON lstu (short) WHERE url IS NULL; -- 1 down DROP TABLE lstu; DROP TABLE sessions; DROP TABLE ban; DROP INDEX empty_short_idx; -- 2 up ALTER TABLE lstu ADD COLUMN created_by TEXT; -- 2 down BEGIN TRANSACTION; CREATE TABLE lstu_backup( short TEXT PRIMARY KEY, url TEXT, counter INTEGER, timestamp INTEGER ); INSERT INTO lstu_backup SELECT short, url, counter, timestamp FROM lstu; DROP TABLE lstu; ALTER TABLE lstu_backup RENAME TO lstu; COMMIT; -- 3 up ALTER TABLE lstu ADD COLUMN disabled INTEGER default 0; -- 3 down BEGIN TRANSACTION; CREATE TABLE lstu_backup( short TEXT PRIMARY KEY, url TEXT, counter INTEGER, timestamp INTEGER, created_by TEXT ); INSERT INTO lstu_backup SELECT short, url, counter, timestamp, created_by FROM lstu; DROP TABLE lstu; ALTER TABLE lstu_backup RENAME TO lstu; COMMIT; ================================================ FILE: utilities/read_conf.pl ================================================ #!/usr/bin/env perl # read_conf.pl - Outputs lstu's config value for given keypath use strict; use warnings; use FindBin; use Cwd qw(realpath); use Config::FromHash; sub print_help { print <new(filename => $filename); my $value = $config->get($ARGV[0]); if (defined $value) { print $value; exit 0; } if (not defined $value and defined $ARGV[1]) { print $ARGV[1]; exit 0; } print STDERR "Error: keypath \"$ARGV[0]\" not found in config, and no default value provided.\n"; exit 1;