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

Log operations #6419

Merged
merged 39 commits into from Jul 2, 2018
Merged

Log operations #6419

merged 39 commits into from Jul 2, 2018

Conversation

Aiky30
Copy link
Contributor

@Aiky30 Aiky30 commented Jun 19, 2018

Summary

The purpose of this PR is to get early feedback on how the logging has been implemented and any ideas / changes that Paulo may require.

Logs are all collected in the cms/utils/log_operations to keep the messages and contents consistent across all operations. It May seem repetitive but allows each log to be unique for different circumstances. The method names aim to make the log being created clear and concise.

Issues with the existing solution:
- A log entry isn't created when a page is created using the new page wizard.
- Difficult to find the log page creation as they are scattered. The existing implementation,uses the LogEntry model directly, others use the ModelAdmin Implementation. There should be one way where possible IMO.

Design Considerations:
- Allow external plugins to integrate with the CMS logger i.e. custom logs, may be useful if the LogEntry method is replaced for a different logging facility??
- A log helper method "create_log" that uses LogEntry and allows the removal of LogEntry at a later date.
- A page move event is recorded as CHANGE with no message. A "Moved." message has been manually added. There is a comment in the code to make this clear.
- Django admin keeps all logs in one area rather than spreading them out and potentially having them set inconsistently and scattered in multiple files.

Links to related discussion

Private documentation

@coveralls
Copy link

coveralls commented Jun 19, 2018

Coverage Status

Coverage remained the same at 78.189% when pulling 2c0a1c6 on Aiky30:log-operations into 61150ed on divio:release/4.0.x.

@@ -5,6 +5,9 @@
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.contrib.admin.utils import (
Copy link
Contributor

Choose a reason for hiding this comment

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

single line

@@ -1221,6 +1246,7 @@ def unpublish(self, request, page_id, language):
page.unpublish(language)
message = _('The %(language)s page "%(page)s" was successfully unpublished') % {
'language': language_name, 'page': page}

Copy link
Contributor

Choose a reason for hiding this comment

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

please avoid cosmetic changes unrelated to pr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood. I'll rectify.

@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
Copy link
Contributor

Choose a reason for hiding this comment

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

rename to log_entries

"""


def create_log(user_id, content_type_id, object_id, object_repr, action_flag, change_message):
Copy link
Contributor

Choose a reason for hiding this comment

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

rename to create_log_entry

@@ -75,6 +75,7 @@
from cms.utils.admin import jsonify_request
from cms.utils.conf import get_cms_setting
from cms.utils.urlutils import admin_reverse
from cms.utils.log_operations import log_page_change, log_page_move, log_page_delete
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm foreseeing issues with the implementations of title, plugins, etc because this is too tied to the admin.
As a result, I think it might be a better approach to use signal handlers and listen for the post_obj_operation signals for pages and post_placeholder_operation signal for placeholders and plugins.

I suggest to create a module called log_entries.py in cms/signals/
and there add all the necessary handlers.

So this won't really change much in your current implementation, other than where the log entry is created.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of this is fine but falls short for title changes as the signals for these are attached to a page change. No signals are fired specifically to title. One way to achieve this would be to catch a page edit and check what was being changed and log dependant on that. It's not a clean solution.

Page logs
"""


Copy link
Contributor

Choose a reason for hiding this comment

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

lots of repetition with the page log functions, also I don't think we should have one for each type of entry.
With the signal handler approach I suggested above, maybe create a page specific handler which receives the operation type and then map operation types to action flags.

message = '[{"added": {}}]'
self.assertEqual(message, log_entry.change_message)
message = 'Added.'
self.assertEqual(message, log_entry.get_change_message())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@czpython how far does the removal of LogEntry dependance need to go i.e.the test for get_change_message is specific to how the admin uses the multi lingual json objects in the change_message field. I sthis test redundant and does that also mean any auto generated messages are also redundant. I'm confused at just how far away this task needs to move away from the Admin LogEntry model.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point. We don't need to test get_change_message.

@czpython czpython self-assigned this Jun 20, 2018
@czpython czpython added this to the 4.0.0 milestone Jun 20, 2018
@czpython czpython changed the title WIP - Log operations Log operations Jun 20, 2018
token=operation_token,
obj=new_page,
)

Copy link
Contributor

Choose a reason for hiding this comment

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

remove newline

@@ -443,6 +467,12 @@ def clean(self):
return data

def save(self, commit=True):

Copy link
Contributor

Choose a reason for hiding this comment

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

remove newline

request=self.request,
operation=CHANGE_PAGE,
)

Copy link
Contributor

Choose a reason for hiding this comment

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

remove newline

request=self.request,
operation=CHANGE_PAGE,
)

data = self.cleaned_data
Copy link
Contributor

Choose a reason for hiding this comment

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

I would move data = self.cleaned_data above operation_token

token=operation_token,
obj=cms_page,
)

Copy link
Contributor

Choose a reason for hiding this comment

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

remove newline

Create a log for the correct placeholder operation type
"""

request = kwargs.pop('request')
Copy link
Contributor

Choose a reason for hiding this comment

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

use .get()

content_type_id = ContentType.objects.get_for_model(page).pk
object_id = page.pk
object_repr = str(page)

Copy link
Contributor

Choose a reason for hiding this comment

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

remove newline

object_id = page.pk
object_repr = str(page)

create_log_entry(user_id, content_type_id, object_id, object_repr, operation_handler['flag'], operation_handler['message'])
Copy link
Contributor

Choose a reason for hiding this comment

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

same as above. kwargs with each arg in one line

operation_handler = _placeholder_operations_map[operation_type]
user_id = request.user.pk
placeholder = kwargs.pop(operation_handler['placeholder_kwarg'])
page = placeholder.page
Copy link
Contributor

Choose a reason for hiding this comment

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

will need to check that placeholder actually has page

from django.forms.models import model_to_dict
from django.utils.translation import ugettext_lazy as _

from cms.api import create_page, add_plugin, create_title
Copy link
Contributor

Choose a reason for hiding this comment

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

import order

Copy link
Contributor Author

@Aiky30 Aiky30 left a comment

Choose a reason for hiding this comment

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

I think I can see your style of coding now so new line comments should be few and far between in the future. :-)

)
return token
operation=operation,
sender=self.__class__,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had initially tried self.model but it wasn't available for some reason. It's possible that one failed so I removed them all for consistency. I'll add them back and test.

}


def create_log_entry(user_id, content_type_id, object_id, object_repr, action_flag, change_message):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup. works.

Create a log for the correct page operation type
"""

request = kwargs.pop('request')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I got this from previous examples of how Django CMS fetched the kwargs. A search in all cms files will show all of the examples of "kwargs.pop". I try to follow how the app is already written when possible. :-). I'll update mine to get. ;-)

operation_type = kwargs.pop('operation')
obj = kwargs.pop('obj')

if operation_type in _page_operations_map:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've created a private function to fix this that checks for if the supplied object is a Page or PageType instance. I'm thinking that checking for just Page is OK though. What is the reasoning for an additional PageType check?

)
return token
operation=operation,
sender=self.__class__,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I hope that I've interpreted your comment correctly. I've removed the private class methods and just used the functions directly. This required changing a lot of the calls to fire a signal.

@Aiky30
Copy link
Contributor Author

Aiky30 commented Jun 27, 2018

@czpython
All checks are passing and I have made all of the changes that you requested. Please let me know if you need me to do anymore. I have also tested the signals by actually playing with the UI too rather than relying purely on the tests. :-)

@@ -480,10 +470,12 @@ def delete_view(self, request, object_id, extra_context=None):
return self.render_delete_form(request, context)

def delete_model(self, request, obj):
operation_token = self._send_pre_page_operation(
request,

Copy link
Contributor

Choose a reason for hiding this comment

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

remove newline

)

Copy link
Contributor

Choose a reason for hiding this comment

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

remove newline

@@ -0,0 +1,136 @@
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing this

user_id = request.user.pk
content_type_id = ContentType.objects.get_for_model(obj).pk
object_id = obj.pk
object_repr = str(obj)
Copy link
Contributor

Choose a reason for hiding this comment

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

this can be passed directly without declaring var

operation_handler = _page_operations_map[operation_type]
user_id = request.user.pk
content_type_id = ContentType.objects.get_for_model(obj).pk
object_id = obj.pk
Copy link
Contributor

Choose a reason for hiding this comment

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

this can be passed directly without declaring var

# Check that we have instructions for the operation and an instance of Page to link to in the log
if operation_type in _page_operations_map and _is_valid_page_instance(obj):
operation_handler = _page_operations_map[operation_type]
user_id = request.user.pk
Copy link
Contributor

Choose a reason for hiding this comment

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

this can be passed directly without declaring var

operation_type = kwargs.get('operation')
obj = kwargs.get('obj')

# Check that we have instructions for the operation and an instance of Page to link to in the log
Copy link
Contributor

Choose a reason for hiding this comment

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

break this into two lines

"""
Create a log for the correct page operation type
"""

Copy link
Contributor

Choose a reason for hiding this comment

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

avoid newlines after doc strings

# Check that we have an instance of Page to link to in the log
if _is_valid_page_instance(page):
content_type_id = ContentType.objects.get_for_model(page).pk
object_id = page.pk
Copy link
Contributor

Choose a reason for hiding this comment

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

see above about passing these directly

Copy link
Contributor Author

@Aiky30 Aiky30 left a comment

Choose a reason for hiding this comment

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

All done as per the request.

@czpython czpython merged commit 0394153 into django-cms:release/4.0.x Jul 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants