Repository: fossasia/open-event-server

Issue:
Transfer the ownership of your events to another organizer.
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
We are implementing feature to provide ability to a user to delete his/her account. If the user is associated with any event, he/she must be given choice to transfer the events to other organizers associated with the event. (Just like in GitHub)

**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
Something similar to `Github's Danger Zone` 
![danger](https://user-images.githubusercontent.com/25428397/57812116-ba387d80-7789-11e9-9efd-211751859832.png)

**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->
Frontend issue: https://github.com/fossasia/open-event-frontend/issues/3012



Discussion:
@shreyanshdwivedi I have did some work regarding change in organisers. See if this is of any help.
https://github.com/uds5501/open-event-server/tree/change-organiser

If you say, I can help.in this issue

This should be transferred to the server
@uds5501 as you've worked on server for this issue, you can handle server side if you want :)
I'll work on the front-end part to make it similar to GitHub danger zone.
@shreyanshdwivedi Okay, I will finalise the endpoint over here, and help you with the frontend part.
This also means we should have the main organizer role to be renamed to "event owner". So, the one who creates an event is the "owner".

Resolving patch:
diff --git a/app/api/helpers/role_invite.py b/app/api/helpers/role_invite.py
new file mode 100644
--- /dev/null
+++ b/app/api/helpers/role_invite.py
@@ -0,0 +1,25 @@
+import logging
+from app.models import db
+from app.models.role_invite import RoleInvite
+
+logger = logging.getLogger(__name__)
+
+
+def delete_previous_uer(uer):
+    """
+    delete previous owner before adding a new one
+    :param uer: User Event Role to be deleted.
+    :return:
+    """
+    role_invite = db.session.query(RoleInvite).filter_by(
+        email=uer.user.email, event_id=uer.event_id, role_name='owner', status='accepted'
+        ).first()
+
+    if role_invite:
+        db.session.delete(role_invite)
+    db.session.delete(uer)
+    try:
+        db.session.commit()
+    except Exception:
+        logger.exception('UER or Role Invite delete exception!')
+        db.session.rollback()
diff --git a/app/api/role_invites.py b/app/api/role_invites.py
--- a/app/api/role_invites.py
+++ b/app/api/role_invites.py
@@ -11,6 +11,7 @@
 from app.api.helpers.notification import send_notif_event_role
 from app.api.helpers.permission_manager import has_access
 from app.api.helpers.query import event_query
+from app.api.helpers.role_invite import delete_previous_uer
 from app.api.helpers.utilities import require_relationship
 from app.api.schema.role_invites import RoleInviteSchema
 from app.models import db
@@ -41,6 +42,16 @@ def before_post(self, args, kwargs, data):
         if not has_access('is_organizer', event_id=data['event']):
             raise ForbiddenException({'source': ''}, 'Organizer access is required.')
 
+    def before_create_object(self, data, view_kwargs):
+        """
+        before create object method for RoleInviteListPost Class
+        :param data:
+        :param view_kwargs:
+        :return:
+        """
+        if data['role_name'] == 'owner' and not has_access('is_owner', event_id=data['event']):
+            raise ForbiddenException({'source': ''}, 'Owner access is required.')
+
     def after_create_object(self, role_invite, data, view_kwargs):
         """
         after create object method for role invite links
@@ -67,6 +78,7 @@ def after_create_object(self, role_invite, data, view_kwargs):
     data_layer = {'session': db.session,
                   'model': RoleInvite,
                   'methods': {
+                      'before_create_object': before_create_object,
                       'after_create_object': after_create_object
                   }}
 
@@ -116,6 +128,8 @@ def before_update_object(self, role_invite, data, view_kwargs):
                                                                                                 user_id=user.id):
                 raise UnprocessableEntity({'source': ''},
                                           "Status can be updated only by event organizer or user hiself")
+        if 'role_name' in data and data['role_name'] == 'owner' and not has_access('is_owner', event_id=data['event']):
+            raise ForbiddenException({'source': ''}, 'Owner access is required.')
         if not user and not has_access('is_organizer', event_id=role_invite.event_id):
             raise UnprocessableEntity({'source': ''}, "User not registered")
         if not has_access('is_organizer', event_id=role_invite.event_id) and (len(list(data.keys())) > 1 or
@@ -159,7 +173,12 @@ def accept_invite():
         event = Event.query.filter_by(id=role_invite.event_id).first()
         uer = UsersEventsRoles.query.filter_by(user=user).filter_by(
             event=event).filter_by(role=role).first()
+
         if not uer:
+            if role_invite.role_name == 'owner':
+                past_owner = UsersEventsRoles.query.filter_by(event=event, role=role).first()
+                if past_owner:
+                    delete_previous_uer(past_owner)
             role_invite.status = "accepted"
             save_to_db(role_invite, 'Role Invite Accepted')
             uer = UsersEventsRoles(user, event, role)


Test patch: