launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24563
[Merge] ~ilasc/launchpad:ui-create-oci-project into launchpad:master
Ioana Lasc has proposed merging ~ilasc/launchpad:ui-create-oci-project into launchpad:master.
Commit message:
Add Page for OCI Project Creation
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ilasc/launchpad/+git/launchpad/+merge/381743
Added a functional page to LP that allows the user to enter
an OCI Project Name and create/edit the project.
This is protected by the OCI_PROJECT_ALLOW_CREATE feature flag
for now, until MP 381321 lands and we can move to
oci_project_admin permissions.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ilasc/launchpad:ui-create-oci-project into launchpad:master.
diff --git a/lib/lp/registry/browser/configure.zcml b/lib/lp/registry/browser/configure.zcml
index 5605a9f..ff35646 100644
--- a/lib/lp/registry/browser/configure.zcml
+++ b/lib/lp/registry/browser/configure.zcml
@@ -2149,6 +2149,13 @@
template="../templates/distribution-newmirror.pt">
</browser:page>
<browser:page
+ name="+newociproject"
+ for="lp.registry.interfaces.distribution.IDistribution"
+ class="lp.registry.browser.ociproject.OCIProjectAddView"
+ permission="launchpad.AnyPerson"
+ template="../templates/ociproject-new.pt"
+ />
+ <browser:page
name="+search"
for="lp.registry.interfaces.distribution.IDistribution"
class="lp.registry.browser.distribution.DistributionPackageSearchView"
diff --git a/lib/lp/registry/browser/distribution.py b/lib/lp/registry/browser/distribution.py
index a17ff8d..82e2de2 100644
--- a/lib/lp/registry/browser/distribution.py
+++ b/lib/lp/registry/browser/distribution.py
@@ -306,11 +306,15 @@ class DistributionNavigationMenu(NavigationMenu, DistributionLinksMixin):
def sharing(self):
return Link('+sharing', 'Sharing', icon='edit')
+ def newociproject(self):
+ text = 'Create an OCI Project'
+ return Link('+newociproject', text, icon='add')
+
@cachedproperty
def links(self):
return [
'edit', 'admin', 'pubconf', 'subscribe_to_bug_mail',
- 'edit_bug_mail', 'sharing']
+ 'edit_bug_mail', 'sharing', 'newociproject']
class DistributionOverviewMenu(ApplicationMenu, DistributionLinksMixin):
diff --git a/lib/lp/registry/browser/ociproject.py b/lib/lp/registry/browser/ociproject.py
index 5ee4205..f9136b9 100644
--- a/lib/lp/registry/browser/ociproject.py
+++ b/lib/lp/registry/browser/ociproject.py
@@ -20,15 +20,24 @@ from zope.interface import implementer
from lp.app.browser.launchpadform import (
action,
LaunchpadEditFormView,
+ LaunchpadFormView,
)
from lp.app.browser.tales import CustomizableFormatter
from lp.app.errors import NotFoundError
from lp.code.browser.vcslisting import TargetDefaultVCSNavigationMixin
from lp.oci.interfaces.ocirecipe import IOCIRecipeSet
+from lp.registry.errors import NoSuchOCIProjectName
from lp.registry.interfaces.ociproject import (
IOCIProject,
IOCIProjectSet,
+ OCI_PROJECT_ALLOW_CREATE,
+ OCIProjectCreateFeatureDisabled,
)
+from lp.registry.interfaces.ociprojectname import (
+ IOCIProjectName,
+ IOCIProjectNameSet,
+ )
+from lp.services.features import getFeatureFlag
from lp.services.webapp import (
canonical_url,
ContextMenu,
@@ -43,6 +52,41 @@ from lp.services.webapp.breadcrumb import Breadcrumb
from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
+class OCIProjectAddView(LaunchpadFormView):
+
+ schema = IOCIProjectName
+ field_names = ['name']
+
+ def initialize(self):
+ if not getFeatureFlag(OCI_PROJECT_ALLOW_CREATE):
+ raise OCIProjectCreateFeatureDisabled
+ super(OCIProjectAddView, self).initialize()
+
+ @action("Create Project", name="create")
+ def create_action(self, action, data):
+ """Create a new OCI Project."""
+ name = data.get('name')
+ try:
+ oci_project_name = getUtility(IOCIProjectNameSet).getByName(name)
+ except NoSuchOCIProjectName:
+ oci_project_name = getUtility(IOCIProjectNameSet).new(name)
+
+ oci_project = getUtility(IOCIProjectSet).getByDistributionAndName(
+ self.context, oci_project_name.name)
+
+ if oci_project:
+ self.setFieldError(
+ 'name',
+ 'There is already an OCI project in %s with this name.' % (
+ self.context.display_name))
+ else:
+ oci_project = getUtility(IOCIProjectSet).new(
+ registrant=self.user,
+ pillar=self.context,
+ name=oci_project_name)
+ self.next_url = canonical_url(oci_project)
+
+
class OCIProjectFormatterAPI(CustomizableFormatter):
"""Adapt `IOCIProject` objects to a formatted string."""
diff --git a/lib/lp/registry/browser/tests/test_ociproject.py b/lib/lp/registry/browser/tests/test_ociproject.py
index 57f1061..ceb82b7 100644
--- a/lib/lp/registry/browser/tests/test_ociproject.py
+++ b/lib/lp/registry/browser/tests/test_ociproject.py
@@ -12,7 +12,9 @@ from datetime import datetime
import pytz
+from lp.registry.interfaces.ociproject import OCI_PROJECT_ALLOW_CREATE
from lp.services.database.constants import UTC_NOW
+from lp.services.features.testing import FeatureFixture
from lp.services.webapp import canonical_url
from lp.services.webapp.escaping import structured
from lp.testing import (
@@ -150,3 +152,48 @@ class TestOCIProjectEditView(BrowserTestCase):
self.assertStartsWith(
extract_text(find_tags_by_class(browser.contents, "message")[1]),
"Invalid name 'invalid name'.")
+
+
+class TestOCIProjectAddView(BrowserTestCase):
+
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestOCIProjectAddView, self).setUp()
+ self.useFixture(FeatureFixture({OCI_PROJECT_ALLOW_CREATE: "on"}))
+
+ def test_create_oci_project(self):
+ oci_project = self.factory.makeOCIProject()
+ new_distribution = self.factory.makeDistribution(
+ owner=oci_project.pillar.owner)
+ browser = self.getViewBrowser(
+ new_distribution, view_name='+newociproject')
+ browser.getControl(name="field.name").value = "new-name"
+ browser.getControl("Create Project").click()
+
+ content = find_main_content(browser.contents)
+ self.assertEqual(
+ "OCI project new-name for %s" % new_distribution.display_name,
+ extract_text(content.h1))
+ self.assertThat(
+ "Distribution:\n%s\n" % (
+ new_distribution.display_name),
+ MatchesTagText(content, "distribution"))
+ self.assertThat(
+ "Name:\nnew-name\n",
+ MatchesTagText(content, "name"))
+
+ def test_create_oci_project_already_exists(self):
+ distribution = self.factory.makeDistribution()
+ self.factory.makeOCIProject(ociprojectname="new-name",
+ pillar=distribution)
+
+ browser = self.getViewBrowser(
+ distribution, view_name='+newociproject')
+ browser.getControl(name="field.name").value = "new-name"
+ browser.getControl("Create Project").click()
+
+ self.assertEqual(
+ "There is already an OCI project in %s with this name." % (
+ distribution.display_name),
+ extract_text(find_tags_by_class(browser.contents, "message")[1]))
diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py
index 8a3e734..e7567e7 100644
--- a/lib/lp/registry/interfaces/ociproject.py
+++ b/lib/lp/registry/interfaces/ociproject.py
@@ -9,11 +9,13 @@ __metaclass__ = type
__all__ = [
'IOCIProject',
'IOCIProjectSet',
- 'OCI_PROJECT_ALLOW_CREATE'
+ 'OCI_PROJECT_ALLOW_CREATE',
+ 'OCIProjectCreateFeatureDisabled'
]
from lazr.restful.declarations import (
call_with,
+ error_status,
export_as_webservice_entry,
export_factory_operation,
exported,
@@ -26,7 +28,7 @@ from lazr.restful.fields import (
Reference,
ReferenceChoice,
)
-from lp.app.validators.path import path_does_not_escape
+from six.moves import http_client
from zope.interface import Interface
from zope.schema import (
Bool,
@@ -35,9 +37,11 @@ from zope.schema import (
Text,
TextLine,
)
+from zope.security.interfaces import Unauthorized
from lp import _
from lp.app.validators.name import name_validator
+from lp.app.validators.path import path_does_not_escape
from lp.bugs.interfaces.bugtarget import IBugTarget
from lp.code.interfaces.gitref import IGitRef
from lp.code.interfaces.hasgitrepositories import IHasGitRepositories
@@ -170,3 +174,12 @@ class IOCIProjectSet(Interface):
def getByDistributionAndName(distribution, name):
"""Get the OCIProjects for a given distribution."""
+
+
+@error_status(http_client.UNAUTHORIZED)
+class OCIProjectCreateFeatureDisabled(Unauthorized):
+ """Only certain users can create new OCI Projects."""
+
+ def __init__(self):
+ super(OCIProjectCreateFeatureDisabled, self).__init__(
+ "You do not have permission to create an OCI Project.")
diff --git a/lib/lp/registry/templates/ociproject-new.pt b/lib/lp/registry/templates/ociproject-new.pt
new file mode 100644
index 0000000..321bf4b
--- /dev/null
+++ b/lib/lp/registry/templates/ociproject-new.pt
@@ -0,0 +1,15 @@
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ metal:use-macro="view/macro:page/main_only"
+ i18n:domain="launchpad">
+
+ <body>
+ <div metal:fill-slot="main">
+ <p>Enter the name for the new OCI Project.</p>
+ <div metal:use-macro="context/@@launchpad_form/form" />
+ </div>
+ </body>
+</html>
\ No newline at end of file