Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object URL references, named paths and pseudo-slugs #1674

Merged
merged 6 commits into from
Oct 22, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 12 additions & 4 deletions evennia/accounts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,19 @@ def setUp(self):
self.s1 = MagicMock()
self.s1.puppet = None
self.s1.sessid = 0
self.s1.data_outj

def tearDown(self):
if hasattr(self, "account"):
self.account.delete()
def test_absolute_url(self):
"Get URL for account detail page on website"
self.account = create.create_account("TestAccount%s" % randint(100000, 999999),
email="test@test.com", password="testpassword", typeclass=DefaultAccount)
self.assertTrue(self.account.web_get_detail_url())

def test_admin_url(self):
"Get object's URL for access via Admin pane"
self.account = create.create_account("TestAccount%s" % randint(100000, 999999),
email="test@test.com", password="testpassword", typeclass=DefaultAccount)
self.assertTrue(self.account.web_get_admin_url())
self.assertTrue(self.account.web_get_admin_url() != '#')

def test_password_validation(self):
"Check password validators deny bad passwords"
Expand Down
8 changes: 5 additions & 3 deletions evennia/objects/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
"""
import time
import inflect
import re
from builtins import object
from future.utils import with_metaclass
from collections import defaultdict

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from django.utils.text import slugify

from evennia.typeclasses.models import TypeclassBase
from evennia.typeclasses.attributes import NickHandler
Expand Down Expand Up @@ -324,7 +328,7 @@ def get_numbered_name(self, count, looker, **kwargs):
# look at 'an egg'.
self.aliases.add(singular, category="plural_key")
return singular, plural

def search(self, searchdata,
global_search=False,
use_nicks=True,
Expand Down Expand Up @@ -1825,7 +1829,6 @@ class DefaultCharacter(DefaultObject):
a character avatar controlled by an account.

"""

def basetype_setup(self):
"""
Setup character-specific security.
Expand Down Expand Up @@ -1942,7 +1945,6 @@ class DefaultRoom(DefaultObject):
This is the base room object. It's just like any Object except its
location is always `None`.
"""

def basetype_setup(self):
"""
Simple room setup setting locks to make sure the room
Expand Down
11 changes: 11 additions & 0 deletions evennia/objects/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from evennia.utils.test_resources import EvenniaTest

class DefaultObjectTest(EvenniaTest):

def test_urls(self):
"Make sure objects are returning URLs"
self.assertTrue(self.char1.get_absolute_url())
self.assertTrue('admin' in self.char1.web_get_admin_url())

self.assertTrue(self.room1.get_absolute_url())
self.assertTrue('admin' in self.room1.web_get_admin_url())
135 changes: 135 additions & 0 deletions evennia/typeclasses/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ class needs to supply a ForeignKey field attr_object pointing to the kind

from django.db.models.base import ModelBase
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from django.urls import reverse
from django.utils.encoding import smart_str
from django.utils.text import slugify

from evennia.typeclasses.attributes import Attribute, AttributeHandler, NAttributeHandler
from evennia.typeclasses.tags import Tag, TagHandler, AliasHandler, PermissionHandler
Expand Down Expand Up @@ -733,3 +736,135 @@ def at_rename(self, oldname, newname):

"""
pass

#
# Web/Django methods
#

def web_get_admin_url(self):
"""
Returns the URI path for the Django Admin page for this object.

ex. Account#1 = '/admin/accounts/accountdb/1/change/'

Returns:
path (str): URI path to Django Admin page for object.

"""
content_type = ContentType.objects.get_for_model(self.__class__)
return reverse("admin:%s_%s_change" % (content_type.app_label, content_type.model), args=(self.id,))

@classmethod
def web_get_create_url(cls):
"""
Returns the URI path for a View that allows users to create new
instances of this object.

ex. Chargen = '/characters/create/'

For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-create' would be referenced by this method.

ex.
url(r'characters/create/', ChargenView.as_view(), name='character-create')

If no View has been created and defined in urls.py, returns an
HTML anchor.

This method is naive and simply returns a path. Securing access to
the actual view and limiting who can create new objects is the
developer's responsibility.

Returns:
path (str): URI path to object creation page, if defined.

"""
try: return reverse('%s-create' % cls._meta.verbose_name.lower())
except: return '#'

def web_get_detail_url(self):
"""
Returns the URI path for a View that allows users to view details for
this object.

ex. Oscar (Character) = '/characters/oscar/1/'

For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-detail' would be referenced by this method.

ex.
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$', CharDetailView.as_view(), name='character-detail')

If no View has been created and defined in urls.py, returns an
HTML anchor.

This method is naive and simply returns a path. Securing access to
the actual view and limiting who can view this object is the developer's
responsibility.

Returns:
path (str): URI path to object detail page, if defined.

"""
try: return reverse('%s-detail' % self._meta.verbose_name.lower(), kwargs={'pk': self.pk, 'slug': slugify(self.name)})
except: return '#'

def web_get_update_url(self):
"""
Returns the URI path for a View that allows users to update this
object.

ex. Oscar (Character) = '/characters/oscar/1/change/'

For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-update' would be referenced by this method.

ex.
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/change/$', CharUpdateView.as_view(), name='character-update')

If no View has been created and defined in urls.py, returns an
HTML anchor.

This method is naive and simply returns a path. Securing access to
the actual view and limiting who can modify objects is the developer's
responsibility.

Returns:
path (str): URI path to object update page, if defined.

"""
try: return reverse('%s-update' % self._meta.verbose_name.lower(), kwargs={'pk': self.pk, 'slug': slugify(self.name)})
except: return '#'

def web_get_delete_url(self):
"""
Returns the URI path for a View that allows users to delete this object.

ex. Oscar (Character) = '/characters/oscar/1/delete/'

For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-detail' would be referenced by this method.

ex.
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/delete/$', CharDeleteView.as_view(), name='character-delete')

If no View has been created and defined in urls.py, returns an
HTML anchor.

This method is naive and simply returns a path. Securing access to
the actual view and limiting who can delete this object is the developer's
responsibility.

Returns:
path (str): URI path to object deletion page, if defined.

"""
try: return reverse('%s-delete' % self._meta.verbose_name.lower(), kwargs={'pk': self.pk, 'slug': slugify(self.name)})
except: return '#'

# Used by Django Sites/Admin
get_absolute_url = web_get_detail_url
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is web_get_detail_url not named web_absolute_url if get_absolute_url is what django expects - would not that name (just adding web_) be more expected?