예제 #1
0
  def testAccessViews(self):
    anyone_view = project_views.ProjectAccessView(
        project_pb2.ProjectAccess.ANYONE)
    self.assertEqual(anyone_view.key, int(project_pb2.ProjectAccess.ANYONE))

    members_only_view = project_views.ProjectAccessView(
        project_pb2.ProjectAccess.MEMBERS_ONLY)
    self.assertEqual(members_only_view.key,
                     int(project_pb2.ProjectAccess.MEMBERS_ONLY))
예제 #2
0
    def GatherPageData(self, _mr):
        """Build up a dictionary of data values to use when rendering the page."""
        available_access_levels = project_helpers.BuildProjectAccessOptions(
            None)
        offer_access_level = len(available_access_levels) > 1
        if settings.default_access_level:
            access_view = project_views.ProjectAccessView(
                settings.default_access_level)
        else:
            access_view = None

        return {
            'initial_name': '',
            'initial_summary': '',
            'initial_description': '',
            'initial_project_home': '',
            'initial_docs_url': '',
            'initial_source_url': '',
            'initial_logo_gcs_id': '',
            'initial_logo_file_name': '',
            'logo_view': tracker_views.LogoView(None),
            'labels': [],
            'max_project_name_length':
            framework_constants.MAX_PROJECT_NAME_LENGTH,
            'offer_access_level': ezt.boolean(offer_access_level),
            'initial_access': access_view,
            'available_access_levels': available_access_levels,
        }
예제 #3
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page."""

        with mr.profiler.Phase('getting project star count'):
            num_stars = self.services.project_star.CountItemStars(
                mr.cnxn, mr.project_id)
            plural = '' if num_stars == 1 else 's'

        page_data = {
            'admin_tab_mode':
            self.PROCESS_TAB_SUMMARY,
            'formatted_project_description':
            markdown.Markdown(mr.project.description),
            'access_level':
            project_views.ProjectAccessView(mr.project.access),
            'num_stars':
            num_stars,
            'plural':
            plural,
            'home_page':
            mr.project.home_page,
            'docs_url':
            mr.project.docs_url,
            'source_url':
            mr.project.source_url,
        }

        return page_data
예제 #4
0
  def GatherPageData(self, mr):
    """Build up a dictionary of data values to use when rendering the page."""
    available_access_levels = project_helpers.BuildProjectAccessOptions(
        mr.project)
    offer_access_level = len(available_access_levels) > 1
    access_view = project_views.ProjectAccessView(mr.project.access)

    return {
        'admin_tab_mode': self.ADMIN_TAB_META,
        'initial_summary': mr.project.summary,
        'initial_project_home': mr.project.home_page,
        'initial_docs_url': mr.project.docs_url,
        'initial_source_url': mr.project.source_url,
        'initial_logo_gcs_id': mr.project.logo_gcs_id,
        'initial_logo_file_name': mr.project.logo_file_name,
        'logo_view': tracker_views.LogoView(mr.project),
        'initial_description': mr.project.description,
        'issue_notify': mr.project.issue_notify_address,
        'process_inbound_email': ezt.boolean(
            mr.project.process_inbound_email),
        'email_from_addr': emailfmt.FormatFromAddr(mr.project),
        'only_owners_remove_restrictions': ezt.boolean(
            mr.project.only_owners_remove_restrictions),
        'only_owners_see_contributors': ezt.boolean(
            mr.project.only_owners_see_contributors),
        'offer_access_level': ezt.boolean(offer_access_level),
        'initial_access': access_view,
        'available_access_levels': available_access_levels,
        }
예제 #5
0
def BuildProjectAccessOptions(project):
    """Return a list of project access values for use in an HTML menu.

  Args:
    project: current Project PB, or None when creating a new project.

  Returns:
    A list of ProjectAccessView objects that can be used in EZT.
  """
    access_levels = [
        project_pb2.ProjectAccess.ANYONE,
        project_pb2.ProjectAccess.MEMBERS_ONLY
    ]
    access_views = []
    for access in access_levels:
        # Offer the allowed access levels.  When editing an existing project,
        # its current access level may always be kept, even if it is no longer
        # in the list of allowed access levels for new projects.
        if (access in settings.allowed_access_levels
                or (project and access == project.access)):
            access_views.append(project_views.ProjectAccessView(access))

    return access_views
예제 #6
0
                summary=summary,
                description=description,
                only_owners_remove_restrictions=only_owners_remove_restrictions,
                only_owners_see_contributors=only_owners_see_contributors,
                process_inbound_email=process_inbound_email,
                access=access,
                home_page=home_page,
                docs_url=docs_url,
                source_url=source_url,
                logo_gcs_id=logo_gcs_id,
                logo_file_name=logo_file_name,
            )

        # 3. Determine the next page in the UI flow.
        if mr.errors.AnyErrors():
            access_view = project_views.ProjectAccessView(access)
            self.PleaseCorrect(mr,
                               initial_summary=summary,
                               initial_description=description,
                               initial_access=access_view)
        else:
            return framework_helpers.FormatAbsoluteURL(mr,
                                                       urls.ADMIN_META,
                                                       saved=1,
                                                       ts=int(time.time()))

    def _ParseMeta(self, post_data, errors):
        """Process a POST on the project metadata section of the admin page."""
        summary = None
        description = None
예제 #7
0
class ProjectCreate(servlet.Servlet):
    """Shows a page with a simple form to create a project."""

    _PAGE_TEMPLATE = 'sitewide/project-create-page.ezt'

    def AssertBasePermission(self, mr):
        """Assert that the user has the permissions needed to view this page."""
        super(ProjectCreate, self).AssertBasePermission(mr)

        if not permissions.CanCreateProject(mr.perms):
            raise permissions.PermissionException(
                'User is not allowed to create a project')

    def GatherPageData(self, _mr):
        """Build up a dictionary of data values to use when rendering the page."""
        available_access_levels = project_helpers.BuildProjectAccessOptions(
            None)
        offer_access_level = len(available_access_levels) > 1
        if settings.default_access_level:
            access_view = project_views.ProjectAccessView(
                settings.default_access_level)
        else:
            access_view = None

        return {
            'initial_name': '',
            'initial_summary': '',
            'initial_description': '',
            'initial_project_home': '',
            'initial_docs_url': '',
            'initial_source_url': '',
            'initial_logo_gcs_id': '',
            'initial_logo_file_name': '',
            'logo_view': tracker_views.LogoView(None),
            'labels': [],
            'max_project_name_length':
            framework_constants.MAX_PROJECT_NAME_LENGTH,
            'offer_access_level': ezt.boolean(offer_access_level),
            'initial_access': access_view,
            'available_access_levels': available_access_levels,
        }

    def ProcessFormData(self, mr, post_data):
        """Process the posted form."""
        # 1. Parse and validate user input.
        # Project name is taken from post_data because we are creating it.
        project_name = post_data.get('projectname')
        if not project_name:
            mr.errors.projectname = _MSG_MISSING_PROJECT_NAME
        elif not framework_bizobj.IsValidProjectName(project_name):
            mr.errors.projectname = _MSG_INVALID_PROJECT_NAME

        summary = post_data.get('summary')
        if not summary:
            mr.errors.summary = _MSG_MISSING_PROJECT_SUMMARY
        description = post_data.get('description', '')

        access = project_helpers.ParseProjectAccess(None,
                                                    post_data.get('access'))
        home_page = post_data.get('project_home')
        if home_page and not (home_page.startswith('http://')
                              or home_page.startswith('https://')):
            mr.errors.project_home = 'Home page link must start with http(s)://'
        docs_url = post_data.get('docs_url')
        if docs_url and not (docs_url.startswith('http:')
                             or docs_url.startswith('https:')):
            mr.errors.docs_url = 'Documentation link must start with http: or https:'

        # These are not specified on via the ProjectCreate form,
        # the user must edit the project after creation to set them.
        committer_ids = []
        contributor_ids = []

        # Validate that provided logo is supported.
        logo_provided = 'logo' in post_data and not isinstance(
            post_data['logo'], string_types)
        if logo_provided:
            item = post_data['logo']
            try:
                gcs_helpers.CheckMimeTypeResizable(
                    filecontent.GuessContentTypeFromFilename(item.filename))
            except gcs_helpers.UnsupportedMimeType, e:
                mr.errors.logo = e.message

        # 2. Call services layer to save changes.
        if not mr.errors.AnyErrors():
            with work_env.WorkEnv(mr, self.services) as we:
                try:
                    project_id = we.CreateProject(project_name,
                                                  [mr.auth.user_id],
                                                  committer_ids,
                                                  contributor_ids,
                                                  summary,
                                                  description,
                                                  access=access,
                                                  home_page=home_page,
                                                  docs_url=docs_url)

                    config = tracker_bizobj.MakeDefaultProjectIssueConfig(
                        project_id)
                    self.services.config.StoreConfig(mr.cnxn, config)
                    # Note: No need to store any canned queries or rules yet.
                    self.services.issue.InitializeLocalID(mr.cnxn, project_id)

                    # Update project with  logo if specified.
                    if logo_provided:
                        item = post_data['logo']
                        logo_file_name = item.filename
                        logo_gcs_id = gcs_helpers.StoreLogoInGCS(
                            logo_file_name, item.value, project_id)
                        we.UpdateProject(project_id,
                                         logo_gcs_id=logo_gcs_id,
                                         logo_file_name=logo_file_name)

                except exceptions.ProjectAlreadyExists:
                    mr.errors.projectname = _MSG_PROJECT_NAME_NOT_AVAIL

        # 3. Determine the next page in the UI flow.
        if mr.errors.AnyErrors():
            access_view = project_views.ProjectAccessView(access)
            self.PleaseCorrect(mr,
                               initial_summary=summary,
                               initial_description=description,
                               initial_name=project_name,
                               initial_access=access_view)
        else:
            # Go to the new project's introduction page.
            return framework_helpers.FormatAbsoluteURL(
                mr, urls.ADMIN_INTRO, project_name=project_name)
예제 #8
0
class ProjectCreate(servlet.Servlet):
    """Shows a page with a simple form to create a project."""

    _PAGE_TEMPLATE = 'sitewide/project-create-page.ezt'

    _CAPTCHA_ACTION_TYPES = [actionlimit.PROJECT_CREATION]

    def AssertBasePermission(self, mr):
        """Assert that the user has the permissions needed to view this page."""
        super(ProjectCreate, self).AssertBasePermission(mr)

        if not permissions.CanCreateProject(mr.perms):
            raise permissions.PermissionException(
                'User is not allowed to create a project')

    def GatherPageData(self, _mr):
        """Build up a dictionary of data values to use when rendering the page."""
        available_access_levels = project_helpers.BuildProjectAccessOptions(
            None)
        offer_access_level = len(available_access_levels) > 1
        if settings.default_access_level:
            access_view = project_views.ProjectAccessView(
                settings.default_access_level)
        else:
            access_view = None

        return {
            'initial_name': '',
            'initial_summary': '',
            'initial_description': '',
            'initial_project_home': '',
            'initial_docs_url': '',
            'initial_source_url': '',
            'initial_logo_gcs_id': '',
            'initial_logo_file_name': '',
            'logo_view': tracker_views.LogoView(None),
            'labels': [],
            'max_project_name_length':
            framework_constants.MAX_PROJECT_NAME_LENGTH,
            'offer_access_level': ezt.boolean(offer_access_level),
            'initial_access': access_view,
            'available_access_levels': available_access_levels,
        }

    def GatherHelpData(self, mr, page_data):
        """Return a dict of values to drive on-page user help.

    Args:
      mr: common information parsed from the HTTP request.
      page_data: Dictionary of base and page template data.

    Returns:
      A dict of values to drive on-page user help, to be added to page_data.
    """
        help_data = super(ProjectCreate, self).GatherHelpData(mr, page_data)
        cue_remaining_projects = None

        (_period, _soft, _hard,
         life_max) = actionlimit.ACTION_LIMITS[actionlimit.PROJECT_CREATION]
        actionlimit_pb = actionlimit.GetLimitPB(mr.auth.user_pb,
                                                actionlimit.PROJECT_CREATION)
        if actionlimit_pb.get_assigned_value('lifetime_limit'):
            life_max = actionlimit_pb.lifetime_limit
        if life_max is not None:
            if (actionlimit_pb.lifetime_count + 10 >= life_max
                    and actionlimit_pb.lifetime_count < life_max):
                cue_remaining_projects = life_max - actionlimit_pb.lifetime_count

        help_data.update({
            'cue_remaining_projects': cue_remaining_projects,
        })
        return help_data

    def ProcessFormData(self, mr, post_data):
        """Process the posted form."""
        # 1. Parse and validate user input.
        # Project name is taken from post_data because we are creating it.
        project_name = post_data.get('projectname')
        if not project_name:
            mr.errors.projectname = _MSG_MISSING_PROJECT_NAME
        elif not framework_bizobj.IsValidProjectName(project_name):
            mr.errors.projectname = _MSG_INVALID_PROJECT_NAME

        summary = post_data.get('summary')
        if not summary:
            mr.errors.summary = _MSG_MISSING_PROJECT_SUMMARY
        description = post_data.get('description', '')

        access = project_helpers.ParseProjectAccess(None,
                                                    post_data.get('access'))
        home_page = post_data.get('project_home')
        if home_page and not (home_page.startswith('http://')
                              or home_page.startswith('https://')):
            mr.errors.project_home = 'Home page link must start with http(s)://'
        docs_url = post_data.get('docs_url')
        if docs_url and not (docs_url.startswith('http:')
                             or docs_url.startswith('https:')):
            mr.errors.docs_url = 'Documentation link must start with http: or https:'

        self.CheckCaptcha(mr, post_data)

        # These are not specified on via the ProjectCreate form,
        # the user must edit the project after creation to set them.
        committer_ids = []
        contributor_ids = []

        # Validate that provided logo is supported.
        logo_provided = 'logo' in post_data and not isinstance(
            post_data['logo'], basestring)
        if logo_provided:
            item = post_data['logo']
            try:
                gcs_helpers.CheckMimeTypeResizable(
                    filecontent.GuessContentTypeFromFilename(item.filename))
            except gcs_helpers.UnsupportedMimeType, e:
                mr.errors.logo = e.message

        # 2. Call services layer to save changes.
        if not mr.errors.AnyErrors():
            try:
                project_id = self.services.project.CreateProject(
                    mr.cnxn,
                    project_name, [mr.auth.user_id],
                    committer_ids,
                    contributor_ids,
                    summary,
                    description,
                    access=access,
                    home_page=home_page,
                    docs_url=docs_url)

                config = tracker_bizobj.MakeDefaultProjectIssueConfig(
                    project_id)
                self.services.config.StoreConfig(mr.cnxn, config)
                # Note: No need to store any canned queries or rules yet.
                self.services.issue.InitializeLocalID(mr.cnxn, project_id)

                # Update project with  logo if specified.
                if logo_provided:
                    item = post_data['logo']
                    logo_file_name = item.filename
                    logo_gcs_id = gcs_helpers.StoreLogoInGCS(
                        logo_file_name, item.value, project_id)
                    self.services.project.UpdateProject(
                        mr.cnxn,
                        project_id,
                        logo_gcs_id=logo_gcs_id,
                        logo_file_name=logo_file_name)

                self.CountRateLimitedActions(mr,
                                             {actionlimit.PROJECT_CREATION: 1})
            except project_svc.ProjectAlreadyExists:
                mr.errors.projectname = _MSG_PROJECT_NAME_NOT_AVAIL

        # 3. Determine the next page in the UI flow.
        if mr.errors.AnyErrors():
            access_view = project_views.ProjectAccessView(access)
            self.PleaseCorrect(mr,
                               initial_summary=summary,
                               initial_description=description,
                               initial_name=project_name,
                               initial_access=access_view)
        else:
            # Go to the new project's introduction page.
            return framework_helpers.FormatAbsoluteURL(
                mr, urls.ADMIN_INTRO, project_name=project_name)