Index: trac/ticket/api.py
===================================================================
--- trac/ticket/api.py	(revision 6191)
+++ trac/ticket/api.py	(working copy)
@@ -275,8 +275,9 @@
 
     def get_permission_actions(self):
         return ['TICKET_APPEND', 'TICKET_CREATE', 'TICKET_CHGPROP',
-                'TICKET_VIEW',
-                ('TICKET_MODIFY', ['TICKET_APPEND', 'TICKET_CHGPROP']),  
+                'TICKET_VIEW', 'TICKET_EDIT_CC',
+                ('TICKET_MODIFY', ['TICKET_APPEND', 'TICKET_CHGPROP',
+                                   'TICKET_EDIT_CC']),  
                 ('TICKET_ADMIN', ['TICKET_CREATE', 'TICKET_MODIFY',  
                                   'TICKET_VIEW'])]
 
Index: trac/ticket/web_ui.py
===================================================================
--- trac/ticket/web_ui.py	(revision 6191)
+++ trac/ticket/web_ui.py	(working copy)
@@ -538,10 +538,49 @@
                                                 absurls=absurls),
                 'preserve_newlines': preserve_newlines}
 
+    def _toggle_cc(self, req, cc):
+        """Return an (action, recipient) tuple corresponding to a change
+        of CC status for this user relative to the current `cc_list`."""
+        entries = []
+        email = req.session.get('email', '').strip()
+        if email:
+            entries.append(email)
+        if req.authname != 'anonymous':
+            entries.append(req.authname)
+        else:
+            author = get_reporter_id(req, 'author').strip()
+            if author != 'anonymous':
+                entries.append(author)
+        add = []
+        remove = []
+        cc_list = Chrome(self.env).cc_list(cc)
+        for entry in entries:
+            if entry in cc_list:
+                remove.append(entry)
+            else:
+                add.append(entry)
+        print repr((entries, cc_list, add, remove))
+        action = entry = ''
+        if remove:
+            action, entry = ('remove', remove[0])
+        elif add:
+            action, entry = ('add', add[0])
+        return (action, entry, cc_list)
+        
     def _populate(self, req, ticket):
         ticket.populate(dict([(k[6:],v) for k,v in req.args.iteritems()
                               if k.startswith('field_')]))
 
+        # special case for updating the Cc: field
+        if 'cc_update' in req.args:
+            cc_action, cc_entry, cc_list = self._toggle_cc(req, ticket['cc'])
+            if cc_action == 'remove':
+                cc_list.remove(cc_entry)
+            elif cc_action == 'add':
+                cc_list.append(cc_entry)
+            print repr((cc_entry, cc_list, ', '.join(cc_list)))
+            ticket['cc'] = ', '.join(cc_list)
+
     def _get_history(self, req, ticket):
         history = []
         for change in self.rendered_changelog_entries(req, ticket):
@@ -982,8 +1021,18 @@
                 field['rendered'] = render_resource_link(self.env, context,
                                                          milestone, 'compact')
             elif name == 'cc':
-                emails = Chrome(self.env).format_emails(context, ticket[name])
+                emails = Chrome(self.env).format_emails(context, ticket[name]) 
                 field['rendered'] = emails
+                if ticket.exists and \
+                        'TICKET_EDIT_CC' not in req.perm(ticket.resource):
+                    cc = ticket._old.get('cc', ticket['cc'])
+                    cc_action, cc_entry, cc_list = self._toggle_cc(req, cc)
+                    field['edit_label'] = {
+                            'add': _("Add to Cc"),
+                            'remove': _("Remove from Cc"),
+                            '': _("Add/Remove from Cc")}[cc_action]
+                    field['cc_entry'] = cc_entry or _("the <Author>")
+                    field['cc_update'] = 'cc_update' in req.args or None
 
             # per type settings
             if type_ in ('radio', 'select'):
Index: trac/ticket/templates/ticket.html
===================================================================
--- trac/ticket/templates/ticket.html	(revision 6191)
+++ trac/ticket/templates/ticket.html	(working copy)
@@ -309,7 +309,7 @@
               <py:for each="idx, field in enumerate(row)">
                 <th class="col${idx + 1}" py:if="idx == 0 or not fullrow">
                   <label for="field-${field.name}" py:if="field"
-                         py:strip="field.type == 'radio'">${field.label or field.name}:</label>
+                         py:strip="field.type == 'radio'">${field.edit_label or field.label or field.name}:</label>
                 </th>
                 <td class="col${idx + 1}" py:if="idx == 0 or not fullrow"
                     colspan="${fullrow and 3 or None}">
@@ -340,8 +340,17 @@
                              checked="${ticket[field.name] == option or None}" />
                       ${option}
                     </label>
-                    <input py:otherwise="" type="text" id="field-${field.name}"
-                           name="field_${field.name}" value="${ticket[field.name]}" />
+                    <py:otherwise><!--! Text input fields -->
+                      <py:choose>
+                        <span py:when="field.cc_entry"><!--! Special case for Cc: field -->
+                          <em>${field.cc_entry}</em>
+                          <input type="checkbox" id="field-cc" name="cc_update" checked="${field.cc_update}" />
+                        </span>
+                        <!--! All the other text input fields, including Cc: when TICKET_EDIT_CC is allowed -->
+                        <input py:otherwise="" type="text" id="field-${field.name}"
+                          name="field_${field.name}" value="${ticket[field.name]}" />
+                      </py:choose>
+                    </py:otherwise>
                   </py:choose>
                 </td>
               </py:for>

