#4128 Add support for giving groups to someone
Merged 5 years ago by pingou. Opened 5 years ago by pingou.

file modified
+4 -2
@@ -1824,7 +1824,8 @@ 

      else:

          _log.info(

              "PR is neither from a remote git repo or an existing local "

-             "repo, bailing")

+             "repo, bailing"

+         )

          return

  

      if not request.project or not os.path.exists(
@@ -1832,7 +1833,8 @@ 

      ):

          _log.info(

              "Could not find the targeted git repository for %s",

-             request.project.fullname)

+             request.project.fullname,

+         )

          raise pagure.exceptions.PagureException(

              "Could not find the targeted git repository for %s"

              % request.project.fullname

file modified
+2 -1
@@ -1046,7 +1046,8 @@ 

      else:

          _log.info(

              "LINK_PR_TO_TICKET: PR neither remote, nor with a "

-             "project_from, bailing: %s" % pr_uid)

+             "project_from, bailing: %s" % pr_uid

+         )

          return

  

      repo_obj = pygit2.Repository(repopath)

@@ -1,13 +1,20 @@ 

  {% extends "master.html" %}

  {% from "_formhelper.html" import render_bootstrap_field %}

  

+ {% block header %}

+ <link href="{{ url_for('static', filename='vendor/selectize/selectize.bootstrap3.css') }}?version={{ g.version}}"

+   rel="stylesheet" />

+ {% endblock %}

+ 

  {% set tag = "groups" %}

  {% block title %}Edit group: {{ group.group_name }}{% endblock %}

  

  {% block content %}

- <div class="container">

+ 

+ <div class="container p-t-3">

    <div class="row">

-     <div class="col-md-8 col-md-offset-2">

+     <div class="col-md-8 mx-auto mt-5">

+ 

        <div class="card m-t-3">

          <div class="card-header">

            <strong>Edit group: {{ group.group_name }}</strong>
@@ -24,7 +31,10 @@ 

                      field_description="Short description of the group") }}

                </table>

              <p class="buttons indent">

-               <input type="button" value="Cancel" class="btn btn-secondary" onclick="history.back();">

+               <a href="{{ url_for(

+                   'ui_ns.view_group', group=group.group_name) }}">

+                 <input type="button" value="Cancel" class="btn btn-secondary">

+               </a>

                <input type="submit" class="btn btn-primary" value="Edit">

                {{ form.csrf_token }}

              </p>
@@ -32,7 +42,61 @@ 

          </div>

        </div>

      </div>

+ 

+     {% if group.creator.user == g.fas_user.username or g.admin %}

+     <div class="col-md-8 mx-auto mt-5">

+       <div class="col-md-offset-2">

+         <div class="card-header">

+           <strong>Give group away: {{ group.group_name }}</strong>

+         </div>

+         <div class="card-block">

+           <form action="{{ url_for(

+               'ui_ns.give_group', group=group.group_name) }}"

+                 method="post" class="icon">

+             {{ form.csrf_token }}

+             <input class="form-control" name="username" id="user"

+               placeholder="Start typing to search users"

+               value="" style="margin-bottom:1%"/>

+             <button class="btn btn-danger" type="submit"

+               onclick="return confirm('Are you sure to give {{ group.group_name }}? \nThis is final and cannot be un-done.');"

+               title="Give the group to someone">

+                 <i class="fa fa-share-square-o"></i>&nbsp; Give the {{ group.group_name }} group

+             </button>

+           </form>

+         </div>

+       </div>

+     </div>

+     {% endif %}

+ 

    </div>

  </div>

  

  {% endblock %}

+ 

+ 

+ {% block jscripts %}

+ {{ super() }}

+ 

+ <script type="text/javascript" src="{{ url_for('static', filename='vendor/selectize/selectize.min.js') }}?version={{ g.version}}"></script>

+ 

+ <script type="text/javascript">

+ $('#user').selectize({

+   valueField: 'user',

+   labelField: 'user',

+   searchField: 'user',

+   maxItems: 1,

+   create: false,

+   load: function(query, callback) {

+     if (!query.length) return callback();

+     $.getJSON(

+       "{{ url_for('api_ns.api_users') }}", {

+         pattern: query.term

+       },

+       function( data ) {

+         callback( data.users.map(function(x) { return { user: x }; }) );

+       }

+     );

+   }

+ });

+ </script>

+ {% endblock %}

@@ -21,8 +21,8 @@ 

            <h3 class="mb-0 font-weight-bold">{{group.display_name }}</h3>

            {% if group.description %}<div>{{ group.description }}</div>{% endif %}

            <div><small>

-               formed {{ group.created |humanize }}

-               by <a href="{{ url_for('ui_ns.view_user', username=group.creator.user)}}">{{ group.creator.user }}</a></small>

+               formed {{ group.created |humanize }},

+               administered by <a href="{{ url_for('ui_ns.view_user', username=group.creator.user)}}">{{ group.creator.user }}</a></small>

            </div>

          </div>

          {% if g.authenticated and (member or g.admin)

file modified
+74
@@ -180,6 +180,80 @@ 

      return flask.render_template("edit_group.html", group=group, form=form)

  

  

+ @UI_NS.route("/group/<group>/give", methods=["POST"])

+ @login_required

+ def give_group(group):

+     """ Allows giving away a group. """

+     if not pagure_config.get("ENABLE_USER_MNGT", True):

+         flask.abort(404)

+ 

+     group_type = "user"

+     is_admin = pagure.utils.is_admin()

+     if is_admin:

+         group_type = None

+     group = pagure.lib.query.search_groups(

+         flask.g.session, group_name=group, group_type=group_type

+     )

+ 

+     if not group:

+         flask.abort(404, "Group not found")

+ 

+     if group.creator.user != flask.g.fas_user.username and not flask.g.admin:

+         flask.abort(403, "You are not allowed to give away this group")

+ 

+     # Give away group

+     form = pagure.forms.ConfirmationForm()

+     if form.validate_on_submit():

+ 

+         username = flask.request.form.get("username")

+         if not username:

+             flask.flash(

+                 "No user %s found to give this group to" % username, "error"

+             )

+             return flask.redirect(

+                 flask.url_for("ui_ns.view_group", group=group.group_name)

+             )

+ 

+         user = pagure.lib.query.search_user(flask.g.session, username=username)

+         if not user:

+             flask.flash(

+                 "No user %s found to give this group to" % username, "error"

+             )

+             return flask.redirect(

+                 flask.url_for("ui_ns.view_group", group=group.group_name)

+             )

+ 

+         try:

+             if user not in group.users:

+                 pagure.lib.query.add_user_to_group(

+                     session=flask.g.session,

+                     username=username,

+                     group=group,

+                     user=flask.g.fas_user.username,

+                     is_admin=flask.g.admin,

+                     from_external=False,

+                 )

+             group.user_id = user.id

+             flask.g.session.add(group)

+             flask.g.session.commit()

+             flask.flash("Group given")

+             return flask.redirect(

+                 flask.url_for("ui_ns.view_group", group=group.group_name)

+             )

+         except SQLAlchemyError:  # pragma: no cover

+             flask.g.session.rollback()

+             flask.flash(

+                 "Could not give away group `%s`." % (group.group_name), "error"

+             )

+             _log.exception(

+                 "Could not give away group `%s`." % (group.group_name)

+             )

+ 

+     return flask.redirect(

+         flask.url_for("ui_ns.view_group", group=group.group_name)

+     )

+ 

+ 

  @UI_NS.route("/group/<group>/<user>/delete", methods=["POST"])

  @login_required

  def group_user_delete(user, group):

@@ -223,6 +223,76 @@ 

                  'Group &#34;Test Group edited&#34; (test_group) edited',

                  output.get_data(as_text=True))

  

+     def test_give_group(self):

+         """ Test the give_group endpoint. """

+ 

+         output = self.app.post('/group/test_group/give')

+         self.assertEqual(output.status_code, 302)

+ 

+         user = tests.FakeUser()

+         with tests.user_set(self.app.application, user):

+             output = self.app.post('/group/test_group/give')

+             self.assertEqual(output.status_code, 404)

+             self.assertIn('<p>Group not found</p>', output.get_data(as_text=True))

+ 

+         self.test_add_group()

+ 

+         user.username = 'foo'

+         with tests.user_set(self.app.application, user):

+             output = self.app.post('/group/foo/give')

+             self.assertEqual(output.status_code, 404)

+             self.assertIn('<p>Group not found</p>', output.get_data(as_text=True))

+ 

+             output = self.app.post('/group/test_group/give')

+             self.assertEqual(output.status_code, 403)

+ 

+             csrf_token = self.get_csrf()

+ 

+         user.username = 'pingou'

+         with tests.user_set(self.app.application, user):

+             # Missing CSRF

+             data = {

+                 'username': 'invalid',

+             }

+ 

+             output = self.app.post(

+                 '/group/test_group/give', data=data, follow_redirects=True)

+             self.assertEqual(output.status_code, 200)

+             output_text = output.get_data(as_text=True)

+             self.assertIn(

+                 '<title>Group test_group - Pagure</title>', output_text)

+             self.assertIn(

+                 'administered by <a href="/user/pingou">pingou</a>',

+                 output_text)

+ 

+             # User not found

+             data['csrf_token'] = csrf_token

+             output = self.app.post(

+                 '/group/test_group/give', data=data, follow_redirects=True)

+             self.assertEqual(output.status_code, 200)

+             output_text = output.get_data(as_text=True)

+             self.assertIn(

+                 '<title>Group test_group - Pagure</title>', output_text)

+             self.assertIn(

+                 '</i> No user invalid found to give this group to</div>',

+                 output_text)

+             self.assertIn(

+                 'administered by <a href="/user/pingou">pingou</a>',

+                 output_text)

+ 

+             # Working

+             data["username"] = "foo"

+ 

+             output = self.app.post(

+                 '/group/test_group/give', data=data, follow_redirects=True)

+             self.assertEqual(output.status_code, 200)

+             output_text = output.get_data(as_text=True)

+             self.assertIn(

+                 '<title>Group test_group - Pagure</title>', output_text)

+             self.assertIn('</i> Group given</div>', output_text)

+             self.assertIn(

+                 'administered by <a href="/user/foo">foo</a>', output_text)

+ 

      def test_group_delete(self):

          """ Test the group_delete endpoint. """

          output = self.app.post('/group/foo/delete')