Skip to content

Course visibility options - #18537

Closed
viadanna wants to merge 3 commits into
openedx:masterfrom
open-craft:paulo/course-visibility-options
Closed

Course visibility options#18537
viadanna wants to merge 3 commits into
openedx:masterfrom
open-craft:paulo/course-visibility-options

Conversation

@viadanna

@viadanna viadanna commented Jul 9, 2018

Copy link
Copy Markdown
Contributor

This PR adds the course setting course visibility in CMS to allow preview and public access.

This requires the course waffle flag enable_anonymous_access.

Feature description based on this PR

Settings / Options
Thinking through potential combinations of settings and override concerns for authors I might suggest a minor change to what you suggested above.

Course Visibility Default Setting: Public, Preview Only, Private
Public: Course outline and all units are available by default publicly, unless a subsection or unit is forced to be private.
Preview Only: Only the course outline is available publicly but content pages are locked / gated with a message explaining that users must log in to access the content.
Private: Our current default, but anybody who attempts to access specific content pages is redirected to the course home page (current behavior.)

Sandbox URL:
LMS
CMS

The staff user for these have superuser rights

Testing instructions:

  1. Either logout or open a private browsing window and navigate to the demo course.
  2. Make sure the outline isn't displayed and accessing a unit redirects to login.
  3. Enable seo.enable_anonymous_acess waffle flag to the course and make sure the Course Visibility For Unauthenticated Students setting appear for the course in Studio.
  4. Make sure the outline isn't displayed and accessing a unit redirects to login.
  5. Change the Course Visibility For Unauthenticated Students advanced setting for the course to preview.
  6. Make sure the outline is displayed without links or handouts.
  7. Change the Course Visibility For Unauthenticated Students advanced setting for the course to public.
  8. Refresh the course outline in the private browsing session and make sure the course outline is displayed with links.
  9. Navigate to a unit and check it displays, even with broken blocks.
  10. Disable the course waffle flag and make sure the course visibility setting doesn't shown up in advanced course settings.

Author notes and concerns:

  1. Public access must be supported on XBlocks as well, so most will be broken for now.

Reviewers

@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @viadanna! I've created OSPR-2510 to keep track of it in JIRA. JIRA is a place for product owners to prioritize feature reviews by the engineering development teams.

Feel free to add as much of the following information to the ticket:

  • supporting documentation
  • edx-code email threads
  • timeline information ("this must be merged by XX date", and why that is)
  • partner information ("this is a course on edx.org")
  • any other information that can help Product understand the context for the PR

All technical communication about the code itself will still be done via the GitHub pull request interface. As a reminder, our process documentation is here.

If you like, you can add yourself to the AUTHORS file for this repo, though that isn't required. Please see the CONTRIBUTING file for more information.

@openedx-webhooks openedx-webhooks added needs triage open-source-contribution PR author is not from Axim or 2U labels Jul 9, 2018
@mduboseedx

Copy link
Copy Markdown
Contributor

@viadanna Thanks for the contribution! Let me know when this is ready for a review.

@openedx-webhooks openedx-webhooks added waiting on author PR author needs to resolve review requests, answer questions, fix tests, etc. and removed needs triage labels Jul 9, 2018

@symbolist symbolist left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@viadanna My understanding is that this task adds a setting field to the course and for now that is being kept behind the existing waffle flag. So something like this:

  1. A setting needs to be added to https://github.com/edx/edx-platform/blob/master/common/lib/xmodule/xmodule/course_module.py#L182 whose values can be private (default), 'preview' or public.
  2. This setting should only be visible in Studio Advanced Settings if the WaffleFlag enable_anonymous_courseware_access is enabled for the course.
  3. This setting should only be used by the CourseHomeView if the WaffleFlagenable_anonymous_courseware_access is enabled for the course. If the flag is enabled for the course, then you can do checks like if course.access is 'preview': and make the relevant changes to the CourseHome rendering.

@xitij2000 Can you please confirm the acceptance criteria for this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lets not make changes to things besides the outline. If the user is anonymous, CourseHomeFragmentView should not render the WelcomeMessageFragmentView.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

@symbolist symbolist left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@viadanna Also can you remove all the changes added the SequenceModule? There is already a separate PR for those.

@xitij2000

Copy link
Copy Markdown
Contributor

@symbolist @viadanna Yes, that is the correct acceptance criterion. This needs to be a course setting that itself is only activated/visible/effective if the waffle flag is set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The multiple checks here are making the code complex quite complex. Try this instead:

  1. Move all the assignments to None in the else: clause above the line if user_access['is_enrolled'] or user_access['is_staff']:.
  2. Then instead of modifying the if, add another clause:
else if (course.access is "preview" or course.access is "public"):
  outline_fragment = <init>
  course_sock_fragment = <init>

This will make it clear what is being set up in each of the three cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Showing the handouts for anonymous users is also out of scope of this task as far as I understand. In that case, you can move this to the first if clause above instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the requirement is that if the course access is set to public the outline should show and the links should be enabled. And if the course access is set to preview, the outline should show but the links are not enabled. This code is disabling the links in both cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hum, that's strange, it's called with the value status of allow_preview and renders correctly in the sandbox.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looking at this again, you are right. :) It will likely be clearer if the parameter was made enable_anonymous_access (which would be passed the value of the WaffleFlag) and then in the context the exact criteria was listed:

'enable_links': request.user.is_authenticated or (enable_anonymous_access and request.user.is_anonymous() and course.access is 'public')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not setting the href may make more sense than changing the element type (though not sure what the a11y requirements are). If you want to go with the span, the styling needs to be updated. Currently, the unit items are all showing in a single line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's odd, I've updated the scss and this was working here. I'll review that approach.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm. Okay, it could just be that my assets were not updated correctly. I'll check it again in the next pass.

@viadanna

Copy link
Copy Markdown
Contributor Author

Thanks for clearing up the acceptance criteria @symbolist @xitij2000
I'll make the appropriate changes and rollback the SequenceModule ones.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You should be able to access the course_key via course_module.id or descriptor.id (if it is a course descriptor) and so shouldn't need to pass it as a separate parameter if either is also a parameter. For a bit of context before XBlocks the equivalent functionality was provided by a Descriptor and Module pair. Course hasn't been migrated over yet which is why the disparate APIs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Much better, alright.

Comment thread lms/djangoapps/course_api/blocks/api.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Can we go with user.is_authenticated? It seems clearer than a not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This context manager needs to be moved as well. It is needed when accessing the modulestore which is being done in get_course_with_access().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

On second thoughts, I would recommend leaving the CoursewareIndex without any changes for now. The next task will be making further changes to it and it makes sense to do all in one go.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You mean no further changes? Reverting this back will break the functionality.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Well, the only effect of the changes in this file is restricting courseware access to the case where course_visibility is set to public. If you want to keep this change as part of this PR, here are a few points:

  1. The get_course_with_access() needs to be inside a try statement as it was originally.
  2. I am not sure if there is any performance impact of splitting the work across two bulk_operations context managers.

How about instead of moving get_course_with_access() just move the access check and redirect to before self.is_staff = has_access(request.user, 'staff', self.course) below? That should get the expected result but the course content loading/rendering logic remains exactly the same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, that's a good solution.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Can we go with request.user.is_authenticated? It seems clearer than a not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You forgot to update the display_name values.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh, what a distraction, will fix that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is already a test for the flag: https://github.com/open-craft/edx-platform/blob/aa7599b49f1af7566f5a858c784f6b9687d439cd/lms/djangoapps/courseware/tests/test_views.py#L2332 . So instead of creating a new class, you can just add these ddt variations to it.

Also instead of patching the descriptor, you should be able to do course = CourseFactory(course_visibility= course_visibility).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Otherwise the test looks good.

@viadanna viadanna Jul 14, 2018

Copy link
Copy Markdown
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 new class to perform a different setUp. It make sense to have all in a single place, but the code was getting very complex.

On the patching, do you mean creating a new course on each variation of the ddt?

Nevermind, I got it.

@symbolist symbolist Jul 14, 2018

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you explain where all the extra setup that is being done in this setUpClass() is being used? For the purposes of this test, what more do you need than what is already happening in https://github.com/edx/edx-platform/blob/master/lms/djangoapps/courseware/tests/test_views.py#L2333-L2336?

Since the existing test is creating the course in the test, I meant when merging you can just add the parameter to the CourseFactory() call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, you recommendation made sense on a second look at the code, that's unnecessary.

I have updated the code, I'll just make the necessary changes to the other test and push the changes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are now 5-6 places in lms and openedx where this flag is being defined. Can you create one constant in https://github.com/edx/edx-platform/blob/master/openedx/features/course_experience/__init__.py and use it everywhere? (Make sure to follow the naming convention of the existing flags).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this crossed my mind as well, but fell through the cracks.

I'l do that now.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this message not going to be visible in all cases?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually, this was meant for the You must be enrolled in the course to see course content, I just shortened it for readability.

But explicit is better, so I'll adjust that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this not be merged with test_home_page() so that status of all sub-fragments can be tested? For example, the 3 ddt variations with logged in users can have the flag False and these 6 variations can have CourseUserType.ANONYMOUS.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also instead of patching see the update_item(self.course, self.user.id) pattern in courseware/tests/test_views.py for updating the course module fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tried merging at first but the test was getting very complex, so I decided to keep those separate.

I moved to patching as updating the course module was causing troubles on other tests, even when rolledback the changes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since the existing test checks the visibility of the different fragments, it makes sense to have one which checks all the permutations. The new test is not checking the visibility of the other fragments. The main changes I see are:

  1. test_home_page(self, enable_anonymous_courseware_access, course_visibility, user_type, expected_message)
  2. The patch and override_waffle_flag need to go around the self.client.get() as in the new test.
  3. Updating one assert:
self.assertContains(response, TEST_CHAPTER_NAME, count=1 if (is_enrolled or is_unenrolled_staff or (enable_anonymous_courseware_access and course_visiblity in ['preview', 'public'])) else 0)

What else needs to change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, I also had to change the expected number of welcome messages.

@viadanna
viadanna force-pushed the paulo/course-visibility-options branch from 6a4e571 to 9df137c Compare July 14, 2018 16:19
@symbolist

Copy link
Copy Markdown
Contributor

jenkins run bokchoy

@viadanna
viadanna force-pushed the paulo/course-visibility-options branch 2 times, most recently from 4dfda0c to e629775 Compare July 16, 2018 11:05
@viadanna viadanna changed the title WIP: Course visibility options Course visibility options Jul 16, 2018
@viadanna

Copy link
Copy Markdown
Contributor Author

jenkins run quality

Comment thread lms/djangoapps/course_api/blocks/api.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thinking about this some more, this method should not care about authentication. If user.is_authenticated is False, user=None should be passed to it so that this extra check is not needed here. I think the only change that will be required is in edx-platform/openedx/features/course_experience.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can get the value via kwargs['enable_anonymous_access'] instead of adding a parameter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you move this where it was so that it does not look like a change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually, moving this changed the behaviour of this function redirects when the user wasn't logged in and anonymous access was disabled.

I don't like this repetition either.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I meant to line 99 above. That should not have any effect right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh, sorry, I misunderstood your request.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We will likely need a different message for this case but it is something the edX UX can recommend.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you also add checks that CourseUserType.UNENROLLED see the same content as anonymous learners?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

While the checks are all correct, they can be made clearer to read by moving self.assertContains(response, TEST_CHAPTER_NAME, count=expected_chapter_count) and all the variables that are only needed for it to line 261 (and leave an empty line). You can add a comment like "# Verify the outline is shown for enrolled users and if needed for anonymous users."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also is your editor line width set to 80? edX uses 120 and I think there are a lot of multi-lines in this PR which would not be.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that makes it more readable.

On the multilines, I've must have set that manually for readability, I'll review it and update accordingly.

@viadanna

Copy link
Copy Markdown
Contributor Author

Hello there, @mduboseedx
The PR is ready for a review.

@mduboseedx

Copy link
Copy Markdown
Contributor

@edx/educator-product Could you give this a quick look? This is related to course visibility and uses a waffle flag.

@openedx-webhooks openedx-webhooks added product review PR requires product review before merging and removed waiting on author PR author needs to resolve review requests, answer questions, fix tests, etc. labels Jul 18, 2018
@viadanna
viadanna force-pushed the paulo/course-visibility-options branch from 35cb113 to c2c2dc5 Compare August 2, 2018 13:23
@mduboseedx

Copy link
Copy Markdown
Contributor

@sstack22 Have you had a chance to give this a look over from a product perspective?

@viadanna

Copy link
Copy Markdown
Contributor Author

@mduboseedx @sstack22 We've found an issue in our implementation regarding the block list transformers, so you can delay this review until we fix this. Sorry about that.

@viadanna

Copy link
Copy Markdown
Contributor Author

jenkins run bokchoy

@viadanna
viadanna force-pushed the paulo/course-visibility-options branch from a6f367d to b2e28fb Compare August 23, 2018 14:12

@symbolist symbolist left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@viadanna Given the updates to https://github.com/edx/edx-platform/pull/18134, we need to make sure that the logged-in but unenrolled learners see the same content as anonymous learners. I have left some notes about those. Also thanks for the updates to the transformers checks! Can you also add a test for the MilestonesAndSpecialExamsTransformer when usage_info.user.is_authenticated is False?

assert response.status_code == expected_status

user = UserFactory()
CourseEnrollmentFactory(user=user, course_id=course.id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since now we are using this behavior for unenrolled users as well, can you also check before enrolling the user that they see what anonymous users see?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

depth=CONTENT_DEPTH,
check_if_enrolled=not self.enable_anonymous_courseware_access,
)
if not (request.user.is_authenticated or self.course.course_visibility == 'public'):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does the check needs to be updated here too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍


# Waffle flag to enable anonymous access to a course
SEO_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='seo')
COURSE_ENABLE_ANONYMOUS_ACCESS_FLAG = CourseWaffleFlag(SEO_WAFFLE_FLAG_NAMESPACE, 'enable_anonymous_courseware_access')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this be renamed to COURSE_ENABLE_UNENROLLED_ACCESS_FLAG ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you also add checks that CourseUserType.UNENROLLED see the same content as anonymous learners?

user=request.user,
course_key=course_key
)
if request.user.is_authenticated:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This would need an update too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

if not course_block_tree:
return None

enable_links = request.user.is_authenticated or (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This method may also need a couple of updates for unenrolled but logged-in learners.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

@viadanna
viadanna force-pushed the paulo/course-visibility-options branch from 42fe970 to 9e77345 Compare September 21, 2018 16:38
@edx-status-bot

Copy link
Copy Markdown

Your PR has finished running tests. The following contexts failed:

  • jenkins/python
  • jenkins/quality
  • jenkins/bokchoy

@viadanna

viadanna commented Oct 9, 2018

Copy link
Copy Markdown
Contributor Author

Closed in favor of #18720

@viadanna viadanna closed this Oct 9, 2018
@openedx-webhooks

Copy link
Copy Markdown

@viadanna Even though your pull request wasn’t merged, please take a moment to answer a two question survey so we can improve your experience in the future.

@openedx-webhooks openedx-webhooks added rejected and removed product review PR requires product review before merging labels Jan 22, 2021
@bradenmacdonald
bradenmacdonald deleted the paulo/course-visibility-options branch January 12, 2022 01:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

open-source-contribution PR author is not from Axim or 2U rejected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants