~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/soyuz/browser/archive.py

[r=benji][bug=795573,
 796233] On DistroSeries:+localpackagediffs ensure that the comment
 form is hidden after adding a new comment to a DistroSeriesDifference,
 prevent empty comments from being submitted,
 and add some animations and effects to make the UI less jarring and easier to
 follow.

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
    datetime,
34
34
    timedelta,
35
35
    )
36
 
from urlparse import urlparse
37
36
 
38
37
import pytz
39
38
from sqlobject import SQLObjectNotFound
138
137
    PackagePublishingStatus,
139
138
    )
140
139
from lp.soyuz.interfaces.archive import (
 
140
    ArchiveDependencyError,
141
141
    CannotCopy,
142
142
    IArchive,
143
143
    IArchiveEditDependenciesForm,
144
144
    IArchiveSet,
145
145
    NoSuchPPA,
 
146
    validate_external_dependencies,
146
147
    )
147
148
from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
148
149
from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriberSet
1357
1358
    def do_copy(self, sources_field_name, source_pubs, dest_archive,
1358
1359
                dest_series, dest_pocket, include_binaries,
1359
1360
                dest_url=None, dest_display_name=None, person=None,
1360
 
                check_permissions=True):
 
1361
                check_permissions=True, force_async=False):
1361
1362
        """Copy packages and add appropriate feedback to the browser page.
1362
1363
 
1363
1364
        This may either copy synchronously, if there are few enough
1381
1382
        :param person: The person requesting the copy.
1382
1383
        :param: check_permissions: boolean indicating whether or not the
1383
1384
            requester's permissions to copy should be checked.
 
1385
        :param force_async: Force the copy to create package copy jobs and
 
1386
            perform the copy asynchronously.
1384
1387
 
1385
1388
        :return: True if the copying worked, False otherwise.
1386
1389
        """
1387
1390
        try:
1388
 
            if self.canCopySynchronously(source_pubs):
 
1391
            if (force_async == False and
 
1392
                    self.canCopySynchronously(source_pubs)):
1389
1393
                notification = copy_synchronously(
1390
1394
                    source_pubs, dest_archive, dest_series, dest_pocket,
1391
1395
                    include_binaries, dest_url=dest_url,
1861
1865
        self._messages.append(structured(
1862
1866
            '<p>Primary dependency added: %s</p>', primary_dependency.title))
1863
1867
 
1864
 
    def validate(self, data):
1865
 
        """Validate dependency configuration changes.
1866
 
 
1867
 
        Skip checks if no dependency candidate was sent in the form.
1868
 
 
1869
 
        Validate if the requested PPA dependency is sane (different than
1870
 
        the context PPA and not yet registered).
1871
 
 
1872
 
        Also check if the dependency candidate is private, if so, it can
1873
 
        only be set if the user has 'launchpad.View' permission on it and
1874
 
        the context PPA is also private (this way P3A credentials will be
1875
 
        sanitized from buildlogs).
1876
 
        """
1877
 
        dependency_candidate = data.get('dependency_candidate')
1878
 
 
1879
 
        if dependency_candidate is None:
1880
 
            return
1881
 
 
1882
 
        if dependency_candidate == self.context:
1883
 
            self.setFieldError('dependency_candidate',
1884
 
                               "An archive should not depend on itself.")
1885
 
            return
1886
 
 
1887
 
        if self.context.getArchiveDependency(dependency_candidate):
1888
 
            self.setFieldError('dependency_candidate',
1889
 
                               "This dependency is already registered.")
1890
 
            return
1891
 
 
1892
 
        if not check_permission('launchpad.View', dependency_candidate):
1893
 
            self.setFieldError(
1894
 
                'dependency_candidate',
1895
 
                "You don't have permission to use this dependency.")
1896
 
            return
1897
 
 
1898
 
        if dependency_candidate.private and not self.context.private:
1899
 
            self.setFieldError(
1900
 
                'dependency_candidate',
1901
 
                "Public PPAs cannot depend on private ones.")
1902
 
 
1903
1868
    @action(_("Save"), name="save")
1904
1869
    def save_action(self, action, data):
1905
1870
        """Save dependency configuration changes.
1911
1876
        refreshing. And render a page notification with the summary of the
1912
1877
        changes made.
1913
1878
        """
1914
 
        # Redirect after POST.
1915
 
        self.next_url = self.request.URL
1916
 
 
1917
1879
        # Process the form.
1918
1880
        self._add_primary_dependencies(data)
1919
 
        self._add_ppa_dependencies(data)
 
1881
        try:
 
1882
            self._add_ppa_dependencies(data)
 
1883
        except ArchiveDependencyError as e:
 
1884
            self.setFieldError('dependency_candidate', str(e))
 
1885
            return
1920
1886
        self._remove_dependencies(data)
1921
1887
 
1922
1888
        # Issue a notification if anything was changed.
1923
1889
        if len(self.messages) > 0:
1924
1890
            self.request.response.addNotification(
1925
1891
                structured(self.messages))
 
1892
        # Redirect after POST.
 
1893
        self.next_url = self.request.URL
1926
1894
 
1927
1895
 
1928
1896
class ArchiveActivateView(LaunchpadFormView):
2125
2093
        # Check the external_dependencies field.
2126
2094
        ext_deps = data.get('external_dependencies')
2127
2095
        if ext_deps is not None:
2128
 
            errors = self.validate_external_dependencies(ext_deps)
 
2096
            errors = validate_external_dependencies(ext_deps)
2129
2097
            if len(errors) != 0:
2130
2098
                error_text = "\n".join(errors)
2131
2099
                self.setFieldError('external_dependencies', error_text)
2135
2103
                'commercial',
2136
2104
                'Can only set commericial for private archives.')
2137
2105
 
2138
 
    def validate_external_dependencies(self, ext_deps):
2139
 
        """Validate the external_dependencies field.
2140
 
 
2141
 
        :param ext_deps: The dependencies form field to check.
2142
 
        """
2143
 
        errors = []
2144
 
        # The field can consist of multiple entries separated by
2145
 
        # newlines, so process each in turn.
2146
 
        for dep in ext_deps.splitlines():
2147
 
            try:
2148
 
                deb, url, suite, components = dep.split(" ", 3)
2149
 
            except ValueError:
2150
 
                errors.append(
2151
 
                    "'%s' is not a complete and valid sources.list entry"
2152
 
                        % dep)
2153
 
                continue
2154
 
 
2155
 
            if deb != "deb":
2156
 
                errors.append("%s: Must start with 'deb'" % dep)
2157
 
            url_components = urlparse(url)
2158
 
            if not url_components[0] or not url_components[1]:
2159
 
                errors.append("%s: Invalid URL" % dep)
2160
 
 
2161
 
        return errors
2162
 
 
2163
2106
    @property
2164
2107
    def owner_is_private_team(self):
2165
2108
        """Is the owner a private team?