-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
edx-username-changer plugin added (#420)
* feat: edx-username-changer plugin added * fix: absolute import added * build: paver removed from OpenedX master * build: keeping the version same * fix: pants build fixed * docs: unnecessary steps removed
- Loading branch information
1 parent
de9c2ca
commit 5496c24
Showing
10 changed files
with
416 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
python_sources( | ||
name="edx_username_changer_source", | ||
) | ||
|
||
python_distribution( | ||
name="edx_username_changer_package", | ||
dependencies=[ | ||
":edx_username_changer_source", | ||
], | ||
provides=setup_py( | ||
name="edx-username-changer", | ||
version="0.3.2", | ||
description="An edX plugin to change username of edx accounts through admin panel", | ||
license="BSD-3-Clause", | ||
author="MIT Office of Digital Learning", | ||
include_package_data=True, | ||
zip_safe=False, | ||
keywords="Python edx", | ||
entry_points={ | ||
"lms.djangoapp": [ | ||
"edx_username_changer = edx_username_changer.apps:EdxUsernameChangerConfig", | ||
], | ||
"cms.djangoapp": [ | ||
"edx_username_changer = edx_username_changer.apps:EdxUsernameChangerConfig", | ||
], | ||
}, | ||
), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
Copyright (C) 2022 MIT Open Learning | ||
|
||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are met: | ||
|
||
* Redistributions of source code must retain the above copyright notice, this | ||
list of conditions and the following disclaimer. | ||
|
||
* Redistributions in binary form must reproduce the above copyright notice, | ||
this list of conditions and the following disclaimer in the documentation | ||
and/or other materials provided with the distribution. | ||
|
||
* Neither the name of the copyright holder nor the names of its | ||
contributors may be used to endorse or promote products derived from | ||
this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
Edx Username Changer | ||
======================= | ||
|
||
A plugin to enable update usernames through admin panel in Open edX (and other apps that log into Open edX via OAuth). | ||
|
||
Version Compatibility | ||
--------------------- | ||
|
||
It only supports koa and latest releases of Open edX. | ||
|
||
Installing The Plugin | ||
--------------------- | ||
|
||
You can install this plugin into any Open edX instance by using any of the following methods: | ||
|
||
**Option 1: Install from PyPI** | ||
|
||
.. code-block:: | ||
# If running devstack in docker, first open a shell in LMS (make lms-shell) | ||
pip install edx-username-changer | ||
**Option 2: Build the package locally and install it** | ||
|
||
Follow these steps in a terminal on your machine: | ||
|
||
1. Navigate to ``open-edx-plugins`` directory | ||
2. Run ``./pants package ::``. This will create a "dist" directory inside "open-edx-plugins" directory with ".whl" & ".tar.gz" format packages for all plugins in the src directory | ||
3. Move/copy any of the ".whl" or ".tar.gz" files for this plugin that were generated in the above step to the machine/container running Open edX (NOTE: If running devstack via Docker, you can use ``docker cp`` to copy these files into your LMS or CMS containers) | ||
4. Run a shell in the machine/container running Open edX, and install this plugin using pip | ||
|
||
|
||
``Note``: In some cases you might need to restart edx-platform after installing the plugin to reflect the changes. | ||
|
||
Configurations | ||
-------------- | ||
To configure this plugin, you need to do the following one step: | ||
|
||
1. Add/Enable a feature flag (ENABLE_EDX_USERNAME_CHANGER) into your environment variables (through ``private.py`` in LMS or CMS, depending upon where you are installing the plugin) | ||
|
||
.. code-block:: | ||
FEATURES["ENABLE_EDX_USERNAME_CHANGER"] = True | ||
How to use | ||
---------- | ||
Its usage is as simple as changing the username of a user account through django's admin panel. Here are the steps (for clarity): | ||
|
||
1. Install edx-username-changer plugin. | ||
2. Use an admin account to access django admin panel. | ||
3. Go to Users model and select an account to change its username. | ||
4. In the account editor page change the username field. | ||
5. Hit Save (present at the bottom-right of page). | ||
|
||
The whole process can be done on lms or studio or on both of them. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# pylint: disable=missing-module-docstring | ||
|
||
default_app_config = "edx_username_changer.apps.EdxUsernameChangerConfig" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
""" | ||
Django admin pages for edx-username-changer plugin | ||
""" | ||
|
||
import contextlib | ||
|
||
from common.djangoapps.student.admin import ( | ||
UserAdmin as BaseUserAdmin, | ||
) | ||
from django.conf import settings | ||
from django.contrib import admin | ||
from django.contrib.admin.sites import NotRegistered | ||
from django.contrib.auth import get_user_model | ||
|
||
User = get_user_model() | ||
|
||
|
||
class UserAdmin(BaseUserAdmin): | ||
""" | ||
Admin interface for the User model. | ||
""" | ||
|
||
def get_readonly_fields(self, request, obj=None): | ||
""" | ||
Remove 'username' from the read-only fields | ||
to make it editable through the admin panel | ||
""" | ||
readonly_fields = super().get_readonly_fields(request, obj) | ||
if settings.FEATURES.get("ENABLE_EDX_USERNAME_CHANGER") and obj: | ||
return tuple([name for name in readonly_fields if name != "username"]) | ||
return readonly_fields | ||
|
||
|
||
# We must first un-register the User model since it was registered by edX's core code. | ||
with contextlib.suppress(NotRegistered): | ||
admin.site.unregister(User) | ||
|
||
admin.site.register(User, UserAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
""" | ||
App configuration for edx-username-changer plugin | ||
""" | ||
|
||
from django.apps import AppConfig | ||
from edx_django_utils.plugins.constants import PluginSignals | ||
from openedx.core.djangoapps.plugins.constants import ( | ||
ProjectType, | ||
) | ||
|
||
|
||
class EdxUsernameChangerConfig(AppConfig): | ||
name = "edx_username_changer" | ||
verbose_name = "Open edX Username Changer" | ||
|
||
plugin_app = { | ||
PluginSignals.CONFIG: { | ||
ProjectType.LMS: { | ||
PluginSignals.RECEIVERS: [ | ||
{ | ||
PluginSignals.RECEIVER_FUNC_NAME: "user_pre_save_callback", | ||
PluginSignals.SIGNAL_PATH: "django.db.models.signals.pre_save", | ||
PluginSignals.SENDER_PATH: "django.contrib.auth.models.User", | ||
}, | ||
{ | ||
PluginSignals.RECEIVER_FUNC_NAME: "user_post_save_callback", | ||
PluginSignals.SIGNAL_PATH: "django.db.models.signals.post_save", | ||
PluginSignals.SENDER_PATH: "django.contrib.auth.models.User", | ||
}, | ||
], | ||
}, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
""" | ||
Exceptions for edx-username-changer plugin | ||
""" | ||
|
||
|
||
class UpdateFailedException(Exception): # noqa: N818 | ||
""" | ||
Exception to throw when there is an update failure in username | ||
""" | ||
|
||
def __init__(self, url, new_username): | ||
self.url = url | ||
self.new_username = new_username | ||
|
||
def __str__(self): | ||
return ( | ||
f"Username update failed for username: {self.new_username}, url: {self.url}" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
Signals and Signal Handlers for edx-username-changer plugin | ||
""" | ||
|
||
from common.djangoapps.util.model_utils import ( # pylint: disable=import-error | ||
get_changed_fields_dict, | ||
) | ||
from django.conf import settings | ||
from django.db import transaction | ||
from edx_username_changer.tasks import task_update_username_in_forum | ||
from edx_username_changer.utils import update_user_social_auth_uid | ||
|
||
|
||
def user_pre_save_callback(sender, **kwargs): | ||
""" | ||
Pre-save signal handler of User model to store changed fields to be used later | ||
""" | ||
if settings.FEATURES.get("ENABLE_EDX_USERNAME_CHANGER"): | ||
user = kwargs["instance"] | ||
fields_to_update = get_changed_fields_dict(user, sender) | ||
if "username" in fields_to_update: | ||
fields_to_update.update({"new_username": user.username}) | ||
user._updated_fields = fields_to_update # noqa: SLF001 | ||
|
||
|
||
def user_post_save_callback(sender, **kwargs): # noqa: ARG001 | ||
""" | ||
Post-save signal handler of User model to update username throughout the application | ||
""" | ||
if settings.FEATURES.get("ENABLE_EDX_USERNAME_CHANGER"): | ||
user = kwargs["instance"] | ||
if ( | ||
hasattr(user, "_updated_fields") | ||
and user._updated_fields # noqa: SLF001 | ||
and {"username", "new_username"}.issubset(user._updated_fields) # noqa: SLF001 | ||
): | ||
new_username = user._updated_fields["new_username"] # noqa: SLF001 | ||
transaction.on_commit( | ||
lambda: task_update_username_in_forum.delay(new_username) | ||
) | ||
update_user_social_auth_uid(user._updated_fields["username"], new_username) # noqa: SLF001 | ||
delattr(user, "_updated_fields") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
This file contains celery tasks related to edx_username_changer plugin. | ||
""" | ||
|
||
from celery import shared_task | ||
from django.contrib.auth import get_user_model | ||
from edx_username_changer.utils import ( | ||
get_authored_threads_and_comments, | ||
get_enrolled_course_ids, | ||
update_comment_user_username, | ||
update_comment_username, | ||
update_thread_username, | ||
) | ||
from openedx.core.djangoapps.django_comment_common.comment_client.user import ( | ||
User as CommentUser, | ||
) | ||
|
||
COMMENT_TYPE = "comment" | ||
THREAD_TYPE = "thread" | ||
User = get_user_model() | ||
|
||
|
||
@shared_task() | ||
def task_update_username_in_forum(username): | ||
""" | ||
Change username in Discussion-Forum service | ||
""" | ||
user = User.objects.get(username=username) | ||
comment_user = CommentUser.from_django_user(user) | ||
update_comment_user_username(comment_user, user.username) | ||
enrolled_course_ids = get_enrolled_course_ids(user) | ||
authored_items = get_authored_threads_and_comments( | ||
comment_user, enrolled_course_ids | ||
) | ||
|
||
for authored_item in authored_items: | ||
item_id = authored_item["id"] | ||
item_type = str(authored_item.get("type")) | ||
if item_type == THREAD_TYPE: | ||
update_thread_username(item_id, user.username) | ||
elif item_type == COMMENT_TYPE: | ||
update_comment_username(item_id, user.username) |
Oops, something went wrong.