Commits

Anonymous committed c26cdcf Draft

merging trac-0.13dev_11046 into trunk

Comments (0)

Files changed (49)

  * InterAct Trac-ja Team          trac-ja@i-act.co.jp
  * Paul Irish                     paul.irishEWWSPAM@gmail.com
  * Masaharu Iwai
+ * Ethan Jucovy                   ethan.jucovy@gmail.com
  * Noah Kantrowitz (coderanger)   coderanger@yahoo.com
  * Alexey Kinyov
  * Tomas Kopecek
  * Simon Martin
  * Narine Martirosyan             narine_martirosyan@instigatedesign.com
  * Mark Mc Mahon                  mark.m.mcmahon@gmail.com
+ * Brian Meeker                   meeker.brian@gmail.com
  * Aristotelis Mertis
  * Wojciech Michalski
  * Keir Mierle                    keir@cs.utoronto.ca

contrib/checkwiki.py

  "TracAccessibility",
  "TracAdmin",
  "TracBackup",
+ "TracBatchModify",
  "TracBrowser",
  "TracCgi",
  "TracChangeset",
         trac.prefs = trac.prefs.web_ui
         trac.search = trac.search.web_ui
         trac.ticket.admin = trac.ticket.admin
+        trac.ticket.batch = trac.ticket.batch
         trac.ticket.query = trac.ticket.query
         trac.ticket.report = trac.ticket.report
         trac.ticket.roadmap = trac.ticket.roadmap

trac/admin/tests/console-tests.txt

  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_add_one_action_ok =====
 
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_add_multiple_actions_ok =====
 
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_add_already_exists =====
 The user anonymous already has permission WIKI_VIEW.
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_remove_one_action_ok =====
 
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_remove_multiple_actions_ok =====
 
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_remove_all_actions_for_user =====
 
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_remove_action_for_all_users =====
 
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_remove_unknown_user =====
 Error: Cannot remove permission TICKET_VIEW for user joe.
  MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
  PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
  REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
- SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
- TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
- TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
- VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_RENAME, WIKI_VIEW
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_BATCH_MODIFY,
+ TICKET_CHGPROP, TICKET_CREATE, TICKET_EDIT_CC, TICKET_EDIT_COMMENT,
+ TICKET_EDIT_DESCRIPTION, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW,
+ TRAC_ADMIN, VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE,
+ WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW
 
 ===== test_component_list_ok =====
 

trac/attachment.py

 
 from cStringIO import StringIO
 from datetime import datetime
+import errno
 import os.path
 import re
 import shutil
 from trac.perm import PermissionError, IPermissionPolicy
 from trac.resource import *
 from trac.search import search_to_sql, shorten_result
-from trac.util import content_disposition, create_unique_file, get_reporter_id
+from trac.util import content_disposition, get_reporter_id
+from trac.util.compat import sha1
 from trac.util.datefmt import format_datetime, from_utimestamp, \
                               to_datetime, to_utimestamp, utc
 from trac.util.text import exception_to_unicode, path_to_unicode, \
                            pretty_size, print_table, unicode_quote, \
-                           unicode_unquote
+                           unicode_unquote, printerr
 from trac.util.translation import _, tag_
 from trac.web import HTTPBadRequest, IRequestHandler, RequestDone
 from trac.web.chrome import (INavigationContributor, add_ctxtnav, add_link,
                                    _('Invalid Attachment'))
 
     def _get_path(self, parent_realm, parent_id, filename):
+        path = os.path.join(self.env.path, 'files', 'attachments',
+                            parent_realm)
+        hash = sha1(parent_id.encode('utf-8')).hexdigest()
+        path = os.path.join(path, hash[0:3], hash)
+        if filename:
+            path = os.path.join(path, self._get_hashed_filename(filename))
+        return os.path.normpath(path)
+
+    _extension_re = re.compile(r'\.[A-Za-z0-9]+\Z')
+
+    def _get_hashed_filename(self, filename):
+        hash = sha1(filename.encode('utf-8')).hexdigest()
+        match = self._extension_re.search(filename)
+        return hash + match.group(0) if match else hash
+
+    def _get_path_old(self, parent_realm, parent_id, filename):
         path = os.path.join(self.env.path, 'attachments', parent_realm,
                             unicode_quote(parent_id))
         if filename:
             db("""
                 DELETE FROM attachment WHERE type=%s AND id=%s AND filename=%s
                 """, (self.parent_realm, self.parent_id, self.filename))
-            if os.path.isfile(self.path):
+            path = self.path
+            if os.path.isfile(path):
                 try:
-                    os.unlink(self.path)
+                    os.unlink(path)
                 except OSError, e:
                     self.env.log.error("Failed to delete attachment "
                                        "file %s: %s",
-                                       self.path,
+                                       path,
                                        exception_to_unicode(e, traceback=True))
                     raise TracError(_("Could not delete attachment"))
 
         # Make sure the path to the attachment is inside the environment
         # attachments directory
         attachments_dir = os.path.join(os.path.normpath(self.env.path),
-                                       'attachments')
+                                       'files', 'attachments')
         commonprefix = os.path.commonprefix([attachments_dir, new_path])
         if commonprefix != attachments_dir:
             raise TracError(_('Cannot reparent attachment "%(att)s" as '
             dirname = os.path.dirname(new_path)
             if not os.path.exists(dirname):
                 os.makedirs(dirname)
-            if os.path.isfile(self.path):
+            path = self.path
+            if os.path.isfile(path):
                 try:
-                    os.rename(self.path, new_path)
+                    os.rename(path, new_path)
                 except OSError, e:
                     self.env.log.error("Failed to move attachment file %s: %s",
-                                       self.path,
+                                       path,
                                        exception_to_unicode(e, traceback=True))
                     raise TracError(_("Could not reparent attachment %(name)s",
                                       name=self.filename))
         # Make sure the path to the attachment is inside the environment
         # attachments directory
         attachments_dir = os.path.join(os.path.normpath(self.env.path),
-                                       'attachments')
-        commonprefix = os.path.commonprefix([attachments_dir, self.path])
+                                       'files', 'attachments')
+        dir = self.path
+        commonprefix = os.path.commonprefix([attachments_dir, dir])
         if commonprefix != attachments_dir:
             raise TracError(_('Cannot create attachment "%(att)s" as '
                               '%(realm)s:%(id)s is invalid', 
                               att=filename, realm=self.parent_realm,
                               id=self.parent_id))
 
-        if not os.access(self.path, os.F_OK):
-            os.makedirs(self.path)
-        filename = unicode_quote(filename)
-        path, targetfile = create_unique_file(os.path.join(self.path,
-                                                           filename))
+        if not os.access(dir, os.F_OK):
+            os.makedirs(dir)
+        filename, targetfile = self._create_unique_file(dir, filename)
         with targetfile:
-            # Note: `path` is an unicode string because `self.path` was one.
-            # As it contains only quoted chars and numbers, we can use `ascii`
-            basename = os.path.basename(path).encode('ascii')
-            filename = unicode_unquote(basename)
-
             with self.env.db_transaction as db:
                 db("INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
                    (self.parent_realm, self.parent_id, filename, self.size,
                     attachment_dir, exception_to_unicode(e, traceback=True))
             
     def open(self):
-        self.env.log.debug('Trying to open attachment at %s', self.path)
+        path = self.path
+        self.env.log.debug('Trying to open attachment at %s', path)
         try:
-            fd = open(self.path, 'rb')
+            fd = open(path, 'rb')
         except IOError:
             raise ResourceNotFound(_("Attachment '%(filename)s' not found",
                                      filename=self.filename))
         return fd
 
+    def _create_unique_file(self, dir, filename):
+        parts = os.path.splitext(filename)
+        flags = os.O_CREAT + os.O_WRONLY + os.O_EXCL
+        if hasattr(os, 'O_BINARY'):
+            flags += os.O_BINARY
+        idx = 1
+        while 1:
+            path = os.path.join(dir, self._get_hashed_filename(filename))
+            try:
+                return filename, os.fdopen(os.open(path, flags, 0666), 'w')
+            except OSError, e:
+                if e.errno != errno.EEXIST:
+                    raise
+                idx += 1
+                # A sanity check
+                if idx > 100:
+                    raise Exception('Failed to create unique name: ' + path)
+                filename = '%s.%d%s' % (parts[0], idx, parts[1])
+
+
+class AttachmentSetup(Component):
+
+    implements(IEnvironmentSetupParticipant)
+
+    required = True
+
+    # IEnvironmentSetupParticipant methods
+
+    def environment_created(self):
+        """Create the attachments directory."""
+        path = self.env.path
+        if path:
+            os.makedirs(os.path.join(path, 'files', 'attachments'))
+
+    def environment_needs_upgrade(self, db):
+        path = self.env.path
+        if path:
+            return os.path.exists(os.path.join(path, 'attachments'))
+
+    def upgrade_environment(self, db):
+        """Migrate attachments from old-style directory to new-style
+        directory.
+        """
+        path = self.env.path
+        old_dir = os.path.join(path, 'attachments')
+        old_stat = os.stat(old_dir)
+        new_dir = os.path.join(path, 'files', 'attachments')
+        if not os.path.exists(new_dir):
+            os.makedirs(new_dir)
+
+        for row in db("""
+                SELECT type, id, filename, description, size, time, author,
+                ipnr FROM attachment ORDER BY type, id"""):
+            attachment = Attachment(self.env, row[0], row[1])
+            attachment._from_database(*row[2:])
+            self._move_attachment_file(attachment)
+
+        # Try to preserve permissions and ownerships of the attachments
+        # directory for $ENV/files
+        for dir, dirs, files in os.walk(os.path.join(path, 'files')):
+            try:
+                if hasattr(os, 'chmod'):
+                    os.chmod(dir, old_stat.st_mode)
+                if hasattr(os, 'chflags') and hasattr(old_stat, 'st_flags'):
+                    os.chflags(dir, old_stat.st_flags)
+                if hasattr(os, 'chown'):
+                    os.chown(dir, old_stat.st_uid, old_stat.st_gid)
+            except OSError:
+                pass
+
+        try:
+            for dir, dirs, files in os.walk(old_dir, topdown=False):
+                os.rmdir(dir)
+        except OSError, e:
+            self.log.error("Can't delete old attachments directory %s: %s",
+                           old_dir, exception_to_unicode(e, traceback=True))
+            printerr(_("Error while deleting old attachments directory. "
+                       "Please move or remove files in\nthe directory and try "
+                       "again."))
+            raise
+
+    def _move_attachment_file(self, attachment):
+        old_path = attachment._get_path_old(attachment.parent_realm,
+                                            attachment.parent_id,
+                                            attachment.filename)
+        if os.path.isfile(old_path):
+            os.renames(old_path, attachment.path)
+
 
 class AttachmentModule(Component):
 
-    implements(IEnvironmentSetupParticipant, IRequestHandler,
-               INavigationContributor, IWikiSyntaxProvider,
+    implements(IRequestHandler, INavigationContributor, IWikiSyntaxProvider,
                IResourceManager)
 
     change_listeners = ExtensionPoint(IAttachmentChangeListener)
         For public sites where anonymous users can create attachments it is
         recommended to leave this option disabled (which is the default).""")
 
-    # IEnvironmentSetupParticipant methods
-
-    def environment_created(self):
-        """Create the attachments directory."""
-        if self.env.path:
-            os.mkdir(os.path.join(self.env.path, 'attachments'))
-
-    def environment_needs_upgrade(self, db):
-        return False
-
-    def upgrade_environment(self, db):
-        pass
-
     # INavigationContributor methods
 
     def get_active_navigation_item(self, req):
         for attachment in attachments:
             zipinfo = ZipInfo()
             zipinfo.filename = attachment.filename.encode('utf-8')
+            zipinfo.flag_bits |= 0x800 # filename is encoded with utf-8
             zipinfo.date_time = attachment.date.utctimetuple()[:6]
             zipinfo.compress_type = ZIP_DEFLATED
             if attachment.description:

trac/htdocs/batchmodify.png

Added
New image

trac/htdocs/css/report.css

   text-align: center;
   font-size: 85%;
 }
+
+/* Batchmod Form */
+
+#batchmod_form fieldset input#batchmod_submit { font-size: 14px; }
+#batchmod_form fieldset input[type="button"]{ padding: 0.1em 0.5em; }
+#batchmod_form fieldset { margin-top: 1em }
+#batchmod_form fieldset.collapsed { 
+ border-width: 0;
+ margin-bottom: 0pt;
+ padding: 0pt .5em;
+}
+.batchmod_property { width: 100%; }
+.batchmod_required:before { content: " * "; }
+
+#batchmod_form fieldset input,
+#batchmod_form fieldset select,
+.batchmod_property,
+.batchmod_label {
+    font-size: 11px;
+}
+
+#batchmod_action { line-height: 2em }
+
+#batchmod_form th {
+    text-align: right;
+    white-space: nowrap;
+    font-size: 11px;
+}
+
+.batchmod_required {
+    color: red;
+    font-size: 11px;
+    font-weight: bold;
+    font-style: italic;
+    padding-left: .5em;
+}
+
+#batchmod_help {
+ clear: both;
+ color: #999;
+ margin: 1em;
+ text-align: right;
+}
+#batchmod_help :link, #batchmod_help :visited { cursor: help }

trac/htdocs/css/timeline.css

 dt.reopenedticket, dt.reopenedticket a { background-image: url(../newticket.png) !important }
 dt.editedticket, dt.editedticket a { background-image: url(../editedticket.png) !important }
 dt.closedticket, dt.closedticket a { background-image: url(../closedticket.png) !important }
+dt.batchmodify, dt.batchmodify a { background-image: url(../batchmodify.png) !important }
 dt.wiki, dt.wiki a { background-image: url(../wiki.png) !important }
 dt.milestone, dt.milestone a { background-image: url(../milestone.png) !important }
 dt.attachment, dt.attachment a { background-image: url(../attachment.png) !important }

trac/htdocs/js/query.js

 
 (function($){
+    
+  // Create a <label>
+  function createLabel(text, htmlFor) {
+    var label = $($.htmlFormat("<label>$1</label>", text));
+    if (htmlFor)
+      label.attr("for", htmlFor).addClass("control");
+    return label;
+  }
+
+  // Create an <input type="text">
+  function createText(name, size) {
+    return $($.htmlFormat('<input type="text" name="$1" size="$2">', 
+                          name, size));
+  }
+
+  // Create an <input type="checkbox">
+  function createCheckbox(name, value, id) {
+    return $($.htmlFormat('<input type="checkbox" id="$1" name="$2"' +
+                          ' value="$3">', id, name, value));
+  }
+
+  // Create an <input type="radio">
+  function createRadio(name, value, id) {
+    // Workaround for IE, otherwise the radio buttons are not selectable
+    return $($.htmlFormat('<input type="radio" id="$1" name="$2"' +
+                          ' value="$3">', id, name, value));
+  }
+
+  // Append a list of <option> to an element
+  function appendOptions(e, options) {
+    for (var i = 0; i < options.length; i++) {
+      var opt = options[i], v = opt, t = opt;
+      if (typeof opt == "object") 
+        v = opt.value, t = opt.name;
+      $($.htmlFormat('<option value="$1">$2</option>', v, t)).appendTo(e);
+    }
+  }
+
+  // Create a <select>
+  function createSelect(name, options, optional, optgroups) {
+    var e = $($.htmlFormat('<select name="$1">', name));
+    if (optional)
+      $("<option>").appendTo(e);
+    appendOptions(e, options);
+    if (optgroups) {
+      for (var i = 0; i < optgroups.length; i++) {
+        var grp = optgroups[i];
+        var optgrp = $($.htmlFormat('<optgroup label="$1">', grp.label));
+        appendOptions(optgrp, grp.options);
+        optgrp.appendTo(e);
+      }
+    }
+    return e;
+  }
   
   window.initializeFilters = function() {
     // Remove an existing row from the filters table
       $(this).replaceWith(
         $($.htmlFormat('<input type="button" value="$1">', this.value))
           .click(function() { 
-                   removeRow(this, propertyName);
-                   return false;
+            removeRow(this, propertyName);
+            return false;
       }));
     });
     
-    // Create a <label>
-    function createLabel(text, htmlFor) {
-      var label = $($.htmlFormat("<label>$1</label>", text));
-      if (htmlFor)
-        label.attr("for", htmlFor).addClass("control");
-      return label;
-    }
-    
-    // Create an <input type="text">
-    function createText(name, size) {
-      return $($.htmlFormat('<input type="text" name="$1" size="$2">', 
-                            name, size));
-    }
-    
-    // Create an <input type="checkbox">
-    function createCheckbox(name, value, id) {
-      return $($.htmlFormat('<input type="checkbox" id="$1" name="$2"' +
-                            ' value="$3">', id, name, value));
-    }
-    
-    // Create an <input type="radio">
-    function createRadio(name, value, id) {
-      // Workaround for IE, otherwise the radio buttons are not selectable
-      return $($.htmlFormat('<input type="radio" id="$1" name="$2"' +
-                            ' value="$3">', id, name, value));
-    }
-    
-    // Append a list of <option> to an element
-    function appendOptions(e, options) {
-      for (var i = 0; i < options.length; i++) {
-        var opt = options[i], v = opt, t = opt;
-        if (typeof opt == "object") 
-          v = opt.value, t = opt.name;
-        $($.htmlFormat('<option value="$1">$2</option>', v, t)).appendTo(e);
-      }
-    }
-    
-    // Create a <select>
-    function createSelect(name, options, optional, optgroups) {
-      var e = $($.htmlFormat('<select name="$1">', name));
-      if (optional)
-        $("<option>").appendTo(e);
-      appendOptions(e, options);
-      if (optgroups) {
-        for (var i = 0; i < optgroups.length; i++) {
-          var grp = optgroups[i];
-          var optgrp = $($.htmlFormat('<optgroup label="$1">', grp.label));
-          appendOptions(optgrp, grp.options);
-          optgrp.appendTo(e);
-        }
-      }
-      return e;
-    }
-    
     // Make the drop-down menu for adding a filter a client-side trigger
     $("#filters select[name^=add_filter_]").change(function() {
       if (this.selectedIndex < 1)
       add_clause.attr("disabled", true);
     }
   }
+  
+  window.initializeBatch = function(){
+    // Create the appropriate input for the property.
+    function createBatchInput(propertyName, property){
+      var td = $('<td class="batchmod_property">');
+      var inputName = getBatchInputName(propertyName);
+      switch(property.type){
+        case 'select':
+          td.append(createSelect(inputName, property.options, true,
+                                 property.optgroups));
+          break;
+        case 'radio':
+          for (var i = 0; i < property.options.length; i++) {
+            var option = property.options[i];
+            td.append(createRadio(inputName, option, inputName + "_" + option))
+              .append(" ")
+              .append(createLabel(option ? option : "none",
+                      inputName + "_" + option))
+              .append(" ");
+          }
+          break;
+        case 'checkbox':
+          td.append(createRadio(inputName, "1", inputName + "_on"))
+            .append(" ").append(createLabel(_("yes"), inputName + "_on"))
+            .append(" ")
+            .append(createRadio(inputName, "0", inputName + "_off"))
+            .append(" ").append(createLabel(_("no"), inputName + "_off"));
+          break;
+        case 'text':
+          if ($.inArray(propertyName, batch_list_properties) >= 0) {
+            appendBatchListControls(td, inputName);
+          } else {
+            td.append(createText(inputName, 42));
+          }
+          break;
+        case 'time':
+          td.append(createText(inputName, 42).addClass("time"));
+          break;
+      }
+      return td;
+    }
+    
+    function appendBatchListControls(td, inputName) {
+      var modeSelect = createSelect(inputName + "_mode", batch_list_modes);
+      var text1 = createText(inputName, 42);
+      var text2 = createText(inputName + "_secondary", 42);
+      var label1 = createLabel(" " + batch_list_modes[0]['name'] + ":",
+                               inputName);
+      var label2 = createLabel(_(" remove:"), inputName + "_secondary");
+      td.append(modeSelect);
+      td.append(label1);
+      td.append(text1);
+      
+      // Only mode 2 ("Add / remove") has a second text field:
+      $(modeSelect).change(function() {
+        if (this.selectedIndex == 2) {
+          td.append(label2);
+          td.append(text2);
+          label1.text(_(" add:"));
+        } else {
+          if (td.has(text2).length) {
+            text2.remove();
+            label2.remove();
+          }
+          label1.text(" " + batch_list_modes[this.selectedIndex]['name'] +
+                      ":");
+        }
+      });
+    }
+    
+    function getBatchInputName(propertyName){
+      return 'batchmod_value_' + propertyName;
+    }
+
+    function getDisabledBatchOptions(){
+      return $("#add_batchmod_field option:disabled");
+    }
+    
+    // Add a new column with checkboxes for each ticket.
+    // Selecting a ticket marks it for inclusion in the batch. 
+    $("table.listing tr td.id").each(function() {
+      tId=$(this).text().substring(1); 
+      $(this).before('<td><input type="checkbox" name="selected_ticket"' +
+                     ' class="batchmod_selector" value="'+tId+'"/></td>');
+    });
+
+    // Add a checkbox at the top of the column
+    // to select ever ticket in the group.
+    $("table.listing tr th.id").each(function() { 
+      $(this).before('<th class="batchmod_selector"><input type="checkbox"' +
+                     ' name="batchmod_toggleGroup" /></th>');
+    });
+
+    // Add the click behavior for the group toggle. 
+    $("input[name='batchmod_toggleGroup']").click(function() { 
+      $("tr td input.batchmod_selector",
+        $(this).parents("table.listing tbody, table.listing thead").next())
+        .attr("checked",this.checked);
+    });
+    
+    // Fix group table headers, increasing column span (for checkbox column).
+    $("table.listing tr.trac-group th").each(function() {
+      $(this).attr('colSpan', parseInt($(this).attr('colSpan')) + 1);
+    });
+  
+    // At least one ticket must be selected to submit the batch.
+    $("form#batchmod_form").submit(function() {
+      // First remove all existing validation messages.
+      $(".batchmod_required").remove();
+      
+      var valid = true;
+      var selectedTix=[];    
+      $("input[name=selected_ticket]:checked").each( function(){
+        selectedTix.push(this.value);
+      });
+      $("input[name=selected_tickets]").val(selectedTix);
+      
+      // At least one ticket must be selected.
+      if(selectedTix.length === 0){
+        $("#batchmod_submit")
+          .after('<span class="batchmod_required">' +
+                 'You must select at least one ticket.</span>');
+        valid = false;
+      }
+      
+      // Check that each radio property has something selected.
+      getDisabledBatchOptions().each(function(){
+        var propertyName = $(this).val();
+        if(properties[propertyName].type == "radio"){
+          var isChecked = false;
+          var inputName = getBatchInputName(propertyName);
+          $("[name=" + inputName + "]").each(function(){
+            isChecked = isChecked || $(this).is(':checked');
+          });
+          if(!isChecked){
+            // Select the last label in the row to add the error message
+            $("[name=" + inputName + "] ~ label:last")
+              .after('<span class="batchmod_required">Required</span>');
+            valid = false;
+          }
+        }
+      });
+      
+      // If the status is set to closed, a resolution must be selected.
+      if($("#batchmod_value_status_closed").checked()
+         && $("#batchmod_resolution").length < 1){
+        $("[name=batchmod_value_status] ~ label:last")
+          .after('<span class="batchmod_required">Resolution Required</span>');
+        valid = false;
+      }
+      
+      return valid;
+    });
+  
+    // Collapse the form by default
+    $("#batchmod_fieldset").toggleClass("collapsed");
+  
+    // Add the new batch modify field when the user selects one from the
+    // dropdown.
+    $("#add_batchmod_field").change(function() {
+      if (this.selectedIndex < 1){
+          return;
+      }
+      
+      // Trac has a properties object that has information about each property
+      // that filters could be built with. We use it hear to add batchmod
+      // fields.
+      var propertyName = this.options[this.selectedIndex].value;
+      var property = properties[propertyName];
+      
+      var tr = $("<tr>").attr('id', 'batchmod_' + propertyName);
+      
+      // Add the remove button
+      tr.append($('<td>')
+          .append($('<div class="inlinebuttons">')
+            .append($('<input type="button" value="&ndash;">')
+              .click(function() { 
+                $('#batchmod_' + propertyName).remove();
+                $($.htmlFormat("#add_batchmod_field option[value='$1']",
+                               propertyName)).enable(); 
+              })
+            )
+          )
+      );
+      
+      // Add the header row.
+      tr.append($('<th scope="row">')
+        .append(createLabel(property.label, getBatchInputName(propertyName)))
+      );
+      
+      // Add the input element.
+      tr.append(createBatchInput(propertyName, property));
+      
+      // New rows are added in the same order as listed in the dropdown.
+      // This is the same behavior as the filters.
+      var insertionPoint = null;
+      for (var i = this.selectedIndex + 1; i < this.options.length; i++) {
+        if (insertionPoint === null && this.options[i].disabled) {
+          insertionPoint = $("#batchmod_" + this.options[i].value);
+        }
+      }
+      if (insertionPoint === null) {
+        insertionPoint = $("#add_batchmod_field_row");
+      }
+      insertionPoint.before(tr);
+      
+      // Disable each element from the option list when it is selected.
+      this.options[this.selectedIndex].disabled = 'disabled';
+    });
+  }
 
 })(jQuery);

trac/htdocs/js/timeline_multirepos.js

 jQuery(document).ready(function($){
-  csetfilter = $("input[name=changeset]");
+  var csetfilter = $("input[name=changeset]");
   function toggleRepositories() {
     $("input[name^=repo-]").parent().toggle();
   }

trac/htdocs/js/workflow_graph.js

   
   $(document).ready(function() {
     $('.trac-workflow-graph').each(function (index) {
-      var data = window['graph_' + this.id.slice(-8)];
+      var data = window['graph_' + this.id.slice(-12)];
       var width = data.width, height = data.height;
       var nodes = [], actions = [], edges = [];
       for (var i = 0; i < data.nodes.length; ++i)

trac/locale/en_GB/LC_MESSAGES/tracini.po

 
 #: trac/web/chrome.py:368
 msgid ""
-"Location of the jQuery !JavaScript library (version 1.5.1).\n"
+"Location of the jQuery !JavaScript library (version 1.7.2).\n"
 "\n"
 "An empty value loads jQuery from the copy bundled with Trac.\n"
 "\n"
 "Alternatively, jQuery could be loaded from a CDN, for example:\n"
-"http://code.jquery.com/jquery-1.5.1.min.js,\n"
-"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.min.js or\n"
-"https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js.\n"
+"http://code.jquery.com/jquery-1.7.2.min.js,\n"
+"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js or\n"
+"https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js.\n"
 "\n"
 "(''since 0.13'')"
 msgstr ""
-"Location of the jQuery !JavaScript library (version 1.5.1).\n"
+"Location of the jQuery !JavaScript library (version 1.7.2).\n"
 "\n"
 "An empty value loads jQuery from the copy bundled with Trac.\n"
 "\n"
 "Alternatively, jQuery could be loaded from a CDN, for example:\n"
-"http://code.jquery.com/jquery-1.5.1.min.js,\n"
-"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.min.js or\n"
-"https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js.\n"
+"http://code.jquery.com/jquery-1.7.2.min.js,\n"
+"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js or\n"
+"https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js.\n"
 "\n"
 "(''since 0.13'')"
 

trac/locale/fr/LC_MESSAGES/tracini.po

 
 #: trac/web/chrome.py:368
 msgid ""
-"Location of the jQuery !JavaScript library (version 1.5.1).\n"
+"Location of the jQuery !JavaScript library (version 1.7.2).\n"
 "\n"
 "An empty value loads jQuery from the copy bundled with Trac.\n"
 "\n"
 "Alternatively, jQuery could be loaded from a CDN, for example:\n"
-"http://code.jquery.com/jquery-1.5.1.min.js,\n"
-"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.min.js or\n"
-"https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js.\n"
+"http://code.jquery.com/jquery-1.7.2.min.js,\n"
+"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js or\n"
+"https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js.\n"
 "\n"
 "(''since 0.13'')"
 msgstr ""

trac/locale/hu/LC_MESSAGES/messages-js.po

 msgstr ""
 "Project-Id-Version: Trac 0.12\n"
 "Report-Msgid-Bugs-To: trac-dev@googlegroups.com\n"
-"POT-Creation-Date: 2010-05-24 23:55+0200\n"
+"POT-Creation-Date: 2012-02-10 02:32+0100\n"
 "PO-Revision-Date: 2011-11-01 10:17+0100\n"
 "Last-Translator: Nagy Zoltán <nzoltan@freemail.hu>\n"
 "Language-Team: hu_HU <trac-dev@googlegroups.com>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 0.9.5\n"
+"Generated-By: Babel 0.9.6\n"
 
 #: trac/htdocs/js/blame.js:84
 msgid "(no changeset information)"
 msgid "Re-expand directory"
 msgstr "Könyvtár ismételt kibontása"
 
-#: trac/htdocs/js/expand_dir.js:120
+#: trac/htdocs/js/expand_dir.js:121
 #, python-format
 msgid "Loading %(entry)s..."
 msgstr "A(z) %(entry)s betöltése..."
 
-#: trac/htdocs/js/expand_dir.js:148
+#: trac/htdocs/js/expand_dir.js:149
 msgid "(empty)"
 msgstr "(üres)"
 
-#: trac/htdocs/js/expand_dir.js:156
+#: trac/htdocs/js/expand_dir.js:157
 msgid "(error)"
 msgstr "(hiba)"
 
-#: trac/htdocs/js/expand_dir.js:163
+#: trac/htdocs/js/expand_dir.js:164
 msgid "Fold directory"
 msgstr "Könyvtár összehajtása"
 
 msgid "%(title)s (click to hide column)"
 msgstr "%(title)s (kattintson az oszlop elrejtéséhez)"
 
-#: trac/htdocs/js/query.js:118
+#: trac/htdocs/js/query.js:131
 msgid "A filter already exists for that property"
 msgstr "A jellemzőhöz már létezik szűrő"
 
-#: trac/htdocs/js/query.js:145
+#: trac/htdocs/js/query.js:158
 msgid "or"
 msgstr "vagy"
 
-#: trac/htdocs/js/query.js:164
+#: trac/htdocs/js/query.js:177
 msgid "yes"
 msgstr "igen"
 
-#: trac/htdocs/js/query.js:167
+#: trac/htdocs/js/query.js:180
 msgid "no"
 msgstr "nem"
 
-#: trac/htdocs/js/query.js:170
+#: trac/htdocs/js/query.js:183
 msgid "between"
 msgstr "között"
 
-#: trac/htdocs/js/query.js:172
+#: trac/htdocs/js/query.js:185
 msgid "and"
 msgstr "és"
 

trac/locale/hu/LC_MESSAGES/messages.po

 msgstr ""
 "Project-Id-Version: Trac 0.12\n"
 "Report-Msgid-Bugs-To: trac-dev@googlegroups.com\n"
-"POT-Creation-Date: 2008-01-30 09:20+0100\n"
+"POT-Creation-Date: 2012-02-22 23:22+0100\n"
 "PO-Revision-Date: 2011-11-01 10:17+0100\n"
 "Last-Translator: Nagy Zoltán <nzoltan@freemail.hu>\n"
 "Language-Team: hu_HU <trac-dev@googlegroups.com>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 0.9.5\n"
-
-#: tracopt/mimeview/php.py:98
+"Generated-By: Babel 0.9.6\n"
+
+#: tracopt/mimeview/php.py:96
 msgid ""
 "You appear to be using the PHP CGI binary. Trac requires the CLI version "
 "for syntax highlighting."
 "Ön valószínűleg a PHP CGI binárist használja. A szintaktikai kiemeléshez "
 "a Tracnak a CLI verzióra van szükséges."
 
-#: tracopt/ticket/deleter.py:69 tracopt/ticket/deleter.py:82
-#: trac/ticket/templates/report_list.html:35
+#: tracopt/ticket/clone.py:48
+#, python-format
+msgid "%(summary)s (cloned)"
+msgstr "%(summary)s (klónozott)"
+
+#: tracopt/ticket/clone.py:52
+#, python-format
+msgid ""
+"Cloned from #%(id)s:\n"
+"----\n"
+"%(description)s"
+msgstr ""
+"Klónozva innen #%(id)s:\n"
+"----\n"
+"%(description)s"
+
+#: tracopt/ticket/clone.py:58
+msgid "Clone"
+msgstr "Klón"
+
+#: tracopt/ticket/clone.py:59
+msgid "Create a copy of this ticket"
+msgstr "Jegy másolatának létrehozása"
+
+#: tracopt/ticket/commit_updater.py:261
+msgid ""
+"Insert a changeset message into the output.\n"
+"\n"
+"This macro must be called using wiki processor syntax as follows:\n"
+"{{{\n"
+"{{{\n"
+"#!CommitTicketReference repository=\"reponame\" revision=\"rev\"\n"
+"}}}\n"
+"}}}\n"
+"where the arguments are the following:\n"
+" - `repository`: the repository containing the changeset\n"
+" - `revision`: the revision of the desired changeset"
+msgstr ""
+"A változtatás leírásának beszúrása a kimenetbe.\n"
+"\n"
+"Ezt a makrót a wiki szintaxis szerint kell meghívni az alábbi módon:\n"
+"{{{\n"
+"{{{\n"
+"#!CommitTicketReference repository=\"reponame\" revision=\"rev\"\n"
+"}}}\n"
+"}}}\n"
+"ahol a paraméterek a következők:\n"
+" - `repository`: a tároló amelyben a változtatás található\n"
+" - `revision`: a változtatás változatszáma"
+
+#: tracopt/ticket/deleter.py:70 tracopt/ticket/deleter.py:84
+#: trac/ticket/templates/report_list.html:81
 msgid "Delete"
 msgstr "Törlés"
 
-#: tracopt/ticket/deleter.py:70 tracopt/ticket/templates/ticket_delete.html:43
+#: tracopt/ticket/deleter.py:71 tracopt/ticket/templates/ticket_delete.html:42
 msgid "Delete ticket"
 msgstr "Jegy törlése"
 
-#: tracopt/ticket/deleter.py:83
+#: tracopt/ticket/deleter.py:85
 #, python-format
 msgid "Delete comment %(num)s"
 msgstr "A %(num)s megjegyzés törlése"
 
-#: tracopt/ticket/deleter.py:130
+#: tracopt/ticket/deleter.py:134
 #, python-format
 msgid "The ticket #%(id)s has been deleted."
 msgstr "A #%(id)s jegy törölve."
 
-#: tracopt/ticket/deleter.py:137
+#: tracopt/ticket/deleter.py:141
 #, python-format
 msgid "The ticket comment %(num)s on ticket #%(id)s has been deleted."
 msgstr "A #%(id)s jegy %(num)s megjegyzése törölve."
 
-#: tracopt/ticket/deleter.py:156
+#: tracopt/ticket/deleter.py:161
 #, python-format
 msgid "Comment %(num)s not found"
 msgstr "A %(num)s megjegyzés nem találtható"
 msgstr "A #%(id)s jegy törlése"
 
 #: tracopt/ticket/templates/ticket_delete.html:12
-#: tracopt/ticket/templates/ticket_delete.html:49
+#: tracopt/ticket/templates/ticket_delete.html:48
 #, python-format
 msgid "Delete comment %(num)s on Ticket #%(id)s"
 msgstr "A #%(id)s jegy %(num)s megjegyzésének törlése"
 msgid "Delete [1:Ticket #%(id)s]"
 msgstr "A [1:#%(id)s jegy] törlése"
 
-#: tracopt/ticket/templates/ticket_delete.html:33
+#: tracopt/ticket/templates/ticket_delete.html:32
 msgid "Are you sure you want to delete this ticket?"
 msgstr "Valóban törölni akarja ezt a jegyet?"
 
-#: tracopt/ticket/templates/ticket_delete.html:34
+#: tracopt/ticket/templates/ticket_delete.html:33
 #, python-format
 msgid ""
 "(comments: %(comments)s,\n"
 "(megjegyzések: %(comments)s,\n"
 "                 mellékletek: %(attachments)s)"
 
-#: tracopt/ticket/templates/ticket_delete.html:37
-#: tracopt/ticket/templates/ticket_delete.html:68
-#: trac/templates/attachment.html:70 trac/wiki/templates/wiki_delete.html:46
+#: tracopt/ticket/templates/ticket_delete.html:36
+#: tracopt/ticket/templates/ticket_delete.html:61
+#: trac/templates/attachment.html:70 trac/wiki/templates/wiki_delete.html:95
 msgid "This is an irreversible operation."
 msgstr "Ez a művelet nem visszafordítható."
 
-#: tracopt/ticket/templates/ticket_delete.html:42
-#: tracopt/ticket/templates/ticket_delete.html:72
+#: tracopt/ticket/templates/ticket_delete.html:41
+#: tracopt/ticket/templates/ticket_delete.html:65
 #: trac/admin/templates/admin_components.html:55
 #: trac/admin/templates/admin_enums.html:24
 #: trac/admin/templates/admin_milestones.html:68
 #: trac/ticket/templates/milestone_edit.html:106
 #: trac/ticket/templates/report_delete.html:21
 #: trac/ticket/templates/report_edit.html:44
-#: trac/ticket/templates/ticket_change.html:49
+#: trac/ticket/templates/ticket_change.html:112
 #: trac/versioncontrol/templates/admin_repositories.html:84
-#: trac/wiki/templates/wiki_delete.html:50
-#: trac/wiki/templates/wiki_edit_form.html:77
+#: trac/wiki/templates/wiki_delete.html:98
+#: trac/wiki/templates/wiki_edit_form.html:78
 #: trac/wiki/templates/wiki_rename.html:32
 msgid "Cancel"
 msgstr "Mégsem"
 
-#: tracopt/ticket/templates/ticket_delete.html:56
-msgid "comment:"
-msgstr "megjegyzés:"
-
-#: tracopt/ticket/templates/ticket_delete.html:58
-#: trac/ticket/templates/ticket.html:142
-#, python-format
-msgid "Changed %(date)s ago by %(author)s"
-msgstr "Módosította %(author)s, %(date)s "
-
-#: tracopt/ticket/templates/ticket_delete.html:68
+#: tracopt/ticket/templates/ticket_delete.html:61
 msgid "Are you sure you want to delete this ticket comment?"
 msgstr "Valóban törölni akarja ezt a megjegyzést?"
 
-#: tracopt/ticket/templates/ticket_delete.html:73
+#: tracopt/ticket/templates/ticket_delete.html:66
 msgid "Delete comment"
 msgstr "Megjegyzés törlése"
 
 msgid "About Trac"
 msgstr "A Trac névjegye"
 
-#: trac/attachment.py:152
+#: trac/attachment.py:164
 #, python-format
 msgid "Attachment '%(title)s' does not exist."
 msgstr "A(z) '%(title)s' melléklet nem létezik."
 
-#: trac/attachment.py:154
+#: trac/attachment.py:166
 msgid "Invalid Attachment"
 msgstr "Érvénytelen melléklet"
 
-#: trac/attachment.py:194
+#: trac/attachment.py:205
 msgid "Could not delete attachment"
 msgstr "A melléklet törlése sikertelen"
 
-#: trac/attachment.py:216
+#: trac/attachment.py:223
 #, python-format
 msgid "Cannot reparent attachment \"%(att)s\" as %(realm)s:%(id)s is invalid"
 msgstr "A %(realm)s:%(id)s érvénytelen, a \"%(att)s\" melléklet nem köthető hát"
 
-#: trac/attachment.py:222
+#: trac/attachment.py:228
 #, python-format
 msgid ""
 "Cannot reparent attachment \"%(att)s\" as it already exists in "
 "A \"%(att)s\" melléklet már létezik a %(realm)s tartományban ezért nem "
 "lehet áthelyezni:%(id)s"
 
-#: trac/attachment.py:241
+#: trac/attachment.py:246
 #, python-format
 msgid "Could not reparent attachment %(name)s"
 msgstr "A %(name)s melléklet áthelyezése nem sikerült."
 
-#: trac/attachment.py:270
+#: trac/attachment.py:281
 #, python-format
 msgid "Cannot create attachment \"%(att)s\" as %(realm)s:%(id)s is invalid"
 msgstr "A %(realm)s:%(id)s érvénytelen, a \"%(att)s\" melléklet nem hozható létre"
 
-#: trac/attachment.py:363
+#: trac/attachment.py:370
 #, python-format
 msgid "Attachment '%(filename)s' not found"
 msgstr "A(z) '%(filename)s' nevű melléklet nem található"
 
-#: trac/attachment.py:437
+#: trac/attachment.py:449
 msgid "Bad request"
 msgstr "Hibás kérés"
 
-#: trac/attachment.py:454
+#: trac/attachment.py:466
 #, python-format
 msgid "Back to %(parent)s"
 msgstr "Vissza ide: %(parent)s"
 
-#: trac/attachment.py:547
+#: trac/attachment.py:572
 #, python-format
 msgid "%(attachment)s attached to %(resource)s"
 msgstr "%(attachment)s csatolva ehhez: %(resource)s"
 
-#: trac/attachment.py:604
+#: trac/attachment.py:627
 #, python-format
 msgid "Unparented attachment %(id)s"
 msgstr "Árva melléklet %(id)s"
 
-#: trac/attachment.py:612
+#: trac/attachment.py:635
 #, python-format
 msgid "Attachment '%(id)s' in %(parent)s"
 msgstr "A %(parent)s melléklete '%(id)s'"
 
-#: trac/attachment.py:615
+#: trac/attachment.py:638
 #, python-format
 msgid "Attachments of %(parent)s"
 msgstr "A %(parent)s mellékletei"
 
-#: trac/attachment.py:632
+#: trac/attachment.py:655
 #, python-format
 msgid "%(parent)s doesn't exist, can't create attachment"
 msgstr "a(z) %(parent)s oldal nem létezik, a melléklet nem csatolható"
 
-#: trac/attachment.py:639 trac/attachment.py:662 trac/admin/web_ui.py:426
-#: trac/admin/web_ui.py:429 trac/admin/web_ui.py:433
+#: trac/attachment.py:662 trac/attachment.py:685 trac/admin/web_ui.py:465
+#: trac/admin/web_ui.py:468 trac/admin/web_ui.py:472
 msgid "No file uploaded"
 msgstr "Nincs feltöltve fájl"
 
-#: trac/attachment.py:647
+#: trac/attachment.py:670
 msgid "Can't upload empty file"
 msgstr "Üres fájlt nem lehet feltölteni"
 
-#: trac/attachment.py:652
+#: trac/attachment.py:675
 #, python-format
 msgid "Maximum attachment size: %(num)s bytes"
 msgstr "A melléklet maximális mérete %(num)s bájt lehet"
 
-#: trac/attachment.py:653
+#: trac/attachment.py:676
 msgid "Upload failed"
 msgstr "Sikertelen feltöltés"
 
-#: trac/attachment.py:675
+#: trac/attachment.py:698
 #, python-format
 msgid "Attachment field %(field)s is invalid: %(message)s"
 msgstr "A melléklet %(field)s mezője érvénytelen: %(message)s"
 
-#: trac/attachment.py:679
+#: trac/attachment.py:702
 #, python-format
 msgid "Invalid attachment: %(message)s"
 msgstr "Érvénytelen melléklet: %(message)s"
 
-#: trac/attachment.py:689
+#: trac/attachment.py:712
 #, python-format
 msgid ""
 "You don't have permission to replace the attachment %(name)s. You can "
 "Ön csak a saját mellékleteit cserélheti. Mások mellékleteinek \n"
 "cseréjéhez ATTACHMENT_DELETE jogosultság szükséges."
 
-#: trac/attachment.py:720
+#: trac/attachment.py:743
 #, python-format
 msgid "%(attachment)s (delete)"
 msgstr "%(attachment)s (törlés)"
 
-#: trac/attachment.py:781 trac/versioncontrol/web_ui/browser.py:663
-#: trac/wiki/web_ui.py:68
+#: trac/attachment.py:757
+#, python-format
+msgid "Maximum total attachment size: %(num)s bytes"
+msgstr "A melléklet maximális mérete %(num)s bájt lehet"
+
+#: trac/attachment.py:758
+msgid "Download failed"
+msgstr "A letöltés sikertelen"
+
+#: trac/attachment.py:845 trac/versioncontrol/web_ui/browser.py:665
+#: trac/wiki/web_ui.py:73
 msgid "Plain Text"
 msgstr "Egyszerű szöveg"
 
-#: trac/attachment.py:787 trac/versioncontrol/web_ui/browser.py:669
+#: trac/attachment.py:851 trac/versioncontrol/web_ui/browser.py:671
 msgid "Original Format"
 msgstr "Eredeti formátum"
 
-#: trac/attachment.py:829 trac/attachment.py:835
-#: trac/templates/list_of_attachments.html:20
-#: trac/templates/list_of_attachments.html:21
-#: trac/ticket/templates/ticket_change.html:21
-#: trac/ticket/templates/ticket_change.html:22
+#: trac/attachment.py:893 trac/templates/list_of_attachments.html:20
+#: trac/ticket/templates/ticket_change.html:83
+#: trac/versioncontrol/templates/dir_entries.html:18
+#: trac/versioncontrol/web_ui/browser.py:818
 msgid "Download"
 msgstr "Letöltés"
 
-#: trac/attachment.py:930
+#: trac/attachment.py:987
 #, python-format
 msgid "Invalid resource identifier '%(id)s'"
 msgstr "Érvénytelen erőforrás azonosító '%(id)s'"
 
-#: trac/attachment.py:966 trac/admin/templates/admin_components.html:80
+#: trac/attachment.py:1023 trac/admin/templates/admin_components.html:80
 #: trac/admin/templates/admin_enums.html:48
 #: trac/admin/templates/admin_milestones.html:101
-#: trac/admin/templates/admin_versions.html:78 trac/templates/about.html:84
-#: trac/templates/error.html:160 trac/ticket/admin.py:208
-#: trac/ticket/admin.py:399 trac/ticket/admin.py:560
-#: trac/versioncontrol/admin.py:112
+#: trac/admin/templates/admin_versions.html:78 trac/templates/about.html:69
+#: trac/templates/about.html:90 trac/templates/error.html:160
+#: trac/ticket/admin.py:210 trac/ticket/admin.py:397 trac/ticket/admin.py:555
+#: trac/versioncontrol/admin.py:113
 #: trac/versioncontrol/templates/admin_repositories.html:125
-#: trac/web/session.py:417
+#: trac/web/session.py:402
 msgid "Name"
 msgstr "Név"
 
-#: trac/attachment.py:966
+#: trac/attachment.py:1023
 msgid "Size"
 msgstr "Méret"
 
-#: trac/attachment.py:966 trac/templates/history_view.html:30
-#: trac/ticket/templates/ticket.html:361
-#: trac/versioncontrol/templates/revisionlog.html:98
+#: trac/attachment.py:1023 trac/templates/history_view.html:30
+#: trac/ticket/templates/ticket.html:328
+#: trac/versioncontrol/templates/revisionlog.html:112
 msgid "Author"
 msgstr "Szerző"
 
-#: trac/attachment.py:966 trac/templates/history_view.html:29
+#: trac/attachment.py:1023 trac/templates/history_view.html:29
 msgid "Date"
 msgstr "Dátum"
 
-#: trac/attachment.py:967 trac/templates/attachment.html:93
-#: trac/ticket/api.py:295 trac/ticket/templates/ticket_box.html:60
+#: trac/attachment.py:1024 trac/templates/attachment.html:93
+#: trac/ticket/api.py:299 trac/ticket/templates/ticket_box.html:60
 msgid "Description"
 msgstr "Leírás"
 
-#: trac/attachment.py:992 trac/wiki/admin.py:107
+#: trac/attachment.py:1047 trac/wiki/admin.py:108
 #, python-format
 msgid "File '%(name)s' exists"
 msgstr "A '%(name)s' fájl már létezik."
 
-#: trac/config.py:41
+#: trac/config.py:44
 msgid "Configuration Error"
 msgstr "Konfigurációs hiba"
 
-#: trac/config.py:418
+#: trac/config.py:265
+#, python-format
+msgid "Error reading '%(file)s', make sure it is readable."
+msgstr "Hiba történt '%(file)s' olvasásakor, ellenőrizze, hogy olvasható-e."
+
+#: trac/config.py:420
 #, python-format
 msgid "[%(section)s] %(entry)s: expected integer, got %(value)s"
 msgstr ""
 "[%(section)s] %(entry)s: elvárt típusa egész szám, értéke azonban "
 "%(value)s"
 
-#: trac/config.py:436
+#: trac/config.py:438
 #, python-format
 msgid "[%(section)s] %(entry)s: expected float, got %(value)s"
 msgstr ""
 "[%(section)s] %(entry)s: elvárt típusa természetes szám, értéke azonban "
 "%(value)s"
 
-#: trac/config.py:620
+#: trac/config.py:666
 #, python-format
 msgid "[%(section)s] %(entry)s: expected one of (%(choices)s), got %(value)s"
 msgstr ""
 "[%(section)s] %(entry)s: elvárt értéke a következők egyike (%(choices)s, "
 "értéke azonban %(value)s"
 
-#: trac/config.py:709 trac/config.py:722
+#: trac/config.py:757 trac/config.py:770
 #, python-format
 msgid "Option '%(option)s' doesn't exist in section '%(section)s'"
 msgstr "Az '%(option)s' opciók nem találhatóak '%(section)s' szekcióban"
 msgid "Trac Error"
 msgstr "Trac hiba"
 
-#: trac/env.py:152
+#: trac/env.py:200
 msgid ""
 "Visit the Trac open source project at<br /><a "
 "href=\"http://trac.edgewall.org/\">http://trac.edgewall.org/</a>"
 "Látogassa meg a nyílt forráskódú Trac projekt honlapját:<br /><a "
 "href=\"http://trac.edgewall.org/\">http://trac.edgewall.org/</a>"
 
-#: trac/env.py:576
+#: trac/env.py:722
 msgid "Database newer than Trac version"
 msgstr "Az adatbázis újabb, mint a Trac verziója"
 
-#: trac/env.py:593
+#: trac/env.py:739
 #, python-format
 msgid "No upgrade module for version %(num)i (%(version)s.py)"
 msgstr "Nem található upgrade modul a %(num)i verzióhoz (%(version)s.py)"
 
-#: trac/env.py:639
+#: trac/env.py:786
 msgid ""
 "Missing environment variable \"TRAC_ENV\". Trac requires this variable to"
 " point to a valid Trac environment."
 "A \"TRAC_ENV\" környezeti változó nincs definiálva. A Trac \n"
 "működéséhez a változónak egy érvényes Trac környezetre kell mutatnia."
 
-#: trac/env.py:671
+#: trac/env.py:815
 #, python-format
 msgid ""
 "The Trac Environment needs to be upgraded.\n"
 "\n"
 "Futtassa a \"trac-admin %(path)s upgrade\" parancsot"
 
-#: trac/env.py:705
+#: trac/env.py:854
 msgid "Copying resources from:"
 msgstr "Másolás innen:"
 
-#: trac/env.py:721
+#: trac/env.py:872
 msgid "Creating scripts."
 msgstr "Szkriptek létrehozása."
 
-#: trac/env.py:736
+#: trac/env.py:884
+#, python-format
+msgid "Invalid argument '%(arg)s'"
+msgstr "Érvénytelen paraméter '%(arg)s'"
+
+#: trac/env.py:889
 #, python-format
 msgid "hotcopy can't overwrite existing '%(dest)s'"
 msgstr "a hotcopy nem tudja felülírni a létező célt '%(dest)s'"
 
-#: trac/env.py:746
+#: trac/env.py:898
 #, python-format
 msgid "Hotcopying %(src)s to %(dst)s ..."
 msgstr "Másolás (hotcopyng) %(src)s to %(dst)s ..."
 
-#: trac/env.py:761
+#: trac/env.py:915
 msgid "The following errors happened while copying the environment:"
 msgstr "A következő hiba történt a környezet másolása közben:"
 
-#: trac/env.py:772
+#: trac/env.py:926
+msgid "Backing up database ..."
+msgstr "Adatbázis mentése..."
+
+#: trac/env.py:931
 msgid "Hotcopy done."
 msgstr "Hotcopy kész."
 
-#: trac/env.py:777 trac/admin/api.py:130
+#: trac/env.py:936 trac/admin/api.py:130
 msgid "Invalid arguments"
 msgstr "Érvénytelen paraméter"
 
-#: trac/env.py:780
+#: trac/env.py:939
 msgid "Database is up to date, no upgrade necessary."
 msgstr "Az adatbázis aktuális, nincs szükség frissítésre."
 
-#: trac/env.py:788
+#: trac/env.py:947
 #, python-format
 msgid ""
 "Backup failed: %(msg)s.\n"
 "Hiba történt mentés közben: '%(msg)s'.\n"
 "A frissítéshez hasznája a '--no-backup' kapcsolót a mentés kihagyásához."
 
-#: trac/env.py:798
+#: trac/env.py:957
 msgid ""
 "Warning: the wiki-macros directory in the environment is non-empty, but "
 "Trac\n"
 "Figyelem: A körneyzet wiki-macros könyvtára nem üres, azonban a Trac már "
 "nem használja a könyvtárat. Kérjük kézzel távolítsa el."
 
-#: trac/env.py:809
+#: trac/env.py:968
 #, python-format
 msgid ""
 "Error while removing wiki-macros: %(err)s\n"
 "Hiba történt a wiki-macros eltávolítása közben: %(err)s\n"
 "A Trac már nem használja a könyvtárat. Kérjük kézzel távolítsa el."
 
-#: trac/env.py:811
+#: trac/env.py:973
 #, python-format
 msgid ""
 "Upgrade done.\n"
 "\n"
 "  trac-admin %(path)s wiki upgrade"
 
-#: trac/notification.py:155
+#: trac/notification.py:158
 msgid "TLS enabled but server does not support TLS"
 msgstr "A TLS engedélyezve van, de a szerver nem támogatja a TLS-t"
 
-#: trac/notification.py:303
+#: trac/notification.py:311
 #, python-format
 msgid "Invalid email encoding setting: %(pref)s"
 msgstr "Érvénytelen e-mail kódolási beállítás: %(pref)s"
 
-#: trac/notification.py:317
+#: trac/notification.py:336
 msgid "Unable to send email due to identity crisis."
 msgstr "Nem lehetséges e-mail küldése identitásválság miatt."
 
-#: trac/notification.py:321
+#: trac/notification.py:340
 #, python-format
 msgid "Neither %(from_)s nor %(reply_to)s are specified in the configuration."
 msgstr "Sem %(from_)s sem a %(reply_to)s nincs konfigurálva."
 
-#: trac/notification.py:322
+#: trac/notification.py:341
 msgid "SMTP Notification Error"
 msgstr "SMTP értesítési hiba"
 
-#: trac/notification.py:331
+#: trac/notification.py:350
 msgid "Header length is too short"
 msgstr "A fejléc túl rövid"
 
-#: trac/perm.py:48
-#, python-format
-msgid "%(perm)s privileges are required to perform this operation on %(resource)s"
+#: trac/perm.py:55
+#, python-format
+msgid ""
+"%(perm)s privileges are required to perform this operation on "
+"%(resource)s. You don't have the required permissions."
 msgstr ""
 "%(perm)s jogosultság szükséges a kért művelet végrehajtásához ezen "
-"%(resource)s"
-
-#: trac/perm.py:50
-#, python-format
-msgid "%(perm)s privileges are required to perform this operation"
-msgstr "%(perm)s jogosultság szükséges a művelet végrehajtásához"
-
-#: trac/perm.py:55
+"%(resource)s. Önnek nincs meg a szükséges jogosultsága."
+
+#: trac/perm.py:57
+#, python-format
+msgid ""
+"%(perm)s privileges are required to perform this operation. You don't "
+"have the required permissions."
+msgstr ""
+"%(perm)s jogosultság szükséges a kért művelet végrehajtásához. Önnek "
+"nincs meg a szükséges jogosultsága."
+
+#: trac/perm.py:63
 msgid "Insufficient privileges to perform this operation."
 msgstr "Nincs megfelelő jogosultsága ezen művelet végrehajtására."
 
-#: trac/perm.py:333
+#: trac/perm.py:331
 #, python-format
 msgid "%(name)s is not a valid action."
 msgstr "A(z) %(name)s egy érvénytelen művelet."
 
-#: trac/perm.py:643
+#: trac/perm.py:644
 msgid "User"
 msgstr "Felhasználó"
 
-#: trac/perm.py:643 trac/admin/templates/admin_perms.html:70
-#: trac/ticket/templates/ticket.html:339
+#: trac/perm.py:644 trac/admin/templates/admin_perms.html:70
+#: trac/ticket/templates/ticket.html:299
 msgid "Action"
 msgstr "Művelet"
 
-#: trac/perm.py:645
+#: trac/perm.py:646
 msgid "Available actions:"
 msgstr "Elérhető műveletek:"
 
-#: trac/perm.py:656 trac/admin/web_ui.py:334
+#: trac/perm.py:657 trac/admin/web_ui.py:370
 msgid "All upper-cased tokens are reserved for permission names"
 msgstr "Minden nagybetűs kifejezés foglalt a jogosultság neveknek"
 
+#: trac/perm.py:663
+#, python-format
+msgid "The user %(user)s already has permission %(action)s."
+msgstr "A(z) %(user)s felhasználónak már van jogosultsága %(action)s művelethez."
+
+#: trac/perm.py:677
+#, python-format
+msgid "Cannot remove permission %(action)s for user %(user)s."
+msgstr ""
+"A(z) %(action)s jogosultságot nem lehet megvonni a %(user)s "
+"felhasználótól."
+
+#: trac/perm.py:694
+#, python-format
+msgid "Cannot export to %(filename)s: %(error)s"
+msgstr "Nem lehet exportálni %(filename)s: %(error)s"
+
+#: trac/perm.py:707
+#, python-format
+msgid "Invalid row %(line)d. Expected <user>, <action>, [action], [...]"
+msgstr "Hibás a %(line)d sor. Helyesen <user>, <action>, [action], [...]"
+
+#: trac/perm.py:715
+#, python-format
+msgid ""
+"Invalid user %(user)s on line %(line)d: All upper-cased tokens are "
+"reserved for permission names."
+msgstr ""
+"Érvénytelen felhasználó %(user)s a %(line)d sorban: Minden nagybetűs "
+"kifejezés fenntartott a jogosultságnevek számára."
+
+#: trac/perm.py:724
+#, python-format
+msgid "Cannot import from %(filename)s line %(line)d: %(error)s "
+msgstr "Nem lehet importálni %(filename)s a %(line)d sorban: %(error)s "
+
+#: trac/perm.py:729
+#, python-format
+msgid "Cannot import from %(filename)s: %(error)s"
+msgstr "Nem lehet importálni %(filename)s: %(error)s"
+
 #: trac/resource.py:336
 #, python-format
 msgid "%(name)s at version %(version)s"
 msgstr "A(z) %(name)s  %(version)s. verziója"
 
-#: trac/admin/api.py:134 trac/admin/console.py:267
+#: trac/admin/api.py:134
 msgid "Command not found"
 msgstr "A parancs nem található"
 
 msgid ""
 "Welcome to trac-admin %(version)s\n"
 "Interactive Trac administration console.\n"
-"Copyright (C) 2003-2011 Edgewall Software\n"
+"Copyright (C) 2003-2012 Edgewall Software\n"
 "\n"
 "Type:  '?' or 'help' for help on commands.\n"
 "        "
 msgstr ""
 "Üdvözöli Önt a trac-admin %(version)s\n"
 "Interaktív Trac Adminisztrációs Konzol.\n"
-"Copyright (C) 2003-2011 Edgewall Software\n"
+"Copyright (C) 2003-2012 Edgewall Software\n"
 "\n"
 "Segítséget a '?' vagy 'help' beírásával kaphat.\n"
 "        "
 msgid "Completion error: %(err)s"
 msgstr "Hiba: %(err)s"
 
-#: trac/admin/console.py:300
+#: trac/admin/console.py:305
 #, python-format
 msgid "No documentation found for '%(cmd)s'"
 msgstr "Nem található dokumentáció a '%(cmd)s' parancshoz"
 
-#: trac/admin/console.py:302
+#: trac/admin/console.py:307
 #, python-format
 msgid "trac-admin - The Trac Administration Console %(version)s"
 msgstr "trac-admin - Trac Adminisztrációs Konzol %(version)s"
 
-#: trac/admin/console.py:306
+#: trac/admin/console.py:311
 msgid "Usage: trac-admin </path/to/projenv> [command [subcommand] [option ...]]\n"
 msgstr ""
 "Használata: trac-admin </path/to/projenv> [command [subcommand] [option "
 "...]]\n"
 
-#: trac/admin/console.py:309
+#: trac/admin/console.py:314
 msgid "Invoking trac-admin without command starts interactive mode.\n"
 msgstr "Az interaktív módhoz futtassa a trac-admin parancsot paraméterek nélkül\n"
 
-#: trac/admin/console.py:349
+#: trac/admin/console.py:354
 #, python-format
 msgid "Creating a new Trac environment at %(envname)s"
 msgstr "Új Trac környezet létrehozása: %(envname)s"
 
-#: trac/admin/console.py:351
+#: trac/admin/console.py:356
 msgid ""
 "\n"
 "Trac will first ask a few questions about your environment \n"
 " Kérem adja meg a projekt nevét.\n"
 " Ezt a név fog megjelenni az oldal címekben és leírásokban.\n"
 
-#: trac/admin/console.py:359
+#: trac/admin/console.py:364
 #, python-format
 msgid "Project Name [%(default)s]> "
 msgstr "Projekt neve [%(default)s]> "
 
-#: trac/admin/console.py:361
+#: trac/admin/console.py:366
 msgid ""
 " \n"
 " Please specify the connection string for the database to use.\n"
 " PostgreSQL adatbázis használata is. (a kapcsolati sztring pontos\n"
 " szintaxisátt a Trac dokumentáció ismereti).\n"
 
-#: trac/admin/console.py:369
+#: trac/admin/console.py:374
 #, python-format
 msgid "Database connection string [%(default)s]> "
 msgstr "Adatbázis kapcsolati sztring [%(default)s]> "
 
-#: trac/admin/console.py:376
+#: trac/admin/console.py:381
 #, python-format
 msgid "Initenv for '%(env)s' failed."
 msgstr "A '%(env)s' környezet létrehozása (initenv) nem siekrült."
 
-#: trac/admin/console.py:379
+#: trac/admin/console.py:384
 msgid "Does an environment already exist?"
 msgstr "A környzet már létezik?"
 
-#: trac/admin/console.py:383
+#: trac/admin/console.py:388
 msgid "Directory exists and is not empty."
 msgstr "A könyvtár már létezik és nem üres."
 
-#: trac/admin/console.py:411
+#: trac/admin/console.py:394
+#, python-format
+msgid ""
+"Base directory '%(env)s' does not exist. Please create it manually and "
+"retry."
+msgstr "A(z) '%(env)s' könyvtár nem létezik. Hozza létre majd próbálja újra."
+
+#: trac/admin/console.py:422
 msgid "Creating and Initializing Project"
 msgstr "A projekt létrehozása és inicializálása"
 
-#: trac/admin/console.py:428
+#: trac/admin/console.py:439
 msgid "Failed to create environment."
 msgstr "A környzet létrehozása nem sikerült."
 
-#: trac/admin/console.py:434
+#: trac/admin/console.py:445
 msgid " Installing default wiki pages"
 msgstr " Az alapértelmezett wiki oldalak telepítése"
 
-#: trac/admin/console.py:443
+#: trac/admin/console.py:454
 msgid " Indexing default repository"
 msgstr " Az alapértelmezett tároló indexelése"
 
-#: trac/admin/console.py:446
+#: trac/admin/console.py:457
 msgid ""
 "\n"
 "---------------------------------------------------------------------\n"
 "később ellenőrizze újra a trac.ini fájl [trac] szekciójában a\n"
 "repository_type és repository_path beállításokat.\n"
 
-#: trac/admin/console.py:489
+#: trac/admin/console.py:500
 #, python-format
 msgid ""
 "\n"
 "\n"
 "Gratulálunk!\n"
 
-#: trac/admin/console.py:548
+#: trac/admin/console.py:509
+msgid ""
+"Display help for trac-admin commands.\n"
+"\n"
+"Examples:\n"
+"{{{\n"
+"[[TracAdminHelp]]               # all commands\n"
+"[[TracAdminHelp(wiki)]]         # all wiki commands\n"
+"[[TracAdminHelp(wiki export)]]  # the \"wiki export\" command\n"
+"[[TracAdminHelp(upgrade)]]      # the upgrade command\n"
+"}}}"
+msgstr ""
+"Súgó megjelenítése a trac-admin parancsokhoz.\n"
+"\n"
+"Példák:\n"
+"{{{\n"
+"[[TracAdminHelp]]               # összes parancs\n"
+"[[TracAdminHelp(wiki)]]         # összes wiki parancs\n"
+"[[TracAdminHelp(wiki export)]]  # a \"wiki export\" parancs\n"
+"[[TracAdminHelp(upgrade)]]      # az upgrade parancs\n"
+"}}}"
+
+#: trac/admin/console.py:561
 #, python-format
 msgid "Non-ascii environment path '%(path)s' not supported."
 msgstr "A környzet útvonala kizárólag ASCII karaktereket tartalmazhat: '%(path)s"
 
-#: trac/admin/web_ui.py:65
+#: trac/admin/web_ui.py:74
 msgid "Admin"
 msgstr "Rendszergazda"
 
-#: trac/admin/web_ui.py:66 trac/admin/templates/admin.html:16
+#: trac/admin/web_ui.py:75 trac/admin/templates/admin.html:16
 msgid "Administration"
 msgstr "Adminisztráció"
 
-#: trac/admin/web_ui.py:82
+#: trac/admin/web_ui.py:91
 msgid "No administration panels available"
 msgstr "Nem áll rendelkezésre adminisztrációs panel"
 
-#: trac/admin/web_ui.py:108 trac/admin/web_ui.py:112
+#: trac/admin/web_ui.py:117 trac/admin/web_ui.py:121
 msgid "Unknown administration panel"
 msgstr "Ismeretlen adminisztrációs panel"
 
-#: trac/admin/web_ui.py:186 trac/ticket/admin.py:64 trac/ticket/admin.py:89
-#: trac/ticket/admin.py:275 trac/ticket/admin.py:458 trac/ticket/admin.py:611
-#: trac/ticket/admin.py:697 trac/ticket/report.py:189
-#: trac/ticket/roadmap.py:695 trac/versioncontrol/admin.py:216
+#: trac/admin/web_ui.py:133
+msgid "Untitled"