1
2
3 import os
4 import time
5 import fnmatch
6 import subprocess
7 import json
8 import datetime
9
10 from six.moves.urllib.parse import urljoin
11
12 import flask
13 from flask import render_template, url_for, stream_with_context
14 import sqlalchemy
15 from itertools import groupby
16 from wtforms import ValidationError
17
18 from pygments import highlight
19 from pygments.lexers import get_lexer_by_name
20 from pygments.formatters import HtmlFormatter
21
22 from copr_common.enums import StatusEnum
23 from coprs import app
24 from coprs import db
25 from coprs import rcp
26 from coprs import exceptions
27 from coprs import forms
28 from coprs import helpers
29 from coprs import models
30 from coprs.exceptions import ObjectNotFound
31 from coprs.logic.coprs_logic import CoprsLogic, PinnedCoprsLogic
32 from coprs.logic.stat_logic import CounterStatLogic
33 from coprs.logic.modules_logic import ModulesLogic, ModulemdGenerator, ModuleBuildFacade
34 from coprs.rmodels import TimedStatEvents
35 from coprs.mail import send_mail, LegalFlagMessage, PermissionRequestMessage, PermissionChangeMessage
36
37 from coprs.logic.complex_logic import ComplexLogic
38
39 from coprs.views.misc import login_required, page_not_found, req_with_copr, req_with_copr, generic_error
40
41 from coprs.views.coprs_ns import coprs_ns
42
43 from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic
44 from coprs.helpers import generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, \
45 url_for_copr_view, REPO_DL_STAT_FMT, CounterStatType
52
59
60
61 @coprs_ns.route("/", defaults={"page": 1})
62 @coprs_ns.route("/<int:page>/")
63 -def coprs_show(page=1):
64 query = CoprsLogic.get_multiple(include_unlisted_on_hp=False)
65 query = CoprsLogic.set_query_order(query, desc=True)
66
67 paginator = helpers.Paginator(query, query.count(), page)
68
69 coprs = paginator.sliced_query
70
71
72
73
74 users_builds = builds_logic.BuildsLogic.get_recent_tasks(None, 4)
75
76 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
77
78 return flask.render_template("coprs/show/all.html",
79 coprs=coprs,
80 pinned=[],
81 paginator=paginator,
82 tasks_info=ComplexLogic.get_queue_sizes(),
83 users_builds=users_builds,
84 graph=data)
85
86
87 @coprs_ns.route("/<username>/", defaults={"page": 1})
88 @coprs_ns.route("/<username>/<int:page>/")
89 -def coprs_by_user(username=None, page=1):
90 user = users_logic.UsersLogic.get(username).first()
91 if not user:
92 return page_not_found(
93 "User {0} does not exist.".format(username))
94
95 pinned = [pin.copr for pin in PinnedCoprsLogic.get_by_user_id(user.id)] if page == 1 else []
96 query = CoprsLogic.get_multiple_owned_by_username(username)
97 query = CoprsLogic.filter_without_ids(query, [copr.id for copr in pinned])
98 query = CoprsLogic.filter_without_group_projects(query)
99 query = CoprsLogic.set_query_order(query, desc=True)
100
101 paginator = helpers.Paginator(query, query.count(), page)
102 coprs = paginator.sliced_query
103
104
105 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 4)
106
107 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
108
109 return flask.render_template("coprs/show/user.html",
110 user=user,
111 coprs=coprs,
112 pinned=pinned,
113 paginator=paginator,
114 tasks_info=ComplexLogic.get_queue_sizes(),
115 users_builds=users_builds,
116 graph=data)
117
118
119 @coprs_ns.route("/fulltext/", defaults={"page": 1})
120 @coprs_ns.route("/fulltext/<int:page>/")
121 -def coprs_fulltext_search(page=1):
122 fulltext = flask.request.args.get("fulltext", "")
123 try:
124 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext)
125 except ValueError as e:
126 flask.flash(str(e), "error")
127 return flask.redirect(flask.request.referrer or
128 flask.url_for("coprs_ns.coprs_show"))
129
130 paginator = helpers.Paginator(query, query.count(), page,
131 additional_params={"fulltext": fulltext})
132
133 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
134
135 coprs = paginator.sliced_query
136 return render_template("coprs/show/fulltext.html",
137 coprs=coprs,
138 pinned=[],
139 paginator=paginator,
140 fulltext=fulltext,
141 tasks_info=ComplexLogic.get_queue_sizes(),
142 graph=data)
143
144
145 @coprs_ns.route("/<username>/add/")
146 @coprs_ns.route("/g/<group_name>/add/")
147 @login_required
148 -def copr_add(username=None, group_name=None):
154
155
156 @coprs_ns.route("/<username>/new/", methods=["POST"])
157 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
158 @login_required
159 -def copr_new(username=None, group_name=None):
160 """
161 Receive information from the user (and group) on how to create its new copr
162 and create it accordingly.
163 """
164 group = None
165 redirect = "coprs/add.html"
166 if group_name:
167 group = ComplexLogic.get_group_by_name_safe(group_name)
168 redirect = "coprs/group_add.html"
169
170 form = forms.CoprFormFactory.create_form_cls(group=group)()
171 if form.validate_on_submit():
172 try:
173 copr = coprs_logic.CoprsLogic.add(
174 flask.g.user,
175 name=form.name.data,
176 homepage=form.homepage.data,
177 contact=form.contact.data,
178 repos=form.repos.data.replace("\n", " "),
179 selected_chroots=form.selected_chroots,
180 description=form.description.data,
181 instructions=form.instructions.data,
182 disable_createrepo=form.disable_createrepo.data,
183 build_enable_net=form.build_enable_net.data,
184 unlisted_on_hp=form.unlisted_on_hp.data,
185 group=group,
186 persistent=form.persistent.data,
187 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
188 use_bootstrap_container=form.use_bootstrap_container.data,
189 follow_fedora_branching=form.follow_fedora_branching.data,
190 delete_after_days=form.delete_after_days.data,
191 )
192
193 db.session.commit()
194 after_the_project_creation(copr, form)
195 return flask.redirect(url_for_copr_details(copr))
196 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
197 flask.flash(str(e), "error")
198
199 return flask.render_template(redirect, form=form, group=group)
200
203 flask.flash("New project has been created successfully.", "success")
204 _check_rpmfusion(copr.repos)
205 if form.initial_pkgs.data:
206 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
207
208
209 bad_urls = []
210 for pkg in pkgs:
211 if not pkg.endswith(".src.rpm"):
212 bad_urls.append(pkg)
213 flask.flash("Bad url: {0} (skipped)".format(pkg))
214 for bad_url in bad_urls:
215 pkgs.remove(bad_url)
216
217 if not pkgs:
218 flask.flash("No initial packages submitted")
219 else:
220
221 for pkg in pkgs:
222 builds_logic.BuildsLogic.add(
223 flask.g.user,
224 pkgs=pkg,
225 srpm_url=pkg,
226 copr=copr,
227 enable_net=form.build_enable_net.data
228 )
229
230 db.session.commit()
231 flask.flash("Initial packages were successfully submitted "
232 "for building.")
233
234
235 @coprs_ns.route("/<username>/<coprname>/report-abuse")
236 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse")
237 @req_with_copr
238 @login_required
239 -def copr_report_abuse(copr):
241
246
247
248 @coprs_ns.route("/<username>/<coprname>/")
249 @coprs_ns.route("/g/<group_name>/<coprname>/")
250 @req_with_copr
251 -def copr_detail(copr):
253
256 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr)
257 form = forms.CoprLegalFlagForm()
258 repos_info = {}
259 for chroot in copr.active_chroots:
260 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format(
261 copr_user=copr.owner_name,
262 copr_project_name=copr.name,
263 copr_chroot=chroot.name,
264 )
265 chroot_rpms_dl_stat = TimedStatEvents.get_count(
266 rconnect=rcp.get_connection(),
267 name=chroot_rpms_dl_stat_key,
268 )
269
270 logoset = set()
271 logodir = app.static_folder + "/chroot_logodir"
272 for logo in os.listdir(logodir):
273
274 if fnmatch.fnmatch(logo, "*.png"):
275 logoset.add(logo[:-4])
276
277 if chroot.name_release not in repos_info:
278 logo = None
279 if chroot.name_release in logoset:
280 logo = chroot.name_release + ".png"
281 elif chroot.os_release in logoset:
282 logo = chroot.os_release + ".png"
283
284 repos_info[chroot.name_release] = {
285 "name_release": chroot.name_release,
286 "os_release": chroot.os_release,
287 "os_version": chroot.os_version,
288 "logo": logo,
289 "arch_list": [chroot.arch],
290 "repo_file": "{}-{}.repo".format(copr.repo_id, chroot.name_release),
291 "dl_stat": repo_dl_stat[chroot.name_release],
292 "rpm_dl_stat": {
293 chroot.arch: chroot_rpms_dl_stat
294 }
295 }
296 else:
297 repos_info[chroot.name_release]["arch_list"].append(chroot.arch)
298 repos_info[chroot.name_release]["rpm_dl_stat"][chroot.arch] = chroot_rpms_dl_stat
299 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"])
300 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all()
301
302 return flask.render_template(
303 "coprs/detail/overview.html",
304 copr=copr,
305 user=flask.g.user,
306 form=form,
307 repo_dl_stat=repo_dl_stat,
308 repos_info_list=repos_info_list,
309 latest_build=builds[0] if len(builds) == 1 else None,
310 )
311
312
313 @coprs_ns.route("/<username>/<coprname>/permissions/")
314 @coprs_ns.route("/g/<group_name>/<coprname>/permissions/")
315 @req_with_copr
316 -def copr_permissions(copr):
344
347 if not copr.webhook_secret:
348 copr.new_webhook_secret()
349 db.session.add(copr)
350 db.session.commit()
351
352 bitbucket_url = "https://{}/webhooks/bitbucket/{}/{}/".format(
353 app.config["PUBLIC_COPR_HOSTNAME"],
354 copr.id,
355 copr.webhook_secret)
356
357 github_url = "https://{}/webhooks/github/{}/{}/".format(
358 app.config["PUBLIC_COPR_HOSTNAME"],
359 copr.id,
360 copr.webhook_secret)
361
362 gitlab_url = "https://{}/webhooks/gitlab/{}/{}/".format(
363 app.config["PUBLIC_COPR_HOSTNAME"],
364 copr.id,
365 copr.webhook_secret)
366
367 custom_url = "https://{}/webhooks/custom/{}/{}/".format(
368 app.config["PUBLIC_COPR_HOSTNAME"],
369 copr.id,
370 copr.webhook_secret) + "<PACKAGE_NAME>/"
371
372 return flask.render_template(
373 "coprs/detail/settings/integrations.html",
374 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
375 gitlab_url=gitlab_url, custom_url=custom_url, pagure_form=pagure_form)
376
377
378 @coprs_ns.route("/<username>/<coprname>/integrations/")
379 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/")
380 @login_required
381 @req_with_copr
382 -def copr_integrations(copr):
395
396
397 @coprs_ns.route("/<username>/<coprname>/integrations/update", methods=["POST"])
398 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/update", methods=["POST"])
399 @login_required
400 @req_with_copr
401 -def copr_integrations_update(copr):
418
427
428
429 @coprs_ns.route("/<username>/<coprname>/edit/")
430 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
431 @login_required
432 @req_with_copr
433 -def copr_edit(copr, form=None):
435
438 if "rpmfusion" in repos:
439 message = flask.Markup('Using rpmfusion as dependency is nearly always wrong. Please see <a href="https://docs.pagure.org/copr.copr/user_documentation.html#what-i-can-build-in-copr">What I can build in Copr</a>.')
440 flask.flash(message, "error")
441
474
475
476 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
477 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
478 @login_required
479 @req_with_copr
480 -def copr_update(copr):
488
489
490 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
491 methods=["POST"])
492 @coprs_ns.route("/g/<group_name>/<coprname>/permissions_applier_change/", methods=["POST"])
493 @login_required
494 @req_with_copr
495 -def copr_permissions_applier_change(copr):
496 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
497 applier_permissions_form = \
498 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
499
500 if copr.user == flask.g.user:
501 flask.flash("Owner cannot request permissions for his own project.", "error")
502 elif applier_permissions_form.validate_on_submit():
503
504 if permission is not None:
505 old_builder = permission.copr_builder
506 old_admin = permission.copr_admin
507 else:
508 old_builder = 0
509 old_admin = 0
510 new_builder = applier_permissions_form.copr_builder.data
511 new_admin = applier_permissions_form.copr_admin.data
512 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
513 flask.g.user, copr, permission, new_builder, new_admin)
514 db.session.commit()
515 flask.flash(
516 "Successfully updated permissions for project '{0}'."
517 .format(copr.name))
518
519
520 if flask.current_app.config.get("SEND_EMAILS", False):
521 for mail in copr.admin_mails:
522 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
523 "new_builder": new_builder, "new_admin": new_admin}
524 msg = PermissionRequestMessage(copr, flask.g.user, permission_dict)
525 send_mail(mail, msg, )
526
527 return flask.redirect(helpers.copr_url("coprs_ns.copr_detail", copr))
528
529
530 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
531 @coprs_ns.route("/g/<group_name>/<coprname>/update_permissions/", methods=["POST"])
532 @login_required
533 @req_with_copr
534 -def copr_update_permissions(copr):
535 permissions = copr.copr_permissions
536 permissions_form = forms.PermissionsFormFactory.create_form_cls(
537 permissions)()
538
539 if permissions_form.validate_on_submit():
540
541 try:
542
543
544 permissions.sort(
545 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
546 for perm in permissions:
547 old_builder = perm.copr_builder
548 old_admin = perm.copr_admin
549 new_builder = permissions_form[
550 "copr_builder_{0}".format(perm.user_id)].data
551 new_admin = permissions_form[
552 "copr_admin_{0}".format(perm.user_id)].data
553 coprs_logic.CoprPermissionsLogic.update_permissions(
554 flask.g.user, copr, perm, new_builder, new_admin)
555 if flask.current_app.config.get("SEND_EMAILS", False) and \
556 (old_builder is not new_builder or old_admin is not new_admin):
557 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
558 "new_builder": new_builder, "new_admin": new_admin}
559 msg = PermissionChangeMessage(copr, permission_dict)
560 send_mail(perm.user.mail, msg)
561
562
563 except exceptions.InsufficientRightsException as e:
564 db.session.rollback()
565 flask.flash(str(e), "error")
566 else:
567 db.session.commit()
568 flask.flash("Project permissions were updated successfully.", "success")
569
570 return flask.redirect(url_for_copr_details(copr))
571
572
573 @coprs_ns.route("/<username>/<coprname>/repositories/")
574 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/")
575 @login_required
576 @req_with_copr
577 -def copr_repositories(copr):
583
589
590
591 @coprs_ns.route("/<username>/<coprname>/repositories/", methods=["POST"])
592 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/", methods=["POST"])
593 @login_required
594 @req_with_copr
595 -def copr_repositories_post(copr):
596 if not flask.g.user.can_edit(copr):
597 flask.flash("You don't have access to this page.", "error")
598 return flask.redirect(url_for_copr_details(copr))
599
600 form = forms.CoprChrootExtend()
601 if form.extend.data:
602 delete_after_days = app.config["DELETE_EOL_CHROOTS_AFTER"] + 1
603 chroot_name = form.extend.data
604 flask.flash("Repository for {} will be preserved for another {} days from now"
605 .format(chroot_name, app.config["DELETE_EOL_CHROOTS_AFTER"]))
606 elif form.expire.data:
607 delete_after_days = 0
608 chroot_name = form.expire.data
609 flask.flash("Repository for {} is scheduled to be removed."
610 "If you changed your mind, click 'Extend` to revert your decision."
611 .format(chroot_name))
612 else:
613 raise ValidationError("Copr chroot needs to be either extended or expired")
614
615 copr_chroot = coprs_logic.CoprChrootsLogic.get_by_name(copr, chroot_name, active_only=False).one()
616 delete_after_timestamp = datetime.datetime.now() + datetime.timedelta(days=delete_after_days)
617 coprs_logic.CoprChrootsLogic.update_chroot(flask.g.user, copr_chroot,
618 delete_after=delete_after_timestamp)
619 db.session.commit()
620 return render_copr_repositories(copr)
621
622
623 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
624 @login_required
625 -def copr_createrepo(copr_id):
637
640 form = forms.CoprDeleteForm()
641 if form.validate_on_submit():
642
643 try:
644 ComplexLogic.delete_copr(copr)
645 except (exceptions.ActionInProgressException,
646 exceptions.InsufficientRightsException) as e:
647
648 db.session.rollback()
649 flask.flash(str(e), "error")
650 return flask.redirect(url_on_error)
651 else:
652 db.session.commit()
653 flask.flash("Project has been deleted successfully.")
654 return flask.redirect(url_on_success)
655 else:
656 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
657
658
659 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
660 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
661 @login_required
662 @req_with_copr
663 -def copr_delete(copr):
670
671
672 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
673 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"])
674 @login_required
675 @req_with_copr
676 -def copr_legal_flag(copr):
678
696
697
698 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
699 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/<repofile>")
700 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
701 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/<repofile>")
702 -def generate_repo_file(copr_dirname, name_release, repofile, username=None, group_name=None):
709
712 name_release = app.config["CHROOT_NAME_RELEASE_ALIAS"].get(name_release, name_release)
713 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
714
715 if not mock_chroot:
716 raise ObjectNotFound("Chroot {} does not exist".format(name_release))
717
718 repo_id = "copr:{0}:{1}:{2}".format(app.config["PUBLIC_COPR_HOSTNAME"].split(":")[0],
719 copr_dir.copr.owner_name.replace("@", "group_"),
720 copr_dir.name)
721 url = os.path.join(copr_dir.repo_url, '')
722 repo_url = generate_repo_url(mock_chroot, url)
723 pubkey_url = urljoin(url, "pubkey.gpg")
724 response = flask.make_response(
725 flask.render_template("coprs/copr_dir.repo", copr_dir=copr_dir, url=repo_url, pubkey_url=pubkey_url,
726 repo_id=repo_id))
727 response.mimetype = "text/plain"
728 response.headers["Content-Disposition"] = \
729 "filename={0}.repo".format(copr_dir.repo_name)
730
731 name = REPO_DL_STAT_FMT.format(**{
732 'copr_user': copr_dir.copr.user.name,
733 'copr_project_name': copr_dir.copr.name,
734 'copr_name_release': name_release,
735 })
736 CounterStatLogic.incr(name=name, counter_type=CounterStatType.REPO_DL)
737 db.session.commit()
738
739 return response
740
741
742
743
744
745
746 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
747 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
748 @req_with_copr
749 -def generate_module_repo_file(copr, name_release, module_nsv):
752
754 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
755 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
756 url = os.path.join(copr.main_dir.repo_url, '')
757 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
758 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
759 pubkey_url = urljoin(url, "pubkey.gpg")
760 response = flask.make_response(
761 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
762 baseurl=baseurl, pubkey_url=pubkey_url))
763 response.mimetype = "text/plain"
764 response.headers["Content-Disposition"] = \
765 "filename={0}.cfg".format(copr.repo_name)
766 return response
767
768
769
770 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
771 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
772 try:
773 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
774 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
775 response = flask.make_response(rpm.read())
776 response.mimetype = "application/x-rpm"
777 response.headers["Content-Disposition"] = \
778 "filename={0}".format(rpmfile)
779 return response
780 except IOError:
781 return flask.render_template("404.html")
782
799
800
801 @coprs_ns.route("/<username>/<coprname>/monitor/")
802 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
803 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
804 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
805 @req_with_copr
806 -def copr_build_monitor(copr, detailed=False):
808
809
810 @coprs_ns.route("/<username>/<coprname>/fork/")
811 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
812 @login_required
813 @req_with_copr
814 -def copr_fork(copr):
817
820 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
821
822
823 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
824 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
825 @login_required
826 @req_with_copr
827 -def copr_fork_post(copr):
828 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
829 if form.validate_on_submit():
830 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
831 if flask.g.user.name != form.owner.data and not dstgroup:
832 return generic_error("There is no such group: {}".format(form.owner.data))
833
834 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
835 if created:
836 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
837 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
838 elif not created and form.confirm.data == True:
839 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
840 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
841 else:
842 return render_copr_fork(copr, form, confirm=True)
843
844 db.session.commit()
845 flask.flash(msg)
846
847 return flask.redirect(url_for_copr_details(fcopr))
848 return render_copr_fork(copr, form)
849
850
851 @coprs_ns.route("/<username>/<coprname>/forks/")
852 @coprs_ns.route("/g/<group_name>/<coprname>/forks/")
853 @req_with_copr
854 -def copr_forks(copr):
855 return flask.render_template("coprs/detail/forks.html", copr=copr)
856
860 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1'])
861 return "OK"
862
863
864 @coprs_ns.route("/<username>/<coprname>/modules/")
865 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
866 @req_with_copr
867 -def copr_modules(copr):
869
874
875
876 @coprs_ns.route("/<username>/<coprname>/create_module/")
877 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
878 @login_required
879 @req_with_copr
880 -def copr_create_module(copr):
883
892
893
894 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
895 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
896 @login_required
897 @req_with_copr
898 -def copr_create_module_post(copr):
899 form = forms.CreateModuleForm(copr=copr, meta={'csrf': False})
900 args = [copr, form]
901 if "add_profile" in flask.request.values:
902 return add_profile(*args)
903 if "build_module" in flask.request.values:
904 return build_module(*args)
905
914
917 if not form.validate_on_submit():
918
919 for i in range(2, len(form.profile_names)):
920 form.profile_pkgs.append_entry()
921 return render_create_module(copr, form, profiles=len(form.profile_names))
922
923 summary = "Module from Copr repository: {}".format(copr.full_name)
924 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
925 generator.add_filter(form.filter.data)
926 generator.add_api(form.api.data)
927 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
928 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
929 yaml = generator.generate()
930
931 facade = None
932 try:
933 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
934 module = facade.submit_build()
935 db.session.commit()
936
937 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
938 .format(module.nsv), "success")
939 return flask.redirect(url_for_copr_details(copr))
940
941 except ValidationError as ex:
942 flask.flash(ex.message, "error")
943 return render_create_module(copr, form, len(form.profile_names))
944
945 except sqlalchemy.exc.IntegrityError:
946 flask.flash("Module {}-{}-{} already exists".format(
947 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
948 db.session.rollback()
949 return render_create_module(copr, form, len(form.profile_names))
950
951
952 @coprs_ns.route("/<username>/<coprname>/module/<id>")
953 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
954 @req_with_copr
955 -def copr_module(copr, id):
973
974
975 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
976 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
977 @req_with_copr
978 -def copr_module_raw(copr, id):
985