1. Alex Willmer
  2. trac-ticketlinks

Commits

cboos  committed 32aac19

0.12dev: merged changes from 0.11-stable [7350-7352,7356-7363]

  • Participants
  • Parent commits bd3d64f
  • Branches trunk

Comments (0)

Files changed (18)

File ChangeLog

View file
+Trac 0.11.1 (? ?, 2008)
+http://svn.edgewall.org/repos/trac/tags/trac-0.11.1
+
+ Trac 0.11.1 contains a number of bug fixes and minor enhancements.
+ The following list contains only a few highlights:
+
+ * Safer default umask value for tracd (can be set using --umask option)
+ * Improved DB connection handling (new connection pool)
+
+ The complete list of closed tickets can be found here:
+   http://trac.edgewall.org/query?status=closed&milestone=0.11.1
+
 Trac 0.11 'Genshi' (June 22, 2008)
 http://svn.edgewall.org/repos/trac/tags/trac-0.11
 

File THANKS

View file
  * Christopher Armstrong          radix
  * Jani Averbach                  jaa@jaa.iki.fi
  * Juanma Barranquero             lektu@terra.es
+ * Remy Blank                     remy.blank@pobox.com
  * Christian Boos                 cboos@bct-technology.com
  * Rocky Burt                     rocky.burt@myrealbox.com
  * Toni Brkic                     toni.brkic@switchcore.com

File trac/admin/console.py

View file
 from trac.util.datefmt import parse_date, format_date, format_datetime, utc
 from trac.util.html import html
 from trac.util.text import to_unicode, wrap, unicode_quote, unicode_unquote, \
-                           print_table
+                           print_table, console_print
 from trac.util.translation import _
 from trac.wiki import WikiPage
 from trac.wiki.api import WikiSystem
     Added a `skip` parameter consisting of absolute paths
     which we don't want to copy.
     """
-    names = os.listdir(src)
-    makedirs(dst, overwrite=overwrite)
-    errors = []
-
-    def remove_if_overwriting(path):
-        if overwrite and os.path.exists(path):
-            os.unlink(path)
-
-    for name in names:
-        srcname = os.path.join(src, name)
-        if srcname in skip:
-            continue
-        dstname = os.path.join(dst, name)
-        try:
-            if symlinks and os.path.islink(srcname):
-                remove_if_overwriting(dstname)
-                linkto = os.readlink(srcname)
-                os.symlink(linkto, dstname)
-            elif os.path.isdir(srcname):
-                copytree(srcname, dstname, symlinks, skip, overwrite)
-            else:
-                remove_if_overwriting(dstname)
-                shutil.copy2(srcname, dstname)
-            # XXX What about devices, sockets etc.?
-        except EnvironmentError, why:
-            errors.append((srcname, dstname, why))
-    if errors:
-        raise shutil.Error, errors
+    def str_path(path):
+        if isinstance(path, unicode):
+            path = path.encode(sys.getfilesystemencoding() or
+                               locale.getpreferredencoding())
+        return path
+    skip = [str_path(f) for f in skip]
+    def copytree_rec(src, dst):
+        names = os.listdir(src)
+        makedirs(dst, overwrite=overwrite)
+        errors = []
+        for name in names:
+            srcname = os.path.join(src, name)
+            if srcname in skip:
+                continue
+            dstname = os.path.join(dst, name)
+            try:
+                if symlinks and os.path.islink(srcname):
+                    remove_if_overwriting(dstname)
+                    linkto = os.readlink(srcname)
+                    os.symlink(linkto, dstname)
+                elif os.path.isdir(srcname):
+                    copytree_rec(srcname, dstname)
+                else:
+                    remove_if_overwriting(dstname)
+                    shutil.copy2(srcname, dstname)
+                # XXX What about devices, sockets etc.?
+            except EnvironmentError, why:
+                errors.append((srcname, dstname, why))
+        if errors:
+            raise shutil.Error, errors
+    copytree_rec(str_path(src), str_path(dst))
 
 
 class TracAdmin(cmd.Cmd):
         """`line` may be a `str` or an `unicode` object"""
         try:
             if isinstance(line, str):
-                line = to_unicode(line, sys.stdin.encoding)
+                if self.interactive:
+                    encoding = sys.stdin.encoding
+                else:
+                    encoding = locale.getpreferredencoding() # sys.argv
+                line = to_unicode(line, encoding)
             line = line.replace('\\', '\\\\')
             rv = cmd.Cmd.onecmd(self, line) or 0
         except SystemExit:
             raise
         except TracError, e:
-            print>>sys.stderr, 'Command failed: %s' % e
+            console_print(sys.stderr, 'Command failed:', e)
             rv = 2
         if not self.interactive:
             return rv
                 self.__env = Environment(self.envname)
             return self.__env
         except Exception, e:
-            print 'Failed to open environment.', e
+            console_print(sys.stderr, 'Failed to open environment.', e)
             traceback.print_exc()
             sys.exit(1)
 
             stream = sys.stdout
         if not docs: return
         for cmd, doc in docs:
-            print>>stream, cmd
-            print>>stream, '\t-- %s\n' % doc
+            console_print(stream, cmd)
+            console_print(stream, '\t-- %s\n' % doc)
     print_doc = classmethod(print_doc)
 
     def get_component_list(self):
                 doc = getattr(self, "_help_" + arg[0])
                 self.print_doc(doc)
             except AttributeError:
-                print "No documentation found for '%s'" % arg[0]
+                console_print(sys.stderr, "No documentation found for '%s'" % 
+                              arg[0])
         else:
             print 'trac-admin - The Trac Administration Console %s' \
                   % TRAC_VERSION
         return returnvals
 
     def do_initenv(self, line):
+        def initenv_error(msg):
+            console_print(sys.stderr, "Initenv for '%s' failed.\n%s" % 
+                          (self.envname, msg))
         if self.env_check():
-            print "Initenv for '%s' failed." % self.envname
-            print "Does an environment already exist?"
+            initenv_error("Does an environment already exist?")
             return 2
 
         if os.path.exists(self.envname) and os.listdir(self.envname):
-            print "Initenv for '%s' failed." % self.envname
-            print "Directory exists and is not empty."
+            initenv_error("Directory exists and is not empty.")
             return 2
 
         arg = self.arg_tokenize(line)
             returnvals = self.get_initenv_args()
             project_name, db_str, repository_type, repository_dir = returnvals
         elif len(arg) != 4:
-            print 'Wrong number of arguments to initenv: %d' % len(arg)
+            initenv_error('Wrong number of arguments: %d' % len(arg))
             return 2
         else:
             project_name, db_str, repository_type, repository_dir = arg[:4]
                 self.__env = Environment(self.envname, create=True,
                                          options=options)
             except Exception, e:
-                print 'Failed to create environment.', e
+                initenv_error('Failed to create environment.')
+                console_print(sys.stderr, e)
                 traceback.print_exc()
                 sys.exit(1)
 
                         print ' Indexing repository'
                         repos.sync(self._resync_feedback)
                 except TracError, e:
-                    print>>sys.stderr, "\nWarning:\n"
+                    console_print(sys.stderr, "\nWarning:\n")
                     if repository_type == "svn":
-                        print>>sys.stderr, "You should install the SVN bindings"
+                        console_print(sys.stderr, 
+                                      "You should install the SVN bindings")
                     else:
-                        print>>sys.stderr, "Repository type %s not supported" \
-                                           % repository_type
+                        console_print(sys.stderr, 
+                                      "Repository type %s not supported" %
+                                      repository_type)
         except Exception, e:
-            print 'Failed to initialize environment.', e
+            initenv_error(to_unicode(e))
             traceback.print_exc()
             return 2
 
                              "ORDER BY version DESC LIMIT 1", cursor,
                              params=(title,))
         old = list(rows)
-        cons_charset = getattr(sys.stdout, 'encoding', None) or 'utf-8'        
         if old and title in create_only:
-            print '  %s already exists.' % title.encode(cons_charset)
+            console_print(sys.stdout, '  %s already exists.' % title)
             return False
         if old and data == old[0][0]:
-            print '  %s already up to date.' % title.encode(cons_charset)
+            console_print(sys.stdout, '  %s already up to date.' % title)
             return False
         f.close()
 
 
     def _do_wiki_dump(self, dir):
         pages = self.get_wiki_list()
-        cons_charset = getattr(sys.stdout, 'encoding', None) or 'utf-8'
         if not os.path.isdir(dir):
             if not os.path.exists(dir):
                 os.mkdir(dir)
             else:
-                raise TracError("%s is not a directory" % \
-                                                dir.encode(cons_charset))
+                raise TracError("%s is not a directory" % dir)
         for p in pages:
             dst = os.path.join(dir, unicode_quote(p, ''))
-            print (" %s => %s" % (p, dst)).encode(cons_charset)
+            console_print(sys.stdout, " %s => %s" % (p, dst))
             self._do_wiki_export(p, dst)
 
     def _do_wiki_load(self, dir, cursor=None, ignore=[], create_only=[]):
             try:
                 number = int(arg[1])
             except ValueError:
-                print>>sys.stderr, "<number> must be a number"
+                console_print(sys.stderr, "<number> must be a number")
                 return
             self._do_ticket_remove(number)
         else:    
         elif args[0] in ('-v','--version'):
             print '%s %s' % (os.path.basename(sys.argv[0]), TRAC_VERSION)
         else:
-            admin.env_set(os.path.abspath(args[0]))
+            env_path = os.path.abspath(args[0])
+            try:
+                unicode(env_path, 'ascii')
+            except UnicodeDecodeError:
+                console_print(sys.stderr, _("non-ascii environment path "
+                                            "'%(path)s' not supported.",
+                                            path=env_path))
+                sys.exit(2)
+            admin.env_set(env_path)
             if len(args) > 1:
                 s_args = ' '.join(["'%s'" % c for c in args[2:]])
                 command = args[1] + ' ' +s_args

File trac/admin/web_ui.py

View file
         except AttributeError:
             # OS_BINARY not available on every platform
             pass
-        target_file = os.fdopen(os.open(target_path, flags), 'w')
+        target_file = os.fdopen(os.open(target_path, flags, 0666), 'w')
         try:
             shutil.copyfileobj(upload.file, target_file)
             self.log.info('Plugin %s installed to %s', plugin_filename,

File trac/attachment.py

View file
 from trac.mimeview import *
 from trac.perm import PermissionError, PermissionSystem, IPermissionPolicy
 from trac.resource import *
+from trac.search import search_to_sql, shorten_result
 from trac.util import get_reporter_id, create_unique_file, content_disposition
 from trac.util.datefmt import to_timestamp, utc
 from trac.util.text import unicode_quote, unicode_unquote, pretty_size
             return format_to_oneliner(self.env, context(attachment.parent),
                                       descr)
    
+    def get_search_results(self, req, resource_realm, terms):
+        """Return a search result generator suitable for ISearchSource.
+        
+        Search results are attachments on resources of the given 
+        `resource_realm.realm` whose filename, description or author match 
+        the given terms.
+        """
+        db = self.env.get_db_cnx()
+        sql_query, args = search_to_sql(db, ['filename', 'description', 
+                                        'author'], terms)
+        cursor = db.cursor()
+        cursor.execute("SELECT id,time,filename,description,author "
+                       "FROM attachment "
+                       "WHERE type = %s "
+                       "AND " + sql_query, (resource_realm.realm, ) + args)
+        
+        for id, time, filename, desc, author in cursor:
+            attachment = resource_realm(id=id).child('attachment', filename)
+            if 'ATTACHMENT_VIEW' in req.perm(attachment):
+                yield (get_resource_url(self.env, attachment, req.href),
+                       get_resource_shortname(self.env, attachment),
+                       datetime.fromtimestamp(time, utc), author,
+                       shorten_result(desc, terms))
+    
     # IResourceManager methods
     
     def get_resource_realms(self):
 
     def get_resource_description(self, resource, format=None, **kwargs):
         if format == 'compact':
-            return '%s:%s' % (get_resource_shortname(self.env,
-                                                     resource.parent),
-                              resource.filename)
+            return '%s (%s)' % (resource.id,
+                    get_resource_name(self.env, resource.parent))
         elif format == 'summary':
             return Attachment(self.env, resource).description
         if resource.id:

File trac/loader.py

View file
 def load_eggs(entry_point_name):
     """Loader that loads any eggs on the search path and `sys.path`."""
     def _load_eggs(env, search_path, auto_enable=None):
+        # Note that the following doesn't seem to support unicode search_path
         distributions, errors = working_set.find_plugins(
             pkg_resources.Environment(search_path)
         )

File trac/mimeview/api.py

View file
 
         Note: `content` will usually be an object with a `read` method.
         """        
-        data = {'raw_href': url,
+        data = {'raw_href': url, 'size': length,
                 'max_file_size': self.max_preview_size,
                 'max_file_size_reached': False,
                 'rendered': None,

File trac/mimeview/pygments.py

View file
         add_stylesheet(req, '/pygments/%s.css' %
                        req.session.get('pygments_style', self.default_style))
         try:
-            mimetype = mimetype.split(';', 1)[0]
-            language = self._types[mimetype][0]
-            return self._generate(language, content)
+            if len(content) > 0:
+                mimetype = mimetype.split(';', 1)[0]
+                language = self._types[mimetype][0]
+                return self._generate(language, content)
         except (KeyError, ValueError):
             raise Exception("No Pygments lexer found for mime-type '%s'."
                             % mimetype)

File trac/mimeview/tests/pygments.py

View file
         t = "".join([r[1] for r in result if r[0] is TEXT])
         self.assertEqual("\n\n\n", t)
 
+    def test_empty_content(self):
+        """
+        A '\n' token is generated for an empty file, so we have to bypass
+        pygments when rendering empty files.
+        """
+        result = self.pygments.render(self.context, 'text/x-python', '')
+        self.assertEqual(None, result)
 
 def suite():
     suite = unittest.TestSuite()

File trac/search/templates/search.html

View file
               <dt><a href="${result.href}" class="searchable">${result.title}</a></dt>
               <dd class="searchable">${result.excerpt}</dd>
               <dd>
-                <span class="author">By ${authorinfo(result.author)}</span> &mdash;
+                <py:if test="result.author"><span class="author">By ${format_author(result.author)}</span> &mdash;</py:if>
                 <span class="date">${result.date}</span>
               </dd>
             </py:for>

File trac/templates/macros.html

View file
   <py:def function="preview_file(preview)">
     ${preview.rendered}
     <py:choose>
+      <p py:when="preview.size == 0">
+        <strong>(The file is empty)</strong>
+      </p>
       <p py:when="not preview.rendered">
         <strong>HTML preview not available</strong>,
         <py:choose>

File trac/ticket/roadmap.py

View file
 from trac.mimeview import Context
 from trac.perm import IPermissionRequestor
 from trac.resource import *
+from trac.search import ISearchSource, search_to_sql, shorten_result
 from trac.util.compat import set, sorted
 from trac.util.datefmt import parse_date, utc, to_timestamp, to_datetime, \
                               get_date_format_hint, get_datetime_format_hint, \
 class MilestoneModule(Component):
 
     implements(INavigationContributor, IPermissionRequestor, IRequestHandler,
-               ITimelineEventProvider, IWikiSyntaxProvider, IResourceManager)
+               ITimelineEventProvider, IWikiSyntaxProvider, IResourceManager,
+               ISearchSource)
  
     stats_provider = ExtensionOption('milestone', 'stats_provider',
                                      ITicketGroupStatsProvider,
             return self._render_link(context, resource.id, desc)
         else:
             return desc
+
+    # ISearchSource methods
+
+    def get_search_filters(self, req):
+        if 'MILESTONE_VIEW' in req.perm:
+            yield ('milestone', _('Milestones'))
+
+    def get_search_results(self, req, terms, filters):
+        if not 'milestone' in filters:
+            return
+        db = self.env.get_db_cnx()
+        sql_query, args = search_to_sql(db, ['name', 'description'], terms)
+        cursor = db.cursor()
+        cursor.execute("SELECT name,due,completed,description "
+                       "FROM milestone "
+                       "WHERE " + sql_query, args)
+
+        milestone_realm = Resource('milestone')
+        for name, due, completed, description in cursor:
+            milestone = milestone_realm(id=name)
+            if 'MILESTONE_VIEW' in req.perm(milestone):
+                yield (get_resource_url(self.env, milestone, req.href),
+                       get_resource_name(self.env, milestone),
+                       datetime.fromtimestamp(
+                           completed or due or time(), utc),
+                       '', shorten_result(description, terms))
+        
+        # Attachments
+        for result in AttachmentModule(self.env).get_search_results(
+            req, milestone_realm, terms):
+            yield result

File trac/ticket/web_ui.py

View file
                                                        resolution, type)),
                        datetime.fromtimestamp(ts, utc), author,
                        shorten_result(desc, terms))
+        
+        # Attachments
+        for result in AttachmentModule(self.env).get_search_results(
+            req, ticket_realm, terms):
+            yield result        
 
     # ITimelineEventProvider methods
 

File trac/util/__init__.py

View file
             flags = os.O_CREAT + os.O_WRONLY + os.O_EXCL
             if hasattr(os, 'O_BINARY'):
                 flags += os.O_BINARY
-            return path, os.fdopen(os.open(path, flags), 'w')
+            return path, os.fdopen(os.open(path, flags, 0666), 'w')
         except OSError:
             idx += 1
             # A sanity check

File trac/util/daemon.py

View file
 import sys
 
 def daemonize(pidfile=None, progname=None, stdin='/dev/null',
-              stdout='/dev/null', stderr='/dev/null'):
+              stdout='/dev/null', stderr='/dev/null', umask=022):
     """Fork a daemon process."""
 
     if pidfile:
 
     # Decouple from parent environment
     os.chdir('/')
-    os.umask(0)
+    os.umask(umask)
     os.setsid()
 
     # Perform second fork

File trac/util/text.py

View file
     def __repr__(self):
         return '*******'
 
+def console_print(out, *args):
+    cons_charset = getattr(out, 'encoding', None) or 'utf-8'
+    out.write(' '.join([to_unicode(a).encode(cons_charset) for a in args])+
+              '\n')
 
 # -- Plain text formatting
 

File trac/web/standalone.py

View file
         parser.add_option('--pidfile', action='store',
                           dest='pidfile',
                           help='When daemonizing, file to which to write pid')
+        parser.add_option('--umask', action='store', type='int', dest='umask',
+                          metavar='MASK',
+                          help='When daemonizing, file mode creation mask '
+                          'to use (default 022)')
 
     parser.set_defaults(port=None, hostname='', base_path='', daemonize=False,
-                        protocol='http')
+                        protocol='http', umask=022)
     options, args = parser.parse_args()
 
     if not args and not options.env_parent_dir:
 
     try:
         if options.daemonize:
-            daemon.daemonize(pidfile=options.pidfile, progname='tracd')
+            daemon.daemonize(pidfile=options.pidfile, progname='tracd',
+                             umask=options.umask)
 
         if options.autoreload:
             def modification_callback(file):

File trac/wiki/web_ui.py

View file
                        '%s: %s' % (name, shorten_line(text)),
                        datetime.fromtimestamp(ts, utc), author,
                        shorten_result(text, terms))
+        
+        # Attachments
+        for result in AttachmentModule(self.env).get_search_results(
+            req, wiki_realm, terms):
+            yield result