make it work in IE
authorTony Cook <tony@develop-help.com>
Wed, 22 Jul 2009 09:23:56 +0000 (09:23 +0000)
committertony <tony@45cb6cf1-00bc-42d2-bb5a-07f51df49f94>
Wed, 22 Jul 2009 09:23:56 +0000 (09:23 +0000)
drag and drop values

update arrows for values

site/cgi-bin/bse.cfg
site/cgi-bin/modules/BSE/AdminLogon.pm
site/cgi-bin/modules/BSE/Edit/Article.pm
site/cgi-bin/modules/BSE/Edit/Product.pm
site/cgi-bin/modules/BSE/Request/Base.pm
site/htdocs/js/admin_prodopts.js
site/templates/admin/edit_prodopts.tmpl
site/templates/admin/xbase.tmpl

index a5db8e53b062fac71544b478541c1fcb9235a679..e597cbca8a5b6375acbc083e3c7a02a52b224761 100644 (file)
@@ -384,10 +384,18 @@ display_postcode=Post code
 display_telephone=Phone
 
 [nonajax user agents]
-ie4=MSIE
+ie4=MSIE 4
+ie5=MSIE 5.01
 
 [ajax user agents]
 mozilla=^Mozilla/5\.0
+ie5=MSIE 5.5
+ie6=MSIE 6
+ie7=MSIE 7
+ie8=MSIE 8
+safari=Safari
+khtml=KHTML
+opera=Opera
 
 [ajax definitions]
 includes=<<INLINE
index 45bce5c57c410882d9f60abc1a7d7e80fbf70f96..9ab934e4cc9a8bd8a1bc5d59a808bb51383215bd 100644 (file)
@@ -120,6 +120,7 @@ sub req_logon {
   $user && $user->{password} eq $password
     or return $class->_service_error($req, "Invalid logon or password");
   $req->session->{adminuserid} = $user->{id};
+  delete $req->session->{csrfp};
 
   if ($cgi->param('_service')) {
     return $class->_service_success({});
@@ -138,6 +139,7 @@ sub req_logoff {
   my ($class, $req) = @_;
 
   delete $req->session->{adminuserid};
+  delete $req->session->{csrfp};
   ++$req->session->{changed};
 
   my $r = admin_base_url($req->cfg) . "/cgi-bin/admin/logon.pl";
index 1dbc309e729f41bb82f22a33d6d8f870ddb71fe8..d99e7d51a43272b5aa6fadc7a38a4b5951c085f4 100644 (file)
@@ -32,10 +32,17 @@ This is badly organized and documented.
 sub not_logged_on {
   my ($self, $req) = @_;
 
-  if (() = $req->cgi->param('_') ||
-     (defined $ENV{HTTP_X_REQUESTED_WITH}
-      && $ENV{HTTP_X_REQUESTED_WITH} =~ /XMLHttpRequest/)) {
+  if ($req->is_ajax) {
     # AJAX/Prototype request
+    return $req->json_content
+      (
+       {
+       message => "Access forbidden: user not logged on",
+       errors => {},
+       }
+      );
+  }
+  elsif ($req->cgi->param('_service')) {
     return
       {
        content => 'Access Forbidden: login timed out',
@@ -2464,7 +2471,10 @@ sub _service_error {
   elsif ((() = $req->cgi->param('_')) ||
         (defined $ENV{HTTP_X_REQUESTED_WITH}
          && $ENV{HTTP_X_REQUESTED_WITH} =~ /XMLHttpRequest/)) {
-    return $req->json_content({ errors => $error });
+    $error ||= {};
+    my $result = { errors => $error };
+    $msg and $result->{message} = $msg;
+    return $req->json_content($result);
   }
   else {
     return $self->edit_form($req, $article, $articles, $msg, $error);
@@ -3931,8 +3941,9 @@ sub csrf_error {
   my ($self, $req, $article, $name, $description) = @_;
 
   my %errors;
-  $errors{_csrfp} = "Possible CSRF attempt on $name/$description: " . $req->csrf_error;
-  return $self->_service_error($req, $article, 'Articles', undef, \%errors);
+  my $msg = $req->csrf_error;
+  $errors{_csrfp} = $msg;
+  return $self->_service_error($req, $article, 'Articles', $msg, \%errors);
 }
 
 1;
index 2739cd1b86cf6bf86a9a709831a55f430be4bf46..b169a4574e7899551d19a1748f61bc4827091a2b 100644 (file)
@@ -605,6 +605,8 @@ option as values.
 
 =back
 
+Permission required: bse_edit_prodopt_add 
+
 =cut
 
 sub req_add_option {
@@ -613,6 +615,9 @@ sub req_add_option {
   $req->check_csrf('admin_add_option')
     or return $self->csrf_error($req, $article, "admin_add_option", "Add Product Option");
 
+  $req->user_can(bse_edit_prodopt_add => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to add options");
+
   my %errors;
   $req->validate(fields => \%option_fields,
                 errors => \%errors);
@@ -740,11 +745,16 @@ id.
 
 Template: admin/prodopt_edit
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_edit_option {
   my ($self, $req, $article, $articles, $msg, $errors) = @_;
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   return $self->_common_option('admin/prodopt_edit', $req, $article, 
                               $articles, $msg, $errors);
 }
@@ -759,7 +769,7 @@ my %option_name =
    default_value =>
    {
     description => "Default Value",
-    rules => "integer"
+    rules => "positiveint"
    }
   );
 
@@ -824,6 +834,8 @@ changed.
 
 =back
 
+Permission required: bse_edit_prodopt_save
+
 =cut
 
 sub req_save_option {
@@ -834,23 +846,27 @@ sub req_save_option {
   $req->check_csrf("admin_save_option")
     or return $self->csrf_error($req, $article, "admin_save_option", "Save Product Option");
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   my %errors;
   my $option = $self->_get_option($req, $article, \%errors);
   keys %errors
     and return $self->_service_error($req, $article, $articles, undef, \%errors);
-  $req->validate(rules => \%option_name,
+  $req->validate(fields => \%option_name,
                 errors => \%errors);
   my @values = $option->values;
   my %fields = map {; "value$_->{id}" => \%option_value } @values;
-  $req->validate(rules => \%fields,
-                errors => \%errors);
+  $req->validate(fields => \%fields,
+                errors => \%errors,
+                optional => 1);
   my $default_value = $cgi->param('default_value');
   if (!$errors{default_value} && $default_value) {
     grep $_->{id} == $default_value, @values
       or $errors{default_value} = "Unknown value selected as default";
   }
   keys %errors
-    and return $self->req_edit_option($req, $article, $articles, undef, \%errors);
+    and return $self->_service_error($req, $article, $articles, undef, \%errors);
 
   my $name = $cgi->param("name");
   defined $name
@@ -907,6 +923,9 @@ Template: admin/prodopt_delete
 sub req_delconf_option {
   my ($self, $req, $article, $articles, $msg, $errors) = @_;
 
+  $req->user_can(bse_edit_prodopt_delete => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to delete options");
+
   return $self->_common_option('admin/prodopt_delete', $req, $article, 
                               $articles, $msg, $errors);
 }
@@ -925,6 +944,8 @@ For Ajax requests (or with a _ parameter), returns JSON like:
    success: 1,
   }
 
+Permission required: bse_edit_prodopt_delete
+
 =cut
 
 sub req_delete_option {
@@ -933,6 +954,9 @@ sub req_delete_option {
   $req->check_csrf("admin_delete_option")
     or return $self->csrf_error($req, $article, "admin_delete_option", "Delete Product Option");
 
+  $req->user_can(bse_edit_prodopt_delete => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to delete options");
+
   my %errors;
   my $option = $self->_get_option($req, $article, \%errors);
   keys %errors
@@ -1000,6 +1024,8 @@ value - text of the value to add.
 
 =back
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_add_option_value {
@@ -1008,6 +1034,9 @@ sub req_add_option_value {
   $req->check_csrf("admin_add_option_value")
     or return $self->csrf_error($req, $article, "admin_add_option_value", "Add Product Option Value");
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   my %errors;
   $req->validate(fields => \%add_option_value_fields,
                 errors => \%errors);
@@ -1116,11 +1145,16 @@ given product.
 
 Template: admin/prodopt_value_edit
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_edit_option_value {
   my ($self, $req, $article, $articles, $msg, $errors) = @_;
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   return $self->_common_option_value('admin/prodopt_value_edit', $req,
                                     $article, $articles, $msg, $errors);
 }
@@ -1167,6 +1201,8 @@ value - new displayed value for the option value.
 
 =back
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_save_option_value {
@@ -1175,6 +1211,9 @@ sub req_save_option_value {
   $req->check_csrf("admin_save_option_value")
     or return $self->csrf_error($req, $article, "admin_save_option_value", "Save Product Option Value");
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   my %errors;
   $req->validate(fields => \%save_option_value_fields,
                 errors => \%errors);
@@ -1216,11 +1255,16 @@ value_id - option value id
 
 Template: admin/prodopt_value_delete
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_confdel_option_value {
   my ($self, $req, $article, $articles, $msg, $errors) = @_;
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   return $self->_common_option_value('admin/prodopt_value_delete', $req,
                                     $article, $articles, $msg, $errors);
 }
@@ -1254,6 +1298,8 @@ identified by id.
 
 =back
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_delete_option_value {
@@ -1262,6 +1308,9 @@ sub req_delete_option_value {
   $req->check_csrf("admin_delete_option_value")
     or return $self->csrf_error($req, $article, "admin_delete_option_value", "Delete Product Option Value");
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   my %errors;
   my $option_value = $self->_get_option_value($req, $article, \%errors);
   keys %errors
@@ -1301,6 +1350,9 @@ sub _option_move {
   $req->check_csrf("admin_move_option")
     or return $self->csrf_error($req, $article, "admin_move_option", "Move Product Option");
 
+  $req->user_can(bse_edit_prodopt_move => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to move options");
+
   my %errors;
   my $option = $self->_get_option($req, $article, \%errors);
   keys %errors
@@ -1324,7 +1376,7 @@ sub _option_move {
 
   if ($req->is_ajax) {
     @options = sort { $a->{display_order} <=> $b->{display_order} } @options;
-    return $req->json_content
+    return return $req->json_content
       (
        success => 1,
        order => [ map $_->{id}, @options ]
@@ -1366,6 +1418,8 @@ id.
 
 =back
 
+Permission required: bse_edit_prodopt_move
+
 =cut
 
 sub req_option_moveup {
@@ -1410,6 +1464,8 @@ product identified by id.
 
 =back
 
+Permission required: bse_edit_prodopt_move
+
 =cut
 
 sub req_option_reorder {
@@ -1418,6 +1474,9 @@ sub req_option_reorder {
   $req->check_csrf("admin_move_option")
     or return $self->csrf_error($req, $article, "admin_move_option", "Move Product Option");
 
+  $req->user_can(bse_edit_prodopt_move => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to move options");
+
   my @options = $article->db_options;
   my @order = map { split ',' } $req->cgi->param('option_ids');
   my %options = map { $_->{id} => $_ } @options;
@@ -1450,6 +1509,9 @@ sub _option_value_move {
   $req->check_csrf("admin_move_option_value")
     or return $self->csrf_error($req, $article, "admin_move_option_value", "Move Product Option Value");
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   my %errors;
   my ($option_value, $option) = $self->_get_option_value($req, $article, \%errors);
   keys %errors
@@ -1471,8 +1533,11 @@ sub _option_value_move {
   $option_value->save;
   $other->save;
 
+  # make sure the json gets the new order
+  @values[$index, $other_index] = @values[$other_index, $index];
+
   $req->is_ajax
-    and $req->json_content
+    and return $req->json_content
       (
        success => 1,
        order => [ map $_->{id}, @values ]
@@ -1514,6 +1579,8 @@ id.
 
 =back
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_option_value_moveup {
@@ -1562,6 +1629,8 @@ commas.
 
 =back
 
+Permission required: bse_edit_prodopt_edit
+
 =cut
 
 sub req_option_value_reorder {
@@ -1570,6 +1639,9 @@ sub req_option_value_reorder {
   $req->check_csrf("admin_move_option_value")
     or return $self->csrf_error($req, $article, "admin_move_option_value", "Move Product Option Value");
 
+  $req->user_can(bse_edit_prodopt_edit => $article)
+    or return $self->_service_error($req, $article, $articles, "Insufficient product access to edit options");
+
   my %errors;
   my $option = $self->_get_option($req, $article, \%errors);
   keys %errors
index 4da8188e0f2af2e3072e16929b80caa084d3042e..6a8a738f2565f508f9fb5b605ccc36b8c1c07272 100644 (file)
@@ -283,12 +283,20 @@ sub validate {
   $options{rules} ||= {};
 
   require BSE::Validate;
-  BSE::Validate::bse_validate($req->cgi, $options{errors},
-                             { 
-                              fields => $options{fields},
-                              rules => $options{rules},
-                             },
-                             $req->cfg, $options{section});
+  my %opts =
+    (
+     fields => $options{fields},
+     rules => $options{rules},
+    );
+  exists $options{optional} and $opts{optional} = $options{optional};
+  BSE::Validate::bse_validate
+      (
+       $req->cgi,
+       $options{errors},
+       \%opts,
+       $req->cfg,
+       $options{section}
+      );
 }
 
 sub validate_hash {
@@ -296,13 +304,21 @@ sub validate_hash {
 
   $options{rules} ||= {};
 
+  my %opts =
+    (
+     fields => $options{fields},
+     rules => $options{rules},
+    );
+  exists $options{optional} and $opts{optional} = $options{optional};
   require BSE::Validate;
-  BSE::Validate::bse_validate_hash($options{data}, $options{errors},
-                                  { 
-                                   fields=>$options{fields},
-                                   rules => $options{rules},
-                                  },
-                                  $req->cfg, $options{section});
+  BSE::Validate::bse_validate_hash
+      (
+       $options{data},
+       $options{errors},
+       \%opts,
+       $req->cfg,
+       $options{section}
+      );
 }
 
 sub configure_fields {
index 772b96fd65b3f08283973ad1a49f229d584dd77d..6524b6c89bff49c0ebf1559c3a99b2cdfcf9b21a 100644 (file)
@@ -1,8 +1,12 @@
 var prodopts_by_id = new Object;
 var menu;
 
-function reorder_option_values(id, order) {
-  var parent = $("vallist"+id);
+function handle_exception(e) {
+  alert("Exception: " + e);
+}
+
+function reorder_option_values(opt_id, order) {
+  var parent = $("vallist"+opt_id);
   var nodes = new Array;
   var nodes_by_id = new Object;
   for (var i = 0; i < parent.childNodes.length; ++i) {
@@ -16,7 +20,7 @@ function reorder_option_values(id, order) {
     }
   }
   // remove our value nodes
-  for (var i = 0; i < nodes; ++i) {
+  for (var i = 0; i < nodes.length; ++i) {
     parent.removeChild(nodes[i]);
   }
   // put them back in, in the new order
@@ -26,7 +30,18 @@ function reorder_option_values(id, order) {
       parent.appendChild(n);
   }
 
-  // TODO: reorder the values in prodopts
+  var vals = prodopts_by_id[opt_id].values;
+  var vals_by_id = new Object;
+  for (var i = 0; i < vals.length; ++i) {
+    vals_by_id[vals[i].id] = vals[i];
+  }
+  var new_vals = new Array;
+  for (i = 0; i < order.length; ++i) {
+    new_vals.push(vals_by_id[order[i]]);
+  }
+  prodopts_by_id[opt_id].values = new_vals;
+
+  fix_prodoptval_order_tools(prodopts_by_id[opt_id]);
 }
 
 function reorder_options(order) {
@@ -62,6 +77,44 @@ function reorder_options(order) {
   fix_prodopt_order_tools();
 }
 
+function reorder_values(option_id, order) {
+  var parent = $("vallist"+option_id);
+  var nodes = new Array;
+  var nodes_by_id = new Object;
+  for (var i = 0; i < parent.childNodes.length; ++i) {
+    var n = parent.childNodes[i];
+    if (n.id) {
+      var m = n.id.match(/^valentry(\d+)$/);
+      if (m) {
+        nodes_by_id[m[1]] = n;
+        nodes.push(n);
+      }
+    }
+  }
+  // remove our value nodes
+  for (var i = 0; i < nodes; ++i) {
+    parent.removeChild(nodes[i]);
+  }
+  // put them back in, in the new order
+  for (var i = 0; i < order.length; ++i) {
+    var n = nodes_by_id[order[i]];
+    if (n)
+      parent.appendChild(n);
+  }
+
+  var opt = prodopts_by_id[option_id];
+  var vals_by_id = new Object;
+  for (var i = 0; i < opt.values.length; ++i) {
+    vals_by_id[opt.values[i].id] = opt.values[i];
+  }
+  opt.values = new Array;
+  for (var i = 0; i < order.length; ++i) {
+    opt.values.push(vals_by_id[order[i]]);
+  }
+
+  fix_prodoptval_order_tools(opt);
+}
+
 function do_option_move(key, id) {
   set_busy("prodoptmove"+id);
   var parm = {
@@ -77,17 +130,18 @@ function do_option_move(key, id) {
     onSuccess: function(xport) {
       var json = xport.responseJSON;
       set_not_busy("prodoptmove"+id);
-      if (json.success) {
+      if (json && json.success) {
         reorder_options(json.order);
       }
       else {
-        alert("Error ordering: " + json.error);
+        show_response_error("reordering options", xport);
       }
     },
-    onFailure: function() {
-      alert("Error contacting server");
+    onFailure: function(xport) {
       set_not_busy("prodoptmove"+id);
-    }
+      show_response_error("reordering options", xport);
+    },
+    onException: handle_exception
     });
 }
 
@@ -111,28 +165,107 @@ function fix_prodopt_order_tools() {
       }
     }
     if (i != prodopts.length-1) {
-      var move_down = document.createElement("a");
+      var move_down = new Element("a");
       move_down.href="javascript:prodopt_move_down("+opt.id+")";
-      var down_img = document.createElement("img");
+      var down_img = new Element("img");
       down_img.src = "/images/admin/move_down.gif";
       move_down.appendChild(down_img);
       move_ele.appendChild(move_down);
     }
     else {
-      var empty = document.createElement('img');
+      var empty = new Element('img');
       empty.src = "/images/trans_pixel.gif";
       move_ele.appendChild(empty);
     }
     if (i != 0) {
-      var move_up = document.createElement("a");
+      var move_up = new Element("a");
       move_up.href="javascript:prodopt_move_up("+opt.id+")";
-      var up_img = document.createElement("img");
+      var up_img = new Element("img");
       up_img.src = "/images/admin/move_up.gif";
       move_up.appendChild(up_img);
       move_ele.appendChild(move_up);
     }
     else {
-      var empty = document.createElement('img');
+      var empty = new Element('img');
+      empty.src = "/images/trans_pixel.gif";
+      move_ele.appendChild(empty);
+    }
+  }
+}
+
+function do_value_move(key, opt_id, id) {
+  set_busy("prodoptvaluemove"+id);
+  var parm = {
+      id: article_id,
+      option_id: opt_id,
+      value_id: id,
+      _csrfp: reorder_values_csrf,
+      _: 1
+    };
+  parm[key] = 1;
+  new Ajax.Request(edit_script, {
+    method: "post",
+    parameters: parm,
+    onSuccess: function(xport) {
+      var json = xport.responseJSON;
+      set_not_busy("prodoptvaluemove"+id);
+      if (json && json.success) {
+        reorder_option_values(opt_id, json.order);
+      }
+      else {
+        show_response_error("reordering values", xport);
+      }
+    },
+    onFailure: function(xport) {
+      set_not_busy("prodoptvaluemove"+id);
+      show_response_error("reordering values", xport);
+    },
+    onException: handle_exception
+    });
+}
+
+function prodoptval_move_up(opt_id, id) {
+  do_value_move('a_option_value_moveup', opt_id, id);
+}
+
+function prodoptval_move_down(opt_id, id) {
+  do_value_move('a_option_value_movedown', opt_id, id);
+}
+
+function fix_prodoptval_order_tools(opt) {
+  var vals = opt.values;
+  for (var i = 0; i < vals.length; ++i) {
+    var val = vals[i];
+    var move_ele = $('prodoptvaluemove'+val.id);
+    if (move_ele) {
+      // remove all the kids
+      while (move_ele.firstChild != null) {
+        move_ele.removeChild(move_ele.firstChild);
+      }
+    }
+    if (i != vals.length-1) {
+      var move_down = new Element("a");
+      move_down.href="javascript:prodoptval_move_down("+opt.id+","+val.id+")";
+      var down_img = new Element("img");
+      down_img.src = "/images/admin/move_down.gif";
+      move_down.appendChild(down_img);
+      move_ele.appendChild(move_down);
+    }
+    else {
+      var empty = new Element('img');
+      empty.src = "/images/trans_pixel.gif";
+      move_ele.appendChild(empty);
+    }
+    if (i != 0) {
+      var move_up = new Element("a");
+      move_up.href="javascript:prodoptval_move_up("+opt.id+","+val.id+")";
+      var up_img = new Element("img");
+      up_img.src = "/images/admin/move_up.gif";
+      move_up.appendChild(up_img);
+      move_ele.appendChild(move_up);
+    }
+    else {
+      var empty = new Element('img');
       empty.src = "/images/trans_pixel.gif";
       move_ele.appendChild(empty);
     }
@@ -193,7 +326,8 @@ function sort_prodopt_values(id) {
     onFailure: function() {
       alert("Error contacting server");
       set_not_busy();
-    }
+    },
+    onException: handle_exception
     });
 }
 
@@ -211,17 +345,48 @@ function reorder_prodopts_req(ele_id, ids) {
     onSuccess: function(xport) {
       var json = xport.responseJSON;
       set_not_busy(ele_id);
-      if (json.success) {
+      if (json && json.success) {
         reorder_options(json.order);
       }
       else {
-        alert("Error ordering: " + json.error);
+        show_response_error("re-ordering options", xport);
       }
     },
-    onFailure: function() {
-      alert("Error contacting server");
+    onFailure: function(xport) {
       set_not_busy(ele_id);
-    }
+      show_response_error("re-ordering options", xport);
+    },
+    onException: handle_exception
+    });
+}
+
+function reorder_prodoptvals_req(ele_id, opt_id, ids) {
+  set_busy(ele_id);
+  new Ajax.Request(edit_script, {
+    method: "post",
+    parameters: {
+      a_option_value_reorder: 1,
+      id: article_id,
+      option_id: opt_id,
+      value_ids: ids.join(","),
+      _csrfp: reorder_values_csrf,
+      _: 1
+    },
+    onSuccess: function(xport) {
+      var json = xport.responseJSON;
+      set_not_busy(ele_id);
+      if (json && json.success) {
+        reorder_values(opt_id, json.order);
+      }
+      else {
+        show_response_error("re-ordering values", xport);
+      }
+    },
+    onFailure: function(xport) {
+      set_not_busy(ele_id);
+      show_response_error("re-ordering values", xport);
+    },
+    onException: handle_exception
     });
 }
 
@@ -252,6 +417,63 @@ function reverse_prodopts() {
   reorder_prodopts_req("reverseoptions", ids);
 }
 
+function show_response_error(action, xport) {
+  if (xport.responseJSON) {
+    if (xport.responseJSON.errors
+        && xport.responseJSON.errors._csrfp) {
+      alert("Please reload the page, your update tokens may be out of date\n\n"+xport.responseJSON.errors._csrfp);
+    }
+    else if (xport.responseJSON.message)
+       alert("Error "+action+": " + xport.responseJSON.message);
+    else
+      alert("Unknown error "+action);
+  }
+  else if (xport.responseText) {
+    alert(xport.responseText);
+  }
+  else
+    alert("Unknown error "+action);
+}
+
+function prodopt_add_option_ipe(opt) {
+    new Ajax.InPlaceEditor("prodoptname"+opt.id, edit_script,
+      {
+      cancelControl: "button",
+      okText: "Save",
+      cancelText: "Cancel",
+      callback: function(f, v) {
+        return "_=1&_t=prodopts&_csrfp="+ edit_option_csrf +"&id="+article_id+"&a_save_option=1&option_id="+this.id+"&name="+encodeURIComponent(v);
+      }.bind(opt),
+      onComplete: function(xport) {
+        var name_ele = $("prodoptname"+this.id);
+        name_ele.innerHTML = "";
+        var new_name;
+        if (xport
+            && xport.status == 200 
+            && xport.responseJSON
+            && xport.responseJSON.success) {
+          new_name = xport.responseJSON.option.name;
+         prodopts_by_id[this.id].name = new_name;
+          name_ele.appendChild(document.createTextNode(new_name));
+        }
+        else {
+          // restore the original name
+          new_name = prodopts_by_id[this.id].name;
+          name_ele.appendChild(document.createTextNode(new_name));
+          if (xport) {
+            if (xport.responseJSON && xport.responseJSON.errors.name) {
+              alert("Error saving option name: " 
+                   + xport.responseJSON.errors.name);
+           }
+            else
+              show_response_error("saving option name", xport);
+         }  // otherwise cancelled edit
+        }
+      }.bind(opt)
+      });
+
+}
+
 function prodopts_start() {
   menu = $('prodoptmenu');
   var sortopts = $('sortoptions');
@@ -263,17 +485,20 @@ function prodopts_start() {
     reverseopts.href="javascript:reverse_prodopts()";
   }
   busy_img = $('busy_img');
-  Sortable.create("productoptions",
-    { 
-      tag: "div",
-      only: "prodopt",
-      format: /^prodopt(\d+)$/,
-      handle: "prodoptmenu",
-      onUpdate: function () {
-        reorder_prodopts_req("sortoptions", 
-            Sortable.sequence("productoptions"));
-      }
-    });
+  if (user_can_move_option) {
+    Sortable.create("productoptions",
+      { 
+        tag: "div",
+        only: "prodopt",
+        format: /^prodopt(\d+)$/,
+        handle: "prodoptmenu",
+        onUpdate: function () {
+          reorder_prodopts_req("sortoptions", 
+              Sortable.sequence("productoptions"));
+        },
+            onException: handle_exception
+      });
+  }
   for (var i = 0; i < prodopts.length; ++i) {
     var opt = prodopts[i];
     prodopts_by_id[opt.id] = opt;
@@ -281,23 +506,23 @@ function prodopts_start() {
     var opt_ele = $(opt_ele_id);
     var edit_ele_id = "editoption" + opt.id;
     var edit_ele = $(edit_ele_id);
-    new Ajax.InPlaceEditor("prodoptname"+opt.id, edit_script,
-      {
-      cancelControl: "button",
-      okText: "Save",
-      cancelText: "Cancel",
-      callback: function(option_id, f, v) {
-        return "_=1&_t=prodopts&_csrfp="+ edit_option_csrf +"&id="+article_id+"&a_save_option=1&option_id="+option_id+"&name="+encodeURIComponent(v);
-      }.bind(opt_ele, opt.id),
-      onComplete: function(id, xport) {
-        var name_ele = $("prodoptname"+id);
-        name_ele.innerHTML = "";
-        var new_name = xport.responseJSON.option.name;
-        name_ele.appendChild(document.createTextNode(new_name));
-       prodopts_by_id[id].name = new_name;
-      }.bind(opt_ele, opt.id)
-      });
-
+    if (user_can_edit_option)
+      prodopt_add_option_ipe(opt);
+      if (user_can_edit_option) {
+        fix_prodoptval_order_tools(opt);
+        Sortable.create("vallist"+opt.id, 
+          {
+            format: /^valentry(\d+)$/,
+            onUpdate: function (parent) {
+              var m = /^vallist(\d+)/.exec(parent.id);
+              if (m) {
+                reorder_prodoptvals_req("sortvalues"+m[1], m[1],
+                  Sortable.sequence(parent.id));
+              }
+            },
+            onException: handle_exception
+         });
+      }
 //    opt_ele.appendChild(document.createTextNode(" "));
 //    var sort_a = document.createElement("a");
 //    sort_a.href = "javascript:sort_prodopt_values('" + opt.id + "')";
@@ -307,3 +532,15 @@ function prodopts_start() {
   }
   fix_prodopt_order_tools();
 }
+
+Event.observe(document, "dom:loaded",
+  function() {
+    var add_option_form = $('addoptionform');
+    if (add_option_form)
+      add_option_form.style.display='none';
+    if ($('addoptionbutton'))
+      $('addoptionbutton').style.display='block';
+  });
+
+Event.observe(document, "dom:loaded", function() { prodopts_start() });
+
index 2f67fd9966b63e3372b89af117300a4e487849ec..73078b3f5b57700bbd49d77cba607270bac05ae5 100644 (file)
@@ -4,41 +4,55 @@
 
 <:if Dboptions:>
 <h2>Product options</h2>
+<:if UserCan bse_edit_prodopt_move:article:>
 <div id="prodoptmenu">All options:
 <a id="sortoptions" href="<:script:>?a_option_reorder=1&amp;_t=prodopts&amp;id=<:article id:>&amp;_csrfp=<:csrfp admin_move_option:>&amp;option_ids=<:arithmetic join ",", map $_->{id}, sort { lc $a->{name} cmp lc $b->{name} } Products->getByPkey([article id])->db_options:>">Sort</a>
 <a id="reverseoptions" href="<:script:>?a_option_reorder=1&amp;_t=prodopts&amp;id=<:article id:>&amp;_csrfp=<:csrfp admin_move_option:>&amp;option_ids=<:arithmetic join ",", map $_->{id}, reverse Products->getByPkey([article id])->db_options:>">Reverse</a>
 <img src="/images/admin/busy.gif" id="busy_img" style="visibility: hidden" />
 </div>
+<:or UserCan:><:eif UserCan:>
 <div id="productoptions">
 <:iterator begin dboptions:>
 <div id="prodopt<:dboption id:>" class="prodopt">
 <div id="prodoptmenu<:dboption id:>" class="prodoptmenu">Option: <span id="prodoptname<:dboption id:>"><:dboption name:></span>
 <div class="prodoptmenuoptions">
+<:if UserCan bse_edit_prodopt_edit:article :>
 <a id="editoption<:dboption id:>" href="<:script:>?id=<:article id:>&amp;a_edit_option=1&amp;option_id=<:dboption id:>">Edit</a>
 <a href="<:script:>?id=<:article id:>&amp;a_delconf_option=1&amp;option_id=<:dboption id:>">Delete</a>
 <a id="sortvals<:dboption id:>" href="<:script:>?id=<:article id:>&amp;a_option_value_reorder=1&amp;option_id=<:dboption id:>&amp;_csrfp=<:csrfp admin_move_option_value:>&amp;_t=prodopts&amp;value_ids=<:arithmetic join ',', map $_->{id}, sort { lc $a->{value} cmp lc $b->{value} } BSE::TB::ProductOptions->getByPkey([dboption id])->values:>">Sort</a>
 <a id="reversevals<:dboption id:>" href="<:script:>?id=<:article id:>&amp;a_option_value_reorder=1&amp;option_id=<:dboption id:>&amp;_csrfp=<:csrfp admin_move_option_value:>&amp;_t=prodopts&amp;value_ids=<:arithmetic join ',', map $_->{id}, reverse BSE::TB::ProductOptions->getByPkey([dboption id])->values:>">Reverse</a>
-<:dboption_move:>
+<:or UserCan:><:eif UserCan:>
+<:ifUserCan bse_edit_prodopt_move:article:><:dboption_move:><:or:><:eif:>
 </div>
 </div>
 <:if Dboptionvalues:>
 <ul id="vallist<:dboption id:>" class="prodoptvalues">
 <:iterator begin dboptionvalues:>
-<li id="valentry<:dboptionvalue id:>"><:dboptionvalue value:> <:ifEq [dboptionvalue id] [dboption default_value]:>(default)<:or:><:eif:> <a href="<:script:>?id=<:product id:>&amp;value_id=<:dboptionvalue id:>&amp;a_edit_option_value=1">Edit</a> <a href="<:script:>?id=<:product id:>&amp;value_id=<:dboptionvalue id:>&amp;a_confdel_option_value=1">Delete</a> <:dboptionvalue_move:></li>
+<li id="valentry<:dboptionvalue id:>"><:dboptionvalue value:>
+<:ifEq [dboptionvalue id] [dboption default_value]:>(default)<:or:><:eif:>
+<:if UserCan bse_edit_prodopt_edit:article:>
+<a href="<:script:>?id=<:product id:>&amp;value_id=<:dboptionvalue id:>&amp;a_edit_option_value=1">Edit</a>
+<a href="<:script:>?id=<:product id:>&amp;value_id=<:dboptionvalue id:>&amp;a_confdel_option_value=1">Delete</a>
+<:dboptionvalue_move:>
+<:or UserCan:><:eif UserCan:>
+</li>
 <:iterator end dboptionvalues:>
-</ul>  
-<form action="<:script:>" method="post" id="valform<:dboption id:>" /><input type="hidden" name="id" value="<:article id:>" /><input type="hidden" name="option_id" value="<:dboption id:>" /><input type="hidden" name="_t" value="prodopts" /><:csrfp admin_add_option_value hidden:><input type="text" name="value" /><input type="submit" name="a_add_option_value" value="Add Value" /></form>
+</ul>
 <:or Dboptionvalues:><:eif Dboptionvalues:>
+<:if UserCan bse_edit_prodopt_edit:article:>
+<form action="<:script:>" method="post" id="valform<:dboption id:>" /><input type="hidden" name="id" value="<:article id:>" /><input type="hidden" name="option_id" value="<:dboption id:>" /><input type="hidden" name="_t" value="prodopts" /><:csrfp admin_add_option_value hidden:><input type="text" name="value" /><input type="submit" name="a_add_option_value" value="Add Value" /></form>
+<:or UserCan:><:eif UserCan:>
 </div>
 <:iterator end dboptions:>
 </div>
 <:or Dboptions:><:eif Dboptions:>
+<:if UserCan bse_edit_prodopt_add:article:>
 <div id="addoptionform">
 <form action="<:script:>" method="post">
 <:csrfp admin_add_option hidden:>
 <input type="hidden" name="_t" value="prodopts" />
 <input type="hidden" name="id" value="<:article id:>" />
-<table>
+<table class="editform editformsmall">
   <tr>
     <th>Name</th>
     <td><input type="text" name="name" value="<:old name:>" size="40" maxlength="40" /><:error_img name:></td>
 <div id="addoptionbutton" style="display: none">
 <a href="#" onclick="javascript: document.getElementById('addoptionform').style.display='block'; return false;">Add an option</a>
 </div>
+<:or UserCan:><:eif UserCan:>
 <script>
 
-<:ifDboptions:>
-$('addoptionform').style.display='none';
-$('addoptionbutton').style.display='block';
-<:or:><:eif:>
-
 var prodopts = <:dboptionsjson:>;
 var reorder_values_csrf = '<:csrfp admin_move_option_value:>';
 var reorder_options_csrf = '<:csrfp admin_move_option:>';
@@ -74,6 +84,8 @@ var edit_option_csrf = '<:csrfp admin_save_option:>';
 var article_id = "<:article id:>";
 var edit_script = "<:script:>";
 
-Event.observe(window, "load", function() { prodopts_start() });
+var user_can_edit_option = <:ifUserCan bse_edit_prodopt_edit:>1<:or:>0<:eif:>;
+var user_can_delete_option = <:ifUserCan bse_edit_prodopt_delete:>1<:or:>0<:eif:>;
+var user_can_move_option = <:ifUserCan bse_edit_prodopt_move:>1<:or:>0<:eif:>;
 
 </script>
index 4aa8a990d0d63e697d8c440c972f49d4e65a15e5..aebcb78c39bfbb28c4b0372ce86f17b68fd97194 100644 (file)
@@ -4,7 +4,7 @@
 <html><head><title>BSE - <:param title:></title>
 <link rel="stylesheet" href="/css/admin.css" />
 <:ajax includes:>
-<:ifParam js:><script type="text/javascript" src="/js/<:param js:>"></script><:or:><:eif:>
+<:ifAnd [param js] [ifAjax]:><script type="text/javascript" src="/js/<:param js:>"></script><:or:><:eif:>
 </head>
 <body>
 <:ifParam showtitle:><h1><:param title:></h1><:or:><:eif:>