Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/developer/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ Once you have created the models, add the following to your
GEO_LOCATION_MODEL = "sample_geo.Location"
GEO_FLOORPLAN_MODEL = "sample_geo.FloorPlan"
GEO_DEVICELOCATION_MODEL = "sample_geo.DeviceLocation"
GEO_ORGANIZATIONGEOSETTINGS_MODEL = "sample_geo.OrganizationGeoSettings"
CONNECTION_CREDENTIALS_MODEL = "sample_connection.Credentials"
CONNECTION_DEVICECONNECTION_MODEL = "sample_connection.DeviceConnection"
CONNECTION_COMMAND_MODEL = "sample_connection.Command"
Expand Down Expand Up @@ -456,7 +457,11 @@ For example:

.. code-block:: python

from openwisp_controller.geo.admin import FloorPlanAdmin, LocationAdmin
from openwisp_controller.geo.admin import (
FloorPlanAdmin,
LocationAdmin,
GeoSettingsInline,
)

FloorPlanAdmin.fields += ["example"] # <-- monkey patching example

Expand Down
28 changes: 28 additions & 0 deletions docs/developer/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,31 @@ The signal is emitted when the peers of VPN server gets changed.

It is only emitted for ``Vpn`` object with **WireGuard** or **VXLAN over
WireGuard** backend.

``whois_fetched``
~~~~~~~~~~~~~~~~~

**Path**: ``openwisp_controller.config.signals.whois_fetched``

**Arguments**:

- ``whois``: instance of ``WHOISInfo`` that was created or updated
- ``updated_fields``: list of fields updated in the lookup
- ``device``: optional instance of ``Device`` related to this WHOIS lookup

This signal is emitted when a WHOIS lookup task successfully creates or
updates a ``WHOISInfo`` record.

``whois_lookup_skipped``
~~~~~~~~~~~~~~~~~~~~~~~~

**Path**: ``openwisp_controller.config.signals.whois_lookup_skipped``

**Arguments**:

- ``device``: instance of ``Device`` for which the WHOIS lookup was
skipped

This signal is emitted when a WHOIS lookup is not triggered because the
lookup conditions were not met (for example, an up-to-date WHOIS record
already exists).
47 changes: 47 additions & 0 deletions docs/user/rest-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,53 @@ device is updating it's position.
},
}'

Get Organization Geographic Settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: text

GET /api/v1/controller/organization/{organization_pk}/geo-settings/

This endpoint allows retrieving geographic settings for a specific
organization.

Update Organization Geographic Settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This endpoint allows updating geographic settings for a specific
organization.

.. code-block:: text

PUT /api/v1/controller/organization/{organization_pk}/geo-settings/

Comment thread
coderabbitai[bot] marked this conversation as resolved.
.. code-block:: text

curl -X PUT \
'http://127.0.0.1:8000/api/v1/controller/organization/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/geo-settings/' \
-H 'authorization: Bearer <token>' \
-H 'content-type: application/json' \
-d '{"estimated_location_enabled": true}'

Partially Update Organization Geographic Settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This endpoint allows partial updates of Organization geographic settings
using the PATCH method. PATCH accepts a subset of fields and applies a
partial update to the resource at the same endpoint path.

.. code-block:: text

PATCH /api/v1/controller/organization/{organization_pk}/geo-settings/

.. code-block:: text

curl -X PATCH \
'http://127.0.0.1:8000/api/v1/controller/organization/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/geo-settings/' \
-H 'authorization: Bearer <token>' \
-H 'content-type: application/json' \
-d '{"estimated_location_enabled": true}'
Comment thread
coderabbitai[bot] marked this conversation as resolved.

List Locations
~~~~~~~~~~~~~~

Expand Down
8 changes: 3 additions & 5 deletions openwisp_controller/config/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,7 @@ def change_group(self, request, queryset):
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
"opts": self.model._meta,
"changelist_url": (
f"{request.resolver_match.app_name}:"
f"{request.resolver_match.url_name}"
f"{request.resolver_match.app_name}:{request.resolver_match.url_name}"
),
}

Expand Down Expand Up @@ -1104,8 +1103,7 @@ def save_clones(view, user, queryset, organization=None):
validated_org = Organization.objects.get(pk=organization)
except (ValidationError, Organization.DoesNotExist) as e:
logger.warning(
"Detected tampering in clone template "
f"form by user {user}: {e}"
f"Detected tampering in clone template form by user {user}: {e}"
)
return
if not user.is_superuser and not user.is_manager(organization):
Expand Down Expand Up @@ -1393,7 +1391,7 @@ def get_fields(self, request, obj=None):
if app_settings.REGISTRATION_ENABLED:
fields += ["registration_enabled", "shared_secret"]
if app_settings.WHOIS_CONFIGURED:
fields += ["whois_enabled", "estimated_location_enabled"]
fields += ["whois_enabled"]
fields += ["context"]
return fields

Expand Down
14 changes: 0 additions & 14 deletions openwisp_controller/config/base/multitenancy.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ class AbstractOrganizationConfigSettings(UUIDModel):
fallback=app_settings.WHOIS_ENABLED,
verbose_name=_("WHOIS Enabled"),
)
estimated_location_enabled = FallbackBooleanChoiceField(
help_text=_("Whether the estimated location feature is enabled"),
fallback=app_settings.ESTIMATED_LOCATION_ENABLED,
verbose_name=_("Estimated Location Enabled"),
)
context = JSONField(
blank=True,
default=dict,
Expand Down Expand Up @@ -77,15 +72,6 @@ def clean(self):
)
}
)
if not self.whois_enabled and self.estimated_location_enabled:
raise ValidationError(
{
"estimated_location_enabled": _(
"Estimated Location feature requires "
"WHOIS Lookup feature to be enabled."
)
}
)
return super().clean()

def save(
Expand Down
13 changes: 0 additions & 13 deletions openwisp_controller/config/base/whois.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,3 @@ def _location_name(self):
}
# Use named placeholder for consistency
return _("Estimated Location: %(ip)s") % {"ip": self.ip_address}

def _get_defaults_for_estimated_location(self):
"""
Used to get default values for creating or updating
an estimated location based on the WHOIS information.
"""
return {
"name": self._location_name,
"type": "outdoor",
"is_mobile": False,
"geometry": self.coordinates,
"address": self.formatted_address,
}
9 changes: 9 additions & 0 deletions openwisp_controller/config/migrations/0062_whoisinfo.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Generated by Django 5.2.1 on 2025-06-26 02:13
# Updated on 2026-04-06

import django.contrib.gis.db.models.fields
import django.utils.timezone
import model_utils.fields
from django.contrib.gis.db.models.fields import PointField
from django.db import migrations, models

import openwisp_utils.fields
Expand Down Expand Up @@ -59,6 +62,12 @@ class Migration(migrations.Migration):
max_length=100,
),
),
(
"coordinates",
PointField(
blank=True, help_text="Coordinates", null=True, srid=4326
),
),
],
options={
"abstract": False,
Expand Down

This file was deleted.

13 changes: 2 additions & 11 deletions openwisp_controller/config/settings.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import logging

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext_lazy as _

logger = logging.getLogger(__name__)

from ..settings import get_setting

def get_setting(option, default):
return getattr(settings, f"OPENWISP_CONTROLLER_{option}", default)
logger = logging.getLogger(__name__)


BACKENDS = get_setting(
Expand Down Expand Up @@ -85,9 +82,3 @@ def get_setting(option, default):
raise ImproperlyConfigured(
"OPENWISP_CONTROLLER_WHOIS_REFRESH_THRESHOLD_DAYS must be a positive integer"
)
ESTIMATED_LOCATION_ENABLED = get_setting("ESTIMATED_LOCATION_ENABLED", False)
if ESTIMATED_LOCATION_ENABLED and not WHOIS_ENABLED:
raise ImproperlyConfigured(
"OPENWISP_CONTROLLER_WHOIS_ENABLED must be set to True before "
"setting OPENWISP_CONTROLLER_ESTIMATED_LOCATION_ENABLED to True."
)
8 changes: 8 additions & 0 deletions openwisp_controller/config/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,11 @@
vpn_server_modified.__doc__ = """
providing arguments: ['instance']
"""
whois_fetched = Signal()
whois_fetched.__doc__ = """
Providing arguments: ['whois', 'updated_fields', 'device']
"""
whois_lookup_skipped = Signal()
whois_lookup_skipped.__doc__ = """
Providing arguments: ['device']
"""
2 changes: 0 additions & 2 deletions openwisp_controller/config/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
Device = load_model("config", "Device")
Config = load_model("config", "Config")
DeviceGroup = load_model("config", "DeviceGroup")
DeviceLocation = load_model("geo", "DeviceLocation")
Location = load_model("geo", "Location")
OrganizationUser = load_model("openwisp_users", "OrganizationUser")


Expand Down
Loading
Loading