mirror of
https://github.com/LdesignMedia/moodle-availability_ipaddress.git
synced 2026-05-17 05:48:41 +02:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d65fd40621 | ||
|
|
4632008dfb | ||
|
|
c91646df81 | ||
|
|
d2c231e657 | ||
|
|
700820480d | ||
|
|
e91befdf10 | ||
|
|
3f5f4ee6cb | ||
|
|
cccebb6c15 | ||
|
|
af275d1699 | ||
|
|
5dce3ed488 | ||
|
|
e74ed2ae69 | ||
|
|
15ba2b295b | ||
|
|
fe3a5b2302 | ||
|
|
26669e73f1 | ||
|
|
e7e5cbe91d | ||
|
|
d56173f6b8 | ||
|
|
c9b5e6cb7d | ||
|
|
fcfa4eb773 | ||
|
|
b98a63e1f4 | ||
|
|
a836731b24 | ||
|
|
c2f5e7e22c | ||
|
|
761d643479 | ||
|
|
5ef0c811c0 | ||
|
|
6633439651 | ||
|
|
4b4fced58b | ||
|
|
8ae54193d2 | ||
|
|
26bbc1cbdb | ||
|
|
5d615ddb24 | ||
|
|
5e2566fbba | ||
|
|
96c93c4096 | ||
|
|
d302aa626a | ||
|
|
787dba81f0 | ||
|
|
f24ef270a5 | ||
|
|
0e7ec99338 | ||
|
|
3b489f3406 | ||
|
|
84ea4a9165 | ||
|
|
9d6f7e51b9 | ||
|
|
855f8fecaa | ||
|
|
30459042d7 | ||
|
|
a6b22c06e3 | ||
|
|
e44acd4a7f | ||
|
|
4754b1e03e | ||
|
|
54ac7002d8 | ||
|
|
5d25933dfb | ||
|
|
28f6d4603b | ||
|
|
527831df6b | ||
|
|
d41c96e035 | ||
|
|
29a4857d8a | ||
|
|
163ee678a5 | ||
|
|
c5a8b00380 |
214
.eslintrc
214
.eslintrc
@@ -1,214 +0,0 @@
|
|||||||
{
|
|
||||||
'plugins': [
|
|
||||||
'promise',
|
|
||||||
],
|
|
||||||
'env': {
|
|
||||||
'browser': true,
|
|
||||||
'amd': true
|
|
||||||
},
|
|
||||||
'globals': {
|
|
||||||
'M': true,
|
|
||||||
'Y': true
|
|
||||||
},
|
|
||||||
'rules': {
|
|
||||||
// See http://eslint.org/docs/rules/ for all rules and explanations of all
|
|
||||||
// rules.
|
|
||||||
|
|
||||||
// === Possible Errors ===
|
|
||||||
'comma-dangle': 'off',
|
|
||||||
'no-compare-neg-zero': 'error',
|
|
||||||
'no-cond-assign': 'error',
|
|
||||||
'no-console': 'error',
|
|
||||||
'no-constant-condition': 'error',
|
|
||||||
'no-control-regex': 'error',
|
|
||||||
'no-debugger': 'error',
|
|
||||||
'no-dupe-args': 'error',
|
|
||||||
'no-dupe-keys': 'error',
|
|
||||||
'no-duplicate-case': 'error',
|
|
||||||
'no-empty': 'warn',
|
|
||||||
'no-empty-character-class': 'error',
|
|
||||||
'no-ex-assign': 'error',
|
|
||||||
'no-extra-boolean-cast': 'error',
|
|
||||||
'no-extra-parens': 'off',
|
|
||||||
'no-extra-semi': 'error',
|
|
||||||
'no-func-assign': 'error',
|
|
||||||
'no-inner-declarations': 'error',
|
|
||||||
'no-invalid-regexp': 'error',
|
|
||||||
'no-irregular-whitespace': 'error',
|
|
||||||
'no-obj-calls': 'error',
|
|
||||||
'no-prototype-builtins': 'off',
|
|
||||||
'no-regex-spaces': 'error',
|
|
||||||
'no-sparse-arrays': 'error',
|
|
||||||
'no-unexpected-multiline': 'error',
|
|
||||||
'no-unreachable': 'warn',
|
|
||||||
'no-unsafe-finally': 'error',
|
|
||||||
'no-unsafe-negation': 'error',
|
|
||||||
'use-isnan': 'error',
|
|
||||||
'valid-jsdoc': ['warn', { 'requireReturn': false, 'requireParamDescription': false, 'requireReturnDescription': false }],
|
|
||||||
'valid-typeof': 'error',
|
|
||||||
|
|
||||||
// === Best Practices ===
|
|
||||||
// (these mostly match our jshint config)
|
|
||||||
'array-callback-return': 'warn',
|
|
||||||
'block-scoped-var': 'warn',
|
|
||||||
'complexity': 'warn',
|
|
||||||
'consistent-return': 'warn',
|
|
||||||
'curly': 'error',
|
|
||||||
'dot-notation': 'warn',
|
|
||||||
'no-alert': 'warn',
|
|
||||||
'no-caller': 'error',
|
|
||||||
'no-case-declarations': 'error',
|
|
||||||
'no-div-regex': 'error',
|
|
||||||
'no-empty-pattern': 'error',
|
|
||||||
'no-empty-function': 'warn',
|
|
||||||
'no-eq-null': 'error',
|
|
||||||
'no-eval': 'error',
|
|
||||||
'no-extend-native': 'error',
|
|
||||||
'no-extra-bind': 'warn',
|
|
||||||
'no-fallthrough': 'error',
|
|
||||||
'no-floating-decimal': 'warn',
|
|
||||||
'no-global-assign': 'warn',
|
|
||||||
'no-implied-eval': 'error',
|
|
||||||
'no-invalid-this': 'error',
|
|
||||||
'no-iterator': 'error',
|
|
||||||
'no-labels': 'error',
|
|
||||||
'no-loop-func': 'error',
|
|
||||||
'no-multi-spaces': 'warn',
|
|
||||||
'no-multi-str': 'error',
|
|
||||||
'no-new-func': 'error',
|
|
||||||
'no-new-wrappers': 'error',
|
|
||||||
'no-octal': 'error',
|
|
||||||
'no-octal-escape': 'error',
|
|
||||||
'no-proto': 'error',
|
|
||||||
'no-redeclare': 'warn',
|
|
||||||
'no-return-assign': 'error',
|
|
||||||
'no-script-url': 'error',
|
|
||||||
'no-self-assign': 'error',
|
|
||||||
'no-self-compare': 'error',
|
|
||||||
'no-sequences': 'warn',
|
|
||||||
'no-throw-literal': 'warn',
|
|
||||||
'no-unmodified-loop-condition': 'error',
|
|
||||||
'no-unused-expressions': 'error',
|
|
||||||
'no-unused-labels': 'error',
|
|
||||||
'no-useless-call': 'warn',
|
|
||||||
'no-useless-escape': 'warn',
|
|
||||||
'no-with': 'error',
|
|
||||||
'wrap-iife': ['error', 'any'],
|
|
||||||
|
|
||||||
// === Variables ===
|
|
||||||
'no-delete-var': 'error',
|
|
||||||
'no-undef': 'error',
|
|
||||||
'no-undef-init': 'error',
|
|
||||||
'no-unused-vars': ['error', { 'caughtErrors': 'none' }],
|
|
||||||
|
|
||||||
// === Stylistic Issues ===
|
|
||||||
'array-bracket-spacing': 'warn',
|
|
||||||
'block-spacing': 'warn',
|
|
||||||
'brace-style': ['warn', '1tbs'],
|
|
||||||
'camelcase': 'warn',
|
|
||||||
'capitalized-comments': ['warn', 'always', { 'ignoreConsecutiveComments': true }],
|
|
||||||
'comma-spacing': ['warn', { 'before': false, 'after': true }],
|
|
||||||
'comma-style': ['warn', 'last'],
|
|
||||||
'computed-property-spacing': 'error',
|
|
||||||
'consistent-this': 'off',
|
|
||||||
'eol-last': 'off',
|
|
||||||
'func-call-spacing': ['warn', 'never'],
|
|
||||||
'func-names': 'off',
|
|
||||||
'func-style': 'off',
|
|
||||||
// indent currently not doing well with our wrapping style, so disabled.
|
|
||||||
'indent': ['off', 4, { 'SwitchCase': 1 }],
|
|
||||||
'key-spacing': ['warn', { 'beforeColon': false, 'afterColon': true, 'mode': minimum }],
|
|
||||||
'keyword-spacing': 'warn',
|
|
||||||
'linebreak-style': ['error', 'unix'],
|
|
||||||
'lines-around-comment': 'off',
|
|
||||||
'max-len': ['error', 132],
|
|
||||||
'max-lines': 'off',
|
|
||||||
'max-depth': 'warn',
|
|
||||||
'max-nested-callbacks': ['warn', 5],
|
|
||||||
'max-params': 'off',
|
|
||||||
'max-statements': 'off',
|
|
||||||
'max-statements-per-line': ['warn', { max: 2 }],
|
|
||||||
'new-cap': ['warn', { 'properties': false }],
|
|
||||||
'new-parens': 'warn',
|
|
||||||
'newline-after-var': 'off',
|
|
||||||
'newline-before-return': 'off',
|
|
||||||
'newline-per-chained-call': 'off',
|
|
||||||
'no-array-constructor': 'off',
|
|
||||||
'no-bitwise': 'error',
|
|
||||||
'no-continue': 'off',
|
|
||||||
'no-inline-comments': 'off',
|
|
||||||
'no-lonely-if': 'off',
|
|
||||||
'no-mixed-operators': 'off',
|
|
||||||
'no-mixed-spaces-and-tabs': 'error',
|
|
||||||
'no-multiple-empty-lines': 'warn',
|
|
||||||
'no-negated-condition': 'off',
|
|
||||||
'no-nested-ternary': 'warn',
|
|
||||||
'no-new-object': 'off',
|
|
||||||
'no-plusplus': 'off',
|
|
||||||
'no-tabs': 'error',
|
|
||||||
'no-ternary': 'off',
|
|
||||||
'no-trailing-spaces': 'error',
|
|
||||||
'no-underscore-dangle': 'off',
|
|
||||||
'no-unneeded-ternary': 'off',
|
|
||||||
'no-whitespace-before-property': 'warn',
|
|
||||||
'object-curly-newline': 'off',
|
|
||||||
'object-curly-spacing': 'warn',
|
|
||||||
'object-property-newline': 'off',
|
|
||||||
'one-var': 'off',
|
|
||||||
'one-var-declaration-per-line': ['warn', 'initializations'],
|
|
||||||
'operator-assignment': 'off',
|
|
||||||
'operator-linebreak': 'off',
|
|
||||||
'padded-blocks': 'off',
|
|
||||||
'quote-props': ['warn', 'as-needed', {'unnecessary': false, 'keywords': true, 'numbers': true}],
|
|
||||||
'quotes': 'off',
|
|
||||||
'require-jsdoc': 'warn',
|
|
||||||
'semi': 'error',
|
|
||||||
'semi-spacing': ['warn', {'before': false, 'after': true}],
|
|
||||||
'sort-vars': 'off',
|
|
||||||
'space-before-blocks': 'warn',
|
|
||||||
'space-before-function-paren': ['warn', 'never'],
|
|
||||||
'space-in-parens': 'warn',
|
|
||||||
'space-infix-ops': 'warn',
|
|
||||||
'space-unary-ops': 'warn',
|
|
||||||
'spaced-comment': 'warn',
|
|
||||||
'unicode-bom': 'error',
|
|
||||||
'wrap-regex': 'off',
|
|
||||||
|
|
||||||
// === Promises ===
|
|
||||||
'promise/always-return': 'warn',
|
|
||||||
'promise/no-return-wrap': 'warn',
|
|
||||||
'promise/param-names': 'warn',
|
|
||||||
'promise/catch-or-return': ['warn', {terminationMethod: ['catch', 'fail']}],
|
|
||||||
'promise/no-native': 'warn',
|
|
||||||
'promise/no-promise-in-callback': 'warn',
|
|
||||||
'promise/no-callback-in-promise': 'warn',
|
|
||||||
'promise/avoid-new': 'warn',
|
|
||||||
|
|
||||||
// === Deprecations ===
|
|
||||||
"no-restricted-properties": ['warn', {
|
|
||||||
'object': 'M',
|
|
||||||
'property': 'str',
|
|
||||||
'message': 'Use AMD module "core/str" or M.util.get_string()'
|
|
||||||
}],
|
|
||||||
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["**/yui/src/**/*.js"],
|
|
||||||
// Disable some rules which we can't safely define for YUI rollups.
|
|
||||||
rules: {
|
|
||||||
'no-undef': 'off',
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'no-unused-expressions': 'off'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ["**/amd/src/*.js"],
|
|
||||||
// Check AMD with some slightly stricter rules.
|
|
||||||
rules: {
|
|
||||||
'no-unused-vars': 'error',
|
|
||||||
'no-implicit-globals': 'error'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
18
.github/workflows/ci.yml
vendored
Normal file
18
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# .github/workflows/ci.yml
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
uses: catalyst/catalyst-moodle-workflows/.github/workflows/ci.yml@main
|
||||||
|
secrets:
|
||||||
|
# Required if you plan to publish (uncomment the below)
|
||||||
|
moodle_org_token: ${{ secrets.MOODLE_ORG_TOKEN }}
|
||||||
|
with:
|
||||||
|
# Grunt fails due to CSS styling, when needing an !important statement.
|
||||||
|
disable_grunt: false
|
||||||
|
disable_phpunit: true
|
||||||
|
disable_release : false
|
||||||
|
release_branches: main
|
||||||
|
min_php : 7.4
|
||||||
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
62
.travis.yml
62
.travis.yml
@@ -1,62 +0,0 @@
|
|||||||
language: php
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
addons:
|
|
||||||
hosts:
|
|
||||||
- moodle.test
|
|
||||||
firefox: "35.0.1"
|
|
||||||
postgresql: "9.4"
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- oracle-java9-installer
|
|
||||||
- oracle-java9-set-default
|
|
||||||
- redis-server
|
|
||||||
|
|
||||||
services:
|
|
||||||
- redis-server
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.composer/cache
|
|
||||||
- $HOME/.npm
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- IGNORE_PATHS=vendor,source
|
|
||||||
- IGNORE_NAMES=*.txt,*.md
|
|
||||||
|
|
||||||
# Alternate tests with MySQL and PostgreSQL
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# PHP 7.0
|
|
||||||
- php: 7.0
|
|
||||||
env: DB=mysqli MOODLE_BRANCH=MOODLE_35_STABLE
|
|
||||||
- php: 7.2
|
|
||||||
env: DB=pgsql MOODLE_BRANCH=MOODLE_37_STABLE
|
|
||||||
- php: 7.2
|
|
||||||
env: DB=pgsql MOODLE_BRANCH=master
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- phpenv config-rm xdebug.ini
|
|
||||||
- cd ../..
|
|
||||||
- nvm install 8.9;
|
|
||||||
nvm use 8.9;
|
|
||||||
composer create-project -n --no-dev --prefer-dist moodlerooms/moodle-plugin-ci ci ^2;
|
|
||||||
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- moodle-plugin-ci install -vvv
|
|
||||||
- echo '$CFG->cookiesecure = false;' >> moodle/config.php
|
|
||||||
|
|
||||||
script:
|
|
||||||
- moodle-plugin-ci validate
|
|
||||||
- moodle-plugin-ci phplint
|
|
||||||
- moodle-plugin-ci phpcpd
|
|
||||||
- moodle-plugin-ci phpmd
|
|
||||||
- moodle-plugin-ci codechecker
|
|
||||||
- moodle-plugin-ci savepoints
|
|
||||||
- moodle-plugin-ci mustache
|
|
||||||
- moodle-plugin-ci grunt || false
|
|
||||||
- moodle-plugin-ci phpunit
|
|
||||||
- moodle-plugin-ci behat
|
|
||||||
30
CHANGELOG.md
Normal file
30
CHANGELOG.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 5.1.1 (2026031800)
|
||||||
|
* Fixed PHP 7.3 compatibility issue that broke Moodle 4.0.x servers ([#13](https://github.com/LdesignMedia/moodle-availability_ipaddress/issues/13))
|
||||||
|
* Removed typed properties (PHP 7.4+) to support PHP 7.3+
|
||||||
|
* Lowered environment.xml PHP requirement from 7.4 to 7.3
|
||||||
|
|
||||||
|
## 5.1.0 (2026030300)
|
||||||
|
- Tested and verified on Moodle 5.1
|
||||||
|
|
||||||
|
## 5.0.2 (2025080401)
|
||||||
|
- Added support for pre-configuring IP ranges by admins
|
||||||
|
|
||||||
|
## 5.0.1 (2025052200)
|
||||||
|
- Tested on Moodle 5.0
|
||||||
|
|
||||||
|
## 4.5.0 (2025040400)
|
||||||
|
- Tested on Moodle 4.5
|
||||||
|
|
||||||
|
## 4.4.0 (2024072000)
|
||||||
|
- Tested on Moodle 4.4
|
||||||
|
|
||||||
|
## 4.0.3 (2022052801)
|
||||||
|
- Test in Moodle 4.0 @[hamzatamyachte](https://github.com/hamzatamyachte)
|
||||||
|
|
||||||
|
## 4.0.2 (2022052800)
|
||||||
|
- Fixed the [issue 6](https://github.com/ldesignmediaNL/moodle-availability_ipaddress/issues/6) @[hamzatamyachte](https://github.com/hamzatamyachte)
|
||||||
|
|
||||||
|
## 3.9.0 (2022021100)
|
||||||
|
- Thanks for adding ip-range support @[juacas](https://github.com/juacas)
|
||||||
47
README.md
47
README.md
@@ -1,22 +1,38 @@
|
|||||||
## Moodle - availability ip address plugin
|
## Moodle - availability ip address plugin
|
||||||
Restrict access to any activity by ip-address. Most activities don't support this feature.
|
|
||||||
|
[](https://github.com/LdesignMedia/moodle-availability_ipaddress/actions/workflows/ci.yml)
|
||||||
|
|
||||||
|
Enhance activity security by restricting access based on IP address. This plugin allows you to control the availability of any chosen
|
||||||
|
activity, making it accessible only to users from specified IP addresses.
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||

|
<img src="https://ldesignmedia.nl/themes/ldesignmedia/assets/images/logo/logo.svg" alt="ldesignmedia" height="70px">
|
||||||
|
|
||||||
* Author: Luuk Verhoeven, [MFreak.nl](https://MFreak.nl/)
|
* Author: Luuk Verhoeven, [ldesignmedia.nl](https://ldesignmedia.nl/)
|
||||||
* Min. required: Moodle 3.2.x
|
* Author: Vincent Cornelis, [ldesignmedia.nl](https://ldesignmedia.nl/)
|
||||||
* Supports PHP: 7.0 | 7.1
|
* Min. required: Moodle 4.0
|
||||||
|
* Supports PHP: 7.3+
|
||||||
|
|
||||||
[](https://travis-ci.org/MFreakNL/moodle-availability_ipaddress)
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||

|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## List of features
|
## List of features
|
||||||
- Supports comma separate list of ip-addresses
|
- Supports comma separate list of ip-addresses
|
||||||
- Subnet support, eg 192.168.1.0/24
|
- Subnet support, eg 192.168.1.0/24
|
||||||
- Inline ip-address validation
|
- Inline ip-address validation
|
||||||
- Turning on/off with eye icon, without lossing the input value.
|
- Turning on/off with eye icon, without losing the input value.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
1. Copy this plugin to the `availability\condition\ipaddress` folder on the server
|
1. Copy this plugin to the `availability\condition\ipaddress` folder on the server
|
||||||
@@ -24,12 +40,21 @@ Restrict access to any activity by ip-address. Most activities don't support thi
|
|||||||
3. Go to Site Administrator > Notification
|
3. Go to Site Administrator > Notification
|
||||||
4. Install the plugin
|
4. Install the plugin
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Add or edit an activity in a Moodle course.
|
||||||
|
2. Go to the section "Restrict access"
|
||||||
|
3. Click IP address in the modal
|
||||||
|
4. There's a new input field that supports a list of comma separated ip address e.g. 127.0.0.1, 192.168.1.0/24
|
||||||
|
1. The users with matching ip addresses can view the activity.
|
||||||
|
5. Save the activity
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
- Behat tests ip validation
|
- Behat tests ip validation
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
If you discover any security related issues, please email [luuk@MFreak.nl](mailto:luuk@MFreak.nl) instead of using the issue tracker.
|
If you discover any security related issues, please email [luuk@ldesignmedia.nl](mailto:luuk@ldesignmedia.nl) instead of using the issue tracker.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -38,3 +63,7 @@ The GNU GENERAL PUBLIC LICENSE. Please see [License File](LICENSE) for more info
|
|||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Contributions are welcome and will be fully credited. We accept contributions via Pull Requests on Github.
|
Contributions are welcome and will be fully credited. We accept contributions via Pull Requests on Github.
|
||||||
|
|
||||||
|
### Changelog
|
||||||
|
|
||||||
|
See [CHANGELOG.md](CHANGELOG.md) for a full list of changes.
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ namespace availability_ipaddress;
|
|||||||
|
|
||||||
use core_availability\info;
|
use core_availability\info;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class condition
|
* Class condition
|
||||||
*
|
*
|
||||||
@@ -38,27 +36,42 @@ defined('MOODLE_INTERNAL') || die;
|
|||||||
* @copyright 2019-05-14 Mfreak.nl | LdesignMedia.nl - Luuk Verhoeven
|
* @copyright 2019-05-14 Mfreak.nl | LdesignMedia.nl - Luuk Verhoeven
|
||||||
*/
|
*/
|
||||||
class condition extends \core_availability\condition {
|
class condition extends \core_availability\condition {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Manual provided IP addresses.
|
||||||
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $ipaddresses = '';
|
protected $ipaddresses = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predefined IP address ranges.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $predefinedranges = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* condition constructor.
|
* condition constructor.
|
||||||
*
|
*
|
||||||
* @param \stdClass $structure
|
* @param \stdClass $structure
|
||||||
*/
|
*/
|
||||||
public function __construct($structure) {
|
public function __construct(\stdClass $structure) {
|
||||||
if (isset($structure->ipaddresses)) {
|
if (isset($structure->ipaddresses)) {
|
||||||
$this->ipaddresses = $structure->ipaddresses;
|
$this->ipaddresses = $structure->ipaddresses;
|
||||||
}
|
}
|
||||||
|
if (isset($structure->predefined_ranges)) {
|
||||||
|
$this->predefinedranges = $structure->predefined_ranges;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether a particular item is currently available
|
* Determines whether a particular item is currently available
|
||||||
* according to this availability condition.
|
* according to this availability condition.
|
||||||
*
|
*
|
||||||
|
* Note: Cannot add type declarations for $not, $grabthelot, and $userid parameters
|
||||||
|
* as the parent core_availability\condition::is_available() method doesn't have them,
|
||||||
|
* and PHP requires compatibility with parent method signatures when overriding.
|
||||||
|
*
|
||||||
* If implementations require a course or modinfo, they should use
|
* If implementations require a course or modinfo, they should use
|
||||||
* the get methods in $info.
|
* the get methods in $info.
|
||||||
*
|
*
|
||||||
@@ -75,22 +88,51 @@ class condition extends \core_availability\condition {
|
|||||||
* @param bool $grabthelot Performance hint: if true, caches information
|
* @param bool $grabthelot Performance hint: if true, caches information
|
||||||
* required for all course-modules, to make the front page and similar
|
* required for all course-modules, to make the front page and similar
|
||||||
* pages work more quickly (works only for current user)
|
* pages work more quickly (works only for current user)
|
||||||
* @param int $userid User ID to check availability for
|
* @param int $userid User ID to check availability for
|
||||||
*
|
*
|
||||||
* @return bool True if available
|
* @return bool True if available
|
||||||
*/
|
*/
|
||||||
public function is_available($not, info $info, $grabthelot, $userid) {
|
public function is_available($not, info $info, $grabthelot, $userid): bool {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
if (empty($this->ipaddresses)) {
|
// Collect all IP addresses to check.
|
||||||
return true;
|
$allipaddresses = [];
|
||||||
|
|
||||||
|
// Add custom IP addresses.
|
||||||
|
if (!empty($this->ipaddresses)) {
|
||||||
|
$allipaddresses[] = trim($this->ipaddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if ip-address matches.
|
// Add predefined ranges.
|
||||||
if (address_in_subnet(getremoteaddr(), trim($this->ipaddresses))) {
|
if (!empty($this->predefinedranges)) {
|
||||||
return true;
|
$ranges = $DB->get_records_list(
|
||||||
|
'availability_ipaddress_pre',
|
||||||
|
'id',
|
||||||
|
$this->predefinedranges,
|
||||||
|
'',
|
||||||
|
'ipaddresses'
|
||||||
|
);
|
||||||
|
foreach ($ranges as $range) {
|
||||||
|
if (!empty($range->ipaddresses)) {
|
||||||
|
$allipaddresses[] = trim($range->ipaddresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// If no IP addresses are configured, the condition passes.
|
||||||
|
if (empty($allipaddresses)) {
|
||||||
|
return !$not;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user's IP matches any of the allowed addresses.
|
||||||
|
$userip = getremoteaddr();
|
||||||
|
foreach ($allipaddresses as $iplist) {
|
||||||
|
if (address_in_subnet($userip, $iplist)) {
|
||||||
|
return !$not;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $not;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,6 +141,9 @@ class condition extends \core_availability\condition {
|
|||||||
* students if the activity is not available to them, and for staff to see
|
* students if the activity is not available to them, and for staff to see
|
||||||
* what conditions are.
|
* what conditions are.
|
||||||
*
|
*
|
||||||
|
* Note: Cannot add type declarations for $full and $not parameters as the parent
|
||||||
|
* core_availability\condition::get_description() method doesn't have them.
|
||||||
|
*
|
||||||
* The $full parameter can be used to distinguish between 'staff' cases
|
* The $full parameter can be used to distinguish between 'staff' cases
|
||||||
* (when displaying all information about the activity) and 'student' cases
|
* (when displaying all information about the activity) and 'student' cases
|
||||||
* (when displaying only conditions they don't meet).
|
* (when displaying only conditions they don't meet).
|
||||||
@@ -114,12 +159,13 @@ class condition extends \core_availability\condition {
|
|||||||
* @param bool $not Set true if we are inverting the condition
|
* @param bool $not Set true if we are inverting the condition
|
||||||
* @param info $info Item we're checking
|
* @param info $info Item we're checking
|
||||||
*
|
*
|
||||||
* @return string Information string (for admin) about all restrictions on
|
* @return string Information string (for admin) about all restrictions on this item
|
||||||
* this item
|
|
||||||
* @throws \coding_exception
|
|
||||||
*/
|
*/
|
||||||
public function get_description($full, $not, info $info) {
|
public function get_description($full, $not, info $info): string {
|
||||||
return get_string('require_condition', 'availability_ipaddress');
|
|
||||||
|
$desc = $not ? 'require_condition_not' : 'require_condition';
|
||||||
|
|
||||||
|
return get_string($desc, 'availability_ipaddress', getremoteaddr());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,7 +174,7 @@ class condition extends \core_availability\condition {
|
|||||||
*
|
*
|
||||||
* @return string Text representation of parameters
|
* @return string Text representation of parameters
|
||||||
*/
|
*/
|
||||||
protected function get_debug_string() {
|
protected function get_debug_string(): string {
|
||||||
return !empty($this->ipaddresses) ? 'ipaddresses ON' : 'ipaddresses OFF';
|
return !empty($this->ipaddresses) ? 'ipaddresses ON' : 'ipaddresses OFF';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,41 +188,28 @@ class condition extends \core_availability\condition {
|
|||||||
*
|
*
|
||||||
* @return \stdClass Object representing condition
|
* @return \stdClass Object representing condition
|
||||||
*/
|
*/
|
||||||
public static function get_json($ipaddresses) {
|
public static function get_json(string $ipaddresses): \stdClass {
|
||||||
return (object)[
|
return (object) [
|
||||||
'type' => 'ipaddress',
|
'type' => 'ipaddress',
|
||||||
'ipaddresses' => $ipaddresses,
|
'ipaddresses' => $ipaddresses,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if ip-address is valid
|
|
||||||
*
|
|
||||||
* @param string $ipaddresses
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function is_valid_ipaddresses($ipaddresses) {
|
|
||||||
$ipaddresses = implode(',', $ipaddresses);
|
|
||||||
foreach ($ipaddresses as $ipaddress) {
|
|
||||||
if (!filter_var($ipaddress, FILTER_VALIDATE_IP)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves tree data back to a structure object.
|
* Saves tree data back to a structure object.
|
||||||
*
|
*
|
||||||
* @return \stdClass Structure object (ready to be made into JSON format)
|
* @return \stdClass Structure object (ready to be made into JSON format)
|
||||||
*/
|
*/
|
||||||
public function save() {
|
public function save(): \stdClass {
|
||||||
return (object)[
|
$result = (object) [
|
||||||
'type' => 'ipaddress',
|
'type' => 'ipaddress',
|
||||||
'ipaddresses' => $this->ipaddresses,
|
'ipaddresses' => $this->ipaddresses,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!empty($this->predefinedranges)) {
|
||||||
|
$result->predefined_ranges = $this->predefinedranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
153
classes/form/range_form.php
Normal file
153
classes/form/range_form.php
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form for managing predefined IP ranges.
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace availability_ipaddress\form;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
|
require_once($CFG->libdir . '/formslib.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form for managing predefined IP ranges.
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class range_form extends \moodleform {
|
||||||
|
/**
|
||||||
|
* Define the form.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function definition(): void {
|
||||||
|
$mform = $this->_form;
|
||||||
|
$id = $this->_customdata['id'] ?? 0;
|
||||||
|
|
||||||
|
// Hidden id field.
|
||||||
|
$mform->addElement('hidden', 'id');
|
||||||
|
$mform->setType('id', PARAM_INT);
|
||||||
|
$mform->setDefault('id', $id);
|
||||||
|
|
||||||
|
// Name field.
|
||||||
|
$mform->addElement('text', 'name', get_string('range_name', 'availability_ipaddress'));
|
||||||
|
$mform->setType('name', PARAM_TEXT);
|
||||||
|
$mform->addRule('name', null, 'required', null, 'client');
|
||||||
|
$mform->addHelpButton('name', 'range_name', 'availability_ipaddress');
|
||||||
|
|
||||||
|
// IP addresses field.
|
||||||
|
$mform->addElement(
|
||||||
|
'text',
|
||||||
|
'ipaddresses',
|
||||||
|
get_string('ipaddresses', 'availability_ipaddress'),
|
||||||
|
['size' => 100]
|
||||||
|
);
|
||||||
|
$mform->setType('ipaddresses', PARAM_TEXT);
|
||||||
|
$mform->addRule('ipaddresses', null, 'required', null, 'client');
|
||||||
|
$mform->addHelpButton('ipaddresses', 'ipaddresses_help', 'availability_ipaddress');
|
||||||
|
|
||||||
|
// Description field.
|
||||||
|
$mform->addElement(
|
||||||
|
'textarea',
|
||||||
|
'description',
|
||||||
|
get_string('description'),
|
||||||
|
['rows' => 3, 'cols' => 60]
|
||||||
|
);
|
||||||
|
$mform->setType('description', PARAM_TEXT);
|
||||||
|
|
||||||
|
// Enabled field.
|
||||||
|
$mform->addElement('advcheckbox', 'enabled', get_string('enabled', 'availability_ipaddress'));
|
||||||
|
$mform->setDefault('enabled', 1);
|
||||||
|
|
||||||
|
// Action buttons.
|
||||||
|
$this->add_action_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the form data.
|
||||||
|
*
|
||||||
|
* Note: Parameter type declarations cannot be added here as the parent
|
||||||
|
* moodleform::validation() method doesn't have them, and PHP requires
|
||||||
|
* compatibility with parent method signatures when overriding.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @param array $files
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function validation($data, $files) {
|
||||||
|
$errors = parent::validation($data, $files);
|
||||||
|
|
||||||
|
// Validate IP addresses.
|
||||||
|
if (!empty($data['ipaddresses'])) {
|
||||||
|
$ipaddresses = explode(',', $data['ipaddresses']);
|
||||||
|
foreach ($ipaddresses as $ip) {
|
||||||
|
$ip = trim($ip);
|
||||||
|
if (!$this->validate_ip_format($ip)) {
|
||||||
|
$errors['ipaddresses'] = get_string('error_ipaddress', 'availability_ipaddress');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate IP address format.
|
||||||
|
*
|
||||||
|
* @param string $ip
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function validate_ip_format(string $ip): bool {
|
||||||
|
|
||||||
|
// Use the same validation logic as the main plugin.
|
||||||
|
// This is a simplified version - you might want to use the same regex as in JS.
|
||||||
|
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for CIDR notation.
|
||||||
|
if (strpos($ip, '/') !== false) {
|
||||||
|
[$addr, $mask] = explode('/', $ip);
|
||||||
|
if (filter_var($addr, FILTER_VALIDATE_IP) && is_numeric($mask)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for IP range.
|
||||||
|
if (strpos($ip, '-') !== false) {
|
||||||
|
[$start, $end] = explode('-', $ip);
|
||||||
|
if (filter_var($start, FILTER_VALIDATE_IP)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
namespace availability_ipaddress;
|
namespace availability_ipaddress;
|
||||||
defined('MOODLE_INTERNAL') || die;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class frontend
|
* Class frontend
|
||||||
@@ -35,30 +34,50 @@ defined('MOODLE_INTERNAL') || die;
|
|||||||
* @copyright 2019-05-14 Mfreak.nl | LdesignMedia.nl - Luuk Verhoeven
|
* @copyright 2019-05-14 Mfreak.nl | LdesignMedia.nl - Luuk Verhoeven
|
||||||
*/
|
*/
|
||||||
class frontend extends \core_availability\frontend {
|
class frontend extends \core_availability\frontend {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get_javascript_strings
|
* get_javascript_strings
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function get_javascript_strings() {
|
protected function get_javascript_strings(): array {
|
||||||
return [
|
return [
|
||||||
'js:ipaddress',
|
'js:ipaddress',
|
||||||
'error_ipaddress',
|
'error_ipaddress',
|
||||||
|
'predefined_ranges',
|
||||||
|
'custom_ipaddress',
|
||||||
|
'use_predefined',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decides whether this plugin should be available in a given course. The
|
* Get additional parameters for the JavaScript module.
|
||||||
* plugin can do this depending on course or system settings.
|
|
||||||
*
|
*
|
||||||
* @param \stdClass $course Course object
|
* Note: Cannot add type declaration for $course parameter as the parent
|
||||||
* @param \cm_info $cm Course-module currently being edited (null if none)
|
* core_availability\frontend::get_javascript_init_params() method doesn't
|
||||||
* @param \section_info $section Section currently being edited (null if none)
|
* have it, and PHP requires compatibility with parent method signatures.
|
||||||
*
|
*
|
||||||
* @return bool True if there are completion criteria
|
* @param \stdClass $course Course object
|
||||||
|
* @param \cm_info|null $cm Course module
|
||||||
|
* @param \section_info|null $section Section
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function allow_add($course, \cm_info $cm = null, \section_info $section = null) {
|
protected function get_javascript_init_params($course, ?\cm_info $cm = null, ?\section_info $section = null): array {
|
||||||
return true;
|
global $DB;
|
||||||
|
|
||||||
|
// Get enabled predefined IP ranges.
|
||||||
|
$ranges = $DB->get_records('availability_ipaddress_pre', ['enabled' => 1], 'name', 'id, name, ipaddresses');
|
||||||
|
|
||||||
|
// Format for JavaScript.
|
||||||
|
$rangedata = [];
|
||||||
|
foreach ($ranges as $range) {
|
||||||
|
$rangedata[] = [
|
||||||
|
'id' => $range->id,
|
||||||
|
'name' => format_string($range->name),
|
||||||
|
'ipaddresses' => $range->ipaddresses,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$rangedata];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
362
classes/helper.php
Normal file
362
classes/helper.php
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper functions for availability_ipaddress.
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace availability_ipaddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for availability_ipaddress.
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class helper {
|
||||||
|
/**
|
||||||
|
* Check if a predefined range is in use.
|
||||||
|
*
|
||||||
|
* @param int $rangeid The ID of the range to check.
|
||||||
|
*
|
||||||
|
* @return array Array with 'inuse' boolean and 'count' of uses.
|
||||||
|
*/
|
||||||
|
public static function is_range_in_use(int $rangeid): array {
|
||||||
|
$activities = [];
|
||||||
|
|
||||||
|
// Check course modules.
|
||||||
|
$moduleactivities = self::check_range_in_modules($rangeid);
|
||||||
|
$activities = array_merge($activities, $moduleactivities);
|
||||||
|
|
||||||
|
// Check sections.
|
||||||
|
$sectionactivities = self::check_range_in_sections($rangeid);
|
||||||
|
$activities = array_merge($activities, $sectionactivities);
|
||||||
|
|
||||||
|
$count = count($activities);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'inuse' => ($count > 0),
|
||||||
|
'count' => $count,
|
||||||
|
'activities' => $activities,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a range is used in course modules.
|
||||||
|
*
|
||||||
|
* @param int $rangeid The ID of the range to check.
|
||||||
|
*
|
||||||
|
* @return array Array of activities using the range.
|
||||||
|
*/
|
||||||
|
private static function check_range_in_modules(int $rangeid): array {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$activities = [];
|
||||||
|
|
||||||
|
$sql = "SELECT cm.id, cm.course, cm.availability, cm.module, cm.instance,
|
||||||
|
c.fullname as coursename, m.name as modname
|
||||||
|
FROM {course_modules} cm
|
||||||
|
JOIN {course} c ON c.id = cm.course
|
||||||
|
JOIN {modules} m ON m.id = cm.module
|
||||||
|
WHERE cm.availability IS NOT NULL AND cm.availability != ''";
|
||||||
|
|
||||||
|
$modules = $DB->get_records_sql($sql);
|
||||||
|
|
||||||
|
foreach ($modules as $module) {
|
||||||
|
$availability = json_decode($module->availability);
|
||||||
|
if (!$availability || !self::check_availability_tree($availability, $rangeid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$activityname = self::get_module_name($module);
|
||||||
|
$activities[] = [
|
||||||
|
'coursename' => $module->coursename,
|
||||||
|
'cmid' => $module->id,
|
||||||
|
'name' => $activityname,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $activities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of a module.
|
||||||
|
*
|
||||||
|
* @param \stdClass $module The module record.
|
||||||
|
*
|
||||||
|
* @return string The module name.
|
||||||
|
*/
|
||||||
|
private static function get_module_name(\stdClass $module): string {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($DB->get_manager()->table_exists($module->modname)) {
|
||||||
|
$activity = $DB->get_record($module->modname, ['id' => $module->instance], 'name', IGNORE_MISSING);
|
||||||
|
if ($activity && !empty($activity->name)) {
|
||||||
|
return $activity->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Table doesn't exist or other database error - fall through to default.
|
||||||
|
debugging('Error getting module name: ' . $e->getMessage(), DEBUG_DEVELOPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_string('modulename', $module->modname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a range is used in course sections.
|
||||||
|
*
|
||||||
|
* @param int $rangeid The ID of the range to check.
|
||||||
|
*
|
||||||
|
* @return array Array of sections using the range.
|
||||||
|
*/
|
||||||
|
private static function check_range_in_sections(int $rangeid): array {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$activities = [];
|
||||||
|
|
||||||
|
$sql = "SELECT cs.id, cs.course, cs.availability, cs.name, cs.section, c.fullname as coursename
|
||||||
|
FROM {course_sections} cs
|
||||||
|
JOIN {course} c ON c.id = cs.course
|
||||||
|
WHERE cs.availability IS NOT NULL AND cs.availability != ''";
|
||||||
|
|
||||||
|
$sections = $DB->get_records_sql($sql);
|
||||||
|
|
||||||
|
foreach ($sections as $section) {
|
||||||
|
$availability = json_decode($section->availability);
|
||||||
|
if (!$availability || !self::check_availability_tree($availability, $rangeid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sectionname = $section->name ?: get_string('section') . ' ' . $section->section;
|
||||||
|
$activities[] = [
|
||||||
|
'coursename' => $section->coursename,
|
||||||
|
'cmid' => 0,
|
||||||
|
'name' => $sectionname,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $activities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively check availability tree for range usage.
|
||||||
|
*
|
||||||
|
* @param \stdClass $availability The availability tree.
|
||||||
|
* @param int $rangeid The range ID to look for.
|
||||||
|
*
|
||||||
|
* @return bool True if range is found in tree.
|
||||||
|
*/
|
||||||
|
private static function check_availability_tree(\stdClass $availability, int $rangeid): bool {
|
||||||
|
|
||||||
|
// Check if this is an IP address condition.
|
||||||
|
if (isset($availability->type) && $availability->type === 'ipaddress') {
|
||||||
|
if (isset($availability->predefined_ranges) && is_array($availability->predefined_ranges)) {
|
||||||
|
return in_array($rangeid, $availability->predefined_ranges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check nested conditions (for groups).
|
||||||
|
if (isset($availability->c) && is_array($availability->c)) {
|
||||||
|
foreach ($availability->c as $condition) {
|
||||||
|
if (self::check_availability_tree($condition, $rangeid)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get usage details for a range as HTML.
|
||||||
|
*
|
||||||
|
* @param int $rangeid The range ID.
|
||||||
|
*
|
||||||
|
* @return string HTML string with usage details.
|
||||||
|
*/
|
||||||
|
public static function get_range_usage_html(int $rangeid): string {
|
||||||
|
$usage = self::is_range_in_use($rangeid);
|
||||||
|
|
||||||
|
if (!$usage['inuse']) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = \html_writer::tag('p', get_string('range_in_use_count', 'availability_ipaddress', $usage['count']));
|
||||||
|
|
||||||
|
if (!empty($usage['activities'])) {
|
||||||
|
$items = [];
|
||||||
|
foreach (array_slice($usage['activities'], 0, 5) as $activity) {
|
||||||
|
$items[] = $activity['coursename'] . ': ' . $activity['name'];
|
||||||
|
}
|
||||||
|
$html .= \html_writer::alist($items);
|
||||||
|
|
||||||
|
if (count($usage['activities']) > 5) {
|
||||||
|
$more = count($usage['activities']) - 5;
|
||||||
|
$html .= \html_writer::tag('p', get_string('and_x_more', 'availability_ipaddress', $more));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a predefined range from all availability restrictions.
|
||||||
|
*
|
||||||
|
* @param int $rangeid The ID of the range to remove.
|
||||||
|
*
|
||||||
|
* @return int Number of restrictions updated.
|
||||||
|
*/
|
||||||
|
public static function remove_range_from_restrictions(int $rangeid): int {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$updatecount = 0;
|
||||||
|
|
||||||
|
// Update course modules.
|
||||||
|
$sql = "SELECT cm.id, cm.availability
|
||||||
|
FROM {course_modules} cm
|
||||||
|
WHERE cm.availability IS NOT NULL AND cm.availability != ''";
|
||||||
|
|
||||||
|
$modules = $DB->get_records_sql($sql);
|
||||||
|
|
||||||
|
foreach ($modules as $module) {
|
||||||
|
$availability = json_decode($module->availability);
|
||||||
|
if ($availability && self::remove_range_from_tree($availability, $rangeid)) {
|
||||||
|
$module->availability = json_encode($availability);
|
||||||
|
$DB->update_record('course_modules', $module);
|
||||||
|
$updatecount++;
|
||||||
|
|
||||||
|
// Rebuild course cache.
|
||||||
|
$course = $DB->get_record('course', ['id' => $DB->get_field('course_modules', 'course', ['id' => $module->id])]);
|
||||||
|
if ($course) {
|
||||||
|
rebuild_course_cache($course->id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update course sections.
|
||||||
|
$sql = "SELECT cs.id, cs.course, cs.availability
|
||||||
|
FROM {course_sections} cs
|
||||||
|
WHERE cs.availability IS NOT NULL AND cs.availability != ''";
|
||||||
|
|
||||||
|
$sections = $DB->get_records_sql($sql);
|
||||||
|
|
||||||
|
foreach ($sections as $section) {
|
||||||
|
$availability = json_decode($section->availability);
|
||||||
|
if ($availability && self::remove_range_from_tree($availability, $rangeid)) {
|
||||||
|
$section->availability = json_encode($availability);
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
$updatecount++;
|
||||||
|
|
||||||
|
// Rebuild course cache.
|
||||||
|
rebuild_course_cache($section->course, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $updatecount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively remove range from availability tree.
|
||||||
|
*
|
||||||
|
* @param \stdClass $availability The availability tree.
|
||||||
|
* @param int $rangeid The range ID to remove.
|
||||||
|
*
|
||||||
|
* @return bool True if tree was modified.
|
||||||
|
*/
|
||||||
|
private static function remove_range_from_tree(\stdClass $availability, int $rangeid): bool {
|
||||||
|
$modified = false;
|
||||||
|
|
||||||
|
// Process IP address conditions.
|
||||||
|
if (self::is_ipaddress_condition($availability)) {
|
||||||
|
$modified = self::remove_range_from_condition($availability, $rangeid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process nested conditions.
|
||||||
|
return self::process_nested_conditions($availability, $rangeid) || $modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the availability item is an IP address condition.
|
||||||
|
*
|
||||||
|
* @param \stdClass $availability The availability item.
|
||||||
|
*
|
||||||
|
* @return bool True if it's an IP address condition.
|
||||||
|
*/
|
||||||
|
private static function is_ipaddress_condition(\stdClass $availability): bool {
|
||||||
|
return isset($availability->type) && $availability->type === 'ipaddress';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove range from an IP address condition.
|
||||||
|
*
|
||||||
|
* @param \stdClass $availability The availability condition.
|
||||||
|
* @param int $rangeid The range ID to remove.
|
||||||
|
*
|
||||||
|
* @return bool True if the condition was modified.
|
||||||
|
*/
|
||||||
|
private static function remove_range_from_condition(\stdClass $availability, int $rangeid): bool {
|
||||||
|
if (!isset($availability->predefined_ranges) || !is_array($availability->predefined_ranges)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = array_search($rangeid, $availability->predefined_ranges);
|
||||||
|
if ($key === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the range from the array.
|
||||||
|
array_splice($availability->predefined_ranges, $key, 1);
|
||||||
|
|
||||||
|
// If no ranges left, remove the predefined_ranges property.
|
||||||
|
if (empty($availability->predefined_ranges)) {
|
||||||
|
unset($availability->predefined_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process nested conditions in availability tree.
|
||||||
|
*
|
||||||
|
* @param \stdClass $availability The availability item.
|
||||||
|
* @param int $rangeid The range ID to remove.
|
||||||
|
*
|
||||||
|
* @return bool True if any nested condition was modified.
|
||||||
|
*/
|
||||||
|
private static function process_nested_conditions(\stdClass $availability, int $rangeid): bool {
|
||||||
|
if (!isset($availability->c) || !is_array($availability->c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$modified = false;
|
||||||
|
foreach ($availability->c as $condition) {
|
||||||
|
if (self::remove_range_from_tree($condition, $rangeid)) {
|
||||||
|
$modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $modified;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,8 +26,6 @@
|
|||||||
|
|
||||||
namespace availability_ipaddress\privacy;
|
namespace availability_ipaddress\privacy;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Privacy Subsystem for availability_ipaddress implementing null_provider.
|
* Privacy Subsystem for availability_ipaddress implementing null_provider.
|
||||||
*
|
*
|
||||||
@@ -35,14 +33,13 @@ defined('MOODLE_INTERNAL') || die();
|
|||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
class provider implements \core_privacy\local\metadata\null_provider {
|
class provider implements \core_privacy\local\metadata\null_provider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the language string identifier with the component's language
|
* Get the language string identifier with the component's language
|
||||||
* file to explain why this plugin stores no data.
|
* file to explain why this plugin stores no data.
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function get_reason() : string {
|
public static function get_reason(): string {
|
||||||
return 'privacy:metadata';
|
return 'privacy:metadata';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
249
classes/table/ipranges_table.php
Normal file
249
classes/table/ipranges_table.php
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table class for displaying IP address ranges.
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace availability_ipaddress\table;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
|
require_once($CFG->libdir . '/tablelib.php');
|
||||||
|
|
||||||
|
use table_sql;
|
||||||
|
use html_writer;
|
||||||
|
use moodle_url;
|
||||||
|
use pix_icon;
|
||||||
|
use confirm_action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table class for IP address ranges.
|
||||||
|
*
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
*/
|
||||||
|
class ipranges_table extends table_sql {
|
||||||
|
/**
|
||||||
|
* @var moodle_url The base URL for the page.
|
||||||
|
*/
|
||||||
|
public $baseurl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $uniqueid Unique ID for the table.
|
||||||
|
* @param moodle_url $baseurl The base URL for the page.
|
||||||
|
*/
|
||||||
|
public function __construct(string $uniqueid, moodle_url $baseurl) {
|
||||||
|
parent::__construct($uniqueid);
|
||||||
|
$this->baseurl = $baseurl;
|
||||||
|
|
||||||
|
// Define columns and headers.
|
||||||
|
$columns = ['name', 'description', 'ipaddresses', 'enabled', 'actions'];
|
||||||
|
$headers = [
|
||||||
|
get_string('name'),
|
||||||
|
get_string('description'),
|
||||||
|
get_string('ipaddresses', 'availability_ipaddress'),
|
||||||
|
get_string('enabled', 'availability_ipaddress'),
|
||||||
|
get_string('actions'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->define_columns($columns);
|
||||||
|
$this->define_headers($headers);
|
||||||
|
|
||||||
|
// Set attributes.
|
||||||
|
$this->set_attribute('class', 'generaltable');
|
||||||
|
$this->sortable(true, 'name', SORT_ASC);
|
||||||
|
$this->no_sorting('description');
|
||||||
|
$this->no_sorting('actions');
|
||||||
|
$this->collapsible(false);
|
||||||
|
$this->pageable(true);
|
||||||
|
$this->is_downloadable(false);
|
||||||
|
|
||||||
|
$this->define_baseurl($baseurl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the SQL query.
|
||||||
|
*
|
||||||
|
* @param int $pagesize Number of records per page.
|
||||||
|
* @param bool $useinitialsbar Whether to use the initials bar.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set_sql_data(int $pagesize = 30, bool $useinitialsbar = false): void {
|
||||||
|
$fields = 'id, name, description, ipaddresses, enabled, timecreated, timemodified';
|
||||||
|
$from = '{availability_ipaddress_pre}';
|
||||||
|
$where = '1=1';
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
$this->set_sql($fields, $from, $where, $params);
|
||||||
|
$this->set_count_sql("SELECT COUNT(*) FROM {availability_ipaddress_pre}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column for name.
|
||||||
|
*
|
||||||
|
* @param \stdClass $range The range record.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function col_name(\stdClass $range): string {
|
||||||
|
return format_string($range->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column for description.
|
||||||
|
*
|
||||||
|
* @param \stdClass $range The range record.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function col_description(\stdClass $range): string {
|
||||||
|
if (empty($range->description)) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
$description = format_string($range->description);
|
||||||
|
// Truncate if too long.
|
||||||
|
if (strlen($description) > 100) {
|
||||||
|
$truncated = substr($description, 0, 97) . '...';
|
||||||
|
|
||||||
|
return html_writer::tag('span', $truncated, ['title' => $description]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column for IP addresses.
|
||||||
|
*
|
||||||
|
* @param \stdClass $range The range record.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function col_ipaddresses(\stdClass $range): string {
|
||||||
|
$ips = s($range->ipaddresses);
|
||||||
|
// Truncate if too long and add tooltip.
|
||||||
|
if (strlen($ips) > 50) {
|
||||||
|
$truncated = substr($ips, 0, 47) . '...';
|
||||||
|
|
||||||
|
return html_writer::tag(
|
||||||
|
'span',
|
||||||
|
html_writer::tag('code', $truncated),
|
||||||
|
['title' => $ips]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html_writer::tag('code', $ips);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column for enabled status.
|
||||||
|
*
|
||||||
|
* @param \stdClass $range The range record.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function col_enabled(\stdClass $range): string {
|
||||||
|
return $range->enabled ? get_string('yes') : get_string('no');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column for actions.
|
||||||
|
*
|
||||||
|
* @param \stdClass $range The range record.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function col_actions(\stdClass $range): string {
|
||||||
|
global $OUTPUT;
|
||||||
|
|
||||||
|
$actions = [];
|
||||||
|
|
||||||
|
// Edit action.
|
||||||
|
$editurl = new moodle_url($this->baseurl, ['action' => 'edit', 'id' => $range->id]);
|
||||||
|
$actions[] = $OUTPUT->action_icon(
|
||||||
|
$editurl,
|
||||||
|
new pix_icon('t/edit', get_string('edit'))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Toggle action.
|
||||||
|
$toggleurl = new moodle_url($this->baseurl, ['action' => 'toggle', 'id' => $range->id, 'sesskey' => sesskey()]);
|
||||||
|
$toggleicon = $range->enabled ? 't/hide' : 't/show';
|
||||||
|
$togglestring = $range->enabled ? get_string('disable') : get_string('enable');
|
||||||
|
|
||||||
|
// Check if range is in use and add confirmation if disabling.
|
||||||
|
if ($range->enabled) {
|
||||||
|
$usage = \availability_ipaddress\helper::is_range_in_use($range->id);
|
||||||
|
if ($usage['inuse']) {
|
||||||
|
// Create confirmation message with usage details.
|
||||||
|
$message = \availability_ipaddress\helper::get_range_usage_html($range->id);
|
||||||
|
$message .= \html_writer::tag(
|
||||||
|
'p',
|
||||||
|
get_string('confirm_disable_range', 'availability_ipaddress'),
|
||||||
|
['class' => 'font-weight-bold']
|
||||||
|
);
|
||||||
|
$actions[] = $OUTPUT->action_icon(
|
||||||
|
$toggleurl,
|
||||||
|
new pix_icon($toggleicon, $togglestring),
|
||||||
|
new confirm_action($message)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$actions[] = $OUTPUT->action_icon($toggleurl, new pix_icon($toggleicon, $togglestring));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Enabling doesn't need confirmation.
|
||||||
|
$actions[] = $OUTPUT->action_icon($toggleurl, new pix_icon($toggleicon, $togglestring));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete action.
|
||||||
|
$deleteurl = new moodle_url($this->baseurl, [
|
||||||
|
'action' => 'delete',
|
||||||
|
'id' => $range->id,
|
||||||
|
'sesskey' => sesskey(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Check if range is in use and add usage info to confirmation.
|
||||||
|
$usage = \availability_ipaddress\helper::is_range_in_use($range->id);
|
||||||
|
if ($usage['inuse']) {
|
||||||
|
$message = \availability_ipaddress\helper::get_range_usage_html($range->id);
|
||||||
|
$message .= \html_writer::tag(
|
||||||
|
'p',
|
||||||
|
get_string('confirm_delete_range', 'availability_ipaddress'),
|
||||||
|
['class' => 'font-weight-bold']
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$message = get_string('confirm_delete_range', 'availability_ipaddress');
|
||||||
|
}
|
||||||
|
|
||||||
|
$actions[] = $OUTPUT->action_icon(
|
||||||
|
$deleteurl,
|
||||||
|
new pix_icon('t/delete', get_string('delete')),
|
||||||
|
new confirm_action($message)
|
||||||
|
);
|
||||||
|
|
||||||
|
return implode(' ', $actions);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
db/install.xml
Normal file
25
db/install.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<XMLDB PATH="availability/condition/ipaddress/db" VERSION="20240104" COMMENT="XMLDB file for availability_ipaddress plugin"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
|
||||||
|
>
|
||||||
|
<TABLES>
|
||||||
|
<TABLE NAME="availability_ipaddress_pre" COMMENT="Stores predefined IP address ranges for availability_ipaddress">
|
||||||
|
<FIELDS>
|
||||||
|
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||||
|
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" COMMENT="Display name for the IP range"/>
|
||||||
|
<FIELD NAME="ipaddresses" TYPE="text" NOTNULL="true" COMMENT="Comma-separated list of IP addresses/ranges"/>
|
||||||
|
<FIELD NAME="description" TYPE="text" NOTNULL="false" COMMENT="Optional description of this IP range"/>
|
||||||
|
<FIELD NAME="enabled" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" COMMENT="Whether this range is available for selection"/>
|
||||||
|
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0"/>
|
||||||
|
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0"/>
|
||||||
|
</FIELDS>
|
||||||
|
<KEYS>
|
||||||
|
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||||
|
</KEYS>
|
||||||
|
<INDEXES>
|
||||||
|
<INDEX NAME="enabled" UNIQUE="false" FIELDS="enabled"/>
|
||||||
|
</INDEXES>
|
||||||
|
</TABLE>
|
||||||
|
</TABLES>
|
||||||
|
</XMLDB>
|
||||||
94
db/upgrade.php
Normal file
94
db/upgrade.php
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade script for availability_ipaddress.
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade function.
|
||||||
|
*
|
||||||
|
* @param int $oldversion The old version of the plugin
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function xmldb_availability_ipaddress_upgrade(int $oldversion): bool {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$dbman = $DB->get_manager();
|
||||||
|
|
||||||
|
if ($oldversion < 2025070400) {
|
||||||
|
// Define table availability_ipaddress_pre to be created.
|
||||||
|
$table = new xmldb_table('availability_ipaddress_pre');
|
||||||
|
|
||||||
|
// Adding fields to table availability_ipaddress_pre.
|
||||||
|
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||||
|
$table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
|
||||||
|
$table->add_field('ipaddresses', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
|
||||||
|
$table->add_field('description', XMLDB_TYPE_TEXT, null, null, null, null, null);
|
||||||
|
$table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1');
|
||||||
|
$table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
|
||||||
|
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
|
||||||
|
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
|
||||||
|
|
||||||
|
// Adding keys to table availability_ipaddress_pre.
|
||||||
|
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
|
||||||
|
|
||||||
|
// Adding indexes to table availability_ipaddress_pre.
|
||||||
|
$table->add_index('enabled_sortorder', XMLDB_INDEX_NOTUNIQUE, ['enabled', 'sortorder']);
|
||||||
|
|
||||||
|
// Conditionally launch create table for availability_ipaddress_pre.
|
||||||
|
if (!$dbman->table_exists($table)) {
|
||||||
|
$dbman->create_table($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Availability_ipaddress savepoint reached.
|
||||||
|
upgrade_plugin_savepoint(true, 2025070400, 'availability', 'ipaddress');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oldversion < 2025080401) {
|
||||||
|
// Remove sortorder field from availability_ipaddress_pre table.
|
||||||
|
$table = new xmldb_table('availability_ipaddress_pre');
|
||||||
|
|
||||||
|
// Drop the index that includes sortorder.
|
||||||
|
$index = new xmldb_index('enabled_sortorder', XMLDB_INDEX_NOTUNIQUE, ['enabled', 'sortorder']);
|
||||||
|
if ($dbman->index_exists($table, $index)) {
|
||||||
|
$dbman->drop_index($table, $index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the sortorder field.
|
||||||
|
$field = new xmldb_field('sortorder');
|
||||||
|
if ($dbman->field_exists($table, $field)) {
|
||||||
|
$dbman->drop_field($table, $field);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new index for enabled only.
|
||||||
|
$index = new xmldb_index('enabled', XMLDB_INDEX_NOTUNIQUE, ['enabled']);
|
||||||
|
if (!$dbman->index_exists($table, $index)) {
|
||||||
|
$dbman->add_index($table, $index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Availability_ipaddress savepoint reached.
|
||||||
|
upgrade_plugin_savepoint(true, 2025080401, 'availability', 'ipaddress');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
6
environment.xml
Normal file
6
environment.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<COMPATIBILITY_MATRIX>
|
||||||
|
<PLUGIN name="availability_ipaddress">
|
||||||
|
<PHP version="7.3" level="required"/>
|
||||||
|
</PLUGIN>
|
||||||
|
</COMPATIBILITY_MATRIX>
|
||||||
@@ -23,16 +23,54 @@
|
|||||||
* @copyright 2019-05-14 Mfreak.nl | LdesignMedia.nl - Luuk Verhoeven
|
* @copyright 2019-05-14 Mfreak.nl | LdesignMedia.nl - Luuk Verhoeven
|
||||||
* @author Luuk Verhoeven
|
* @author Luuk Verhoeven
|
||||||
**/
|
**/
|
||||||
$string['pluginname'] = 'IP address';
|
|
||||||
$string['title'] = 'IP address';
|
|
||||||
$string['description'] = 'Restrict access by ip-address or subnet';
|
|
||||||
$string['require_condition'] = 'Matching ip-address/subnet';
|
|
||||||
|
|
||||||
// Javascript strings.
|
// We like comments and our own sorting.
|
||||||
$string['js:ipaddress'] = 'Require network address';
|
// phpcs:disable moodle.Files.LangFilesOrdering.UnexpectedComment
|
||||||
|
// phpcs:disable moodle.Files.LangFilesOrdering.IncorrectOrder
|
||||||
|
|
||||||
|
$string['pluginname'] = 'IP address';
|
||||||
|
$string['description'] = 'Restrict access by ip-address or subnet';
|
||||||
|
$string['title'] = 'IP address';
|
||||||
|
$string['require_condition'] = 'ip-address/subnet is allowed (Your IP:{$a})';
|
||||||
|
$string['require_condition_not'] = 'ip-address/subnet is not blocked (Your IP:{$a})';
|
||||||
|
|
||||||
// Errors.
|
// Errors.
|
||||||
$string['error_ipaddress'] = 'Incorrect ip-address/subnet format';
|
$string['error_ipaddress'] = 'Incorrect ip-address/subnet format';
|
||||||
|
|
||||||
|
// Javascript strings.
|
||||||
|
$string['js:ipaddress'] = 'Require network address';
|
||||||
|
|
||||||
// Privacy provider.
|
// Privacy provider.
|
||||||
$string['privacy:metadata'] = 'The restriction by activity ipaddress plugin does not store any personal data.';
|
$string['privacy:metadata'] = 'The restriction by activity ipaddress plugin does not store any personal data.';
|
||||||
|
|
||||||
|
// Predefined ranges.
|
||||||
|
$string['setting:manage_predefined_ranges'] = 'IP address - Manage predefined IP ranges';
|
||||||
|
$string['manage_predefined_ranges'] = 'Manage predefined IP ranges';
|
||||||
|
$string['predefined_ranges'] = 'Predefined IP ranges';
|
||||||
|
$string['custom_ipaddress'] = 'Custom IP address(es)';
|
||||||
|
$string['use_predefined'] = 'Use predefined IP addresses';
|
||||||
|
$string['range_name'] = 'Range name';
|
||||||
|
$string['range_name_help'] = 'A descriptive name for this IP range, e.g., "Campus Network" or "Library Computers"';
|
||||||
|
$string['ipaddresses'] = 'IP addresses';
|
||||||
|
$string['ipaddresses_help'] = 'Enter IP addresses separated by commas. Supports single IPs (192.168.1.1), ranges (192.168.1.1-255), and subnets (192.168.1.0/24)';
|
||||||
|
$string['ipaddresses_help_help'] = '<p>Enter one or more IP addresses or ranges, separated by commas.</p>
|
||||||
|
<p><strong>Examples:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Single IP:</strong> <code>192.168.1.1</code></li>
|
||||||
|
<li><strong>IP range:</strong> <code>192.168.1.1-255</code></li>
|
||||||
|
<li><strong>Subnet:</strong> <code>192.168.1.0/24</code></li>
|
||||||
|
<li><strong>Multiple:</strong> <code>192.168.1.1,10.0.0.0/8,172.16.0.1-255</code></li>
|
||||||
|
</ul>';
|
||||||
|
$string['enabled'] = 'Enabled';
|
||||||
|
$string['sortorder'] = 'Sort order';
|
||||||
|
$string['existing_ranges'] = 'Existing IP ranges';
|
||||||
|
$string['range_created'] = 'IP range created successfully';
|
||||||
|
$string['range_updated'] = 'IP range updated successfully';
|
||||||
|
$string['range_deleted'] = 'IP range deleted successfully';
|
||||||
|
$string['confirm_delete_range'] = 'Deleting this IP range will remove it from all restrictions where it is used. Are you sure you want to permanently delete it?';
|
||||||
|
$string['range_in_use_count'] = 'This IP range is currently used in {$a} restriction(s).';
|
||||||
|
$string['and_x_more'] = '... and {$a} more.';
|
||||||
|
$string['confirm_disable_range'] = 'This IP range is currently in use. Disabling it will remove it from all restrictions where it is used. Are you sure you want to continue?';
|
||||||
|
$string['range_in_use_title'] = 'IP Range In Use';
|
||||||
|
$string['range_disabled_and_removed'] = 'IP range disabled and removed from {$a} restriction(s).';
|
||||||
|
$string['range_deleted_and_removed'] = 'IP range deleted and removed from {$a} restriction(s).';
|
||||||
|
|||||||
143
manage_ranges.php
Normal file
143
manage_ranges.php
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage predefined IP address ranges.
|
||||||
|
*
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/../../../config.php');
|
||||||
|
require_once($CFG->libdir . '/adminlib.php');
|
||||||
|
|
||||||
|
admin_externalpage_setup('availability_ipaddress_ranges');
|
||||||
|
|
||||||
|
$action = optional_param('action', '', PARAM_ALPHA);
|
||||||
|
$id = optional_param('id', 0, PARAM_INT);
|
||||||
|
|
||||||
|
$PAGE->set_url('/availability/condition/ipaddress/manage_ranges.php');
|
||||||
|
$PAGE->set_title(get_string('manage_predefined_ranges', 'availability_ipaddress'));
|
||||||
|
$PAGE->set_heading(get_string('manage_predefined_ranges', 'availability_ipaddress'));
|
||||||
|
|
||||||
|
// Handle actions.
|
||||||
|
if ($action === 'delete' && confirm_sesskey()) {
|
||||||
|
// Remove from all restrictions before deleting.
|
||||||
|
$removed = \availability_ipaddress\helper::remove_range_from_restrictions($id);
|
||||||
|
|
||||||
|
$DB->delete_records('availability_ipaddress_pre', ['id' => $id]);
|
||||||
|
|
||||||
|
if ($removed > 0) {
|
||||||
|
redirect(
|
||||||
|
$PAGE->url,
|
||||||
|
get_string('range_deleted_and_removed', 'availability_ipaddress', $removed),
|
||||||
|
null,
|
||||||
|
\core\output\notification::NOTIFY_SUCCESS
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
redirect(
|
||||||
|
$PAGE->url,
|
||||||
|
get_string('range_deleted', 'availability_ipaddress'),
|
||||||
|
null,
|
||||||
|
\core\output\notification::NOTIFY_SUCCESS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'toggle' && confirm_sesskey()) {
|
||||||
|
$record = $DB->get_record('availability_ipaddress_pre', ['id' => $id], '*', MUST_EXIST);
|
||||||
|
|
||||||
|
$record->enabled = !$record->enabled;
|
||||||
|
$record->timemodified = time();
|
||||||
|
$DB->update_record('availability_ipaddress_pre', $record);
|
||||||
|
|
||||||
|
// If we just disabled the range, remove it from all restrictions.
|
||||||
|
if (!$record->enabled) {
|
||||||
|
$removed = \availability_ipaddress\helper::remove_range_from_restrictions($id);
|
||||||
|
if ($removed > 0) {
|
||||||
|
redirect(
|
||||||
|
$PAGE->url,
|
||||||
|
get_string('range_disabled_and_removed', 'availability_ipaddress', $removed),
|
||||||
|
null,
|
||||||
|
\core\output\notification::NOTIFY_SUCCESS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect($PAGE->url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle form submission for adding/editing.
|
||||||
|
if ($action === 'add' || $action === 'edit') {
|
||||||
|
$formurl = new moodle_url($PAGE->url, ['action' => $action, 'id' => $id]);
|
||||||
|
$form = new \availability_ipaddress\form\range_form($formurl, ['id' => $id]);
|
||||||
|
|
||||||
|
if ($form->is_cancelled()) {
|
||||||
|
redirect($PAGE->url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data = $form->get_data()) {
|
||||||
|
if ($data->id) {
|
||||||
|
// Update existing.
|
||||||
|
$data->timemodified = time();
|
||||||
|
$DB->update_record('availability_ipaddress_pre', $data);
|
||||||
|
redirect(
|
||||||
|
$PAGE->url,
|
||||||
|
get_string('range_updated', 'availability_ipaddress'),
|
||||||
|
null,
|
||||||
|
\core\output\notification::NOTIFY_SUCCESS
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Create new.
|
||||||
|
$data->timecreated = time();
|
||||||
|
$data->timemodified = time();
|
||||||
|
$DB->insert_record('availability_ipaddress_pre', $data);
|
||||||
|
redirect(
|
||||||
|
$PAGE->url,
|
||||||
|
get_string('range_created', 'availability_ipaddress'),
|
||||||
|
null,
|
||||||
|
\core\output\notification::NOTIFY_SUCCESS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load data for editing.
|
||||||
|
if ($action === 'edit' && $id) {
|
||||||
|
$record = $DB->get_record('availability_ipaddress_pre', ['id' => $id], '*', MUST_EXIST);
|
||||||
|
$form->set_data($record);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $OUTPUT->header();
|
||||||
|
$form->display();
|
||||||
|
echo $OUTPUT->footer();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display page.
|
||||||
|
echo $OUTPUT->header();
|
||||||
|
|
||||||
|
// Add new button.
|
||||||
|
echo $OUTPUT->single_button(new moodle_url($PAGE->url, ['action' => 'add']), get_string('add'), 'get');
|
||||||
|
|
||||||
|
// Create and display table.
|
||||||
|
$table = new \availability_ipaddress\table\ipranges_table('availability-ipaddress-ranges', $PAGE->url);
|
||||||
|
$table->set_sql_data(30);
|
||||||
|
$table->out(30, true);
|
||||||
|
|
||||||
|
echo $OUTPUT->footer();
|
||||||
46
settings.php
Normal file
46
settings.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings for availability_ipaddress.
|
||||||
|
*
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*
|
||||||
|
* @package availability_ipaddress
|
||||||
|
* @copyright 04/08/2025 LdesignMedia.nl - Luuk Verhoeven
|
||||||
|
* @author Vincent Cornelis
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
global $ADMIN;
|
||||||
|
|
||||||
|
if ($hassiteconfig) {
|
||||||
|
// Add external page for managing IP ranges.
|
||||||
|
$ADMIN->add(
|
||||||
|
'availabilitysettings',
|
||||||
|
new admin_externalpage(
|
||||||
|
'availability_ipaddress_ranges',
|
||||||
|
get_string('setting:manage_predefined_ranges', 'availability_ipaddress'),
|
||||||
|
new moodle_url('/availability/condition/ipaddress/manage_ranges.php'),
|
||||||
|
'moodle/site:config'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the visible name of auto generated settings page to empty string,
|
||||||
|
// to avoid showing it in the settings tree, as we only add the external page.
|
||||||
|
$settings->visiblename = '';
|
||||||
@@ -27,7 +27,8 @@
|
|||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$plugin->component = 'availability_ipaddress';
|
$plugin->component = 'availability_ipaddress';
|
||||||
$plugin->version = 2020061500;
|
$plugin->version = 2026031800;
|
||||||
$plugin->release = '3.9.0';
|
$plugin->release = '5.1.1';
|
||||||
$plugin->requires = 2016120500;
|
$plugin->requires = 2016120500;
|
||||||
$plugin->maturity = MATURITY_BETA;
|
$plugin->maturity = MATURITY_STABLE;
|
||||||
|
$plugin->supported = [400, 501];
|
||||||
|
|||||||
@@ -43,9 +43,11 @@ M.availability_ipaddress.form = Y.Object(M.core_availability.plugin);
|
|||||||
* @method initInner
|
* @method initInner
|
||||||
* @param {Array} param Array of objects
|
* @param {Array} param Array of objects
|
||||||
*/
|
*/
|
||||||
M.availability_ipaddress.form.initInner = function() {
|
M.availability_ipaddress.form.initInner = function(param) {
|
||||||
"use strict";
|
"use strict";
|
||||||
Y.log('M.availability_ipaddress 1.10');
|
Y.log('M.availability_ipaddress 1.10');
|
||||||
|
// Store predefined ranges from backend.
|
||||||
|
this.predefinedRanges = param || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,24 +82,68 @@ M.availability_ipaddress.form.getValue = function(field, node) {
|
|||||||
*/
|
*/
|
||||||
M.availability_ipaddress.form.getNode = function(json) {
|
M.availability_ipaddress.form.getNode = function(json) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var html, node, root, id;
|
var html, node, root, id, selectId, i, range;
|
||||||
|
|
||||||
// Make sure we work with unique id.
|
// Make sure we work with unique id.
|
||||||
id = 'ipaddresses' + M.availability_ipaddress.form.instId;
|
id = 'ipaddresses' + M.availability_ipaddress.form.instId;
|
||||||
|
selectId = 'predefined' + M.availability_ipaddress.form.instId;
|
||||||
M.availability_ipaddress.form.instId += 1;
|
M.availability_ipaddress.form.instId += 1;
|
||||||
|
|
||||||
// Create HTML structure.
|
// Create HTML structure.
|
||||||
html = '';
|
html = '<div class="availability-ipaddress-container">';
|
||||||
html += '<span class="availability-group"><label for="' + id + '"><span class="p-r-1">' +
|
|
||||||
M.util.get_string('title', 'availability_ipaddress') + ' </span></label>';
|
// Add predefined ranges if available.
|
||||||
html += '<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="' + id + '">';
|
if (this.predefinedRanges && this.predefinedRanges.length > 0) {
|
||||||
node = Y.Node.create('<span class="form-inline">' + html + '</span>');
|
html += '<div class="availability-group">';
|
||||||
|
html += '<label><span class="p-r-1">' +
|
||||||
|
M.util.get_string('use_predefined', 'availability_ipaddress') + '</span></label>';
|
||||||
|
html += '<select name="predefined_ranges" id="' + selectId +
|
||||||
|
'" multiple="multiple" class="form-control" style="min-height: 100px;">';
|
||||||
|
|
||||||
|
for (i = 0; i < this.predefinedRanges.length; i++) {
|
||||||
|
range = this.predefinedRanges[i];
|
||||||
|
html += '<option value="' + range.id + '" data-ipaddresses="' + Y.Escape.html(range.ipaddresses) + '">';
|
||||||
|
html += Y.Escape.html(range.name);
|
||||||
|
html += '</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</select>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div class="availability-group" style="margin-top: 10px;">';
|
||||||
|
html += '<label for="' + id + '"><span class="p-r-1">' +
|
||||||
|
M.util.get_string('custom_ipaddress', 'availability_ipaddress') + '</span></label>';
|
||||||
|
} else {
|
||||||
|
html += '<div class="availability-group">';
|
||||||
|
html += '<label for="' + id + '"><span class="p-r-1">' +
|
||||||
|
M.util.get_string('title', 'availability_ipaddress') + '</span></label>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="' +
|
||||||
|
id + '" class="form-control">';
|
||||||
|
html += '</div>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
node = Y.Node.create('<div>' + html + '</div>');
|
||||||
|
|
||||||
// Set initial values, if specified.
|
// Set initial values, if specified.
|
||||||
if (json.ipaddresses !== undefined) {
|
if (json.ipaddresses !== undefined) {
|
||||||
node.one('input[name=ipaddresses]').set('value', json.ipaddresses);
|
node.one('input[name=ipaddresses]').set('value', json.ipaddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set selected predefined ranges if specified.
|
||||||
|
if (json.predefined_ranges !== undefined && this.predefinedRanges && this.predefinedRanges.length > 0) {
|
||||||
|
var select = node.one('select[name=predefined_ranges]');
|
||||||
|
if (select) {
|
||||||
|
json.predefined_ranges.forEach(function(rangeId) {
|
||||||
|
var option = select.one('option[value="' + rangeId + '"]');
|
||||||
|
if (option) {
|
||||||
|
option.set('selected', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add event handlers (first time only).
|
// Add event handlers (first time only).
|
||||||
if (!M.availability_ipaddress.form.addedEvents) {
|
if (!M.availability_ipaddress.form.addedEvents) {
|
||||||
M.availability_ipaddress.form.addedEvents = true;
|
M.availability_ipaddress.form.addedEvents = true;
|
||||||
@@ -106,6 +152,11 @@ M.availability_ipaddress.form.getNode = function(json) {
|
|||||||
// Trigger the updating of the hidden availability data whenever the ipaddress field changes.
|
// Trigger the updating of the hidden availability data whenever the ipaddress field changes.
|
||||||
M.core_availability.form.update();
|
M.core_availability.form.update();
|
||||||
}, '.availability_ipaddress input[name=ipaddresses]');
|
}, '.availability_ipaddress input[name=ipaddresses]');
|
||||||
|
|
||||||
|
root.delegate('change', function() {
|
||||||
|
// Trigger the updating when predefined ranges are selected.
|
||||||
|
M.core_availability.form.update();
|
||||||
|
}, '.availability_ipaddress select[name=predefined_ranges]');
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@@ -120,6 +171,10 @@ M.availability_ipaddress.form.getNode = function(json) {
|
|||||||
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
||||||
'use strict';
|
'use strict';
|
||||||
Y.log(ipaddresses);
|
Y.log(ipaddresses);
|
||||||
|
// Return true for empty string - it's valid to have no custom IPs
|
||||||
|
if (!ipaddresses || ipaddresses.trim() === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
ipaddresses = ipaddresses.split(',');
|
ipaddresses = ipaddresses.split(',');
|
||||||
for (var i in ipaddresses) {
|
for (var i in ipaddresses) {
|
||||||
|
|
||||||
@@ -131,6 +186,18 @@ M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ipv4Regex = new RegExp(
|
||||||
|
'^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)' +
|
||||||
|
'(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-' +
|
||||||
|
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$',
|
||||||
|
'gm'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ipv4Regex.test(ipaddresses[i])) {
|
||||||
|
Y.log('Correct ipv4 range.');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (new RegExp(M.availability_ipaddress.v6)
|
if (new RegExp(M.availability_ipaddress.v6)
|
||||||
.test(ipaddresses[i])) {
|
.test(ipaddresses[i])) {
|
||||||
Y.log('Correct ipv6');
|
Y.log('Correct ipv6');
|
||||||
@@ -166,6 +233,20 @@ M.availability_ipaddress.form.fillValue = function(value, node) {
|
|||||||
// with the structure used in the __construct and save functions
|
// with the structure used in the __construct and save functions
|
||||||
// within condition.php.
|
// within condition.php.
|
||||||
value.ipaddresses = this.getValue('ipaddresses', node);
|
value.ipaddresses = this.getValue('ipaddresses', node);
|
||||||
|
|
||||||
|
// Get selected predefined ranges.
|
||||||
|
var select = node.one('select[name=predefined_ranges]');
|
||||||
|
if (select) {
|
||||||
|
var selectedRanges = [];
|
||||||
|
select.get('options').each(function(option) {
|
||||||
|
if (option.get('selected')) {
|
||||||
|
selectedRanges.push(parseInt(option.get('value')));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (selectedRanges.length > 0) {
|
||||||
|
value.predefined_ranges = selectedRanges;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,10 +259,12 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
|
|||||||
var value = {};
|
var value = {};
|
||||||
this.fillValue(value, node);
|
this.fillValue(value, node);
|
||||||
|
|
||||||
// Basic ipaddresses checks.
|
// Basic ipaddresses checks - only validate if not empty.
|
||||||
if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
if (value.ipaddresses && value.ipaddresses.trim() !== '' &&
|
||||||
|
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||||
errors.push('availability_ipaddress:error_ipaddress');
|
errors.push('availability_ipaddress:error_ipaddress');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}, '@VERSION@', {"requires": ["base", "node", "event", "moodle-core_availability-form"]});
|
}, '@VERSION@', {"requires": ["base", "node", "event", "moodle-core_availability-form"]});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -43,8 +43,10 @@ M.availability_ipaddress.form = Y.Object(M.core_availability.plugin);
|
|||||||
* @method initInner
|
* @method initInner
|
||||||
* @param {Array} param Array of objects
|
* @param {Array} param Array of objects
|
||||||
*/
|
*/
|
||||||
M.availability_ipaddress.form.initInner = function() {
|
M.availability_ipaddress.form.initInner = function(param) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
// Store predefined ranges from backend.
|
||||||
|
this.predefinedRanges = param || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,24 +79,68 @@ M.availability_ipaddress.form.getValue = function(field, node) {
|
|||||||
*/
|
*/
|
||||||
M.availability_ipaddress.form.getNode = function(json) {
|
M.availability_ipaddress.form.getNode = function(json) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var html, node, root, id;
|
var html, node, root, id, selectId, i, range;
|
||||||
|
|
||||||
// Make sure we work with unique id.
|
// Make sure we work with unique id.
|
||||||
id = 'ipaddresses' + M.availability_ipaddress.form.instId;
|
id = 'ipaddresses' + M.availability_ipaddress.form.instId;
|
||||||
|
selectId = 'predefined' + M.availability_ipaddress.form.instId;
|
||||||
M.availability_ipaddress.form.instId += 1;
|
M.availability_ipaddress.form.instId += 1;
|
||||||
|
|
||||||
// Create HTML structure.
|
// Create HTML structure.
|
||||||
html = '';
|
html = '<div class="availability-ipaddress-container">';
|
||||||
html += '<span class="availability-group"><label for="' + id + '"><span class="p-r-1">' +
|
|
||||||
M.util.get_string('title', 'availability_ipaddress') + ' </span></label>';
|
// Add predefined ranges if available.
|
||||||
html += '<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="' + id + '">';
|
if (this.predefinedRanges && this.predefinedRanges.length > 0) {
|
||||||
node = Y.Node.create('<span class="form-inline">' + html + '</span>');
|
html += '<div class="availability-group">';
|
||||||
|
html += '<label><span class="p-r-1">' +
|
||||||
|
M.util.get_string('use_predefined', 'availability_ipaddress') + '</span></label>';
|
||||||
|
html += '<select name="predefined_ranges" id="' + selectId +
|
||||||
|
'" multiple="multiple" class="form-control" style="min-height: 100px;">';
|
||||||
|
|
||||||
|
for (i = 0; i < this.predefinedRanges.length; i++) {
|
||||||
|
range = this.predefinedRanges[i];
|
||||||
|
html += '<option value="' + range.id + '" data-ipaddresses="' + Y.Escape.html(range.ipaddresses) + '">';
|
||||||
|
html += Y.Escape.html(range.name);
|
||||||
|
html += '</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</select>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div class="availability-group" style="margin-top: 10px;">';
|
||||||
|
html += '<label for="' + id + '"><span class="p-r-1">' +
|
||||||
|
M.util.get_string('custom_ipaddress', 'availability_ipaddress') + '</span></label>';
|
||||||
|
} else {
|
||||||
|
html += '<div class="availability-group">';
|
||||||
|
html += '<label for="' + id + '"><span class="p-r-1">' +
|
||||||
|
M.util.get_string('title', 'availability_ipaddress') + '</span></label>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="' +
|
||||||
|
id + '" class="form-control">';
|
||||||
|
html += '</div>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
node = Y.Node.create('<div>' + html + '</div>');
|
||||||
|
|
||||||
// Set initial values, if specified.
|
// Set initial values, if specified.
|
||||||
if (json.ipaddresses !== undefined) {
|
if (json.ipaddresses !== undefined) {
|
||||||
node.one('input[name=ipaddresses]').set('value', json.ipaddresses);
|
node.one('input[name=ipaddresses]').set('value', json.ipaddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set selected predefined ranges if specified.
|
||||||
|
if (json.predefined_ranges !== undefined && this.predefinedRanges && this.predefinedRanges.length > 0) {
|
||||||
|
var select = node.one('select[name=predefined_ranges]');
|
||||||
|
if (select) {
|
||||||
|
json.predefined_ranges.forEach(function(rangeId) {
|
||||||
|
var option = select.one('option[value="' + rangeId + '"]');
|
||||||
|
if (option) {
|
||||||
|
option.set('selected', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add event handlers (first time only).
|
// Add event handlers (first time only).
|
||||||
if (!M.availability_ipaddress.form.addedEvents) {
|
if (!M.availability_ipaddress.form.addedEvents) {
|
||||||
M.availability_ipaddress.form.addedEvents = true;
|
M.availability_ipaddress.form.addedEvents = true;
|
||||||
@@ -103,6 +149,11 @@ M.availability_ipaddress.form.getNode = function(json) {
|
|||||||
// Trigger the updating of the hidden availability data whenever the ipaddress field changes.
|
// Trigger the updating of the hidden availability data whenever the ipaddress field changes.
|
||||||
M.core_availability.form.update();
|
M.core_availability.form.update();
|
||||||
}, '.availability_ipaddress input[name=ipaddresses]');
|
}, '.availability_ipaddress input[name=ipaddresses]');
|
||||||
|
|
||||||
|
root.delegate('change', function() {
|
||||||
|
// Trigger the updating when predefined ranges are selected.
|
||||||
|
M.core_availability.form.update();
|
||||||
|
}, '.availability_ipaddress select[name=predefined_ranges]');
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@@ -116,6 +167,10 @@ M.availability_ipaddress.form.getNode = function(json) {
|
|||||||
*/
|
*/
|
||||||
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
// Return true for empty string - it's valid to have no custom IPs
|
||||||
|
if (!ipaddresses || ipaddresses.trim() === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
ipaddresses = ipaddresses.split(',');
|
ipaddresses = ipaddresses.split(',');
|
||||||
for (var i in ipaddresses) {
|
for (var i in ipaddresses) {
|
||||||
|
|
||||||
@@ -126,6 +181,17 @@ M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ipv4Regex = new RegExp(
|
||||||
|
'^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)' +
|
||||||
|
'(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-' +
|
||||||
|
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$',
|
||||||
|
'gm'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ipv4Regex.test(ipaddresses[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (new RegExp(M.availability_ipaddress.v6)
|
if (new RegExp(M.availability_ipaddress.v6)
|
||||||
.test(ipaddresses[i])) {
|
.test(ipaddresses[i])) {
|
||||||
continue;
|
continue;
|
||||||
@@ -157,6 +223,20 @@ M.availability_ipaddress.form.fillValue = function(value, node) {
|
|||||||
// with the structure used in the __construct and save functions
|
// with the structure used in the __construct and save functions
|
||||||
// within condition.php.
|
// within condition.php.
|
||||||
value.ipaddresses = this.getValue('ipaddresses', node);
|
value.ipaddresses = this.getValue('ipaddresses', node);
|
||||||
|
|
||||||
|
// Get selected predefined ranges.
|
||||||
|
var select = node.one('select[name=predefined_ranges]');
|
||||||
|
if (select) {
|
||||||
|
var selectedRanges = [];
|
||||||
|
select.get('options').each(function(option) {
|
||||||
|
if (option.get('selected')) {
|
||||||
|
selectedRanges.push(parseInt(option.get('value')));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (selectedRanges.length > 0) {
|
||||||
|
value.predefined_ranges = selectedRanges;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,10 +249,12 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
|
|||||||
var value = {};
|
var value = {};
|
||||||
this.fillValue(value, node);
|
this.fillValue(value, node);
|
||||||
|
|
||||||
// Basic ipaddresses checks.
|
// Basic ipaddresses checks - only validate if not empty.
|
||||||
if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
if (value.ipaddresses && value.ipaddresses.trim() !== '' &&
|
||||||
|
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||||
errors.push('availability_ipaddress:error_ipaddress');
|
errors.push('availability_ipaddress:error_ipaddress');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}, '@VERSION@', {"requires": ["base", "node", "event", "moodle-core_availability-form"]});
|
}, '@VERSION@', {"requires": ["base", "node", "event", "moodle-core_availability-form"]});
|
||||||
|
|||||||
100
yui/src/form/js/form.js
vendored
100
yui/src/form/js/form.js
vendored
@@ -41,9 +41,11 @@ M.availability_ipaddress.form = Y.Object(M.core_availability.plugin);
|
|||||||
* @method initInner
|
* @method initInner
|
||||||
* @param {Array} param Array of objects
|
* @param {Array} param Array of objects
|
||||||
*/
|
*/
|
||||||
M.availability_ipaddress.form.initInner = function() {
|
M.availability_ipaddress.form.initInner = function(param) {
|
||||||
"use strict";
|
"use strict";
|
||||||
Y.log('M.availability_ipaddress 1.10');
|
Y.log('M.availability_ipaddress 1.10');
|
||||||
|
// Store predefined ranges from backend.
|
||||||
|
this.predefinedRanges = param || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,24 +80,68 @@ M.availability_ipaddress.form.getValue = function(field, node) {
|
|||||||
*/
|
*/
|
||||||
M.availability_ipaddress.form.getNode = function(json) {
|
M.availability_ipaddress.form.getNode = function(json) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var html, node, root, id;
|
var html, node, root, id, selectId, i, range;
|
||||||
|
|
||||||
// Make sure we work with unique id.
|
// Make sure we work with unique id.
|
||||||
id = 'ipaddresses' + M.availability_ipaddress.form.instId;
|
id = 'ipaddresses' + M.availability_ipaddress.form.instId;
|
||||||
|
selectId = 'predefined' + M.availability_ipaddress.form.instId;
|
||||||
M.availability_ipaddress.form.instId += 1;
|
M.availability_ipaddress.form.instId += 1;
|
||||||
|
|
||||||
// Create HTML structure.
|
// Create HTML structure.
|
||||||
html = '';
|
html = '<div class="availability-ipaddress-container">';
|
||||||
html += '<span class="availability-group"><label for="' + id + '"><span class="p-r-1">' +
|
|
||||||
M.util.get_string('title', 'availability_ipaddress') + ' </span></label>';
|
// Add predefined ranges if available.
|
||||||
html += '<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="' + id + '">';
|
if (this.predefinedRanges && this.predefinedRanges.length > 0) {
|
||||||
node = Y.Node.create('<span class="form-inline">' + html + '</span>');
|
html += '<div class="availability-group">';
|
||||||
|
html += '<label><span class="p-r-1">' +
|
||||||
|
M.util.get_string('use_predefined', 'availability_ipaddress') + '</span></label>';
|
||||||
|
html += '<select name="predefined_ranges" id="' + selectId +
|
||||||
|
'" multiple="multiple" class="form-control" style="min-height: 100px;">';
|
||||||
|
|
||||||
|
for (i = 0; i < this.predefinedRanges.length; i++) {
|
||||||
|
range = this.predefinedRanges[i];
|
||||||
|
html += '<option value="' + range.id + '" data-ipaddresses="' + Y.Escape.html(range.ipaddresses) + '">';
|
||||||
|
html += Y.Escape.html(range.name);
|
||||||
|
html += '</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</select>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div class="availability-group" style="margin-top: 10px;">';
|
||||||
|
html += '<label for="' + id + '"><span class="p-r-1">' +
|
||||||
|
M.util.get_string('custom_ipaddress', 'availability_ipaddress') + '</span></label>';
|
||||||
|
} else {
|
||||||
|
html += '<div class="availability-group">';
|
||||||
|
html += '<label for="' + id + '"><span class="p-r-1">' +
|
||||||
|
M.util.get_string('title', 'availability_ipaddress') + '</span></label>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="' +
|
||||||
|
id + '" class="form-control">';
|
||||||
|
html += '</div>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
node = Y.Node.create('<div>' + html + '</div>');
|
||||||
|
|
||||||
// Set initial values, if specified.
|
// Set initial values, if specified.
|
||||||
if (json.ipaddresses !== undefined) {
|
if (json.ipaddresses !== undefined) {
|
||||||
node.one('input[name=ipaddresses]').set('value', json.ipaddresses);
|
node.one('input[name=ipaddresses]').set('value', json.ipaddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set selected predefined ranges if specified.
|
||||||
|
if (json.predefined_ranges !== undefined && this.predefinedRanges && this.predefinedRanges.length > 0) {
|
||||||
|
var select = node.one('select[name=predefined_ranges]');
|
||||||
|
if (select) {
|
||||||
|
json.predefined_ranges.forEach(function(rangeId) {
|
||||||
|
var option = select.one('option[value="' + rangeId + '"]');
|
||||||
|
if (option) {
|
||||||
|
option.set('selected', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add event handlers (first time only).
|
// Add event handlers (first time only).
|
||||||
if (!M.availability_ipaddress.form.addedEvents) {
|
if (!M.availability_ipaddress.form.addedEvents) {
|
||||||
M.availability_ipaddress.form.addedEvents = true;
|
M.availability_ipaddress.form.addedEvents = true;
|
||||||
@@ -104,6 +150,11 @@ M.availability_ipaddress.form.getNode = function(json) {
|
|||||||
// Trigger the updating of the hidden availability data whenever the ipaddress field changes.
|
// Trigger the updating of the hidden availability data whenever the ipaddress field changes.
|
||||||
M.core_availability.form.update();
|
M.core_availability.form.update();
|
||||||
}, '.availability_ipaddress input[name=ipaddresses]');
|
}, '.availability_ipaddress input[name=ipaddresses]');
|
||||||
|
|
||||||
|
root.delegate('change', function() {
|
||||||
|
// Trigger the updating when predefined ranges are selected.
|
||||||
|
M.core_availability.form.update();
|
||||||
|
}, '.availability_ipaddress select[name=predefined_ranges]');
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@@ -118,6 +169,10 @@ M.availability_ipaddress.form.getNode = function(json) {
|
|||||||
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
||||||
'use strict';
|
'use strict';
|
||||||
Y.log(ipaddresses);
|
Y.log(ipaddresses);
|
||||||
|
// Return true for empty string - it's valid to have no custom IPs
|
||||||
|
if (!ipaddresses || ipaddresses.trim() === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
ipaddresses = ipaddresses.split(',');
|
ipaddresses = ipaddresses.split(',');
|
||||||
for (var i in ipaddresses) {
|
for (var i in ipaddresses) {
|
||||||
|
|
||||||
@@ -129,6 +184,18 @@ M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ipv4Regex = new RegExp(
|
||||||
|
'^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)' +
|
||||||
|
'(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-' +
|
||||||
|
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$',
|
||||||
|
'gm'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ipv4Regex.test(ipaddresses[i])) {
|
||||||
|
Y.log('Correct ipv4 range.');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (new RegExp(M.availability_ipaddress.v6)
|
if (new RegExp(M.availability_ipaddress.v6)
|
||||||
.test(ipaddresses[i])) {
|
.test(ipaddresses[i])) {
|
||||||
Y.log('Correct ipv6');
|
Y.log('Correct ipv6');
|
||||||
@@ -164,6 +231,20 @@ M.availability_ipaddress.form.fillValue = function(value, node) {
|
|||||||
// with the structure used in the __construct and save functions
|
// with the structure used in the __construct and save functions
|
||||||
// within condition.php.
|
// within condition.php.
|
||||||
value.ipaddresses = this.getValue('ipaddresses', node);
|
value.ipaddresses = this.getValue('ipaddresses', node);
|
||||||
|
|
||||||
|
// Get selected predefined ranges.
|
||||||
|
var select = node.one('select[name=predefined_ranges]');
|
||||||
|
if (select) {
|
||||||
|
var selectedRanges = [];
|
||||||
|
select.get('options').each(function(option) {
|
||||||
|
if (option.get('selected')) {
|
||||||
|
selectedRanges.push(parseInt(option.get('value')));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (selectedRanges.length > 0) {
|
||||||
|
value.predefined_ranges = selectedRanges;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,8 +257,9 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
|
|||||||
var value = {};
|
var value = {};
|
||||||
this.fillValue(value, node);
|
this.fillValue(value, node);
|
||||||
|
|
||||||
// Basic ipaddresses checks.
|
// Basic ipaddresses checks - only validate if not empty.
|
||||||
if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
if (value.ipaddresses && value.ipaddresses.trim() !== '' &&
|
||||||
|
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||||
errors.push('availability_ipaddress:error_ipaddress');
|
errors.push('availability_ipaddress:error_ipaddress');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user