Пример #1
0
class ISeriesMixin(IHasDrivers):
    """Methods & properties shared between distro & product series."""

    active = exported(Bool(
        title=_("Active"),
        description=_(
            "Whether or not this series is stable and supported, or "
            "under current development. This excludes series which "
            "are experimental or obsolete.")))

    summary = exported(
        Summary(title=_("Summary"),
             description=_('A single paragraph that explains the goals of '
                           'of this series and the intended users. '
                           'For example: "The 2.0 series of Apache '
                           'represents the current stable series, '
                           'and is recommended for all new deployments".'),
             required=True))

    drivers = exported(
        CollectionField(
            title=_(
                'A list of the people or teams who are drivers for this '
                'series. This list is made up of any drivers or owners '
                'from this series and the parent drivers.'),
            readonly=True,
            value_type=Reference(schema=IPerson)))

    bug_supervisor = CollectionField(
        title=_('Currently just a reference to the parent bug '
                'supervisor.'),
        readonly=True,
        value_type=Reference(schema=IPerson))
Пример #2
0
class AddAnnouncementForm(Interface):
    """Form definition for the view which creates new Announcements."""

    title = Title(title=_('Headline'), required=True)
    summary = Summary(title=_('Summary'), required=True)
    url = TextLine(title=_('URL'),
                   required=False,
                   constraint=valid_webref,
                   description=_("The web location of your announcement."))
    publication_date = AnnouncementDate(title=_('Date'), required=True)
Пример #3
0
class IAddBugTaskWithProductCreationForm(ILinkPackaging):

    bug_url = StrippedTextLine(
        title=_('Bug URL'), required=True, constraint=valid_remote_bug_url,
        description=_("The URL of this bug in the remote bug tracker."))
    displayname = TextLine(title=_('Project name'))
    name = ProductNameField(
        title=_('Project ID'), constraint=name_validator, required=True,
        description=_(
            "A short name starting with a lowercase letter or number, "
            "followed by letters, dots, hyphens or plusses. e.g. firefox, "
            "linux, gnome-terminal."))
    summary = Summary(title=_('Project summary'), required=True)
Пример #4
0
class IProductView(
    ICanGetMilestonesDirectly, IHasAppointedDriver, IHasBranches,
    IHasExternalBugTracker,
    IHasMergeProposals, IHasMilestones, IHasExpirableBugs,
    IHasMugshot, IHasSprints, IHasTranslationImports,
    ITranslationPolicy, IKarmaContext, IMakesAnnouncements,
    IOfficialBugTagTargetPublic, IHasOOPSReferences,
    IHasRecipes, IHasCodeImports, IServiceUsage, IHasGitRepositories):
    """Public IProduct properties."""

    registrant = exported(
        PublicPersonChoice(
            title=_('Registrant'),
            required=True,
            readonly=True,
            vocabulary='ValidPersonOrTeam',
            description=_("This person registered the project in "
                          "Launchpad.")))

    driver = exported(
        PersonChoice(
            title=_("Driver"),
            description=_(
                "This person or team will be able to set feature goals for "
                "and approve bug targeting or backporting for ANY major "
                "series in this project. You might want to leave this blank "
                "and just appoint a team for each specific series, rather "
                "than having one project team that does it all."),
            required=False, vocabulary='ValidPersonOrTeam'))

    summary = exported(
        Summary(
            title=_('Summary'),
            description=_(
                "A short paragraph to introduce the project's work.")))

    description = exported(
        Description(
            title=_('Description'),
            required=False,
            description=_(
                "Details about the project's work, highlights, goals, and "
                "how to contribute. Use plain text, paragraphs are preserved "
                "and URLs are linked in pages. Don't repeat the Summary.")))

    datecreated = exported(
        Datetime(
            title=_('Date Created'),
            required=True, readonly=True,
            description=_("The date this project was created in Launchpad.")),
        exported_as='date_created')

    homepageurl = exported(
        URIField(
            title=_('Homepage URL'),
            required=False,
            allowed_schemes=['http', 'https', 'ftp'], allow_userinfo=False,
            description=_("""The project home page. Please include
                the http://""")),
        exported_as="homepage_url")

    wikiurl = exported(
        URIField(
            title=_('Wiki URL'),
            required=False,
            allowed_schemes=['http', 'https', 'ftp'], allow_userinfo=False,
            description=_("""The full URL of this project's wiki, if it has
                one. Please include the http://""")),
        exported_as='wiki_url')

    screenshotsurl = exported(
        URIField(
            title=_('Screenshots URL'),
            required=False,
            allowed_schemes=['http', 'https', 'ftp'], allow_userinfo=False,
            description=_("""The full URL for screenshots of this project,
                if available. Please include the http://""")),
        exported_as='screenshots_url')

    downloadurl = exported(
        URIField(
            title=_('Download URL'),
            required=False,
            allowed_schemes=['http', 'https', 'ftp'], allow_userinfo=False,
            description=_("""The full URL where downloads for this project
                are located, if available. Please include the http://""")),
        exported_as='download_url')

    programminglang = exported(
        TextLine(
            title=_('Programming Languages'),
            required=False,
            description=_("""A comma delimited list of programming
                languages used for this project.""")),
        exported_as='programming_language')

    sourceforgeproject = exported(
        TextLine(
            title=_('Sourceforge Project'),
            required=False,
            constraint=sourceforge_project_name_validator,
            description=_("""The SourceForge project name for
                this project, if it is in sourceforge.""")),
        exported_as='sourceforge_project')

    freshmeatproject = exported(
        TextLine(
            title=_('Freshmeat Project'),
            required=False, description=_("""The Freshmeat project name for
                this project, if it is in freshmeat. [DEPRECATED]""")),
        exported_as='freshmeat_project')

    homepage_content = Text(
        title=_("Homepage Content"), required=False,
        description=_(
            "The content of this project's home page. Edit this and it will "
            "be displayed for all the world to see. It is NOT a wiki "
            "so you cannot undo changes."))

    mugshot = exported(
        MugshotImageUpload(
            title=_("Brand"), required=False,
            default_image_resource='/@@/product-mugshot',
            description=_(
                "A large image of exactly 192x192 pixels, that will be "
                "displayed on this project's home page in Launchpad. It "
                "should be no bigger than 100kb in size.")),
        exported_as='brand')

    autoupdate = Bool(
        title=_('Automatic update'),
        description=_("Whether or not this project's attributes are "
                      "updated automatically."))

    private_bugs = exported(
        Bool(
            title=_('Private bugs (obsolete; always False)'), readonly=True,
            description=_("Replaced by bug_sharing_policy.")),
        ('devel', dict(exported=False)))

    branch_sharing_policy = exported(Choice(
        title=_('Branch sharing policy'),
        description=_("Sharing policy for this project's branches."),
        required=True, readonly=True, vocabulary=BranchSharingPolicy),
        as_of='devel')
    bug_sharing_policy = exported(Choice(
        title=_('Bug sharing policy'),
        description=_("Sharing policy for this project's bugs."),
        required=True, readonly=True, vocabulary=BugSharingPolicy),
        as_of='devel')
    specification_sharing_policy = exported(Choice(
        title=_('Blueprint sharing policy'),
        description=_("Sharing policy for this project's specifications."),
        required=True, readonly=True, vocabulary=SpecificationSharingPolicy),
        as_of='devel')

    licenses = exported(
        Set(title=_('Licences'),
            value_type=Choice(vocabulary=License)))

    license_info = exported(
        Description(
            title=_('Description of additional licences'),
            required=False,
            description=_(
                "Description of licences that do not appear in the list "
                "above.")))

    bugtracker = exported(
        ProductBugTracker(
            title=_('Bugs are tracked'),
            vocabulary="BugTracker"),
        exported_as='bug_tracker')

    sourcepackages = Attribute(_("List of packages for this product"))

    date_next_suggest_packaging = exported(
        Datetime(
            title=_('Next suggest packaging date'),
            description=_(
                "Obsolete. The date to resume Ubuntu package suggestions."),
            required=False),
        ('devel', dict(exported=False)))

    distrosourcepackages = Attribute(_("List of distribution packages for "
        "this product"))

    ubuntu_packages = Attribute(
        _("List of distribution packages for this product in Ubuntu"))

    series = exported(
        doNotSnapshot(
            CollectionField(value_type=Object(schema=IProductSeries))))

    development_focus = exported(
        ReferenceChoice(
            title=_('Development focus'), required=True,
            vocabulary='FilteredProductSeries',
            schema=IProductSeries,
            description=_(
                'The series that represents the master or trunk branch. '
                'The Bazaar URL lp:<project> points to the development focus '
                'series branch.')))
    development_focusID = Attribute("The development focus ID.")

    releases = exported(
        doNotSnapshot(
            CollectionField(
                title=_("An iterator over the ProductReleases for "
                        "this product."),
                readonly=True,
                value_type=Reference(schema=IProductRelease))))

    translation_focus = exported(
        ReferenceChoice(
            title=_("Translation focus"), required=False,
            vocabulary='FilteredProductSeries',
            schema=IProductSeries,
            description=_(
                'Project series that translators should focus on.')))

    translatable_packages = Attribute(
        "A list of the source packages for this product that can be "
        "translated sorted by distroseries.name and sourcepackage.name.")

    translatable_series = Attribute(
        "A list of the series of this product for which we have translation "
        "templates.")

    obsolete_translatable_series = Attribute("""
        A list of the series of this product with obsolete translation
        templates.""")

    primary_translatable = Attribute(
        "The best guess we have for what new translators will want to "
        "translate for a given product: the latest series for which we have "
        "templates, and failing that, an Ubuntu package.")

    translationgroups = Attribute("The list of applicable translation "
        "groups for a product. There can be several: one from the product, "
        "and potentially one from the project, too.")

    commercial_subscription = exported(
        Reference(
            ICommercialSubscription,
            title=_("Commercial subscriptions"),
            description=_(
                "An object which contains the timeframe and the voucher "
                "code of a subscription.")))

    commercial_subscription_is_due = exported(
            Bool(
                title=_("Commercial subscription is due"),
                readonly=True,
                description=_(
                    "Whether the project's licensing requires a new "
                    "commercial subscription to use launchpad.")))

    has_current_commercial_subscription = Attribute("""
        Whether the project has a current commercial subscription.""")

    license_status = Attribute("""
        Whether the licence is OPENSOURCE, UNREVIEWED, or PROPRIETARY.""")

    remote_product = exported(
        TextLine(
            title=_('Remote bug tracker project id'), required=False,
            description=_(
                "Some bug trackers host multiple projects at the same URL "
                "and require an identifier for the specific project.")))

    active_or_packaged_series = Attribute(
        _("Series that are active and/or have been packaged."))

    packagings = Attribute(_("All the packagings for the project."))

    security_contact = exported(
        TextLine(
            title=_('Security contact'), required=False, readonly=True,
            description=_('Security contact (obsolete; always None)')),
            ('devel', dict(exported=False)), as_of='1.0')

    vcs = exported(
        Choice(
            title=_("VCS"),
            required=False,
            vocabulary=VCSType,
            description=_(
                "Version control system for this project's code.")))

    inferred_vcs = Choice(
        title=_("Inferred VCS"),
        readonly=True,
        vocabulary=VCSType,
        description=_(
            "Inferred version control system for this project's code."))

    def getAllowedBugInformationTypes():
        """Get the information types that a bug in this project can have.

        :return: A sequence of `InformationType`s.
        """

    def getDefaultBugInformationType():
        """Get the default information type of a new bug in this project.

        :return: The `InformationType`.
        """

    def getVersionSortedSeries(statuses=None, filter_statuses=None):
        """Return all the series sorted by the name field as a version.

        The development focus field is an exception. It will always
        be sorted first.

        :param statuses: If statuses is not None, only include series
                         which are in the given statuses.
        :param filter_statuses: Filter out any series with statuses listed in
                                filter_statuses.
        """

    def redeemSubscriptionVoucher(voucher, registrant, purchaser,
                                  subscription_months, whiteboard=None,
                                  current_datetime=None):
        """Redeem a voucher and extend the subscription expiration date.

        The voucher must have already been verified to be redeemable.
        :param voucher: The voucher id as tracked in the external system.
        :param registrant: Who is redeeming the voucher.
        :param purchaser: Who purchased the voucher.  May not be known.
        :param subscription_months: integer indicating the number of months
            the voucher is for.
        :param whiteboard: Notes for this activity.
        :param current_datetime: Current time.  Will be datetime.now() if not
            specified.
        :return: None
        """

    def getPackage(distroseries):
        """Return a package in that distroseries for this product."""

    @operation_parameters(
        name=TextLine(title=_("Name"), required=True))
    @operation_returns_entry(IProductSeries)
    @export_read_operation()
    def getSeries(name):
        """Return the series for this product for the given name, or None."""

    @operation_parameters(
        version=TextLine(title=_("Version"), required=True))
    @operation_returns_entry(IProductRelease)
    @export_read_operation()
    def getRelease(version):
        """Return the release for this product that has the version given."""

    def getMilestonesAndReleases():
        """Return all the milestones and releases for this product."""

    def packagedInDistros():
        """Returns the distributions this product has been packaged in."""

    def userCanEdit(user):
        """Can the user edit this product?"""

    def getLinkedBugWatches():
        """Return all the bug watches that are linked to this Product.

        Being linked, means that a bug watch having the same bug tracker
        as this Product is using, is linked to a bug task targeted to
        this Product.
        """

    @operation_parameters(
        include_inactive=Bool(title=_("Include inactive"),
                              required=False, default=False))
    @export_read_operation()
    @export_operation_as('get_timeline')
    def getTimeline(include_inactive):
        """Return basic timeline data useful for creating a diagram.
Пример #5
0
class ITranslationGroup(IHasOwner):
    """A TranslationGroup."""

    export_as_webservice_entry(
        singular_name='translation_group', plural_name='translation_groups')

    id = Int(
            title=_('Translation Group ID'), required=True, readonly=True,
            )
    name = exported(
        TextLine(
            title=_('Name'), required=True,
            description=_("""Keep this name very short, unique, and
            descriptive, because it will be used in URLs. Examples:
            gnome-translation-project, ubuntu-translators."""),
            constraint=name_validator),
        as_of="devel"
        )
    title = exported(
        Title(
            title=_('Title'), required=True,
            description=_("""Title of this Translation Group.
            This title is displayed at the top of the Translation Group
            page and in lists or reports of translation groups.  Do not
            add "translation group" to this title, or it will be shown
            double.
            """),),
        as_of="devel"
        )
    summary = Summary(
            title=_('Summary'), required=True,
            description=_("""A single-paragraph description of the
            group. This will also be displayed in most
            translation group listings."""),
            )
    datecreated = Datetime(
            title=_('Date Created'), required=True, readonly=True,
            )
    owner = PublicPersonChoice(
            title=_('Owner'), required=True, vocabulary='ValidOwner',
            description=_("The owner's IPerson"))
    # joins
    translators = Attribute('The set of translators for this group.')
    projects = Attribute('The projects for which this group translates.')
    products = Attribute('The projects to which this group is directly '
        'appointed as a translator. There may be other projects that are '
        'part of project groups for which the group also translates.')
    distributions = Attribute('The distros for which this group translates.')

    translation_guide_url = URIField(
        title=_('Translation instructions'), required=False,
        allowed_schemes=['http', 'https', 'ftp'],
        allow_userinfo=False,
        description=_("The URL of the generic translation instructions "
                      "followed by this particular translation group. "
                      "This should include team policies and "
                      "recommendations, specific instructions for "
                      "any non-standard behaviour and other documentation."
                      "Can be any of http://, https://, or ftp://."))

    # accessing the translator list
    def query_translator(language):
        """Retrieve a translator, or None, based on a Language"""

    # adding and removing translators
    def remove_translator(language):
        """Remove the translator for this language from the group."""

    # used for the form machinery
    def add(content):
        """Add a new object."""

    top_projects = Attribute(
        "Most relevant projects using this translation group.")

    number_of_remaining_projects = Attribute(
        "Count of remaining projects not listed in the `top_projects`.")

    def __getitem__(language_code):
        """Retrieve the translator for the given language in this group.

        This is used for navigation through the group.
        """

    def fetchTranslatorData():
        """Fetch translators and related data.

        Prefetches display-related properties.

        :return: A result set of (`Translator`, `Language`, `Person`),
            ordered by language name in English.
        """

    def fetchProjectsForDisplay(user):
        """Fetch `Product`s using this group, for display purposes.

        Prefetches display-related properties.

        :return: A result set of `Product`, ordered by display name.
        """

    def fetchProjectGroupsForDisplay():
        """Fetch `Project`s using this group, for display purposes.

        Prefetches display-related properties.

        :return: A result set of `Project`, ordered by display name.
        """

    def fetchDistrosForDisplay():
        """Fetch `Distribution`s using this group, for display purposes.
class IBranchMergeProposalView(Interface):

    registrant = exported(
        PublicPersonChoice(
            title=_('Person'),
            required=True,
            vocabulary='ValidPersonOrTeam',
            readonly=True,
            description=_('The person who registered the merge proposal.')))

    description = exported(
        Text(title=_('Description'),
             required=False,
             description=_(
                 "A detailed description of the changes that are being "
                 "addressed by the branch being proposed to be merged."),
             max_length=50000))

    whiteboard = Whiteboard(title=_('Whiteboard'),
                            required=False,
                            description=_('Notes about the merge.'))

    queue_status = exported(
        Choice(title=_('Status'),
               vocabulary=BranchMergeProposalStatus,
               required=True,
               readonly=True,
               description=_("The current state of the proposal.")))

    # Not to be confused with a code reviewer. A code reviewer is someone who
    # can vote or has voted on a proposal.
    reviewer = exported(
        PersonChoice(title=_('Review person or team'),
                     required=False,
                     readonly=True,
                     vocabulary='ValidPersonOrTeam',
                     description=_(
                         "The person that accepted (or rejected) the code "
                         "for merging.")))

    next_preview_diff_job = Attribute(
        'The next BranchMergeProposalJob that will update a preview diff.')

    preview_diffs = Attribute('All preview diffs for this merge proposal.')

    preview_diff = exported(
        Reference(IPreviewDiff,
                  title=_('The current diff of the source branch against the '
                          'target branch.'),
                  readonly=True))

    reviewed_revision_id = exported(Text(
        title=_("The revision id that has been approved by the reviewer.")),
                                    exported_as='reviewed_revid')

    commit_message = exported(
        Summary(title=_("Commit Message"),
                required=False,
                description=_("The commit message that should be used when "
                              "merging the source branch."),
                strip_text=True))

    queue_position = exported(
        Int(title=_("Queue Position"),
            required=False,
            readonly=True,
            description=_("The position in the queue.")))

    queuer = exported(
        PublicPersonChoice(
            title=_('Queuer'),
            vocabulary='ValidPerson',
            required=False,
            readonly=True,
            description=_("The person that queued up the branch.")))

    queued_revision_id = exported(Text(
        title=_("Queued Revision ID"),
        readonly=True,
        required=False,
        description=_("The revision id that has been queued for "
                      "landing.")),
                                  exported_as='queued_revid')

    merged_revno = exported(
        Int(title=_("Merged Revision Number"),
            required=False,
            readonly=True,
            description=_("The revision number on the target branch which "
                          "contains the merge from the source branch.")))

    date_merged = exported(
        Datetime(title=_('Date Merged'),
                 required=False,
                 readonly=True,
                 description=_(
                     "The date that the source branch was merged into "
                     "the target branch")))

    title = Attribute(
        "A nice human readable name to describe the merge proposal. "
        "This is generated from the source and target branch, and used "
        "as the tal fmt:link text and for email subjects.")

    merge_reporter = exported(
        PublicPersonChoice(
            title=_("Merge Reporter"),
            vocabulary="ValidPerson",
            required=False,
            readonly=True,
            description=_("The user that marked the branch as merged.")))

    supersedes = exported(
        Reference(title=_("Supersedes"),
                  schema=Interface,
                  required=False,
                  readonly=True,
                  description=_("The branch merge proposal that this one "
                                "supersedes.")))
    superseded_by = exported(
        Reference(title=_("Superseded By"),
                  schema=Interface,
                  required=False,
                  readonly=True,
                  description=_(
                      "The branch merge proposal that supersedes this one.")))

    date_created = exported(
        Datetime(title=_('Date Created'), required=True, readonly=True))
    date_review_requested = exported(
        Datetime(title=_('Date Review Requested'),
                 required=False,
                 readonly=True))
    date_reviewed = exported(
        Datetime(title=_('Date Reviewed'), required=False, readonly=True))
    date_queued = exported(
        Datetime(title=_('Date Queued'), required=False, readonly=True))
    root_message_id = Text(
        title=_('The email message id from the first message'), required=False)
    all_comments = exported(
        CollectionField(
            title=_("All messages discussing this merge proposal"),
            # Really ICodeReviewComment.
            value_type=Reference(schema=Interface),
            readonly=True))

    address = exported(
        TextLine(title=_('The email address for this proposal.'),
                 readonly=True,
                 description=_('Any emails sent to this address will result'
                               'in comments being added.')))

    revision_end_date = Datetime(title=_('Cutoff date for showing revisions.'),
                                 required=False,
                                 readonly=True)

    @operation_parameters(id=Int(title=_("A CodeReviewComment ID.")))
    # Really ICodeReviewComment.
    @operation_returns_entry(Interface)
    @export_read_operation()
    def getComment(id):
        """Return the CodeReviewComment with the specified ID."""

    @call_with(user=REQUEST_USER)
    # Really IBugTask.
    @operation_returns_collection_of(Interface)
    @export_read_operation()
    @operation_for_version('devel')
    def getRelatedBugTasks(user):
        """Return the Bug tasks related to this merge proposal."""

    def getRevisionsSinceReviewStart():
        """Return all the revisions added since the review began.

        Revisions are grouped by creation (i.e. push) time.
        :return: An iterator of (date, iterator of revision data)
        """

    def getVoteReference(id):
        """Return the CodeReviewVoteReference with the specified ID."""

    def getNotificationRecipients(min_level):
        """Return the people who should be notified.

        Recipients will be returned as a dictionary where the key is the
        person, and the values are (subscription, rationale) tuples.

        :param min_level: The minimum notification level needed to be
            notified.
        """

    votes = exported(
        CollectionField(
            title=_('The votes cast or expected for this proposal'),
            # Really ICodeReviewVoteReference.
            value_type=Reference(schema=Interface),
            readonly=True))

    def isValidTransition(next_state, user=None):
        """True if it is valid for user update the proposal to next_state."""

    def isMergable():
        """Is the proposal in a state that allows it to being merged?

        As long as the proposal isn't in one of the end states, it is valid
        to be merged.
        """

    def getUnlandedSourceBranchRevisions():
        """Return a sequence of `BranchRevision` objects.

        Returns up to 10 revisions that are in the revision history for the
        source branch that are not in the revision history of the target
        branch.  These are the revisions that have been committed to the
        source branch since it branched off the target branch.
        """

    def getUsersVoteReference(user):
        """Get the existing vote reference for the given user.

        :return: A `CodeReviewVoteReference` or None.
        """

    def generateIncrementalDiff(old_revision, new_revision, diff=None):
        """Generate an incremental diff for the merge proposal.

        :param old_revision: The `Revision` to generate the diff from.
        :param new_revision: The `Revision` to generate the diff to.
        :param diff: If supplied, a pregenerated `Diff`.
        """

    def getIncrementalDiffs(revision_list):
        """Return a list of diffs for the specified revisions.
Пример #7
0
class IProjectGroupPublic(ICanGetMilestonesDirectly, IHasAppointedDriver,
                          IHasBranches, IHasBugs, IHasDrivers, IHasIcon,
                          IHasLogo, IHasMergeProposals, IHasMilestones,
                          IHasMugshot, IHasOwner, IHasSpecifications,
                          IHasSprints, IMakesAnnouncements, IKarmaContext,
                          IHasOfficialBugTags, IServiceUsage):
    """Public IProjectGroup properties."""

    id = Int(title=_('ID'), readonly=True)

    # The following milestone collections are copied from IHasMilestone so that
    # we can override the collection value types to be IProjectGroupMilestone.
    milestones = copy_field(
        IHasMilestones['milestones'],
        value_type=Reference(schema=IProjectGroupMilestone))

    all_milestones = copy_field(
        IHasMilestones['all_milestones'],
        value_type=Reference(schema=IProjectGroupMilestone))

    owner = exported(
        PublicPersonChoice(
            title=_('Maintainer'),
            required=True,
            vocabulary='ValidPillarOwner',
            description=_("The restricted team, moderated team, or person "
                          "who maintains the project group information in "
                          "Launchpad.")))

    registrant = exported(
        PublicPersonChoice(title=_('Registrant'),
                           required=True,
                           readonly=True,
                           vocabulary='ValidPersonOrTeam',
                           description=_(
                               "Project group registrant. Must be a valid "
                               "Launchpad Person.")))

    display_name = exported(
        TextLine(title=_('Display Name'),
                 description=_(
                     "Appropriately capitalised, "
                     'and typically ending in "Project". '
                     "Examples: the Apache Project, the Mozilla Project, "
                     "the Gimp Project.")))

    displayname = Attribute('Display name (deprecated)')

    title = exported(
        Title(title=_('Title'),
              description=_("The full name of the project group, "
                            "which can contain spaces, special characters, "
                            "etc.")))

    summary = exported(
        Summary(
            title=_('Project Group Summary'),
            description=_(
                "A short paragraph to introduce the project group's work.")))

    description = exported(
        Text(title=_('Description'),
             description=_(
                 "Details about the project group's work, goals, and "
                 "how to contribute. Use plain text, paragraphs are preserved "
                 "and URLs are linked in pages. Don't repeat the Summary.")))

    datecreated = exported(Datetime(
        title=_('Date Created'),
        description=_("The date this project group was created in Launchpad."),
        readonly=True),
                           exported_as="date_created")

    driver = exported(
        PublicPersonChoice(
            title=_("Driver"),
            description=_(
                "This is a project group-wide appointment. Think carefully "
                "here! This person or team will be able to set feature goals "
                "and approve bug targeting and backporting for ANY series in "
                "ANY project in this group. You can also appoint drivers "
                "at the level of a specific project or series. So you may "
                "just want to leave this space blank, and instead let the "
                "individual projects and series have drivers."),
            required=False,
            vocabulary='ValidPersonOrTeam'))

    homepageurl = exported(URIField(title=_('Homepage URL'),
                                    required=False,
                                    allowed_schemes=['http', 'https', 'ftp'],
                                    allow_userinfo=False,
                                    description=_(
                                        "The project group home page. "
                                        "Please include the http://")),
                           exported_as="homepage_url")

    wikiurl = exported(URIField(
        title=_('Wiki URL'),
        required=False,
        allowed_schemes=['http', 'https', 'ftp'],
        allow_userinfo=False,
        description=_("The URL of this project group's wiki, "
                      "if it has one. Please include the http://")),
                       exported_as="wiki_url")

    lastdoap = TextLine(title=_('Last-parsed RDF fragment'),
                        description=_("The last RDF fragment for this "
                                      "entity that we received and parsed, or "
                                      "generated."),
                        required=False)

    sourceforgeproject = exported(TextLine(
        title=_("SourceForge Project Name"),
        description=_("The SourceForge project name for this "
                      "project group, if it is in SourceForge."),
        required=False),
                                  exported_as="sourceforge_project")

    freshmeatproject = exported(TextLine(
        title=_("Freshmeat Project Name"),
        description=_("The Freshmeat project name for this "
                      "project group, if it is in Freshmeat. "
                      "[DEPRECATED]"),
        required=False),
                                exported_as="freshmeat_project")

    homepage_content = exported(
        Text(title=_("Homepage Content"),
             required=False,
             description=_(
                 "The content of this project group's home page. Edit this "
                 "and it will be displayed for all the world to see. It is "
                 "NOT a wiki so you cannot undo changes.")))

    icon = exported(
        IconImageUpload(
            title=_("Icon"),
            required=False,
            default_image_resource='/@@/project',
            description=_(
                "A small image of exactly 14x14 pixels and at most 5kb in "
                "size, that can be used to identify this project group. The "
                "icon will be displayed in Launchpad everywhere that we link "
                "to this project group. For example in listings or tables of "
                "active project groups.")))

    logo = exported(
        LogoImageUpload(
            title=_("Logo"),
            required=False,
            default_image_resource='/@@/project-logo',
            description=_(
                "An image of exactly 64x64 pixels that will be displayed in "
                "the heading of all pages related to this project group. It "
                "should be no bigger than 50kb in size.")))

    mugshot = exported(
        MugshotImageUpload(
            title=_("Brand"),
            required=False,
            default_image_resource='/@@/project-mugshot',
            description=_(
                "A large image of exactly 192x192 pixels, that will be "
                "displayed on this project group's home page in Launchpad. "
                "It should be no bigger than 100kb in size. ")))

    bugtracker = exported(ReferenceChoice(
        title=_('Bug Tracker'),
        required=False,
        vocabulary='BugTracker',
        schema=IBugTracker,
        description=_(
            "The bug tracker the projects in this project group use.")),
                          exported_as="bug_tracker")

    # products.value_type will be set to IProduct once IProduct is defined.
    products = exported(CollectionField(
        title=_('List of active projects for this project group.'),
        value_type=Reference(Interface)),
                        exported_as="projects")

    bug_reporting_guidelines = exported(
        Text(title=(u"If I\N{right single quotation mark}m reporting a bug, "
                    u"I should include, if possible"),
             description=(u"These guidelines will be shown to "
                          "anyone reporting a bug."),
             required=False,
             max_length=50000))

    bug_reported_acknowledgement = exported(
        Text(title=(u"After reporting a bug, I can expect the following."),
             description=(u"This message of acknowledgement will be displayed "
                          "to anyone after reporting a bug."),
             required=False,
             max_length=50000))

    enable_bugfiling_duplicate_search = Bool(
        title=u"Search for possible duplicate bugs when a new bug is filed",
        required=False,
        readonly=True)

    translatables = Attribute("Products that are translatable in LP")

    def getProduct(name):
        """Get a product with name `name`."""

    def getConfigurableProducts():
        """Get all products that can be edited by user."""

    def has_translatable():
        """Return a boolean showing the existance of translatables products.
        """

    def has_branches():
        """Return a boolean showing the existance of products with branches.
        """

    def hasProducts():
        """Returns True if a project has products associated with it, False
        otherwise.
        """

    def getSeries(series_name):
        """Return a ProjectGroupSeries object with name `series_name`."""

    product_milestones = Attribute('all the milestones for all the products.')
Пример #8
0
class IDistributionPublic(
    IBugTarget, ICanGetMilestonesDirectly, IHasAppointedDriver,
    IHasBuildRecords, IHasDrivers, IHasMilestones, IHasSharingPolicies,
    IHasOOPSReferences, IHasOwner, IHasSprints, IHasTranslationImports,
    ITranslationPolicy, IKarmaContext, ILaunchpadUsage, IMakesAnnouncements,
    IOfficialBugTagTargetPublic, IPillar, IServiceUsage,
    ISpecificationTarget, IHasExpirableBugs):
    """Public IDistribution properties."""

    id = Attribute("The distro's unique number.")
    name = exported(
        DistributionNameField(
            title=_("Name"),
            constraint=name_validator,
            description=_("The distro's name."), required=True))
    displayname = exported(
        TextLine(
            title=_("Display Name"),
            description=_("The displayable name of the distribution."),
            required=True),
        exported_as='display_name')
    title = exported(
        Title(
            title=_("Title"),
            description=_("The distro's title."), required=True))
    summary = exported(
        Summary(
            title=_("Summary"),
            description=_(
                "A short paragraph to introduce the goals and highlights "
                "of the distribution."),
            required=True))
    homepage_content = exported(
        Text(
            title=_("Homepage Content"), required=False,
            description=_(
                "The content of this distribution's home page. Edit this and "
                "it will be displayed for all the world to see. It is NOT a "
                "wiki so you cannot undo changes.")))
    icon = exported(
        IconImageUpload(
            title=_("Icon"), required=False,
            default_image_resource='/@@/distribution',
            description=_(
                "A small image of exactly 14x14 pixels and at most 5kb in "
                "size, that can be used to identify this distribution. The "
                "icon will be displayed everywhere we list the distribution "
                "and link to it.")))
    logo = exported(
        LogoImageUpload(
            title=_("Logo"), required=False,
            default_image_resource='/@@/distribution-logo',
            description=_(
                "An image of exactly 64x64 pixels that will be displayed in "
                "the heading of all pages related to this distribution. It "
                "should be no bigger than 50kb in size.")))
    mugshot = exported(
        MugshotImageUpload(
            title=_("Brand"), required=False,
            default_image_resource='/@@/distribution-mugshot',
            description=_(
                "A large image of exactly 192x192 pixels, that will be "
                "displayed on this distribution's home page in Launchpad. "
                "It should be no bigger than 100kb in size. ")))
    description = exported(
        Description(
            title=_("Description"),
            description=_(
                "Details about the distributions's work, highlights, goals, "
                "and how to contribute. Use plain text, paragraphs are "
                "preserved and URLs are linked in pages. Don't repeat the "
                "Summary."),
            required=True))
    domainname = exported(
        TextLine(
            title=_("Web site URL"),
            description=_("The distro's web site URL."), required=True),
        exported_as='domain_name')
    owner = exported(
        PublicPersonChoice(
            title=_("Owner"),
            required=True,
            vocabulary='ValidPillarOwner',
            description=_("The restricted team, moderated team, or person "
                          "who maintains the distribution information in "
                          "Launchpad.")))
    registrant = exported(
        PublicPersonChoice(
            title=_("Registrant"), vocabulary='ValidPersonOrTeam',
            description=_("The distro's registrant."), required=True,
            readonly=True))
    date_created = exported(
        Datetime(title=_('Date created'),
                 description=_("The date this distribution was registered.")))
    driver = exported(
        PublicPersonChoice(
            title=_("Driver"),
            description=_(
                "The person or team responsible for decisions about features "
                "and bugs that will be targeted for any series in this "
                "distribution. Note that you can also specify a driver "
                "on each series whose permissions will be limited to that "
                "specific series."),
            required=False, vocabulary='ValidPersonOrTeam'))
    drivers = Attribute(
        "Presents the distro driver as a list for consistency with "
        "IProduct.drivers where the list might include a project driver.")
    members = exported(PublicPersonChoice(
        title=_("Members"),
        description=_("The distro's members team."), required=True,
        vocabulary='ValidPersonOrTeam'))
    mirror_admin = exported(PublicPersonChoice(
        title=_("Mirror Administrator"),
        description=_("The person or team that has the rights to review and "
                      "mark this distribution's mirrors as official."),
        required=True, vocabulary='ValidPersonOrTeam'))
    archive_mirrors = exported(doNotSnapshot(
        CollectionField(
            description=_("All enabled and official ARCHIVE mirrors "
                          "of this Distribution."),
            readonly=True, value_type=Object(schema=IDistributionMirror))))
    archive_mirrors_by_country = doNotSnapshot(CollectionField(
            description=_("All enabled and official ARCHIVE mirrors "
                          "of this Distribution."),
            readonly=True, value_type=Object(schema=IDistributionMirror)))
    cdimage_mirrors = exported(doNotSnapshot(
        CollectionField(
            description=_("All enabled and official RELEASE mirrors "
                          "of this Distribution."),
            readonly=True, value_type=Object(schema=IDistributionMirror))))
    cdimage_mirrors_by_country = doNotSnapshot(CollectionField(
            description=_("All enabled and official ARCHIVE mirrors "
                          "of this Distribution."),
            readonly=True, value_type=Object(schema=IDistributionMirror)))
    disabled_mirrors = Attribute(
        "All disabled and official mirrors of this Distribution.")
    unofficial_mirrors = Attribute(
        "All unofficial mirrors of this Distribution.")
    pending_review_mirrors = Attribute(
        "All mirrors of this Distribution that haven't been reviewed yet.")
    series = exported(doNotSnapshot(
        CollectionField(
            title=_("DistroSeries inside this Distribution"),
            # Really IDistroSeries, see _schema_circular_imports.py.
            value_type=Reference(schema=Interface))))
    derivatives = exported(doNotSnapshot(
        CollectionField(
            title=_("This Distribution's derivatives"),
            # Really IDistroSeries, see _schema_circular_imports.py.
            value_type=Reference(schema=Interface))))
    architectures = List(
        title=_("DistroArchSeries inside this Distribution"))
    uploaders = Attribute(_(
        "ArchivePermission records for uploaders with rights to upload to "
        "this distribution."))
    package_derivatives_email = TextLine(
        title=_("Package Derivatives Email Address"),
        description=_(
            "The email address to send information about updates to packages "
            "that are derived from another distribution. The sequence "
            "{package_name} is replaced with the actual package name."),
        required=False)

    # properties
    currentseries = exported(
        Reference(
            # Really IDistroSeries, see _schema_circular_imports.py.
            Interface,
            title=_("Current series"),
            description=_(
                "The current development series of this distribution. "
                "Note that all maintainerships refer to the current "
                "series. When people ask about the state of packages "
                "in the distribution, we should interpret that query "
                "in the context of the currentseries.")),
        exported_as="current_series")

    full_functionality = Attribute(
        "Whether or not we enable the full functionality of Launchpad for "
        "this distribution. Currently only Ubuntu and some derivatives "
        "get the full functionality of LP")

    translation_focus = Choice(
        title=_("Translation focus"),
        description=_(
            "The release series that translators should focus on."),
        required=False,
        vocabulary='FilteredDistroSeries')

    language_pack_admin = Choice(
        title=_("Language Pack Administrator"),
        description=_("The distribution language pack administrator."),
        required=False, vocabulary='ValidPersonOrTeam')

    main_archive = exported(
        Reference(
            title=_('Distribution Main Archive.'), readonly=True,
            # Really IArchive, see _schema_circular_imports.py.
            schema=Interface))

    all_distro_archives = exported(doNotSnapshot(
        CollectionField(
            title=_(
                "A sequence of the distribution's primary, "
                "partner and debug archives."),
            readonly=True, required=False,
            value_type=Reference(schema=Interface))),
                # Really IArchive, see _schema_circular_imports.py.
        exported_as='archives')

    all_distro_archive_ids = Attribute(
        "A list containing the IDs of all the non-PPA archives.")

    has_published_binaries = Bool(
        title=_("Has Published Binaries"),
        description=_("True if this distribution has binaries published "
                      "on disk."),
        readonly=True, required=False)

    has_published_sources = Bool(
        title=_("Has Published Sources"),
        description=_("True if this distribution has sources published."),
        readonly=True, required=False)

    redirect_release_uploads = exported(Bool(
        title=_("Redirect release pocket uploads"),
        description=_("Redirect release pocket uploads to proposed pocket"),
        readonly=False, required=True))

    development_series_alias = exported(DistroSeriesNameField(
        title=_("Alias for development series"),
        description=_(
            "If set, an alias for the current development series in this "
            "distribution."),
        constraint=name_validator, readonly=False, required=False))

    def getArchiveIDList(archive=None):
        """Return a list of archive IDs suitable for sqlvalues() or quote().

        If the archive param is supplied, just its ID will be returned in
        a list of one item.  If it is not supplied, return a list of
        all the IDs for all the archives for the distribution.
        """

    def __getitem__(name):
        """Returns a DistroSeries that matches name, or raises and
        exception if none exists."""

    def __iter__():
        """Iterate over the series for this distribution."""

    @operation_parameters(
        name=TextLine(title=_("Archive name"), required=True))
    @operation_returns_entry(Interface)
    @export_read_operation()
    def getArchive(name):
        """Return the distribution archive with the given name.

        Only distribution archives are considered -- PPAs will not be found.

        :param name: The name of the archive, e.g. 'partner'
        """

    # Really IDistroSeries, see _schema_circular_imports.py.
    @operation_returns_collection_of(Interface)
    @export_operation_as(name="getDevelopmentSeries")
    @export_read_operation()
    def getDevelopmentSeries():
        """Return the DistroSeries which are marked as in development."""

    def resolveSeriesAlias(name):
        """Resolve a series alias.

        :param name: The name to resolve.
        :raises NoSuchDistroSeries: If there is no match.
        """

    @operation_parameters(
        name_or_version=TextLine(title=_("Name or version"), required=True))
    # Really IDistroSeries, see _schema_circular_imports.py.
    @operation_returns_entry(Interface)
    @call_with(follow_aliases=True)
    @export_read_operation()
    def getSeries(name_or_version, follow_aliases=False):
        """Return the series with the name or version given.

        :param name_or_version: The `IDistroSeries.name` or
            `IDistroSeries.version`.
        """

    # This API is specifically for Ensemble's Principia.  It does not scale
    # well to distributions of Ubuntu's scale, and is not intended for it.
    # Therefore, this should probably never be exposed for a webservice
    # version other than "devel".
    @operation_parameters(
        since=Datetime(
            title=_("Time of last change"),
            description=_(
                "Return branches that have new tips since this timestamp."),
            required=False))
    @call_with(user=REQUEST_USER)
    @export_operation_as(name="getBranchTips")
    @export_read_operation()
    @operation_for_version('devel')
    def getBranchTips(user=None, since=None):
        """Return a list of branches which have new tips since a date.

        Each branch information is a tuple of (branch_unique_name,
        tip_revision, (official_series*)).

        So for each branch in the distribution, you'll get the branch unique
        name, the revision id of tip, and if the branch is official for some
        series, the list of series name.

        :param: user: If specified, shows the branches visible to that user.
            if not specified, only branches visible to the anonymous user are
            shown.

        :param since: If specified, limits results to branches modified since
            that date and time.
        """

    @operation_parameters(
        name=TextLine(title=_("Name"), required=True))
    @operation_returns_entry(IDistributionMirror)
    @export_read_operation()
    def getMirrorByName(name):
        """Return the mirror with the given name for this distribution or None
        if it's not found.
        """

    @operation_parameters(
        country=copy_field(IDistributionMirror['country'], required=True),
        mirror_type=copy_field(IDistributionMirror['content'], required=True))
    @operation_returns_entry(IDistributionMirror)
    @export_read_operation()
    def getCountryMirror(country, mirror_type):
        """Return the country DNS mirror for a country and content type."""

    def newMirror(owner, speed, country, content, displayname=None,
                  description=None, http_base_url=None,
                  ftp_base_url=None, rsync_base_url=None, enabled=False,
                  official_candidate=False, whiteboard=None):
        """Create a new DistributionMirror for this distribution.

        At least one of http_base_url or ftp_base_url must be provided in
        order to create a mirror.
        """

    @operation_parameters(
        name=TextLine(title=_("Package name"), required=True))
    # Really returns IDistributionSourcePackage, see
    # _schema_circular_imports.py.
    @operation_returns_entry(Interface)
    @export_read_operation()
    def getSourcePackage(name):
        """Return a DistributionSourcePackage with the given name for this
        distribution, or None.
        """

    def getSourcePackageRelease(sourcepackagerelease):
        """Returns an IDistributionSourcePackageRelease

        Receives a sourcepackagerelease.
        """

    def getCurrentSourceReleases(source_package_names):
        """Get the current release of a list of source packages.

        :param source_package_names: a list of `ISourcePackageName`
            instances.

        :return: a dict where the key is a `IDistributionSourcePackage`
            and the value is a `IDistributionSourcePackageRelease`.
        """

    def getDistroSeriesAndPocket(distroseriesname, follow_aliases=False):
        """Return a (distroseries,pocket) tuple which is the given textual
        distroseriesname in this distribution."""

    def getSeriesByStatus(status):
        """Query context distribution for distroseries with a given status.

        :param status: Series status to look for
        :return: list of `IDistroSeries`
        """

    @rename_parameters_as(text="source_match")
    @operation_parameters(
        text=TextLine(title=_("Source package name substring match"),
                      required=True))
    # Really returns IDistributionSourcePackage, see
    # _schema_circular_imports.py.
    @operation_returns_collection_of(Interface)
    @export_read_operation()
    def searchSourcePackages(
        text, has_packaging=None, publishing_distroseries=None):
        """Search for source packages that correspond to the given text.

        This method just decorates the result of searchSourcePackageCaches()
        to return DistributionSourcePackages.
        """

    def searchSourcePackageCaches(
        text, has_packaging=None, publishing_distroseries=None):
        """Search for source packages that correspond to the given text.

        :param text: The text that will be matched.
        :param has_packaging: If True, it will filter out
            packages with no packaging (i.e. no link to the upstream
            project). False will do the reverse filtering, and None
            will do no filtering on this field.
        :param publishing_distroseries: If it is not None, then
            it will filter out source packages that do not have a
            publishing history for the given distroseries.
        :return: A result set containing
            (DistributionSourcePackageCache, SourcePackageName, rank) tuples
            ordered by rank.
        """

    def searchBinaryPackages(package_name, exact_match=False):
        """Search for binary packages in this distribution.

        :param package_name: The binary package name to match.
        :param exact_match: If False, substring matches are done on the
            binary package names; if True only a full string match is
            returned.
        :return: A result set containing appropriate DistributionSourcePackage
            objects for the matching source.

        The returned results will consist of source packages that match
        (a substring of) their binary package names.
        """

    def guessPublishedSourcePackageName(pkgname):
        """Return the "published" SourcePackageName related to pkgname.

        If pkgname corresponds to a source package that was published in
        any of the distribution series, that's the SourcePackageName that is
        returned.

        If there is any official source package branch linked, then that
        source package name is returned.

        Otherwise, try to find a published binary package name and then return
        the source package name from which it comes from.

        :raises NotFoundError: when pkgname doesn't correspond to either a
            published source or binary package name in this distribution.
        """

    def getAllPPAs():
        """Return all PPAs for this distribution."""

    def searchPPAs(text=None, show_inactive=False):
        """Return all PPAs matching the given text in this distribution.

        'text', when passed, will restrict results to Archives with matching
        description (using substring) or matching Archive.owner (using
        available person fti/ftq).

        'show_inactive', when False, will restrict results to Archive with
        at least one source publication in PENDING or PUBLISHED status.
        """

    def getPendingAcceptancePPAs():
        """Return only pending acceptance PPAs in this distribution."""

    def getPendingPublicationPPAs():
        """Return all PPAs in this distribution that are pending publication.

        A PPA is said to be pending publication if it has publishing records
        in the pending state or if it had packages deleted from it.
        """

    def getArchiveByComponent(component_name):
        """Return the archive most appropriate for the component name.

        Where different components may imply a different archive (e.g.
        partner), this method will return the archive for that component.

        If the component_name supplied is unknown, None is returned.
        """

    def getAllowedBugInformationTypes():
        """Get the information types that a bug in this distribution can have.

        :return: A sequence of `InformationType`s.
        """

    def getDefaultBugInformationType():
        """Get the default information type of a new bug in this distro.

        :return: The `InformationType`.
        """

    def userCanEdit(user):
        """Can the user edit this distribution?"""
Пример #9
0
class ISpecificationView(IHasOwner, IHasLinkedBranches):
    """Specification's attributes and methods that require
    the permission launchpad.LimitedView.
    """

    name = exported(SpecNameField(
        title=_('Name'),
        required=True,
        readonly=False,
        description=_(
            "May contain lower-case letters, numbers, and dashes. "
            "It will be used in the specification url. "
            "Examples: mozilla-type-ahead-find, postgres-smart-serial.")),
                    as_of="devel")
    title = exported(Title(
        title=_('Title'),
        required=True,
        description=_(
            "Describe the feature as clearly as possible in up to 70 "
            "characters. This title is displayed in every feature "
            "list or report.")),
                     as_of="devel")
    specurl = exported(
        SpecURLField(
            title=_('Specification URL'),
            required=False,
            description=_(
                "The URL of the specification. This is usually a wiki page."),
            constraint=valid_webref),
        exported_as="specification_url",
        as_of="devel",
    )
    summary = exported(Summary(
        title=_('Summary'),
        required=True,
        description=_(
            "A single-paragraph description of the feature. "
            "This will also be displayed in most feature listings.")),
                       as_of="devel")

    definition_status = exported(Choice(
        title=_('Definition Status'),
        readonly=True,
        vocabulary=SpecificationDefinitionStatus,
        default=SpecificationDefinitionStatus.NEW,
        description=_(
            "The current status of the process to define the "
            "feature and get approval for the implementation plan.")),
                                 as_of="devel")

    assignee = exported(PublicPersonChoice(
        title=_('Assignee'),
        required=False,
        description=_("The person responsible for implementing the feature."),
        vocabulary='ValidPersonOrTeam'),
                        as_of="devel")
    assigneeID = Attribute('db assignee value')
    drafter = exported(PublicPersonChoice(
        title=_('Drafter'),
        required=False,
        description=_(
            "The person responsible for drafting the specification."),
        vocabulary='ValidPersonOrTeam'),
                       as_of="devel")
    drafterID = Attribute('db drafter value')
    approver = exported(PublicPersonChoice(
        title=_('Approver'),
        required=False,
        description=_(
            "The person responsible for approving the specification, "
            "and for reviewing the code when it's ready to be landed."),
        vocabulary='ValidPersonOrTeam'),
                        as_of="devel")
    approverID = Attribute('db approver value')

    priority = exported(Choice(title=_('Priority'),
                               vocabulary=SpecificationPriority,
                               default=SpecificationPriority.UNDEFINED,
                               required=True),
                        as_of="devel")
    datecreated = exported(
        Datetime(title=_('Date Created'), required=True, readonly=True),
        as_of="devel",
        exported_as="date_created",
    )
    owner = exported(PublicPersonChoice(title=_('Owner'),
                                        required=True,
                                        readonly=True,
                                        vocabulary='ValidPersonOrTeam'),
                     as_of="devel")

    product = Choice(title=_('Project'), required=False, vocabulary='Product')
    distribution = Choice(title=_('Distribution'),
                          required=False,
                          vocabulary='Distribution')

    # Exported as readonly for simplicity, but could be exported as read-write
    # using setTarget() as the mutator.
    target = exported(
        ReferenceChoice(
            title=_('For'),
            required=True,
            vocabulary='DistributionOrProduct',
            description=_(
                "The project for which this proposal is being made."),
            schema=ISpecificationTarget),
        as_of="devel",
        readonly=True,
    )

    productseries = Choice(
        title=_('Series Goal'),
        required=False,
        vocabulary='FilteredProductSeries',
        description=_(
            "Choose a series in which you would like to deliver this "
            "feature. Selecting '(nothing selected)' will clear the goal."))
    distroseries = Choice(
        title=_('Series Goal'),
        required=False,
        vocabulary='FilteredDistroSeries',
        description=_(
            "Choose a series in which you would like to deliver this "
            "feature. Selecting '(nothing selected)' will clear the goal."))

    # milestone
    milestone = exported(ReferenceChoice(
        title=_('Milestone'),
        required=False,
        vocabulary='Milestone',
        description=_(
            "The milestone in which we would like this feature to be "
            "delivered."),
        schema=IMilestone),
                         as_of="devel")

    # nomination to a series for release management
    # XXX: It'd be nice to export goal as read-only, but it's tricky because
    # users will need to be aware of goalstatus as what's returned by .goal
    # may not be the accepted goal.
    goal = Attribute("The series for which this feature is a goal.")
    goalstatus = Choice(
        title=_('Goal Acceptance'),
        vocabulary=SpecificationGoalStatus,
        default=SpecificationGoalStatus.PROPOSED,
        description=_(
            "Whether or not the drivers have accepted this feature as "
            "a goal for the targeted series."))
    goal_proposer = Attribute("The person who nominated the spec for "
                              "this series.")
    date_goal_proposed = Attribute("The date of the nomination.")
    goal_decider = Attribute("The person who approved or declined "
                             "the spec a a goal.")
    date_goal_decided = Attribute("The date the spec was approved "
                                  "or declined as a goal.")

    work_items = List(description=_(
        "All non-deleted work items for this spec, sorted by "
        "their 'sequence'"),
                      value_type=Reference(schema=ISpecificationWorkItem),
                      readonly=True)
    whiteboard = exported(Text(
        title=_('Status Whiteboard'),
        required=False,
        description=_("Any notes on the status of this spec you would like to "
                      "make. Your changes will override the current text.")),
                          as_of="devel")
    workitems_text = exported(WorkItemsText(
        title=_('Work Items'),
        required=False,
        readonly=True,
        description=_(
            "Work items for this specification input in a text format. "
            "Your changes will override the current work items.")),
                              as_of="devel")
    direction_approved = exported(Bool(
        title=_('Basic direction approved?'),
        required=True,
        default=False,
        description=_("Check this to indicate that the drafter and assignee "
                      "have satisfied the approver that they are headed in "
                      "the right basic direction with this specification.")),
                                  as_of="devel")
    man_days = Int(
        title=_("Estimated Developer Days"),
        required=False,
        default=None,
        description=_(
            "An estimate of the "
            "number of developer days it will take to implement this feature. "
            "Please only provide an estimate if you are relatively confident "
            "in the number."))
    implementation_status = exported(Choice(
        title=_("Implementation Status"),
        required=True,
        readonly=True,
        default=SpecificationImplementationStatus.UNKNOWN,
        vocabulary=SpecificationImplementationStatus,
        description=_("The state of progress being made on the actual "
                      "implementation or delivery of this feature.")),
                                     as_of="devel")
    superseded_by = Choice(
        title=_("Superseded by"),
        required=False,
        default=None,
        vocabulary='Specification',
        description=_(
            "The specification "
            "which supersedes this one. Note that selecting a specification "
            "here and pressing Continue will change the specification "
            "status to Superseded."))

    # lifecycle
    starter = exported(PublicPersonChoice(
        title=_('Starter'),
        required=False,
        readonly=True,
        description=_(
            'The person who first set the state of the '
            'spec to the values that we consider mark it as started.'),
        vocabulary='ValidPersonOrTeam'),
                       as_of="devel")
    date_started = exported(Datetime(
        title=_('Date Started'),
        required=False,
        readonly=True,
        description=_('The date when this spec was marked started.')),
                            as_of="devel")

    completer = exported(PublicPersonChoice(
        title=_('Starter'),
        required=False,
        readonly=True,
        description=_(
            'The person who finally set the state of the '
            'spec to the values that we consider mark it as complete.'),
        vocabulary='ValidPersonOrTeam'),
                         as_of="devel")

    date_completed = exported(Datetime(
        title=_('Date Completed'),
        required=False,
        readonly=True,
        description=_(
            'The date when this spec was marked '
            'complete. Note that complete also includes "obsolete" and '
            'superseded. Essentially, it is the state where no more work '
            'will be done on the feature.')),
                              as_of="devel")

    # joins
    subscriptions = Attribute('The set of subscriptions to this spec.')
    subscribers = Attribute('The set of subscribers to this spec.')
    sprints = Attribute('The sprints at which this spec is discussed.')
    sprint_links = Attribute('The entries that link this spec to sprints.')
    dependencies = exported(
        CollectionField(
            title=_('Specs on which this one depends.'),
            value_type=Reference(schema=Interface),  # ISpecification, really.
            readonly=True),
        as_of="devel")
    linked_branches = exported(
        CollectionField(
            title=_("Branches associated with this spec, usually "
                    "branches on which this spec is being implemented."),
            value_type=Reference(schema=Interface),  # ISpecificationBranch
            readonly=True),
        as_of="devel")

    def getDependencies():
        """Specs on which this one depends."""

    def getBlockedSpecs():
        """Specs for which this spec is a dependency."""

    # emergent properties
    informational = Attribute('Is True if this spec is purely informational '
                              'and requires no implementation.')
    is_complete = exported(Bool(
        title=_('Is started'),
        readonly=True,
        required=True,
        description=_(
            'Is True if this spec is already completely implemented. '
            'Note that it is True for informational specs, since '
            'they describe general functionality rather than specific '
            'code to be written. It is also true of obsolete and '
            'superseded specs, since there is no longer any need '
            'to schedule work for them.')),
                           as_of="devel")

    is_incomplete = Attribute(
        'Is True if this work still needs to '
        'be done. Is in fact always the opposite of is_complete.')
    is_blocked = Attribute('Is True if this spec depends on another spec '
                           'which is still incomplete.')
    is_started = exported(Bool(
        title=_('Is started'),
        readonly=True,
        required=True,
        description=_(
            'Is True if the spec is in a state which '
            'we consider to be "started". This looks at the delivery '
            'attribute, and also considers informational specs to be '
            'started when they are approved.')),
                          as_of="devel")

    lifecycle_status = exported(Choice(
        title=_('Lifecycle Status'),
        vocabulary=SpecificationLifecycleStatus,
        default=SpecificationLifecycleStatus.NOTSTARTED,
        readonly=True),
                                as_of="devel")

    def all_deps():
        """All the dependencies, including dependencies of dependencies.

        If a user is provided, filters to only dependencies the user can see.
        """

    def all_blocked():
        """All specs blocked on this, and those blocked on the blocked ones.

        If a user is provided, filters to only blocked dependencies the user
        can see.
        """

    def validateMove(target):
        """Check that the specification can be moved to the target."""

    def getSprintSpecification(sprintname):
        """Get the record that links this spec to the named sprint."""

    def notificationRecipientAddresses():
        """Return the list of email addresses that receive notifications."""

    has_accepted_goal = exported(Bool(
        title=_('Series goal is accepted'),
        readonly=True,
        required=True,
        description=_(
            'Is true if this specification has been '
            'proposed as a goal for a specific series, '
            'and the drivers of that series have accepted the goal.')),
                                 as_of="devel")

    # lifecycle management
    def updateLifecycleStatus(user):
        """Mark the specification as started, and/or complete, if appropriate.

        This will verify that the state of the specification is in fact
        "complete" (there is a completeness test in
        Specification.is_complete) and then record the completer and the
        date_completed. If the spec is not completed, then it ensures that
        nothing is recorded about its completion.

        It returns a SpecificationLifecycleStatus dbschema showing the
        overall state of the specification IF the state has changed.
        """

    # event-related methods
    def getDelta(old_spec, user):
        """Return a dictionary of things that changed between this spec and
        the old_spec.

        This method is primarily used by event subscription code, to
        determine what has changed during an ObjectModifiedEvent.
        """

    # subscription-related methods
    def subscription(person):
        """Return the subscription for this person to this spec, or None."""

    @operation_parameters(person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True),
                          essential=copy_field(
                              ISpecificationSubscription['essential'],
                              required=False))
    @call_with(subscribed_by=REQUEST_USER)
    @export_write_operation()
    @operation_for_version('devel')
    def subscribe(person, subscribed_by=None, essential=False):
        """Subscribe this person to the feature specification."""

    @operation_parameters(person=Reference(IPerson,
                                           title=_('Person'),
                                           required=False))
    @call_with(unsubscribed_by=REQUEST_USER)
    @export_write_operation()
    @operation_for_version('devel')
    def unsubscribe(person, unsubscribed_by):
        """Remove the person's subscription to this spec."""

    def getSubscriptionByName(name):
        """Return a subscription based on the person's name, or None."""

    def isSubscribed(person):
        """Is person subscribed to this spec?

        Returns True if the user is explicitly subscribed to this spec
        (no matter what the type of subscription), otherwise False.

        If person is None, the return value is always False.
        """

    # sprints
    def linkSprint(sprint, user):
        """Put this spec on the agenda of the sprint."""

    def unlinkSprint(sprint):
        """Remove this spec from the agenda of the sprint."""

    # dependencies
    def createDependency(specification):
        """Create a dependency for this spec on the spec provided."""

    def removeDependency(specification):
        """Remove any dependency of this spec on the spec provided."""

    # branches
    def getBranchLink(branch):
        """Return the SpecificationBranch link for the branch, or None."""

    def getLinkedBugTasks(user):
        """Return the bug tasks that are relevant to this blueprint.

        When multiple tasks are on a bug, if one of the tasks is for the
        target, then only that task is returned. Otherwise the default
        bug task is returned.

        :param user: The user doing the search.
        """

    def getAllowedInformationTypes(who):
        """Get a list of acceptable `InformationType`s for this spec."""