[freeside-commits] branch master updated. a69299c596de60f4b26db7431165f7f3ffe928e2

Mark Wells mark at 420.am
Wed Mar 14 13:44:17 PDT 2012


The branch, master has been updated
       via  a69299c596de60f4b26db7431165f7f3ffe928e2 (commit)
      from  da590709ee5f3e6a661950fcfce39dbbf9d6add4 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit a69299c596de60f4b26db7431165f7f3ffe928e2
Author: Mark Wells <mark at freeside.biz>
Date:   Wed Mar 14 13:44:00 2012 -0700

    svc_hardware MAC address input format, #16266

diff --git a/FS/FS/svc_hardware.pm b/FS/FS/svc_hardware.pm
index b4eb8cc..22e6275 100644
--- a/FS/FS/svc_hardware.pm
+++ b/FS/FS/svc_hardware.pm
@@ -164,7 +164,7 @@ sub check {
   if ( $conf->exists('svc_hardware-check_mac_addr') ) {
     $hw_addr = uc($hw_addr);
     $hw_addr =~ /^[0-9A-F]{12}$/ 
-      or return "Illegal (MAC address) ".$self->getfield('hw_addr');
+      or return "Illegal (MAC address) '".$self->getfield('hw_addr')."'";
   }
   $self->setfield('hw_addr', $hw_addr);
 
diff --git a/httemplate/docs/credits.html b/httemplate/docs/credits.html
index 7b3b0b9..9bb1dec 100644
--- a/httemplate/docs/credits.html
+++ b/httemplate/docs/credits.html
@@ -61,6 +61,7 @@ Joe Camadine<BR>
 Chris Cappuccio<BR>
 Rebecca Cardennis<BR>
 Shane Chrisp<BR>
+Kendall Conrad<BR>
 Luke Crawford<BR>
 Brad Dameron<BR>
 Dave Denney<BR>
diff --git a/httemplate/docs/license.html b/httemplate/docs/license.html
index e0e40b7..fab8cd0 100644
--- a/httemplate/docs/license.html
+++ b/httemplate/docs/license.html
@@ -112,6 +112,11 @@ terms as Perl (GPL/Artistic).
 Contains code derived from HTML::GoogleMapsV3 by David Peters, licensed under
 the same terms as Perl (GPL/Artistic).
 
+<P>
+Contains the Masked Input JavaScript library by Kendall Conrad, licensed under
+a <a href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons 
+Attribution-ShareAlike 3.0 United States</a> license.
+
 <!-- artwork -->
 
 <P>
diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi
index b07c725..b266928 100644
--- a/httemplate/edit/svc_broadband.cgi
+++ b/httemplate/edit/svc_broadband.cgi
@@ -103,7 +103,8 @@ my @fields = (
   qw( description speed_down speed_up ),
   { field=>'sectornum', type=>'select-tower_sector', },
   { field=>'routernum', type=>'select-router_block_ip' },
-    qw( mac_addr latitude longitude altitude vlan_profile 
+  { field=>'mac_addr' , type=>'input-mac_addr' },
+    qw( latitude longitude altitude vlan_profile 
     performance_profile authkey plan_id )
 );
 
diff --git a/httemplate/edit/svc_hardware.cgi b/httemplate/edit/svc_hardware.cgi
index dcf83de..d9cd4cd 100644
--- a/httemplate/edit/svc_hardware.cgi
+++ b/httemplate/edit/svc_hardware.cgi
@@ -26,7 +26,8 @@ my @fields = (
   },
   {
     field => 'hw_addr',
-    type  => 'text',
+    type  => $conf->exists('svc_hardware-check_mac_addr') ? 
+              'input-mac_addr' : 'text',
     label => 'Hardware address',
   },
   {
diff --git a/httemplate/elements/masked_input_1.1.js b/httemplate/elements/masked_input_1.1.js
new file mode 100644
index 0000000..05efa77
--- /dev/null
+++ b/httemplate/elements/masked_input_1.1.js
@@ -0,0 +1,195 @@
+/***********************************************************************
+                       Masked Input version 1.1
+************************************************************************
+Author: Kendall Conrad
+Home page: http://www.angelwatt.com/coding/masked_input.php
+Created:  2008-12-16
+Modified: 2010-04-14
+Description:
+License: This work is licensed under a Creative Commons Attribution-Share Alike
+  3.0 United States License http://creativecommons.org/licenses/by-sa/3.0/us/
+
+Argument pieces:
+- elm:        [req] text input node to apply the mask on
+- format:     [req] string format for the mask
+- allowed:    [opt, '0123456789'] string with chars allowed to be typed
+- sep:        [opt, '\/:-'] string of char(s) used as separators in mask
+- typeon:     [opt, '_YMDhms'] string of chars in mask that can be typed on
+- onbadkey:   [opt, null] function to run when user types a unallowed key
+- badkeywait: [opt, 0] used with onbadkey. Indicates how long (in ms) to lock
+  text input for onbadkey function to run
+***********************************************************************/
+function MaskedInput(args)
+{
+  if (args['elm'] === null || args['format'] === null) { return false; }
+  var el     = args['elm'],
+    format   = args['format'],
+    allowed  = args['allowed']    || '0123456789',
+    sep      = args['separator']  || '\/:-',
+    open     = args['typeon']     || '_YMDhms',
+    onbadkey = args['onbadkey']   || function(){},
+    badwait  = args['badkeywait'] || 0;
+  
+  var locked = false, hold = 0;
+  el.value = format;
+  // Assign events
+  el.onkeydown  = KeyHandlerDown;  //
+  el.onkeypress = KeyHandlerPress; // add event handlers to element
+  el.onkeyup    = KeyHandlerUp;    //
+
+  function GetKey(code)
+  {
+    code = code || window.event, ch = '';
+    var keyCode = code.which, evt = code.type;
+    if (keyCode == null) { keyCode = code.keyCode; }
+    if (keyCode === null) { return ''; } // no key, no play
+    // deal with special keys
+    switch (keyCode) {
+    case 8:  ch = 'bksp'; break;
+    case 46: // handle del and . both being 46
+      ch = (evt == 'keydown') ? 'del' : '.'; break;
+    case 16: ch = 'shift'; break;//shift
+    case 0:/*CRAP*/ case 9:/*TAB*/ case 13:/*ENTER*/
+      ch = 'etc'; break;
+    case 37: case 38: case 39: case 40: // arrow keys
+      ch = (!code.shiftKey &&
+           (code.charCode != 39 && code.charCode !== undefined)) ?
+        'etc' : String.fromCharCode(keyCode);
+      break;
+    // default to thinking it's a character or digit
+    default: ch = String.fromCharCode(keyCode);
+    }
+    return ch;
+  }
+  function KeyHandlerDown(e)
+  {
+    e = e || event;
+    if (locked) { return false; }
+    var key = GetKey(e);
+    if (el.value == '') { el.value = format; SetTextCursor(el,0); }
+    // Only do update for bksp del
+    if (key == 'bksp' || key == 'del') { Update(key); return false; }
+    else if (key == 'etc' || key == 'shift') { return true; }
+    else { return true; }    
+  }
+  function KeyHandlerPress(e)
+  {
+    e = e || event;
+    if (locked) { return false; }
+    var key = GetKey(e);
+    // Check if modifier key is being pressed; command
+    if (key=='etc' || e.metaKey || e.ctrlKey || e.altKey) { return true; }
+    if (key != 'bksp' && key != 'del' && key != 'etc' && key != 'shift') {
+      if (!GoodOnes(key)) { return false; }
+      return Update(key);
+    }
+    else { return false; }
+  }
+  function KeyHandlerUp(e) { hold = 0; }
+  function Update(key)
+  {
+    var p = GetTextCursor(el), c = el.value, val = '';
+    // Handle keys now
+    switch (true) {
+    case (allowed.indexOf(key) != -1):
+      if (++p > format.length) { return false; } // if text csor at end
+      // Handle cases where user places csor before separator
+      while (sep.indexOf(c.charAt(p-1)) != -1 && p <= format.length) { p++; }
+      val = c.substr(0, p-1) + key + c.substr(p);
+      // Move csor up a spot if next char is a separator char
+      if (allowed.indexOf(c.charAt(p)) == -1
+          && open.indexOf(c.charAt(p)) == -1) { p++; }
+      break;
+    case (key=='bksp'): // backspace
+      if (--p < 0) return false; // at start of field
+      // If previous char is a separator, move a little more
+      while (allowed.indexOf(c.charAt(p)) == -1
+             && open.indexOf(c.charAt(p)) == -1
+             && p > 1) { p--; }
+      val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1);
+      break;
+    case (key=='del'): // forward delete
+      if (p >= c.length) { return false; } // at end of field
+      // If next char is a separator and not the end of the text field
+      while (sep.indexOf(c.charAt(p)) != -1
+             && c.charAt(p) != '') { p++; }
+      val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1);
+      p++; // Move position forward
+      break;
+    case (key=='etc'): return true; // Catch other allowed chars
+    default: return false;   // Ignore the rest
+    }
+    el.value = '';        // blank it first (Firefox issue)
+    el.value = val;       // put updated value back in
+    SetTextCursor(el, p); // Set the text cursor
+    return false;
+  }
+  function GetTextCursor(node)
+  {
+    try {
+      if (node.selectionStart >= 0) { return node.selectionStart; }
+      else if (document.selection) {// IE
+        var ntxt = node.value; // getting starting text
+        var rng = document.selection.createRange();
+        rng.text = '|%|';
+        var start = node.value.indexOf('|%|');
+        rng.moveStart('character', -3);
+        rng.text = '';
+        // put starting text back in,
+        // fixes issue if all text was highlighted
+        node.value = ntxt;
+        return start;
+      } return -1;
+    } catch(e) { return false; }
+  }
+  function SetTextCursor(node, pos)
+  {
+    try {
+      if (node.selectionStart) {
+        node.focus();
+        node.setSelectionRange(pos,pos);
+      }
+      else if (node.createTextRange) { // IE
+        var rng = node.createTextRange();
+        rng.move('character', pos);
+        rng.select();
+      }
+    } catch(e) { return false; }
+  }
+  function GoodOnes(k)
+  {
+    if (allowed.indexOf(k) == -1 && k!='bksp' && k!='del' && k!='etc') {
+      var p = GetTextCursor(el); // Need to ensure cursor position not lost
+      locked = true; onbadkey();
+      // Hold lock long enough for onbadkey function to run
+      setTimeout(function(){locked=false; SetTextCursor(el,p);}, badwait);
+      return false;
+    } return true;
+  }
+  function resetField() {
+    el.value = format;
+  }
+  function setAllowed(a) {
+    allowed = a;
+    resetField();
+  }
+  function setFormat(f) {
+    format = f;
+    resetField();
+  }
+  function setSeparator(s) {
+    sep = s;
+    resetField();
+  }
+  function setTypeon(t) {
+    open = t;
+    resetField();
+  }
+  return {
+    resetField:resetField,
+    setAllowed:setAllowed,
+    setFormat:setFormat,
+    setSeparator:setSeparator,
+    setTypeon:setTypeon
+  }
+}
diff --git a/httemplate/elements/tr-input-mac_addr.html b/httemplate/elements/tr-input-mac_addr.html
new file mode 100644
index 0000000..d768d4e
--- /dev/null
+++ b/httemplate/elements/tr-input-mac_addr.html
@@ -0,0 +1,11 @@
+<& /elements/tr-input-mask.html, 
+  format => '__:__:__:__:__:__',
+  allowed => '0123456789ABCDEFabcdef',
+  %opt,
+&>
+<%init>
+my %opt = @_;
+my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value};
+$value =~ s/\W//g;
+$opt{curr_value} = join(':', $value =~ /../g);
+</%init>
diff --git a/httemplate/elements/tr-input-mask.html b/httemplate/elements/tr-input-mask.html
new file mode 100644
index 0000000..33725b9
--- /dev/null
+++ b/httemplate/elements/tr-input-mask.html
@@ -0,0 +1,41 @@
+% if ( !$init ) {
+<script type="text/javascript" src="<%$p%>elements/masked_input_1.1.js">
+</script>
+% $init++;
+% }
+<& /elements/tr-input-text.html, id => $id, @_ &>
+<script type="text/javascript">
+MaskedInput({
+  elm: document.getElementById('<%$id%>'),
+  format: '<% $opt{format} %>',
+  <% $opt{allowed} ? "allowed: '$opt{allowed}'," : '' %>
+  <% $opt{typeon}  ? "typeon:  '$opt{typeon}',"  : '' %>
+});
+document.getElementById('<%$id%>').value = <% $value |js_string %>;
+</script>
+<%shared>
+my $init = 0;
+</%shared>
+<%init>
+my %opt = @_;
+# must have a DOM id
+my $id = $opt{id} || sprintf('input%04d',int(rand(10000)));
+my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value} || '';
+</%init>
+<%doc>
+Set up a text input field with input masking.
+
+<& /elements/tr-input-mask.html,
+  format    => '____-__-__',
+  #typeon   => '_YMDhms',    # which characters in the format represent blanks
+  #allowed  => '0123456789', # characters allowed in the blanks
+  ... all other options as for tr-input-text.html
+&>
+
+Note that the value sent on form submission will contain the mask 
+separators, and if value/curr_value is passed, it should also be 
+formatted to fit the mask.
+
+Uses masked_input_1.1.js by Kendall Conrad, available under a Creative Commons
+Attribution-ShareAlike license.
+</%doc>
diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi
index af2c575..131582f 100644
--- a/httemplate/view/svc_broadband.cgi
+++ b/httemplate/view/svc_broadband.cgi
@@ -31,7 +31,7 @@ my @fields = (
   'speed_up',
   { field => 'ip_addr', value => \&ip_addr },
   { field => 'sectornum', value => \&sectornum },
-  'mac_addr',
+  { field => 'mac_addr', value => \&mac_addr },
   #'latitude',
   #'longitude',
   { field => 'coordinates', value => \&coordinates },
@@ -66,6 +66,11 @@ sub ip_addr {
   $out;
 }
 
+sub mac_addr {
+  my $svc = shift;
+  join(':', $svc->mac_addr =~ /../g);
+}
+
 sub usergroup {
   my $svc = shift;
   my $usergroup = $svc->usergroup;
diff --git a/httemplate/view/svc_hardware.cgi b/httemplate/view/svc_hardware.cgi
index 1d88235..725358c 100644
--- a/httemplate/view/svc_hardware.cgi
+++ b/httemplate/view/svc_hardware.cgi
@@ -6,6 +6,7 @@
 %>
 <%init>
 
+my $conf = new FS::Conf;
 my $fields = FS::svc_hardware->table_info->{'fields'};
 my %labels = map { $_ =>  ( ref($fields->{$_})
                              ? $fields->{$_}{'label'}
@@ -24,5 +25,22 @@ my $note =   { field => 'note',
                type  => 'text',
                value => sub { encode_entities($_[0]->note) }
              };
-my @fields = ($model, qw( serial hw_addr ip_addr smartcard ), $status, $note );
+my $hw_addr ={ field => 'hw_addr',
+               type  => 'text',
+               value => sub { 
+                my $hw_addr = $_[0]->hw_addr;
+                $conf->exists('svc_hardware-check_mac_addr') ?
+                  join(':', $hw_addr =~ /../g) : $hw_addr
+                },
+              };
+
+my @fields = (
+  $model,
+  'serial',
+  $hw_addr,
+  'ip_addr',
+  'smartcard',
+  $status,
+  $note,
+);
 </%init>

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/svc_hardware.pm                      |    2 +-
 httemplate/docs/credits.html               |    1 +
 httemplate/docs/license.html               |    5 +
 httemplate/edit/svc_broadband.cgi          |    3 +-
 httemplate/edit/svc_hardware.cgi           |    3 +-
 httemplate/elements/masked_input_1.1.js    |  195 ++++++++++++++++++++++++++++
 httemplate/elements/tr-input-mac_addr.html |   11 ++
 httemplate/elements/tr-input-mask.html     |   41 ++++++
 httemplate/view/svc_broadband.cgi          |    7 +-
 httemplate/view/svc_hardware.cgi           |   20 +++-
 10 files changed, 283 insertions(+), 5 deletions(-)
 create mode 100644 httemplate/elements/masked_input_1.1.js
 create mode 100644 httemplate/elements/tr-input-mac_addr.html
 create mode 100644 httemplate/elements/tr-input-mask.html




More information about the freeside-commits mailing list