Rafał Pitoń 10 лет назад
Родитель
Сommit
19e82582dc

+ 2 - 1
misago/static/misago/css/misago/userslists.less

@@ -120,7 +120,7 @@
     padding: @line-height-computed / 2 0px;
 
     &:last-child {
-    	border-bottom: none;
+      border-bottom: none;
     }
 
     img {
@@ -133,6 +133,7 @@
     }
 
     .btn {
+      margin-left: @line-height-computed / 2;
       margin-top: -3px;
     }
   }

+ 15 - 7
misago/static/misago/js/misago-thread-participants.js

@@ -32,7 +32,7 @@ MisagoThreadParticipants = function() {
 
       // suppress default submission handling
 
-      this.$container.find('form').submit(function(e) {
+      this.$container.find('.add-participants').submit(function(e) {
 
         e.preventDefault();
         return false;
@@ -49,18 +49,19 @@ MisagoThreadParticipants = function() {
 
       })
 
-      this.$container.find('.btn-remove-participant').click(function() {
-
-        var $participant = $(this).parents('li.participant');
-        _this.remove($participant, $(this).data('remove-url'));
-
-      })
+      this.activate_list_buttons();
 
     }
 
     this.update_list = function(new_html) {
 
       this.$users.html(new_html);
+      this.activate_list_buttons();
+
+    }
+
+    this.activate_list_buttons = function() {
+
       this.$container.find('.btn-remove-participant').click(function() {
 
         var $participant = $(this).parents('li.participant');
@@ -68,6 +69,13 @@ MisagoThreadParticipants = function() {
 
       })
 
+      this.$container.find('.make-owner').submit(function() {
+
+        var decision = confirm(lang_give_ownership);
+        return decision;
+
+      });
+
     }
 
     this.update_form_visibility = function() {

+ 1 - 1
misago/templates/misago/privatethreads/participants_modal.html

@@ -22,7 +22,7 @@
 
   <p class="text-danger fade"></p>
 
-  <form method="POST">
+  <form method="POST" class="add-participants">
     {% csrf_token %}
     <div class="invite-form">
 

+ 7 - 0
misago/templates/misago/privatethreads/participants_modal_list.html

@@ -7,6 +7,13 @@
   <button type="button" class="btn btn-default btn-sm pull-right btn-remove-participant" data-remove-url="{% url 'misago:private_thread_remove_participant' thread_slug=thread.slug thread_id=thread.id user_id=participant.user.id %}">
     {% trans "Remove" %}
   </button>
+  <form method="POST" class="pull-right make-owner">
+    {% csrf_token %}
+    <input type="hidden" name="thread_action" value="make_owner:{{ participant.user_id }}">
+    <button type="submit" name="" class="btn btn-default btn-sm">
+      {% trans "Make owner" %}
+    </button>
+  </form>
   {% endif %}
 </li>
 {% endfor %}

+ 3 - 0
misago/templates/misago/privatethreads/thread.html

@@ -46,6 +46,8 @@
 {% block javascripts %}
 {{ block.super }}
 <script lang="JavaScript">
+  var lang_give_ownership = "{% trans "Are you sure you want to pass thread ownership to this user?" %}";
+
   $(function() {
     var participants_cache = {};
 
@@ -85,6 +87,7 @@
     {% endif %}
 
     {% if 'participants' in thread_actions %}
+    var lang_give_ownership = "{% trans "Are you sure you want to give thread ownership to this user?" %}";
     $('.action-participants').click(function() {
       Misago.ParticipantsEditor.open({
         api_url: '{% url "misago:private_thread_edit_participants" thread_id=thread.id thread_slug=thread.slug %}'

+ 22 - 0
misago/threads/tests/test_privatethread_view.py

@@ -1,3 +1,4 @@
+from django.contrib.auth import get_user_model
 from django.utils import timezone
 
 from misago.acl.testutils import override_acl
@@ -95,3 +96,24 @@ class PrivateThreadTests(AuthenticatedUserTestCase):
 
         user = self.thread.threadparticipant_set.get(user=self.user)
         self.assertTrue(user.is_owner)
+
+    def test_owner_can_pass_thread_to_participant(self):
+        """thread owner can pass thread to other participant"""
+        User = get_user_model()
+        new_owner = User.objects.create_user("Bob", "bob@bob.com", "pass123")
+
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+        ThreadParticipant.objects.add_participant(self.thread, new_owner)
+
+        override_acl(self.user, {'can_use_private_threads': True})
+
+        response = self.client.post(self.thread.get_absolute_url(), data={
+            'thread_action': 'make_owner:%s' % new_owner.id
+        })
+        self.assertEqual(response.status_code, 302)
+
+        user = self.thread.threadparticipant_set.get(user=self.user)
+        self.assertFalse(user.is_owner)
+
+        user = self.thread.threadparticipant_set.get(user=new_owner)
+        self.assertTrue(user.is_owner)

+ 5 - 1
misago/threads/views/generic/actions.py

@@ -125,7 +125,11 @@ class ActionsBase(object):
             return redirect(request.path)
 
     def get_list(self):
-        return self.available_actions
+        visible_actions = []
+        for action in self.available_actions:
+            if not action.get('is_hidden'):
+                visible_actions.append(action)
+        return visible_actions
 
     def get_selected_ids(self):
         return self.selected_ids

+ 38 - 1
misago/threads/views/privatethreads.py

@@ -133,7 +133,7 @@ class PrivateThreadActions(generic.ThreadActions):
             actions.append({
                 'action': 'takeover',
                 'icon': 'level-up',
-                'name': _("Takeover thread")
+                'name': _("Take thread over")
             })
 
         if is_owner:
@@ -144,6 +144,13 @@ class PrivateThreadActions(generic.ThreadActions):
                 'is_button': True
             })
 
+            for participant in thread.participants_list:
+                if not participant.is_owner:
+                    actions.append({
+                        'action': 'make_owner:%s' % participant.user_id,
+                        'is_hidden': True
+                    })
+
         if is_moderator:
             if thread.is_closed:
                 actions.append({
@@ -168,10 +175,40 @@ class PrivateThreadActions(generic.ThreadActions):
 
         return actions
 
+    @atomic
     def action_takeover(self, request, thread):
         participants.set_thread_owner(thread, request.user)
         messages.success(request, _("You are now owner of this thread."))
 
+        message = _("%(user)s took over this thread.")
+        record_event(request.user, thread, 'user', message, {
+            'user': request.user,
+        })
+        thread.save(update_fields=['has_events'])
+
+    @atomic
+    def action_make_owner(self, request, thread, new_owner_id):
+        new_owner_id = int(new_owner_id)
+
+        new_owner = None
+        for participant in thread.participants_list:
+            if participant.user.id == int(new_owner_id):
+                new_owner = participant.user
+                break
+
+        if new_owner:
+            participants.set_thread_owner(thread, new_owner)
+
+            message = _("You have passed thread ownership to %(user)s.")
+            messages.success(request, message % {'user': new_owner.username})
+
+            message = _("%(user)s passed thread ownership to %(participant)s.")
+            record_event(request.user, thread, 'user', message, {
+                'user': request.user,
+                'participant': new_owner
+            })
+            thread.save(update_fields=['has_events'])
+
 
 @private_threads_view
 class ThreadView(PrivateThreadsMixin, generic.ThreadView):