[docs] Fixed various spelling mistakes and typos#1399
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis pull request fixes spelling and grammar mistakes in comments, docstrings, HTML template comments, Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
openwisp_controller/connection/tests/test_notifications.py (1)
51-57:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAlign the comment with the actual notification types.
The block still mentions
config_error/device_registered, but the code unregistersconnection_is_not_working/connection_is_working, so the comment is misleading.As per coding guidelines, comments should explain why code is shaped a certain way, and this one now documents the wrong thing.
🛠️ Suggested comment fix
- # Unregister "config_error" and "device_registered" notification - # types to avoid getting raising ImproperlyConfigured exceptions + # Unregister the connection notification types so registration + # does not raise ImproperlyConfigured.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openwisp_controller/connection/tests/test_notifications.py` around lines 51 - 57, The comment in the block starting with "Simulates if 'default notification type is already unregistered" incorrectly references the notification types config_error and device_registered, but the actual code unregisters connection_is_not_working and connection_is_working. Update the comment text to accurately reflect the actual notification types being unregistered by the unregister_notification_type calls that follow, so the comment correctly documents why these specific unregistrations are needed.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@openwisp_controller/connection/tests/test_notifications.py`:
- Around line 51-57: The comment in the block starting with "Simulates if
'default notification type is already unregistered" incorrectly references the
notification types config_error and device_registered, but the actual code
unregisters connection_is_not_working and connection_is_working. Update the
comment text to accurately reflect the actual notification types being
unregistered by the unregister_notification_type calls that follow, so the
comment correctly documents why these specific unregistrations are needed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 178858c5-93cc-451f-98de-c0ed95a0caf6
📒 Files selected for processing (18)
openwisp_controller/config/static/config/js/lib/jsonschema-ui.jsopenwisp_controller/config/templates/admin/config/device/change_form.htmlopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/subnet_division/tests/test_models.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,html}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
openwisp_controller/config/templates/admin/config/device/change_form.htmlopenwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}
📄 CodeRabbit inference engine (Custom checks)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously
Files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}
📄 CodeRabbit inference engine (Custom checks)
Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries
Files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}
📄 CodeRabbit inference engine (Custom checks)
Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable
Files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
🧠 Learnings (6)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.
Applied to files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.
Applied to files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.
Applied to files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/config/tests/test_views.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/geo/tests/test_api.py
🔇 Additional comments (16)
openwisp_controller/config/templates/admin/config/device/change_form.html (1)
20-20: LGTM!openwisp_controller/connection/base/models.py (1)
110-110: LGTM!Also applies to: 567-567
openwisp_controller/connection/commands.py (1)
61-61: LGTM!openwisp_controller/connection/connectors/openwrt/ssh.py (1)
14-14: LGTM!openwisp_controller/connection/connectors/ssh.py (1)
141-143: LGTM!Also applies to: 190-195
openwisp_controller/subnet_division/base/models.py (1)
135-139: LGTM!openwisp_controller/subnet_division/rule_types/base.py (1)
177-180: LGTM!openwisp_controller/config/tests/test_admin.py (1)
1318-1320: LGTM!openwisp_controller/config/tests/test_config.py (1)
105-120: LGTM!openwisp_controller/geo/tests/test_api.py (1)
183-200: LGTM!openwisp_controller/subnet_division/tests/test_models.py (1)
179-187: LGTM!Also applies to: 281-284, 317-320, 498-504
openwisp_controller/config/tests/test_selenium.py (1)
330-334: LGTM!Also applies to: 367-371
openwisp_controller/config/tests/test_views.py (1)
308-309: LGTM!openwisp_controller/connection/tests/test_selenium.py (1)
58-61: LGTM!openwisp_controller/connection/tests/utils.py (1)
19-21: LGTM!openwisp_controller/geo/tests/pytest.py (1)
163-164: LGTM!Also applies to: 184-185
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3). |
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (2/3). |
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (3/3). |
Commit Message Format FailureHello @ApurveKaranwal, The commit message is not in the required format. Please ensure your commit messages follow the Conventional Commits specification, including a type prefix, a capitalized title, and an optional issue reference. Example of a correct commit message: |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
openwisp_controller/subnet_division/base/models.py (1)
131-141:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAllow the exact-fit case here.
>= availablerejects configurations that exactly fill the remaining subnet slots after reserving one subnet, even though the validation message implies only over-capacity cases should fail. Change this to>so the maximum valid request is accepted.Suggested fix
- if self.number_of_subnets >= available: + if self.number_of_subnets > available:🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openwisp_controller/subnet_division/base/models.py` around lines 131 - 141, The validation check for number_of_subnets uses the >= operator which incorrectly rejects configurations that exactly match the available subnet slots after reservation. Change the comparison operator from >= to > in the condition `if self.number_of_subnets >= available:` so that exact-fit cases are allowed and only truly over-capacity configurations are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@openwisp_controller/subnet_division/base/models.py`:
- Around line 131-141: The validation check for number_of_subnets uses the >=
operator which incorrectly rejects configurations that exactly match the
available subnet slots after reservation. Change the comparison operator from >=
to > in the condition `if self.number_of_subnets >= available:` so that
exact-fit cases are allowed and only truly over-capacity configurations are
rejected.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c7f10de2-cf01-40ee-b5d5-88d114c5d5e6
📒 Files selected for processing (21)
openwisp_controller/config/static/config/js/lib/jsonschema-ui.jsopenwisp_controller/config/templates/admin/config/device/change_form.htmlopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_selenium.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/connection/commands.pyopenwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/tests/test_selenium.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}
📄 CodeRabbit inference engine (Custom checks)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously
Files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}
📄 CodeRabbit inference engine (Custom checks)
Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries
Files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}
📄 CodeRabbit inference engine (Custom checks)
Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable
Files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
**/*.{py,html}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/config/templates/admin/config/device/change_form.htmlopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
🧠 Learnings (7)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.
Applied to files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.
Applied to files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.
Applied to files:
openwisp_controller/connection/connectors/openwrt/ssh.pyopenwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/commands.pyopenwisp_controller/subnet_division/base/models.pyopenwisp_controller/connection/base/models.pyopenwisp_controller/subnet_division/rule_types/base.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/connection/connectors/ssh.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/connection/tests/utils.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/pytest.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/connection/tests/test_notifications.pyopenwisp_controller/tests/test_selenium.pyopenwisp_controller/connection/tests/test_selenium.pyopenwisp_controller/config/whois/tests/tests.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_views.pyopenwisp_controller/config/tests/test_admin.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/geo/tests/test_api.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-03-27T20:50:26.240Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:26.240Z
Learning: In openwisp-controller’s WHOIS and estimated-location services (openwisp_controller/config/whois/ and openwisp_controller/geo/estimated_location/), these components only process public IP addresses. When reviewing logs/error/debug messages in this area, treat logging the IP address as acceptable and do not flag it as a privacy/security concern—unless the logged value can originate from non-public/private IPs in that specific code path.
Applied to files:
openwisp_controller/config/whois/tests/tests.py
🔇 Additional comments (21)
openwisp_controller/config/whois/tests/tests.py (1)
1181-1181: LGTM!openwisp_controller/geo/tests/test_selenium.py (3)
111-111: LGTM!
115-122: LGTM!
123-123: LGTM!openwisp_controller/tests/test_selenium.py (1)
92-92: LGTM!openwisp_controller/config/templates/admin/config/device/change_form.html (1)
20-20: LGTM!openwisp_controller/config/tests/test_selenium.py (1)
330-334: LGTM!Also applies to: 633-654
openwisp_controller/connection/tests/test_notifications.py (1)
55-55: LGTM!openwisp_controller/connection/tests/test_selenium.py (1)
58-60: LGTM!openwisp_controller/geo/tests/test_api.py (1)
188-200: LGTM!openwisp_controller/subnet_division/tests/test_models.py (1)
181-185: LGTM!Also applies to: 282-284, 318-320, 498-504
openwisp_controller/connection/base/models.py (1)
110-110: LGTM!Also applies to: 567-567
openwisp_controller/connection/commands.py (1)
61-62: LGTM!openwisp_controller/connection/connectors/openwrt/ssh.py (1)
14-15: LGTM!openwisp_controller/connection/connectors/ssh.py (1)
141-143: LGTM!Also applies to: 190-201
openwisp_controller/subnet_division/rule_types/base.py (1)
180-180: LGTM!openwisp_controller/config/tests/test_admin.py (1)
1318-1320: LGTM!openwisp_controller/config/tests/test_config.py (1)
114-114: LGTM!openwisp_controller/config/tests/test_views.py (1)
308-309: LGTM!openwisp_controller/connection/tests/utils.py (1)
19-21: LGTM!openwisp_controller/geo/tests/pytest.py (1)
163-163: LGTM!Also applies to: 184-184
nemesifier
left a comment
There was a problem hiding this comment.
Usually I don't accept PRs like this one. I do not endorse this kind of AI usage, we risk getting hundred of similar PRs in different repos, which creates unnecessary burden for maintainers for almost zero value.
I will accept this one due to tue amount of typos found by the AI but please refrain from doing the same in all other repositories.
See my comment below.
| @@ -3,7 +3,7 @@ | |||
| * Released under the MIT license | |||
| **/ | |||
| 'use strict'; | |||
| !function(){var a;!function(){var b=!1,c=/xyz/.test(function(){window.postMessage("xyz")})?/\b_super\b/:/.*/;return a=function(){},a.extend=function a(d){function e(){!b&&this.init&&this.init.apply(this,arguments)}var f=this.prototype;b=!0;var g=new this;b=!1;for(var h in d)g[h]="function"==typeof d[h]&&"function"==typeof f[h]&&c.test(d[h])?function(a,b){return function(){var c=this._super;this._super=f[a];var d=b.apply(this,arguments);return this._super=c,d}}(h,d[h]):d[h];return e.prototype=g,e.prototype.constructor=e,e.extend=a,e},a}(),function(){function a(a,b){b=b||{bubbles:!1,cancelable:!1,detail:void 0};var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,b.bubbles,b.cancelable,b.detail),c}a.prototype=window.Event.prototype,window.CustomEvent=a}(),function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!window.requestAnimationFrame;++c)window.requestAnimationFrame=window[b[c]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[b[c]+"CancelAnimationFrame"]||window[b[c]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(b,c){var d=(new Date).getTime(),e=Math.max(0,16-(d-a)),f=window.setTimeout(function(){b(d+e)},e);return a=d+e,f}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)})}(),function(){Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)})}();var b=function(a){return!("object"!=typeof a||a.nodeType||null!==a&&a===a.window||a.constructor&&!Object.prototype.hasOwnProperty.call(a.constructor.prototype,"isPrototypeOf"))},c=function(a){var d,e,f;for(e=1;e<arguments.length;e++){d=arguments[e];for(f in d)d.hasOwnProperty(f)&&(d[f]&&b(d[f])?(a.hasOwnProperty(f)||(a[f]={}),c(a[f],d[f])):a[f]=d[f])}return a},d=function(a,b){if(a&&"object"==typeof a){var c;if(Array.isArray(a)||"number"==typeof a.length&&a.length>0&&a.length-1 in a){for(c=0;c<a.length;c++)if(b(c,a[c])===!1)return}else if(Object.keys){var d=Object.keys(a);for(c=0;c<d.length;c++)if(b(d[c],a[d[c]])===!1)return}else for(c in a)if(a.hasOwnProperty(c)&&b(c,a[c])===!1)return}},e=function(a,b){var c=document.createEvent("HTMLEvents");c.initEvent(b,!0,!0),a.dispatchEvent(c)},f=function(a,b){if(!(a instanceof Element))throw new Error("element should be an instance of Element");b=c({},f.defaults.options,b||{}),this.element=a,this.options=b,this.init()};f.prototype={constructor:f,init:function(){var a=this;this.ready=!1;var b=f.defaults.themes[this.options.theme||f.defaults.theme];if(!b)throw"Unknown theme "+(this.options.theme||f.defaults.theme);this.schema=this.options.schema,this.theme=new b,this.template=this.options.template,this.refs=this.options.refs||{},this.uuid=0,this.__data={};var c=f.defaults.iconlibs[this.options.iconlib||f.defaults.iconlib];c&&(this.iconlib=new c),this.root_container=this.theme.getContainer(),this.element.appendChild(this.root_container),this.translate=this.options.translate||f.defaults.translate,this._loadExternalRefs(this.schema,function(){a._getDefinitions(a.schema);var b={};a.options.custom_validators&&(b.custom_validators=a.options.custom_validators),a.validator=new f.Validator(a,null,b);var c=a.getEditorClass(a.schema);a.root=a.createEditor(c,{jsoneditor:a,schema:a.schema,required:!0,container:a.root_container}),a.root.preBuild(),a.root.build(),a.root.postBuild(),a.options.startval&&a.root.setValue(a.options.startval),a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.ready=!0,window.requestAnimationFrame(function(){a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.trigger("ready"),a.trigger("change"))})})},getValue:function(){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before getting the value";return this.root.getValue()},setValue:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before setting the value";return this.root.setValue(a),this},validate:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before validating";return 1===arguments.length?this.validator.validate(a):this.validation_results},destroy:function(){this.destroyed||this.ready&&(this.schema=null,this.options=null,this.root.destroy(),this.root=null,this.root_container=null,this.validator=null,this.validation_results=null,this.theme=null,this.iconlib=null,this.template=null,this.__data=null,this.ready=!1,this.element.innerHTML="",this.destroyed=!0)},on:function(a,b){return this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[],this.callbacks[a].push(b),this},off:function(a,b){if(a&&b){this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[];for(var c=[],d=0;d<this.callbacks[a].length;d++)this.callbacks[a][d]!==b&&c.push(this.callbacks[a][d]);this.callbacks[a]=c}else a?(this.callbacks=this.callbacks||{},this.callbacks[a]=[]):this.callbacks={};return this},trigger:function(a){if(this.callbacks&&this.callbacks[a]&&this.callbacks[a].length)for(var b=0;b<this.callbacks[a].length;b++)this.callbacks[a][b]();return this},setOption:function(a,b){if("show_errors"!==a)throw"Option "+a+" must be set during instantiation and cannot be changed later";return this.options.show_errors=b,this.onChange(),this},getEditorClass:function(a){var b;if(a=this.expandSchema(a),d(f.defaults.resolvers,function(c,d){var e=d(a);if(e&&f.defaults.editors[e])return b=e,!1}),!b)throw"Unknown editor for schema "+JSON.stringify(a);if(!f.defaults.editors[b])throw"Unknown editor "+b;return f.defaults.editors[b]},createEditor:function(a,b){return b=c({},a.options||{},b),new a(b)},onChange:function(){if(this.ready&&!this.firing_change){this.firing_change=!0;var a=this;return window.requestAnimationFrame(function(){a.firing_change=!1,a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),"never"!==a.options.show_errors?a.root.showValidationErrors(a.validation_results):a.root.showValidationErrors([]),a.trigger("change"))}),this}},compileTemplate:function(a,b){b=b||f.defaults.template;var c;if("string"==typeof b){if(!f.defaults.templates[b])throw"Unknown template engine "+b;if(c=f.defaults.templates[b](),!c)throw"Template engine "+b+" missing required library."}else c=b;if(!c)throw"No template engine set";if(!c.compile)throw"Invalid template engine set";return c.compile(a)},_data:function(a,b,c){if(3!==arguments.length)return a.hasAttribute("data-jsoneditor-"+b)?this.__data[a.getAttribute("data-jsoneditor-"+b)]:null;var d;a.hasAttribute("data-jsoneditor-"+b)?d=a.getAttribute("data-jsoneditor-"+b):(d=this.uuid++,a.setAttribute("data-jsoneditor-"+b,d)),this.__data[d]=c},registerEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=a,this},unregisterEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=null,this},getEditor:function(a){if(this.editors)return this.editors[a]},watch:function(a,b){return this.watchlist=this.watchlist||{},this.watchlist[a]=this.watchlist[a]||[],this.watchlist[a].push(b),this},unwatch:function(a,b){if(!this.watchlist||!this.watchlist[a])return this;if(!b)return this.watchlist[a]=null,this;for(var c=[],d=0;d<this.watchlist[a].length;d++)this.watchlist[a][d]!==b&&c.push(this.watchlist[a][d]);return this.watchlist[a]=c.length?c:null,this},notifyWatchers:function(a){if(!this.watchlist||!this.watchlist[a])return this;for(var b=0;b<this.watchlist[a].length;b++)this.watchlist[a][b]()},isEnabled:function(){return!this.root||this.root.isEnabled()},enable:function(){this.root.enable()},disable:function(){this.root.disable()},_getDefinitions:function(a,b){if(b=b||"#/definitions/",a.definitions)for(var c in a.definitions)a.definitions.hasOwnProperty(c)&&(this.refs[b+c]=a.definitions[c],a.definitions[c].definitions&&this._getDefinitions(a.definitions[c],b+c+"/definitions/"))},_getExternalRefs:function(a){var b={},c=function(a){for(var c in a)a.hasOwnProperty(c)&&(b[c]=!0)};a.$ref&&"object"!=typeof a.$ref&&"#"!==a.$ref.substr(0,1)&&!this.refs[a.$ref]&&(b[a.$ref]=!0);for(var d in a)if(a.hasOwnProperty(d))if(a[d]&&"object"==typeof a[d]&&Array.isArray(a[d]))for(var e=0;e<a[d].length;e++)"object"==typeof a[d][e]&&c(this._getExternalRefs(a[d][e]));else a[d]&&"object"==typeof a[d]&&c(this._getExternalRefs(a[d]));return b},_loadExternalRefs:function(a,b){var c=this,e=this._getExternalRefs(a),f=0,g=0,h=!1;d(e,function(a){if(!c.refs[a]){if(!c.options.ajax)throw"Must set ajax option to true to load external ref "+a;c.refs[a]="loading",g++;var d=new XMLHttpRequest;d.open("GET",a,!0),d.onreadystatechange=function(){if(4==d.readyState){if(200!==d.status)throw window.console.log(d),"Failed to fetch ref via ajax- "+a;var e;try{e=JSON.parse(d.responseText)}catch(b){throw window.console.log(b),"Failed to parse external ref "+a}if(!e||"object"!=typeof e)throw"External ref does not contain a valid schema - "+a;c.refs[a]=e,c._loadExternalRefs(e,function(){f++,f>=g&&!h&&(h=!0,b())})}},d.send()}}),g||b()},expandRefs:function(a){for(a=c({},a);a.$ref;){var b=a.$ref;delete a.$ref,this.refs[b]||(b=decodeURIComponent(b)),a=this.extendSchemas(a,this.refs[b])}return a},expandSchema:function(a){var b,e=this,f=c({},a);if("object"==typeof a.type&&(Array.isArray(a.type)?d(a.type,function(b,c){"object"==typeof c&&(a.type[b]=e.expandSchema(c))}):a.type=e.expandSchema(a.type)),"object"==typeof a.disallow&&(Array.isArray(a.disallow)?d(a.disallow,function(b,c){"object"==typeof c&&(a.disallow[b]=e.expandSchema(c))}):a.disallow=e.expandSchema(a.disallow)),a.anyOf&&d(a.anyOf,function(b,c){a.anyOf[b]=e.expandSchema(c)}),a.dependencies&&d(a.dependencies,function(b,c){"object"!=typeof c||Array.isArray(c)||(a.dependencies[b]=e.expandSchema(c))}),a.not&&(a.not=this.expandSchema(a.not)),a.allOf){for(b=0;b<a.allOf.length;b++)f=this.extendSchemas(f,this.expandSchema(a.allOf[b]));delete f.allOf}if(a.extends){if(Array.isArray(a.extends))for(b=0;b<a.extends.length;b++)f=this.extendSchemas(f,this.expandSchema(a.extends[b]));else f=this.extendSchemas(f,this.expandSchema(a.extends));delete f.extends}if(a.oneOf){var g=c({},f);for(delete g.oneOf,b=0;b<a.oneOf.length;b++)f.oneOf[b]=this.extendSchemas(this.expandSchema(a.oneOf[b]),g)}return this.expandRefs(f)},extendSchemas:function(a,b){a=c({},a),b=c({},b);var e=this,f={};return d(a,function(a,c){"undefined"!=typeof b[a]?"required"!==a&&"defaultProperties"!==a||"object"!=typeof c||!Array.isArray(c)?"type"!==a||"string"!=typeof c&&!Array.isArray(c)?"object"==typeof c&&Array.isArray(c)?f[a]=c.filter(function(c){return b[a].indexOf(c)!==-1}):"object"==typeof c&&null!==c?f[a]=e.extendSchemas(c,b[a]):f[a]=c:("string"==typeof c&&(c=[c]),"string"==typeof b.type&&(b.type=[b.type]),b.type&&b.type.length?f.type=c.filter(function(a){return b.type.indexOf(a)!==-1}):f.type=c,1===f.type.length&&"string"==typeof f.type[0]?f.type=f.type[0]:0===f.type.length&&delete f.type):f[a]=c.concat(b[a]).reduce(function(a,b){return a.indexOf(b)<0&&a.push(b),a},[]):f[a]=c}),d(b,function(b,c){"undefined"==typeof a[b]&&(f[b]=c)}),f}},f.defaults={themes:{},templates:{},iconlibs:{},editors:{},languages:{},resolvers:[],custom_validators:[]},f.Validator=a.extend({init:function(a,b,c){this.jsoneditor=a,this.schema=b||this.jsoneditor.schema,this.options=c||{},this.translate=this.jsoneditor.translate||f.defaults.translate},validate:function(a){return this._validateSchema(this.schema,a)},_validateSchema:function(a,b,e){var g,h,i,j=this,k=[],l=JSON.stringify(b);if(e=e||"root",a=c({},this.jsoneditor.expandRefs(a)),a.required&&a.required===!0){if("undefined"==typeof b)return k.push({path:e,property:"required",message:this.translate("error_notset")}),k}else if("undefined"==typeof b){if(!this.jsoneditor.options.required_by_default)return k;k.push({path:e,property:"required",message:this.translate("error_notset")})}if(a.enum){for(g=!1,h=0;h<a.enum.length;h++)l===JSON.stringify(a.enum[h])&&(g=!0);g||k.push({path:e,property:"enum",message:this.translate("error_enum")})}if(a.extends)for(h=0;h<a.extends.length;h++)k=k.concat(this._validateSchema(a.extends[h],b,e));if(a.allOf)for(h=0;h<a.allOf.length;h++)k=k.concat(this._validateSchema(a.allOf[h],b,e));if(a.anyOf){for(g=!1,h=0;h<a.anyOf.length;h++)if(!this._validateSchema(a.anyOf[h],b,e).length){g=!0;break}g||k.push({path:e,property:"anyOf",message:this.translate("error_anyOf")})}if(a.oneOf){g=0;var m=[];for(h=0;h<a.oneOf.length;h++){var n=this._validateSchema(a.oneOf[h],b,e);for(n.length||g++,i=0;i<n.length;i++)n[i].path=e+".oneOf["+h+"]"+n[i].path.substr(e.length);m=m.concat(n)}1!==g&&(k.push({path:e,property:"oneOf",message:this.translate("error_oneOf",[g])}),k=k.concat(m))}if(a.not&&(this._validateSchema(a.not,b,e).length||k.push({path:e,property:"not",message:this.translate("error_not")})),a.type)if(Array.isArray(a.type)){for(g=!1,h=0;h<a.type.length;h++)if(this._checkType(a.type[h],b)){g=!0;break}g||k.push({path:e,property:"type",message:this.translate("error_type_union")})}else this._checkType(a.type,b)||k.push({path:e,property:"type",message:this.translate("error_type",[a.type])});if(a.disallow)if(Array.isArray(a.disallow)){for(g=!0,h=0;h<a.disallow.length;h++)if(this._checkType(a.disallow[h],b)){g=!1;break}g||k.push({path:e,property:"disallow",message:this.translate("error_disallow_union")})}else this._checkType(a.disallow,b)&&k.push({path:e,property:"disallow",message:this.translate("error_disallow",[a.disallow])});if("number"==typeof b){if(a.multipleOf||a.divisibleBy){var o=a.multipleOf||a.divisibleBy;g=b/o===Math.floor(b/o),window.math?g=window.math.mod(window.math.bignumber(b),window.math.bignumber(o)).equals(0):window.Decimal&&(g=new window.Decimal(b).mod(new window.Decimal(o)).equals(0)),g||k.push({path:e,property:a.multipleOf?"multipleOf":"divisibleBy",message:this.translate("error_multipleOf",[o])})}a.hasOwnProperty("maximum")&&(g=a.exclusiveMaximum?b<a.maximum:b<=a.maximum,window.math?g=window.math[a.exclusiveMaximum?"smaller":"smallerEq"](window.math.bignumber(b),window.math.bignumber(a.maximum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMaximum?"lt":"lte"](new window.Decimal(a.maximum))),g||k.push({path:e,property:"maximum",message:this.translate(a.exclusiveMaximum?"error_maximum_excl":"error_maximum_incl",[a.maximum])})),a.hasOwnProperty("minimum")&&(g=a.exclusiveMinimum?b>a.minimum:b>=a.minimum,window.math?g=window.math[a.exclusiveMinimum?"larger":"largerEq"](window.math.bignumber(b),window.math.bignumber(a.minimum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMinimum?"gt":"gte"](new window.Decimal(a.minimum))),g||k.push({path:e,property:"minimum",message:this.translate(a.exclusiveMinimum?"error_minimum_excl":"error_minimum_incl",[a.minimum])}))}else if("string"==typeof b)a.maxLength&&(b+"").length>a.maxLength&&k.push({path:e,property:"maxLength",message:this.translate("error_maxLength",[a.maxLength])}),a.minLength&&(b+"").length<a.minLength&&k.push({path:e,property:"minLength",message:this.translate(1===a.minLength?"error_notempty":"error_minLength",[a.minLength])}),a.pattern&&(new RegExp(a.pattern).test(b)||k.push({path:e,property:"pattern",message:this.translate("error_pattern",[a.pattern])}));else if("object"==typeof b&&null!==b&&Array.isArray(b)){if(a.items)if(Array.isArray(a.items))for(h=0;h<b.length;h++)if(a.items[h])k=k.concat(this._validateSchema(a.items[h],b[h],e+"."+h));else{if(a.additionalItems===!0)break;if(!a.additionalItems){if(a.additionalItems===!1){k.push({path:e,property:"additionalItems",message:this.translate("error_additionalItems")});break}break}k=k.concat(this._validateSchema(a.additionalItems,b[h],e+"."+h))}else for(h=0;h<b.length;h++)k=k.concat(this._validateSchema(a.items,b[h],e+"."+h));if(a.maxItems&&b.length>a.maxItems&&k.push({path:e,property:"maxItems",message:this.translate("error_maxItems",[a.maxItems])}),a.minItems&&b.length<a.minItems&&k.push({path:e,property:"minItems",message:this.translate("error_minItems",[a.minItems])}),a.uniqueItems){var p={};for(h=0;h<b.length;h++){if(g=JSON.stringify(b[h]),p[g]){k.push({path:e,property:"uniqueItems",message:this.translate("error_uniqueItems")});break}p[g]=!0}}}else if("object"==typeof b&&null!==b){if(a.maxProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g>a.maxProperties&&k.push({path:e,property:"maxProperties",message:this.translate("error_maxProperties",[a.maxProperties])})}if(a.minProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g<a.minProperties&&k.push({path:e,property:"minProperties",message:this.translate("error_minProperties",[a.minProperties])})}if(a.required&&Array.isArray(a.required))for(h=0;h<a.required.length;h++)"undefined"==typeof b[a.required[h]]&&k.push({path:e,property:"required",message:this.translate("error_required",[a.required[h]])});var q={};if(a.properties)for(h in a.properties)a.properties.hasOwnProperty(h)&&(q[h]=!0,k=k.concat(this._validateSchema(a.properties[h],b[h],e+"."+h)));if(a.patternProperties)for(h in a.patternProperties)if(a.patternProperties.hasOwnProperty(h)){var r=new RegExp(h);for(i in b)b.hasOwnProperty(i)&&r.test(i)&&(q[i]=!0,k=k.concat(this._validateSchema(a.patternProperties[h],b[i],e+"."+i)))}if("undefined"!=typeof a.additionalProperties||!this.jsoneditor.options.no_additional_properties||a.oneOf||a.anyOf||(a.additionalProperties=!1),"undefined"!=typeof a.additionalProperties)for(h in b)if(b.hasOwnProperty(h)&&!q[h]){if(!a.additionalProperties){k.push({path:e,property:"additionalProperties",message:this.translate("error_additional_properties",[h])});break}if(a.additionalProperties===!0)break;k=k.concat(this._validateSchema(a.additionalProperties,b[h],e+"."+h))}if(a.dependencies)for(h in a.dependencies)if(a.dependencies.hasOwnProperty(h)&&"undefined"!=typeof b[h])if(Array.isArray(a.dependencies[h]))for(i=0;i<a.dependencies[h].length;i++)"undefined"==typeof b[a.dependencies[h][i]]&&k.push({path:e,property:"dependencies",message:this.translate("error_dependency",[a.dependencies[h][i]])});else k=k.concat(this._validateSchema(a.dependencies[h],b,e))}return d(f.defaults.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),this.options.custom_validators&&d(this.options.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),k},_checkType:function(a,b){return"string"==typeof a?"string"===a?"string"==typeof b:"number"===a?"number"==typeof b:"integer"===a?"number"==typeof b&&b===Math.floor(b):"boolean"===a?"boolean"==typeof b:"array"===a?Array.isArray(b):"object"===a?null!==b&&!Array.isArray(b)&&"object"==typeof b:"null"!==a||null===b:!this._validateSchema(a,b).length}}),f.AbstractEditor=a.extend({onChildEditorChange:function(a){this.onChange(!0)},notify:function(){this.jsoneditor.notifyWatchers(this.path)},change:function(){this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange()},onChange:function(a){this.notify(),this.watch_listener&&this.watch_listener(),a&&this.change()},register:function(){this.jsoneditor.registerEditor(this),this.onChange()},unregister:function(){this.jsoneditor&&this.jsoneditor.unregisterEditor(this)},getNumColumns:function(){return 12},init:function(a){this.jsoneditor=a.jsoneditor,this.theme=this.jsoneditor.theme,this.template_engine=this.jsoneditor.template,this.iconlib=this.jsoneditor.iconlib,this.translate=this.jsoneditor.translate||f.defaults.translate,this.original_schema=a.schema,this.schema=this.jsoneditor.expandSchema(this.original_schema),this.options=c({},this.options||{},a.schema.options||{},a),a.path||this.schema.id||(this.schema.id="root"),this.path=a.path||"root",this.formname=a.formname||this.path.replace(/\.([^.]+)/g,"[$1]"),this.jsoneditor.options.form_name_root&&(this.formname=this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+"[")),this.key=this.path.split(".").pop(),this.parent=a.parent,this.link_watchers=[],a.container&&this.setContainer(a.container)},setContainer:function(a){this.container=a,this.schema.id&&this.container.setAttribute("data-schemaid",this.schema.id),this.schema.type&&"string"==typeof this.schema.type&&this.container.setAttribute("data-schematype",this.schema.type),this.container.setAttribute("data-schemapath",this.path)},preBuild:function(){},build:function(){},postBuild:function(){this.setupWatchListeners(),this.addLinks(),this.setValue(this.getDefault(),!0),this.updateHeaderText(),this.register(),this.onWatchedFieldChange()},setupWatchListeners:function(){var a=this;if(this.watched={},this.schema.vars&&(this.schema.watch=this.schema.vars),this.watched_values={},this.watch_listener=function(){a.refreshWatchedFieldValues()&&a.onWatchedFieldChange()},this.register(),this.schema.hasOwnProperty("watch")){var b,c,d,e,f;for(var g in this.schema.watch)if(this.schema.watch.hasOwnProperty(g)){if(b=this.schema.watch[g],Array.isArray(b)){if(b.length<2)continue;c=[b[0]].concat(b[1].split("."))}else c=b.split("."),a.theme.closest(a.container,'[data-schemaid="'+c[0]+'"]')||c.unshift("#");if(d=c.shift(),"#"===d&&(d=a.jsoneditor.schema.id||"root"),e=a.theme.closest(a.container,'[data-schemaid="'+d+'"]'),!e)throw"Could not find ancestor node with id "+d;f=e.getAttribute("data-schemapath")+"."+c.join("."),a.jsoneditor.watch(f,a.watch_listener),a.watched[g]=f}}this.schema.headerTemplate&&(this.header_template=this.jsoneditor.compileTemplate(this.schema.headerTemplate,this.template_engine))},addLinks:function(){if(!this.no_link_holder&&(this.link_holder=this.theme.getLinksHolder(),this.container.appendChild(this.link_holder),this.schema.links))for(var a=0;a<this.schema.links.length;a++)this.addLink(this.getLink(this.schema.links[a]))},getButton:function(a,b,c){var d="json-editor-btn-"+b;b=this.iconlib?this.iconlib.getIcon(b):null,!b&&c&&(a=c,c=null);var e=this.theme.getButton(a,b,c);return e.className+=" "+d+" ",e},setButtonText:function(a,b,c,d){return c=this.iconlib?this.iconlib.getIcon(c):null,!c&&d&&(b=d,d=null),this.theme.setButtonText(a,b,c,d)},addLink:function(a){this.link_holder&&this.link_holder.appendChild(a)},getLink:function(a){var b,c,d=a.mediaType||"application/javascript",e=d.split("/")[0],f=this.jsoneditor.compileTemplate(a.href,this.template_engine),g=null;if(a.download&&(g=a.download),g&&g!==!0&&(g=this.jsoneditor.compileTemplate(g,this.template_engine)),"image"===e){b=this.theme.getBlockLinkHolder(),c=document.createElement("a"),c.setAttribute("target","_blank");var h=document.createElement("img");this.theme.createImageLink(b,c,h),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.setAttribute("title",a.rel||d),h.setAttribute("src",d)})}else if(["audio","video"].indexOf(e)>=0){b=this.theme.getBlockLinkHolder(),c=this.theme.getBlockLink(),c.setAttribute("target","_blank");var i=document.createElement(e);i.setAttribute("controls","controls"),this.theme.createMediaLink(b,c,i),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.textContent=a.rel||d,i.setAttribute("src",d)})}else c=b=this.theme.getBlockLink(),b.setAttribute("target","_blank"),b.textContent=a.rel,this.link_watchers.push(function(c){var d=f(c);b.setAttribute("href",d),b.textContent=a.rel||d});return g&&c&&(g===!0?c.setAttribute("download",""):this.link_watchers.push(function(a){c.setAttribute("download",g(a))})),a.class&&(c.className=c.className+" "+a.class),b},refreshWatchedFieldValues:function(){if(this.watched_values){var a={},b=!1,c=this;if(this.watched){var d,e;for(var f in this.watched)this.watched.hasOwnProperty(f)&&(e=c.jsoneditor.getEditor(this.watched[f]),d=e?e.getValue():null,c.watched_values[f]!==d&&(b=!0),a[f]=d)}return a.self=this.getValue(),this.watched_values.self!==a.self&&(b=!0),this.watched_values=a,b}},getWatchedFieldValues:function(){return this.watched_values},updateHeaderText:function(){if(this.header)if(this.header.children.length){for(var a=0;a<this.header.childNodes.length;a++)if(3===this.header.childNodes[a].nodeType){this.header.childNodes[a].nodeValue=this.getHeaderText();break}}else this.header.textContent=this.getHeaderText()},getHeaderText:function(a){return this.header_text?this.header_text:a?this.schema.title:this.getTitle()},onWatchedFieldChange:function(){var a;if(this.header_template){a=c(this.getWatchedFieldValues(),{key:this.key,i:this.key,i0:1*this.key,i1:1*this.key+1,title:this.getTitle()});var b=this.header_template(a);b!==this.header_text&&(this.header_text=b,this.updateHeaderText(),this.notify())}if(this.link_watchers.length){a=this.getWatchedFieldValues();for(var d=0;d<this.link_watchers.length;d++)this.link_watchers[d](a)}},setValue:function(a){this.value=a},getValue:function(){return this.value},refreshValue:function(){},getChildEditors:function(){return!1},destroy:function(){var a=this;this.unregister(this),d(this.watched,function(b,c){a.jsoneditor.unwatch(c,a.watch_listener)}),this.watched=null,this.watched_values=null,this.watch_listener=null,this.header_text=null,this.header_template=null,this.value=null,this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container),this.container=null,this.jsoneditor=null,this.schema=null,this.path=null,this.key=null,this.parent=null},getDefault:function(){if(this.schema.default)return this.schema.default;if(this.schema.enum)return this.schema.enum[0];var a=this.schema.type||this.schema.oneOf;if(a&&Array.isArray(a)&&(a=a[0]),a&&"object"==typeof a&&(a=a.type),a&&Array.isArray(a)&&(a=a[0]),"string"==typeof a){if("number"===a)return 0;if("boolean"===a)return!1;if("integer"===a)return 0;if("string"===a)return"";if("object"===a)return{};if("array"===a)return[]}return null},getTitle:function(){return this.schema.title||this.key},enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},isEnabled:function(){return!this.disabled},isRequired:function(){return"boolean"==typeof this.schema.required?this.schema.required:this.parent&&this.parent.schema&&Array.isArray(this.parent.schema.required)?this.parent.schema.required.indexOf(this.key)>-1:!!this.jsoneditor.options.required_by_default},getDisplayText:function(a){var b=[],c={};d(a,function(a,b){b.title&&(c[b.title]=c[b.title]||0,c[b.title]++),b.description&&(c[b.description]=c[b.description]||0,c[b.description]++),b.format&&(c[b.format]=c[b.format]||0,c[b.format]++),b.type&&(c[b.type]=c[b.type]||0,c[b.type]++)}),d(a,function(a,d){var e;e="string"==typeof d?d:d.title&&c[d.title]<=1?d.title:d.format&&c[d.format]<=1?d.format:d.type&&c[d.type]<=1?d.type:d.description&&c[d.description]<=1?d.descripton:d.title?d.title:d.format?d.format:d.type?d.type:d.description?d.description:JSON.stringify(d).length<50?JSON.stringify(d):"type",b.push(e)});var e={};return d(b,function(a,d){e[d]=e[d]||0,e[d]++,c[d]>1&&(b[a]=d+" "+e[d])}),b},getOption:function(a){try{throw"getOption is deprecated"}catch(a){window.console.error(a)}return this.options[a]},showValidationErrors:function(a){}}),f.defaults.editors.null=f.AbstractEditor.extend({getValue:function(){return null},setValue:function(){this.onChange()},getNumColumns:function(){return 2}}),f.defaults.editors.string=f.AbstractEditor.extend({register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},setValue:function(a,b,c){if((!this.template||c)&&(null===a||"undefined"==typeof a?a="":"object"==typeof a?a=JSON.stringify(a):"string"!=typeof a&&(a=""+a),a!==this.serialized)){var d=this.sanitize(a);if(this.input.value!==d){this.input.value=d,this.sceditor_instance?this.sceditor_instance.val(d):this.epiceditor?this.epiceditor.importFile(null,d):this.ace_editor&&this.ace_editor.setValue(d);var e=c||this.getValue()!==a;this.refreshValue(),b?this.is_dirty=!1:"change"===this.jsoneditor.options.show_errors&&(this.is_dirty=!0),this.adjust_height&&this.adjust_height(this.input),this.onChange(e)}}},getNumColumns:function(){var a,b=Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);return a="textarea"===this.input_type?6:["text","email"].indexOf(this.input_type)>=0?4:2,Math.min(12,Math.max(b,a))},build:function(){var a=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.format=this.schema.format,!this.format&&this.schema.media&&this.schema.media.type&&(this.format=this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,"")),!this.format&&this.options.default_format&&(this.format=this.options.default_format),this.options.format&&(this.format=this.options.format),this.format)if("textarea"===this.format)this.input_type="textarea",this.input=this.theme.getTextareaInput();else if("range"===this.format){this.input_type="range";var b=this.schema.minimum||0,c=this.schema.maximum||Math.max(100,b+1),d=1;this.schema.multipleOf&&(b%this.schema.multipleOf&&(b=Math.ceil(b/this.schema.multipleOf)*this.schema.multipleOf),c%this.schema.multipleOf&&(c=Math.floor(c/this.schema.multipleOf)*this.schema.multipleOf),d=this.schema.multipleOf),this.input=this.theme.getRangeInput(b,c,d)}else["actionscript","batchfile","bbcode","c","c++","cpp","coffee","csharp","css","dart","django","ejs","erlang","golang","groovy","handlebars","haskell","haxe","html","ini","jade","java","javascript","json","less","lisp","lua","makefile","markdown","matlab","mysql","objectivec","pascal","perl","pgsql","php","python","r","ruby","sass","scala","scss","smarty","sql","stylus","svg","twig","vbscript","xml","yaml"].indexOf(this.format)>=0?(this.input_type=this.format,this.source_code=!0,this.input=this.theme.getTextareaInput()):(this.input_type=this.format,this.input=this.theme.getFormInputField(this.input_type));else this.input_type="text",this.input=this.theme.getFormInputField(this.input_type);"undefined"!=typeof this.schema.maxLength&&this.input.setAttribute("maxlength",this.schema.maxLength),"undefined"!=typeof this.schema.pattern?this.input.setAttribute("pattern",this.schema.pattern):"undefined"!=typeof this.schema.minLength&&this.input.setAttribute("pattern",".{"+this.schema.minLength+",}"),this.options.compact?this.container.className+=" compact":this.options.input_width&&(this.input.style.width=this.options.input_width),(this.schema.readOnly||this.schema.readonly||this.schema.template)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),a.schema.template)return void(this.value=a.value);var c=this.value,d=a.sanitize(c);c!==d&&(this.value=d),a.is_dirty=!0,a.refreshValue(),a.onChange(!0)}),this.options.input_height&&(this.input.style.height=this.options.input_height),this.options.expand_height&&(this.adjust_height=function(a){if(a){var b,c=a.offsetHeight;if(a.offsetHeight<a.scrollHeight)for(b=0;a.offsetHeight<a.scrollHeight+3&&!(b>100);)b++,c++,a.style.height=c+"px";else{for(b=0;a.offsetHeight>=a.scrollHeight+3&&!(b>100);)b++,c--,a.style.height=c+"px";a.style.height=c+1+"px"}}},this.input.addEventListener("keyup",function(b){a.adjust_height(this)}),this.input.addEventListener("change",function(b){a.adjust_height(this)}),this.adjust_height()),this.format&&this.input.setAttribute("data-schemaformat",this.format),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),window.requestAnimationFrame(function(){a.input.parentNode&&a.afterInputReady(),a.adjust_height&&a.adjust_height(a.input)}),this.schema.template?(this.template=this.jsoneditor.compileTemplate(this.schema.template,this.template_engine),this.refreshValue()):this.refreshValue()},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},afterInputReady:function(){var a,b=this;if(this.source_code)if(this.options.wysiwyg&&["html","bbcode"].indexOf(this.input_type)>=0&&window.jQuery&&window.jQuery.fn&&window.jQuery.fn.sceditor)a=c({},{ | |||
| !function(){var a;!function(){var b=!1,c=/xyz/.test(function(){window.postMessage("xyz")})?/\b_super\b/:/.*/;return a=function(){},a.extend=function a(d){function e(){!b&&this.init&&this.init.apply(this,arguments)}var f=this.prototype;b=!0;var g=new this;b=!1;for(var h in d)g[h]="function"==typeof d[h]&&"function"==typeof f[h]&&c.test(d[h])?function(a,b){return function(){var c=this._super;this._super=f[a];var d=b.apply(this,arguments);return this._super=c,d}}(h,d[h]):d[h];return e.prototype=g,e.prototype.constructor=e,e.extend=a,e},a}(),function(){function a(a,b){b=b||{bubbles:!1,cancelable:!1,detail:void 0};var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,b.bubbles,b.cancelable,b.detail),c}a.prototype=window.Event.prototype,window.CustomEvent=a}(),function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!window.requestAnimationFrame;++c)window.requestAnimationFrame=window[b[c]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[b[c]+"CancelAnimationFrame"]||window[b[c]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(b,c){var d=(new Date).getTime(),e=Math.max(0,16-(d-a)),f=window.setTimeout(function(){b(d+e)},e);return a=d+e,f}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)})}(),function(){Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)})}();var b=function(a){return!("object"!=typeof a||a.nodeType||null!==a&&a===a.window||a.constructor&&!Object.prototype.hasOwnProperty.call(a.constructor.prototype,"isPrototypeOf"))},c=function(a){var d,e,f;for(e=1;e<arguments.length;e++){d=arguments[e];for(f in d)d.hasOwnProperty(f)&&(d[f]&&b(d[f])?(a.hasOwnProperty(f)||(a[f]={}),c(a[f],d[f])):a[f]=d[f])}return a},d=function(a,b){if(a&&"object"==typeof a){var c;if(Array.isArray(a)||"number"==typeof a.length&&a.length>0&&a.length-1 in a){for(c=0;c<a.length;c++)if(b(c,a[c])===!1)return}else if(Object.keys){var d=Object.keys(a);for(c=0;c<d.length;c++)if(b(d[c],a[d[c]])===!1)return}else for(c in a)if(a.hasOwnProperty(c)&&b(c,a[c])===!1)return}},e=function(a,b){var c=document.createEvent("HTMLEvents");c.initEvent(b,!0,!0),a.dispatchEvent(c)},f=function(a,b){if(!(a instanceof Element))throw new Error("element should be an instance of Element");b=c({},f.defaults.options,b||{}),this.element=a,this.options=b,this.init()};f.prototype={constructor:f,init:function(){var a=this;this.ready=!1;var b=f.defaults.themes[this.options.theme||f.defaults.theme];if(!b)throw"Unknown theme "+(this.options.theme||f.defaults.theme);this.schema=this.options.schema,this.theme=new b,this.template=this.options.template,this.refs=this.options.refs||{},this.uuid=0,this.__data={};var c=f.defaults.iconlibs[this.options.iconlib||f.defaults.iconlib];c&&(this.iconlib=new c),this.root_container=this.theme.getContainer(),this.element.appendChild(this.root_container),this.translate=this.options.translate||f.defaults.translate,this._loadExternalRefs(this.schema,function(){a._getDefinitions(a.schema);var b={};a.options.custom_validators&&(b.custom_validators=a.options.custom_validators),a.validator=new f.Validator(a,null,b);var c=a.getEditorClass(a.schema);a.root=a.createEditor(c,{jsoneditor:a,schema:a.schema,required:!0,container:a.root_container}),a.root.preBuild(),a.root.build(),a.root.postBuild(),a.options.startval&&a.root.setValue(a.options.startval),a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.ready=!0,window.requestAnimationFrame(function(){a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.trigger("ready"),a.trigger("change"))})})},getValue:function(){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before getting the value";return this.root.getValue()},setValue:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before setting the value";return this.root.setValue(a),this},validate:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before validating";return 1===arguments.length?this.validator.validate(a):this.validation_results},destroy:function(){this.destroyed||this.ready&&(this.schema=null,this.options=null,this.root.destroy(),this.root=null,this.root_container=null,this.validator=null,this.validation_results=null,this.theme=null,this.iconlib=null,this.template=null,this.__data=null,this.ready=!1,this.element.innerHTML="",this.destroyed=!0)},on:function(a,b){return this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[],this.callbacks[a].push(b),this},off:function(a,b){if(a&&b){this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[];for(var c=[],d=0;d<this.callbacks[a].length;d++)this.callbacks[a][d]!==b&&c.push(this.callbacks[a][d]);this.callbacks[a]=c}else a?(this.callbacks=this.callbacks||{},this.callbacks[a]=[]):this.callbacks={};return this},trigger:function(a){if(this.callbacks&&this.callbacks[a]&&this.callbacks[a].length)for(var b=0;b<this.callbacks[a].length;b++)this.callbacks[a][b]();return this},setOption:function(a,b){if("show_errors"!==a)throw"Option "+a+" must be set during instantiation and cannot be changed later";return this.options.show_errors=b,this.onChange(),this},getEditorClass:function(a){var b;if(a=this.expandSchema(a),d(f.defaults.resolvers,function(c,d){var e=d(a);if(e&&f.defaults.editors[e])return b=e,!1}),!b)throw"Unknown editor for schema "+JSON.stringify(a);if(!f.defaults.editors[b])throw"Unknown editor "+b;return f.defaults.editors[b]},createEditor:function(a,b){return b=c({},a.options||{},b),new a(b)},onChange:function(){if(this.ready&&!this.firing_change){this.firing_change=!0;var a=this;return window.requestAnimationFrame(function(){a.firing_change=!1,a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),"never"!==a.options.show_errors?a.root.showValidationErrors(a.validation_results):a.root.showValidationErrors([]),a.trigger("change"))}),this}},compileTemplate:function(a,b){b=b||f.defaults.template;var c;if("string"==typeof b){if(!f.defaults.templates[b])throw"Unknown template engine "+b;if(c=f.defaults.templates[b](),!c)throw"Template engine "+b+" missing required library."}else c=b;if(!c)throw"No template engine set";if(!c.compile)throw"Invalid template engine set";return c.compile(a)},_data:function(a,b,c){if(3!==arguments.length)return a.hasAttribute("data-jsoneditor-"+b)?this.__data[a.getAttribute("data-jsoneditor-"+b)]:null;var d;a.hasAttribute("data-jsoneditor-"+b)?d=a.getAttribute("data-jsoneditor-"+b):(d=this.uuid++,a.setAttribute("data-jsoneditor-"+b,d)),this.__data[d]=c},registerEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=a,this},unregisterEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=null,this},getEditor:function(a){if(this.editors)return this.editors[a]},watch:function(a,b){return this.watchlist=this.watchlist||{},this.watchlist[a]=this.watchlist[a]||[],this.watchlist[a].push(b),this},unwatch:function(a,b){if(!this.watchlist||!this.watchlist[a])return this;if(!b)return this.watchlist[a]=null,this;for(var c=[],d=0;d<this.watchlist[a].length;d++)this.watchlist[a][d]!==b&&c.push(this.watchlist[a][d]);return this.watchlist[a]=c.length?c:null,this},notifyWatchers:function(a){if(!this.watchlist||!this.watchlist[a])return this;for(var b=0;b<this.watchlist[a].length;b++)this.watchlist[a][b]()},isEnabled:function(){return!this.root||this.root.isEnabled()},enable:function(){this.root.enable()},disable:function(){this.root.disable()},_getDefinitions:function(a,b){if(b=b||"#/definitions/",a.definitions)for(var c in a.definitions)a.definitions.hasOwnProperty(c)&&(this.refs[b+c]=a.definitions[c],a.definitions[c].definitions&&this._getDefinitions(a.definitions[c],b+c+"/definitions/"))},_getExternalRefs:function(a){var b={},c=function(a){for(var c in a)a.hasOwnProperty(c)&&(b[c]=!0)};a.$ref&&"object"!=typeof a.$ref&&"#"!==a.$ref.substr(0,1)&&!this.refs[a.$ref]&&(b[a.$ref]=!0);for(var d in a)if(a.hasOwnProperty(d))if(a[d]&&"object"==typeof a[d]&&Array.isArray(a[d]))for(var e=0;e<a[d].length;e++)"object"==typeof a[d][e]&&c(this._getExternalRefs(a[d][e]));else a[d]&&"object"==typeof a[d]&&c(this._getExternalRefs(a[d]));return b},_loadExternalRefs:function(a,b){var c=this,e=this._getExternalRefs(a),f=0,g=0,h=!1;d(e,function(a){if(!c.refs[a]){if(!c.options.ajax)throw"Must set ajax option to true to load external ref "+a;c.refs[a]="loading",g++;var d=new XMLHttpRequest;d.open("GET",a,!0),d.onreadystatechange=function(){if(4==d.readyState){if(200!==d.status)throw window.console.log(d),"Failed to fetch ref via ajax- "+a;var e;try{e=JSON.parse(d.responseText)}catch(b){throw window.console.log(b),"Failed to parse external ref "+a}if(!e||"object"!=typeof e)throw"External ref does not contain a valid schema - "+a;c.refs[a]=e,c._loadExternalRefs(e,function(){f++,f>=g&&!h&&(h=!0,b())})}},d.send()}}),g||b()},expandRefs:function(a){for(a=c({},a);a.$ref;){var b=a.$ref;delete a.$ref,this.refs[b]||(b=decodeURIComponent(b)),a=this.extendSchemas(a,this.refs[b])}return a},expandSchema:function(a){var b,e=this,f=c({},a);if("object"==typeof a.type&&(Array.isArray(a.type)?d(a.type,function(b,c){"object"==typeof c&&(a.type[b]=e.expandSchema(c))}):a.type=e.expandSchema(a.type)),"object"==typeof a.disallow&&(Array.isArray(a.disallow)?d(a.disallow,function(b,c){"object"==typeof c&&(a.disallow[b]=e.expandSchema(c))}):a.disallow=e.expandSchema(a.disallow)),a.anyOf&&d(a.anyOf,function(b,c){a.anyOf[b]=e.expandSchema(c)}),a.dependencies&&d(a.dependencies,function(b,c){"object"!=typeof c||Array.isArray(c)||(a.dependencies[b]=e.expandSchema(c))}),a.not&&(a.not=this.expandSchema(a.not)),a.allOf){for(b=0;b<a.allOf.length;b++)f=this.extendSchemas(f,this.expandSchema(a.allOf[b]));delete f.allOf}if(a.extends){if(Array.isArray(a.extends))for(b=0;b<a.extends.length;b++)f=this.extendSchemas(f,this.expandSchema(a.extends[b]));else f=this.extendSchemas(f,this.expandSchema(a.extends));delete f.extends}if(a.oneOf){var g=c({},f);for(delete g.oneOf,b=0;b<a.oneOf.length;b++)f.oneOf[b]=this.extendSchemas(this.expandSchema(a.oneOf[b]),g)}return this.expandRefs(f)},extendSchemas:function(a,b){a=c({},a),b=c({},b);var e=this,f={};return d(a,function(a,c){"undefined"!=typeof b[a]?"required"!==a&&"defaultProperties"!==a||"object"!=typeof c||!Array.isArray(c)?"type"!==a||"string"!=typeof c&&!Array.isArray(c)?"object"==typeof c&&Array.isArray(c)?f[a]=c.filter(function(c){return b[a].indexOf(c)!==-1}):"object"==typeof c&&null!==c?f[a]=e.extendSchemas(c,b[a]):f[a]=c:("string"==typeof c&&(c=[c]),"string"==typeof b.type&&(b.type=[b.type]),b.type&&b.type.length?f.type=c.filter(function(a){return b.type.indexOf(a)!==-1}):f.type=c,1===f.type.length&&"string"==typeof f.type[0]?f.type=f.type[0]:0===f.type.length&&delete f.type):f[a]=c.concat(b[a]).reduce(function(a,b){return a.indexOf(b)<0&&a.push(b),a},[]):f[a]=c}),d(b,function(b,c){"undefined"==typeof a[b]&&(f[b]=c)}),f}},f.defaults={themes:{},templates:{},iconlibs:{},editors:{},languages:{},resolvers:[],custom_validators:[]},f.Validator=a.extend({init:function(a,b,c){this.jsoneditor=a,this.schema=b||this.jsoneditor.schema,this.options=c||{},this.translate=this.jsoneditor.translate||f.defaults.translate},validate:function(a){return this._validateSchema(this.schema,a)},_validateSchema:function(a,b,e){var g,h,i,j=this,k=[],l=JSON.stringify(b);if(e=e||"root",a=c({},this.jsoneditor.expandRefs(a)),a.required&&a.required===!0){if("undefined"==typeof b)return k.push({path:e,property:"required",message:this.translate("error_notset")}),k}else if("undefined"==typeof b){if(!this.jsoneditor.options.required_by_default)return k;k.push({path:e,property:"required",message:this.translate("error_notset")})}if(a.enum){for(g=!1,h=0;h<a.enum.length;h++)l===JSON.stringify(a.enum[h])&&(g=!0);g||k.push({path:e,property:"enum",message:this.translate("error_enum")})}if(a.extends)for(h=0;h<a.extends.length;h++)k=k.concat(this._validateSchema(a.extends[h],b,e));if(a.allOf)for(h=0;h<a.allOf.length;h++)k=k.concat(this._validateSchema(a.allOf[h],b,e));if(a.anyOf){for(g=!1,h=0;h<a.anyOf.length;h++)if(!this._validateSchema(a.anyOf[h],b,e).length){g=!0;break}g||k.push({path:e,property:"anyOf",message:this.translate("error_anyOf")})}if(a.oneOf){g=0;var m=[];for(h=0;h<a.oneOf.length;h++){var n=this._validateSchema(a.oneOf[h],b,e);for(n.length||g++,i=0;i<n.length;i++)n[i].path=e+".oneOf["+h+"]"+n[i].path.substr(e.length);m=m.concat(n)}1!==g&&(k.push({path:e,property:"oneOf",message:this.translate("error_oneOf",[g])}),k=k.concat(m))}if(a.not&&(this._validateSchema(a.not,b,e).length||k.push({path:e,property:"not",message:this.translate("error_not")})),a.type)if(Array.isArray(a.type)){for(g=!1,h=0;h<a.type.length;h++)if(this._checkType(a.type[h],b)){g=!0;break}g||k.push({path:e,property:"type",message:this.translate("error_type_union")})}else this._checkType(a.type,b)||k.push({path:e,property:"type",message:this.translate("error_type",[a.type])});if(a.disallow)if(Array.isArray(a.disallow)){for(g=!0,h=0;h<a.disallow.length;h++)if(this._checkType(a.disallow[h],b)){g=!1;break}g||k.push({path:e,property:"disallow",message:this.translate("error_disallow_union")})}else this._checkType(a.disallow,b)&&k.push({path:e,property:"disallow",message:this.translate("error_disallow",[a.disallow])});if("number"==typeof b){if(a.multipleOf||a.divisibleBy){var o=a.multipleOf||a.divisibleBy;g=b/o===Math.floor(b/o),window.math?g=window.math.mod(window.math.bignumber(b),window.math.bignumber(o)).equals(0):window.Decimal&&(g=new window.Decimal(b).mod(new window.Decimal(o)).equals(0)),g||k.push({path:e,property:a.multipleOf?"multipleOf":"divisibleBy",message:this.translate("error_multipleOf",[o])})}a.hasOwnProperty("maximum")&&(g=a.exclusiveMaximum?b<a.maximum:b<=a.maximum,window.math?g=window.math[a.exclusiveMaximum?"smaller":"smallerEq"](window.math.bignumber(b),window.math.bignumber(a.maximum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMaximum?"lt":"lte"](new window.Decimal(a.maximum))),g||k.push({path:e,property:"maximum",message:this.translate(a.exclusiveMaximum?"error_maximum_excl":"error_maximum_incl",[a.maximum])})),a.hasOwnProperty("minimum")&&(g=a.exclusiveMinimum?b>a.minimum:b>=a.minimum,window.math?g=window.math[a.exclusiveMinimum?"larger":"largerEq"](window.math.bignumber(b),window.math.bignumber(a.minimum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMinimum?"gt":"gte"](new window.Decimal(a.minimum))),g||k.push({path:e,property:"minimum",message:this.translate(a.exclusiveMinimum?"error_minimum_excl":"error_minimum_incl",[a.minimum])}))}else if("string"==typeof b)a.maxLength&&(b+"").length>a.maxLength&&k.push({path:e,property:"maxLength",message:this.translate("error_maxLength",[a.maxLength])}),a.minLength&&(b+"").length<a.minLength&&k.push({path:e,property:"minLength",message:this.translate(1===a.minLength?"error_notempty":"error_minLength",[a.minLength])}),a.pattern&&(new RegExp(a.pattern).test(b)||k.push({path:e,property:"pattern",message:this.translate("error_pattern",[a.pattern])}));else if("object"==typeof b&&null!==b&&Array.isArray(b)){if(a.items)if(Array.isArray(a.items))for(h=0;h<b.length;h++)if(a.items[h])k=k.concat(this._validateSchema(a.items[h],b[h],e+"."+h));else{if(a.additionalItems===!0)break;if(!a.additionalItems){if(a.additionalItems===!1){k.push({path:e,property:"additionalItems",message:this.translate("error_additionalItems")});break}break}k=k.concat(this._validateSchema(a.additionalItems,b[h],e+"."+h))}else for(h=0;h<b.length;h++)k=k.concat(this._validateSchema(a.items,b[h],e+"."+h));if(a.maxItems&&b.length>a.maxItems&&k.push({path:e,property:"maxItems",message:this.translate("error_maxItems",[a.maxItems])}),a.minItems&&b.length<a.minItems&&k.push({path:e,property:"minItems",message:this.translate("error_minItems",[a.minItems])}),a.uniqueItems){var p={};for(h=0;h<b.length;h++){if(g=JSON.stringify(b[h]),p[g]){k.push({path:e,property:"uniqueItems",message:this.translate("error_uniqueItems")});break}p[g]=!0}}}else if("object"==typeof b&&null!==b){if(a.maxProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g>a.maxProperties&&k.push({path:e,property:"maxProperties",message:this.translate("error_maxProperties",[a.maxProperties])})}if(a.minProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g<a.minProperties&&k.push({path:e,property:"minProperties",message:this.translate("error_minProperties",[a.minProperties])})}if(a.required&&Array.isArray(a.required))for(h=0;h<a.required.length;h++)"undefined"==typeof b[a.required[h]]&&k.push({path:e,property:"required",message:this.translate("error_required",[a.required[h]])});var q={};if(a.properties)for(h in a.properties)a.properties.hasOwnProperty(h)&&(q[h]=!0,k=k.concat(this._validateSchema(a.properties[h],b[h],e+"."+h)));if(a.patternProperties)for(h in a.patternProperties)if(a.patternProperties.hasOwnProperty(h)){var r=new RegExp(h);for(i in b)b.hasOwnProperty(i)&&r.test(i)&&(q[i]=!0,k=k.concat(this._validateSchema(a.patternProperties[h],b[i],e+"."+i)))}if("undefined"!=typeof a.additionalProperties||!this.jsoneditor.options.no_additional_properties||a.oneOf||a.anyOf||(a.additionalProperties=!1),"undefined"!=typeof a.additionalProperties)for(h in b)if(b.hasOwnProperty(h)&&!q[h]){if(!a.additionalProperties){k.push({path:e,property:"additionalProperties",message:this.translate("error_additional_properties",[h])});break}if(a.additionalProperties===!0)break;k=k.concat(this._validateSchema(a.additionalProperties,b[h],e+"."+h))}if(a.dependencies)for(h in a.dependencies)if(a.dependencies.hasOwnProperty(h)&&"undefined"!=typeof b[h])if(Array.isArray(a.dependencies[h]))for(i=0;i<a.dependencies[h].length;i++)"undefined"==typeof b[a.dependencies[h][i]]&&k.push({path:e,property:"dependencies",message:this.translate("error_dependency",[a.dependencies[h][i]])});else k=k.concat(this._validateSchema(a.dependencies[h],b,e))}return d(f.defaults.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),this.options.custom_validators&&d(this.options.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),k},_checkType:function(a,b){return"string"==typeof a?"string"===a?"string"==typeof b:"number"===a?"number"==typeof b:"integer"===a?"number"==typeof b&&b===Math.floor(b):"boolean"===a?"boolean"==typeof b:"array"===a?Array.isArray(b):"object"===a?null!==b&&!Array.isArray(b)&&"object"==typeof b:"null"!==a||null===b:!this._validateSchema(a,b).length}}),f.AbstractEditor=a.extend({onChildEditorChange:function(a){this.onChange(!0)},notify:function(){this.jsoneditor.notifyWatchers(this.path)},change:function(){this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange()},onChange:function(a){this.notify(),this.watch_listener&&this.watch_listener(),a&&this.change()},register:function(){this.jsoneditor.registerEditor(this),this.onChange()},unregister:function(){this.jsoneditor&&this.jsoneditor.unregisterEditor(this)},getNumColumns:function(){return 12},init:function(a){this.jsoneditor=a.jsoneditor,this.theme=this.jsoneditor.theme,this.template_engine=this.jsoneditor.template,this.iconlib=this.jsoneditor.iconlib,this.translate=this.jsoneditor.translate||f.defaults.translate,this.original_schema=a.schema,this.schema=this.jsoneditor.expandSchema(this.original_schema),this.options=c({},this.options||{},a.schema.options||{},a),a.path||this.schema.id||(this.schema.id="root"),this.path=a.path||"root",this.formname=a.formname||this.path.replace(/\.([^.]+)/g,"[$1]"),this.jsoneditor.options.form_name_root&&(this.formname=this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+"[")),this.key=this.path.split(".").pop(),this.parent=a.parent,this.link_watchers=[],a.container&&this.setContainer(a.container)},setContainer:function(a){this.container=a,this.schema.id&&this.container.setAttribute("data-schemaid",this.schema.id),this.schema.type&&"string"==typeof this.schema.type&&this.container.setAttribute("data-schematype",this.schema.type),this.container.setAttribute("data-schemapath",this.path)},preBuild:function(){},build:function(){},postBuild:function(){this.setupWatchListeners(),this.addLinks(),this.setValue(this.getDefault(),!0),this.updateHeaderText(),this.register(),this.onWatchedFieldChange()},setupWatchListeners:function(){var a=this;if(this.watched={},this.schema.vars&&(this.schema.watch=this.schema.vars),this.watched_values={},this.watch_listener=function(){a.refreshWatchedFieldValues()&&a.onWatchedFieldChange()},this.register(),this.schema.hasOwnProperty("watch")){var b,c,d,e,f;for(var g in this.schema.watch)if(this.schema.watch.hasOwnProperty(g)){if(b=this.schema.watch[g],Array.isArray(b)){if(b.length<2)continue;c=[b[0]].concat(b[1].split("."))}else c=b.split("."),a.theme.closest(a.container,'[data-schemaid="'+c[0]+'"]')||c.unshift("#");if(d=c.shift(),"#"===d&&(d=a.jsoneditor.schema.id||"root"),e=a.theme.closest(a.container,'[data-schemaid="'+d+'"]'),!e)throw"Could not find ancestor node with id "+d;f=e.getAttribute("data-schemapath")+"."+c.join("."),a.jsoneditor.watch(f,a.watch_listener),a.watched[g]=f}}this.schema.headerTemplate&&(this.header_template=this.jsoneditor.compileTemplate(this.schema.headerTemplate,this.template_engine))},addLinks:function(){if(!this.no_link_holder&&(this.link_holder=this.theme.getLinksHolder(),this.container.appendChild(this.link_holder),this.schema.links))for(var a=0;a<this.schema.links.length;a++)this.addLink(this.getLink(this.schema.links[a]))},getButton:function(a,b,c){var d="json-editor-btn-"+b;b=this.iconlib?this.iconlib.getIcon(b):null,!b&&c&&(a=c,c=null);var e=this.theme.getButton(a,b,c);return e.className+=" "+d+" ",e},setButtonText:function(a,b,c,d){return c=this.iconlib?this.iconlib.getIcon(c):null,!c&&d&&(b=d,d=null),this.theme.setButtonText(a,b,c,d)},addLink:function(a){this.link_holder&&this.link_holder.appendChild(a)},getLink:function(a){var b,c,d=a.mediaType||"application/javascript",e=d.split("/")[0],f=this.jsoneditor.compileTemplate(a.href,this.template_engine),g=null;if(a.download&&(g=a.download),g&&g!==!0&&(g=this.jsoneditor.compileTemplate(g,this.template_engine)),"image"===e){b=this.theme.getBlockLinkHolder(),c=document.createElement("a"),c.setAttribute("target","_blank");var h=document.createElement("img");this.theme.createImageLink(b,c,h),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.setAttribute("title",a.rel||d),h.setAttribute("src",d)})}else if(["audio","video"].indexOf(e)>=0){b=this.theme.getBlockLinkHolder(),c=this.theme.getBlockLink(),c.setAttribute("target","_blank");var i=document.createElement(e);i.setAttribute("controls","controls"),this.theme.createMediaLink(b,c,i),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.textContent=a.rel||d,i.setAttribute("src",d)})}else c=b=this.theme.getBlockLink(),b.setAttribute("target","_blank"),b.textContent=a.rel,this.link_watchers.push(function(c){var d=f(c);b.setAttribute("href",d),b.textContent=a.rel||d});return g&&c&&(g===!0?c.setAttribute("download",""):this.link_watchers.push(function(a){c.setAttribute("download",g(a))})),a.class&&(c.className=c.className+" "+a.class),b},refreshWatchedFieldValues:function(){if(this.watched_values){var a={},b=!1,c=this;if(this.watched){var d,e;for(var f in this.watched)this.watched.hasOwnProperty(f)&&(e=c.jsoneditor.getEditor(this.watched[f]),d=e?e.getValue():null,c.watched_values[f]!==d&&(b=!0),a[f]=d)}return a.self=this.getValue(),this.watched_values.self!==a.self&&(b=!0),this.watched_values=a,b}},getWatchedFieldValues:function(){return this.watched_values},updateHeaderText:function(){if(this.header)if(this.header.children.length){for(var a=0;a<this.header.childNodes.length;a++)if(3===this.header.childNodes[a].nodeType){this.header.childNodes[a].nodeValue=this.getHeaderText();break}}else this.header.textContent=this.getHeaderText()},getHeaderText:function(a){return this.header_text?this.header_text:a?this.schema.title:this.getTitle()},onWatchedFieldChange:function(){var a;if(this.header_template){a=c(this.getWatchedFieldValues(),{key:this.key,i:this.key,i0:1*this.key,i1:1*this.key+1,title:this.getTitle()});var b=this.header_template(a);b!==this.header_text&&(this.header_text=b,this.updateHeaderText(),this.notify())}if(this.link_watchers.length){a=this.getWatchedFieldValues();for(var d=0;d<this.link_watchers.length;d++)this.link_watchers[d](a)}},setValue:function(a){this.value=a},getValue:function(){return this.value},refreshValue:function(){},getChildEditors:function(){return!1},destroy:function(){var a=this;this.unregister(this),d(this.watched,function(b,c){a.jsoneditor.unwatch(c,a.watch_listener)}),this.watched=null,this.watched_values=null,this.watch_listener=null,this.header_text=null,this.header_template=null,this.value=null,this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container),this.container=null,this.jsoneditor=null,this.schema=null,this.path=null,this.key=null,this.parent=null},getDefault:function(){if(this.schema.default)return this.schema.default;if(this.schema.enum)return this.schema.enum[0];var a=this.schema.type||this.schema.oneOf;if(a&&Array.isArray(a)&&(a=a[0]),a&&"object"==typeof a&&(a=a.type),a&&Array.isArray(a)&&(a=a[0]),"string"==typeof a){if("number"===a)return 0;if("boolean"===a)return!1;if("integer"===a)return 0;if("string"===a)return"";if("object"===a)return{};if("array"===a)return[]}return null},getTitle:function(){return this.schema.title||this.key},enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},isEnabled:function(){return!this.disabled},isRequired:function(){return"boolean"==typeof this.schema.required?this.schema.required:this.parent&&this.parent.schema&&Array.isArray(this.parent.schema.required)?this.parent.schema.required.indexOf(this.key)>-1:!!this.jsoneditor.options.required_by_default},getDisplayText:function(a){var b=[],c={};d(a,function(a,b){b.title&&(c[b.title]=c[b.title]||0,c[b.title]++),b.description&&(c[b.description]=c[b.description]||0,c[b.description]++),b.format&&(c[b.format]=c[b.format]||0,c[b.format]++),b.type&&(c[b.type]=c[b.type]||0,c[b.type]++)}),d(a,function(a,d){var e;e="string"==typeof d?d:d.title&&c[d.title]<=1?d.title:d.format&&c[d.format]<=1?d.format:d.type&&c[d.type]<=1?d.type:d.description&&c[d.description]<=1?d.description:d.title?d.title:d.format?d.format:d.type?d.type:d.description?d.description:JSON.stringify(d).length<50?JSON.stringify(d):"type",b.push(e)});var e={};return d(b,function(a,d){e[d]=e[d]||0,e[d]++,c[d]>1&&(b[a]=d+" "+e[d])}),b},getOption:function(a){try{throw"getOption is deprecated"}catch(a){window.console.error(a)}return this.options[a]},showValidationErrors:function(a){}}),f.defaults.editors.null=f.AbstractEditor.extend({getValue:function(){return null},setValue:function(){this.onChange()},getNumColumns:function(){return 2}}),f.defaults.editors.string=f.AbstractEditor.extend({register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},setValue:function(a,b,c){if((!this.template||c)&&(null===a||"undefined"==typeof a?a="":"object"==typeof a?a=JSON.stringify(a):"string"!=typeof a&&(a=""+a),a!==this.serialized)){var d=this.sanitize(a);if(this.input.value!==d){this.input.value=d,this.sceditor_instance?this.sceditor_instance.val(d):this.epiceditor?this.epiceditor.importFile(null,d):this.ace_editor&&this.ace_editor.setValue(d);var e=c||this.getValue()!==a;this.refreshValue(),b?this.is_dirty=!1:"change"===this.jsoneditor.options.show_errors&&(this.is_dirty=!0),this.adjust_height&&this.adjust_height(this.input),this.onChange(e)}}},getNumColumns:function(){var a,b=Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);return a="textarea"===this.input_type?6:["text","email"].indexOf(this.input_type)>=0?4:2,Math.min(12,Math.max(b,a))},build:function(){var a=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.format=this.schema.format,!this.format&&this.schema.media&&this.schema.media.type&&(this.format=this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,"")),!this.format&&this.options.default_format&&(this.format=this.options.default_format),this.options.format&&(this.format=this.options.format),this.format)if("textarea"===this.format)this.input_type="textarea",this.input=this.theme.getTextareaInput();else if("range"===this.format){this.input_type="range";var b=this.schema.minimum||0,c=this.schema.maximum||Math.max(100,b+1),d=1;this.schema.multipleOf&&(b%this.schema.multipleOf&&(b=Math.ceil(b/this.schema.multipleOf)*this.schema.multipleOf),c%this.schema.multipleOf&&(c=Math.floor(c/this.schema.multipleOf)*this.schema.multipleOf),d=this.schema.multipleOf),this.input=this.theme.getRangeInput(b,c,d)}else["actionscript","batchfile","bbcode","c","c++","cpp","coffee","csharp","css","dart","django","ejs","erlang","golang","groovy","handlebars","haskell","haxe","html","ini","jade","java","javascript","json","less","lisp","lua","makefile","markdown","matlab","mysql","objectivec","pascal","perl","pgsql","php","python","r","ruby","sass","scala","scss","smarty","sql","stylus","svg","twig","vbscript","xml","yaml"].indexOf(this.format)>=0?(this.input_type=this.format,this.source_code=!0,this.input=this.theme.getTextareaInput()):(this.input_type=this.format,this.input=this.theme.getFormInputField(this.input_type));else this.input_type="text",this.input=this.theme.getFormInputField(this.input_type);"undefined"!=typeof this.schema.maxLength&&this.input.setAttribute("maxlength",this.schema.maxLength),"undefined"!=typeof this.schema.pattern?this.input.setAttribute("pattern",this.schema.pattern):"undefined"!=typeof this.schema.minLength&&this.input.setAttribute("pattern",".{"+this.schema.minLength+",}"),this.options.compact?this.container.className+=" compact":this.options.input_width&&(this.input.style.width=this.options.input_width),(this.schema.readOnly||this.schema.readonly||this.schema.template)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),a.schema.template)return void(this.value=a.value);var c=this.value,d=a.sanitize(c);c!==d&&(this.value=d),a.is_dirty=!0,a.refreshValue(),a.onChange(!0)}),this.options.input_height&&(this.input.style.height=this.options.input_height),this.options.expand_height&&(this.adjust_height=function(a){if(a){var b,c=a.offsetHeight;if(a.offsetHeight<a.scrollHeight)for(b=0;a.offsetHeight<a.scrollHeight+3&&!(b>100);)b++,c++,a.style.height=c+"px";else{for(b=0;a.offsetHeight>=a.scrollHeight+3&&!(b>100);)b++,c--,a.style.height=c+"px";a.style.height=c+1+"px"}}},this.input.addEventListener("keyup",function(b){a.adjust_height(this)}),this.input.addEventListener("change",function(b){a.adjust_height(this)}),this.adjust_height()),this.format&&this.input.setAttribute("data-schemaformat",this.format),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),window.requestAnimationFrame(function(){a.input.parentNode&&a.afterInputReady(),a.adjust_height&&a.adjust_height(a.input)}),this.schema.template?(this.template=this.jsoneditor.compileTemplate(this.schema.template,this.template_engine),this.refreshValue()):this.refreshValue()},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},afterInputReady:function(){var a,b=this;if(this.source_code)if(this.options.wysiwyg&&["html","bbcode"].indexOf(this.input_type)>=0&&window.jQuery&&window.jQuery.fn&&window.jQuery.fn.sceditor)a=c({},{ | |||
There was a problem hiding this comment.
Do not change this please, revert.
Revert the change in openwisp_controller/config/static/config/js/lib/jsonschema-ui.js because it is a minified third-party library and modifying it causes an unwanted large diff.
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3). |
Test Failures in CIHello @ApurveKaranwal,
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openwisp_controller/geo/tests/test_selenium.py`:
- Around line 98-100: The move_by_offset(30, 15) call in the ActionChains method
chain uses magic numbers without explanation, making the code unclear and
fragile. Replace the numeric literals 30 and 15 with named constants (for
example, OFFSET_X and OFFSET_Y) defined at the top of the test class or test
method, and add a concise comment explaining why this specific offset is
necessary for the map interaction (such as ensuring the click lands on a
specific map element or avoiding unintended interactions). This makes the test
more maintainable and helps future developers understand the intent.
- Around line 21-22: The patches to geocoding_views.geocode and
geocoding_views.reverse_geocode are applied at module-level (import time),
causing mocked behavior to leak into other tests and create order-dependent
failures. Move these MagicMock assignments from the module-level scope into the
appropriate test setup method (such as setUp in a test class) or use a context
manager like unittest.mock.patch to scope the mocks to individual test
lifecycles, ensuring the originals are properly restored after each test
completes.
- Around line 103-106: Replace the fixed sleep(0.05) call with an explicit wait
condition that checks for the address field update before proceeding. Use
WebDriverWait to wait until the address_input element's value attribute contains
"Lazio 00185, ITA" (the new_address value), then remove the sleep statement
entirely. This will make the test deterministic and eliminate the
timing-sensitive failure mode in CI environments. The wait should target the
element retrieved by By.ID with value "id_address" and poll until the condition
is satisfied before the assertion at Line 106.
In `@openwisp_controller/pki/base/models.py`:
- Around line 18-22: Replace `datetime.now()` with `timezone.now()` when
calculating the `start` variable to ensure Django's configured timezone is
consistently used throughout the calculation. The `timezone.now()` function
automatically respects Django's `USE_TZ` setting and will return either a
timezone-aware or naive datetime as appropriate, eliminating the mismatch
between how the initial datetime is obtained and how it is later made aware,
which prevents date shifting when the server timezone differs from the Django
configured timezone.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c07eeb77-faf6-4ced-b847-8226a8318f96
📒 Files selected for processing (4)
openwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/base/models.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.pytests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}
📄 CodeRabbit inference engine (Custom checks)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously
Files:
tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.pyopenwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}
📄 CodeRabbit inference engine (Custom checks)
Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries
Files:
tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.pyopenwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}
📄 CodeRabbit inference engine (Custom checks)
Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable
Files:
tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.pyopenwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.{py,html}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.pyopenwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.pyopenwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
🧠 Learnings (6)
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.
Applied to files:
tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.pyopenwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.pyopenwisp_controller/geo/tests/test_selenium.py
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.
Applied to files:
openwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.
Applied to files:
openwisp_controller/pki/base/models.pyopenwisp_controller/geo/tests/test_selenium.pyopenwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/geo/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/geo/tests/test_selenium.py
🔇 Additional comments (3)
openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py (1)
12-30: LGTM!tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py (1)
14-32: LGTM!openwisp_controller/geo/tests/test_selenium.py (1)
15-20: LGTM!Also applies to: 81-102, 107-113, 157-177
| geocoding_views.geocode = MagicMock(return_value=MockLocation()) | ||
| geocoding_views.reverse_geocode = MagicMock(return_value=MockLocation()) |
There was a problem hiding this comment.
Avoid module-level global monkeypatching of geocoding_views in tests.
At Line 21 and Line 22, patching geocoding_views.geocode / reverse_geocode at import time can leak mocked behavior into other tests in the same process and create order-dependent failures. Scope these mocks to the test/class lifecycle and restore originals.
Proposed scoped patching approach
+from unittest.mock import patch
+
class TestDeviceAdminGeoSelenium(
@@
inline_field_prefix = "devicelocation"
+
+ `@classmethod`
+ def setUpClass(cls):
+ super().setUpClass()
+ cls._geocode_patcher = patch.object(
+ geocoding_views, "geocode", return_value=MockLocation()
+ )
+ cls._reverse_geocode_patcher = patch.object(
+ geocoding_views, "reverse_geocode", return_value=MockLocation()
+ )
+ cls._geocode_patcher.start()
+ cls._reverse_geocode_patcher.start()
+
+ `@classmethod`
+ def tearDownClass(cls):
+ cls._reverse_geocode_patcher.stop()
+ cls._geocode_patcher.stop()
+ super().tearDownClass()
@@
-geocoding_views.geocode = MagicMock(return_value=MockLocation())
-geocoding_views.reverse_geocode = MagicMock(return_value=MockLocation())🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@openwisp_controller/geo/tests/test_selenium.py` around lines 21 - 22, The
patches to geocoding_views.geocode and geocoding_views.reverse_geocode are
applied at module-level (import time), causing mocked behavior to leak into
other tests and create order-dependent failures. Move these MagicMock
assignments from the module-level scope into the appropriate test setup method
(such as setUp in a test class) or use a context manager like
unittest.mock.patch to scope the mocks to individual test lifecycles, ensuring
the originals are properly restored after each test completes.
| ActionChains(self.web_driver).move_to_element(elem).move_by_offset( | ||
| 30, 15 | ||
| ).click().perform() |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Document the map-click offset rationale or replace magic numbers with named constants.
At Line 98-Line 100, move_by_offset(30, 15) is non-obvious and fragile without context. Add a brief “why” comment and/or constants so future maintainers know why this offset is acceptable.
As per coding guidelines, cryptic code must include a concise explanation of why the complexity is needed and acceptable.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@openwisp_controller/geo/tests/test_selenium.py` around lines 98 - 100, The
move_by_offset(30, 15) call in the ActionChains method chain uses magic numbers
without explanation, making the code unclear and fragile. Replace the numeric
literals 30 and 15 with named constants (for example, OFFSET_X and OFFSET_Y)
defined at the top of the test class or test method, and add a concise comment
explaining why this specific offset is necessary for the map interaction (such
as ensuring the click lands on a specific map element or avoiding unintended
interactions). This makes the test more maintainable and helps future developers
understand the intent.
Source: Coding guidelines
| sleep(0.05) | ||
| new_address = "Lazio 00185, ITA" | ||
| address_input = self.find_element(by=By.ID, value="id_address") | ||
| self.assertIn(new_address, address_input.get_attribute("value")) |
There was a problem hiding this comment.
Replace fixed sleep with an explicit wait on address-field update.
At Line 103, sleep(0.05) is timing-sensitive and can still fail under CI jitter. Wait directly for the address value condition before asserting at Line 106.
Proposed deterministic wait
- sleep(0.05)
new_address = "Lazio 00185, ITA"
- address_input = self.find_element(by=By.ID, value="id_address")
- self.assertIn(new_address, address_input.get_attribute("value"))
+ WebDriverWait(self.web_driver, 10).until(
+ lambda d: new_address in d.find_element(By.ID, "id_address").get_attribute("value")
+ )
+ address_input = self.find_element(by=By.ID, value="id_address")
+ self.assertIn(new_address, address_input.get_attribute("value"))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@openwisp_controller/geo/tests/test_selenium.py` around lines 103 - 106,
Replace the fixed sleep(0.05) call with an explicit wait condition that checks
for the address field update before proceeding. Use WebDriverWait to wait until
the address_input element's value attribute contains "Lazio 00185, ITA" (the
new_address value), then remove the sleep statement entirely. This will make the
test deterministic and eliminate the timing-sensitive failure mode in CI
environments. The wait should target the element retrieved by By.ID with value
"id_address" and poll until the condition is satisfied before the assertion at
Line 106.
| start = datetime.now() - timedelta(days=1) | ||
| start = start.replace(hour=0, minute=0, second=0, microsecond=0) | ||
| if settings.USE_TZ: | ||
| return timezone.make_aware(start) | ||
| return start |
There was a problem hiding this comment.
Build validity_start from Django timezone, not host-local naive time.
Current logic can shift the date when server timezone and Django configured timezone differ, because datetime.now() and timezone.make_aware() may use different timezone bases. Use timezone.now() as the source and normalize from there.
Proposed fix
def default_validity_start():
- from datetime import datetime, timedelta
+ from datetime import timedelta
from django.conf import settings
from django.utils import timezone
- start = datetime.now() - timedelta(days=1)
- start = start.replace(hour=0, minute=0, second=0, microsecond=0)
+ if settings.USE_TZ:
+ start = timezone.localtime(timezone.now()) - timedelta(days=1)
+ else:
+ start = timezone.now() - timedelta(days=1)
+ start = start.replace(hour=0, minute=0, second=0, microsecond=0)
if settings.USE_TZ:
- return timezone.make_aware(start)
+ return timezone.make_aware(start) if timezone.is_naive(start) else start
return start🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@openwisp_controller/pki/base/models.py` around lines 18 - 22, Replace
`datetime.now()` with `timezone.now()` when calculating the `start` variable to
ensure Django's configured timezone is consistently used throughout the
calculation. The `timezone.now()` function automatically respects Django's
`USE_TZ` setting and will return either a timezone-aware or naive datetime as
appropriate, eliminating the mismatch between how the initial datetime is
obtained and how it is later made aware, which prevents date shifting when the
server timezone differs from the Django configured timezone.
|
You started changing things that don't have anything to do with spelling mistakes. I am sorry but we don't have time to deal with this kind of work right now in the @openwisp project. |
Checklist
Reference to Existing Issue
Closes #1398
Description of Changes
This PR fixes various spelling mistakes, grammatical errors, and typos across the codebase (including Python comments, docstrings, tests, and HTML templates).
The misspellings were identified using
codespellas well as manual review. Some of the corrections include:commiting->committingtaks->tasksatleast->at leastdeafult->defaultSelenium tests do not support).These changes have zero impact on the system's core logic but improve code readability and maintainability.
Screenshot
N/A