]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1489718 - Insert form widgets for approval flag requests instead of free-form...
authorKohei Yoshino <kohei.yoshino@gmail.com>
Wed, 26 Sep 2018 15:29:19 +0000 (11:29 -0400)
committerDylan William Hardison <dylan@hardison.net>
Wed, 26 Sep 2018 15:29:19 +0000 (11:29 -0400)
extensions/BMO/template/en/default/hook/flag/type_comment-form.html.tmpl [new file with mode: 0644]
extensions/FlagTypeComment/template/en/default/flag/type_comment.html.tmpl
extensions/FlagTypeComment/web/js/ftc.js [new file with mode: 0644]
extensions/FlagTypeComment/web/styles/ftc.css [new file with mode: 0644]
skins/standard/attachment.css
skins/standard/global.css
template/en/default/flag/list.html.tmpl

diff --git a/extensions/BMO/template/en/default/hook/flag/type_comment-form.html.tmpl b/extensions/BMO/template/en/default/hook/flag/type_comment-form.html.tmpl
new file mode 100644 (file)
index 0000000..7963af8
--- /dev/null
@@ -0,0 +1,202 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+  # License, v. 2.0. If a copy of the MPL was not distributed with this
+  # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+  #
+  # This Source Code Form is "Incompatible With Secondary Licenses", as
+  # defined by the Mozilla Public License, v. 2.0.
+  #%]
+
+<template class="approval-request" data-flags="approval‑mozilla‑beta approval‑mozilla‑release">
+  <fieldset>
+    <legend>Beta/Release Uplift Approval Request</legend>
+    <table>
+      <tr>
+        <th id="_ar_beta_i1_label">Feature/[% terms.Bug %] causing the regression</th>
+        <td><input type="text" placeholder="[% terms.Bug %] ID" aria-labelledby="_ar_beta_i1_label" data-type="b[% %]ug"></td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i2_label">User impact if declined</th>
+        <td><textarea aria-labelledby="_ar_beta_i2_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i3_label">Is this code covered by automated tests?</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_beta_i3_label">
+            <div class="item"><input id="_ar_beta_i3_r1" type="radio" name="_ar_beta_i3_radio" value="Yes"><label for="_ar_beta_i3_r1">Yes</label></div>
+            <div class="item"><input id="_ar_beta_i3_r2" type="radio" name="_ar_beta_i3_radio" value="No"><label for="_ar_beta_i3_r2">No</label></div>
+            <div class="item"><input id="_ar_beta_i3_r3" type="radio" name="_ar_beta_i3_radio" value="Dunno"><label for="_ar_beta_i3_r3">Dunno</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i4_label">Has the fix been verified in Nightly?</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_beta_i4_label">
+            <div class="item"><input id="_ar_beta_i4_r1" type="radio" name="_ar_beta_i4_radio" value="Yes"><label for="_ar_beta_i4_r1">Yes</label></div>
+            <div class="item"><input id="_ar_beta_i4_r2" type="radio" name="_ar_beta_i4_radio" value="No"><label for="_ar_beta_i4_r2">No</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i5_label">Needs manual test from QE?</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_beta_i5_label">
+            <div class="item"><input id="_ar_beta_i5_r1" type="radio" name="_ar_beta_i5_radio" value="Yes"><label for="_ar_beta_i5_r1">Yes</label></div>
+            <div class="item"><input id="_ar_beta_i5_r2" type="radio" name="_ar_beta_i5_radio" value="No"><label for="_ar_beta_i5_r2">No</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i6_label">If yes, steps to reproduce</th>
+        <td><textarea aria-labelledby="_ar_beta_i6_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i7_label">List of other uplifts needed</th>
+        <td><input type="text" placeholder="[% terms.Bug %] IDs" aria-labelledby="_ar_beta_i7_label" data-type="b[% %]ugs"></td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i8_label">Risk to taking this patch</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_beta_i8_label">
+            <div class="item"><input id="_ar_beta_i8_r1" type="radio" name="_ar_beta_i8_radio" value="Low"><label for="_ar_beta_i8_r1">Low</label></div>
+            <div class="item"><input id="_ar_beta_i8_r2" type="radio" name="_ar_beta_i8_radio" value="Medium"><label for="_ar_beta_i8_r2">Medium</label></div>
+            <div class="item"><input id="_ar_beta_i8_r3" type="radio" name="_ar_beta_i8_radio" value="High"><label for="_ar_beta_i8_r3">High</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i9_label">Why is the change risky/not risky?<br>(and alternatives if risky)</th>
+        <td><textarea aria-labelledby="_ar_beta_i9_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_beta_i10_label">String changes made/needed</th>
+        <td><input type="text" class="long" aria-labelledby="_ar_beta_i10_label"></td>
+      </tr>
+    </table>
+  </fieldset>
+</template>
+
+<template class="approval-request" data-flags="approval‑mozilla‑esr*">
+  <fieldset>
+    <legend>ESR Uplift Approval Request</legend>
+    <table>
+      <tr>
+        <th id="_ar_esr_i1_label">If this is not a sec:{high,crit} [% terms.bug %], please state case for ESR consideration</th>
+        <td><textarea aria-labelledby="_ar_esr_i1_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_esr_i2_label">User impact if declined</th>
+        <td><textarea aria-labelledby="_ar_esr_i2_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_esr_i3_label">Fix Landed on Version</th>
+        <td><input type="text" aria-labelledby="_ar_esr_i3_label"></td>
+      </tr>
+      <tr>
+        <th id="_ar_esr_i4_label">Risk to taking this patch</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_esr_i4_label">
+            <div class="item"><input id="_ar_esr_i4_r1" type="radio" name="_ar_esr_i4_radio" value="Low"><label for="_ar_esr_i4_r1">Low</label></div>
+            <div class="item"><input id="_ar_esr_i4_r2" type="radio" name="_ar_esr_i4_radio" value="Medium"><label for="_ar_esr_i4_r2">Medium</label></div>
+            <div class="item"><input id="_ar_esr_i4_r3" type="radio" name="_ar_esr_i4_radio" value="High"><label for="_ar_esr_i4_r3">High</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_esr_i5_label">Why is the change risky/not risky?<br>(and alternatives if risky)</th>
+        <td><textarea aria-labelledby="_ar_esr_i5_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_esr_i6_label">String or UUID changes made by this patch</th>
+        <td><input type="text" class="long" aria-labelledby="_ar_esr_i6_label"></td>
+      </tr>
+    </table>
+    <p>See <a href="https://wiki.mozilla.org/Release_Management/ESR_Landing_Process" target="_blank">ESR Landing Process</a> for more info.</p>
+  </fieldset>
+</template>
+
+<template class="approval-request" data-flags="approval‑mozilla‑geckoview*">
+  <fieldset>
+    <legend>GeckoView Uplift Approval Request</legend>
+    <table>
+      <tr>
+        <th id="_ar_gkv_i1_label">If this is not a sec:{high,crit} [% terms.bug %], please state case for consideration</th>
+        <td><textarea aria-labelledby="_ar_gkv_i1_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_gkv_i2_label">User impact if declined</th>
+        <td><textarea aria-labelledby="_ar_gkv_i2_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_gkv_i3_label">Fix Landed on Version</th>
+        <td><input type="text" aria-labelledby="_ar_gkv_i3_label"></td>
+      </tr>
+      <tr>
+        <th id="_ar_gkv_i4_label">Risk to taking this patch</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_gkv_i4_label">
+            <div class="item"><input id="_ar_gkv_i4_r1" type="radio" name="_ar_gkv_i4_radio" value="Low"><label for="_ar_gkv_i4_r1">Low</label></div>
+            <div class="item"><input id="_ar_gkv_i4_r2" type="radio" name="_ar_gkv_i4_radio" value="Medium"><label for="_ar_gkv_i4_r2">Medium</label></div>
+            <div class="item"><input id="_ar_gkv_i4_r3" type="radio" name="_ar_gkv_i4_radio" value="High"><label for="_ar_gkv_i4_r3">High</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_gkv_i5_label">Why is the change risky/not risky?<br>(and alternatives if risky)</th>
+        <td><textarea aria-labelledby="_ar_gkv_i5_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_gkv_i6_label">String or UUID changes made by this patch</th>
+        <td><input type="text" class="long" aria-labelledby="_ar_gkv_i6_label"></td>
+      </tr>
+    </table>
+    <p>See <a href="https://wiki.mozilla.org/Release_Management/Uplift_rules" target="_blank">Patch uplifting rules</a> for more info.</p>
+  </fieldset>
+</template>
+
+<template class="approval-request" data-flags="sec‑approval">
+  <fieldset>
+    <legend>Security Approval Request</legend>
+    <table>
+      <tr>
+        <th id="_ar_sec_i1_label">How easily could an exploit be constructed based on the patch?</th>
+        <td><textarea aria-labelledby="_ar_sec_i1_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_sec_i2_label">Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_sec_i2_label">
+            <div class="item"><input id="_ar_sec_i2_r1" type="radio" name="_ar_sec_i2_radio" value="Yes"><label for="_ar_sec_i2_r1">Yes</label></div>
+            <div class="item"><input id="_ar_sec_i2_r2" type="radio" name="_ar_sec_i2_radio" value="No"><label for="_ar_sec_i2_r2">No</label></div>
+            <div class="item"><input id="_ar_sec_i2_r3" type="radio" name="_ar_sec_i2_radio" value="Dunno"><label for="_ar_sec_i2_r3">Dunno</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_sec_i3_label">Which older supported branches are affected by this flaw?</th>
+        <td><input type="text" aria-labelledby="_ar_sec_i3_label"></td>
+      </tr>
+      <tr>
+        <th id="_ar_sec_i4_label">If not all supported branches, which [% terms.bug %] introduced the flaw?</th>
+        <td><input type="text" placeholder="[% terms.Bug %] ID" aria-labelledby="_ar_sec_i4_label" data-type="b[% %]ug"></td>
+      </tr>
+      <tr>
+        <th id="_ar_sec_i5_label">Do you have backports for the affected branches?</th>
+        <td>
+          <div role="radiogroup" class="buttons toggle" aria-labelledby="_ar_sec_i5_label">
+            <div class="item"><input id="_ar_sec_i5_r1" type="radio" name="_ar_sec_i5_radio" value="Yes"><label for="_ar_sec_i5_r1">Yes</label></div>
+            <div class="item"><input id="_ar_sec_i5_r2" type="radio" name="_ar_sec_i5_radio" value="No"><label for="_ar_sec_i5_r2">No</label></div>
+          </div>
+        </td>
+      </tr>
+      <tr>
+        <th id="_ar_sec_i6_label">If not, how different, hard to create, and risky will they be?</th>
+        <td><textarea aria-labelledby="_ar_sec_i6_label"></textarea></td>
+      </tr>
+      <tr>
+        <th id="_ar_sec_i7_label">How likely is this patch to cause regressions; how much testing does it need?</th>
+        <td><textarea aria-labelledby="_ar_sec_i7_label"></textarea></td>
+      </tr>
+    </table>
+  </fieldset>
+</template>
index 88d9d4dd7835decdc2853ba0c95b0c4cf5310b65..56e03040ac2393e6b9a230cd44e8425efc5eeb3f 100644 (file)
   #   byron jones <glob@mozilla.com>
   #%]
 
-[% IF ftc_flags.keys.size %]
-  <script [% script_nonce FILTER none %]>
-  YAHOO.util.Event.onDOMReady(function() {
-    var selects = YAHOO.util.Dom.getElementsByClassName('flag_select');
-    for (var i = 0; i < selects.length; i++) {
-      YAHOO.util.Event.on(selects[i], 'change', ftc_on_change);
-    }
-  });
+<link rel="stylesheet" href="extensions/FlagTypeComment/web/styles/ftc.css">
+<script [% script_nonce FILTER none %] src="extensions/FlagTypeComment/web/js/ftc.js"></script>
 
-  function ftc_on_change(ev) {
-    var id = ev.target.id.split('-')[1];
-    var state = ev.target.value;
-    var commentEl = document.getElementById('comment');
-    if (!commentEl) return;
-    [% FOREACH type_id = ftc_flags.keys %]
-      [% FOREACH state = ftc_states %]
-        if ([% type_id FILTER none %] == id && '[% state FILTER js %]' == state) {
-          var text = '[% ftc_flags.$type_id.$state FILTER js %]';
-          var value = commentEl.value;
-          if (value == text) {
-            return;
-          } else if (value == '') {
-            commentEl.value = text;
-          } else {
-            commentEl.value = text + "\n\n" + value;
-          }
-        }
-      [% END %]
+[% IF ftc_flags.keys.size %]
+  [%# plaintext templates from database %]
+  [% FOREACH type_id = ftc_flags.keys %]
+    [% FOREACH state = ftc_states %]
+      <meta name="ftc:[% type_id FILTER none %]:[% state FILTER html %]"
+            content="[% ftc_flags.$type_id.$state FILTER html_linebreak %]">
     [% END %]
-  }
-  </script>
+  [% END %]
+  [%# HTML form templates from extensions %]
+  [% Hook.process("form") %]
 [% END %]
diff --git a/extensions/FlagTypeComment/web/js/ftc.js b/extensions/FlagTypeComment/web/js/ftc.js
new file mode 100644 (file)
index 0000000..2682721
--- /dev/null
@@ -0,0 +1,189 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+/**
+ * Reference or define the Bugzilla app namespace.
+ * @namespace
+ */
+var Bugzilla = Bugzilla || {}; // eslint-disable-line no-var
+
+/**
+ * Provide the ability to insert a comment template when a patch's approval flag is selected.
+ */
+Bugzilla.FlagTypeComment = class FlagTypeComment {
+  /**
+   * Initialize a new FlagTypeComment instance.
+   */
+  constructor() {
+    this.templates = [...document.querySelectorAll('template.approval-request')];
+    this.$flags = document.querySelector('#flags');
+    this.$comment = document.querySelector('#comment');
+
+    if (this.$flags && this.$comment) {
+      this.selects = [...this.$flags.querySelectorAll('.flag_select')];
+      this.selects.forEach($select => $select.addEventListener('change', () => this.flag_onselect($select)));
+      this.$comment.form.addEventListener('submit', () => this.form_onsubmit());
+    }
+  }
+
+  /**
+   * Check if a `<fieldset>` is compatible with the given flag. For example, `approval‑mozilla‑beta` matches
+   * `<fieldset data-flags="approval‑mozilla‑beta approval‑mozilla‑release">` while `approval‑mozilla‑esr60`
+   * matches `<fieldset data-flags="approval‑mozilla‑esr*">`.
+   * @param {String} name Flag name, such as `approval‑mozilla‑beta`.
+   * @param {(HTMLFieldSetElement|HTMLTemplateElement)} $element `<fieldset>` or `<template>` element with the
+   * `data-flags` attribute which is a space-separated list of flag names (wildcard chars can be used).
+   * @returns {Boolean} Whether the `<fieldset>` is compatible.
+   */
+  check_compatibility(name, $element) {
+    return !!$element.dataset.flags.split(' ')
+      .find(_name => !!name.match(new RegExp(`^${_name.replace('*', '.+')}$`, 'i')));
+  }
+
+  /**
+   * Return a list of temporary `<fieldset>`s already inserted to the current page.
+   * @type {Array<HTMLFieldSetElement>}
+   */
+  get inserted_fieldsets() {
+    return [...this.$flags.parentElement.querySelectorAll('fieldset.approval-request')];
+  }
+
+  /**
+   * Find a temporary `<fieldset>` already inserted to the current page by a flag name.
+   * @param {String} name Flag name, such as `approval‑mozilla‑beta`.
+   * @returns {HTMLFieldSetElement} Any `<fieldset>` element.
+   */
+  find_inserted_fieldset(name) {
+    return this.inserted_fieldsets.find($fieldset => this.check_compatibility(name, $fieldset));
+  }
+
+  /**
+   * Find an available `<fieldset>` embedded in HTML by a flag name.
+   * @param {String} name Flag name, such as `approval‑mozilla‑beta`.
+   * @returns {HTMLFieldSetElement} Any `<fieldset>` element.
+   */
+  find_available_fieldset(name) {
+    for (const $template of this.templates) {
+      if (this.check_compatibility(name, $template)) {
+        const $fieldset = $template.content.cloneNode(true).querySelector('fieldset');
+
+        $fieldset.className = 'approval-request';
+        $fieldset.dataset.flags = $template.dataset.flags;
+
+        return $fieldset;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Find a `<select>` element for a requested flag that matches the given `<fieldset>`.
+   * @param {HTMLFieldSetElement} $fieldset `<fieldset>` element with the `data-flags` attribute.
+   * @returns {HTMLSelectElement} Any `<select>` element.
+   */
+  find_select($fieldset) {
+    return this.selects
+      .find($_select => $_select.value === '?' && this.check_compatibility($_select.dataset.name, $fieldset));
+  }
+
+  /**
+   * Add text to the comment box at the end of any existing comment.
+   * @param {String} text Comment text to be added.
+   */
+  add_comment(text) {
+    this.$comment.value = this.$comment.value.match(/\S+/g) ? [this.$comment.value, text].join('\n\n') : text;
+  }
+
+  /**
+   * Called whenever a flag selection is changed. Insert or remove a comment template.
+   * @param {HTMLSelectElement} $select `<select>` element that the `change` event is fired.
+   */
+  flag_onselect($select) {
+    const id = Number($select.dataset.id);
+    const { name } = $select.dataset;
+    const state = $select.value;
+    let $fieldset = this.find_inserted_fieldset(name);
+
+    // Remove the temporary `<fieldset>` if not required. One `<fieldset>` can support multiple flags, so, for example,
+    // if `approval‑mozilla‑release` is unselected but `approval‑mozilla‑beta` is still selected, keep it
+    if (state !== '?' && $fieldset && !this.find_select($fieldset)) {
+      $fieldset.remove();
+    }
+
+    // Insert a temporary `<fieldset>` if available
+    if (state === '?' && !$fieldset) {
+      $fieldset = this.find_available_fieldset(name);
+
+      if ($fieldset) {
+        this.$flags.parentElement.appendChild($fieldset);
+      }
+    }
+
+    // Insert a traditional plaintext comment template if available
+    if (!$fieldset) {
+      const $meta = document.querySelector(`meta[name="ftc:${id}:${state}"]`);
+      const text = $meta ? $meta.content : '';
+
+      if (text && this.$comment.value !== text) {
+        this.add_comment(text);
+      }
+    }
+  }
+
+  /**
+   * Convert the input values into comment text and remove the temporary `<fieldset>` before submitting the form.
+   * @returns {Boolean} Always `true` to allow submitting the form.
+   */
+  form_onsubmit() {
+    for (const $fieldset of this.inserted_fieldsets) {
+      const text = [
+        `[${$fieldset.querySelector('legend').innerText}]`,
+        ...[...$fieldset.querySelectorAll('tr')].map($tr => {
+          const checkboxes = [...$tr.querySelectorAll('input[type="checkbox"]:checked')];
+          const $radio = $tr.querySelector('input[type="radio"]:checked');
+          const $input = $tr.querySelector('textarea,select,input');
+          const label = $tr.querySelector('th').innerText.replace(/\n/g, ' ');
+          let value = '';
+
+          if (checkboxes.length) {
+            value = checkboxes.map($checkbox => $checkbox.value.trim()).join(', ');
+          } else if ($radio) {
+            value = $radio.value.trim();
+          } else if ($input) {
+            value = $input.value.trim();
+
+            if ($input.dataset.type === 'bug') {
+              if (!value) {
+                value = 'None';
+              } else if (!isNaN(value)) {
+                value = `Bug ${value}`;
+              }
+            }
+
+            if ($input.dataset.type === 'bugs') {
+              if (!value) {
+                value = 'None';
+              } else {
+                value = value.split(/,\s*/).map(str => (!isNaN(str) ? `Bug ${str}` : str)).join(', ');
+              }
+            }
+          }
+
+          return `${label}: ${value}`;
+        }),
+      ].join('\n\n');
+
+      this.add_comment(text);
+      $fieldset.remove();
+    }
+
+    return true;
+  }
+};
+
+window.addEventListener('DOMContentLoaded', () => new Bugzilla.FlagTypeComment());
diff --git a/extensions/FlagTypeComment/web/styles/ftc.css b/extensions/FlagTypeComment/web/styles/ftc.css
new file mode 100644 (file)
index 0000000..93d1909
--- /dev/null
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+fieldset.approval-request {
+  margin: 1em 0;
+}
+
+fieldset.approval-request legend {
+  font-weight: bold;
+}
+
+fieldset.approval-request th {
+  padding: .4em;
+  width: 20em;
+  line-height: 1.25;
+  font-weight: normal;
+  text-align: right;
+  vertical-align: top;
+  white-space: normal !important;
+}
+
+fieldset.approval-request td {
+  vertical-align: middle;
+}
+
+fieldset.approval-request label {
+  font-weight: normal !important;
+}
+
+fieldset.approval-request input[type="text"] {
+  width: 10em;
+}
+
+fieldset.approval-request input.long[type="text"] {
+  width: 40em;
+}
+
+fieldset.approval-request textarea {
+  width: 40em;
+  min-height: 5em;
+  font-size: inherit;
+  line-height: inherit;
+  font-family: inherit;
+  resize: vertical;
+}
+
+fieldset.approval-request div {
+  padding: 0 !important;
+}
+
+fieldset.approval-request table ~ p:last-child {
+  margin: .4em;
+  text-align: right;
+}
index cec2d49e19bfd8dfbb50d09dd26471c69f9169ce..008bc70313ba91eec838cc24baa38f89ca377f2a 100644 (file)
@@ -133,10 +133,6 @@ table.attachment_info td {
     display: block;
 }
 
-#smallCommentFrame, #attachment_flags {
-    float: left;
-}
-
 #smallCommentFrame {
     margin-right: 1.5em;
 }
index e7028f8927bbb47dbdf027a4fb811581c200b1f8..81a420077b16a1ec299b5e0de4b848b611317113 100644 (file)
@@ -1110,6 +1110,67 @@ select, select[multiple] {
     font-size: 12px;
 }
 
+.buttons.toggle[role="radiogroup"] {
+  display: inline-flex;
+}
+
+.buttons.toggle[role="radiogroup"] .item {
+  display: flex;
+}
+
+.buttons.toggle[role="radiogroup"] input[type="radio"] {
+  position: absolute;
+  left: -99999px;
+}
+
+.buttons.toggle[role="radiogroup"] label {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  z-index: 1;
+  border: 1px solid #B2B2B2;
+  padding: 4px 12px;
+  min-width: 1em;
+  min-height: 1em;
+  background-color: #FFF;
+  box-shadow: inset 1px 1px 2px rgba(0, 0, 0, .1);
+  -moz-user-select: none;
+  -webkit-user-select: none;
+  user-select: none;
+}
+
+.buttons.toggle[role="radiogroup"] .item:first-child label {
+  border-radius: 4px 0 0 4px;
+}
+
+.buttons.toggle[role="radiogroup"] .item:last-child label {
+  border-radius: 0 4px 4px 0;
+}
+
+.buttons.toggle[role="radiogroup"] .item:not(:first-child) label {
+  margin-left: -1px;
+  border-left-color: #D2D2D2;
+}
+
+.buttons.toggle[role="radiogroup"] .item:not(:last-child) label {
+  border-right-color: #D2D2D2;
+}
+
+.buttons.toggle[role="radiogroup"] input[type="radio"]:checked + label {
+  z-index: 2;
+  border-color: #B2B2B2;
+  color: #111;
+  background-color: #DDD;
+  box-shadow: inset 1px 1px 2px rgba(0, 0, 0, .2);
+}
+
+.buttons.toggle[role="radiogroup"] input[type="radio"]:focus + label {
+  z-index: 2;
+  border-color: #42A4E0;
+  box-shadow: inset 1px 1px 2px rgba(0, 0, 0, .2), 0 0 0 2px rgba(73, 173, 227, .4);
+}
+
 hr {
     border: none;
     height: 1px;
index 6e411add9284dd96e52685522c18a1313a447aac..bf1c8a6b31988b91067c1e48e14e616b32a91050 100644 (file)
                 title="[% type.description FILTER html %]"
                 onchange="toggleRequesteeField(this);"
                 class="flag_select flag_type-[% type.id %]"
+                data-id="[% type.id %]" data-name="[% type.name FILTER html FILTER no_break %]"
                 [% IF !can_edit_flag %] disabled="disabled"[% END %]>
         [%# Only display statuses the user is allowed to set. %]
         [% IF !flag