Beispiel #1
0
    def check_acl(self, session, project, username, refname, pull_request,
                  repotype, is_internal, **info):
        if is_internal:
            self.info("Internal push allowed")
            return True

        # Check whether a PR is required for this repo or in general
        global_pr_only = pagure_config.get("PR_ONLY", False)
        pr_only = project.settings.get("pull_request_access_only", False)
        if repotype == "main":
            if (pr_only or
                (global_pr_only and not project.is_fork)) and not pull_request:
                self.info("Pull request required")
                return False

        if username is None:
            return False

        # Determine whether the current user is allowed to push
        is_committer = is_repo_committer(project, username, session)
        deploykey = lookup_deploykey(project, username)
        if deploykey is not None:
            self.info("Deploykey used. Push access: %s" % deploykey.pushaccess)
            is_committer = deploykey.pushaccess
        self.info("Has commit access: %s" % is_committer)

        return is_committer
Beispiel #2
0
    def check_acl(
        self,
        session,
        project,
        username,
        refname,
        pull_request,
        repotype,
        is_internal,
        **info
    ):
        if is_internal:
            self.info("Internal push allowed")
            return True

        # Check whether a PR is required for this repo or in general
        global_pr_only = pagure_config.get("PR_ONLY", False)
        pr_only = project.settings.get("pull_request_access_only", False)
        if repotype == "main":
            if (
                pr_only or (global_pr_only and not project.is_fork)
            ) and not pull_request:
                self.info("Pull request required")
                return False

        # Determine whether the current user is allowed to push
        is_committer = is_repo_committer(project, username, session)
        deploykey = lookup_deploykey(project, username)
        if deploykey is not None:
            self.info("Deploykey used. Push access: %s" % deploykey.pushaccess)
            is_committer = deploykey.pushaccess
        self.info("Has commit access: %s" % is_committer)

        return is_committer
Beispiel #3
0
def _check_private_pull_request_access(request):
    """Check if user can access PR. Must be repo committer
    or author to see private PR.
    :param request: PullRequest object
    :raises pagure.exceptions.APIError: when access denied
    """
    if (request.private and not is_repo_committer(request.project)
            and (not api_authenticated()
                 or not request.user.user == flask.g.fas_user.username)):
        raise pagure.exceptions.APIError(403,
                                         error_code=APIERROR.EPRNOTALLOWED)
Beispiel #4
0
def _check_private_issue_access(issue):
    """Check if user can access issue. Must be repo committer
    or author to see private issues.
    :param issue: issue object
    :raises pagure.exceptions.APIError: when access denied
    """
    if (issue.private and not is_repo_committer(issue.project)
            and (not api_authenticated()
                 or not issue.user.user == flask.g.fas_user.username)):
        raise pagure.exceptions.APIError(403,
                                         error_code=APIERROR.EISSUENOTALLOWED)
Beispiel #5
0
def _check_private_pull_request_access(request):
    """Check if user can access PR. Must be repo committer
    or author to see private PR.
    :param request: PullRequest object
    :raises pagure.exceptions.APIError: when access denied
    """
    if (
        request.private
        and not is_repo_committer(request.project)
        and (
            not api_authenticated()
            or not request.user.user == flask.g.fas_user.username
        )
    ):
        raise pagure.exceptions.APIError(
            403, error_code=APIERROR.EPRNOTALLOWED
        )
Beispiel #6
0
def _check_private_issue_access(issue):
    """Check if user can access issue. Must be repo committer
    or author to see private issues.
    :param issue: issue object
    :raises pagure.exceptions.APIError: when access denied
    """
    if (
        issue.private
        and not is_repo_committer(issue.project)
        and (
            not api_authenticated()
            or not issue.user.user == flask.g.fas_user.username
        )
    ):
        raise pagure.exceptions.APIError(
            403, error_code=APIERROR.EISSUENOTALLOWED
        )
Beispiel #7
0
def api_pull_request_close(repo, requestid, username=None, namespace=None):
    """
    Close a pull-request
    --------------------
    Instruct Pagure to close a pull request.

    ::

        POST /api/0/<repo>/pull-request/<request id>/close
        POST /api/0/<namespace>/<repo>/pull-request/<request id>/close

    ::

        POST /api/0/fork/<username>/<repo>/pull-request/<request id>/close
        POST /api/0/fork/<username>/<namespace>/<repo>/pull-request/<request id>/close

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Pull-request closed!"
        }

    """  # noqa
    output = {}

    repo = get_authorized_api_project(
        flask.g.session, repo, user=username, namespace=namespace
    )

    if repo is None:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT)

    if not repo.settings.get("pull_requests", True):
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.EPULLREQUESTSDISABLED
        )

    if repo != flask.g.token.project:
        raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK)

    request = pagure.lib.query.search_pull_requests(
        flask.g.session, project_id=repo.id, requestid=requestid
    )

    if not request:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOREQ)

    if not is_repo_committer(repo):
        raise pagure.exceptions.APIError(403, error_code=APIERROR.ENOPRCLOSE)

    try:
        pagure.lib.query.close_pull_request(
            flask.g.session, request, flask.g.fas_user.username, merged=False
        )
        flask.g.session.commit()
        output["message"] = "Pull-request closed!"
    except SQLAlchemyError as err:  # pragma: no cover
        flask.g.session.rollback()
        _log.exception(err)
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    jsonout = flask.jsonify(output)
    return jsonout
Beispiel #8
0
def api_pull_request_merge(repo, requestid, username=None, namespace=None):
    """
    Merge a pull-request
    --------------------
    Instruct Pagure to merge a pull request.

    This is an asynchronous call.

    ::

        POST /api/0/<repo>/pull-request/<request id>/merge
        POST /api/0/<namespace>/<repo>/pull-request/<request id>/merge

    ::

        POST /api/0/fork/<username>/<repo>/pull-request/<request id>/merge
        POST /api/0/fork/<username>/<namespace>/<repo>/pull-request/<request id>/merge

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        wait=False:
        {
          "message": "Merging queued",
          "taskid": "123-abcd"
        }

        wait=True:
        {
          "message": "Changes merged!"
        }

    """  # noqa
    output = {}

    repo = get_authorized_api_project(
        flask.g.session, repo, user=username, namespace=namespace
    )

    if repo is None:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT)

    if not repo.settings.get("pull_requests", True):
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.EPULLREQUESTSDISABLED
        )

    if flask.g.token.project and repo != flask.g.token.project:
        raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK)

    request = pagure.lib.query.search_pull_requests(
        flask.g.session, project_id=repo.id, requestid=requestid
    )

    if not request:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOREQ)

    if not is_repo_committer(repo):
        raise pagure.exceptions.APIError(403, error_code=APIERROR.ENOPRCLOSE)

    if repo.settings.get("Only_assignee_can_merge_pull-request", False):
        if not request.assignee:
            raise pagure.exceptions.APIError(
                403, error_code=APIERROR.ENOTASSIGNED
            )

        if request.assignee.username != flask.g.fas_user.username:
            raise pagure.exceptions.APIError(
                403, error_code=APIERROR.ENOTASSIGNEE
            )

    threshold = repo.settings.get("Minimum_score_to_merge_pull-request", -1)
    if threshold > 0 and int(request.score) < int(threshold):
        raise pagure.exceptions.APIError(403, error_code=APIERROR.EPRSCORE)

    task = pagure.lib.tasks.merge_pull_request.delay(
        repo.name, namespace, username, requestid, flask.g.fas_user.username
    )
    output = {"message": "Merging queued", "taskid": task.id}

    if get_request_data().get("wait", True):
        task.get()
        output = {"message": "Changes merged!"}

    jsonout = flask.jsonify(output)
    return jsonout
Beispiel #9
0
def api_view_issues(repo, username=None, namespace=None):
    """
    List project's issues
    ---------------------
    List issues of a project.

    ::

        GET /api/0/<repo>/issues
        GET /api/0/<namespace>/<repo>/issues

    ::

        GET /api/0/fork/<username>/<repo>/issues
        GET /api/0/fork/<username>/<namespace>/<repo>/issues

    Parameters
    ^^^^^^^^^^

    +---------------+---------+--------------+---------------------------+
    | Key           | Type    | Optionality  | Description               |
    +===============+=========+==============+===========================+
    | ``status``    | string  | Optional     | | Filters the status of   |
    |               |         |              |   issues. Fetches all the |
    |               |         |              |   issues if status is     |
    |               |         |              |   ``all``. Default:       |
    |               |         |              |   ``Open``                |
    +---------------+---------+--------------+---------------------------+
    | ``tags``      | string  | Optional     | | A list of tags you      |
    |               |         |              |   wish to filter. If      |
    |               |         |              |   you want to filter      |
    |               |         |              |   for issues not having   |
    |               |         |              |   a tag, add an           |
    |               |         |              |   exclamation mark in     |
    |               |         |              |   front of it             |
    +---------------+---------+--------------+---------------------------+
    | ``assignee``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by assignee             |
    +---------------+---------+--------------+---------------------------+
    | ``author``    | string  | Optional     | | Filter the issues       |
    |               |         |              |   by creator              |
    +---------------+---------+--------------+---------------------------+
    | ``milestones``| list of | Optional     | | Filter the issues       |
    |               | strings |              |   by milestone            |
    +---------------+---------+--------------+---------------------------+
    | ``priority``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by priority             |
    +---------------+---------+--------------+---------------------------+
    | ``no_stones`` | boolean | Optional     | | If true returns only the|
    |               |         |              |   issues having no        |
    |               |         |              |   milestone, if false     |
    |               |         |              |   returns only the issues |
    |               |         |              |   having a milestone      |
    +---------------+---------+--------------+---------------------------+
    | ``since``     | string  | Optional     | | Filter the issues       |
    |               |         |              |   updated after this date.|
    |               |         |              |   The date can either be  |
    |               |         |              |   provided as an unix date|
    |               |         |              |   or in the format Y-M-D  |
    +---------------+---------+--------------+---------------------------+
    | ``order``     | string  | Optional     | | Set the ordering of the |
    |               |         |              |   issues. This can be     |
    |               |         |              |   ``asc`` or ``desc``.    |
    |               |         |              |   Default: ``desc``       |
    +---------------+---------+--------------+---------------------------+
    | ``page``      | int      | Optional    | | Specifies which         |
    |               |          |             |   page to return          |
    |               |          |             |   (defaults to: 1)        |
    +---------------+----------+-------------+---------------------------+
    | ``per_page``  | int      | Optional    | | The number of projects  |
    |               |          |             |   to return per page.     |
    |               |          |             |   The maximum is 100.     |
    +---------------+----------+-------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "args": {
            "assignee": null,
            "author": null,
            'milestones': [],
            'no_stones': null,
            'order': null,
            'priority': null,
            "since": null,
            "status": "Closed",
            "tags": [
              "0.1"
            ]
          },
          "total_issues": 1,
          "issues": [
            {
              "assignee": null,
              "blocks": ["1"],
              "close_status": null,
              "closed_at": null,
              "closed_by": null,
              "comments": [],
              "content": "asd",
              "custom_fields": [],
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "last_updated": "1533815358",
              "milestone": null,
              "priority": null,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ],
          'pagination': {
            'first': 'http://localhost/api/0/test/issues?per_page=20&page=1',
            'last': 'http://localhost/api/0/test/issues?per_page=20&page=1',
            'next': null,
            'page': 1,
            'pages': 1,
            'per_page': 20,
            'prev': null
          },
        }

    """
    repo = _get_repo(repo, username, namespace)
    _check_issue_tracker(repo)
    _check_token(repo)

    assignee = flask.request.args.get("assignee", None)
    author = flask.request.args.get("author", None)
    milestone = flask.request.args.getlist("milestones", None)
    no_stones = flask.request.args.get("no_stones", None)
    if no_stones is not None:
        no_stones = is_true(no_stones)
    priority = flask.request.args.get("priority", None)
    since = flask.request.args.get("since", None)
    order = flask.request.args.get("order", None)
    status = flask.request.args.get("status", None)
    tags = flask.request.args.getlist("tags")
    tags = [tag.strip() for tag in tags if tag.strip()]
    search_id = flask.request.args.get("query_id", None)

    priority_key = None
    if priority:
        found = False
        if priority in repo.priorities:
            found = True
            priority_key = int(priority)
        else:
            for key, val in repo.priorities.items():
                if val.lower() == priority.lower():
                    priority_key = key
                    found = True
                    break

        if not found:
            raise pagure.exceptions.APIError(
                400, error_code=APIERROR.EINVALIDPRIORITY
            )

    # Hide private tickets
    private = False
    # If user is authenticated, show him/her his/her private tickets
    if api_authenticated():
        private = flask.g.fas_user.username
    # If user is repo committer, show all tickets included the private ones
    if is_repo_committer(repo):
        private = None

    params = {
        "session": flask.g.session,
        "repo": repo,
        "tags": tags,
        "assignee": assignee,
        "author": author,
        "private": private,
        "milestones": milestone,
        "priority": priority_key,
        "order": order,
        "no_milestones": no_stones,
        "search_id": search_id,
    }

    if status is not None:
        if status.lower() == "all":
            params.update({"status": None})
        elif status.lower() == "closed":
            params.update({"closed": True})
        else:
            params.update({"status": status})
    else:
        params.update({"status": "Open"})

    updated_after = None
    if since:
        # Validate and convert the time
        if since.isdigit():
            # We assume its a timestamp, so convert it to datetime
            try:
                updated_after = arrow.get(int(since)).datetime
            except ValueError:
                raise pagure.exceptions.APIError(
                    400, error_code=APIERROR.ETIMESTAMP
                )
        else:
            # We assume datetime format, so validate it
            try:
                updated_after = datetime.datetime.strptime(since, "%Y-%m-%d")
            except ValueError:
                raise pagure.exceptions.APIError(
                    400, error_code=APIERROR.EDATETIME
                )

    params.update({"updated_after": updated_after})

    page = get_page()
    per_page = get_per_page()
    params["count"] = True
    issue_cnt = pagure.lib.query.search_issues(**params)
    pagination_metadata = pagure.lib.query.get_pagination_metadata(
        flask.request, page, per_page, issue_cnt
    )
    query_start = (page - 1) * per_page
    query_limit = per_page

    params["count"] = False
    params["limit"] = query_limit
    params["offset"] = query_start
    issues = pagure.lib.query.search_issues(**params)

    jsonout = flask.jsonify(
        {
            "total_issues": len(issues),
            "issues": [issue.to_json(public=True) for issue in issues],
            "args": {
                "assignee": assignee,
                "author": author,
                "milestones": milestone,
                "no_stones": no_stones,
                "order": order,
                "priority": priority,
                "since": since,
                "status": status,
                "tags": tags,
            },
            "pagination": pagination_metadata,
        }
    )
    return jsonout
Beispiel #10
0
def format_loc(loc,
               commit=None,
               filename=None,
               tree_id=None,
               prequest=None,
               index=None):
    """ Template filter putting the provided lines of code into a table
    """
    if loc is None:
        return

    output = ['<div class="highlight">', '<table class="code_table">']

    comments = {}
    if prequest and not isinstance(prequest, flask.wrappers.Request):
        for com in prequest.comments:
            if commit and unicode(com.commit_id) == unicode(commit) \
                    and unicode(com.filename) == unicode(filename):
                if com.line in comments:
                    comments[com.line].append(com)
                else:
                    comments[com.line] = [com]
    for key in comments:
        comments[key] = sorted(comments[key], key=lambda obj: obj.date_created)

    if not index:
        index = ''

    cnt = 1
    for line in loc.split('\n'):
        if line == '</pre></div>':
            break
        if filename and commit:
            output.append(
                '<tr id="c-%(commit)s-%(cnt_lbl)s"><td class="cell1">'
                '<a id="%(cnt)s" href="#%(cnt)s" data-line-number='
                '"%(cnt_lbl)s"></a></td>'
                '<td class="prc" data-row="%(cnt_lbl)s"'
                ' data-filename="%(filename)s" data-commit="%(commit)s"'
                ' data-tree="%(tree_id)s">'
                '<p>'
                '<span class="oi prc_img" data-glyph="comment-square" '
                'alt="Add comment" title="Add comment"></span>'
                '</p>'
                '</td>' % ({
                    'cnt': '%s_%s' % (index, cnt),
                    'cnt_lbl': cnt,
                    'filename': filename.decode('UTF-8'),
                    'commit': commit,
                    'tree_id': tree_id,
                }))
        else:
            output.append('<tr><td class="cell1">'
                          '<a id="%(cnt)s" href="#%(cnt)s" data-line-number='
                          '"%(cnt_lbl)s"></a></td>' %
                          ({
                              'cnt': '%s_%s' % (index, cnt),
                              'cnt_lbl': cnt,
                          }))

        cnt += 1
        if not line:
            output.append(line)
            continue
        if line.startswith('<div'):
            line = line.split('<pre style="line-height: 125%">')[1]
            if prequest and prequest.project_from:
                rangeline = line.partition('font-weight: bold">@@ ')[2] \
                    if line.partition('font-weight: bold">@@ ')[1] == \
                    'font-weight: bold">@@ ' else None
                if rangeline:
                    rangeline = rangeline.split(' @@</span>')[0]
                    linenumber = rangeline.split('+')[1].split(',')[0]
                    line = line + '&nbsp;<a href="%s#_%s" target="_blank" ' % (
                        flask.url_for(
                            'ui_ns.view_file',
                            repo=prequest.project_from.name,
                            username=prequest.project_from.user.username
                            if prequest.project_from.is_fork else None,
                            namespace=prequest.project_from.namespace,
                            identifier=prequest.branch_from,
                            filename=filename), linenumber)
                    line = line + 'class="open_changed_file_icon_wrap">' + \
                        '<span class="oi open_changed_file_icon" ' + \
                        'data-glyph="eye" alt="Open changed file" ' + \
                        'title="Open changed file"></span></a>'
        output.append('<td class="cell2"><pre>%s</pre></td>' % line)
        output.append('</tr>')

        tpl_edit = '<a href="%(edit_url)s" ' \
            'class="btn btn-secondary btn-sm" data-comment="%(commentid)s" ' \
            'data-objid="%(requestid)s">' \
            '<span class="oi" data-glyph="pencil"></span>' \
            '</a>'
        tpl_edited = '<small class="text-muted" title="%(edit_date)s"> ' \
            'Edited %(human_edit_date)s by %(user)s </small>'

        tpl_delete = '<button class="btn btn-secondary btn-sm" '\
            'title="Remove comment" '\
            'name="drop_comment" value="%(commentid)s" type="submit" ' \
            'onclick="return confirm(\'Do you really want to remove this' \
            ' comment?\');" ><span class="oi" data-glyph="trash"></span>' \
            '</button>'

        if cnt - 1 in comments:
            for comment in comments[cnt - 1]:

                templ_delete = ''
                templ_edit = ''
                templ_edited = ''
                status = str(comment.parent.status).lower()
                if authenticated() and (
                    (status in ['true', 'open']
                     and comment.user.user == flask.g.fas_user.username)
                        or is_repo_committer(comment.parent.project)):
                    templ_delete = tpl_delete % ({'commentid': comment.id})
                    templ_edit = tpl_edit % ({
                        'edit_url':
                        flask.url_for(
                            'ui_ns.pull_request_edit_comment',
                            repo=comment.parent.project.name,
                            requestid=comment.parent.id,
                            commentid=comment.id,
                            username=comment.parent.user.user
                            if comment.parent.project.is_fork else None),
                        'requestid':
                        comment.parent.id,
                        'commentid':
                        comment.id,
                    })

                if comment.edited_on:
                    templ_edited = tpl_edited % (
                        {
                            'edit_date': format_ts(comment.edited_on),
                            'human_edit_date': humanize_date(
                                comment.edited_on),
                            'user': comment.editor.user,
                        })

                output.append(
                    '<tr class="inline-pr-comment"><td></td>'
                    '<td colspan="2">'
                    '<div class="card clearfix m-x-1 ">'
                    '<div class="card-block">'
                    '<small><div id="comment-%(commentid)s">'
                    '<img class="avatar circle" src="%(avatar_url)s"/>'
                    '<a href="%(url)s" title="%(user_html)s">'
                    '%(user)s</a> commented '
                    '<a class="headerlink" title="Permalink '
                    'to this headline" href="#comment-%(commentid)s">'
                    '<span title="%(date)s">%(human_date)s</span>'
                    '</a></div></small>'
                    '<section class="issue_comment">'
                    '<div class="comment_body">'
                    '%(comment)s'
                    '</div>'
                    '</section>'
                    '<div class="issue_actions m-t-2">'
                    '%(templ_edited)s'
                    '<aside class="btn-group issue_action icon '
                    'pull-xs-right p-b-1">'
                    '%(templ_edit)s'
                    '%(templ_delete)s'
                    '</aside>'
                    '</div></div></div>'
                    '</td></tr>' % ({
                        'url':
                        flask.url_for('ui_ns.view_user',
                                      username=comment.user.user),
                        'templ_delete':
                        templ_delete,
                        'templ_edit':
                        templ_edit,
                        'templ_edited':
                        templ_edited,
                        'user':
                        comment.user.user,
                        'user_html':
                        comment.user.html_title,
                        'avatar_url':
                        avatar_url(comment.user.default_email, 16),
                        'date':
                        format_ts(comment.date_created),
                        'human_date':
                        humanize_date(comment.date_created),
                        'comment':
                        markdown_filter(comment.comment),
                        'commentid':
                        comment.id,
                    }))

    output.append('</table></div>')

    return '\n'.join(output)
Beispiel #11
0
def format_loc(
    loc,
    commit=None,
    filename=None,
    tree_id=None,
    prequest=None,
    index=None,
    isprdiff=False,
):
    """ Template filter putting the provided lines of code into a table
    """
    if loc is None:
        return

    output = ['<div class="highlight">', '<table class="code_table">']

    commit_hash = commit
    if hasattr(commit_hash, "hex"):
        commit_hash = commit_hash.hex

    comments = {}
    if prequest and not isinstance(prequest, flask.wrappers.Request):
        for com in prequest.comments:
            if (commit and com.commit_id == commit_hash
                    and com.filename == filename):
                if com.line in comments:
                    comments[com.line].append(com)
                else:
                    comments[com.line] = [com]
    for key in comments:
        comments[key] = sorted(comments[key], key=lambda obj: obj.date_created)

    if not index:
        index = ""

    cnt = 1
    for line in loc.split("\n"):
        if filename and commit:
            if isinstance(filename, str) and six.PY2:
                filename = filename.decode("UTF-8")

            if isprdiff and (line.startswith("@@") or line.startswith("+")
                             or line.startswith("-")):
                if line.startswith("@@"):
                    output.append('<tr class="stretch-table-column bg-light"\
                    id="c-%(commit)s-%(cnt_lbl)s">' % ({
                        "cnt_lbl": cnt,
                        "commit": commit
                    }))
                elif line.startswith("+"):
                    output.append(
                        '<tr class="stretch-table-column alert-success" \
                    id="c-%(commit)s-%(cnt_lbl)s">' % ({
                            "cnt_lbl": cnt,
                            "commit": commit
                        }))
                elif line.startswith("-"):
                    output.append(
                        '<tr class="stretch-table-column alert-danger" \
                    id="c-%(commit)s-%(cnt_lbl)s">' % ({
                            "cnt_lbl": cnt,
                            "commit": commit
                        }))
            else:
                output.append('<tr id="c-%(commit)s-%(cnt_lbl)s">' %
                              ({
                                  "cnt_lbl": cnt,
                                  "commit": commit
                              }))

            output.append(
                '<td class="cell1">'
                '<a id="%(cnt)s" href="#%(cnt)s" data-line-number='
                '"%(cnt_lbl)s" data-file-number='
                '"%(line)s"></a></td>'
                '<td class="prc border-right" data-row="%(cnt_lbl)s"'
                ' data-filename="%(filename)s" data-commit="%(commit)s"'
                ' data-tree="%(tree_id)s">'
                "<p>"
                '<span class="fa fa-comment prc_img" style="display: none;"'
                'alt="Add comment" title="Add comment"></span>'
                "</p>"
                "</td>" % ({
                    "cnt": "_%s__%s" % (index, cnt),
                    "cnt_lbl": cnt,
                    "line": index,
                    "filename": filename,
                    "commit": commit,
                    "tree_id": tree_id,
                }))
        else:
            output.append('<tr><td class="cell1">'
                          '<a id="%(cnt)s" href="#%(cnt)s" data-line-number='
                          '"%(cnt_lbl)s"></a></td>' %
                          ({
                              "cnt": "%s_%s" % (index, cnt),
                              "cnt_lbl": cnt
                          }))

        cnt += 1
        if not line:
            output.append(line)
            continue
        if line.startswith("@@"):
            if prequest and prequest.project_from:
                rangeline = (line.partition("@@ ")[2]
                             if line.partition("@@ ")[1] == "@@ " else None)
                if rangeline:
                    rangeline = rangeline.split(" @@")[0]
                    linenumber = rangeline.split("+")[1].split(",")[0]
                    line = line + '&nbsp;<a href="%s#_%s" target="_blank" ' % (
                        flask.url_for(
                            "ui_ns.view_file",
                            repo=prequest.project_from.name,
                            username=prequest.project_from.user.username
                            if prequest.project_from.is_fork else None,
                            namespace=prequest.project_from.namespace,
                            identifier=prequest.branch_from,
                            filename=filename,
                        ),
                        linenumber,
                    )
                    line = (line + 'class="open_changed_file_icon_wrap">' +
                            '<span class="fa fa-file-code-o fa-fw" ' +
                            'alt="Open changed file" ' +
                            'title="Open changed file"></span></a>')

        if isprdiff and (line.startswith("@@") or line.startswith("+")
                         or line.startswith("-")):
            if line.startswith("@@"):
                output.append('<td class="cell2 stretch-table-column">\
                    <pre class="text-muted"><code>%s</code></pre></td>' % line)
            elif line.startswith("+"):
                output.append('<td class="cell2 stretch-table-column">\
                    <pre class="alert-success"><code>%s</code></pre></td>' %
                              escape(line))
            elif line.startswith("-"):
                output.append('<td class="cell2 stretch-table-column">\
                    <pre class="alert-danger"><code>%s</code></pre></td>' %
                              escape(line))
        else:
            output.append('<td class="cell2"><pre><code>%s</code></pre></td>' %
                          (escape(line)))

        output.append("</tr>")

        tpl_edit = ('<a href="%(edit_url)s" '
                    'class="btn btn-outline-primary border-0" '
                    'data-comment="%(commentid)s" '
                    'data-objid="%(requestid)s">'
                    '<i class="fa fa-pencil"></i>'
                    "</a>")
        tpl_edited = ('<small class="text-muted" title="%(edit_date)s"> '
                      "Edited %(human_edit_date)s by %(user)s </small>")

        tpl_delete = (
            '<button class="btn btn-outline-primary border-0" '
            'title="Remove comment" '
            'name="drop_comment" value="%(commentid)s" type="submit" '
            "onclick=\"return confirm('Do you really want to remove this"
            ' comment?\');" ><i class="fa fa-trash"></i>'
            "</button>")

        if cnt - 1 in comments:
            for comment in comments[cnt - 1]:

                templ_delete = ""
                templ_edit = ""
                templ_edited = ""
                if authenticated() and (
                    (is_true(comment.parent.status, ["true", "open"])
                     and comment.user.user == flask.g.fas_user.username)
                        or is_repo_committer(comment.parent.project)):
                    templ_delete = tpl_delete % ({"commentid": comment.id})
                    templ_edit = tpl_edit % ({
                        "edit_url":
                        flask.url_for(
                            "ui_ns.pull_request_edit_comment",
                            repo=comment.parent.project.name,
                            requestid=comment.parent.id,
                            commentid=comment.id,
                            username=comment.parent.user.user
                            if comment.parent.project.is_fork else None,
                        ),
                        "requestid":
                        comment.parent.id,
                        "commentid":
                        comment.id,
                    })

                if comment.edited_on:
                    templ_edited = tpl_edited % (
                        {
                            "edit_date": format_ts(comment.edited_on),
                            "human_edit_date": humanize_date(
                                comment.edited_on),
                            "user": comment.editor.user,
                        })

                output.append(
                    '<tr class="inline-pr-comment">'
                    '<td colspan="3" class="p-3 border">'
                    '<div class="card clearfix">'
                    '<div class="card-header bg-light d-flex '
                    'align-items-center px-3 py-2">'
                    "<div>"
                    '<div id="comment-%(commentid)s">'
                    '<img class="avatar circle" src="%(avatar_url)s"/>'
                    '<a href="%(url)s" title="%(user_html)s">'
                    "%(user)s</a> commented "
                    '<a class="headerlink" title="Permalink '
                    'to this headline" href="#comment-%(commentid)s">'
                    '<span title="%(date)s">%(human_date)s</span>'
                    "</a></div>"
                    "</div>"
                    '<div class="mr-auto">'
                    "%(templ_edit)s"
                    "%(templ_delete)s"
                    "</div>"
                    "</div>"
                    '<div class="card-block">'
                    "<small></small>"
                    '<section class="issue_comment">'
                    '<div class="comment_body">'
                    "%(comment)s"
                    "</div>"
                    "</section>"
                    "</div></div>"
                    "</td></tr>" % ({
                        "url":
                        flask.url_for("ui_ns.view_user",
                                      username=comment.user.user),
                        "templ_delete":
                        templ_delete,
                        "templ_edit":
                        templ_edit,
                        "templ_edited":
                        templ_edited,
                        "user":
                        comment.user.user,
                        "user_html":
                        comment.user.html_title,
                        "avatar_url":
                        avatar_url(comment.user.default_email, 16),
                        "date":
                        format_ts(comment.date_created),
                        "human_date":
                        humanize_date(comment.date_created),
                        "comment":
                        markdown_filter(comment.comment),
                        "commentid":
                        comment.id,
                    }))

    output.append("</table></div>")

    return "\n".join(output)
Beispiel #12
0
def api_view_issues(repo, username=None, namespace=None):
    """
    List project's issues
    ---------------------
    List issues of a project.

    ::

        GET /api/0/<repo>/issues
        GET /api/0/<namespace>/<repo>/issues

    ::

        GET /api/0/fork/<username>/<repo>/issues
        GET /api/0/fork/<username>/<namespace>/<repo>/issues

    Parameters
    ^^^^^^^^^^

    +---------------+---------+--------------+---------------------------+
    | Key           | Type    | Optionality  | Description               |
    +===============+=========+==============+===========================+
    | ``status``    | string  | Optional     | | Filters the status of   |
    |               |         |              |   issues. Fetches all the |
    |               |         |              |   issues if status is     |
    |               |         |              |   ``all``. Default:       |
    |               |         |              |   ``Open``                |
    +---------------+---------+--------------+---------------------------+
    | ``tags``      | string  | Optional     | | A list of tags you      |
    |               |         |              |   wish to filter. If      |
    |               |         |              |   you want to filter      |
    |               |         |              |   for issues not having   |
    |               |         |              |   a tag, add an           |
    |               |         |              |   exclamation mark in     |
    |               |         |              |   front of it             |
    +---------------+---------+--------------+---------------------------+
    | ``assignee``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by assignee             |
    +---------------+---------+--------------+---------------------------+
    | ``author``    | string  | Optional     | | Filter the issues       |
    |               |         |              |   by creator              |
    +---------------+---------+--------------+---------------------------+
    | ``milestones``| list of | Optional     | | Filter the issues       |
    |               | strings |              |   by milestone            |
    +---------------+---------+--------------+---------------------------+
    | ``priority``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by priority             |
    +---------------+---------+--------------+---------------------------+
    | ``no_stones`` | boolean | Optional     | | If true returns only the|
    |               |         |              |   issues having no        |
    |               |         |              |   milestone, if false     |
    |               |         |              |   returns only the issues |
    |               |         |              |   having a milestone      |
    +---------------+---------+--------------+---------------------------+
    | ``since``     | string  | Optional     | | Filter the issues       |
    |               |         |              |   updated after this date.|
    |               |         |              |   The date can either be  |
    |               |         |              |   provided as an unix date|
    |               |         |              |   or in the format Y-M-D  |
    +---------------+---------+--------------+---------------------------+
    | ``order``     | string  | Optional     | | Set the ordering of the |
    |               |         |              |   issues. This can be     |
    |               |         |              |   ``asc`` or ``desc``.    |
    |               |         |              |   Default: ``desc``       |
    +---------------+---------+--------------+---------------------------+
    | ``page``      | int      | Optional    | | Specifies which         |
    |               |          |             |   page to return          |
    |               |          |             |   (defaults to: 1)        |
    +---------------+----------+-------------+---------------------------+
    | ``per_page``  | int      | Optional    | | The number of projects  |
    |               |          |             |   to return per page.     |
    |               |          |             |   The maximum is 100.     |
    +---------------+----------+-------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "args": {
            "assignee": null,
            "author": null,
            'milestones': [],
            'no_stones': null,
            'order': null,
            'priority': null,
            "since": null,
            "status": "Closed",
            "tags": [
              "0.1"
            ]
          },
          "total_issues": 1,
          "issues": [
            {
              "assignee": null,
              "blocks": ["1"],
              "close_status": null,
              "closed_at": null,
              "closed_by": null,
              "comments": [],
              "content": "asd",
              "custom_fields": [],
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "last_updated": "1533815358",
              "milestone": null,
              "priority": null,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ],
          'pagination': {
            'first': 'http://localhost/api/0/test/issues?per_page=20&page=1',
            'last': 'http://localhost/api/0/test/issues?per_page=20&page=1',
            'next': null,
            'page': 1,
            'pages': 1,
            'per_page': 20,
            'prev': null
          },
        }

    """
    repo = _get_repo(repo, username, namespace)
    _check_issue_tracker(repo)
    _check_token(repo, project_token=False)

    assignee = flask.request.args.get("assignee", None)
    author = flask.request.args.get("author", None)
    milestone = flask.request.args.getlist("milestones", None)
    no_stones = flask.request.args.get("no_stones", None)
    if no_stones is not None:
        no_stones = is_true(no_stones)
    priority = flask.request.args.get("priority", None)
    since = flask.request.args.get("since", None)
    order = flask.request.args.get("order", None)
    status = flask.request.args.get("status", None)
    tags = flask.request.args.getlist("tags")
    tags = [tag.strip() for tag in tags if tag.strip()]
    search_id = flask.request.args.get("query_id", None)

    priority_key = None
    if priority:
        found = False
        if priority in repo.priorities:
            found = True
            priority_key = int(priority)
        else:
            for key, val in repo.priorities.items():
                if val.lower() == priority.lower():
                    priority_key = key
                    found = True
                    break

        if not found:
            raise pagure.exceptions.APIError(
                400, error_code=APIERROR.EINVALIDPRIORITY)

    # Hide private tickets
    private = False
    # If user is authenticated, show him/her his/her private tickets
    if api_authenticated():
        private = flask.g.fas_user.username
    # If user is repo committer, show all tickets included the private ones
    if is_repo_committer(repo):
        private = None

    params = {
        "session": flask.g.session,
        "repo": repo,
        "tags": tags,
        "assignee": assignee,
        "author": author,
        "private": private,
        "milestones": milestone,
        "priority": priority_key,
        "order": order,
        "no_milestones": no_stones,
        "search_id": search_id,
    }

    if status is not None:
        if status.lower() == "all":
            params.update({"status": None})
        elif status.lower() == "closed":
            params.update({"closed": True})
        else:
            params.update({"status": status})
    else:
        params.update({"status": "Open"})

    updated_after = None
    if since:
        # Validate and convert the time
        if since.isdigit():
            # We assume its a timestamp, so convert it to datetime
            try:
                updated_after = arrow.get(int(since)).datetime
            except ValueError:
                raise pagure.exceptions.APIError(
                    400, error_code=APIERROR.ETIMESTAMP)
        else:
            # We assume datetime format, so validate it
            try:
                updated_after = datetime.datetime.strptime(since, "%Y-%m-%d")
            except ValueError:
                raise pagure.exceptions.APIError(400,
                                                 error_code=APIERROR.EDATETIME)

    params.update({"updated_after": updated_after})

    page = get_page()
    per_page = get_per_page()
    params["count"] = True
    issue_cnt = pagure.lib.query.search_issues(**params)
    pagination_metadata = pagure.lib.query.get_pagination_metadata(
        flask.request, page, per_page, issue_cnt)
    query_start = (page - 1) * per_page
    query_limit = per_page

    params["count"] = False
    params["limit"] = query_limit
    params["offset"] = query_start
    issues = pagure.lib.query.search_issues(**params)

    jsonout = flask.jsonify({
        "total_issues":
        len(issues),
        "issues": [issue.to_json(public=True) for issue in issues],
        "args": {
            "assignee": assignee,
            "author": author,
            "milestones": milestone,
            "no_stones": no_stones,
            "order": order,
            "priority": priority,
            "since": since,
            "status": status,
            "tags": tags,
        },
        "pagination":
        pagination_metadata,
    })
    return jsonout
Beispiel #13
0
def format_loc(
    loc,
    commit=None,
    filename=None,
    tree_id=None,
    prequest=None,
    index=None,
    isprdiff=False,
):
    """ Template filter putting the provided lines of code into a table
    """
    if loc is None:
        return

    output = ['<div class="highlight">', '<table class="code_table">']

    commit_hash = commit
    if hasattr(commit_hash, "hex"):
        commit_hash = commit_hash.hex

    comments = {}
    if prequest and not isinstance(prequest, flask.wrappers.Request):
        for com in prequest.comments:
            if (
                commit
                and com.commit_id == commit_hash
                and com.filename == filename
            ):
                if com.line in comments:
                    comments[com.line].append(com)
                else:
                    comments[com.line] = [com]
    for key in comments:
        comments[key] = sorted(comments[key], key=lambda obj: obj.date_created)

    if not index:
        index = ""

    cnt = 1
    for line in loc.split("\n"):
        if filename and commit:
            if isinstance(filename, str) and six.PY2:
                filename = filename.decode("UTF-8")

            if isprdiff and (
                line.startswith("@@")
                or line.startswith("+")
                or line.startswith("-")
            ):
                if line.startswith("@@"):
                    output.append(
                        '<tr class="stretch-table-column bg-light"\
                    id="c-%(commit)s-%(cnt_lbl)s">'
                        % ({"cnt_lbl": cnt, "commit": commit})
                    )
                elif line.startswith("+"):
                    output.append(
                        '<tr class="stretch-table-column alert-success" \
                    id="c-%(commit)s-%(cnt_lbl)s">'
                        % ({"cnt_lbl": cnt, "commit": commit})
                    )
                elif line.startswith("-"):
                    output.append(
                        '<tr class="stretch-table-column alert-danger" \
                    id="c-%(commit)s-%(cnt_lbl)s">'
                        % ({"cnt_lbl": cnt, "commit": commit})
                    )
            else:
                output.append(
                    '<tr id="c-%(commit)s-%(cnt_lbl)s">'
                    % ({"cnt_lbl": cnt, "commit": commit})
                )

            output.append(
                '<td class="cell1">'
                '<a id="%(cnt)s" href="#%(cnt)s" data-line-number='
                '"%(cnt_lbl)s" data-file-number='
                '"%(line)s"></a></td>'
                '<td class="prc border-right" data-row="%(cnt_lbl)s"'
                ' data-filename="%(filename)s" data-commit="%(commit)s"'
                ' data-tree="%(tree_id)s">'
                "<p>"
                '<span class="fa fa-comment prc_img" style="display: none;"'
                'alt="Add comment" title="Add comment"></span>'
                "</p>"
                "</td>"
                % (
                    {
                        "cnt": "_%s__%s" % (index, cnt),
                        "cnt_lbl": cnt,
                        "line": index,
                        "filename": filename,
                        "commit": commit,
                        "tree_id": tree_id,
                    }
                )
            )
        else:
            output.append(
                '<tr><td class="cell1">'
                '<a id="%(cnt)s" href="#%(cnt)s" data-line-number='
                '"%(cnt_lbl)s"></a></td>'
                % ({"cnt": "%s_%s" % (index, cnt), "cnt_lbl": cnt})
            )

        cnt += 1
        if not line:
            output.append(line)
            continue
        if line.startswith("@@"):
            if prequest and prequest.project_from:
                rangeline = (
                    line.partition("@@ ")[2]
                    if line.partition("@@ ")[1] == "@@ "
                    else None
                )
                if rangeline:
                    rangeline = rangeline.split(" @@")[0]
                    linenumber = rangeline.split("+")[1].split(",")[0]
                    line = line + '&nbsp;<a href="%s#_%s" target="_blank" ' % (
                        flask.url_for(
                            "ui_ns.view_file",
                            repo=prequest.project_from.name,
                            username=prequest.project_from.user.username
                            if prequest.project_from.is_fork
                            else None,
                            namespace=prequest.project_from.namespace,
                            identifier=prequest.branch_from,
                            filename=filename,
                        ),
                        linenumber,
                    )
                    line = (
                        line
                        + 'class="open_changed_file_icon_wrap">'
                        + '<span class="fa fa-file-code-o fa-fw" '
                        + 'alt="Open changed file" '
                        + 'title="Open changed file"></span></a>'
                    )

        if isprdiff and (
            line.startswith("@@")
            or line.startswith("+")
            or line.startswith("-")
        ):
            if line.startswith("@@"):
                output.append(
                    '<td class="cell2 stretch-table-column">\
                    <pre class="text-muted"><code>%s</code></pre></td>'
                    % line
                )
            elif line.startswith("+"):
                output.append(
                    '<td class="cell2 stretch-table-column">\
                    <pre class="alert-success"><code>%s</code></pre></td>'
                    % escape(line)
                )
            elif line.startswith("-"):
                output.append(
                    '<td class="cell2 stretch-table-column">\
                    <pre class="alert-danger"><code>%s</code></pre></td>'
                    % escape(line)
                )
        else:
            output.append(
                '<td class="cell2"><pre><code>%s</code></pre></td>'
                % (escape(line))
            )

        output.append("</tr>")

        tpl_edit = (
            '<a href="%(edit_url)s" '
            'class="btn btn-outline-primary border-0" '
            'data-comment="%(commentid)s" '
            'data-objid="%(requestid)s">'
            '<i class="fa fa-pencil"></i>'
            "</a>"
        )
        tpl_edited = (
            '<small class="text-muted" title="%(edit_date)s"> '
            "Edited %(human_edit_date)s by %(user)s </small>"
        )

        tpl_delete = (
            '<button class="btn btn-outline-primary border-0" '
            'title="Remove comment" '
            'name="drop_comment" value="%(commentid)s" type="submit" '
            "onclick=\"return confirm('Do you really want to remove this"
            ' comment?\');" ><i class="fa fa-trash"></i>'
            "</button>"
        )

        if cnt - 1 in comments:
            for comment in comments[cnt - 1]:

                templ_delete = ""
                templ_edit = ""
                templ_edited = ""
                if authenticated() and (
                    (
                        is_true(comment.parent.status, ["true", "open"])
                        and comment.user.user == flask.g.fas_user.username
                    )
                    or is_repo_committer(comment.parent.project)
                ):
                    templ_delete = tpl_delete % ({"commentid": comment.id})
                    templ_edit = tpl_edit % (
                        {
                            "edit_url": flask.url_for(
                                "ui_ns.pull_request_edit_comment",
                                repo=comment.parent.project.name,
                                namespace=comment.parent.project.namespace,
                                requestid=comment.parent.id,
                                commentid=comment.id,
                                username=comment.parent.user.user
                                if comment.parent.project.is_fork
                                else None,
                            ),
                            "requestid": comment.parent.id,
                            "commentid": comment.id,
                        }
                    )

                if comment.edited_on:
                    templ_edited = tpl_edited % (
                        {
                            "edit_date": format_ts(comment.edited_on),
                            "human_edit_date": humanize_date(
                                comment.edited_on
                            ),
                            "user": comment.editor.user,
                        }
                    )

                output.append(
                    '<tr class="inline-pr-comment">'
                    '<td colspan="3" class="p-3 border">'
                    '<div class="card clearfix">'
                    '<div class="card-header bg-light d-flex '
                    'align-items-center px-3 py-2">'
                    "<div>"
                    '<div id="comment-%(commentid)s">'
                    '<img class="avatar circle" src="%(avatar_url)s"/>'
                    '<a href="%(url)s" title="%(user_html)s">'
                    "%(user)s</a> commented "
                    '<a class="headerlink" title="Permalink '
                    'to this headline" href="#comment-%(commentid)s">'
                    '<span title="%(date)s">%(human_date)s</span>'
                    "</a></div>"
                    "</div>"
                    '<div class="mr-auto">'
                    "%(templ_edit)s"
                    "%(templ_delete)s"
                    "</div>"
                    "</div>"
                    '<div class="card-block">'
                    "<small></small>"
                    '<section class="issue_comment">'
                    '<div class="comment_body">'
                    "%(comment)s"
                    "</div>"
                    "</section>"
                    "</div></div>"
                    "</td></tr>"
                    % (
                        {
                            "url": flask.url_for(
                                "ui_ns.view_user", username=comment.user.user
                            ),
                            "templ_delete": templ_delete,
                            "templ_edit": templ_edit,
                            "templ_edited": templ_edited,
                            "user": comment.user.user,
                            "user_html": comment.user.html_title,
                            "avatar_url": avatar_url(
                                comment.user.default_email, 16
                            ),
                            "date": format_ts(comment.date_created),
                            "human_date": humanize_date(comment.date_created),
                            "comment": markdown_filter(comment.comment),
                            "commentid": comment.id,
                        }
                    )
                )

    output.append("</table></div>")

    return "\n".join(output)
Beispiel #14
0
def api_view_issues(repo, username=None, namespace=None):
    """
    List project's issues
    ---------------------
    List issues of a project.

    ::

        GET /api/0/<repo>/issues
        GET /api/0/<namespace>/<repo>/issues

    ::

        GET /api/0/fork/<username>/<repo>/issues
        GET /api/0/fork/<username>/<namespace>/<repo>/issues

    Parameters
    ^^^^^^^^^^

    +---------------+---------+--------------+---------------------------+
    | Key           | Type    | Optionality  | Description               |
    +===============+=========+==============+===========================+
    | ``status``    | string  | Optional     | | Filters the status of   |
    |               |         |              |   issues. Fetches all the |
    |               |         |              |   issues if status is     |
    |               |         |              |   ``all``. Default:       |
    |               |         |              |   ``Open``                |
    +---------------+---------+--------------+---------------------------+
    | ``tags``      | string  | Optional     | | A list of tags you      |
    |               |         |              |   wish to filter. If      |
    |               |         |              |   you want to filter      |
    |               |         |              |   for issues not having   |
    |               |         |              |   a tag, add an           |
    |               |         |              |   exclamation mark in     |
    |               |         |              |   front of it             |
    +---------------+---------+--------------+---------------------------+
    | ``assignee``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by assignee             |
    +---------------+---------+--------------+---------------------------+
    | ``author``    | string  | Optional     | | Filter the issues       |
    |               |         |              |   by creator              |
    +---------------+---------+--------------+---------------------------+
    | ``milestones``| list of | Optional     | | Filter the issues       |
    |               | strings |              |   by milestone            |
    +---------------+---------+--------------+---------------------------+
    | ``priority``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by priority             |
    +---------------+---------+--------------+---------------------------+
    | ``no_stones`` | boolean | Optional     | | If true returns only the|
    |               |         |              |   issues having no        |
    |               |         |              |   milestone, if false     |
    |               |         |              |   returns only the issues |
    |               |         |              |   having a milestone      |
    +---------------+---------+--------------+---------------------------+
    | ``since``     | string  | Optional     | | Filter the issues       |
    |               |         |              |   updated after this date.|
    |               |         |              |   The date can either be  |
    |               |         |              |   provided as an unix date|
    |               |         |              |   or in the format Y-M-D  |
    +---------------+---------+--------------+---------------------------+
    | ``order``     | string  | Optional     | | Set the ordering of the |
    |               |         |              |   issues. This can be     |
    |               |         |              |   ``asc`` or ``desc``.    |
    |               |         |              |   Default: ``desc``       |
    +---------------+---------+--------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "args": {
            "assignee": null,
            "author": null,
            'milestones': [],
            'no_stones': null,
            'order': null,
            'priority': null,
            "since": null,
            "status": "Closed",
            "tags": [
              "0.1"
            ]
          },
          "total_issues": 1,
          "issues": [
            {
              "assignee": null,
              "blocks": ["1"],
              "comments": [],
              "content": "asd",
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ]
        }

    """
    repo = _get_repo(repo, username, namespace)
    _check_issue_tracker(repo)
    _check_token(repo)

    assignee = flask.request.args.get('assignee', None)
    author = flask.request.args.get('author', None)
    milestone = flask.request.args.getlist('milestones', None)
    no_stones = flask.request.args.get('no_stones', None)
    if no_stones is not None:
        if str(no_stones).lower() in ['1', 'true', 't']:
            no_stones = True
        else:
            no_stones = False
    priority = flask.request.args.get('priority', None)
    since = flask.request.args.get('since', None)
    order = flask.request.args.get('order', None)
    status = flask.request.args.get('status', None)
    tags = flask.request.args.getlist('tags')
    tags = [tag.strip() for tag in tags if tag.strip()]

    priority_key = None
    if priority:
        found = False
        if priority in repo.priorities:
            found = True
            priority_key = int(priority)
        else:
            for key, val in repo.priorities.items():
                if val.lower() == priority.lower():
                    priority_key = key
                    found = True
                    break

        if not found:
            raise pagure.exceptions.APIError(
                400, error_code=APIERROR.EINVALIDPRIORITY)

    # Hide private tickets
    private = False
    # If user is authenticated, show him/her his/her private tickets
    if api_authenticated():
        private = flask.g.fas_user.username
    # If user is repo committer, show all tickets included the private ones
    if is_repo_committer(repo):
        private = None

    params = {
        'session': flask.g.session,
        'repo': repo,
        'tags': tags,
        'assignee': assignee,
        'author': author,
        'private': private,
        'milestones': milestone,
        'priority': priority_key,
        'order': order,
        'no_milestones': no_stones,
    }

    if status is not None:
        if status.lower() == 'all':
            params.update({'status': None})
        elif status.lower() == 'closed':
            params.update({'closed': True})
        else:
            params.update({'status': status})
    else:
        params.update({'status': 'Open'})

    updated_after = None
    if since:
        # Validate and convert the time
        if since.isdigit():
            # We assume its a timestamp, so convert it to datetime
            try:
                updated_after = arrow.get(int(since)).datetime
            except ValueError:
                raise pagure.exceptions.APIError(
                    400, error_code=APIERROR.ETIMESTAMP)
        else:
            # We assume datetime format, so validate it
            try:
                updated_after = datetime.datetime.strptime(since, '%Y-%m-%d')
            except ValueError:
                raise pagure.exceptions.APIError(
                    400, error_code=APIERROR.EDATETIME)

    params.update({'updated_after': updated_after})
    issues = pagure.lib.search_issues(**params)
    jsonout = flask.jsonify({
        'total_issues': len(issues),
        'issues': [issue.to_json(public=True) for issue in issues],
        'args': {
            'assignee': assignee,
            'author': author,
            'milestones': milestone,
            'no_stones': no_stones,
            'order': order,
            'priority': priority,
            'since': since,
            'status': status,
            'tags': tags,
        }
    })
    return jsonout