Compare commits
376 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2e96cc1fa | |||
| c1af55a9de | |||
| 87640ed8da | |||
| ad4e35f452 | |||
| ae79a59639 | |||
| 354c9788c8 | |||
| 67573db0e0 | |||
| 22013f2267 | |||
| 8370b2ba98 | |||
| 144d2475cb | |||
| 96ae3ad7fb | |||
| a7fc1df893 | |||
| 5b929bf0ca | |||
| 0b326e71e6 | |||
| c6d86368c5 | |||
| 1d59aff5a6 | |||
| 50b5c4d09e | |||
| 42a0d7e610 | |||
| c5b9a613a8 | |||
| 362ca05ab8 | |||
| a7cabf0d63 | |||
| f25b3c0b76 | |||
| 543070a94f | |||
| 786a110b48 | |||
| 348fa4607d | |||
| f90d1e0667 | |||
| e5216c6c5f | |||
| 6ed24ef0d2 | |||
| 829ee33c32 | |||
| 44a3e406a8 | |||
| cdbbe92e89 | |||
| d62ada6c53 | |||
| 342712e72f | |||
| 59c17419a9 | |||
| ac978f9555 | |||
| 4f97663619 | |||
| 7723a55306 | |||
| 45c8131c84 | |||
| 147bd635fc | |||
| a0e78d7fa6 | |||
| 7e32c3ad6f | |||
| 6e6a5ce191 | |||
| eb3f749ba1 | |||
| f005cb7b17 | |||
| dbec9f22b2 | |||
| ab7c1a4c1c | |||
| e37c4ad69b | |||
| cbcdb22670 | |||
| babf14a8d2 | |||
| bae8079268 | |||
| cf5405478f | |||
| 31029a8cac | |||
| 7821dd0142 | |||
| 12023e23e0 | |||
| c9853f6217 | |||
| 5a6f1d14c2 | |||
| 4d62d2083a | |||
| 6364067bac | |||
| 542beb0c67 | |||
| 8a9dc51b73 | |||
| 12d827ae67 | |||
| b8ae49178c | |||
| 461c481607 | |||
| 24d1d29944 | |||
| a51e2557c9 | |||
| 8f0887ac8d | |||
| 8ba66866e2 | |||
| 358ad425b8 | |||
| 5ffc647e64 | |||
| ef572aa7f8 | |||
| 6c54afad92 | |||
| 8df5414e6e | |||
| ae550148d4 | |||
| ff30bc43a3 | |||
| bdcaf06d8d | |||
| 9f5e19bfcf | |||
| 318fd04ff7 | |||
| f723404f96 | |||
| 6114af4cc6 | |||
| 9b5fe093b0 | |||
| c336fc1f97 | |||
| 38b0667d2b | |||
| 9c9f8317b2 | |||
| 9ad65bbae5 | |||
| ffe73e0e5a | |||
| f258bbc81f | |||
| 72286377df | |||
| dee6227989 | |||
| 29bf38aab2 | |||
| b6004b3a31 | |||
| a53ff1b03c | |||
| 95aa91f33b | |||
| 583f27f95d | |||
| 4df9ce571a | |||
| c9f7ce7b1a | |||
| 41f3d384c5 | |||
| d25965a116 | |||
| 45603d8794 | |||
| 3aeb363485 | |||
| 02d566809c | |||
| c7386c4ef2 | |||
| 31e86e0af9 | |||
| 8e38572d31 | |||
| fcb6b286f3 | |||
| 14e3c538ff | |||
| 83b02c7eb6 | |||
| 16448dab95 | |||
| 0e21a8adb6 | |||
| 45cafda543 | |||
| b774c997da | |||
| 19b99919d2 | |||
| c5f556a901 | |||
| 95aa3009dc | |||
| d03e0b2987 | |||
| 998025b55c | |||
| 42fa5a1c6d | |||
|
|
b20a7ca05c | ||
| 6e085b82cb | |||
| e26760bdc4 | |||
| 508c193c50 | |||
| da393c74f0 | |||
| 88b8391673 | |||
| 1e071fd0cc | |||
| 63bfe34e35 | |||
| 6f632e9d4f | |||
| 81c442d5b0 | |||
| 893b8f98a8 | |||
| 7db39d5c5b | |||
| d74547ce41 | |||
| e44539454f | |||
| 49ee8ace0d | |||
| 6960102a81 | |||
| dafab7f85f | |||
| 23923fd694 | |||
| f20e5d36a6 | |||
| d4bb902410 | |||
| aec7a9195b | |||
| 56aff15a70 | |||
| 264ac59cbe | |||
| f2fa11cfca | |||
| dbd0071bc9 | |||
| 3bd9f96c6c | |||
| 3cf01e6bf5 | |||
| 8abb8c8efd | |||
| 91921c809f | |||
| 6eeb0e12b6 | |||
| eb58b19791 | |||
| a558d8c78f | |||
| 118fcf23b6 | |||
| c7d2e99de3 | |||
| 1e2cb8a3e3 | |||
| b1869dc000 | |||
| 806d2b6cfd | |||
| c7822b9fc2 | |||
|
|
6467b38d04 | ||
| 187cbba693 | |||
| 09384070f9 | |||
| c0cf251e97 | |||
| 7e17ff6f78 | |||
| b2e5fbbe9d | |||
| b9e557e6b8 | |||
| 4396dd2b44 | |||
| 9ffc935ded | |||
| 5bd5e0a5e4 | |||
| 763594b3ec | |||
| cd35c1c26d | |||
| 6a8f1f2813 | |||
| 7bf5dafec5 | |||
|
|
2fbf09911f | ||
| 5ab37a5227 | |||
|
|
7bec2586fb | ||
| 44037fb22d | |||
|
|
fa8c56144a | ||
| dc551a972f | |||
|
|
4070dcf19f | ||
| 8bd1dad245 | |||
|
|
dd14305803 | ||
| 5fee872830 | |||
| 4593e66e55 | |||
| 38d18dc8c2 | |||
| 48adee4823 | |||
|
|
68e2197d24 | ||
|
|
d3630b919b | ||
|
|
78d151f5ea | ||
|
|
4291709620 | ||
| 0edd1e2bff | |||
|
|
9c3c69fd07 | ||
| 786208c09e | |||
| e11eafa795 | |||
|
|
8fbb19a349 | ||
|
|
9f8737ed3d | ||
| 1db17b29ae | |||
| c38ed1e1b4 | |||
| 2147f68168 | |||
| a7d60cd0e7 | |||
| 772d06b3ef | |||
| dcf1821398 | |||
| 726dd77649 | |||
| a0ec7b148b | |||
| eb62053bf4 | |||
| 2dda30ed75 | |||
| 3d85327afe | |||
| 6256e83f9e | |||
|
|
3134a0053f | ||
| ba2e3199cb | |||
|
|
65d3631194 | ||
| d344903d40 | |||
|
|
1619fc5675 | ||
| e33f92b07a | |||
| b92c0f9c50 | |||
| 4529c9c372 | |||
|
|
27d1ee65d9 | ||
|
|
748825e40c | ||
|
|
29cee882c0 | ||
|
|
934735e15d | ||
| 3f554e1266 | |||
|
|
ff1d283448 | ||
| fe83d13b48 | |||
| 32c56b5c9d | |||
| 5f45255c24 | |||
| 1a5d2d8a13 | |||
| 69e46539d0 | |||
| 5fc1852035 | |||
| d2501c2d3a | |||
| 8c5cbde7b5 | |||
| fd517885f8 | |||
| b196d285cd | |||
| c4f7a7991a | |||
| 816c7151ab | |||
| 18c80b047d | |||
| fab5fa2e14 | |||
| b8a87576c0 | |||
| 9e371e7012 | |||
| 70d0b51c92 | |||
| e9d1014e3f | |||
| 413e5ca70d | |||
| 7d91d5bcf7 | |||
| 8a2d0a3209 | |||
| a9e5c204fe | |||
| e59635f4ae | |||
| f9c8214348 | |||
| 3e29317264 | |||
| 6a12114927 | |||
| c437076c49 | |||
| ffa3f65f10 | |||
| 31a1cb0a86 | |||
| 50ff264517 | |||
| 416898f83b | |||
| f42812d771 | |||
| f25200f5fd | |||
| a55999a14c | |||
| c9f25316a2 | |||
| ab10032de9 | |||
| 1356b7cb67 | |||
| e3dfbc704e | |||
| c7cf535652 | |||
| a5424825b8 | |||
|
|
9ab30b47e8 | ||
|
|
04550673f9 | ||
|
|
7852d26b6c | ||
|
|
40e1d2d6fa | ||
| af1f560c66 | |||
| 6ec7fccac9 | |||
| 288e21a710 | |||
|
|
b063643e2f | ||
| 81a5bac780 | |||
| 1f5094c165 | |||
| f4de3114b8 | |||
| c9f7043a90 | |||
| b7e7645eef | |||
|
|
0dc29460e4 | ||
| 5f4dc8b314 | |||
| dbe96d4cdd | |||
| cf4486e397 | |||
| 180d271c66 | |||
| 71c3eec62d | |||
|
|
99c9f00db4 | ||
| e27958fd2f | |||
| 174864b3aa | |||
|
|
adf2d4002d | ||
|
|
559d95cf16 | ||
| 9c737b5552 | |||
| e82eda3252 | |||
| 229209c3a3 | |||
| 1d445b72f0 | |||
| 2ec999c49d | |||
| 8f8a5fa341 | |||
| dbec5531e8 | |||
| ce68275e4e | |||
| 96ca0b6690 | |||
| e437bec53e | |||
| bddbfe6547 | |||
| 42799ec995 | |||
| 7fef2bfaa8 | |||
| 32e1a595eb | |||
| 0bf402827f | |||
| 750f174508 | |||
| 675d0c4182 | |||
|
|
dc80cf2ccd | ||
|
|
745e594941 | ||
|
|
0115e0382b | ||
|
|
5ba9989c93 | ||
|
|
233e7f1023 | ||
|
|
f73400884f | ||
| d0d0ba7ddb | |||
|
|
d4495fa735 | ||
|
|
00be74d8dd | ||
| 6e40b1fdec | |||
| 628caae82c | |||
| 4276bd7862 | |||
| 099f9e7fa6 | |||
| 178441ab87 | |||
| 8c4bf49270 | |||
| bf5c7e257d | |||
| 631258b5f6 | |||
| c6488102c1 | |||
| ddf7c80b3a | |||
| fd84b89f12 | |||
| ae74311d11 | |||
|
|
f6105b7476 | ||
|
|
75f8dfe60d | ||
|
|
b67f421edc | ||
|
|
9c39d75cc7 | ||
| 0ecc3c8c8d | |||
| ad0f7e0c65 | |||
| f87eac943e | |||
|
|
d1e605cadb | ||
| d8f52a063f | |||
|
|
5154650c25 | ||
| 394fbe9c66 | |||
| 3ec835cb73 | |||
|
|
281232f0a9 | ||
| 00cc3f780c | |||
| 900fd2635c | |||
| 8edd8d1dcd | |||
| e7750a2281 | |||
| 735f580b32 | |||
| 287df1e4ec | |||
| 1144db0434 | |||
| 25d309fe81 | |||
| 29d25a08ad | |||
| 60c9f486d0 | |||
| 4835eb8884 | |||
| a24e9681ec | |||
|
|
c96fdb8c49 | ||
| 8918870bde | |||
|
|
196bb0f435 | ||
| 9d53108f4f | |||
| 5066a6d21d | |||
| 9e0b760237 | |||
| 48793c4190 | |||
|
|
6dada559f1 | ||
|
|
2b006cc113 | ||
|
|
4772796817 | ||
| 82c4072160 | |||
| 468b046104 | |||
|
|
e317b74aba | ||
| 8b993edc28 | |||
| 94f727a993 | |||
| 8656b9be92 | |||
| 23a510d9d3 | |||
| a4a1fc757a | |||
|
|
f2463752e5 | ||
|
|
2e906a8d94 | ||
|
|
6c641f1125 | ||
|
|
f59cedcea0 | ||
|
|
cdab84b54b | ||
|
|
c91a765a31 | ||
| 8050d7ed6c | |||
| 0ec3464962 | |||
| 1e78e4da64 | |||
| 34f61ea7d9 | |||
| 6f6b15f6a1 | |||
| 956dcec279 | |||
|
|
154aa4235b | ||
| 97b2d790c7 |
77 changed files with 9456 additions and 3361 deletions
|
|
@ -10,8 +10,8 @@ end_of_line = lf
|
|||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{java,skin}]
|
||||
indent_size = 3
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{java}]
|
||||
indent_size = 3
|
||||
|
|
|
|||
48
.github/workflows/deploy.yml
vendored
Normal file
48
.github/workflows/deploy.yml
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
name: Deploy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
hostname:
|
||||
description: Hostname
|
||||
type: string
|
||||
required: true
|
||||
default: antville.org
|
||||
|
||||
jobs:
|
||||
stage:
|
||||
runs-on: antville
|
||||
|
||||
environment:
|
||||
name: production
|
||||
url: ${{ inputs.hostname }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew :build
|
||||
|
||||
- name: Copy files to server
|
||||
run: |
|
||||
rsync ./build/install/antville/ ${{ inputs.hostname }}:./apps/antville/ \
|
||||
--archive --compress --delete --verbose \
|
||||
--filter '+ /claustra' \
|
||||
--filter '+ /code' \
|
||||
--filter '+ /compat' \
|
||||
--filter '+ /db' \
|
||||
--filter '+ /i18n' \
|
||||
--filter '+ /lib' \
|
||||
--filter '- /*'
|
||||
rsync ./build/install/antville/static/ ${{ inputs.hostname }}:./apps/antville/static/ \
|
||||
--archive --compress --verbose \
|
||||
--filter '+ /fonts' \
|
||||
--filter '+ /formica.html' \
|
||||
--filter '+ /img' \
|
||||
--filter '+ /scripts' \
|
||||
--filter '+ /styles' \
|
||||
--filter '- /*'
|
||||
|
||||
- name: Restart Helma
|
||||
run: ssh ${{ inputs.hostname }} restart
|
||||
|
||||
60
.github/workflows/release.yml
vendored
Normal file
60
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: '1.6.*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: antville
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
LC_TIME: en_US.UTF-8
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew assembleDist
|
||||
|
||||
- name: Create release notes
|
||||
id: create_release_notes
|
||||
run: |
|
||||
release_notes=$(npx git-cliff@latest --latest)
|
||||
# Write the release notes as a heredoc to the workflow output
|
||||
# ⚠️ No white space around `<<` is crucial!
|
||||
echo "release_notes<<.eot0x03" >> $GITHUB_OUTPUT
|
||||
echo "$release_notes" >> $GITHUB_OUTPUT
|
||||
echo ".eot0x03" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create release
|
||||
uses: actions/forgejo-release@v2
|
||||
with:
|
||||
direction: upload
|
||||
url: https://code.host.antville.org
|
||||
token: ${{ github.token }}
|
||||
title: Antville ${{ github.ref_name }}
|
||||
release-dir: build/distributions
|
||||
release-notes: ${{ steps.create_release_notes.outputs.release_notes }}
|
||||
hide-archive-link: true
|
||||
verbose: true
|
||||
|
||||
- name: Create release at GitHub
|
||||
run: |
|
||||
gh release create "$GITHUB_REF_NAME" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--title "Antville ${{ github.ref_name }}" \
|
||||
--notes "${{ steps.create_release_notes.outputs.release_notes }}"
|
||||
|
||||
- name: Upload release assets to GitHub
|
||||
run: |
|
||||
gh release upload "$GITHUB_REF_NAME" build/distributions/ant*.* \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--clobber
|
||||
41
.github/workflows/renovate.yml
vendored
Normal file
41
.github/workflows/renovate.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Run Renovate
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "42 * * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
renovate:
|
||||
runs-on: antville
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run Renovate
|
||||
# See <https://docs.renovatebot.com/troubleshooting/#log-debug-levels>
|
||||
# debug | info | warn | error | fatal
|
||||
run: LOG_LEVEL=info npx renovate
|
||||
env:
|
||||
# Renovate is using this token to retrieve release notes
|
||||
GITHUB_COM_TOKEN: ${{ secrets.renovate_github_com_token }}
|
||||
RENOVATE_CONFIG_FILE: renovate.json
|
||||
RENOVATE_ENDPOINT: ${{ github.api_url }}
|
||||
RENOVATE_GIT_AUTHOR: Renovate Bot <mail+renovate@antville.org>
|
||||
RENOVATE_IGNORE_PR_AUTHOR: 'true'
|
||||
RENOVATE_LOG_FILE: renovate-log.ndjson
|
||||
RENOVATE_LOG_FILE_LEVEL: debug
|
||||
RENOVATE_PLATFORM: gitea
|
||||
RENOVATE_REPOSITORIES: ${{ github.repository }}
|
||||
RENOVATE_REPOSITORY_CACHE: 'enabled'
|
||||
# github.token is not working here, it lacks some permissions required by Renovate
|
||||
RENOVATE_TOKEN: ${{ secrets.renovate_token }}
|
||||
|
||||
- name: Save log file
|
||||
# FIXME: v4 of this action causes an error on Forgejo (“You must configure a GitHub token”)
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: renovate-log.ndjson
|
||||
path: renovate-log.ndjson
|
||||
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
save-exact = true
|
||||
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
lts/*
|
||||
34
CHANGES.md
34
CHANGES.md
|
|
@ -1,11 +1,37 @@
|
|||
# Change Log
|
||||
|
||||
## May 23, 2020
|
||||
## 2024 January 13
|
||||
|
||||
* Fix cross-site scripting vulnerability in search output
|
||||
* Fix new choices being added to an existing poll at the wrong position
|
||||
* Fix errors in the client-side code filtering the referrers
|
||||
* Correctly load and initialize Alpine.js
|
||||
|
||||
## 2024 January 7
|
||||
|
||||
* Upgrade third-party dependencies
|
||||
|
||||
## 2023 December 30
|
||||
|
||||
* Fix initialization of MarkDown in UIKit editor
|
||||
* Add support for images in webp format
|
||||
|
||||
## 2023 August 26
|
||||
|
||||
* Migrate from Yarn back to NPM
|
||||
* Some major upgrades of third-party dependencies
|
||||
* Rewrite `for…each` loops with `for…of` (needs enabled ES6 features in Rhino)
|
||||
|
||||
## 2023 March 5
|
||||
|
||||
* Fix SQL injection vulnerability
|
||||
|
||||
## 2020 May 23
|
||||
|
||||
* Render tables with stripes and hover colors extrapolated from layout settings
|
||||
* Replaced Google’s ReCaptcha with [hCaptcha](https://hcaptcha.com)
|
||||
|
||||
## May 17, 2020
|
||||
## 2020 May 17
|
||||
|
||||
* Reverted Yarn scripts into Gradle tasks
|
||||
* Implemented complete build system in Gradle
|
||||
|
|
@ -13,13 +39,13 @@
|
|||
* Replaced Node module for rendering Markdown (marked) with Java implementation ([CommonMark](https://github.com/atlassian/commonmark-java))
|
||||
* Replaced Node module for sanitizing HTML (string-strip-html) with Java implementation ([jsoup](https://jsoup.org))
|
||||
|
||||
## May 1, 2020
|
||||
## 2020 May 1
|
||||
|
||||
* Upgraded jQuery to version 3
|
||||
* Refactored remaining cient-side jQuery code using [Alpine](https://github.com/alpinejs/alpine) (jQuery is still a dependency of UIkit, though)
|
||||
* Moved code for jQuery [CollagePlus](https://github.com/antville/jquery-collagePlus) plugin into extra file
|
||||
|
||||
## April 13, 2020
|
||||
## 2020 April 13
|
||||
|
||||
* Rewrote most client-side code without using jQuery
|
||||
* Refactored Formica bookmarklet
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Helma Object Publisher
|
||||
|
||||
Antville needs Helma Object Publisher to be installed on the desired machine. If not already done so please [download Helma](https://github.com/antville/helma/releases) and follow the [installation instructions](https://github.com/antville/helma/#how-to-helma).
|
||||
Antville needs Helma Object Publisher to be installed on the desired machine. If not already done so please [download Helma](https://code.host.antville.org/antville/helma/releases) and follow the [installation instructions](https://code.host.antville.org/antville/helma/#how-to-helma).
|
||||
|
||||
## SQL Database
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# License
|
||||
|
||||
Copyright 2001-2020 by the Workers of Antville.
|
||||
Copyright 2001-2025 by Antville – Verein für Entwicklung, Betrieb und
|
||||
Unterstützung freier Publishing-Software, Austria.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ There still could be bugs hidden in Antville’s source code. If you find one pl
|
|||
|
||||
## System Requirements
|
||||
|
||||
To run Antville you need [Helma Object Publisher](https://github.com/antville/helma) and a relational database software. We tested Antville with [PostgreSQL](https://postgresql.org) and [MySQL](https://mysql.com) – [MariaDB](https://mariadb.com) should work, too.
|
||||
To run Antville you need [Helma Object Publisher](https://code.host.antville.org/antville/helma) and a relational database software. We tested Antville with [PostgreSQL](https://postgresql.org) and [MySQL](https://mysql.com) – [MariaDB](https://mariadb.com) should work, too.
|
||||
|
||||
To enable Antville sending notification e-mails you need access to an SMTP server.
|
||||
|
||||
|
|
@ -33,8 +33,6 @@ For documentation and further information about Antville you can refer to:
|
|||
|
||||
Feel free to ask any question about Antville at our [support site](https://help.antville.org).
|
||||
|
||||
You should follow Antville on [Facebook](https://facebook.com/Antville) and [Twitter](https://twitter.com/antville_org)!
|
||||
|
||||
## Bug Reporting and Feature Requests
|
||||
|
||||
If you think you found a bug [please let us know](https://project.antville.org).
|
||||
|
|
@ -45,7 +43,7 @@ Antville is open-source, and we want to encourage you to change its code accordi
|
|||
|
||||
## About Helma Object Publisher
|
||||
|
||||
[Helma Object Publisher](https://github.com/antville/helma) is an open source project providing a powerful, fast and scriptable web application server written in Java.
|
||||
[Helma Object Publisher](https://code.host.antville.org/antville/helma) is an open source project providing a powerful, fast and scriptable web application server written in Java.
|
||||
|
||||
Define HopObjects and map them to a relational database table. Create, change and delete HopObjects at your whim using a comfortable object-container model. Manual fiddling around with database code is not necessary.
|
||||
|
||||
|
|
|
|||
253
build.gradle
253
build.gradle
|
|
@ -1,18 +1,13 @@
|
|||
plugins {
|
||||
id 'base'
|
||||
id 'java'
|
||||
|
||||
id 'com.github.node-gradle.node' version '2.2.3'
|
||||
id 'org.ajoberstar.grgit' version '4.0.2' apply false
|
||||
id 'com.github.node-gradle.node' version '7.1.0'
|
||||
}
|
||||
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
|
||||
tasks.build.dependsOn 'assemble'
|
||||
|
||||
node {
|
||||
version = '12.16.3'
|
||||
yarnVersion = '1.22.4'
|
||||
version = '20.9.0'
|
||||
download = true
|
||||
}
|
||||
|
||||
|
|
@ -21,21 +16,19 @@ allprojects {
|
|||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
project.ext.distVersion = { ->
|
||||
def json = new groovy.json.JsonSlurper()
|
||||
def packageData = json.parse(file("${rootProject.projectDir}/package.json"))
|
||||
return packageData.version.substring(0, packageData.version.size() - 2)
|
||||
return packageData.version;
|
||||
}
|
||||
|
||||
project.ext.antvilleBuildDir = "${rootProject.buildDir}/tmp/antville"
|
||||
project.ext.antvilleInstallDir = "${rootProject.buildDir}/install/antville"
|
||||
|
||||
project.ext.antvilleDistFiles = copySpec {
|
||||
from fileTree(antvilleBuildDir).matching {
|
||||
}
|
||||
from fileTree(antvilleBuildDir).matching {}
|
||||
}
|
||||
|
||||
// Hide some purely Java-related tasks
|
||||
|
|
@ -49,73 +42,91 @@ allprojects {
|
|||
|
||||
version = distVersion()
|
||||
|
||||
configurations {
|
||||
lessCss
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.atlassian.commonmark:commonmark:0.14.0'
|
||||
implementation 'com.atlassian.commonmark:commonmark-ext-autolink:0.14.0'
|
||||
implementation 'com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:0.14.0'
|
||||
implementation 'com.atlassian.commonmark:commonmark-ext-gfm-tables:0.14.0'
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
implementation 'org.commonmark:commonmark:0.24.0'
|
||||
implementation 'org.commonmark:commonmark-ext-autolink:0.24.0'
|
||||
implementation 'org.commonmark:commonmark-ext-gfm-strikethrough:0.24.0'
|
||||
implementation 'org.commonmark:commonmark-ext-gfm-tables:0.24.0'
|
||||
implementation 'org.jsoup:jsoup:1.19.1'
|
||||
implementation 'rome:rome:1.0'
|
||||
|
||||
implementation('org.lesscss:lesscss:1.7.0.1.1') {
|
||||
lessCss('org.lesscss:lesscss:1.7.0.1.1') {
|
||||
exclude group: 'org.mozilla', module: 'rhino'
|
||||
exclude group: 'org.slf4j', module: 'slf4j-api'
|
||||
exclude group: 'org.slf4j', module: 'slf4j-simple'
|
||||
}
|
||||
}
|
||||
|
||||
task assemble(type: Copy, overwrite: true) {
|
||||
assemble {
|
||||
dependsOn 'installAntville'
|
||||
dependsOn 'installJars'
|
||||
dependsOn 'buildStaticFiles'
|
||||
|
||||
from fileTree(antvilleBuildDir).matching {
|
||||
exclude 'node_modules'
|
||||
exclude 'package.json'
|
||||
exclude 'tests'
|
||||
exclude 'tools/client'
|
||||
exclude 'tools/antclick'
|
||||
exclude 'yarn.lock'
|
||||
} into antvilleInstallDir
|
||||
doFirst {
|
||||
copy {
|
||||
from fileTree(antvilleBuildDir).matching {
|
||||
exclude 'node_modules'
|
||||
exclude 'package.json'
|
||||
exclude 'tests'
|
||||
exclude 'tools/client'
|
||||
exclude 'tools/antclick'
|
||||
exclude 'package-lock.json'
|
||||
} into antvilleInstallDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task installAntville {
|
||||
tasks.register('installAntville') {
|
||||
description 'Clone the Antville repository and remove all unnecessary files.'
|
||||
group 'installation'
|
||||
|
||||
def tempDir = "$project.buildDir/tmp/repo"
|
||||
|
||||
outputs.dirs tempDir, antvilleBuildDir
|
||||
|
||||
doFirst {
|
||||
Grgit.clone(dir: tempDir, uri: project.ext['antville.repo.url'])
|
||||
}
|
||||
outputs.dirs antvilleBuildDir
|
||||
|
||||
doLast {
|
||||
def git = Grgit.open(dir: tempDir)
|
||||
def hash = git.head().abbreviatedId
|
||||
def date = new Date().format('d MMM yyyy')
|
||||
def gitOutput = new ByteArrayOutputStream()
|
||||
|
||||
exec {
|
||||
commandLine 'git', 'rev-parse', '--short', 'HEAD'
|
||||
standardOutput = gitOutput
|
||||
errorOutput = new ByteArrayOutputStream()
|
||||
ignoreExitValue = true
|
||||
}
|
||||
|
||||
def tag = gitOutput.toString().trim()
|
||||
|
||||
copy {
|
||||
from "$tempDir/code/app.properties"
|
||||
from "${project.projectDir}/code/app.properties"
|
||||
into "$antvilleBuildDir/code"
|
||||
filter { line -> line.replaceAll('(version =) 0.0.0', "\$1 $version.$hash")
|
||||
.replaceAll('(buildDate =) 18 Oct 1971', "\$1 $date")
|
||||
filter { line -> line.replaceAll('(version =) 0.0.0', "\$1 $version.$tag")
|
||||
.replaceAll('(buildDate =) 18 Jun 2001', "\$1 $date")
|
||||
}
|
||||
}
|
||||
|
||||
copy {
|
||||
from fileTree(tempDir).matching {
|
||||
from fileTree(project.projectDir).matching {
|
||||
include 'claustra/**'
|
||||
include 'code/**'
|
||||
exclude 'code/app.properties'
|
||||
exclude '*gradle*'
|
||||
exclude '.*'
|
||||
include 'compat/**'
|
||||
include 'db/**'
|
||||
include 'i18n/**'
|
||||
exclude 'i18n/*.po*'
|
||||
include 'static/**'
|
||||
include 'tools/**'
|
||||
include 'CHANGES.md'
|
||||
include 'INSTALL.md'
|
||||
include 'README.md'
|
||||
} into antvilleBuildDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task installJars(type: Copy) {
|
||||
tasks.register('installJars', Copy) {
|
||||
description 'Download required JAR libraries.'
|
||||
group 'installation'
|
||||
dependsOn 'installAntville'
|
||||
|
|
@ -128,22 +139,22 @@ task installJars(type: Copy) {
|
|||
into outputDir
|
||||
}
|
||||
|
||||
task installNodeModules(type: YarnTask) {
|
||||
tasks.register('installNodeModules', NpmTask) {
|
||||
description 'Download required Node modules.'
|
||||
group 'build'
|
||||
dependsOn 'installAntville'
|
||||
|
||||
inputs.files "$antvilleBuildDir/package.json"
|
||||
outputs.dir "$antvilleBuildDir/node_modules"
|
||||
inputs.files "${project.projectDir}/package-lock.json"
|
||||
outputs.dir "${project.projectDir}/node_modules"
|
||||
|
||||
args = ['-s', 'install']
|
||||
args = ['--silent', 'install']
|
||||
|
||||
execOverrides {
|
||||
it.workingDir = antvilleBuildDir
|
||||
}
|
||||
}
|
||||
|
||||
task buildStaticFiles(type: Copy) {
|
||||
tasks.register('buildStaticFiles', Copy) {
|
||||
description 'Build fonts, client-side scripts and stylesheets.'
|
||||
group 'build'
|
||||
|
||||
|
|
@ -156,7 +167,7 @@ task buildStaticFiles(type: Copy) {
|
|||
dependsOn 'buildGalleryScript'
|
||||
dependsOn 'buildLicenses'
|
||||
|
||||
def inputDir = "$antvilleBuildDir/node_modules/uikit/dist/fonts"
|
||||
def inputDir = "${project.projectDir}/node_modules/uikit/dist/fonts"
|
||||
def outputDir = "$antvilleBuildDir/static/fonts"
|
||||
|
||||
inputs.dir inputDir
|
||||
|
|
@ -167,56 +178,69 @@ task buildStaticFiles(type: Copy) {
|
|||
}
|
||||
|
||||
['main', 'editor', 'gallery'].each { name ->
|
||||
task "build${name.capitalize()}Script" (type: YarnTask) {
|
||||
tasks.register("build${name.capitalize()}Script", NpmTask) {
|
||||
description "Build the ${name} client-side scripts."
|
||||
group 'build'
|
||||
dependsOn 'installNodeModules'
|
||||
|
||||
def inputFile = "tools/client/${name}.js"
|
||||
def outputFile = "static/scripts/${name}.min.js"
|
||||
def inputFile = "${project.projectDir}/tools/client/${name}.js"
|
||||
def outputFile = "$antvilleBuildDir/static/scripts/${name}.min.js"
|
||||
|
||||
inputs.files "$antvilleBuildDir/$inputFile"
|
||||
outputs.files "$antvilleBuildDir/$outputFile"
|
||||
inputs.files inputFile
|
||||
outputs.files outputFile
|
||||
|
||||
args = ['-s', 'browserify', inputFile, '-o', outputFile, '-g', 'uglifyify']
|
||||
|
||||
execOverrides {
|
||||
it.workingDir = antvilleBuildDir
|
||||
}
|
||||
args = [
|
||||
'--silent',
|
||||
'exec', '--',
|
||||
'browserify', inputFile,
|
||||
'--outfile', outputFile,
|
||||
'-g', 'uglifyify'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
['main', 'editor'].each { name ->
|
||||
task "build${name.capitalize()}Styles" (type: YarnTask) {
|
||||
tasks.register("build${name.capitalize()}Styles", NpmTask) {
|
||||
description "Build the ${name} stylesheet."
|
||||
group 'build'
|
||||
dependsOn 'installNodeModules'
|
||||
|
||||
def inputFile = "tools/client/${name}.less"
|
||||
def outputFile = "static/styles/${name}.min.css"
|
||||
def inputFile = "${project.projectDir}/tools/client/${name}.less"
|
||||
def outputFile = "$antvilleBuildDir/static/styles/${name}.min.css"
|
||||
|
||||
inputs.files "$antvilleBuildDir/$inputFile"
|
||||
outputs.files "$antvilleBuildDir/$outputFile"
|
||||
inputs.files inputFile
|
||||
outputs.files outputFile
|
||||
|
||||
args = ['-s', 'lessc', '--clean-css', inputFile, outputFile]
|
||||
|
||||
execOverrides {
|
||||
it.workingDir = antvilleBuildDir
|
||||
}
|
||||
args = [
|
||||
'--silent',
|
||||
'exec', '--',
|
||||
'lessc',
|
||||
'--clean-css',
|
||||
inputFile,
|
||||
outputFile
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
task buildLicenses(type: YarnTask) {
|
||||
description 'Build licenses file from client-side dependecies.'
|
||||
tasks.register('buildLicenses', NpmTask) {
|
||||
description 'Build licenses file from client-side dependencies.'
|
||||
group 'build'
|
||||
dependsOn 'installNodeModules'
|
||||
|
||||
def outputFile = "$antvilleBuildDir/static/licenses.txt"
|
||||
|
||||
inputs.file "$antvilleBuildDir/package.json"
|
||||
inputs.file "${project.projectDir}/package-lock.json"
|
||||
outputs.file outputFile
|
||||
|
||||
args = ['-s', 'licenses', 'generate-disclaimer']
|
||||
args = [
|
||||
'--silent',
|
||||
'exec', '--',
|
||||
'generate-license-file',
|
||||
'--input', "${project.projectDir}/package.json",
|
||||
'--output', outputFile,
|
||||
'--overwrite',
|
||||
'--eol', 'lf'
|
||||
]
|
||||
|
||||
execOverrides {
|
||||
it.workingDir = antvilleBuildDir
|
||||
|
|
@ -224,7 +248,7 @@ task buildLicenses(type: YarnTask) {
|
|||
}
|
||||
}
|
||||
|
||||
task jsdoc(type: YarnTask) {
|
||||
tasks.register('jsdoc', NpmTask) {
|
||||
description 'Generates JavaScript API documentation for the main source code.'
|
||||
group 'documentation'
|
||||
dependsOn 'installNodeModules'
|
||||
|
|
@ -235,14 +259,22 @@ task jsdoc(type: YarnTask) {
|
|||
inputs.dir inputDir
|
||||
outputs.dir outputDir
|
||||
|
||||
args = ['-s', 'jsdoc', '-r', '-d', outputDir, inputDir]
|
||||
args = [
|
||||
'--silent',
|
||||
'exec', '--',
|
||||
'jsdoc',
|
||||
'--recurse',
|
||||
'--destination',
|
||||
outputDir,
|
||||
inputDir
|
||||
]
|
||||
|
||||
execOverrides {
|
||||
it.workingDir = antvilleBuildDir
|
||||
}
|
||||
}
|
||||
|
||||
task assembleDist {
|
||||
tasks.register('assembleDist') {
|
||||
description 'Creates the Antville download packages.'
|
||||
group 'distribution'
|
||||
|
||||
|
|
@ -251,7 +283,7 @@ task assembleDist {
|
|||
dependsOn 'distTar'
|
||||
}
|
||||
|
||||
task distZip(type: Zip) {
|
||||
tasks.register('distZip', Zip) {
|
||||
description 'Creates the Antville download package as Zip file.'
|
||||
group 'distribution'
|
||||
dependsOn 'assemble'
|
||||
|
|
@ -263,25 +295,80 @@ task distZip(type: Zip) {
|
|||
inputs.dir antvilleInstallDir
|
||||
outputs.file "$outputDir/$outputFile"
|
||||
|
||||
from antvilleInstallDir
|
||||
from (antvilleInstallDir) {
|
||||
into "antville-${version}"
|
||||
}
|
||||
|
||||
destinationDirectory = file(outputDir)
|
||||
archiveFileName = outputFile
|
||||
}
|
||||
|
||||
task distTar(type: Tar) {
|
||||
description 'Creates the Antville download package as Bzip2 file.'
|
||||
tasks.register('distTar', Tar) {
|
||||
description 'Creates the Antville download package as compressed archive file.'
|
||||
group 'distribution'
|
||||
dependsOn 'assemble'
|
||||
|
||||
def version = project.distVersion()
|
||||
def outputDir = "${project.buildDir}/distributions"
|
||||
def outputFile = "antville-${version}.tbz"
|
||||
def outputFile = "antville-${version}.tgz"
|
||||
|
||||
inputs.dir antvilleInstallDir
|
||||
outputs.file "$outputDir/$outputFile"
|
||||
|
||||
from antvilleInstallDir
|
||||
compression = Compression.BZIP2
|
||||
from (antvilleInstallDir) {
|
||||
into "antville-${version}"
|
||||
}
|
||||
|
||||
compression = Compression.GZIP
|
||||
destinationDirectory = file(outputDir)
|
||||
archiveFileName = outputFile
|
||||
}
|
||||
|
||||
// Task to patch the lesscss JAR file
|
||||
// This task modifies the lessc-rhino-1.7.0.js file in the JAR to comment out an annoying console.log statement (“done”)
|
||||
def patchLessCssJar = tasks.register('patchLessCssJar') {
|
||||
def name = 'lesscss-1.7.0.1.1'
|
||||
def targetFile = 'META-INF/lessc-rhino-1.7.0.js' // File to patch
|
||||
def tempDir = file("${buildDir}/${name}") // Temporary directory for extraction
|
||||
def patchedJar = file("${buildDir}/${name}-patched.jar") // Output patched JAR
|
||||
|
||||
outputs.file patchedJar
|
||||
|
||||
doLast {
|
||||
if (tempDir.exists()) {
|
||||
tempDir.deleteDir()
|
||||
}
|
||||
tempDir.mkdirs()
|
||||
|
||||
def jarFile = configurations.lessCss.find {
|
||||
it.name.contains(name)
|
||||
}
|
||||
|
||||
println "Patching $jarFile…"
|
||||
ant.unzip(src: jarFile, dest: tempDir)
|
||||
|
||||
def targetFilePath = new File(tempDir, targetFile)
|
||||
def content = targetFilePath.text
|
||||
content = content.replace('console.log("done")', '// console.log("done")')
|
||||
targetFilePath.text = content
|
||||
|
||||
ant.zip(destfile: patchedJar) {
|
||||
fileset(dir: tempDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the patchLessCssJar task runs before the classes task
|
||||
tasks.named('build').configure {
|
||||
dependsOn patchLessCssJar
|
||||
}
|
||||
|
||||
// Exclude the original lesscss JAR from the runtime classpath
|
||||
// and include the patched JAR instead
|
||||
configurations.runtimeClasspath {
|
||||
exclude group: 'org.lesscss', module: 'lesscss'
|
||||
|
||||
dependencies {
|
||||
runtimeOnly(patchLessCssJar.map { it.outputs.files })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Proxy.prototype.main_action = function () {
|
|||
|
||||
let content = new java.lang.String(data.content, 'utf-8');
|
||||
|
||||
if (!data.type.startsWith('text/')) {
|
||||
if (data.type && !data.type.startsWith('text/')) {
|
||||
content = new java.lang.String(content.enbase64());
|
||||
}
|
||||
|
||||
|
|
@ -55,10 +55,10 @@ Proxy.prototype.main_action = function () {
|
|||
} else {
|
||||
res.contentType = data.type;
|
||||
|
||||
if (data.type.startsWith('text/')) {
|
||||
res.write(java.lang.String(data.content, 'utf-8'));
|
||||
} else {
|
||||
if (data.type && !data.type.startsWith('text/')) {
|
||||
res.writeBinary(data.content);
|
||||
} else {
|
||||
res.write(java.lang.String(data.content, 'utf-8'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
52
cliff.toml
Normal file
52
cliff.toml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# git-cliff ~ default configuration file
|
||||
# https://git-cliff.org/docs/configuration
|
||||
#
|
||||
# Lines starting with "#" are comments.
|
||||
# Configuration options are organized into tables and keys.
|
||||
# See documentation for more information on available options.
|
||||
|
||||
[changelog]
|
||||
trim = true
|
||||
|
||||
header = "## Changes"
|
||||
|
||||
body = """
|
||||
{% for group, commits in commits | filter(attribute="merge_commit") | group_by(attribute="group") %}
|
||||
### {{ group | striptags | trim | upper_first }}
|
||||
{% for commit in commits %}
|
||||
* [<tt>{{ commit.id | split(pat="") | slice(end=11) | join() }}</tt>]\
|
||||
(https://code.host.antville.org/antville/antville/commit/{{ commit.id }}) \
|
||||
{% if commit.breaking %}**Breaking:** {% endif %}\
|
||||
{{ commit.message | split(pat="\\n") | first | upper_first | escape }}\
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
**Full Changelog:** [{{ previous.version }} → {{ version }}]\
|
||||
(https://code.host.antville.org/antville/antville/compare/\
|
||||
{{ previous.version | urlencode }}..{{ version | urlencode }})\n\n
|
||||
"""
|
||||
|
||||
footer = """
|
||||
Generated by [git-cliff](https://git-cliff.org/).
|
||||
"""
|
||||
|
||||
[git]
|
||||
conventional_commits = false
|
||||
filter_commits = false
|
||||
filter_unconventional = false
|
||||
protect_breaking_commits = false
|
||||
sort_commits = "newest"
|
||||
split_commits = false
|
||||
topo_order = false
|
||||
|
||||
commit_parsers = [
|
||||
{ message = "^Apply \\d+ suggestion", skip = true },
|
||||
{ message = "^Merge .*(branch|dependabot|dependency|renovate)", skip = true },
|
||||
{ message = "^Lock file maintenance", skip = true },
|
||||
{ message = "yarn\\.lock", skip = true },
|
||||
|
||||
{ message = "^[Ff]ix", group = "<!-- 0 --> 🐛 Bug Fixes" },
|
||||
{ field = "author.name", pattern = "[Rr]enovate|[Dd]ependabot", group = "<!-- 3 --> 📦 Dependency Updates" },
|
||||
{ message = "^Merge pull request", group = "<!-- 1 --> 🔀 Merges" },
|
||||
{ message = ".*", group = "<!-- 2 --> Uncategorized" },
|
||||
]
|
||||
|
|
@ -272,7 +272,8 @@ Admin.purgeReferrers = function() {
|
|||
Admin.commitRequests = function() {
|
||||
var requests = app.data.requests;
|
||||
app.data.requests = {};
|
||||
for each (var item in requests) {
|
||||
for (let key in requests) {
|
||||
let item = requests[key];
|
||||
switch (item.type) {
|
||||
case Story:
|
||||
var story = Story.getById(item.id);
|
||||
|
|
@ -292,7 +293,7 @@ Admin.commitEntries = function() {
|
|||
app.data.entries = [];
|
||||
var history = [];
|
||||
|
||||
for each (var item in entries) {
|
||||
for (let item of entries) {
|
||||
var referrer = helma.Http.evalUrl(item.referrer);
|
||||
if (!referrer) {
|
||||
continue;
|
||||
|
|
@ -385,7 +386,7 @@ Admin.updateDomains = function() {
|
|||
}
|
||||
}
|
||||
var map = res.pop();
|
||||
var file = new java.io.File(app.dir, 'domains.map');
|
||||
var file = new java.io.File(app.appsProperties.static, 'domains.map');
|
||||
var out = new java.io.BufferedWriter(new java.io.OutputStreamWriter(
|
||||
new java.io.FileOutputStream(file), 'UTF-8'));
|
||||
out.write(map);
|
||||
|
|
|
|||
|
|
@ -55,10 +55,7 @@ Api.blogger._getContentParts = function(content) {
|
|||
* @returns {Object} Properties: userid, nickname and url
|
||||
*/
|
||||
Api.blogger.getUserInfo = function(appKey, name, password) {
|
||||
var user = User.getByName(name);
|
||||
if (!user) {
|
||||
throw Error('User ' + name + ' does not exist on this server');
|
||||
}
|
||||
var user = Api.getUser(name, password);
|
||||
return {
|
||||
userid: name,
|
||||
nickname: name,
|
||||
|
|
@ -115,7 +112,7 @@ Api.blogger.getRecentPosts = function(appKey, id, name, password, limit) {
|
|||
var result = [];
|
||||
var stories = res.handlers.membership.stories;
|
||||
var max = Math.min(stories.size(), Number(limit) || Infinity, 20);
|
||||
for each (var story in stories.list(0, max)) {
|
||||
for (let story of stories.list(0, max)) {
|
||||
result.push({
|
||||
postid: story._id,
|
||||
userid: story.creator.name,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ Api.metaWeblog.getRecentPosts = function(id, name, password, limit) {
|
|||
var result = [];
|
||||
var stories = res.handlers.membership.stories;
|
||||
var max = Math.min(stories.size(), Number(limit) || Infinity, 20);
|
||||
for each (var story in stories.list(0, max)) {
|
||||
for (let story of stories.list(0, max)) {
|
||||
result.push(Api.metaWeblog._getStruct(story));
|
||||
}
|
||||
return result;
|
||||
|
|
@ -182,7 +182,7 @@ Api.metaWeblog.getCategories = function(id, name, password) {
|
|||
|
||||
var result = [];
|
||||
var tags = site.getTags('tags', Tags.ALL).list();
|
||||
for each (var tag in tags) {
|
||||
for (let tag of tags) {
|
||||
result.push({
|
||||
description: tag.name,
|
||||
htmlUrl: tag.href(),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ Api.mt.getRecentPostTitles = function(id, name, password, limit) {
|
|||
var result = [];
|
||||
var stories = res.handlers.membership.stories;
|
||||
var max = Math.min(stories.size(), Number(limit) || Infinity, 20);
|
||||
for each (var story in stories.list(0, max)) {
|
||||
for (let story of stories.list(0, max)) {
|
||||
result.push({
|
||||
postid: story._id,
|
||||
username: story.creator.name,
|
||||
|
|
@ -76,7 +76,7 @@ Api.mt.getCategoryList = function(id, name, password) {
|
|||
|
||||
var result = [];
|
||||
var tags = site.getTags('tags', Tags.ALL).list();
|
||||
for each (var tag in tags) {
|
||||
for (let tag of tags) {
|
||||
result.push({
|
||||
categoryId: tag.name, // FIXME: tag._id,
|
||||
categoryName: tag.name
|
||||
|
|
@ -104,7 +104,7 @@ Api.mt.getPostCategories = function(id, name, password) {
|
|||
}
|
||||
|
||||
var result = [];
|
||||
for each (var tag in story.getTags()) {
|
||||
for (let tag of story.getTags()) {
|
||||
result.push({
|
||||
categoryId: tag,
|
||||
categoryName: tag,
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ Archive.prototype.stories_macro = function() {
|
|||
var site = res.handlers.site;
|
||||
var offset = (page - 1) * pageSize;
|
||||
var stories = site.stories.featured.list(offset, pageSize);
|
||||
for each (var story in stories) {
|
||||
for (let story of stories) {
|
||||
renderStory(story);
|
||||
};
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<div class='av-choice uk-form-row'>
|
||||
<div class='uk-form-label'>#<% param.index %></div>
|
||||
<div>
|
||||
<input type='text' name='title' class='uk-width-1-1' value='<% choice.title %>' />
|
||||
<input type='text' name='title' class='uk-width-1-1' value='<% choice.title %>' x-init="counter += 1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ markgettext('Comment');
|
|||
markgettext('comment');
|
||||
markgettext('a comment // accusative');
|
||||
|
||||
Comment.HTML_WHITELIST = Packages.org.jsoup.safety.Whitelist.relaxed();
|
||||
Comment.HTML_WHITELIST = Packages.org.jsoup.safety.Safelist.relaxed();
|
||||
|
||||
/**
|
||||
* @see defineConstants
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
</ul>
|
||||
</header>
|
||||
<div class='uk-comment-body'>
|
||||
<% comment.text | comment.format %>
|
||||
<% comment.text | comment.format markdown %>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
|
|
|||
|
|
@ -65,8 +65,7 @@ File.remove = function() {
|
|||
* @param {String} name
|
||||
*/
|
||||
File.getName = function(name) {
|
||||
// TODO: Maybe return a hash if name is undefined?
|
||||
return name ? String(name).replace(/[^\w\d\s,._-]/g, String.EMPTY) : null;
|
||||
return String(name).md5() + '-' + String.random(16) + '-' + Date.now();
|
||||
}
|
||||
|
||||
File.getGenericType = function (contentType) {
|
||||
|
|
@ -271,27 +270,31 @@ File.prototype.update = function(data) {
|
|||
|
||||
if (mime.contentLength > 0) {
|
||||
var mimeName = mime.normalizeFilename(mime.name);
|
||||
|
||||
this.contentLength = mime.contentLength;
|
||||
this.contentType = mime.contentType;
|
||||
this.setOrigin(origin);
|
||||
|
||||
if (!this.name) {
|
||||
var name = File.getName(data.name) || mimeName.split('.')[0];
|
||||
if (this.isTransient()) {
|
||||
var name = data.name || mimeName.replace(/\.[^.]+$/, '');
|
||||
this.name = this.site.files.getAccessName(name);
|
||||
|
||||
// Make the file persistent before proceeding with writing it to disk (also see Helma bug #607)
|
||||
this.persist();
|
||||
}
|
||||
|
||||
// Make the file persistent before proceeding with writing
|
||||
// it to disk (also see Helma bug #607)
|
||||
this.isTransient() && this.persist();
|
||||
|
||||
var extension = mimeName.substr(mimeName.lastIndexOf('.')) || String.EMPTY;
|
||||
var fileName = this.name + extension;
|
||||
var fileName = File.getName(this.name) + extension;
|
||||
|
||||
if (fileName !== this.fileName) {
|
||||
// Remove existing file if the file name has changed
|
||||
this.getFile().remove();
|
||||
}
|
||||
|
||||
this.fileName = fileName;
|
||||
|
||||
var file = this.getFile();
|
||||
|
||||
mime.writeToFile(file.getParent(), file.getName());
|
||||
}
|
||||
|
||||
|
|
@ -330,7 +333,7 @@ File.prototype.description_macro = function(param) {
|
|||
} else if (param['default']) {
|
||||
res.write(param['default']);
|
||||
} else if (this.origin) {
|
||||
var text = this.origin.replace(new RegExp('^.+:///?(?:www\.)?([^/]+).*$'), '$1');
|
||||
var text = this.origin.replace(new RegExp('^.+:///?(?:www\\.)?([^/]+).*$'), '$1');
|
||||
var link = html.linkAsString({href: this.origin}, text);
|
||||
res.write(gettext('Source: {0}', link));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
* @fileOverview Defines the Exporter namespace.
|
||||
*/
|
||||
|
||||
app.addRepository('lib/gson-2.8.6.jar');
|
||||
// This code requires the Gson Java library bundled with Helma
|
||||
|
||||
global.Exporter = (function() {
|
||||
const gson = new JavaImporter(
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@
|
|||
String.ELLIPSIS = '…';
|
||||
|
||||
app.addRepository(app.dir + '/../lib/jdom-1.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/jsoup-1.13.1.jar');
|
||||
app.addRepository(app.dir + '/../lib/lesscss-1.7.0.1.1.jar');
|
||||
app.addRepository(app.dir + '/../lib/jsoup-1.19.1.jar');
|
||||
// See the patchLessCssJar task in build.gradle
|
||||
app.addRepository(app.dir + '/../lib/lesscss-1.7.0.1.1-patched.jar');
|
||||
app.addRepository(app.dir + '/../lib/rome-1.0.jar');
|
||||
|
||||
app.addRepository('modules/core/Global.js');
|
||||
|
|
@ -47,7 +48,7 @@ app.addRepository('modules/jala/code/Utilities.js');
|
|||
// Adding i18n message files as repositories
|
||||
(function() {
|
||||
var dir = new helma.File(app.dir, '../i18n');
|
||||
for each (let fname in dir.list()) {
|
||||
for (let fname of dir.list()) {
|
||||
fname.endsWith('.js') && app.addRepository(app.dir + '/../i18n/' + fname);
|
||||
}
|
||||
})();
|
||||
|
|
@ -682,7 +683,8 @@ function list_macro(param, id, limit) {
|
|||
return;
|
||||
}
|
||||
|
||||
var skin, collection;
|
||||
var skin;
|
||||
var collection = [];
|
||||
var max = Math.min(limit || 25, 100);
|
||||
|
||||
if (id === 'sites') {
|
||||
|
|
@ -768,7 +770,7 @@ function list_macro(param, id, limit) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
for each (var item in collection) {
|
||||
for (let item of collection) {
|
||||
item && item.renderSkin(param.skin || skin);
|
||||
}
|
||||
return;
|
||||
|
|
@ -998,7 +1000,7 @@ function json_filter(value, param) {
|
|||
|
||||
function script_filter(value, param) {
|
||||
// Remove <script> element and comments (order in brackets is crucial)
|
||||
return value.replace(/\s*(?:<!--|-->|<\/?script[^>]*>)\s*/g, String.EMPTY);
|
||||
return value.replace(/\s*(?:<!--|--!?>|<\/?script[^>]*>)\s*/gi, String.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1077,7 +1079,7 @@ function validateUrl(str) {
|
|||
function quote(str, pattern) {
|
||||
str = String(str);
|
||||
if (!pattern || RegExp(pattern).test(str) || /['"]/.test(str)) {
|
||||
str = "'" + str.replace(/("|')/g, '\\$1') + "'";
|
||||
str = "'" + str.replace(/('|"|\\)/g, '\\$1') + "'";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
|
@ -1307,7 +1309,7 @@ function getTimeZones(language) {
|
|||
locale = getLocale(language),
|
||||
ids = java.util.TimeZone.getAvailableIDs();
|
||||
|
||||
for each (let id in ids) {
|
||||
for (let id of ids) {
|
||||
// Exclude confusing time zones
|
||||
if (id.length < 4 || !id.contains('/') ||
|
||||
id.startsWith('Etc') || id.startsWith('System')) {
|
||||
|
|
|
|||
543
code/Global/Robots.js
Normal file
543
code/Global/Robots.js
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
// Robots parser adapted for Rhino-compatible JavaScript
|
||||
// Source: <https://github.com/samclarke/robots-parser>
|
||||
// Copyright (c) 2014 Sam Clarke
|
||||
// Copyright (c) 2025 Antville.org
|
||||
// MIT License (MIT)
|
||||
|
||||
// Transformation steps:
|
||||
// 1. Add IIFE around the code
|
||||
// 2. Replace module.exports with return statement
|
||||
// 3. Add conditional module.exports for CommonJS support
|
||||
// 4. Add URL class imitation
|
||||
|
||||
var Robots = (() => {
|
||||
/**
|
||||
* Half-baked (read-only) imitation of the URL class of Node.js
|
||||
*/
|
||||
function nodeJsUrl(str, base) {
|
||||
if (!str.includes('://')) {
|
||||
str = (base || 'http://localhost') + str;
|
||||
}
|
||||
|
||||
const url = new java.net.URL(str);
|
||||
const port = url.port < 0 ? '' : url.port;
|
||||
const userInfo = (url.getUserInfo() || "").split(':');
|
||||
|
||||
return {
|
||||
hash: url.ref ? '#' + url.ref : '',
|
||||
href: url.toString(),
|
||||
host: url.host + (port ? ':' + port : port),
|
||||
hostname: url.host,
|
||||
password: userInfo[1] || "",
|
||||
pathname: url.path,
|
||||
origin: url.protocol + '://' + url.host + (port ? ':' + port : port),
|
||||
port,
|
||||
protocol: url.protocol,
|
||||
search: url.queryy ? '?' + url.query : '',
|
||||
searchParams: {
|
||||
get: () => null,
|
||||
set: () => null
|
||||
},
|
||||
username: userInfo[0] || "",
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof URL === 'undefined') {
|
||||
globalThis.URL = nodeJsUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trims the white space from the start and end of the line.
|
||||
*
|
||||
* If the line is an array it will strip the white space from
|
||||
* the start and end of each element of the array.
|
||||
*
|
||||
* @param {string|Array} line
|
||||
* @return {string|Array}
|
||||
* @private
|
||||
*/
|
||||
function trimLine(line) {
|
||||
if (!line) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Array.isArray(line)) {
|
||||
return line.map(trimLine);
|
||||
}
|
||||
|
||||
return String(line).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove comments from lines
|
||||
*
|
||||
* @param {string} line
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
function removeComments(line) {
|
||||
var commentStartIndex = line.indexOf('#');
|
||||
if (commentStartIndex > -1) {
|
||||
return line.substr(0, commentStartIndex);
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a line at the first occurrence of :
|
||||
*
|
||||
* @param {string} line
|
||||
* @return {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
function splitLine(line) {
|
||||
var idx = String(line).indexOf(':');
|
||||
|
||||
if (!line || idx < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [line.slice(0, idx), line.slice(idx + 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalises the user-agent string by converting it to
|
||||
* lower case and removing any version numbers.
|
||||
*
|
||||
* @param {string} userAgent
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
function formatUserAgent(userAgent) {
|
||||
var formattedUserAgent = userAgent.toLowerCase();
|
||||
|
||||
// Strip the version number from robot/1.0 user agents
|
||||
var idx = formattedUserAgent.indexOf('/');
|
||||
if (idx > -1) {
|
||||
formattedUserAgent = formattedUserAgent.substr(0, idx);
|
||||
}
|
||||
|
||||
return formattedUserAgent.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalises the URL encoding of a path by encoding
|
||||
* unicode characters.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
function normaliseEncoding(path) {
|
||||
try {
|
||||
return urlEncodeToUpper(encodeURI(path).replace(/%25/g, '%'));
|
||||
} catch (e) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert URL encodings to support case.
|
||||
*
|
||||
* e.g.: %2a%ef becomes %2A%EF
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
function urlEncodeToUpper(path) {
|
||||
return path.replace(/%[0-9a-fA-F]{2}/g, function (match) {
|
||||
return match.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a pattern with the specified path
|
||||
*
|
||||
* Uses same algorithm to match patterns as the Google implementation in
|
||||
* google/robotstxt so it should be consistent with the spec.
|
||||
*
|
||||
* @see https://github.com/google/robotstxt/blob/f465f0ede81099dd8bc4aeb2966b3a892bd488b3/robots.cc#L74
|
||||
* @param {string} pattern
|
||||
* @param {string} path
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function matches(pattern, path) {
|
||||
// I've added extra comments to try make this easier to understand
|
||||
|
||||
// Stores the lengths of all the current matching substrings.
|
||||
// Maximum number of possible matching lengths is every length in path plus
|
||||
// 1 to handle 0 length too (if pattern starts with * which is zero or more)
|
||||
var matchingLengths = new Array(path.length + 1);
|
||||
var numMatchingLengths = 1;
|
||||
|
||||
// Initially longest match is 0
|
||||
matchingLengths[0] = 0;
|
||||
|
||||
for (var p = 0; p < pattern.length; p++) {
|
||||
// If $ is at the end of pattern then we must match the whole path.
|
||||
// Which is true if the longest matching length matches path length
|
||||
if (pattern[p] === '$' && p + 1 === pattern.length) {
|
||||
return matchingLengths[numMatchingLengths - 1] === path.length;
|
||||
}
|
||||
|
||||
// Handle wildcards
|
||||
if (pattern[p] == '*') {
|
||||
// Wildcard so all substrings minus the current smallest matching
|
||||
// length are matches
|
||||
numMatchingLengths = path.length - matchingLengths[0] + 1;
|
||||
|
||||
// Update matching lengths to include the smallest all the way up
|
||||
// to numMatchingLengths
|
||||
// Don't update smallest possible match as * matches zero or more
|
||||
// so the smallest current match is also valid
|
||||
for (var i = 1; i < numMatchingLengths; i++) {
|
||||
matchingLengths[i] = matchingLengths[i - 1] + 1;
|
||||
}
|
||||
} else {
|
||||
// Check the char at the matching length matches the pattern, if it
|
||||
// does increment it and add it as a valid length, ignore if not.
|
||||
var numMatches = 0;
|
||||
for (var i = 0; i < numMatchingLengths; i++) {
|
||||
if (
|
||||
matchingLengths[i] < path.length &&
|
||||
path[matchingLengths[i]] === pattern[p]
|
||||
) {
|
||||
matchingLengths[numMatches++] = matchingLengths[i] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// No paths matched the current pattern char so not a match
|
||||
if (numMatches == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
numMatchingLengths = numMatches;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseRobots(contents, robots) {
|
||||
var newlineRegex = /\r\n|\r|\n/;
|
||||
var lines = contents
|
||||
.split(newlineRegex)
|
||||
.map(removeComments)
|
||||
.map(splitLine)
|
||||
.map(trimLine);
|
||||
|
||||
var currentUserAgents = [];
|
||||
var isNoneUserAgentState = true;
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
|
||||
if (!line || !line[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (line[0].toLowerCase()) {
|
||||
case 'user-agent':
|
||||
if (isNoneUserAgentState) {
|
||||
currentUserAgents.length = 0;
|
||||
}
|
||||
|
||||
if (line[1]) {
|
||||
currentUserAgents.push(formatUserAgent(line[1]));
|
||||
}
|
||||
break;
|
||||
case 'disallow':
|
||||
robots.addRule(currentUserAgents, line[1], false, i + 1);
|
||||
break;
|
||||
case 'allow':
|
||||
robots.addRule(currentUserAgents, line[1], true, i + 1);
|
||||
break;
|
||||
case 'crawl-delay':
|
||||
robots.setCrawlDelay(currentUserAgents, line[1]);
|
||||
break;
|
||||
case 'sitemap':
|
||||
if (line[1]) {
|
||||
robots.addSitemap(line[1]);
|
||||
}
|
||||
break;
|
||||
case 'host':
|
||||
if (line[1]) {
|
||||
robots.setPreferredHost(line[1].toLowerCase());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
isNoneUserAgentState = line[0].toLowerCase() !== 'user-agent';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a pattern is allowed by the specified rules.
|
||||
*
|
||||
* @param {string} path
|
||||
* @param {Array.<Object>} rules
|
||||
* @return {Object?}
|
||||
* @private
|
||||
*/
|
||||
function findRule(path, rules) {
|
||||
var matchedRule = null;
|
||||
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
var rule = rules[i];
|
||||
|
||||
if (!matches(rule.pattern, path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The longest matching rule takes precedence
|
||||
// If rules are the same length then allow takes precedence
|
||||
if (!matchedRule || rule.pattern.length > matchedRule.pattern.length) {
|
||||
matchedRule = rule;
|
||||
} else if (
|
||||
rule.pattern.length == matchedRule.pattern.length &&
|
||||
rule.allow &&
|
||||
!matchedRule.allow
|
||||
) {
|
||||
matchedRule = rule;
|
||||
}
|
||||
}
|
||||
|
||||
return matchedRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts provided string into an URL object.
|
||||
*
|
||||
* Will return null if provided string is not a valid URL.
|
||||
*
|
||||
* @param {string} url
|
||||
* @return {?URL}
|
||||
* @private
|
||||
*/
|
||||
function parseUrl(url) {
|
||||
try {
|
||||
// Specify a URL to be used with relative paths
|
||||
// Using non-existent subdomain so can never cause conflict unless
|
||||
// trying to crawl it but doesn't exist and even if tried worst that can
|
||||
// happen is it allows relative URLs on it.
|
||||
var url = new URL(url, 'http://robots-relative.samclarke.com/');
|
||||
|
||||
if (!url.port) {
|
||||
url.port = url.protocol === 'https:' ? 443 : 80;
|
||||
}
|
||||
|
||||
return url;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function Robots(url, contents) {
|
||||
this._url = parseUrl(url) || {};
|
||||
this._rules = Object.create(null);
|
||||
this._sitemaps = [];
|
||||
this._preferredHost = null;
|
||||
|
||||
parseRobots(contents || '', this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified allow/deny rule to the rules
|
||||
* for the specified user-agents.
|
||||
*
|
||||
* @param {Array.<string>} userAgents
|
||||
* @param {string} pattern
|
||||
* @param {boolean} allow
|
||||
* @param {number} [lineNumber] Should use 1-based indexing
|
||||
*/
|
||||
Robots.prototype.addRule = function (userAgents, pattern, allow, lineNumber) {
|
||||
var rules = this._rules;
|
||||
|
||||
userAgents.forEach(function (userAgent) {
|
||||
rules[userAgent] = rules[userAgent] || [];
|
||||
|
||||
if (!pattern) {
|
||||
return;
|
||||
}
|
||||
|
||||
rules[userAgent].push({
|
||||
pattern: normaliseEncoding(pattern),
|
||||
allow: allow,
|
||||
lineNumber: lineNumber
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the specified delay to the specified user agents.
|
||||
*
|
||||
* @param {Array.<string>} userAgents
|
||||
* @param {string} delayStr
|
||||
*/
|
||||
Robots.prototype.setCrawlDelay = function (userAgents, delayStr) {
|
||||
var rules = this._rules;
|
||||
var delay = Number(delayStr);
|
||||
|
||||
userAgents.forEach(function (userAgent) {
|
||||
rules[userAgent] = rules[userAgent] || [];
|
||||
|
||||
if (isNaN(delay)) {
|
||||
return;
|
||||
}
|
||||
|
||||
rules[userAgent].crawlDelay = delay;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a sitemap
|
||||
*
|
||||
* @param {string} url
|
||||
*/
|
||||
Robots.prototype.addSitemap = function (url) {
|
||||
this._sitemaps.push(url);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the preferred host name
|
||||
*
|
||||
* @param {string} url
|
||||
*/
|
||||
Robots.prototype.setPreferredHost = function (url) {
|
||||
this._preferredHost = url;
|
||||
};
|
||||
|
||||
Robots.prototype._getRule = function (url, ua, explicit) {
|
||||
var parsedUrl = parseUrl(url) || {};
|
||||
var userAgent = formatUserAgent(ua || '*');
|
||||
|
||||
// The base URL must match otherwise this robots.txt is not valid for it.
|
||||
if (
|
||||
parsedUrl.protocol !== this._url.protocol ||
|
||||
parsedUrl.hostname !== this._url.hostname ||
|
||||
parsedUrl.port !== this._url.port
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
var rules = this._rules[userAgent];
|
||||
if (!explicit) {
|
||||
rules = rules || this._rules['*'];
|
||||
}
|
||||
rules = rules || [];
|
||||
var path = urlEncodeToUpper(parsedUrl.pathname + parsedUrl.search);
|
||||
var rule = findRule(path, rules);
|
||||
|
||||
return rule;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if allowed, false if not allowed.
|
||||
*
|
||||
* Will return undefined if the URL is not valid for
|
||||
* this robots.txt file.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string?} ua
|
||||
* @return {boolean?}
|
||||
*/
|
||||
Robots.prototype.isAllowed = function (url, ua) {
|
||||
var rule = this._getRule(url, ua, false);
|
||||
|
||||
if (typeof rule === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
return !rule || rule.allow;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the line number of the matching directive for the specified
|
||||
* URL and user-agent if any.
|
||||
*
|
||||
* The line numbers start at 1 and go up (1-based indexing).
|
||||
*
|
||||
* Return -1 if there is no matching directive. If a rule is manually
|
||||
* added without a lineNumber then this will return undefined for that
|
||||
* rule.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string?} ua
|
||||
* @return {number?}
|
||||
*/
|
||||
Robots.prototype.getMatchingLineNumber = function (url, ua) {
|
||||
var rule = this._getRule(url, ua, false);
|
||||
|
||||
return rule ? rule.lineNumber : -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the opposite of isAllowed()
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string?} ua
|
||||
* @return {boolean}
|
||||
*/
|
||||
Robots.prototype.isDisallowed = function (url, ua) {
|
||||
return !this.isAllowed(url, ua);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns trues if explicitly disallowed
|
||||
* for the specified user agent (User Agent wildcards are discarded).
|
||||
*
|
||||
* This will return undefined if the URL is not valid for this robots.txt file.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} ua
|
||||
* @return {boolean?}
|
||||
*/
|
||||
Robots.prototype.isExplicitlyDisallowed = function (url, ua) {
|
||||
var rule = this._getRule(url, ua, true);
|
||||
if (typeof rule === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
return !(!rule || rule.allow);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the crawl delay if there is one.
|
||||
*
|
||||
* Will return undefined if there is no crawl delay set.
|
||||
*
|
||||
* @param {string} ua
|
||||
* @return {number?}
|
||||
*/
|
||||
Robots.prototype.getCrawlDelay = function (ua) {
|
||||
var userAgent = formatUserAgent(ua || '*');
|
||||
|
||||
return (this._rules[userAgent] || this._rules['*'] || {}).crawlDelay;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the preferred host if there is one.
|
||||
*
|
||||
* @return {string?}
|
||||
*/
|
||||
Robots.prototype.getPreferredHost = function () {
|
||||
return this._preferredHost;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of sitemap URLs if there are any.
|
||||
*
|
||||
* @return {Array.<string>}
|
||||
*/
|
||||
Robots.prototype.getSitemaps = function () {
|
||||
return this._sitemaps.slice(0);
|
||||
};
|
||||
|
||||
return Robots;
|
||||
})();
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = Robots;
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ var Sql = function(options) {
|
|||
}
|
||||
|
||||
this.next = function() {
|
||||
for each (var key in columns) {
|
||||
for (let key of columns) {
|
||||
this.values[key] = result.getColumnItem(key);
|
||||
}
|
||||
return;
|
||||
|
|
@ -92,6 +92,32 @@ var Sql = function(options) {
|
|||
return sql;
|
||||
}
|
||||
|
||||
const prepare = function(args) {
|
||||
const sql = args[0];
|
||||
|
||||
if (args.length > 1) {
|
||||
const values = Array.prototype.splice.call(args, 1);
|
||||
const connection = db.getMetaData().getConnection();
|
||||
const statement = connection.prepareStatement(sql);
|
||||
|
||||
for (let index = 1, limit = values.length; index <= limit; index += 1) {
|
||||
let value = values[index - 1];
|
||||
|
||||
switch (typeof value) {
|
||||
case 'number':
|
||||
statement.setInt(index, value);
|
||||
break;
|
||||
case 'string':
|
||||
statement.setString(index, value);
|
||||
default:
|
||||
statement.setString(index, String(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return statement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes an SQL command.
|
||||
* @param {String} sql The SQL command.
|
||||
|
|
@ -118,6 +144,9 @@ var Sql = function(options) {
|
|||
* @returns {String}
|
||||
*/
|
||||
this.retrieve = function() {
|
||||
if (options.prepared === true) {
|
||||
return query = prepare(arguments);
|
||||
}
|
||||
return log.info(query = resolve(arguments));
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +155,14 @@ var Sql = function(options) {
|
|||
* @param {Function} callback The callback function executed for each record.
|
||||
*/
|
||||
this.traverse = function(callback) {
|
||||
var rows = db.executeRetrieval(query);
|
||||
let rows;
|
||||
|
||||
if (query instanceof java.sql.PreparedStatement) {
|
||||
rows = db.executePreparedRetrieval(query);
|
||||
} else {
|
||||
rows = db.executeRetrieval(query);
|
||||
}
|
||||
|
||||
if (rows && rows.next()) {
|
||||
do {
|
||||
var sql = new SqlData(rows);
|
||||
|
|
@ -176,9 +212,9 @@ Sql.PURGEREFERRERS = "delete from log where action = 'main' and " +
|
|||
* SQL query for searching stories.
|
||||
* @constant
|
||||
*/
|
||||
Sql.STORY_SEARCH = "select content.id from content, site, metadata, account as creator, account as modifier where site.id = $0 and content.prototype = 'Story' and site.id = content.site_id and content.status in ('public', 'shared', 'open') and content.creator_id = creator.id and content.modifier_id = modifier.id and creator.status <> 'deleted' and modifier.status <> 'deleted' and content.prototype = metadata.parent_type and content.id = metadata.parent_id and metadata.name in ('title', 'text') and lower(metadata.value) like lower('%$1%') group by content.id, content.created order by content.created desc limit $2";
|
||||
Sql.STORY_SEARCH = "select content.id from content, site, metadata, account as creator, account as modifier where site.id = ? and content.prototype = 'Story' and site.id = content.site_id and content.status in ('public', 'shared', 'open') and content.creator_id = creator.id and content.modifier_id = modifier.id and creator.status <> 'deleted' and modifier.status <> 'deleted' and content.prototype = metadata.parent_type and content.id = metadata.parent_id and metadata.name in ('title', 'text') and lower(metadata.value) like lower(?) group by content.id, content.created order by content.created desc limit ?";
|
||||
|
||||
Sql.COMMENT_SEARCH = "select comment.id from content as comment, content as story, site, metadata, account as creator, account as modifier where site.id = $0 and comment.prototype = 'Comment' and site.id = comment.site_id and comment.story_id = story.id and story.status in ('public', 'shared', 'open') and story.comment_mode in ('open') and comment.creator_id = creator.id and comment.modifier_id = modifier.id and creator.status <> 'deleted' and modifier.status <> 'deleted' and comment.prototype = metadata.parent_type and comment.id = metadata.parent_id and metadata.name in ('title', 'text') and lower(metadata.value) like lower('%$1%') group by comment.id, comment.created order by comment.created desc limit $2";
|
||||
Sql.COMMENT_SEARCH = "select comment.id from content as comment, content as story, site, metadata, account as creator, account as modifier where site.id = ? and comment.prototype = 'Comment' and site.id = comment.site_id and comment.story_id = story.id and story.status in ('public', 'shared', 'open') and story.comment_mode in ('open') and comment.creator_id = creator.id and comment.modifier_id = modifier.id and creator.status <> 'deleted' and modifier.status <> 'deleted' and comment.prototype = metadata.parent_type and comment.id = metadata.parent_id and metadata.name in ('title', 'text') and lower(metadata.value) like lower(?) group by comment.id, comment.created order by comment.created desc limit ?";
|
||||
|
||||
/**
|
||||
* SQL query for searching accounts which are not already members of the desired site.
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ app.addRepository('modules/helma/Aspects.js');
|
|||
}
|
||||
|
||||
var prototypes = app.__app__.getPrototypes().toArray();
|
||||
for each (var prototype in prototypes) {
|
||||
for (let prototype of prototypes) {
|
||||
if (prototype.name in global) {
|
||||
global[prototype.name].prototype.onCodeUpdate = function() {
|
||||
this.__renderSkin__ = this.renderSkin;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Root.prototype.extractMessages = function(script, scanDirs, potFile) {
|
|||
return res.pop();
|
||||
}
|
||||
var args = ['-o', potFile, '-e', 'utf-8'];
|
||||
for each (var dir in scanDirs.split(' ')) {
|
||||
for (let dir of scanDirs.split(' ')) {
|
||||
args.push(app.dir + '/../' + dir);
|
||||
}
|
||||
var file = new helma.File(script);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
app.addRepository(app.dir + '/../lib/autolink-0.10.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-0.14.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-ext-autolink-0.14.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-ext-gfm-strikethrough-0.14.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-ext-gfm-tables-0.14.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/autolink-0.11.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-0.24.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-ext-autolink-0.24.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-ext-gfm-strikethrough-0.24.0.jar');
|
||||
app.addRepository(app.dir + '/../lib/commonmark-ext-gfm-tables-0.24.0.jar');
|
||||
|
||||
var renderMarkdown = (function() {
|
||||
const commonMark = new JavaImporter(
|
||||
|
|
@ -41,8 +41,15 @@ var renderMarkdown = (function() {
|
|||
.extensions(extensions)
|
||||
.build();
|
||||
|
||||
// Save HTML entities like `–` from being erroneously encoded
|
||||
// See <https://github.com/commonmark/commonmark-java/issues/307>
|
||||
const entityRegex = /&(#\d+;)/g;
|
||||
const entityMarker = "<ampersand/>";
|
||||
const entityMarkerRegex = new RegExp(entityMarker + entityRegex.source.substring(1), 'g');
|
||||
|
||||
return function(str) {
|
||||
str = str.replace(entityRegex, entityMarker + '$1');
|
||||
const document = parser.parse(str);
|
||||
return renderer.render(document);
|
||||
return renderer.render(document).replace(entityMarkerRegex, '&$1');
|
||||
};
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -140,10 +140,18 @@ HopObject.prototype.onRequest = function() {
|
|||
}
|
||||
}
|
||||
|
||||
// Set up layout handler and skin path
|
||||
HopObject.confirmConstructor(Layout);
|
||||
res.handlers.layout = res.handlers.site.layout || new Layout;
|
||||
res.skinpath = res.handlers.layout.getSkinPath();
|
||||
|
||||
if (res.handlers.site.enforceRobotsTxt()) {
|
||||
res.status = 403
|
||||
res.data.error = gettext('The <a href="{0}">robots.txt</a> file disallows access to this page.', res.handlers.site.href('robots.txt'));
|
||||
root.error_action();
|
||||
res.stop();
|
||||
}
|
||||
|
||||
if (!this.getPermission(req.action)) {
|
||||
if (!session.user) {
|
||||
User.setLocation(root.href() + req.path);
|
||||
|
|
@ -186,8 +194,9 @@ HopObject.prototype.delete_action = function() {
|
|||
var parent = this._parent;
|
||||
var type = this._prototype;
|
||||
var url = this.constructor.remove.call(this, req.postParams) || parent.href();
|
||||
|
||||
res.message = gettext('{0} was successfully deleted.', gettext(type));
|
||||
res.redirect(User.getLocation() || url);
|
||||
res.redirect(url);
|
||||
} catch(ex) {
|
||||
redirectUrl = session.data.referer;
|
||||
res.message = ex;
|
||||
|
|
@ -195,8 +204,13 @@ HopObject.prototype.delete_action = function() {
|
|||
}
|
||||
}
|
||||
|
||||
if (req.data.http_get_remainder === 'safemode') { res.skinpath = [app.dir]; }
|
||||
if (!res.data.action) { res.data.action = this.href(req.action); }
|
||||
if (req.data.http_get_remainder === 'safemode') {
|
||||
res.skinpath = [app.dir];
|
||||
}
|
||||
|
||||
if (!res.data.action) {
|
||||
res.data.action = this.href(req.action);
|
||||
}
|
||||
|
||||
res.data.title = gettext('Confirm Deletion');
|
||||
session.data.location = redirectUrl;
|
||||
|
|
@ -305,7 +319,7 @@ HopObject.prototype.setTags = function(tags) {
|
|||
for (var i in tags) {
|
||||
// Trim and remove troublesome characters (like ../.. etc.)
|
||||
// We call getAccessName with a virgin HopObject to allow most names
|
||||
tag = tags[i] = this.getAccessName.call(new HopObject, File.getName(tags[i]));
|
||||
tag = tags[i] = this.getAccessName.call(new HopObject, tags[i]);
|
||||
if (tag && diff[tag] == null) {
|
||||
diff[tag] = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ Image.add = function(data, parent, user) {
|
|||
user || (user = session.user);
|
||||
var image = new Image;
|
||||
if (data) {
|
||||
for each (var key in Image.KEYS) {
|
||||
for (let key of Image.KEYS) {
|
||||
image[key] = data[key];
|
||||
}
|
||||
}
|
||||
|
|
@ -94,8 +94,6 @@ Image.getFileExtension = function(type) {
|
|||
type = type.substr(0, index);
|
||||
}
|
||||
switch (type.toLowerCase()) {
|
||||
//case 'image/x-icon':
|
||||
//return '.ico';
|
||||
case 'image/gif':
|
||||
return '.gif';
|
||||
case 'image/jpg':
|
||||
|
|
@ -105,6 +103,8 @@ Image.getFileExtension = function(type) {
|
|||
case 'image/png':
|
||||
case 'image/x-png':
|
||||
return '.png';
|
||||
case 'image/webp':
|
||||
return '.webp';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -301,8 +301,8 @@ Image.prototype.update = function(data) {
|
|||
this.contentType = mime.contentType;
|
||||
File.prototype.setOrigin.call(this, origin);
|
||||
|
||||
if (!this.name) {
|
||||
var name = File.getName(data.name || mimeName.split('.')[0]);
|
||||
if (this.isTransient()) {
|
||||
var name = data.name || mimeName.replace(/\.[^.]+$/, '');
|
||||
this.name = this.parent.images.getAccessName(name);
|
||||
}
|
||||
|
||||
|
|
@ -313,21 +313,24 @@ Image.prototype.update = function(data) {
|
|||
this.width = image.width;
|
||||
this.height = image.height;
|
||||
|
||||
// Create a thumbnail version if the image size exceeds
|
||||
// Create a thumbnail version if the image size exceeds constraints
|
||||
if (this.width > Image.THUMBNAILWIDTH) {
|
||||
thumbnail = this.getHelmaImage(mime, [Image.THUMBNAILWIDTH]);
|
||||
this.thumbnailWidth = thumbnail.width;
|
||||
this.thumbnailHeight = thumbnail.height;
|
||||
} else if (this.isPersistent()) {
|
||||
this.getThumbnailFile().remove();
|
||||
|
||||
// NOTE: delete operator won't work here due to getter/setter methods
|
||||
this.deleteMetadata('thumbnailName', 'thumbnailWidth', 'thumbnailHeight');
|
||||
}
|
||||
|
||||
// Make the image persistent before proceeding with writing files and
|
||||
// setting tags (also see Helma bug #607)
|
||||
if (this.isTransient()) this.persist();
|
||||
var fileName = this.name + extension;
|
||||
// Make the image persistent before proceeding with writing files and setting tags (also see Helma bug #607)
|
||||
if (this.isTransient()) {
|
||||
this.persist();
|
||||
}
|
||||
|
||||
var fileName = File.getName(this.name) + extension;
|
||||
|
||||
// Remove existing image files if the file name has changed
|
||||
if (fileName !== this.fileName) {
|
||||
|
|
@ -335,7 +338,10 @@ Image.prototype.update = function(data) {
|
|||
}
|
||||
|
||||
this.fileName = fileName;
|
||||
if (thumbnail) this.thumbnailName = this.name + '_small' + extension;
|
||||
|
||||
if (thumbnail) {
|
||||
this.thumbnailName = fileName.replace(/(\.[^.]+$)/, '-small$1');
|
||||
}
|
||||
|
||||
this.writeFiles(image.data || mime, thumbnail && thumbnail.data);
|
||||
this.contentLength = this.getFile().getLength();
|
||||
|
|
@ -401,6 +407,7 @@ Image.prototype.thumbnail_macro = function(param) {
|
|||
param.src = encodeURI(this.getUrl(this.getThumbnailFile().getName()));
|
||||
if (!param.title) { param.title = encode(this.description || ''); }
|
||||
param.alt = encode(param.alt || param.title);
|
||||
param.loading = "lazy";
|
||||
var width = param.width || this.thumbnailWidth;
|
||||
var height = param.height || this.thumbnailHeight;
|
||||
var style = [];
|
||||
|
|
@ -423,6 +430,7 @@ Image.prototype.render_macro = function(param) {
|
|||
param.src = encodeURI(this.getUrl());
|
||||
if (!param.title) { param.title = encode(this.description || ''); }
|
||||
param.alt = encode(param.alt || param.title);
|
||||
param.loading = "lazy";
|
||||
var style = [];
|
||||
param.width && style.push('width:', param.width + 'px;');
|
||||
param.height && style.push('height:', param.height + 'px;');
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>
|
||||
// Setup drag&drop for image uploads
|
||||
(function() {
|
||||
document.addEventListener("alpine:init", () => {
|
||||
const dropElement = document.querySelector('.av-upload-drop');
|
||||
const parent = location.href.lastIndexOf('/layout/images/') > -1 ? 'layout' : 'site';
|
||||
const uploadUrl = '<% site.images.href upload %>?parent=' + parent;
|
||||
|
|
@ -84,5 +84,5 @@
|
|||
error: restoreDrop,
|
||||
abort: restoreDrop
|
||||
});
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ Layout.prototype.getArchive = function(skinPath) {
|
|||
/* Most likely the thumbnail file is identical to the image */
|
||||
}
|
||||
var image = new HopObject;
|
||||
for each (var key in Image.KEYS) {
|
||||
for (let key of Image.KEYS) {
|
||||
image[key] = this[key];
|
||||
data.images.add(image);
|
||||
}
|
||||
|
|
@ -507,7 +507,7 @@ Layout.prototype.values_macro = function() {
|
|||
this.renderSkin('$Layout#value', {'class': 'uk-hidden'});
|
||||
|
||||
values.sort(new String.Sorter('key'));
|
||||
for each (var pair in values) {
|
||||
for (let pair of values) {
|
||||
var type = getType(pair.key);
|
||||
this.renderSkin('$Layout#value', {
|
||||
title: pair.key.capitalize(),
|
||||
|
|
|
|||
|
|
@ -202,7 +202,22 @@ Members.prototype.login_action = function() {
|
|||
}
|
||||
|
||||
res.message = gettext('Welcome to {0}, {1}. Have fun!', res.handlers.site.getTitle(), user.name);
|
||||
res.redirect(User.getLocation() || this._parent.href());
|
||||
const location = User.getLocation() || this._parent.href();
|
||||
|
||||
// If the requested host is outside of the cookie domain, redirect and login to the root site, too
|
||||
if (this._parent !== root && !req.getHeader("Host").includes(app.appsProperties.cookieDomain)) {
|
||||
const token = session.data.token = java.util.UUID.randomUUID();
|
||||
const digest = session.user.getDigest(token);
|
||||
res.redirect(
|
||||
root.href('cookie')
|
||||
+ '?digest=' + encodeURIComponent(digest)
|
||||
+ '&name=' + encodeURIComponent(req.postParams.name)
|
||||
+ '&location=' + encodeURIComponent(location)
|
||||
+ (req.postParams.remember ? '&remember=1' : '')
|
||||
);
|
||||
}
|
||||
|
||||
res.redirect(location);
|
||||
} catch (ex) {
|
||||
res.message = ex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
<% poll.link . ' ' class='uk-icon-button uk-icon-link uk-text-middle' %>
|
||||
</h1>
|
||||
<div class='uk-article-meta'><% poll.skin $HopObject#meta %></div>
|
||||
<form class='uk-margin-top uk-form uk-form-stacked' method="post" action="<% response.action %>" x-data='{ counter: 2 }'>
|
||||
<form class='uk-margin-top uk-form uk-form-stacked' method="post" action="<% response.action %>" x-data='{ counter: 1 }'>
|
||||
<div class='uk-form-row'>
|
||||
<div class='uk-form-label'><% gettext Question %></div>
|
||||
<div class='uk-form-controls'>
|
||||
|
|
@ -93,9 +93,11 @@
|
|||
</div>
|
||||
<fieldset class='uk-margin-top'>
|
||||
<legend><% gettext Choices %></legend>
|
||||
<% poll.input choices %>
|
||||
<div class="uk-margin-bottom">
|
||||
<% poll.input choices %>
|
||||
</div>
|
||||
<div class='uk-text-small uk-form-row' id='av-add-choice' hidden :hidden='false'>
|
||||
<a href='javascript:' @click='const choice = document.querySelectorAll(".av-choice")[counter - 1]; const clone = choice.cloneNode(true); counter += 1; clone.querySelector(".uk-form-label").innerText = "#" + counter; const input = clone.querySelector("input"); input.value = ""; choice.insertAdjacentElement("afterEnd", clone); input.focus();'>
|
||||
<a href='javascript:' @click='const choice = document.querySelector(".av-choice:last-child"); const clone = choice.cloneNode(true); clone.querySelector(".uk-form-label").innerText = `#${counter}`; const input = clone.querySelector("input"); input.value = ""; choice.insertAdjacentElement("afterEnd", clone); input.focus();'>
|
||||
<i class='uk-icon-plus'></i>
|
||||
<% gettext "Add Choice" %>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ Poll.prototype.status_action = function () {
|
|||
*/
|
||||
Poll.prototype.update = function(data) {
|
||||
var choices = [];
|
||||
for each (var title in data.title_array) {
|
||||
for (let title of data.title_array) {
|
||||
if (title = title.trim()) {
|
||||
choices.push(title);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
<h1><% response.title %></h1>
|
||||
<div class='uk-panel uk-panel-header'>
|
||||
<h3 class='uk-panel-title'><% gettext Status %></h3>
|
||||
<p><% site.skin Root#status %></p>
|
||||
<% site.skin Root#status %>
|
||||
</div>
|
||||
<div class='uk-panel uk-panel-header'>
|
||||
<h3 class='uk-panel-title'><% gettext Details %></h3>
|
||||
|
|
@ -85,17 +85,17 @@
|
|||
<dt><% gettext Requests %></dt>
|
||||
<dd>
|
||||
<% gettext '{0} total' <% param.requestCount %> %>,
|
||||
<% gettext '{0} in the last 5 min.' <% param.requestsPerUnit default=n.a. %> %>
|
||||
<% gettext '{0} in the last 5 min.' <% param.requestsPerUnit default=0 %> %>
|
||||
</dd>
|
||||
<dt><% gettext 'Errors' %></dt>
|
||||
<dd>
|
||||
<% gettext '{0} total' <% param.errorCount %> %>,
|
||||
<% gettext '{0} in the last 5 min.' <% param.errorsPerUnit default=n.a. %> %>
|
||||
<% gettext '{0} in the last 5 min.' <% param.errorsPerUnit default=0 %> %>
|
||||
</dd>
|
||||
<dt><% gettext 'Error Ratio' %></dt>
|
||||
<dd>
|
||||
<% gettext '{0}% total' <% param.errorRatio %> %>,
|
||||
<% gettext '{0}% in the last 5 min.' <% param.errorRatioPerUnit %> %>
|
||||
<% gettext '{0}% in the last 5 min.' <% param.errorRatioPerUnit default=0 %> %>
|
||||
</dd>
|
||||
<dt><% gettext 'Xml-Rpc Count' %></dt>
|
||||
<dd><% ngettext '{0} request' '{0} requests' <% param.xmlrpcCount %> %></dd>
|
||||
|
|
@ -114,13 +114,38 @@
|
|||
</dd>
|
||||
<dd></dd>
|
||||
<dd><% gettext '{0} log entries' <% param.entries %> %></dd>
|
||||
<dt><% gettext Version %></dt>
|
||||
<dd><% version %></dd>
|
||||
<dt><% gettext Build %></dt>
|
||||
<dd>
|
||||
<% link <% version hash prefix='https://github.com/antville/antville/commit/' %> <% version hash %> %> (<% version date %>)
|
||||
</dd>
|
||||
<dt><% gettext 'Helma Version' %></dt>
|
||||
<dd><% param.helma %></dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class='uk-panel uk-panel-header'>
|
||||
<h3 class='uk-panel-title'><% gettext Versions %></h3>
|
||||
<dl class='uk-description-list-horizontal'>
|
||||
<dt><% gettext Antville %></dt>
|
||||
<dd title='The phinal version'>φ (<% version %>)</dd>
|
||||
<dt><% gettext Commit %></dt>
|
||||
<dd>
|
||||
<a href='https://github.com/antville/antville/commit/<% version hash %>'>
|
||||
<% version hash %>
|
||||
</a>
|
||||
(<% version date %>)
|
||||
</dd>
|
||||
<dt><% gettext "Application Server" %></dt>
|
||||
<dd>
|
||||
<a href='https://github.com/antville/helma/commit/<% param.helmaCommitHash %>'>
|
||||
Helma <% param.helmaVersion %> <% param.helmaCommitHash %>
|
||||
</a>
|
||||
(<% param.helmaBuildDate %>)
|
||||
</dd>
|
||||
<dt><% gettext "Scripting Engine" %></dt>
|
||||
<dd><a href="https://github.com/mozilla/rhino/releases/tag/<% param.rhino | replace \\. _ | replace \\s '' %>_Release"><% param.rhino %></a></dd>
|
||||
<dt><% gettext "Webserver" %></dt>
|
||||
<dd><a href="https://github.com/jetty/jetty.project/releases/tag/jetty-<% param.jetty %>">Jetty <% param.jetty %></a></dd>
|
||||
<dt><% gettext "Servlet Interface" %></dt>
|
||||
<dd>
|
||||
<a href="https://jakarta.ee/specifications/servlet/<% param.servlet %>">
|
||||
Jakarta <% param.servlet %>
|
||||
</a>
|
||||
</dd>
|
||||
<dt><% gettext "Virtual Machine" %></dt>
|
||||
<dd><a href="https://openjdk.org/projects/jdk/<% param.java | replace \\.\\d+\\.\\d+$ '' %>">Java <% param.java %></a></dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,25 +24,25 @@ Root.VERSION = (function(versionString, buildDate) {
|
|||
// A valid version string is e.g. '1.2.3alpha.c0ffee'.
|
||||
// Repositories could add something like '-compatible' to it,
|
||||
// FIXME: This should be refactored for modular extension.
|
||||
var re = /^(\d+)\.(\d+)(?:\.(\d+))?(.+)?\.([a-f0-9]+)(?:-(.*))?$/;
|
||||
var re = /^(\d+)\.(\d+)(?:\.(\d+))?\.([a-f0-9]+)(?:-(.*))?$/;
|
||||
var parts = re.exec(versionString);
|
||||
if (parts) {
|
||||
var result = {
|
||||
date: buildDate,
|
||||
hash: parts[4],
|
||||
major: parts[1],
|
||||
parts: parts,
|
||||
toString: function() {return parts[0]},
|
||||
major: parseInt(parts[1]),
|
||||
hash: parts[5],
|
||||
date: new Date(buildDate).toLocaleDateString()
|
||||
toString: function() { return parts[1] }
|
||||
};
|
||||
result.minor = result.major + parseInt(parts[2] || 0) / 10;
|
||||
result.minor = result.major + '.' + (parts[2] || 0);
|
||||
result.bugfix = result.minor + '.' + (parts[3] || 0);
|
||||
result.development = parts[4] || '';
|
||||
result.development = parts[5] || '';
|
||||
result['default'] = result[parts[3] ? 'bugfix' : 'minor'] + result.development +
|
||||
(parts[6] ? '-' + parts[6] : String.EMPTY);
|
||||
return result;
|
||||
}
|
||||
return versionString;
|
||||
})(getProperty('version', '0.0.0'), getProperty('buildDate', '18 Oct 1971'));
|
||||
})(getProperty('version', '0.0.0'), getProperty('buildDate', '18 Jun 2001'));
|
||||
|
||||
this.handleMetadata('creationDelay');
|
||||
this.handleMetadata('creationScope');
|
||||
|
|
@ -94,6 +94,7 @@ Root.prototype.getPermission = function(action) {
|
|||
switch (action) {
|
||||
case '.':
|
||||
case 'main':
|
||||
case 'cookie':
|
||||
case 'debug':
|
||||
case 'default.hook':
|
||||
case 'favicon.ico':
|
||||
|
|
@ -212,7 +213,7 @@ Root.prototype.updates_xml_action = function() {
|
|||
var entries = new java.util.ArrayList();
|
||||
var entry, description;
|
||||
var sites = root.updates.list(0, 25);
|
||||
for each (var site in sites) {
|
||||
for (let site of sites) {
|
||||
var story = site.stories.union.get(0);
|
||||
if (!story) {
|
||||
continue;
|
||||
|
|
@ -254,32 +255,54 @@ Root.prototype.sitemap_xml_action = function() {
|
|||
}
|
||||
|
||||
Root.prototype.health_action = function() {
|
||||
var jvm = java.lang.Runtime.getRuntime();
|
||||
var totalMemory = jvm.totalMemory() / 1024 / 1024;
|
||||
var freeMemory = jvm.freeMemory() / 1024 / 1024;
|
||||
const jvm = java.lang.Runtime.getRuntime();
|
||||
const totalMemory = jvm.totalMemory() / 1024 / 1024;
|
||||
const freeMemory = jvm.freeMemory() / 1024 / 1024;
|
||||
let servletApi;
|
||||
|
||||
try {
|
||||
const getJavaClass = Packages.java.lang.Class.forName;
|
||||
servletApi = getJavaClass('jakarta.servlet.Servlet');
|
||||
} catch {
|
||||
// Fall back to older Servlet API
|
||||
servletApi = getJavaClass('javax.servlet.Servlet');
|
||||
}
|
||||
|
||||
var param = {
|
||||
uptime: formatNumber((new Date - app.upSince.getTime()) /
|
||||
Date.ONEDAY, '0.##'),
|
||||
uptime: formatNumber((new Date - app.upSince.getTime()) / Date.ONEDAY, '0.##'),
|
||||
freeMemory: formatNumber(freeMemory),
|
||||
totalMemory: formatNumber(totalMemory),
|
||||
usedMemory: formatNumber(totalMemory - freeMemory),
|
||||
sessions: formatNumber(app.countSessions()),
|
||||
cacheSize: formatNumber(getProperty('cacheSize')),
|
||||
helma: Packages.helma.main.Server.getServer().version
|
||||
helmaVersion: Packages.helma.main.Server.server.version,
|
||||
helmaBuildDate: Packages.helma.main.Server.server.buildDate,
|
||||
helmaCommitHash: Packages.helma.main.Server.server.commitHash,
|
||||
jetty: Packages.org.eclipse.jetty.util.Jetty.VERSION,
|
||||
rhino: Packages.org.mozilla.javascript.ImplementationVersion.get(),
|
||||
servlet: servletApi.package.specificationVersion,
|
||||
java: java.lang.System.getProperty('java.version')
|
||||
};
|
||||
|
||||
for each (key in ['activeThreads', 'freeThreads', 'requestCount',
|
||||
'errorCount', 'xmlrpcCount', 'cacheusage']) {
|
||||
for (
|
||||
let key of [
|
||||
'activeThreads',
|
||||
'freeThreads',
|
||||
'requestCount',
|
||||
'errorCount',
|
||||
'xmlrpcCount',
|
||||
'cacheusage'
|
||||
]
|
||||
) {
|
||||
param[key] = formatNumber(app[key]);
|
||||
}
|
||||
|
||||
param.errorRatio = formatNumber(100 * app.errorCount / app.requestCount || 0);
|
||||
param.errorRatioPerUnit = formatNumber(Admin.health.errorsPerUnit / Admin.health.requestsPerUnit || 0);
|
||||
|
||||
if (Admin.health) {
|
||||
param.requestsPerUnit = formatNumber(Admin.health.requestsPerUnit);
|
||||
param.errorsPerUnit = formatNumber(Admin.health.errorsPerUnit);
|
||||
param.errorRatioPerUnit = formatNumber(Admin.health.errorsPerUnit / Admin.health.requestsPerUnit || 0);
|
||||
}
|
||||
|
||||
param.callbacks = app.data.callbacks.length;
|
||||
|
|
@ -345,6 +368,20 @@ Root.prototype.mrtg_action = function() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Login to the root site if Members#login_action() redirects here
|
||||
// This way custom domains are getting the default domain cookie, too
|
||||
Root.prototype.cookie_action = function() {
|
||||
if (req.data.name && session.data.token) {
|
||||
const user = User.getByName(req.data.name);
|
||||
User.login({
|
||||
digest: user.getDigest(session.data.token),
|
||||
name: req.data.name,
|
||||
remember: req.data.remember
|
||||
});
|
||||
}
|
||||
res.redirect(req.data.location || req.data.http_referer || root.href());
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch some undefined macro handlers, then delegate to the super prototype.
|
||||
* @param {String} name
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<% #status %>
|
||||
<% gettext 'System is up and running.' %>
|
||||
<p>
|
||||
<% gettext 'System is up and running.' %>
|
||||
</p>
|
||||
|
||||
<% #urlPreview %>
|
||||
<% // This snippet contains additional markup to be rendered client-side by Alpine %>
|
||||
|
|
|
|||
|
|
@ -143,6 +143,22 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class='uk-form-row'>
|
||||
<label class='uk-form-label' for='trollFilter'>
|
||||
<% gettext 'Robot rules' %>
|
||||
</label>
|
||||
|
||||
<div class='uk-form-controls'>
|
||||
<label>
|
||||
<% site.checkbox robotsTxtMode %>
|
||||
<% gettext enforced %>
|
||||
</label>
|
||||
<p class="uk-form-help-block">
|
||||
<% gettext 'Edit the rules in the <a href="{0}Site/robots/edit">robots.txt</a> skin.' <% site.layout.skins.href %> %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='uk-form-row'>
|
||||
<label class='uk-form-label' for='trollFilter'>
|
||||
<% gettext 'Troll Filter' %>
|
||||
|
|
@ -214,7 +230,7 @@
|
|||
</form>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
document.addEventListener("alpine:init", () => {
|
||||
// Group related <option> elements by inserting additional <optgroup> elements.
|
||||
const dom = window;
|
||||
const groups = [];
|
||||
|
|
@ -246,7 +262,7 @@
|
|||
// Correct the selected index
|
||||
timeZone.selectedIndex = Array.from(timeZone.options).indexOf(timeZone.querySelector('option[selected]'));
|
||||
});
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
||||
<% #meta %>
|
||||
|
|
@ -336,7 +352,7 @@
|
|||
<% if <% property search.provider %> is null then '' else <% site.skin $Site#moreResults %> %>
|
||||
|
||||
<% #moreResults %>
|
||||
<a href='<% property search.provider %>?q=<% request.q %>+site:<% site.href %>' class='uk-button uk-align-right'>
|
||||
<a href='<% property search.provider %>?q=<% request.q encoding="url" %>+site:<% site.href %>' class='uk-button uk-align-right'>
|
||||
<% gettext 'Search with {0}' <% property search.provider | replace 'https?://([^/]+).*' '$1' %> %>
|
||||
<i class='uk-icon uk-icon-arrow-circle-right'></i>
|
||||
</a>
|
||||
|
|
@ -373,8 +389,8 @@
|
|||
<div><% if <% site.modified %> then <% gettext 'Last modified {0}' <% site.modified text %> %> suffix=. %></div>
|
||||
</div>
|
||||
<div class='uk-text-right'>
|
||||
<% image /smallchaos.gif | link https://antville.org %> &
|
||||
<% image /helma.png | link http://helma.org %>
|
||||
<% image /smallchaos.gif | link https://project.antville.org %> &
|
||||
<% image /helma.png | link https://github.com/antville/helma %>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
|
@ -402,7 +418,7 @@ void (function(url) {
|
|||
|
||||
// FIXME: compatibility
|
||||
|
||||
@base-font: <% value 'base font' default='Helvetica Neue, Helvetica, Arial, sans-serif' %>;
|
||||
@base-font: <% value 'base font' default='Helvetica Neue, Liberation Sans, Helvetica, Arial, sans-serif' %>;
|
||||
@base-font-size: <% value 'base font size' default="14px e('/') 20px" %>;
|
||||
@base-font-color: <% value 'base font color' default=#444 %>;
|
||||
|
||||
|
|
@ -435,12 +451,12 @@ void (function(url) {
|
|||
html, html.uk-notouch, body {
|
||||
background-color: @background;
|
||||
color: @text-color;
|
||||
font-family: @text-font;
|
||||
font: @text-font;
|
||||
}
|
||||
|
||||
body.av-16, body.av-page {
|
||||
max-width: 900px;
|
||||
margin: auto 5%;
|
||||
margin: 1em 5%;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
|
|
@ -560,7 +576,7 @@ hr {
|
|||
}
|
||||
|
||||
&.uk-button-link {
|
||||
border: initial;
|
||||
border-color: transparent;
|
||||
|
||||
&:link, &:visited {
|
||||
color: @link-color;
|
||||
|
|
@ -828,9 +844,9 @@ h4 + .uk-comment-meta li:first-child {
|
|||
.av-header-bg-chaos {
|
||||
width: 274px;
|
||||
height: 53px;
|
||||
margin-left: -54px;
|
||||
margin-left: -53px;
|
||||
background: url('<% image /spritesheet.png url %>');
|
||||
background-position: -9px -139px;
|
||||
background-position: -10px -139px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
|
|
@ -845,11 +861,12 @@ h4 + .uk-comment-meta li:first-child {
|
|||
|
||||
.av-title {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
top: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: 31px;
|
||||
font-family: Verdana, Helvetica, Arial, sans-serif;
|
||||
font-size: 25px;
|
||||
font-weight: bold;
|
||||
|
|
@ -876,7 +893,7 @@ h4 + .uk-comment-meta li:first-child {
|
|||
|
||||
@media (max-width: 767px) {
|
||||
.av-menu {
|
||||
@top: 14px;
|
||||
@top: 23px;
|
||||
@right: 5%;
|
||||
@size: 30px;
|
||||
|
||||
|
|
@ -1012,6 +1029,9 @@ h4 + .uk-comment-meta li:first-child {
|
|||
}
|
||||
}
|
||||
|
||||
// AlpineJS https://alpinejs.dev/directives/cloak
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
// Overwriting some CSS classes for Google’s custom search
|
||||
|
||||
.gs-webResult.gs-result a.gs-title:link,
|
||||
|
|
@ -1131,7 +1151,7 @@ a.gs-title:hover {
|
|||
</table>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
document.addEventListener("alpine:init", () => {
|
||||
const query = new Antville.Query();
|
||||
const spamFilter = new Antville.Filter(JSON.parse('<% site.spamfilter %>'));
|
||||
const searchFilter = new Antville.Filter(query.filter);
|
||||
|
|
@ -1224,12 +1244,12 @@ a.gs-title:hover {
|
|||
if (index2 <= index) return;
|
||||
|
||||
if (element2.innerHTML === html) {
|
||||
count += parseInt(document.querySelector('.av-referrer-count')[index2].innerText, 10);
|
||||
count += parseInt(document.querySelectorAll('.av-referrer-count')[index2].innerText, 10);
|
||||
element2.closest('.av-referrer-row').classList.add('av-delete');
|
||||
}
|
||||
});
|
||||
|
||||
if (count) countElement.innerHTML = parseInt(countElement.html(), 10) + count;
|
||||
if (count) countElement.innerHTML = parseInt(countElement.innerHTML, 10) + count;
|
||||
});
|
||||
|
||||
document.querySelectorAll('.av-referrer-row.av-delete').forEach(function(element) {
|
||||
|
|
@ -1245,7 +1265,7 @@ a.gs-title:hover {
|
|||
});
|
||||
|
||||
sortedRows.forEach(function(row) { table.appendChild(row); });
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
||||
<% #referrer %>
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ this.handleMetadata('notificationMode');
|
|||
this.handleMetadata('notified');
|
||||
this.handleMetadata('pageSize');
|
||||
this.handleMetadata('pageMode');
|
||||
this.handleMetadata('robotsTxtMode');
|
||||
this.handleMetadata('spamfilter');
|
||||
this.handleMetadata('tagline');
|
||||
this.handleMetadata('timeZone');
|
||||
|
|
@ -46,7 +47,7 @@ this.handleMetadata('title');
|
|||
this.handleMetadata('trollFilter');
|
||||
|
||||
/**
|
||||
* Ffunction
|
||||
* @function
|
||||
* @returns {String[]}
|
||||
* @see defineConstants
|
||||
*/
|
||||
|
|
@ -94,6 +95,13 @@ Site.getNotificationModes = defineConstants(Site, markgettext('Nobody'),
|
|||
*/
|
||||
Site.getCallbackModes = defineConstants(Site, markgettext('disabled'),
|
||||
markgettext('enabled'));
|
||||
/**
|
||||
* @function
|
||||
* @returns {String[]}
|
||||
* @see defineConstants
|
||||
*/
|
||||
Site.getRobotsTxtModes = defineConstants(Site, markgettext('suggest'),
|
||||
markgettext('enforce'));
|
||||
|
||||
/**
|
||||
* @param {String} name A unique identifier also used in the URL of a site
|
||||
|
|
@ -132,6 +140,7 @@ Site.add = function(data, user) {
|
|||
configured: now,
|
||||
created: now,
|
||||
creator: user,
|
||||
robotsTxtMode: Site.SUGGEST,
|
||||
modified: now,
|
||||
modifier: user,
|
||||
status: user.status === User.PRIVILEGED ? Site.TRUSTED : user.status,
|
||||
|
|
@ -367,6 +376,8 @@ Site.prototype.getFormOptions = function(name) {
|
|||
switch (name) {
|
||||
case 'archiveMode':
|
||||
return Site.getArchiveModes();
|
||||
case 'callbackMode':
|
||||
return Site.getCallbackModes();
|
||||
case 'commentMode':
|
||||
return Site.getCommentModes();
|
||||
case 'locale':
|
||||
|
|
@ -379,12 +390,12 @@ Site.prototype.getFormOptions = function(name) {
|
|||
return Site.getNotificationModes();
|
||||
case 'pageMode':
|
||||
return Site.getPageModes();
|
||||
case 'robotsTxtMode':
|
||||
return Site.getRobotsTxtModes();
|
||||
case 'status':
|
||||
return Site.getStatus();
|
||||
case 'timeZone':
|
||||
return getTimeZones(this.getLocale());
|
||||
case 'callbackMode':
|
||||
return Site.getCallbackModes();
|
||||
default:
|
||||
return HopObject.prototype.getFormOptions.apply(this, arguments);
|
||||
}
|
||||
|
|
@ -429,12 +440,21 @@ Site.prototype.update = function(data) {
|
|||
data.maxImageWidth = Math.abs(data.maxImageWidth) || Infinity;
|
||||
data.maxImageHeight = Math.abs(data.maxImageHeight) || Infinity;
|
||||
|
||||
let trollFilter = data.trollFilter || [];
|
||||
|
||||
if (typeof trollFilter === 'string') {
|
||||
trollFilter = trollFilter.split(/\r\n|\r|\n/).filter(function (item) {
|
||||
return item.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
this.map({
|
||||
archiveMode: data.archiveMode || Site.CLOSED,
|
||||
callbackMode: data.callbackMode || Site.DISABLED,
|
||||
callbackUrl: data.callbackUrl || this.callbackUrl || String.EMPTY,
|
||||
imageDimensionLimits: [data.maxImageWidth, data.maxImageHeight],
|
||||
commentMode: data.commentMode || Site.DISABLED,
|
||||
robotsTxtMode: data.robotsTxtMode || Site.RELAXED,
|
||||
imageDimensionLimits: [data.maxImageWidth, data.maxImageHeight],
|
||||
locale: data.locale || root.getLocale().toString(),
|
||||
mode: data.mode || Site.CLOSED,
|
||||
notificationMode: data.notificationMode || Site.NOBODY,
|
||||
|
|
@ -444,9 +464,7 @@ Site.prototype.update = function(data) {
|
|||
tagline: data.tagline || String.EMPTY,
|
||||
title: stripTags(data.title) || this.name,
|
||||
timeZone: data.timeZone || root.getTimeZone().getID(),
|
||||
trollFilter: data.trollFilter ? data.trollFilter.split(/\r\n|\r|\n/).filter(function (item) {
|
||||
return item.length > 0;
|
||||
}) : []
|
||||
trollFilter: trollFilter
|
||||
});
|
||||
|
||||
if (User.require(User.PRIVILEGED)) {
|
||||
|
|
@ -471,7 +489,8 @@ Site.prototype.main_css_action = function() {
|
|||
res.push();
|
||||
this.renderSkin('$Site#stylesheet');
|
||||
this.renderSkin('Site#stylesheet');
|
||||
var css = res.pop();
|
||||
var css = res.pop()
|
||||
.replace(/<(\/?style|!).*/g, ''); // TODO: Actually, a compatibility fix (earlier CSS skins contained the <style> element)
|
||||
|
||||
try {
|
||||
lessParser.parse(css, function(error, less) {
|
||||
|
|
@ -607,7 +626,7 @@ Site.prototype.renderXml = function(collection) {
|
|||
var description;
|
||||
var list = collection.constructor === Array ? collection : collection.list(0, 25);
|
||||
|
||||
for each (var item in list) {
|
||||
for (let item of list) {
|
||||
entry = new rome.SyndEntryImpl();
|
||||
entry.setTitle(item.title || formatDate(item.created, 'date'));
|
||||
entry.setLink(item.href());
|
||||
|
|
@ -953,8 +972,8 @@ Site.prototype.search = function (type, term, limit) {
|
|||
search.message = gettext('Please enter a query in the search form.');
|
||||
} else if (term) {
|
||||
var counter = 0;
|
||||
var sql = new Sql({quote: false});
|
||||
sql.retrieve(query, this._id, term, limit + 1);
|
||||
var sql = new Sql({prepared: true});
|
||||
sql.retrieve(query, parseInt(this._id, 10), '%' + term + '%', limit + 1);
|
||||
sql.traverse(function () {
|
||||
if (counter < limit) {
|
||||
search.result.push(Story.getById(this.id));
|
||||
|
|
@ -1118,3 +1137,28 @@ Site.prototype.callback = function(ref) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Site.prototype.enforceRobotsTxt = function() {
|
||||
if (this.robotsTxtMode !== Site.ENFORCE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Override some URLs to prevent a site from becoming inaccessible even for the owner
|
||||
const overrides = [
|
||||
this.href('edit'),
|
||||
this.href('main.css'),
|
||||
this.href('main.js'),
|
||||
this.href('robots.txt'),
|
||||
this.layout.href(),
|
||||
this.members.href()
|
||||
];
|
||||
|
||||
const robotsTxt = root.renderSkinAsString('Site#robots');
|
||||
const robots = new Robots(this.href('robots.txt'), robotsTxt);
|
||||
|
||||
const href = path.href(req.action);
|
||||
const fullUrl = (href.includes('://') ? '' : this.href()) + href.slice(1);
|
||||
|
||||
return !overrides.some(href => fullUrl.includes(href))
|
||||
&& !robots.isAllowed(fullUrl, req.getHeader('user-agent'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ prefix="Last update: " default="None so far" %>)</span></li>
|
|||
<link rel='apple-touch-icon' href='<% image /favicon.png url %>'>
|
||||
<% response.links %>
|
||||
<link rel='stylesheet' href='<% site.href main.css %>'>
|
||||
<script src='<% site.href main.js %>'></script>
|
||||
<script defer src='<% site.href main.js %>'></script>
|
||||
</head>
|
||||
<body class='av-16'>
|
||||
<% site.skin $Site#header %>
|
||||
|
|
@ -115,7 +115,7 @@ function openPopup(img, width, height) {
|
|||
<% value 'muted color' #999999 %>
|
||||
<% value 'hover color' #d50000 %>
|
||||
<% value 'link color' #ff4040 %>
|
||||
<% value 'text font' 'Helvetica, Arial, sans-serif' %>
|
||||
<% value 'title font' 'Helvetica, Arial, sans-serif' %>
|
||||
<% value 'text font' 'Liberation Sans, Helvetica, Arial, sans-serif' %>
|
||||
<% value 'title font' 'Liberation Sans, Helvetica, Arial, sans-serif' %>
|
||||
|
||||
<% #stylesheet %>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
</form>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
document.addEventListener("alpine:init", () => {
|
||||
// Setup skin editor
|
||||
let mode = 'application/x-helma-skin';
|
||||
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
tabSize: 3,
|
||||
viewportMargin: Infinity
|
||||
});
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
||||
<% #compare %>
|
||||
|
|
|
|||
|
|
@ -222,7 +222,10 @@ Skin.prototype.compare_action = function() {
|
|||
} else {
|
||||
res.push();
|
||||
var param = {}, leftLineNumber = rightLineNumber = 0;
|
||||
for each (let line in diff) {
|
||||
for (let line of diff) {
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
if (line.deleted) {
|
||||
param.right = encode(line.value);
|
||||
param.leftStatus = 'added';
|
||||
|
|
@ -249,7 +252,7 @@ Skin.prototype.compare_action = function() {
|
|||
this.renderSkin('$Skin#difference', param);
|
||||
}
|
||||
}
|
||||
if (line.value !== null) {
|
||||
if (line.value !== null && typeof line.value !== 'undefined') {
|
||||
leftLineNumber += 1;
|
||||
rightLineNumber += 1;
|
||||
param.leftLineNumber = leftLineNumber;
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
background-color: #f2fae3;
|
||||
}
|
||||
</style>
|
||||
<script src='<% site.href main.js %>'></script>
|
||||
<script src='<% root.static ../../scripts/editor.min.js %>'></script>
|
||||
<script defer src='<% site.href main.js %>'></script>
|
||||
</head>
|
||||
<body>
|
||||
<span class='uk-margin-right uk-float-right'>
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ Skins.prototype.all_action = function() {
|
|||
res.redirect(res.handlers.layout.skins.href(req.action));
|
||||
}
|
||||
res.push()
|
||||
for each (let set in this.getListOfSkins()) {
|
||||
for (let set of this.getListOfSkins()) {
|
||||
res.write(renderList(set[1], '$Skin#listItem'));
|
||||
}
|
||||
res.data.list = res.pop();
|
||||
|
|
@ -163,12 +163,12 @@ Skins.prototype.getSkin = function(group, name) {
|
|||
Skins.prototype.getListOfSkins = function() {
|
||||
var result = [];
|
||||
var options = Skin.getPrototypeOptions();
|
||||
for each (var option in options) {
|
||||
for (let option of options) {
|
||||
var skins = [];
|
||||
var prototype = option.value;
|
||||
var skinfiles = app.getSkinfilesInPath(res.skinpath);
|
||||
var skin = createSkin(skinfiles[prototype][prototype]);
|
||||
for each (var name in skin.getSubskinNames()) {
|
||||
for (let name of skin.getSubskinNames()) {
|
||||
var subskin = this.getSkin(prototype, name);
|
||||
skins.push(subskin);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ else
|
|||
<% #editor %>
|
||||
<script>
|
||||
// Load additonal scripts and styles for UIkit’s HTML editor plugin
|
||||
(function() {
|
||||
document.addEventListener("alpine:init", () => {
|
||||
const _form = document.querySelector('#av-story-form');
|
||||
|
||||
if (!_form) return;
|
||||
|
|
@ -296,5 +296,5 @@ else
|
|||
|
||||
scheduler();
|
||||
}
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -692,43 +692,8 @@ Story.prototype.format_filter = function(value, param, mode) {
|
|||
}
|
||||
|
||||
Story.prototype.linebreak_filter = function (value, param, mode) {
|
||||
if (mode === 'markdown') {
|
||||
// FIXME: should be obsolete
|
||||
var mdLineBreakMarker = new RegExp('<!--av-break-->', 'g');
|
||||
var mdQuoteMarker = new RegExp('<!--av-quote-->', 'g');
|
||||
var mdCodeMarker = new RegExp('<!--av-code-->', 'g');
|
||||
return value
|
||||
// Prevent Markdown for linebreaks (lines ending with 2 spaces) as well as
|
||||
// code segments (4 spaces) to be removed by Helma’s format() method
|
||||
.replace(/ {2}$/gm, mdLineBreakMarker.source)
|
||||
.replace(/^ {4}/gm, mdCodeMarker.source)
|
||||
// Prevent Markdown for quote segments (lines starting with ‘>’)
|
||||
// to be removed by Helma’s format method()
|
||||
.replace(/^(>+)/gm, function(item) {
|
||||
return mdQuoteMarker.source.repeat(item.length);
|
||||
})
|
||||
// Apply Helma’s format() method for good
|
||||
// FIXME: This should probably be moved to the compat layer
|
||||
.format(value)
|
||||
// Replace trailing HTML linebreaks with Markdown ones
|
||||
.replace(/<br[^>]*>$/gm, String.SPACE.repeat(2))
|
||||
// Replace trailing HTML linebreaks inserted by Helma’s format() method with Markdown ones
|
||||
//.replace(/<br\s*class=['"]?helma-format['"]?\s*\/?>/g, String.SPACE.repeat(2))
|
||||
// Restore Markdown quote segments
|
||||
.replace(mdQuoteMarker, '>')
|
||||
// Restore Markdown linebreaks and code segments
|
||||
.replace(mdLineBreakMarker, String.SPACE.repeat(2))
|
||||
.replace(mdCodeMarker, String.SPACE.repeat(4));
|
||||
} else {
|
||||
var parts = value.split(/(?:\n\n|\r\r|\r\n\r\n)+/);
|
||||
value = format('<p>' + parts.join('</p><p>') + '</p>');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
Story.prototype.code_filter = function (value, param) {
|
||||
value = this.linebreak_filter(value, param);
|
||||
return this.markdown_filter(value, param);
|
||||
var parts = value.split(/(?:\n\n|\r\r|\r\n\r\n)+/);
|
||||
return format('<p>' + parts.join('</p><p>') + '</p>');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,35 +23,37 @@
|
|||
|
||||
<script>
|
||||
// Load additional script for Collage jQuery plugin
|
||||
if ('<% tag.type parent %>' === 'Image') {
|
||||
const script = document.createElement('script');
|
||||
document.addEventListener("alpine:init", () => {
|
||||
if ('<% tag.type parent %>' === 'Image') {
|
||||
const script = document.createElement('script');
|
||||
|
||||
script.onload = function() {
|
||||
$(function() {
|
||||
let resizeTimer = null;
|
||||
script.onload = function() {
|
||||
$(function() {
|
||||
let resizeTimer = null;
|
||||
|
||||
const collage = () => {
|
||||
$('.av-collage').removeWhitespace().collagePlus({
|
||||
allowPartialLastRow: true,
|
||||
fadeSpeed: 'slow'
|
||||
const collage = () => {
|
||||
$('.av-collage').removeWhitespace().collagePlus({
|
||||
allowPartialLastRow: true,
|
||||
fadeSpeed: 'slow'
|
||||
});
|
||||
};
|
||||
|
||||
$('.av-collage').collageCaption();
|
||||
|
||||
$(window).bind('resize', function() {
|
||||
$('.av-tagged-image').css('opacity', 0);
|
||||
if (resizeTimer) clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(collage, 200);
|
||||
});
|
||||
};
|
||||
|
||||
$('.av-collage').collageCaption();
|
||||
|
||||
$(window).bind('resize', function() {
|
||||
$('.av-tagged-image').css('opacity', 0);
|
||||
if (resizeTimer) clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(collage, 200);
|
||||
collage();
|
||||
});
|
||||
};
|
||||
|
||||
collage();
|
||||
});
|
||||
};
|
||||
|
||||
script.src = '<% root.static ../../scripts/gallery.min.js %>';
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
script.src = '<% root.static ../../scripts/gallery.min.js %>';
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<% #simple %>
|
||||
|
|
|
|||
|
|
@ -114,37 +114,30 @@ Tag.prototype.rss_xml_action = function() {
|
|||
|
||||
Tag.prototype.rename_action = function() {
|
||||
var tag = this;
|
||||
|
||||
if (req.data.name) {
|
||||
// Trim and remove troublesome characters (like ../.. etc.)
|
||||
// We call getAccessName with a virgin HopObject to allow most names
|
||||
var name = this.getAccessName.call(new HopObject, File.getName(req.data.name));
|
||||
var name = this.getAccessName.call(new HopObject, req.data.name);
|
||||
|
||||
tag = this.site.getTags(this.type, Tags.ALL).get(name);
|
||||
|
||||
if (!tag) {
|
||||
tag = Tag.add(name, this.type, this.site);
|
||||
}
|
||||
|
||||
if (tag !== this) {
|
||||
this.forEach(function() {
|
||||
this.tag_id = tag._id;
|
||||
});
|
||||
|
||||
this.remove();
|
||||
res.commit();
|
||||
}
|
||||
}
|
||||
|
||||
res.redirect(req.data.http_referer);
|
||||
return;
|
||||
|
||||
// FIXME: Actually, the method should work like this but it caused a mess:
|
||||
if (req.data.name) {
|
||||
var name = this.getAccessName.call(new HopObject, File.getName(req.data.name));
|
||||
var tag = this.site.getTags(this.type, Tags.ALL).get(name);
|
||||
if (tag) {
|
||||
if (tag !== this) {
|
||||
// move tagged items to tag like above
|
||||
}
|
||||
} else {
|
||||
// rename tag like: this.name = name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tag.prototype.delete_action = function() {
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ Tags.prototype.list_macro = function(param, skin) {
|
|||
//var list = new jala.ListRenderer(collection);
|
||||
//list.render(skin || mgrlistitem);
|
||||
var index = start + 1;
|
||||
for each (var item in collection) {
|
||||
for (let item of collection) {
|
||||
// FIXME: Is there a more elegant solution?
|
||||
if (item.constructor !== Tag) {
|
||||
item = item.get(0);
|
||||
|
|
|
|||
|
|
@ -308,27 +308,31 @@ User.autoLogin = function() {
|
|||
if (session.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
var name = req.cookies[User.COOKIE];
|
||||
var hash = req.cookies[User.HASHCOOKIE];
|
||||
|
||||
if (!name || !hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
var user = User.getByName(name);
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
var ip = req.data.http_remotehost.clip(getProperty('cookieLevel', '4'),
|
||||
String.EMPTY, '\\.');
|
||||
if ((user.hash + ip).md5() !== hash) {
|
||||
|
||||
if (!user || User.getAutoLoginHash(user.hash) !== hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.login(user);
|
||||
user.touch();
|
||||
res.message = gettext('Welcome to {0}, {1}. Have fun!',
|
||||
res.handlers.site.title, user.name);
|
||||
res.message = gettext('Welcome to {0}, {1}. Have fun!', res.handlers.site.title, user.name);
|
||||
return;
|
||||
}
|
||||
|
||||
User.getAutoLoginHash = function(salt) {
|
||||
const ip = req.getHeader("X-Forwarded-For") || req.data.http_remotehost;
|
||||
return (salt + ip.replace(/[0-9a-fA-F]+$/, "")).md5();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} data
|
||||
|
|
@ -336,25 +340,29 @@ User.autoLogin = function() {
|
|||
*/
|
||||
User.login = function(data) {
|
||||
var user = User.getByName(data.name);
|
||||
var digest = data.digest;
|
||||
|
||||
if (!user) {
|
||||
throw Error(gettext('Unfortunately, your login failed. Maybe a typo?'));
|
||||
}
|
||||
var digest = data.digest;
|
||||
|
||||
// Calculate digest for JavaScript-disabled browsers
|
||||
if (!digest) {
|
||||
app.logger.warn('Received clear text password from ' + req.data.http_referer);
|
||||
digest = ((data.password + user.salt).md5() + session.data.token).md5();
|
||||
}
|
||||
|
||||
// Check if login is correct
|
||||
if (digest !== user.getDigest(session.data.token)) {
|
||||
throw Error(gettext('Unfortunately, your login failed. Maybe a typo?'))
|
||||
}
|
||||
|
||||
if (data.remember) {
|
||||
// Set long running cookies for automatic login
|
||||
res.setCookie(User.COOKIE, user.name, 365);
|
||||
var ip = req.data.http_remotehost.clip(getProperty('cookieLevel', '4'), String.EMPTY, '\\.');
|
||||
res.setCookie(User.HASHCOOKIE, (user.hash + ip).md5(), 365);
|
||||
res.setCookie(User.HASHCOOKIE, User.getAutoLoginHash(user.hash), 365);
|
||||
}
|
||||
|
||||
user.touch();
|
||||
session.login(user);
|
||||
return user;
|
||||
|
|
@ -594,7 +602,7 @@ User.prototype.timeline_action = function() {
|
|||
res.data.pager = renderPager(count, this.href(req.action), pageSize, page);
|
||||
res.data.title = gettext('Timeline');
|
||||
res.data.body = this.renderSkinAsString('$User#timeline');
|
||||
root.renderSkin('Site#page');
|
||||
res.handlers.site.renderSkin('Site#page');
|
||||
};
|
||||
|
||||
User.prototype.delete_action = function() {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
# The current build version and date
|
||||
version = 0.0.0
|
||||
buildDate = 18 Oct 1971
|
||||
buildDate = 18 Jun 2001
|
||||
|
||||
# The root site
|
||||
rootId = 1
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ Layout.convert = function(fpath) {
|
|||
|
||||
var convert2subskins = function(proto, dir) {
|
||||
res.push();
|
||||
for each (var fname in dir.list()) {
|
||||
for (let fname of dir.list()) {
|
||||
var file = new helma.File(dir, fname);
|
||||
var name = fname.split(".")[0], skin;
|
||||
if (skin = rename(proto, name)) {
|
||||
|
|
@ -238,7 +238,7 @@ Layout.convert = function(fpath) {
|
|||
}
|
||||
|
||||
var dir = new helma.File(fpath, "images");
|
||||
for each (var fname in dir.list()) {
|
||||
for (let fname of dir.list()) {
|
||||
var file = new helma.File(dir, fname);
|
||||
file.move(new helma.File(fpath, fname));
|
||||
}
|
||||
|
|
@ -246,7 +246,7 @@ Layout.convert = function(fpath) {
|
|||
var inventory = new function() {
|
||||
var dir = new helma.File(fpath);
|
||||
var result = {};
|
||||
for each (var fname in dir.list()) {
|
||||
for (let fname of dir.list()) {
|
||||
var file = new helma.File(dir, fname);
|
||||
if (!file.isDirectory()) {
|
||||
// Where does the "image\" prefix come from in files from layouts.antville.org?
|
||||
|
|
@ -277,7 +277,7 @@ Layout.convert = function(fpath) {
|
|||
dir = new helma.File(fpath, "skins");
|
||||
|
||||
var skin;
|
||||
for each (var fname in dir.list()) {
|
||||
for (let fname of dir.list()) {
|
||||
file = new helma.File(dir, fname);
|
||||
skin = convert2subskins(fname, file);
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@ Layout.convert = function(fpath) {
|
|||
data.images = new HopObject;
|
||||
|
||||
var dir = new helma.File(fpath, "imagedata");
|
||||
for each (fname in dir.list()) {
|
||||
for (let fname of dir.list()) {
|
||||
if (fname.endsWith(".xml")) {
|
||||
file = new helma.File(dir, fname);
|
||||
data.images.add(convertImage(Xml.read(file)));
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@
|
|||
|
||||
<script>
|
||||
// Load additional script for Google’s custom search
|
||||
(function() {
|
||||
document.addEventListener("alpine:init", () => {
|
||||
var cx = '008141500676255803966:bw4l-wnuz44';
|
||||
var gcse = document.createElement('script');
|
||||
gcse.type = 'text/javascript';
|
||||
gcse.async = true;
|
||||
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
||||
gcse.src = (document.location.protocol === 'https:' ? 'https:' : 'http:') +
|
||||
'//www.google.com/cse/cse.js?cx=' + cx;
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(gcse, s);
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ Story.prototype.content_macro = function(param) {
|
|||
// Clone param and remove non-HTML attributes from param:
|
||||
var options = Object.clone.call(param, {});
|
||||
var noAttr = 'as clipping delimiter fallback limit part';
|
||||
for each (let key in noAttr.split(String.SPACE)) {
|
||||
for (let key of noAttr.split(String.SPACE)) {
|
||||
delete param[key];
|
||||
}
|
||||
|
||||
|
|
@ -162,7 +162,7 @@ Story.prototype.getRenderedContentPart = function(name, mode) {
|
|||
// Enable caching; some macros (eg. poll, storylist) will set this
|
||||
// to false to prevent caching of a contentpart containing them.
|
||||
res.meta.cachePart = true;
|
||||
part = this.format_filter(part, {});
|
||||
part = this.format_filter(part, {}, "markdown");
|
||||
}
|
||||
this.cache[key] = part;
|
||||
if (res.meta.cachePart) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
antville.url = jdbc:h2:./db/antville;ifexists=true;mode=postgresql
|
||||
antville.url = jdbc:h2:./db/antville;ifexists=true;mode=postgresql;non_keywords=value
|
||||
antville.driver = org.h2.Driver
|
||||
antville.user = antville
|
||||
antville.password = antville
|
||||
|
|
|
|||
|
|
@ -1,4 +1,2 @@
|
|||
org.gradle.console = plain
|
||||
|
||||
antville.repo.url = https://github.com/antville/antville.git
|
||||
helma.download.url = https://github.com/antville/helma/releases/download/v20200614/helma-20200614.tgz
|
||||
antville.repo.url = https://code.host.antville.org/github.com/antville/antville.git
|
||||
helma.download.url = https://code.host.antville.org/antville/helma/releases/download/25.3.1/helma-25.3.1.tgz
|
||||
|
|
|
|||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,5 +1,7 @@
|
|||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-bin.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
|||
313
gradlew
vendored
313
gradlew
vendored
|
|
@ -1,78 +1,129 @@
|
|||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
|
@ -81,92 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
|
|||
80
gradlew.bat
vendored
80
gradlew.bat
vendored
|
|
@ -1,4 +1,22 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
|
|
@ -9,25 +27,29 @@
|
|||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
|
@ -35,48 +57,36 @@ goto fail
|
|||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
638
i18n/de.po
638
i18n/de.po
File diff suppressed because it is too large
Load diff
|
|
@ -39,9 +39,11 @@ global.messages['de'] = {
|
|||
"All Skins": "Alle Skins",
|
||||
"All of this will be deleted irreversibly.": "All das wird unwiderruflich gelöscht werden.",
|
||||
"An error occurred while processing your request.": "Bei der Bearbeitung der Anfrage ist ein Fehler aufgetreten.",
|
||||
"Antville": "Antville",
|
||||
"Antville is an open source project aimed at the development of a simple site hosting system with many advanced <a href=\"{0}\">features</a>.": "Antville ist ein quelloffenes Projekt zum einfachen Veröffentlichen von Websites und bietet einige <a href=\"{0}\">besondere Merkmale</a>.",
|
||||
"Antville itself provides a default callback URL invoking the basic ping method of the weblogs.com API.": "Antville selbst bietet eine standardmäßige Rückruf-Adresse, welche die Ping-Methode der Website weblogs.com aufruft.",
|
||||
"Antville supports the following application programming interfaces:": "Antville unterstützt folgende Schnittstellen zur Anwendungsprogrammierung:",
|
||||
"Application Server": "Anwendungsserver",
|
||||
"Archive": "Archiv",
|
||||
"Are you sure you want to add this URL to the referrer filter? Edit it below to filter a pattern only.": "Sind Sie sicher, dass Sie diesen Rückverweis filtern wollen? Sie können ihn bearbeiten, um ein Schema zu filtern.",
|
||||
"Are you sure you want to proceed?": "Sind Sie sicher, dass Sie fortfahren wollen?",
|
||||
|
|
@ -52,7 +54,6 @@ global.messages['de'] = {
|
|||
"Blocked": "Gesperrt",
|
||||
"Bookmarklet": "Bookmarklet",
|
||||
"Both": "Beides",
|
||||
"Build": "Gestalt",
|
||||
"Cache": "Zwischenspeicher",
|
||||
"Callback URL": "Rückruf-Adresse",
|
||||
"Cancel": "Abbrechen",
|
||||
|
|
@ -68,6 +69,7 @@ global.messages['de'] = {
|
|||
"Comments": "Kommentare",
|
||||
"Comments by {0}": "Kommentare von {0}",
|
||||
"Comments of the story are {0}": "Kommentare zum Beitrag sind {0}",
|
||||
"Commit": "Commit",
|
||||
"Compare": "Vergleichen",
|
||||
"Compare {0}": "Vergleichen {0}",
|
||||
"Confirm Deletion": "Löschen bestätigen",
|
||||
|
|
@ -123,6 +125,7 @@ global.messages['de'] = {
|
|||
"Edit Poll": "Umfrage bearbeiten",
|
||||
"Edit Story": "Beitrag bearbeiten",
|
||||
"Edit the filter in the site settings.": "Der Filter kann in den Einstellungen bearbeitet werden.",
|
||||
"Edit the rules in the <a href=\"{0}Site/robots/edit\">robots.txt</a> skin.": "Bearbeiten Sie die Regeln im <a href=\"{0}Site/robots/edit\">robots.txt</a>-Skin.",
|
||||
"Edit {0}.{1}": "{0}.{1} bearbeiten",
|
||||
"Enabled": "Aktiviert",
|
||||
"Enter one filter {0}pattern{1} per line to be applied on every URL in the referrer and backlink lists.": "Geben Sie ein {0}Filter-Schema{1} pro Zeile ein, das für jede Adresse in den Rückverweis-Listen angewendet werden soll.",
|
||||
|
|
@ -149,7 +152,6 @@ global.messages['de'] = {
|
|||
"Have fun!": "Viele Späße!",
|
||||
"Health": "Statusmonitor",
|
||||
"Hello {0}.": "Hallo {0}.",
|
||||
"Helma Version": "Helma-Version",
|
||||
"Hide": "Verstecken",
|
||||
"I understand and accept the {0}data privacy statement{1}": "Ich verstehe und akzeptiere die {0}Datenschutzerklärung{1}",
|
||||
"I understand and accept the {0}terms and conditions{1}": "Ich verstehe und akzeptiere die {0}Nutzungsbedingungen{1}",
|
||||
|
|
@ -289,17 +291,20 @@ global.messages['de'] = {
|
|||
"Resource type (e.g. Story or Comment)": "Art der Ressource (z.B. Beitrag oder Kommentar)",
|
||||
"Restricted": "Eingeschränkt",
|
||||
"Results": "Ergebnis",
|
||||
"Robot rules": "Regeln für Robots",
|
||||
"Role": "Rolle",
|
||||
"Running": "Laufende",
|
||||
"Running Polls": "Laufende Umfragen",
|
||||
"Save": "Speichern",
|
||||
"Save and Run": "Speichern und starten",
|
||||
"Scripting Engine": "Scripting-Umgebung",
|
||||
"Search": "Suche",
|
||||
"Search with {0}": "Mit {0} suchen",
|
||||
"Select": "Auswählen",
|
||||
"Send": "Senden",
|
||||
"Send Request": "Anfrage senden",
|
||||
"Separated by commas": "Durch Komma getrennt",
|
||||
"Servlet Interface": "Servlet-Schnittstelle",
|
||||
"Sessions": "Sitzungen",
|
||||
"Settings": "Einstellungen",
|
||||
"Setup": "Konfiguration",
|
||||
|
|
@ -356,11 +361,12 @@ global.messages['de'] = {
|
|||
"Terms and Conditions": "Nutzungsbedingungen",
|
||||
"Text": "Text",
|
||||
"Thanks, your vote was registered. You can change your mind until the poll is closed.": "Danke, Ihre Stimme wurde gezählt. Bis die Umfrage beendet ist, können Sie Ihre Meinung jederzeit ändern.",
|
||||
"The <a href=\"{0}\">robots.txt</a> file disallows access to this page.": "Die <a href=\"{0}\">robots.txt</a>-Datei verbietet den Zugriff auf diese Seite.",
|
||||
"The Management": "Die Direktion",
|
||||
"The URL endpoint for each of these APIs is located at": "Die Internet-Adresse für jede dieser Schnittstellen lautet",
|
||||
"The account data will be available for download from here within the next days.": "Die Kontodaten stehen demnächst hier zum Download bereit.",
|
||||
"The account is queued for export.": "Der Export der Kontodaten wird vorbereitet.",
|
||||
"The account {0} was deleted.": "Das Konto {0} wurde gelöscht.",
|
||||
"The account {0} is being deleted.": "Das Konto {0} wird gelöscht.",
|
||||
"The callback URL will be invoked as an HTTP POST request with the following parameters:": "Die Rückruf-Adresse wird mit folgenden Parametern durch die »HTTP Post«-Methode aufgerufen:",
|
||||
"The changes were saved successfully.": "Die Änderungen wurden erfolgreich gespeichert.",
|
||||
"The chosen name is too long. Please enter a shorter one.": "Der gewählte Name ist zu lang. Bitte geben Sie einen kürzeren ein.",
|
||||
|
|
@ -438,12 +444,14 @@ global.messages['de'] = {
|
|||
"Uptime": "Betriebszeit",
|
||||
"User": "Konto",
|
||||
"Username and e-mail address do not match.": "Für die angegebene Kombination aus Name und E-Mail-Adresse ist kein Konto registriert.",
|
||||
"Version": "Version",
|
||||
"Versions": "Versionen",
|
||||
"Via": "Via",
|
||||
"Virtual Machine": "Virtuelle Maschine",
|
||||
"Vote": "Abstimmen",
|
||||
"Warning!": "Warnung!",
|
||||
"We are sorry to inform you that the user {0} has cancelled your membership of the site {1} at {2}.": "Wir bedauern Ihnen mitzuteilen, dass Ihre Mitgliedschaft bei der Website {1} unter {2} von {0} beendet wurde.",
|
||||
"We have updated our terms and conditions. Please reaffirm you understand and accept the following:": "Wir haben unsere Nutzungsbedingungen geändert. Bitte bestätigen Sie im folgenden, dass Sie diese verstehen und akzeptieren:",
|
||||
"Webserver": "Webserver",
|
||||
"Welcome": "Willkommen",
|
||||
"Welcome to {0}, {1}!": "Willkommen bei {0}, {1}!",
|
||||
"Welcome to {0}, {1}. Have fun!": "Willkommen bei {0}, {1}. Viel Spaß!",
|
||||
|
|
@ -524,6 +532,8 @@ global.messages['de'] = {
|
|||
"e-mail": "E-Mail",
|
||||
"e.g. {0}": "z.B. {0}",
|
||||
"enabled": "aktiviert",
|
||||
"enforce": "erzwingen",
|
||||
"enforced": "erzwingen",
|
||||
"export": "Exportieren",
|
||||
"featured": "sichtbar",
|
||||
"file": "Datei",
|
||||
|
|
@ -573,6 +583,7 @@ global.messages['de'] = {
|
|||
"soon": "in Kürze",
|
||||
"stories": "Beiträge",
|
||||
"story": "Beitrag",
|
||||
"suggest": "vorschlagen",
|
||||
"tag": "Stichwort",
|
||||
"tags": "Stichworte",
|
||||
"tomorrow": "morgen",
|
||||
|
|
|
|||
6044
package-lock.json
generated
Normal file
6044
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
52
package.json
52
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "antville",
|
||||
"version": "1.618.0",
|
||||
"version": "1.6.0",
|
||||
"description": "Antville is an open source project aimed at the development of a high performance, feature rich weblog hosting software. It can easily host up to several thousands of sites (the number of weblogs is rather limited by the installation owner’s choice and server power than by software limitations).",
|
||||
"main": "main.js",
|
||||
"directories": {
|
||||
|
|
@ -8,20 +8,21 @@
|
|||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"add-claustra": "tools/claustra/add-claustra.js",
|
||||
"add-patch": "tools/updater/add-patch.js",
|
||||
"patch": "tools/updater/run-patch.js",
|
||||
"build:main.js": "browserify tools/client/main.js -o static/scripts/main.min.js",
|
||||
"build:editor.js": "browserify tools/client/editor.js -o static/scripts/editor.min.js",
|
||||
"build:gallery.js": "browserify tools/client/gallery.js -o static/scripts/gallery.min.js",
|
||||
"claustra:add": "tools/claustra/add-claustra.js",
|
||||
"build": "run-p --continue-on-error --print-label build:*",
|
||||
"build:main.js": "browserify tools/client/main.js --outfile static/scripts/main.min.js",
|
||||
"build:editor.js": "browserify tools/client/editor.js --outfile static/scripts/editor.min.js",
|
||||
"build:gallery.js": "browserify tools/client/gallery.js --outfile static/scripts/gallery.min.js",
|
||||
"build:main.css": "lessc --clean-css tools/client/main.less static/styles/main.min.css",
|
||||
"build:editor.css": "lessc --clean-css tools/client/editor.less static/styles/editor.min.css",
|
||||
"dev": "run-p -cl dev:*.js dev:*.css",
|
||||
"dev:main.js": "onchange tools/client/main.js -- yarn build:main.js",
|
||||
"dev:main.css": "onchange tools/client/main.less -- yarn build:main.css",
|
||||
"dev:editor.js": "onchange tools/client/editor.js -- yarn build:editor.js",
|
||||
"dev:editor.css": "onchange tools/client/editor.less -- yarn build:editor.css",
|
||||
"dev:gallery.js": "onchange tools/client/gallery.js -- yarn build:gallery.js"
|
||||
"patch:add": "tools/updater/add-patch.js",
|
||||
"patch:apply": "tools/updater/run-patch.js",
|
||||
"watch": "run-p --continue-on-error --print-label watch:*",
|
||||
"watch:main.js": "onchange tools/client/main.js -- npm run build:main.js",
|
||||
"watch:main.css": "onchange tools/client/main.less -- npm run build:main.css",
|
||||
"watch:editor.js": "onchange tools/client/editor.js -- npm run build:editor.js",
|
||||
"watch:editor.css": "onchange tools/client/editor.less -- npm run build:editor.css",
|
||||
"watch:gallery.js": "onchange tools/client/gallery.js -- npm run build:gallery.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -30,20 +31,21 @@
|
|||
"author": "The Antville People",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"alpinejs": "=2.3.3",
|
||||
"codemirror": "^5.53.2",
|
||||
"jquery": "^3.5.0",
|
||||
"alpinejs": "3.14.9",
|
||||
"codemirror": "5.65.19",
|
||||
"jquery": "3.7.1",
|
||||
"jquery-collagePlus": "github:antville/jquery-collagePlus#0.3.4",
|
||||
"js-md5": "^0.7.3",
|
||||
"uikit": "^2.27.4"
|
||||
"js-md5": "0.8.3",
|
||||
"uikit": "2.27.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "^16.5.1",
|
||||
"jsdoc": "^3.6.4",
|
||||
"less": "^3.11.1",
|
||||
"less-plugin-clean-css": "^1.5.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"onchange": "^7.0.2",
|
||||
"uglifyify": "^5.0.2"
|
||||
"browserify": "17.0.1",
|
||||
"generate-license-file": "4.0.0",
|
||||
"jsdoc": "4.0.4",
|
||||
"less": "4.3.0",
|
||||
"less-plugin-clean-css": "1.6.0",
|
||||
"npm-run-all2": "7.0.2",
|
||||
"onchange": "7.1.0",
|
||||
"uglifyify": "5.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
52
renovate.json
Normal file
52
renovate.json
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
"mergeConfidence:all-badges",
|
||||
"npm:unpublishSafe",
|
||||
":disableRateLimiting",
|
||||
":pinVersions",
|
||||
":semanticCommitsDisabled"
|
||||
],
|
||||
|
||||
"lockFileMaintenance": { "enabled": true },
|
||||
|
||||
"osvVulnerabilityAlerts": true,
|
||||
|
||||
"vulnerabilityAlerts": {
|
||||
"labels": ["security", "urgent"],
|
||||
"automerge": true
|
||||
},
|
||||
|
||||
"labels": ["dependency"],
|
||||
"gitIgnoredAuthors": ["29139614+renovate[bot]@users.noreply.github.com"],
|
||||
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "Label major version bumps",
|
||||
"matchUpdateTypes": ["major"],
|
||||
"addLabels": ["major"]
|
||||
},
|
||||
{
|
||||
"description": "Label packages required at runtime",
|
||||
"matchDepTypes": ["dependencies"],
|
||||
"addLabels": ["runtime"]
|
||||
},
|
||||
{
|
||||
"description": "Label Java packages",
|
||||
"matchManagers": ["gradle", "gradle-wrapper"],
|
||||
"addLabels": ["java"]
|
||||
},
|
||||
{
|
||||
"description": "Label JavaScript packages",
|
||||
"matchDatasources": ["node-version", "npm"],
|
||||
"addLabels": ["javascript"]
|
||||
},
|
||||
{
|
||||
"description": "Group CommonMark packages",
|
||||
"matchPackagePrefixes": ["org.commonmark"],
|
||||
"groupName": "CommonMark packages"
|
||||
}
|
||||
]
|
||||
}
|
||||
912
tests/robots.js
Normal file
912
tests/robots.js
Normal file
|
|
@ -0,0 +1,912 @@
|
|||
// Unit tests of the robots parser
|
||||
// Source: <https://github.com/samclarke/robots-parser/blob/master/test/Robots.js>
|
||||
// Copyright (c) 2014 Sam Clarke
|
||||
// MIT License (MIT)
|
||||
|
||||
// Run with `npx nyc --reporter=text-summary --reporter=html --reporter=lcovonly mocha tests/robots.js`
|
||||
|
||||
// Set up the test environment with Antville’s version of the robots parser
|
||||
const Robots = require('../code/Global/Robots.js');
|
||||
const robotsParser = (url, contents) => new Robots(url, contents);
|
||||
|
||||
const { expect } = require('chai');
|
||||
|
||||
function testRobots(url, contents, allowed, disallowed) {
|
||||
var robots = robotsParser(url, contents);
|
||||
|
||||
allowed.forEach(function (url) {
|
||||
expect(robots.isAllowed(url)).to.equal(true);
|
||||
});
|
||||
|
||||
disallowed.forEach(function (url) {
|
||||
expect(robots.isDisallowed(url)).to.equal(true);
|
||||
});
|
||||
}
|
||||
|
||||
describe('Robots', function () {
|
||||
it('should parse the disallow directive', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should parse the allow directive', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html',
|
||||
'Allow: /fish/test.html',
|
||||
'Allow: /test.html'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/fish/test.html',
|
||||
'http://www.example.com/Test.html',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should parse patterns', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish*.php',
|
||||
'Disallow: /*.dext$',
|
||||
'Disallow: /dir*'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/Fish.PHP',
|
||||
'http://www.example.com/Fish.dext1',
|
||||
'http://www.example.com/folder/dir.html',
|
||||
'http://www.example.com/folder/dir/test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish.php',
|
||||
'http://www.example.com/fishheads/catfish.php?parameters',
|
||||
'http://www.example.com/AnYthInG.dext',
|
||||
'http://www.example.com/Fish.dext.dext',
|
||||
'http://www.example.com/dir/test.html',
|
||||
'http://www.example.com/directory.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should have the correct order precedence for allow and disallow', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish*.php',
|
||||
'Allow: /fish/index.php',
|
||||
'Disallow: /test',
|
||||
'Allow: /test/',
|
||||
'Disallow: /aa/',
|
||||
'Allow: /aa/',
|
||||
'Allow: /bb/',
|
||||
'Disallow: /bb/',
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/test/index.html',
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/test/',
|
||||
'http://www.example.com/aa/',
|
||||
'http://www.example.com/bb/',
|
||||
'http://www.example.com/x/'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish.php',
|
||||
'http://www.example.com/fishheads/catfish.php?parameters',
|
||||
'http://www.example.com/test'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should have the correct order precedence for wildcards', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /*/',
|
||||
'Allow: /x/',
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/x/',
|
||||
'http://www.example.com/fish.php',
|
||||
'http://www.example.com/test'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/a/',
|
||||
'http://www.example.com/xx/',
|
||||
'http://www.example.com/test/index.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should parse lines delimitated by \\r', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html'
|
||||
].join('\r');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should parse lines delimitated by \\r\\n', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html'
|
||||
].join('\r\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
|
||||
it('should parse lines delimitated by mixed line endings', function () {
|
||||
var contents = [
|
||||
'User-agent: *\r',
|
||||
'Disallow: /fish/\r\n',
|
||||
'Disallow: /test.html\n\n'
|
||||
].join('');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should ignore rules that are not in a group', function () {
|
||||
var contents = [
|
||||
'Disallow: /secret.html',
|
||||
'Disallow: /test',
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/secret.html',
|
||||
'http://www.example.com/test/index.html',
|
||||
'http://www.example.com/test/'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, []);
|
||||
});
|
||||
|
||||
|
||||
it('should ignore comments', function () {
|
||||
var contents = [
|
||||
'#',
|
||||
'# This is a comment',
|
||||
'#',
|
||||
'User-agent: *',
|
||||
'# This is a comment',
|
||||
'Disallow: /fish/ # ignore',
|
||||
'# Disallow: fish',
|
||||
'Disallow: /test.html'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should ignore invalid lines', function () {
|
||||
var contents = [
|
||||
'invalid line',
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
':::::another invalid line:::::',
|
||||
'Disallow: /test.html',
|
||||
'Unknown: tule'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should ignore empty user-agent lines', function () {
|
||||
var contents = [
|
||||
'User-agent:',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html',
|
||||
'http://www.example.com/fish/index.php',
|
||||
'http://www.example.com/fish/',
|
||||
'http://www.example.com/test.html'
|
||||
];
|
||||
|
||||
var disallowed = [];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should support groups with multiple user agents (case insensitive)', function () {
|
||||
var contents = [
|
||||
'User-agent: agenta',
|
||||
'User-agent: agentb',
|
||||
'Disallow: /fish',
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.isAllowed("http://www.example.com/fish", "agenta")).to.equal(false);
|
||||
});
|
||||
|
||||
it('should return undefined for invalid urls', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /secret.html',
|
||||
'Disallow: /test',
|
||||
].join('\n');
|
||||
|
||||
var invalidUrls = [
|
||||
'http://example.com/secret.html',
|
||||
'http://ex ample.com/secret.html',
|
||||
'http://www.example.net/test/index.html',
|
||||
'http://www.examsple.com/test/',
|
||||
'example.com/test/',
|
||||
':::::;;`\\|/.example.com/test/'
|
||||
];
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
invalidUrls.forEach(function (url) {
|
||||
expect(robots.isAllowed(url)).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle Unicode, urlencoded and punycode URLs', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /secret.html',
|
||||
'Disallow: /test',
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.münich.com/index.html',
|
||||
'http://www.xn--mnich-kva.com/index.html',
|
||||
'http://www.m%C3%BCnich.com/index.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.münich.com/secret.html',
|
||||
'http://www.xn--mnich-kva.com/secret.html',
|
||||
'http://www.m%C3%BCnich.com/secret.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.münich.com/robots.txt', contents, allowed, disallowed);
|
||||
testRobots('http://www.xn--mnich-kva.com/robots.txt', contents, allowed, disallowed);
|
||||
testRobots('http://www.m%C3%BCnich.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should handle Unicode and urlencoded paths', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /%CF%80',
|
||||
'Disallow: /%e2%9d%83',
|
||||
'Disallow: /%a%a',
|
||||
'Disallow: /💩',
|
||||
'Disallow: /✼*t$',
|
||||
'Disallow: /%E2%9C%A4*t$',
|
||||
'Disallow: /✿%a',
|
||||
'Disallow: /http%3A%2F%2Fexample.org'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/✼testing',
|
||||
'http://www.example.com/%E2%9C%BCtesting',
|
||||
'http://www.example.com/✤testing',
|
||||
'http://www.example.com/%E2%9C%A4testing',
|
||||
'http://www.example.com/http://example.org',
|
||||
'http://www.example.com/http:%2F%2Fexample.org'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/%CF%80',
|
||||
'http://www.example.com/%CF%80/index.html',
|
||||
'http://www.example.com/π',
|
||||
'http://www.example.com/π/index.html',
|
||||
'http://www.example.com/%e2%9d%83',
|
||||
'http://www.example.com/%E2%9D%83/index.html',
|
||||
'http://www.example.com/❃',
|
||||
'http://www.example.com/❃/index.html',
|
||||
'http://www.example.com/%F0%9F%92%A9',
|
||||
'http://www.example.com/%F0%9F%92%A9/index.html',
|
||||
'http://www.example.com/💩',
|
||||
'http://www.example.com/💩/index.html',
|
||||
'http://www.example.com/%a%a',
|
||||
'http://www.example.com/%a%a/index.html',
|
||||
'http://www.example.com/✼test',
|
||||
'http://www.example.com/%E2%9C%BCtest',
|
||||
'http://www.example.com/✤test',
|
||||
'http://www.example.com/%E2%9C%A4testt',
|
||||
'http://www.example.com/✿%a',
|
||||
'http://www.example.com/%E2%9C%BF%atest',
|
||||
'http://www.example.com/http%3A%2F%2Fexample.org'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should handle lone high / low surrogates', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /\uD800',
|
||||
'Disallow: /\uDC00'
|
||||
].join('\n');
|
||||
|
||||
// These are invalid so can't be disallowed
|
||||
var allowed = [
|
||||
'http://www.example.com/\uDC00',
|
||||
'http://www.example.com/\uD800'
|
||||
];
|
||||
|
||||
var disallowed = [];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should ignore host case', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /secret.html',
|
||||
'Disallow: /test',
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/index.html',
|
||||
'http://www.ExAmPlE.com/index.html',
|
||||
'http://www.EXAMPLE.com/index.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/secret.html',
|
||||
'http://www.ExAmPlE.com/secret.html',
|
||||
'http://www.EXAMPLE.com/secret.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.eXample.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should handle relative paths', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish',
|
||||
'Allow: /fish/test',
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('/robots.txt', contents);
|
||||
expect(robots.isAllowed('/fish/test')).to.equal(true);
|
||||
expect(robots.isAllowed('/fish')).to.equal(false);
|
||||
});
|
||||
|
||||
it('should not allow relative paths if domain specified', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish',
|
||||
'Allow: /fish/test',
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
expect(robots.isAllowed('/fish/test')).to.equal(undefined);
|
||||
expect(robots.isAllowed('/fish')).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should not treat invalid robots.txt URLs as relative', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish',
|
||||
'Allow: /fish/test',
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('https://ex ample.com/robots.txt', contents);
|
||||
expect(robots.isAllowed('/fish/test')).to.equal(undefined);
|
||||
expect(robots.isAllowed('/fish')).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should not allow URls if domain specified and robots.txt is relative', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish',
|
||||
'Allow: /fish/test',
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('/robots.txt', contents);
|
||||
expect(robots.isAllowed('http://www.example.com/fish/test')).to.equal(undefined);
|
||||
expect(robots.isAllowed('http://www.example.com/fish')).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should allow all if empty robots.txt', function () {
|
||||
var allowed = [
|
||||
'http://www.example.com/secret.html',
|
||||
'http://www.example.com/test/index.html',
|
||||
'http://www.example.com/test/'
|
||||
];
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', '');
|
||||
|
||||
allowed.forEach(function (url) {
|
||||
expect(robots.isAllowed(url)).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should treat null as allowing all', function () {
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', null);
|
||||
|
||||
expect(robots.isAllowed("http://www.example.com/", "userAgent")).to.equal(true);
|
||||
expect(robots.isAllowed("http://www.example.com/")).to.equal(true);
|
||||
});
|
||||
|
||||
it('should handle invalid robots.txt urls', function () {
|
||||
var contents = [
|
||||
'user-agent: *',
|
||||
'disallow: /',
|
||||
|
||||
'host: www.example.com',
|
||||
'sitemap: /sitemap.xml'
|
||||
].join('\n');
|
||||
|
||||
var sitemapUrls = [
|
||||
undefined,
|
||||
null,
|
||||
'null',
|
||||
':/wom/test/'
|
||||
];
|
||||
|
||||
sitemapUrls.forEach(function (url) {
|
||||
var robots = robotsParser(url, contents);
|
||||
expect(robots.isAllowed('http://www.example.com/index.html')).to.equal(undefined);
|
||||
expect(robots.getPreferredHost()).to.equal('www.example.com');
|
||||
expect(robots.getSitemaps()).to.eql(['/sitemap.xml']);
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse the crawl-delay directive', function () {
|
||||
var contents = [
|
||||
'user-agent: a',
|
||||
'crawl-delay: 1',
|
||||
|
||||
'user-agent: b',
|
||||
'disallow: /d',
|
||||
|
||||
'user-agent: c',
|
||||
'user-agent: d',
|
||||
'crawl-delay: 10'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getCrawlDelay('a')).to.equal(1);
|
||||
expect(robots.getCrawlDelay('b')).to.equal(undefined);
|
||||
expect(robots.getCrawlDelay('c')).to.equal(10);
|
||||
expect(robots.getCrawlDelay('d')).to.equal(10);
|
||||
expect(robots.getCrawlDelay()).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should ignore invalid crawl-delay directives', function () {
|
||||
var contents = [
|
||||
'user-agent: a',
|
||||
'crawl-delay: 1.2.1',
|
||||
|
||||
'user-agent: b',
|
||||
'crawl-delay: 1.a0',
|
||||
|
||||
'user-agent: c',
|
||||
'user-agent: d',
|
||||
'crawl-delay: 10a'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getCrawlDelay('a')).to.equal(undefined);
|
||||
expect(robots.getCrawlDelay('b')).to.equal(undefined);
|
||||
expect(robots.getCrawlDelay('c')).to.equal(undefined);
|
||||
expect(robots.getCrawlDelay('d')).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should parse the sitemap directive', function () {
|
||||
var contents = [
|
||||
'user-agent: a',
|
||||
'crawl-delay: 1',
|
||||
'sitemap: http://example.com/test.xml',
|
||||
|
||||
'user-agent: b',
|
||||
'disallow: /d',
|
||||
|
||||
'sitemap: /sitemap.xml',
|
||||
'sitemap: http://example.com/test/sitemap.xml '
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getSitemaps()).to.eql([
|
||||
'http://example.com/test.xml',
|
||||
'/sitemap.xml',
|
||||
'http://example.com/test/sitemap.xml'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse the host directive', function () {
|
||||
var contents = [
|
||||
'user-agent: a',
|
||||
'crawl-delay: 1',
|
||||
'host: www.example.net',
|
||||
|
||||
'user-agent: b',
|
||||
'disallow: /d',
|
||||
|
||||
'host: example.com'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getPreferredHost()).to.equal('example.com');
|
||||
});
|
||||
|
||||
it('should parse empty and invalid directives', function () {
|
||||
var contents = [
|
||||
'user-agent:',
|
||||
'user-agent:::: a::',
|
||||
'crawl-delay:',
|
||||
'crawl-delay:::: 0:',
|
||||
'host:',
|
||||
'host:: example.com',
|
||||
'sitemap:',
|
||||
'sitemap:: site:map.xml',
|
||||
'disallow:',
|
||||
'disallow::: /:',
|
||||
'allow:',
|
||||
'allow::: /:',
|
||||
].join('\n');
|
||||
|
||||
robotsParser('http://www.example.com/robots.txt', contents);
|
||||
});
|
||||
|
||||
it('should treat only the last host directive as valid', function () {
|
||||
var contents = [
|
||||
'user-agent: a',
|
||||
'crawl-delay: 1',
|
||||
'host: www.example.net',
|
||||
|
||||
'user-agent: b',
|
||||
'disallow: /d',
|
||||
|
||||
'host: example.net',
|
||||
'host: example.com'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getPreferredHost()).to.equal('example.com');
|
||||
});
|
||||
|
||||
it('should return null when there is no host directive', function () {
|
||||
var contents = [
|
||||
'user-agent: a',
|
||||
'crawl-delay: 1',
|
||||
|
||||
'user-agent: b',
|
||||
'disallow: /d',
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getPreferredHost()).to.equal(null);
|
||||
});
|
||||
|
||||
it('should fallback to * when a UA has no rules of its own', function () {
|
||||
var contents = [
|
||||
'user-agent: *',
|
||||
'crawl-delay: 1',
|
||||
|
||||
'user-agent: b',
|
||||
'crawl-delay: 12',
|
||||
|
||||
'user-agent: c',
|
||||
'user-agent: d',
|
||||
'crawl-delay: 10'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getCrawlDelay('should-fall-back')).to.equal(1);
|
||||
expect(robots.getCrawlDelay('d')).to.equal(10);
|
||||
expect(robots.getCrawlDelay('dd')).to.equal(1);
|
||||
});
|
||||
|
||||
it('should not fallback to * when a UA has rules', function () {
|
||||
var contents = [
|
||||
'user-agent: *',
|
||||
'crawl-delay: 1',
|
||||
|
||||
'user-agent: b',
|
||||
'disallow:'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getCrawlDelay('b')).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should handle UAs with object property names', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish',
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
expect(robots.isAllowed('http://www.example.com/fish', 'constructor')).to.equal(false);
|
||||
expect(robots.isAllowed('http://www.example.com/fish', '__proto__')).to.equal(false);
|
||||
});
|
||||
|
||||
it('should ignore version numbers in the UA string', function () {
|
||||
var contents = [
|
||||
'user-agent: *',
|
||||
'crawl-delay: 1',
|
||||
|
||||
'user-agent: b',
|
||||
'crawl-delay: 12',
|
||||
|
||||
'user-agent: c',
|
||||
'user-agent: d',
|
||||
'crawl-delay: 10'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getCrawlDelay('should-fall-back/1.0.0')).to.equal(1);
|
||||
expect(robots.getCrawlDelay('d/12')).to.equal(10);
|
||||
expect(robots.getCrawlDelay('dd / 0-32-3')).to.equal(1);
|
||||
expect(robots.getCrawlDelay('b / 1.0')).to.equal(12);
|
||||
});
|
||||
|
||||
|
||||
it('should return the line number of the matching directive', function () {
|
||||
var contents = [
|
||||
'',
|
||||
'User-agent: *',
|
||||
'',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html',
|
||||
'Allow: /fish/test.html',
|
||||
'Allow: /test.html',
|
||||
'',
|
||||
'User-agent: a',
|
||||
'allow: /',
|
||||
'',
|
||||
'User-agent: b',
|
||||
'disallow: /test',
|
||||
'disallow: /t*t',
|
||||
'',
|
||||
'User-agent: c',
|
||||
'Disallow: /fish*.php',
|
||||
'Allow: /fish/index.php'
|
||||
].join('\n');
|
||||
|
||||
var robots = robotsParser('http://www.example.com/robots.txt', contents);
|
||||
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/fish')).to.equal(-1);
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/fish/test.html')).to.equal(6);
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/Test.html')).to.equal(-1);
|
||||
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/fish/index.php')).to.equal(4);
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/fish/')).to.equal(4);
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/test.html')).to.equal(7);
|
||||
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/test.html', 'a')).to.equal(10);
|
||||
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/fish.php', 'c')).to.equal(17);
|
||||
expect(robots.getMatchingLineNumber('http://www.example.com/fish/index.php', 'c')).to.equal(18);
|
||||
});
|
||||
|
||||
it('should handle large wildcards efficiently', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /' + '*'.repeat(2048) + '.html',
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com/' + 'sub'.repeat(2048) + 'folder/index.php',
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/secret.html'
|
||||
];
|
||||
|
||||
const start = Date.now();
|
||||
testRobots('http://www.eXample.com/robots.txt', contents, allowed, disallowed);
|
||||
const end = Date.now();
|
||||
|
||||
// Should take less than 500 ms (high to allow for variableness of
|
||||
// machines running the test, should normally be much less)
|
||||
expect(end - start).to.be.lessThan(500);
|
||||
});
|
||||
|
||||
it('should honor given port number', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com:8080/fish',
|
||||
'http://www.example.com:8080/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com/fish',
|
||||
'http://www.example.com/Test.html',
|
||||
'http://www.example.com:80/fish',
|
||||
'http://www.example.com:80/Test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com:8080/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should default to port 80 for http: if no port given', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'http://www.example.com:80/fish',
|
||||
'http://www.example.com:80/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com:443/fish',
|
||||
'http://www.example.com:443/Test.html',
|
||||
'http://www.example.com:80/fish/index.php',
|
||||
'http://www.example.com:80/fish/',
|
||||
'http://www.example.com:80/test.html'
|
||||
];
|
||||
|
||||
testRobots('http://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should default to port 443 for https: if no port given', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /fish/',
|
||||
'Disallow: /test.html'
|
||||
].join('\n');
|
||||
|
||||
var allowed = [
|
||||
'https://www.example.com:443/fish',
|
||||
'https://www.example.com:443/Test.html',
|
||||
'https://www.example.com/fish',
|
||||
'https://www.example.com/Test.html'
|
||||
];
|
||||
|
||||
var disallowed = [
|
||||
'http://www.example.com:80/fish',
|
||||
'http://www.example.com:80/Test.html',
|
||||
'http://www.example.com:443/fish/index.php',
|
||||
'http://www.example.com:443/fish/',
|
||||
'http://www.example.com:443/test.html'
|
||||
];
|
||||
|
||||
testRobots('https://www.example.com/robots.txt', contents, allowed, disallowed);
|
||||
});
|
||||
|
||||
it('should not be disallowed when wildcard is used in explicit mode', function () {
|
||||
var contents = [
|
||||
'User-agent: *',
|
||||
'Disallow: /',
|
||||
].join('\n')
|
||||
|
||||
var url = 'https://www.example.com/hello'
|
||||
var userAgent = 'SomeBot';
|
||||
var robots = robotsParser(url, contents);
|
||||
|
||||
expect(robots.isExplicitlyDisallowed(url, userAgent)).to.equal(false)
|
||||
});
|
||||
|
||||
it('should be disallowed when user agent equal robots rule in explicit mode', function () {
|
||||
var contents = [
|
||||
'User-agent: SomeBot',
|
||||
'Disallow: /',
|
||||
].join('\n')
|
||||
|
||||
var url = 'https://www.example.com/hello'
|
||||
var userAgent = 'SomeBot';
|
||||
var robots = robotsParser(url, contents);
|
||||
|
||||
expect(robots.isExplicitlyDisallowed(url, userAgent)).to.equal(true)
|
||||
});
|
||||
|
||||
it('should return undefined when given an invalid URL in explicit mode', function () {
|
||||
var contents = [
|
||||
'User-agent: SomeBot',
|
||||
'Disallow: /',
|
||||
].join('\n')
|
||||
|
||||
var url = 'https://www.example.com/hello'
|
||||
var userAgent = 'SomeBot';
|
||||
var robots = robotsParser('http://example.com', contents);
|
||||
|
||||
expect(robots.isExplicitlyDisallowed(url, userAgent)).to.equal(undefined)
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
id 'com.github.jk1.dependency-license-report' version '1.13'
|
||||
id 'com.github.jk1.dependency-license-report' version '2.9'
|
||||
}
|
||||
|
||||
def helmaInstallDir = "${rootProject.buildDir}/install/antclick"
|
||||
|
|
@ -11,7 +11,7 @@ clean {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.h2database:h2:1.4.200'
|
||||
implementation 'com.h2database:h2:2.2.220'
|
||||
}
|
||||
|
||||
configurations {
|
||||
|
|
@ -44,7 +44,7 @@ task downloadHelma {
|
|||
}
|
||||
|
||||
// See https://docs.gradle.org/current/userguide/working_with_files.html#sec:unpacking_archives_example
|
||||
task installHelma(type: Copy) {
|
||||
tasks.register('installHelma', Copy) {
|
||||
description 'Download and extract the Helma package.'
|
||||
group 'installation'
|
||||
dependsOn 'downloadHelma'
|
||||
|
|
@ -70,10 +70,10 @@ task installHelma(type: Copy) {
|
|||
} into outputDir
|
||||
}
|
||||
|
||||
task installAntville(type: Copy) {
|
||||
tasks.register('installAntville', Copy) {
|
||||
description 'Install Antville as Helma application'
|
||||
group 'installation'
|
||||
dependsOn 'installHelma'
|
||||
dependsOn ':assemble', ':installAntville', 'installHelma', 'buildLicenses'
|
||||
|
||||
def appsProperties = "$antvilleBuildDir/tools/antclick/apps.properties"
|
||||
def outputDir = "$helmaInstallDir/apps/antville"
|
||||
|
|
@ -95,7 +95,7 @@ task installAntville(type: Copy) {
|
|||
}
|
||||
}
|
||||
|
||||
task installJars(type: Copy) {
|
||||
tasks.register('installJars', Copy) {
|
||||
description 'Download additional JAR libraries.'
|
||||
group 'Installation'
|
||||
dependsOn 'installHelma'
|
||||
|
|
@ -109,11 +109,11 @@ task installJars(type: Copy) {
|
|||
into outputDir
|
||||
}
|
||||
|
||||
task buildDatabase(type: JavaExec) {
|
||||
tasks.register('buildDatabase', JavaExec) {
|
||||
description 'Builds the H2 SQL database file.'
|
||||
group 'build'
|
||||
|
||||
def inputFile = "$antvilleInstallDir/db/postgre.sql"
|
||||
def inputFile = "${project.rootDir}/db/postgre.sql"
|
||||
def outputDir = "$helmaInstallDir/db"
|
||||
|
||||
inputs.file inputFile
|
||||
|
|
@ -124,12 +124,12 @@ task buildDatabase(type: JavaExec) {
|
|||
}
|
||||
|
||||
classpath = files(h2Jar)
|
||||
main = 'org.h2.tools.RunScript'
|
||||
mainClass = 'org.h2.tools.RunScript'
|
||||
|
||||
args = [
|
||||
'-continueOnError',
|
||||
'-script', inputFile,
|
||||
'-url', "jdbc:h2:$outputDir/antville",
|
||||
'-url', "jdbc:h2:$outputDir/antville;mode=postgresql;non_keywords=value",
|
||||
'-user', 'antville',
|
||||
'-password', 'antville'
|
||||
]
|
||||
|
|
@ -138,12 +138,12 @@ task buildDatabase(type: JavaExec) {
|
|||
ignoreExitValue true
|
||||
}
|
||||
|
||||
task buildLicenses(type: Copy) {
|
||||
tasks.register('buildLicenses', Copy) {
|
||||
description 'Build license files from server-side dependecies.'
|
||||
group 'build'
|
||||
dependsOn 'installJars'
|
||||
|
||||
def licensesDir = "$antvilleInstallDir/licenses"
|
||||
def licensesDir = "$helmaInstallDir/licenses"
|
||||
|
||||
inputs.files generateLicenseReport
|
||||
outputs.dir licensesDir
|
||||
|
|
@ -161,10 +161,10 @@ task assembleDist {
|
|||
dependsOn 'distTar'
|
||||
}
|
||||
|
||||
task runH2Console(type: JavaExec) {
|
||||
tasks.register('runH2Console', JavaExec) {
|
||||
description 'Runs the H2 SQL database console.'
|
||||
group 'Help'
|
||||
dependsOn 'installJars'
|
||||
dependsOn 'buildDatabase'
|
||||
|
||||
def h2Jar = configurations.library.files.find { jar ->
|
||||
jar.name.startsWith('h2')
|
||||
|
|
@ -177,12 +177,9 @@ task runH2Console(type: JavaExec) {
|
|||
'-user', 'antville',
|
||||
'-password', 'antville'
|
||||
]
|
||||
|
||||
// standardOutput = new ByteArrayOutputStream()
|
||||
// ignoreExitValue true
|
||||
}
|
||||
|
||||
task distZip(type: Zip) {
|
||||
tasks.register('distZip', Zip) {
|
||||
description 'Creates the AntClick download package as Zip file.'
|
||||
group 'distribution'
|
||||
dependsOn 'assemble'
|
||||
|
|
@ -194,25 +191,31 @@ task distZip(type: Zip) {
|
|||
inputs.dir helmaInstallDir
|
||||
outputs.file "$outputDir/$outputFile"
|
||||
|
||||
from helmaInstallDir
|
||||
from (helmaInstallDir) {
|
||||
into "antclick-${version}"
|
||||
}
|
||||
|
||||
destinationDirectory = file(outputDir)
|
||||
archiveFileName = outputFile
|
||||
}
|
||||
|
||||
task distTar(type: Tar) {
|
||||
description 'Creates the AntClick download package as Bzip2 file.'
|
||||
tasks.register('distTar', Tar) {
|
||||
description 'Creates the AntClick download package as compressed archive file.'
|
||||
group 'distribution'
|
||||
dependsOn 'assemble'
|
||||
|
||||
def version = project.distVersion()
|
||||
def outputDir = "${rootProject.buildDir}/distributions"
|
||||
def outputFile = "antclick-${version}.tbz"
|
||||
def outputFile = "antclick-${version}.tgz"
|
||||
|
||||
inputs.dir helmaInstallDir
|
||||
outputs.file "$outputDir/$outputFile"
|
||||
|
||||
from helmaInstallDir
|
||||
compression = Compression.BZIP2
|
||||
from (helmaInstallDir) {
|
||||
into "antclick-${version}"
|
||||
}
|
||||
|
||||
compression = Compression.GZIP
|
||||
destinationDirectory = file(outputDir)
|
||||
archiveFileName = outputFile
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
window.CodeMirror = require('codemirror/lib/codemirror');
|
||||
window.marked = require('marked/lib/marked');
|
||||
window.marked = require('marked/lib/marked.cjs').marked;
|
||||
|
||||
require('codemirror/mode/css/css');
|
||||
require('codemirror/mode/gfm/gfm');
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
window.$ = window.jQuery = require('jquery');
|
||||
window.Alpine = require('alpinejs').default;
|
||||
|
||||
require('uikit/dist/js/uikit');
|
||||
require('uikit/dist/js/components/form-password');
|
||||
require('uikit/dist/js/components/tooltip');
|
||||
require('uikit/dist/js/components/upload');
|
||||
|
||||
require('alpinejs');
|
||||
|
||||
const Antville = window.Antville = {};
|
||||
|
||||
Antville.hash = require('js-md5/src/md5');
|
||||
|
|
@ -126,7 +125,7 @@ Antville.decode = function(str) {
|
|||
Antville.Referrer = function(url, text, count) {
|
||||
this.url = url;
|
||||
|
||||
const re = new RegExp('https?://(?:www\.)?');
|
||||
const re = new RegExp('https?://(?:www\\.)?');
|
||||
|
||||
this.text = String(text).replace(re, '');
|
||||
|
||||
|
|
@ -262,3 +261,5 @@ Antville.http = (method, url, options) => {
|
|||
httpClient.send(_data);
|
||||
return this;
|
||||
};
|
||||
|
||||
Alpine.start();
|
||||
|
|
|
|||
|
|
@ -3,3 +3,12 @@
|
|||
@import (inline) 'node_modules/uikit/dist/css/components/tooltip.almost-flat.css';
|
||||
|
||||
@import './sprites';
|
||||
|
||||
summary {
|
||||
display: revert;
|
||||
}
|
||||
|
||||
// TODO: Actually a compatibility fix. See https://tobi.antville.org/stories/2317931/
|
||||
td[nowrap][width]:not([width$="%"]) {
|
||||
white-space: normal
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ var ResultWrapper = function(result) {
|
|||
}
|
||||
|
||||
this.update = function() {
|
||||
for each (var key in columns) {
|
||||
for (let key of columns) {
|
||||
this.values[key] = result.getColumnItem(key);
|
||||
}
|
||||
return;
|
||||
|
|
@ -303,18 +303,18 @@ var execute = function(sql /*, value1, ..., valueN */) {
|
|||
|
||||
var archive = function() {
|
||||
var staticDir = new helma.File(app.dir + "/../static");
|
||||
for each (var siteName in staticDir.list()) {
|
||||
for (let siteName of staticDir.list()) {
|
||||
var site = siteName !== "www" ? root.get(siteName) : root;
|
||||
if (!site) {
|
||||
continue;
|
||||
}
|
||||
var dir = new helma.File(staticDir, siteName + "/layouts");
|
||||
for each (var layoutName in dir.list()) {
|
||||
for (let layoutName of dir.list()) {
|
||||
if (layoutName.startsWith(".")) {
|
||||
continue;
|
||||
}
|
||||
var layout = new Layout(site);
|
||||
for each (var image in dir.listRecursive(/\.(jpg|gif|png)$/)) {
|
||||
for (let image of dir.listRecursive(/\.(jpg|gif|png)$/)) {
|
||||
var name = image.split("/").pop().split(".")[0];
|
||||
retrieve(query("archive", name, layoutName, siteName));
|
||||
traverse(function() {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ convert.layoutImages = function() {
|
|||
|
||||
var fpath = antville().properties.staticPath;
|
||||
var files = [metadata.fileName, metadata.thumbnailName];
|
||||
for each (var fname in files) {
|
||||
for (let fname of files) {
|
||||
var source = new helma.File(fpath + "/layouts/" +
|
||||
this.parentLayout, fname);
|
||||
var layoutDir = new helma.File(fpath + this.SITE_ALIAS +
|
||||
|
|
@ -216,7 +216,7 @@ convert.sites = function() {
|
|||
metadata.locale += "_" + metadata.country;
|
||||
}
|
||||
var mode = metadata.usercontrib ? 'open' : this.mode;
|
||||
for each (var key in ["enableping", "usercontrib", "archive",
|
||||
for (let key of ["enableping", "usercontrib", "archive",
|
||||
"discussions", "days", "shortdateformat", "longdateformat",
|
||||
"linkcolor", "alinkcolor", "vlinkcolor", "smallcolor",
|
||||
"titlecolor", "titlefont", "textfont", "textcolor", "smallsize",
|
||||
|
|
@ -459,7 +459,7 @@ convert.skins = function() {
|
|||
}
|
||||
destination.makeDirectory();
|
||||
var files = source.list();
|
||||
for each (var fname in files) {
|
||||
for (let fname of files) {
|
||||
(new helma.File(source, fname)).hardCopy(new helma.File(destination, fname));
|
||||
}
|
||||
}
|
||||
|
|
@ -509,7 +509,7 @@ convert.skins = function() {
|
|||
appSkins[prototype] || (appSkins[prototype] = {});
|
||||
var skin = createSkin(skinfiles[prototype][prototype]);
|
||||
var subskins = skin.getSubskinNames();
|
||||
for each (var name in subskins) {
|
||||
for (let name of subskins) {
|
||||
appSkins[prototype][name] = skin.getSubskin(name).getSource();
|
||||
}
|
||||
}
|
||||
|
|
@ -574,7 +574,7 @@ convert.skins = function() {
|
|||
}
|
||||
if (parent !== null && parent !== undefined) {
|
||||
execute("update skin set source = '" +
|
||||
clean(parent).replace(/'/g, "\\'") + "' where " +
|
||||
clean(parent).replace(/('|\\)/g, "\\$1") + "' where " +
|
||||
'id = ' + this.id);
|
||||
}
|
||||
});
|
||||
|
|
@ -608,7 +608,7 @@ convert.root = function() {
|
|||
}
|
||||
var dir = new helma.File(staticDir, this.name);
|
||||
var files = dir.list();
|
||||
for each (fname in files) {
|
||||
for (let fname of files) {
|
||||
var source = new helma.File(dir, fname);
|
||||
var dest = new helma.File(staticDir, "www/" + fname);
|
||||
log("Rename " + source + " to " + dest);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
var sql = new Sql();
|
||||
var sql2 = new Sql();
|
||||
for each (let table in ["file", "image", "tag"]) {
|
||||
for (let table of ["file", "image", "tag"]) {
|
||||
sql.retrieve("select * from $0 where name like '%?%';", table);
|
||||
sql.traverse(function() {
|
||||
var name = "-".repeat(this.name.count("?"));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue