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
$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({});
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";
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',
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);
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;
=back
+Permission required: bse_edit_prodopt_add
+
=cut
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);
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);
}
default_value =>
{
description => "Default Value",
- rules => "integer"
+ rules => "positiveint"
}
);
=back
+Permission required: bse_edit_prodopt_save
+
=cut
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
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);
}
success: 1,
}
+Permission required: bse_edit_prodopt_delete
+
=cut
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
=back
+Permission required: bse_edit_prodopt_edit
+
=cut
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);
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);
}
=back
+Permission required: bse_edit_prodopt_edit
+
=cut
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);
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);
}
=back
+Permission required: bse_edit_prodopt_edit
+
=cut
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
$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
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 ]
=back
+Permission required: bse_edit_prodopt_move
+
=cut
sub req_option_moveup {
=back
+Permission required: bse_edit_prodopt_move
+
=cut
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;
$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
$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 ]
=back
+Permission required: bse_edit_prodopt_edit
+
=cut
sub req_option_value_moveup {
=back
+Permission required: bse_edit_prodopt_edit
+
=cut
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
$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 {
$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 {
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) {
}
}
// 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
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) {
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 = {
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
});
}
}
}
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);
}
onFailure: function() {
alert("Error contacting server");
set_not_busy();
- }
+ },
+ onException: handle_exception
});
}
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
});
}
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');
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;
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 + "')";
}
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() });
+
<: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&_t=prodopts&id=<:article id:>&_csrfp=<:csrfp admin_move_option:>&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&_t=prodopts&id=<:article id:>&_csrfp=<:csrfp admin_move_option:>&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:>&a_edit_option=1&option_id=<:dboption id:>">Edit</a>
<a href="<:script:>?id=<:article id:>&a_delconf_option=1&option_id=<:dboption id:>">Delete</a>
<a id="sortvals<:dboption id:>" href="<:script:>?id=<:article id:>&a_option_value_reorder=1&option_id=<:dboption id:>&_csrfp=<:csrfp admin_move_option_value:>&_t=prodopts&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:>&a_option_value_reorder=1&option_id=<:dboption id:>&_csrfp=<:csrfp admin_move_option_value:>&_t=prodopts&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:>&value_id=<:dboptionvalue id:>&a_edit_option_value=1">Edit</a> <a href="<:script:>?id=<:product id:>&value_id=<:dboptionvalue id:>&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:>&value_id=<:dboptionvalue id:>&a_edit_option_value=1">Edit</a>
+<a href="<:script:>?id=<:product id:>&value_id=<:dboptionvalue id:>&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:>';
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>
<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:>