예제 #1
0
 def test_get_my_tasks(self):
     dbStub.addResult(project_tasks_result)
     p = Projects()
     project_list = [
         Project(1, "project1", "project1", "Project 1", 1, "2010-01-01"),
         Project(2, "project2", "project2", "Project 2", 2, "2010-01-02")
     ]
     tasks = p.get_all_user_tasks("username", project_list)
     self.assertEquals(len(tasks), 6)
     self.assertTrue(dbStub.closed)
예제 #2
0
    def query_archived_projects(self, query):
        """ Returns a list of archived projects given with query
        """
        projects = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for project in cursor.fetchall():
                    # Create regular project
                    prj = Project(id=project[1],
                                  env_name=project[3],
                                  project_name=project[2],
                                  description=None,
                                  author_id=project[4],
                                  created=project[5],
                                  parent_id=project[6])

                    # Add some archiving parameters
                    prj.archive_folder_name = project[7]
                    prj.archive_path = conf.archive_path + "/" + project[7]
                    prj.archive_date = project[8]
                    prj.remove_due = project[9]
                    prj.removed_at = project[10]
                    prj.project_archive_id = project[0]

                    projects.append(prj)

            except:
                conf.log.exception(
                    "Querying archived projects failed with query '''%s'''" %
                    query)

        return projects
예제 #3
0
    def post_process_request(self, req, template, data, content_type):
        #add_script(req, 'multiproject/js/browser.js')
        repository_name = None
        data_repositories = None
        latest_revisions = []
        if len(req.path_info.split("/")) > 2:
            #Get repository name from path_info
            repository_name = req.path_info.split("/")[2]
        if template == 'browser.html':
            username = urllib.quote(req.authname)
            project = Project.get(self.env)
            schemes = None
            if repository_name:
                scm_type = repository_name + ".type"
                scm_dir = repository_name + ".dir"
                scm = self.env.config.get('repositories', scm_type)
                repository_name = self.env.config.get('repositories', scm_dir).split("/")[-1]
                schemes = self.protocols(project.id, scm)
                rm = RepositoryManager(self.env)
                list_repos = rm.get_real_repositories()
                for r in list_repos:
                    if r.get_base().split("/")[-1] == repository_name:
                        l_rev = r.get_youngest_rev()
                        if l_rev:
                            export_url = '../export/archive/'+repository_name+"?rev="+str(l_rev)+"&format=zip"
                            latest_revisions.append(export_url)

            else:
                scm = self.env.config.get('trac', 'repository_type')
                schemes = self.protocols(project.id, scm)
                data_repo_names = self.get_repositories()
                if len(data_repo_names) > 0:
                    data_repositories = []
                    for repo in data_repo_names:
                        type_scheme = []
                        for data_scheme in self.protocols(project.id, repo[1]):
                            type_scheme.append(self.create_co_command(repo[1], username, data_scheme, repo[0]))
                        data_repositories.append(type_scheme)

            

            names = {'git':'GIT', 'svn':'Subversion', 'hg':'Mercurial'}
            cmd_kinds = {'git':'Clone', 'hg':'Clone', 'svn':'Check out'}

            type = names[scm]
            

            data['kinds'] = cmd_kinds
            data['schemes'] = schemes
            data['name'] = names[scm]
            data['type'] = scm
            data['data_repositories'] = data_repositories
            data['export_urls'] = latest_revisions

            co_commands = {}
            for scheme in schemes:
                co_commands[scheme] = self.create_co_command(scm, username, scheme, repository_name)
            data['co_commands'] = co_commands

        return template, data, content_type
예제 #4
0
    def render_admin_panel(self, req, category, page, path_info):
        """
        Process a request for an admin panel.

        :Returns: A tuple of the form `(template, data)`,
        """
        # Ensure the user has project admin permissions
        req.perm.assert_permission('TRAC_ADMIN')

        backups = []
        backup_href = Href('%s/admin/general/backup' % req.base_path)

        # Load the user based on authname
        user = get_userstore().getUser(req.authname)

        # Get the current environment name
        env_name = conf.resolveProjectName(self.env)

        # Initiate ProjectBackup, containing the backup/restore implementation
        prj = Project.get(env_name=env_name)
        pb = ProjectBackup(prj)
        backups = pb.get_backups()

        # Do the backup
        if req.path_info.endswith('backup/backup') and req.method == 'POST':
            try:
                pb.backup(user_id=user.id, description=req.args.get('description'))
                add_notice(req, _('Backup created'))
            except TracError, e:
                add_warning(req, _('Backup failed: %s' % e))

            # Return back to default backup page
            return req.redirect(backup_href())
예제 #5
0
    def filter_stream(self, req, method, filename, stream, data):
        """
        Adds project total count information in project summary block::

            Downloads: 288

        """
        # TODO: Make interface for the project summary box and implement it here

        # Filter only the summary table wiki macro
        if filename != 'multiproject_summary.html':
            return stream

        # Load project and followers info
        project = Project.get(self.env)
        count = ProjectDownloadEntry.total_download_count(project.id)

        if count == 0:
            return stream

        # Add following information into project summary block
        trans = Transformer('//div[@class="summary"]/table').append(
            tag.tr(
                tag.th('Downloads:'),
                tag.td(count)
            )
        )

        return stream | trans
예제 #6
0
    def target_node(self):
        if self._target_node is not None:
            return self._target_node
        env_name = self.environment_identifier()
        if not env_name:
            conf.log.warning("Failed reading identifier")
            raise NodeError('Target node was invalid: env_name not found')
        project = Project.get(env_name=env_name)
        # env_name might be different from project.env_name!
        base_url = project.get_dav_url(relative=True)
        path_info = normalized_base_url(self.req.uri)
        project_id = project.id
        download_config = FilesDownloadConfig(project.env_name, base_url=base_url)
        if not path_info.startswith(download_config.base_url):
            raise NodeError('Target node was invalid: uri %s does not start with %s',
                path_info, base_url)
        self._download_config = download_config
        try:
            node_factory = MappedFileNode(project_id,
                download_config.base_path, download_config.base_url, download_config.downloads_dir)
            self._target_node = MappedFileNode.from_request_path_info(path_info, node_factory)
        except Exception as e:
            conf.log.warning("Not node %s", str(e))
            raise NodeError('Target node was invalid: %s', str(e))

        return self._target_node
예제 #7
0
    def process_request(self, req):
        """ Process request for listing, creating and removing projects
        """
        add_script(req, 'multiproject/js/summary.js')
        data = {}

        if req.authname == 'anonymous':
            req.perm.require('PROJECT_VIEW')

        if not ('PROJECT_VIEW' in req.perm
                or 'PROJECT_PRIVATE_VIEW' in req.perm):
            return 'no_access.html', data, None

        # Load project from db
        project = Project.get(self.env)

        if req.args.get('action') == 'timeline_events':
            return self.get_summary_timeline_events(req, project.created)

        # TODO: Move project timeline implementation into macro
        # Get recent timeline events

        # TODO: Move project downloads implementation into macro
        # Get list of featured downloads
        downloads = []
        if self.env.is_component_enabled('tracdownloads.api.DownloadsApi'):
            api = None

            # In case of import error we don't have it, so don't return any downloads
            try:
                from tracdownloads.api import DownloadsApi
                api = DownloadsApi(self.env)
            except ImportError:
                self.log.warning(
                    "Unable to import DownloadsApi, but it's enabled.")

            if api:
                downloads = api.get_summary_items()

        # Load wiki:SummaryPage and show it as a main page
        summary_content = None
        summary_wiki = WikiPage(self.env, 'SummaryPage')
        if summary_wiki:
            context = Context.from_request(req, summary_wiki.resource)
            wiki_renderer = WikiTextRenderer(self.env)
            summary_content = wiki_renderer.render(context, 'text/x-trac-wiki',
                                                   summary_wiki.text)

        data = {
            '_project_': project,  # project object of a project we are in
            'downloads': downloads,  # list of featured downloads
            'wiki_summary':
            summary_content,  # Rendered content of the SummaryPage
            'is_summary': True,
            'env': self.env,
        }

        return 'summary.html', data, None
예제 #8
0
    def render_admin_panel(self, req, cat, page, path_info):
        """
        Returns the admin view and handles the form actions
        """
        req.perm.require('TRAC_ADMIN')

        project = Project.get(self.env)

        if req.method == 'POST':
            myprojects_url = conf.url_home_path + "/myprojects"
            conf.log.exception("Redirect URL: %s" % myprojects_url)
            thisurl = Href(req.base_path)(req.path_info)
            context = Context.from_request(req)

            # Handle project remove
            if 'remove' in req.args:
                # Don't allow removing home
                if project.env_name == conf.sys_home_project_name:
                    add_warning(req, 'Cannot remove home project')
                    return req.redirect(thisurl)

                # Archive the project before removal
                archive = ProjectArchive()
                archived = archive.archive(project)

                if not archived:
                    add_warning(req, 'Could not archive project "%s". Will not remove the project' % project.project_name)
                    return req.redirect(thisurl)

                # Notify listeners. The project object still exists, but database does not
                for listener in self.project_change_listeners:
                    listener.project_archived(project)

                # Do the actual project removal
                projects = Projects()
                if projects.remove_project(project):
                    # Notify listeners. The project object still exists, but database does not
                    for listener in self.project_change_listeners:
                        listener.project_deleted(project)

                    # NOTE: We no longer have project tables/session etc available
                    req.redirect(myprojects_url)

                else:
                    add_warning(req, 'Could not remove project "%s". Try again later' % project.project_name)
                    return req.redirect(thisurl)

        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/admin_system.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')

        # NOTE: Trac automatically puts 'project' dict in chrome thus it cannot be used
        data = {'multiproject': {
            'project':project,
            'home_url':conf.url_home_path
        }}
        return 'admin_system.html', data
예제 #9
0
    def query_archived_projects(self, query):
        """ Returns a list of archived projects given with query
        """
        projects = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for project in cursor.fetchall():
                    # Create regular project
                    prj = Project(
                        id=project[1],
                        env_name=project[3],
                        project_name=project[2],
                        description=None,
                        author_id=project[4],
                        created=project[5],
                        parent_id=project[6],
                    )

                    # Add some archiving parameters
                    prj.archive_folder_name = project[7]
                    prj.archive_path = conf.archive_path + "/" + project[7]
                    prj.archive_date = project[8]
                    prj.remove_due = project[9]
                    prj.removed_at = project[10]
                    prj.project_archive_id = project[0]

                    projects.append(prj)

            except:
                conf.log.exception("Querying archived projects failed with query '''%s'''" % query)

        return projects
예제 #10
0
    def remove_project(self, req):
        """
        Handler for removing project

        :param Request req: Trac request

        Handler expects to have following arguments in it:

        - project: Name of the project env
        - goto: Optional URL where to go after project removal. Defaults to /home/myprojects

        """
        backurl = req.args.get('goto', ProjectListModule.home('myprojects'))
        short_name = req.args.get('project')
        if not short_name:
            return self.remove_failure(req)
        if req.args.get('cancel'):
            add_notice(req, 'Project deletion cancelled')
            return self.remove_failure(req)
        author = req.authname
        project = Project.get(env_name=short_name)

        if project is None:
            add_warning(req, 'Specified project does not exist!')
            return self.remove_failure(req)

        # Don't allow removing home
        if project.env_name == conf.sys_home_project_name:
            add_warning(req, 'Cannot remove home project')
            return self.remove_failure(req)
        if req.method != 'POST':
            return self.remove_failure(req, project=project)

        try:
            project.validate()
        except ProjectValidationException:
            req.redirect(backurl)

        # Check the permissions and method
        prjs = Projects()
        if ('TRAC_ADMIN' in req.perm or prjs.is_project_owner(project.env_name, author)) and req.method == 'POST':
            archive = ProjectArchive()
            # Archive the project before removal
            if not archive.archive(project):
                add_warning(req, 'Could not archive project "%s". Will not remove the project' % project.project_name)
            elif prjs.remove_project(project):
                # Notify listeners
                for listener in self.project_change_listeners:
                    listener.project_deleted(project)
                # Notify end user
                add_notice(req, 'Project "%s" removed' % project.project_name)
            else:
                add_warning(req, 'Could not remove project "%s". Try again later' % project.project_name)
        else:
            add_warning(req, 'You are not allowed to remove project "%s" (check permissions and method)' % project.project_name)

        req.redirect(backurl)
예제 #11
0
    def process_request(self, req):
        """ Process request for listing, creating and removing projects
        """
        add_script(req, 'multiproject/js/summary.js')
        data = {}

        if req.authname == 'anonymous':
            req.perm.require('PROJECT_VIEW')


        if not ('PROJECT_VIEW' in req.perm or 'PROJECT_PRIVATE_VIEW' in req.perm):
            return 'no_access.html', data, None

        # Load project from db
        project = Project.get(self.env)

        if req.args.get('action') == 'timeline_events':
            return self.get_summary_timeline_events(req, project.created)

        # TODO: Move project timeline implementation into macro
        # Get recent timeline events
        
        # TODO: Move project downloads implementation into macro
        # Get list of featured downloads
        downloads = []
        if self.env.is_component_enabled('tracdownloads.api.DownloadsApi'):
            api = None

            # In case of import error we don't have it, so don't return any downloads
            try:
                from tracdownloads.api import DownloadsApi
                api = DownloadsApi(self.env)
            except ImportError:
                self.log.warning("Unable to import DownloadsApi, but it's enabled.")

            if api:
                downloads = api.get_summary_items()

        # Load wiki:SummaryPage and show it as a main page
        summary_content = None
        summary_wiki = WikiPage(self.env, 'SummaryPage')
        if summary_wiki:
            context = Context.from_request(req, summary_wiki.resource)
            wiki_renderer = WikiTextRenderer(self.env)
            summary_content = wiki_renderer.render(context, 'text/x-trac-wiki', summary_wiki.text)

        
        data = {
            '_project_': project, # project object of a project we are in
            'downloads': downloads, # list of featured downloads
            'wiki_summary': summary_content, # Rendered content of the SummaryPage
            'is_summary': True,
            'env': self.env,
        }

        return 'summary.html', data, None
예제 #12
0
    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require('TRAC_ADMIN')

        protos = ProtocolManager(Project.get(self.env).id)
        scm_type = self.env.config.get('trac', 'repository_type')

        if req.method == 'POST':
            self._process_changes(req, protos, scm_type)

        return self._show_panel(req, protos, scm_type)
예제 #13
0
    def _notify_admins(self, permission, email_path, debug='false'):
        is_debug = debug.lower() in ('true', 'yes')

        if permission != 'TRAC_ADMIN':
            raise AdminCommandError('Only TRAC_ADMIN permission is supported')

        # A standard thing to do in IAdminCommandProviders (otherwise,
        # accessing project_identifier would give errors)
        if not hasattr(self.env, 'project_identifier'):
            MultiProjectEnvironmentInit(self.env).environment_needs_upgrade(None)

        env_name = self.env.project_identifier
        if env_name == self.env.config.get('multiproject', 'sys_home_project_name'):
            raise AdminCommandError('Command does not support home project')

        if not os.path.exists(email_path):
            raise AdminCommandError(_("Email template was not found!"))

        project = Project.get(self.env)
        email_template = ''
        try:
            with open(email_path) as fd:
                email_template = fd.read()
        except OSError as e:
            raise AdminCommandError(_("Error with opening file %(path)s: %(error_msg)s",
                path=email_path, error_msg=e))
        except Exception as e:
            raise AdminCommandError(_("Unknown error when parsing file %(path)s: %(error_msg)s",
                path=email_path, error_msg=e))
        email_template = [i.strip() for i in email_template.split('\n', 1) if i]
        if not len(email_template) > 1 or not all(email_template):
            raise AdminCommandError(_("Email template %(path)s was invalid.", path=email_path))

        subject, body = email_template
        text_template = NewTextTemplate(body)
        admins = project.get_admin_email_addresses()

        data = {'env_name': env_name}

        if is_debug:
            printout('## DEBUG MODE - NOT SENDING EMAILS ##')

        printout("project: {0}".format(env_name))
        printout("to: {0}".format(','.join(admins)))
        printout("subject: {0}".format(subject))
        printout("----------------------------")
        printout(text_template.generate(**data))
        printout("----------------------------")

        if not is_debug:
            notifier = EmailNotifier(self.env, subject=subject, data=data)
            notifier.template = text_template
            notifier.notify(admins)
            printout('Emails sent')
예제 #14
0
    def _notify_admins(self, permission, email_path, debug="false"):
        is_debug = debug.lower() in ("true", "yes")

        if permission != "TRAC_ADMIN":
            raise AdminCommandError("Only TRAC_ADMIN permission is supported")

        # A standard thing to do in IAdminCommandProviders (otherwise,
        # accessing project_identifier would give errors)
        if not hasattr(self.env, "project_identifier"):
            MultiProjectEnvironmentInit(self.env).environment_needs_upgrade(None)

        env_name = self.env.project_identifier
        if env_name == self.env.config.get("multiproject", "sys_home_project_name"):
            raise AdminCommandError("Command does not support home project")

        if not os.path.exists(email_path):
            raise AdminCommandError(_("Email template was not found!"))

        project = Project.get(self.env)
        email_template = ""
        try:
            with open(email_path) as fd:
                email_template = fd.read()
        except OSError as e:
            raise AdminCommandError(_("Error with opening file %(path)s: %(error_msg)s", path=email_path, error_msg=e))
        except Exception as e:
            raise AdminCommandError(
                _("Unknown error when parsing file %(path)s: %(error_msg)s", path=email_path, error_msg=e)
            )
        email_template = [i.strip() for i in email_template.split("\n", 1) if i]
        if not len(email_template) > 1 or not all(email_template):
            raise AdminCommandError(_("Email template %(path)s was invalid.", path=email_path))

        subject, body = email_template
        text_template = NewTextTemplate(body)
        admins = project.get_admin_email_addresses()

        data = {"env_name": env_name}

        if is_debug:
            printout("## DEBUG MODE - NOT SENDING EMAILS ##")

        printout("project: {0}".format(env_name))
        printout("to: {0}".format(",".join(admins)))
        printout("subject: {0}".format(subject))
        printout("----------------------------")
        printout(text_template.generate(**data))
        printout("----------------------------")

        if not is_debug:
            notifier = EmailNotifier(self.env, subject=subject, data=data)
            notifier.template = text_template
            notifier.notify(admins)
            printout("Emails sent")
예제 #15
0
    def post_process_request(self, req, template, data, content_type):
        """
        Puts global ``mproject`` variable into data
        """
        # Ensure the mproject is set
        data = data or {'mproject': None}

        if self.config.get('multiproject', 'sys_home_project_name', 'home') != self.env.path.split('/')[-1] and data:
            project = Project.get(self.env)
            data.update({'mproject': project})

        return template, data, content_type
예제 #16
0
    def make_membership_request(self, req):
        project = Project.get(self.env)

        # Make a request
        members = MembershipApi(self.env, project)
        members.request_membership(req.authname, req.args.get('message'))

        type = 'membership'
        if req.args.has_key('type'):
            type = req.args.get('type')

        return 'membership_requested.html', {'_project_':project, 'type':type}, None
예제 #17
0
    def render_admin_panel(self, req, cat, page, path_info):
        """ Overrides BasicsAdminPanel rendering function.

            Handle project info update (update global db) and then
            delegate handling back to BasicsAdminPanel
        """
        req.perm.require('TRAC_ADMIN')
        userstore = get_userstore()
        user = userstore.getUser(req.authname)
        project = Project.get(self.env)

        # Update database if form posted
        if req.method == 'POST':
            # Remove icon if requested
            if 'reset' in req.args:
                # NOTE: Icon is removed from filesystem already at this point
                self._unset_icon(req, project)
                project.icon_name = None

            # Update icon if set
            if not isinstance(req.args.get('icon', ''), basestring):
                icon_name = self._set_icon(req, project)
                if icon_name:
                    # If image is changed
                    if icon_name != project.icon_name:
                        self._unset_icon(req, project)
                    project.icon_name = icon_name
                else:
                    add_warning(req, 'Failed to set the project icon')

            # Save changes in database
            if 'apply' in req.args:
                self._apply_changes(req, project)

            # Reload page
            return req.redirect(req.href(req.path_info))

        data = {
            'user': user,
            'icon_size': self.icon_size,
            'mproject': project,
            'allow_public_projects': conf.allow_public_projects            
        }

        # Add javascript libraries for datepicker and autocomplete
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/admin_basics.js')

        Chrome(self.env).add_textarea_grips(req)
        return 'admin_basics_replacement.html', data
예제 #18
0
    def render_admin_panel(self, req, cat, page, path_info):
        """ Overrides BasicsAdminPanel rendering function.

            Handle project info update (update global db) and then
            delegate handling back to BasicsAdminPanel
        """
        req.perm.require('TRAC_ADMIN')
        userstore = get_userstore()
        user = userstore.getUser(req.authname)
        project = Project.get(self.env)

        # Update database if form posted
        if req.method == 'POST':
            # Remove icon if requested
            if 'reset' in req.args:
                # NOTE: Icon is removed from filesystem already at this point
                self._unset_icon(req, project)
                project.icon_name = None

            # Update icon if set
            if not isinstance(req.args.get('icon', ''), basestring):
                icon_name = self._set_icon(req, project)
                if icon_name:
                    # If image is changed
                    if icon_name != project.icon_name:
                        self._unset_icon(req, project)
                    project.icon_name = icon_name
                else:
                    add_warning(req, 'Failed to set the project icon')

            # Save changes in database
            if 'apply' in req.args:
                self._apply_changes(req, project)

            # Reload page
            return req.redirect(req.href(req.path_info))

        data = {
            'user': user,
            'icon_size': self.icon_size,
            'mproject': project,
            'allow_public_projects': conf.allow_public_projects
        }

        # Add javascript libraries for datepicker and autocomplete
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/admin_basics.js')

        Chrome(self.env).add_textarea_grips(req)
        return 'admin_basics_replacement.html', data
예제 #19
0
    def createProject(self, req, projectid, projectname, description, project_visibility, serviceslist):
        """ Request to create a new project
        """
        services = {}

        if str(projectid).strip() == '':
            e = exceptions.Exception
            raise e("Incorrect project identification name")

        if str(projectname).strip() == '':
            e = exceptions.Exception
            raise e("Incorrect project name")

        users = get_userstore()
        author = users.getUser(req.authname)

        if not author.can_create_project():
            raise Exception("You are not allowed to create projects")
        
        public = False
        published = None
        if project_visibility == "on" or project_visibility == "true":
            public = True
            published = datetime.now()

        # Create project class
        project = Project(id=None,
                          env_name=unicode(projectid),
                          project_name=projectname,
                          description=description,
                          author_id=author.id,
                          created=None,
                          public=public,
                          published=published)

        if project_visibility == "on" or project_visibility == "true":
            services['project_visibility'] = 'on'
        else:
            services['project_visibility'] = 'off'

        projects = Projects()
        projects.getServices(services, serviceslist)

        # Create project
        try:
            projects.create_project(project, services)
            return self.get_scm_repository_url(project.env_name)
        except ProjectValidationException as exc:
            raise Exception(exc.value)
        except:
            raise Exception("Creating project failed. Try again later.")
예제 #20
0
    def openProject(self, req, projectname, revision, extensionlist):
        """ Returns project's connection string and repository file list matched to search criteria.
        """
        # Return values
        if str(projectname).strip() == '':
            e = exceptions.Exception
            raise e("Incorrect project name")

        if revision.strip() == '':
            revision = None
        if extensionlist.strip() == '':
            extensionlist = None

        # Find node for the requested path/rev
        repomgr = RepositoryManager(self.env)
        repository = repomgr.get_repository(None)
        projectname = conf.cleanupProjectName(projectname)
        parts = []

        project = Project.get(env_name=projectname)
        if project:
            parts.append(self.get_scm_repository_url(project.env_name))
        else:
            return []

        try:
            if revision:
                revision = repository.normalize_rev(revision)
            rev_or_latest = revision or repository.youngest_rev

            getfiles = []
            self._getfiles(req, getfiles, repository, '', rev_or_latest)

            if extensionlist:
                extensions = extensionlist.split(',')
                searchresult = []
                for file in getfiles:
                    extension = os.path.splitext(str(file))[1].strip('.')
                    if extension in extensions:
                        searchresult.append(file)
                addfiles = ",".join(searchresult)
            else:
                addfiles = ",".join(getfiles)

            if addfiles:
                # Append version control files
                parts.append('versioncontrolfiles|' + addfiles)
        except Exception:
            self.log.exception("ProjectsRPC.openProject failed")
        return parts
예제 #21
0
    def openProject(self, req, projectname, revision, extensionlist):
        """ Returns project's connection string and repository file list matched to search criteria.
        """
        # Return values
        if str(projectname).strip() == '':
            e = exceptions.Exception
            raise e("Incorrect project name")

        if revision.strip() == '':
            revision = None
        if extensionlist.strip() == '':
            extensionlist = None

        # Find node for the requested path/rev
        repomgr = RepositoryManager(self.env)
        repository = repomgr.get_repository(None)
        projectname = conf.cleanupProjectName(projectname)
        parts = []

        project = Project.get(env_name=projectname)
        if project:
            parts.append(self.get_scm_repository_url(project.env_name))
        else:
            return []

        try:
            if revision:
                revision = repository.normalize_rev(revision)
            rev_or_latest = revision or repository.youngest_rev

            getfiles = []
            self._getfiles(req, getfiles, repository, '', rev_or_latest)

            if extensionlist:
                extensions = extensionlist.split(',')
                searchresult = []
                for file in getfiles:
                    extension = os.path.splitext(str(file))[1].strip('.')
                    if extension in extensions:
                        searchresult.append(file)
                addfiles = ",".join(searchresult)
            else:
                addfiles = ",".join(getfiles)

            if addfiles:
                # Append version control files
                parts.append('versioncontrolfiles|' + addfiles)
        except Exception:
            self.log.exception("ProjectsRPC.openProject failed")
        return parts
예제 #22
0
    def get_latest_timeline_events(self, req, count, project_created=None):
        """
        Returns latest (within 10 days) timeline events.
        If count is given, returns only given number of elements.

        :param Request req: Trac request
        :param int count: Number of elements to returns. Defaults to all
        :returns: List of events
        """
        events = []
        available_filters = []

        for event_provider in self.event_providers:
            available_filters += event_provider.get_timeline_filters(req) or []

        # TODO: make this incredibly obscure piece of code readable
        # check the request or session for enabled filters, or use default
        filters = []
        filters_list = []
        for afilter in available_filters:
            filters_list.append(afilter[0])
        filter_set = set(filters_list)
        for item in filter_set:
            filters.append(item)

        # start time of timeline is last update of if not known, last two monts
        if not project_created:
            project = Project.get(self.env)

            project_start_date = project.created
        else:
            project_start_date = project_created
        project_start_date = project_start_date.replace (tzinfo = datefmt.localtz)

        # do the actual event querying
        for provider in self.event_providers:
            todate = datetime.now(datefmt.localtz)
            fromdate = todate - timedelta(days = 10)
            eventcount = 0
            while eventcount < count and todate > project_start_date:
                for event in provider.get_timeline_events(req, fromdate, todate, filters):
                    eventcount += 1
                    events.append(self._event_data(provider, event))
                todate = fromdate
                fromdate = todate - timedelta(days = 10)
        events.sort(lambda x, y: cmp(y['date'], x['date']))

        # Note, when count = None, all the events are returned
        return events[:count] if events else []
예제 #23
0
    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require('TRAC_ADMIN')

        project = Project.get(self.env)
        child_projects = project.get_child_projects()

        data = {
            '_project_': project,
            'parent_project': project.parent_project,
            'child_projects': child_projects,
            'home': conf.url_home_path
        }

        add_script(req, 'multiproject/js/admin_relations.js')
        return 'multiproject_admin_relations.html', data
    def notify_now(self, notification_frequency):
        """
        Send notification email to project watchers.

        :param str notification_frequency: The notification frequency, used to fetch
            users which have something on that watchlist.
        """
        store = CQDEWatchlistStore()
        watches = store.get_by_notification(notification_frequency)

        notifylist = {}
        for w in watches:
            if notifylist.get(w.project_id) is None:
                notifylist[w.project_id] = []
            notifylist[w.project_id].append(w.user_id)

        userstore = get_userstore()

        counter = 0
        for project_id in notifylist.keys():
            project = Project.get(id=project_id)

            # Get all events for this project
            events = self._get_project_events(project, notification_frequency)

            # Send one email per user, because everyone may have a different
            # set of permissions (i.e. which events he/she has permission to see)
            for user_id in notifylist[project_id]:
                user = userstore.getUserWhereId(user_id)
                if user:
                    # filter eventlist by user's permissions
                    filtered_events = WatchlistEvents(self.env).filter_events(events, user, project)
                    if filtered_events:
                        addresses = [user.mail]
                        message = self._format_message(user, project, filtered_events)
                        title = "Project updated: %s" % project.env_name
                        mail = EmailNotifier(self.env, subject=title, data={'body':message})
                        mail.notify_emails(addresses)
                        self.env.log.debug('Sent email notification to: %s' % user)
                        counter += 1
                    else:
                        if notification_frequency != 'immediate':
                            self.env.log.debug('No events to sent to %s about %s' % (user, project))
                else:
                    self.env.log.warning('User %d in notification list was not found in userstore' % user_id)

        # Log the results
        self.env.log.info('Sent %s watchlist notifications (%s)' % (counter, notification_frequency))
예제 #25
0
    def refresh_project(self, project_identifier, from_date, to_date=datetime.now(datefmt.localtz),
                        afilters=None, update=False):
        """
        Will refresh a project events in cache in given date range.

        .. NOTE::

            Dates needs to be given in datefmt.localtz form

        """
        # Initialize objects
        project = Project.get(env_name=project_identifier)
        if not project:
            conf.log.warning('Project {0} is already removed from system or it cannot be found'.format(project_identifier))
            return

        e = open_environment(conf.getEnvironmentSysPath(project.env_name), use_cache=True)
        pte = ProjectTimelineEvents(e)
        providers = pte.event_providers

        project_href = Href(conf.url_projects_path + '/' + project.env_name)

        context = Context(resource=Resource(), href=project_href)
        req = self._create_dummy_req(project_identifier)
        context.req = req
        context.perm = req.perm

        # Read events from timeline
        events = []
        for provider in providers:
            try:
                # Use filters in parameter or check filters from providers
                if afilters:
                    filters = afilters
                else:
                    available_filters = provider.get_timeline_filters(req) or []
                    filters = [f[0] for f in available_filters]

                for event in provider.get_timeline_events(req, from_date, to_date, filters):
                    event_data = self._event_data(provider, event)
                    if event_data['author'] != 'trac': # Skip system events
                        events.append(CachedEvent.from_event_data(project, event_data, context, filters[0]))
            except:
                conf.log.error("Could not read timeline events for %s from %s" % (project_identifier, str(provider)))

        # Write events into sql table
        self._write_events_into_cache(events, update)
예제 #26
0
    def _get_watchlist_events(self, user):
        watchlist = CQDEWatchlistStore().get_projects_by_user(user.id)
        events = []
        event_helper = WatchlistEvents(self.env)

        # TODO: inefficient querying
        for watch in watchlist:
            project = Project.get(id=watch.project_id)
            project_events = event_helper.get_project_events(project, days = 7, minutes = 0)
            # filter eventlist by user's permissions
            project_events = event_helper.filter_events(project_events, user, project)
            if project_events:
                # show only one event per project
                events.append(project_events[0])

        events.sort(lambda x, y: cmp(y[1]['date'], x[1]['date']))
        return events
예제 #27
0
    def expand_macro(self, formatter, name, content):
        """
        Execute / Render the macro. Return the content to caller.
        """
        if name not in self.macros:
            return None

        identifier = self.env.project_identifier
        project = Project.get(self.env)

        if project is None:
            return None

        if name == 'ProjectName':
            return project.project_name

        elif name == 'ProjectIdentifier':
            return identifier

        elif name == 'ProjectOwner':
            return project.author.username

        elif name == 'ProjectCreateDate':
            return project.created

        elif name == 'ProjectUrl':
            url = project.get_url()

            txt = content or identifier
            return html.a(txt, href=url)

        elif name == 'ProjectVersioncontrolUrl':
            url = project.get_repository_url()

            txt = content or url
            return html.a(txt, href=url)

        elif name == 'ProjectWebDavUrl':
            url = project.get_dav_url()

            txt = content or url
            return html.a(txt, href=url)

        elif name == 'WelcomeText':
            return html.h1("Welcome to " + project.project_name,
                           id="WelcomeToProject")
예제 #28
0
    def expand_macro(self, formatter, name, content):
        """
        Execute / Render the macro. Return the content to caller.
        """
        if name not in self.macros:
            return None

        identifier = self.env.project_identifier
        project = Project.get(self.env)

        if project is None:
            return None

        if name == 'ProjectName':
            return project.project_name

        elif name == 'ProjectIdentifier':
            return identifier

        elif name == 'ProjectOwner':
            return project.author.username

        elif name == 'ProjectCreateDate':
            return project.created

        elif name == 'ProjectUrl':
            url = project.get_url()

            txt = content or identifier
            return html.a(txt, href = url)

        elif name == 'ProjectVersioncontrolUrl':
            url = project.get_repository_url()

            txt = content or url
            return html.a(txt, href = url)

        elif name == 'ProjectWebDavUrl':
            url = project.get_dav_url()

            txt = content or url
            return html.a(txt, href = url)

        elif name == 'WelcomeText':
            return html.h1("Welcome to " + project.project_name, id = "WelcomeToProject")
예제 #29
0
    def setUp(self):
        userObj = User()
        userObj.id = 30
        userObj.username = '******'

        self.projectObj = Project(24,
                                  'storageauthtest',
                                  'Storage auth testing',
                                  'Desc',
                                  userObj.id,
                                  None,
                                  author=userObj)
        conf.use_test_db(True)
        self.load_fixtures()
        self.store = CQDEUserGroupStore(self.projectObj.id)
        self.store.remove_group('Owners')
        self.store.remove_group('Public contributors')
        self.store.remove_group('Public viewers')
예제 #30
0
    def notify_project(self, data, project_id=None, store=True):
        # Add timestamp if not already set
        if 'created' not in data:
            data.update({'created': datetime.utcnow().isoformat()})

        if not project_id:
            project_id = Project.get(self.env).id

        # Load notification system and used it for sending - unless disabled
        ns = self.env[NotificationSystem]
        if not ns:
            return

        try:
            count = ns.send_notification([ns.generate_channel_name(project_id=project_id)], data, store)
            self.log.info('Sent project wide notification to project (%s) about: %s and %s received it' % (project_id, data, count))
        except TracError, err:
            self.log.warning('Failed to send notification to project (%s): %s' % (project_id, err))
    def _get_watchlist_events(self, req):
        events = []
        user = get_userstore().getUser(req.authname)
        if not user:
            return None

        watchlist = CQDEWatchlistStore().get_projects_by_user(user.id)
        event_helper = WatchlistEvents(self.env)
        # TODO: inefficient querying
        for watch in watchlist:
            project = Project.get(id=watch.project_id)
            project_events = event_helper.get_project_events(project, days=7, minutes=0)
            # filter eventlist by user's permissions
            project_events = event_helper.filter_events(project_events, user, project)
            if project_events:
                events.extend(project_events)

        events.sort(lambda x, y: cmp(y[1]["date"], x[1]["date"]))
        return events
    def render_preference_panel(self, req, panel):

        if req.authname == 'anonymous':
            raise TracError("Can't change preferences!", "No access!")

        if req.method == 'POST':
            self.save_watchlist(req)

        watchlist = []
        projects = {}
        user = get_userstore().getUser(req.authname)
        w = CQDEWatchlistStore()
        if user:
            watchlist = w.get_projects_by_user(user.id)
            # TODO: inefficient querying
            for watch in watchlist:
                projects[watch.project_id] = Project.get(id=watch.project_id)
        return 'multiproject_user_prefs_watchlist.html', { 'watchlist': watchlist, 'projects' : projects,
            'notification_values': w.notifications }
예제 #33
0
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders announcements admin panel
        """
        req.perm.require('TRAC_ADMIN')

        showforum = self.config.getbool('discussion', 'show_news_forum', True)

        project = Project.get(self.env)
        groupstore = CQDEUserGroupStore(project.trac_environment_key)
        (public_exists, allowed) = self.get_announce_allowed(groupstore)

        if req.method == 'POST':
            if 'hideforum' in req.args:
                self.config.set('discussion', 'show_news_forum', 'false')
                self.config.save()
                showforum = False
                # Notify listeners
                for listener in self.announcement_admin_listeners:
                    listener.announcements_hidden()
            elif 'showforum' in req.args:
                self.config.set('discussion', 'show_news_forum', 'true')
                self.config.save()
                showforum = True
                # Notify listeners
                for listener in self.announcement_admin_listeners:
                    listener.announcements_visible()
            elif 'allow' in req.args:
                #Allow should add the DISCUSSION_ANNOUNCE_APPEND to the "public contributors" group.
                groupstore.grant_permission_to_group("Public contributors", "DISCUSSION_ANNOUNCEAPPEND")
                (public_exists, allowed) = self.get_announce_allowed(groupstore)
            elif 'disallow' in req.args:
                #Disallow remove it.
                groupstore.revoke_permission_from_group("Public contributors", "DISCUSSION_ANNOUNCEAPPEND")
                (public_exists, allowed) = self.get_announce_allowed(groupstore)
            elif 'rename' in req.args:
                news = ProjectNews(project.env_name)
                news.rename_news_forum(req.args.get('forumname'))

        data = {}
        data['showforum'] = showforum
        data['allowed'] = allowed
        data['public_exists'] = public_exists
        return 'admin_news_forum.html', data
예제 #34
0
    def expand_macro(self, formatter, name, content, args=None):
        """
        Returns the outcome from macro.
        Supported arguments:

        - project: Name of the project to show status / provide follow buttons. Defaults to current project

        """
        req = formatter.req

        # Load project from db
        project = Project.get(self.env)

        # Get project metadata (categories)
        (combined_categories, separated_categories_per_context, context_by_id,
         context_order, languages) = self._get_project_categories(project)

        # Get project visibility: public or private
        visibility = _('Public') if project.public else _('Private')

        # Return rendered HTML with JS attached to it
        data = {
            '_project_': project,
            'combined_categories': combined_categories,
            'separated_categories_per_context':
            separated_categories_per_context,
            'context_order': context_order,
            'languages': languages,
            'context_by_id': context_by_id,
            'visibility_label': visibility,  # Private / Public
            'to_web_time': to_web_time  # TODO: Is this really required?
        }

        chrome = Chrome(self.env)
        stream = chrome.render_template(req,
                                        'multiproject_summary.html',
                                        data,
                                        fragment=True)
        if req.form_token:
            stream |= chrome._add_form_token(req.form_token)

        return stream
예제 #35
0
    def has_permission(self):

        identifier = self.environment_identifier()
        if not identifier:
            conf.log.warning("Failed reading identifier")
            return False

        # We need environment for permission check, get it .. albeit ugly
        # TODO: this probably has performance hit as environment is not loaded with caching!
        if identifier == conf.sys_home_project_name:
            env = HomeProject().get_env()
        else:
            proj = Project.get(env_name=identifier)
            env = proj.get_env()

        env.log.debug('basic_access checking has_permission %s for %s' %
                      (self.required_action, self.username))

        perms = env[PermissionSystem].get_user_permissions(self.username)
        return perms.get(self.required_action, False)
예제 #36
0
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders storage admin panel page
        """
        req.perm.require("TRAC_ADMIN")

        project = Project.get(self.env)
        vcs_type = conf.getVersionControlType(self.env.project_identifier)  # TODO: deprecate / heavy call!
        vcs_url = project.get_repository_url()

        data = {
            "vcs_name": conf.getVersionControlName(vcs_type),
            "vcs_type": vcs_type,
            "vcs_url": vcs_url,
            "dav_url": project.get_dav_url(),
            "vcs_cmd": "clone",
        }

        if vcs_type == "svn":
            data["vcs_cmd"] = "checkout"

        return "storages.html", data
예제 #37
0
    def save_watchlist(self, req):
        user = get_userstore().getUser(req.authname)
        if not user:
            return

        w = CQDEWatchlistStore()
        if req.args.get('notification'):
            notifylist = self.__to_list(req.args.get('notification'))
            for item in notifylist:
                project_id, notification = item.split('_')
                w.watch_project(user.id, project_id, notification)
                
                # Notify listeners.
                project = Project.get(id=project_id)
                for listener in self.project_change_listeners:
                    listener.project_watchers(project)

        if req.args.get('remove'):
            removelist = self.__to_list(req.args.get('remove'))
            for project_id in removelist:
                w.unwatch_project(user.id, project_id)
예제 #38
0
    def test_validate(self):
        invalid_identifier = Project(1, u'testi_', u'testi_', u'Long name', 1,
                                     None)
        toolong_identifier = Project(1, u'asdfghjklqwertyuiopzxcvbnmasdfahg',
                                     u'asdfghjklqwertyuiopzxcvbnmasdfahg',
                                     u'Long name', 1, None)

        # Test invalid identifier
        msg = ""
        try:
            invalid_identifier.validate()
        except ProjectValidationException as exc:
            msg = exc.value
        self.assertTrue(
            msg.startswith("Identifier can not start or end with underscore"))

        # Test too long identifier
        msg = ""
        try:
            toolong_identifier.validate()
        except ProjectValidationException as exc:
            msg = exc.value
        self.assertTrue(msg.startswith("Too long project indentifier. L"))
    def _get_watchlist_events(self, req):
        events = []
        user = get_userstore().getUser(req.authname)
        if not user:
            return None

        watchlist = CQDEWatchlistStore().get_projects_by_user(user.id)
        event_helper = WatchlistEvents(self.env)
        # TODO: inefficient querying
        for watch in watchlist:
            project = Project.get(id=watch.project_id)
            project_events = event_helper.get_project_events(project,
                                                             days=7,
                                                             minutes=0)
            # filter eventlist by user's permissions
            project_events = event_helper.filter_events(
                project_events, user, project)
            if project_events:
                events.extend(project_events)

        events.sort(lambda x, y: cmp(y[1]['date'], x[1]['date']))
        return events
예제 #40
0
 def files_node_factory_and_config(self, req=None, context_name='files'):
     """
     :param req: Request object
     :param str context_name: either 'files' or 'webdav'
     :return:
     """
     ctx = None
     ctx_key = 'files.node_factory.' + context_name
     if req:
         ctx = get_context(req)
         try:
             return ctx[ctx_key], self.files_download_config(context_name, req=req)
         except KeyError:
             pass
     download_config = self.files_download_config(context_name=context_name, req=req)
     project = Project.get(self.env)
     project_id = project.id
     node_factory = MappedFileNode(project_id,
         download_config.base_path, download_config.base_url, download_config.downloads_dir)
     if ctx:
         ctx[ctx_key] = node_factory
     return node_factory, download_config
예제 #41
0
    def expand_macro(self, formatter, name, content, args=None):
        """
        Returns the outcome from macro.
        Supported arguments:

        - project: Name of the project to show status / provide follow buttons. Defaults to current project

        """
        req = formatter.req

        # Load project from db
        project = Project.get(self.env)

        # Get project metadata (categories)
        (combined_categories, separated_categories_per_context, context_by_id,
         context_order, languages) = self._get_project_categories(project)

        # Get project visibility: public or private
        ug = CQDEUserGroupStore(project.trac_environment_key)
        visibility = _('Public') if ug.is_public_project() else _('Private')

        # Return rendered HTML with JS attached to it
        data = {
            '_project_': project,
            'combined_categories': combined_categories,
            'separated_categories_per_context': separated_categories_per_context,
            'context_order': context_order,
            'languages': languages,
            'context_by_id': context_by_id,
            'visibility_label': visibility, # Private / Public
            'to_web_time': to_web_time # TODO: Is this really required?
        }

        chrome = Chrome(self.env)
        stream = chrome.render_template(req, 'multiproject_summary.html', data, fragment=True)
        if req.form_token:
            stream |= chrome._add_form_token(req.form_token)
        return stream
예제 #42
0
    def process_request(self, req):
        req.perm.require('MEMBERSHIP_REQUEST_CREATE')
        all_members = {}

        project = Project.get(self.env)
        ug = CQDEUserGroupStore(project.trac_environment_key)
        for member, group in ug.get_all_user_groups():
            all_members[member] = 1

        if req.authname in all_members.keys():
            type = "privilege"
        else:
            type = "membership"

        # Check the message length only if user has clicked 'request'
        msg_ok = 'request' not in req.args or ('message' in req.args and
            len(req.args.get('message')) >= 40)

        # If request, then make request, ow. show form
        if req.args.has_key('request') and msg_ok:
            return self.make_membership_request(req)
        else:
            return 'membership_request_form.html', {'type':type, 'msg_ok':msg_ok,
                                                    'message':req.args.get('message')}, None
예제 #43
0
    def post_process_request(self, req, template, data, content_type):
        if template == 'browser.html':
            username = urllib.quote(req.authname)
            scm = self.env.config.get('trac', 'repository_type')

            project = Project.get(self.env)

            names = {'git':'GIT', 'svn':'Subversion', 'hg':'Mercurial'}
            cmd_kinds = {'git':'Clone', 'hg':'Clone', 'svn':'Check out'}

            type = names[scm]
            schemes = self.protocols(project, scm)

            data['kinds'] = cmd_kinds
            data['schemes'] = schemes
            data['name'] = names[scm]
            data['type'] = scm

            co_commands = {}
            for scheme in schemes:
                co_commands[scheme] = self.create_co_command(scm, username, scheme)
            data['co_commands'] = co_commands

        return template, data, content_type