def _check_link_custom_field(field, links): """Check if the value provided in the link custom field is a link. :param field : The issue custom field key object. :param links : Value of the custom field. :raises pagure.exceptions.APIERROR when invalid. """ if field.key_type == 'link': links = links.split(',') for link in links: link = link.replace(' ', '') if not urlpattern.match(link): raise pagure.exceptions.APIError( 400, error_code=APIERROR.EINVALIDISSUEFIELD_LINK)
def api_update_custom_field( repo, issueid, field, username=None, namespace=None): """ Update custom field ------------------- Update or reset the content of a custom field associated to an issue. :: POST /api/0/<repo>/issue/<issue id>/custom/<field> POST /api/0/<namespace>/<repo>/issue/<issue id>/custom/<field> :: POST /api/0/fork/<username>/<repo>/issue/<issue id>/custom/<field> POST /api/0/fork/<username>/<namespace>/<repo>/issue/<issue id>/custom/<field> Input ^^^^^ +----------------- +---------+--------------+-------------------------+ | Key | Type | Optionality | Description | +==================+=========+==============+=========================+ | ``value`` | string | Optional | The new value of the | | | | | custom field of interest| +----------------- +---------+--------------+-------------------------+ Sample response ^^^^^^^^^^^^^^^ :: { "message": "Custom field adjusted" } """ repo = pagure.lib.get_project( SESSION, repo, user=username, namespace=namespace) output = {} if repo is None: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT) if not repo.settings.get('issue_tracker', True): raise pagure.exceptions.APIError( 404, error_code=APIERROR.ETRACKERDISABLED) if api_authenticated(): if repo != flask.g.token.project: raise pagure.exceptions.APIError( 401, error_code=APIERROR.EINVALIDTOK) issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOISSUE) if issue.private and not is_repo_admin(repo) \ and (not api_authenticated() or not issue.user.user == flask.g.fas_user.username): raise pagure.exceptions.APIError( 403, error_code=APIERROR.EISSUENOTALLOWED) fields = {k.name: k for k in repo.issue_keys} if field not in fields: raise pagure.exceptions.APIError( 400, error_code=APIERROR.EINVALIDISSUEFIELD) key = fields[field] value = flask.request.form.get('value') if value: if key.key_type == 'link': links = value.split(',') for link in links: link = link.replace(' ', '') if not urlpattern.match(link): raise pagure.exceptions.APIError( 400, error_code=APIERROR.EINVALIDISSUEFIELD_LINK) try: message = pagure.lib.set_custom_key_value( SESSION, issue, key, value) SESSION.commit() if message: output['message'] = message else: output['message'] = 'No changes' except pagure.exceptions.PagureException as err: raise pagure.exceptions.APIError( 400, error_code=APIERROR.ENOCODE, error=str(err)) except SQLAlchemyError as err: # pragma: no cover print err SESSION.rollback() raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) jsonout = flask.jsonify(output) return jsonout
def update_issue(repo, issueid, username=None, namespace=None): ''' Add a comment to an issue. ''' is_js = flask.request.args.get('js', False) repo = flask.g.repo if flask.request.method == 'GET': if not is_js: flask.flash('Invalid method: GET', 'error') return flask.redirect(flask.url_for( 'view_issue', username=username, repo=repo.name, namespace=repo.namespace, issueid=issueid)) if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: flask.abort(404, 'Issue not found') if issue.private and not flask.g.repo_admin \ and (not authenticated() or not issue.user.user == flask.g.fas_user.username): flask.abort( 403, 'This issue is private and you are not allowed to view it') if flask.request.form.get('edit_comment'): commentid = flask.request.form.get('edit_comment') form = pagure.forms.EditCommentForm() if form.validate_on_submit(): return edit_comment_issue( repo.name, issueid, commentid, username=username) status = pagure.lib.get_issue_statuses(SESSION) form = pagure.forms.UpdateIssueForm( status=status, priorities=repo.priorities, milestones=repo.milestones, close_status=repo.close_status, ) if form.validate_on_submit(): repo_admin = flask.g.repo_admin if flask.request.form.get('drop_comment'): commentid = flask.request.form.get('drop_comment') comment = pagure.lib.get_issue_comment( SESSION, issue.uid, commentid) if comment is None or comment.issue.project != repo: flask.abort(404, 'Comment not found') if (flask.g.fas_user.username != comment.user.username or comment.parent.status != 'Open') \ and not flask.g.repo_admin: flask.abort( 403, 'You are not allowed to remove this comment from ' 'this issue') issue.last_updated = datetime.datetime.utcnow() SESSION.add(issue) SESSION.delete(comment) try: SESSION.commit() if not is_js: flask.flash('Comment removed') except SQLAlchemyError as err: # pragma: no cover is_js = False SESSION.rollback() LOG.error(err) if not is_js: flask.flash( 'Could not remove the comment: %s' % commentid, 'error') if is_js: return 'ok' else: return flask.redirect(flask.url_for( 'view_issue', username=username, repo=repo.name, namespace=repo.namespace, issueid=issueid)) comment = form.comment.data depends = [] for depend in form.depends.data.split(','): if depend.strip(): try: depends.append(int(depend.strip())) except ValueError: pass blocks = [] for block in form.blocks.data.split(','): if block.strip(): try: blocks.append(int(block.strip())) except ValueError: pass assignee = form.assignee.data.strip() or None new_status = form.status.data.strip() or None close_status = form.close_status.data or None if new_status != 'Closed': close_status = None if close_status not in repo.close_status: close_status = None new_priority = None try: new_priority = int(form.priority.data) except: pass tags = [ tag.strip() for tag in form.tag.data.split(',') if tag.strip()] new_milestone = None try: if repo.milestones: new_milestone = form.milestone.data.strip() or None except: pass try: messages = set() # New comment if comment: message = pagure.lib.add_issue_comment( SESSION, issue=issue, comment=comment, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message and not is_js: messages.add(message) # The status field can be updated by both the admin and the # person who opened the ticket. # Update status if repo_admin or flask.g.fas_user.username == issue.user.user: if new_status in status: message = pagure.lib.edit_issue( SESSION, issue=issue, status=new_status, close_status=close_status, private=issue.private, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message: messages.add(message) # All the other meta-data can be changed only by admins # while other field will be missing for non-admin and thus # reset if we let them if repo_admin: # Adjust (add/remove) tags messages.union(set(pagure.lib.update_tags( SESSION, issue, tags, username=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'] ))) # The meta-data can be changed by admins and issue creator, # where issue creators can only change status of their issue while # other fields will be missing for non-admin and thus reset if we let them if repo_admin: # Assign or update assignee of the ticket message = pagure.lib.add_issue_assignee( SESSION, issue=issue, assignee=assignee or None, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message and message != 'Nothing to change': messages.add(message) # Update priority if str(new_priority) in repo.priorities: message = pagure.lib.edit_issue( SESSION, issue=issue, priority=new_priority, private=issue.private, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message: messages.add(message) # Update milestone and privacy setting message = pagure.lib.edit_issue( SESSION, issue=issue, milestone=new_milestone, private=form.private.data, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message: messages.add(message) # Update the custom keys/fields for key in repo.issue_keys: value = flask.request.form.get(key.name) if value: if key.key_type == 'link': links = value.split(',') for link in links: link = link.replace(' ', '') if not urlpattern.match(link): flask.abort( 400, 'Meta-data "link" field ' '(%s) has invalid url (%s) ' % (key.name, link)) messages.add( pagure.lib.set_custom_key_value( SESSION, issue, key, value) ) # Update ticket this one depends on messages.union(set(pagure.lib.update_dependency_issue( SESSION, repo, issue, depends, username=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ))) # Update ticket(s) depending on this one messages.union(set(pagure.lib.update_blocked_issue( SESSION, repo, issue, blocks, username=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ))) if not is_js: for message in messages: flask.flash(message) except pagure.exceptions.PagureException as err: is_js = False SESSION.rollback() flask.flash(err.message, 'error') except SQLAlchemyError as err: # pragma: no cover is_js = False SESSION.rollback() APP.logger.exception(err) flask.flash(str(err), 'error') except filelock.Timeout as err: # pragma: no cover is_js = False SESSION.rollback() APP.logger.exception(err) flask.flash( 'We could not save all the info, please try again', 'error') else: if is_js: return 'notok: %s' % form.errors if is_js: return 'ok' else: return flask.redirect(flask.url_for( 'view_issue', repo=repo.name, username=username, namespace=namespace, issueid=issueid) )