This commit is contained in:
hummypkg 2014-06-05 21:12:25 +00:00
parent 3f6259fa09
commit f44e2f20fc
12 changed files with 601 additions and 79 deletions

View File

@ -1,9 +1,9 @@
Package: sweeper Package: sweeper
Priority: optional Priority: optional
Section: misc Section: misc
Version: 1.0.13 Version: 1.0.14
Architecture: mipsel Architecture: mipsel
Maintainer: af123@hummypkg.org.uk Maintainer: af123@hummypkg.org.uk
Depends: webif(>=1.0.14-2) Depends: webif(>=1.0.14-3)
Description: Automatically manage single recording files. [Web Interface. Multi-folder support.] Description: Automatically manage single recording files. [Web Interface. Multi-folder support.]
Tags: http://hummy.tv/forum/threads/3843/ Tags: http://hummy.tv/forum/threads/3843/

View File

@ -57,7 +57,7 @@ proc ::sweeper::strcontains {ref val} {
} }
###################################################################### ######################################################################
# Rule clauses # Rule criteria
proc ::sweeper::flag {ts flag} { proc ::sweeper::flag {ts flag} {
return [$ts flag $flag] return [$ts flag $flag]
@ -124,15 +124,23 @@ proc ::sweeper::genre {ts genre} {
proc ::sweeper::lock {ts g} { proc ::sweeper::lock {ts g} {
if {$g} { if {$g} {
log "Locked recording." 0 if {![$ts flag Locked]} {
$ts lock log "Locked recording." 0
$ts lock
}
} else { } else {
log "Unlocked recording." 0 if {[$ts flag Locked]} {
$ts unlock log "Unlocked recording." 0
$ts unlock
}
} }
return 1 return 1
} }
proc ::sweeper::folder_fflag {ts flag} {
return [file exists "[file dirname [$ts get file]]/.$flag"]
}
###################################################################### ######################################################################
proc ::sweeper::action {ts cmds} { proc ::sweeper::action {ts cmds} {
@ -172,6 +180,20 @@ proc ::sweeper::action {ts cmds} {
} }
return 1 return 1
} }
lock {
if {![$ts flag Locked]} {
log "Locked [$ts get file]" 0
$ts lock
}
return 0
}
unlock {
if {[$ts flag Locked]} {
log "Unlocked [$ts get file]" 0
$ts unlock
}
return 0
}
default { default {
log "Unknown action '$cmd'" 0 log "Unknown action '$cmd'" 0
} }
@ -195,6 +217,32 @@ proc ::sweeper::find {root target orig} {
return "" return ""
} }
proc ::sweeper::folder_apply {dir callback} {
log "Applying action to recordings in $dir" 2
foreach e [readdir -nocomplain $dir] {
if {![string match {*.ts} $e]} continue
set entry "$dir/$e"
log "+ folder_apply processing $entry" 2
if {[catch {set ts [ts fetch $entry]} msg} {
log "Error reading TS file, $msg" 0
continue
}
if {$ts == "0"} {
log "Invalid TS file." 2
continue
}
if {[$ts inuse]} {
log "Recording in use." 2
continue
}
$callback $ts
}
}
proc ::sweeper:folder_merge {src dst} { proc ::sweeper:folder_merge {src dst} {
if {$src eq $dst} return if {$src eq $dst} return
log "Moving recordings from $src to $dst" 0 log "Moving recordings from $src to $dst" 0
@ -287,6 +335,24 @@ proc ::sweeper::folder_action {ts cmds} {
::sweeper:folder_merge $folder $target ::sweeper:folder_merge $folder $target
return 1 return 1
} }
lock {
::sweeper::folder_apply $folder [lambda {ts} {
if {![$ts flag Locked]} {
log "Locked [$ts get file]" 0
$ts lock
}
}]
return 0
}
unlock {
::sweeper::folder_apply $folder [lambda {ts} {
if {[$ts flag Locked]} {
log "Unlocked [$ts get file]" 0
$ts unlock
}
}]
return 0
}
default { default {
log "Unknown action '$cmd'" 0 log "Unknown action '$cmd'" 0
} }
@ -384,7 +450,9 @@ proc ::sweeper::apply {dir cf} {
if {[string match {\[*} $e]} continue if {[string match {\[*} $e]} continue
if {$e eq $dustbin} continue if {$e eq $dustbin} continue
log "+ Sweeper processing folder $entry" 2 log "" 2
log "==== folder $entry ====" 2
log "" 2
if {![file exists "$entry/.series"]} { if {![file exists "$entry/.series"]} {
log "Not series folder." 2 log "Not series folder." 2

View File

@ -6,9 +6,14 @@ require system.class
httpheader "text/javascript" httpheader "text/javascript"
puts { set raw 0
if {[file exists /mod/webif/plugin/sweeper/.raw]} {
var showraw = true; set raw 1
} }
puts "
var showraw = $raw;
"

View File

@ -39,6 +39,14 @@ puts {
</div> </div>
<div id=output></div> <div id=output></div>
<div id=macros>
Add pre-defined ruleset:
<select id=macroselect>
<option value=0>--- Select ruleset ---</option>
</select>
<button id=b_macro>Add to rules</button>
</div>
<div id=rule_template class="hidden"> <div id=rule_template class="hidden">
<fieldset class=rule> <fieldset class=rule>
<legend class=comment> <legend class=comment>
@ -55,25 +63,44 @@ puts {
</a> </a>
</span> </span>
<span class=legendright> <span class=legendright>
<span class="disabledtext blood hidden">
(Disabled)
</span>
<a class=enadisrule href=#>
<img src=img/disable.png
alt="Enable/Disable Rule"
title="Enable/Disable Rule">
</a>
<a class=delrule href=#> <a class=delrule href=#>
<img src=/img/context/delete.png> <img src=/img/context/delete.png
alt="Delete Rule" title="Delete Rule">
</a>
<a class=cprule href=#>
<img src=/img/context/page_white_copy.png
alt="Duplicate Rule" title="Duplicate Rule">
</a> </a>
<a class=uprule href=#> <a class=uprule href=#>
<img src=/img/nav/up.png> <img src=/img/nav/up.png
alt="Move Rule Up" title="Move Rule Up">
</a> </a>
<a class=downrule href=#> <a class=downrule href=#>
<img src=/img/nav/down.png> <img src=/img/nav/down.png
alt="Move Rule Down" title="Move Rule Down">
</a> </a>
</span> </span>
</legend> </legend>
<a href=# class=addcriterion>
<img src=/img/context/plus.png
alt="Add condition" title="Add condition">
</a>
<div class=criteria> <div class=criteria>
<table class="criteria keyval"><tbody> <table class="criteria"><tbody></tbody></table>
</tbody></table> </div>
<div class=arrow>
<img src=img/arrow.png height=32>
</div> </div>
<div class=action> <div class=action>
<table class="action keyval"><tbody> <table class="action"><tbody></tbody></table>
</tbody></table>
<button class=addcriterion>Add Criterion</button>
</div> </div>
<div class=raw></div> <div class=raw></div>
</fieldset> </fieldset>
@ -119,6 +146,13 @@ puts {
</select> </select>
<input name=edit_action_arg id=edit_action_arg size=60 maxlength=255 /> <input name=edit_action_arg id=edit_action_arg size=60 maxlength=255 />
</div> </div>
<div class=hidden id=empty_rulebase>
<b>There are no rules currently defined for this folder.</b>
<br>
You can add some example rules by using the <i>Add pre-defined ruleset</i>
option at the bottom of the screen.
</div>
} }
footer footer

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -48,6 +48,12 @@ proc quot {str} {
proc rule {id rule} { proc rule {id rule} {
global clausedescr lcomment global clausedescr lcomment
set enabled 1
if {[lindex $rule 0] eq "##"} {
set enabled 0
set rule [lrange $rule 1 end]
}
if {$lcomment eq ""} { set lcomment "Unnamed rule" } if {$lcomment eq ""} { set lcomment "Unnamed rule" }
if {[lindex $rule 0] eq "folder"} { if {[lindex $rule 0] eq "folder"} {
set type "folder" set type "folder"
@ -61,14 +67,17 @@ proc rule {id rule} {
add_json " \"raw\": \"[quot $rule]\",\n" add_json " \"raw\": \"[quot $rule]\",\n"
add_json " \"name\": \"[quot $lcomment]\",\n" add_json " \"name\": \"[quot $lcomment]\",\n"
add_json " \"type\": \"$type\",\n" add_json " \"type\": \"$type\",\n"
add_json " \"enabled\": \"$enabled\",\n"
add_json " \"criteria\": \[\n" add_json " \"criteria\": \[\n"
set lcomment "" set lcomment ""
set c 0 set c 0
set lockfound -1
while {[llength $rule] > 1} { while {[llength $rule] > 1} {
set rule [lassign $rule cmd arg] set rule [lassign $rule cmd arg]
if {$cmd eq "action"} break if {$cmd eq "action"} break
if {$cmd eq "lock"} { set lockfound $arg }
if {$c} { add_json ",\n" } if {$c} { add_json ",\n" }
incr c incr c
@ -78,7 +87,17 @@ proc rule {id rule} {
add_json " }" add_json " }"
} }
add_json "\n" add_json "\n"
if {$cmd ne "action"} { set arg "continue" } if {$cmd ne "action"} {
if {$lockfound != -1} {
if {$lockfound == "1"} {
set arg "lock"
} else {
set arg "unlock"
}
} else {
set arg "continue"
}
}
lassign $arg cmd arg lassign $arg cmd arg
if {$cmd eq "preserve"} { set cmd 'stop' } if {$cmd eq "preserve"} { set cmd 'stop' }
add_json " ],\n" add_json " ],\n"
@ -95,8 +114,8 @@ foreach rule $rules {
if {[string index $rule 0] eq "#"} { if {[string index $rule 0] eq "#"} {
if {[string index $rule 1] ne "#"} { if {[string index $rule 1] ne "#"} {
comment $n [string range $rule 1 end] comment $n [string range $rule 1 end]
continue
} }
continue
} }
if {[llength $rule] < 2} continue if {[llength $rule] < 2} continue
incr n incr n

View File

@ -6,6 +6,21 @@ require system.class
httpheader httpheader
set act [cgi_get act "save"]
switch $act {
raw {
set val [cgi_get val 0]
if {$val} {
file touch /mod/webif/plugin/sweeper/.raw
} else {
file delete -force /mod/webif/plugin/sweeper/.raw
}
puts "Done."
exit;
}
}
set dir [cgi_get dir ""] set dir [cgi_get dir ""]
set root [system mediaroot] set root [system mediaroot]

View File

@ -1,56 +1,67 @@
var schema = { var schema = {
criterion: { criterion: {
lcn: { lcn: {
'class': 'all',
type: 'int', type: 'int',
desc: 'Logical Channel Number', desc: 'Logical Channel Number',
def: "0" def: "0"
}, },
duration: { duration: {
'class': 'all',
type: 'int', type: 'int',
desc: 'Recording duration (in minutes)', desc: 'Recording duration (in minutes)',
def: "0" def: "0"
}, },
schedduration: { schedduration: {
'class': 'all',
type: 'int', type: 'int',
desc: 'Scheduled duration (in minutes)', desc: 'Scheduled duration (in minutes)',
def: "0" def: "0"
}, },
size: { size: {
'class': 'all',
type: 'int', type: 'int',
desc: 'Recording size (in bytes)', desc: 'Recording size (in bytes)',
def: "0" def: "0"
}, },
hour: { hour: {
'class': 'all',
type: 'int', type: 'int',
desc: 'Hour in which the recording started', desc: 'Hour in which the recording started',
def: "0" def: "0"
}, },
age: { age: {
'class': 'all',
type: 'int', type: 'int',
desc: 'Age (in hours)', desc: 'Age (in hours)',
def: "0" def: "0"
}, },
wage: { wage: {
'class': 'all',
type: 'int', type: 'int',
desc: 'Time since last watched, or recorded (in hours)', desc: 'Time since last watched, or recorded (in hours)',
def: "0" def: "0"
}, },
title: { title: {
'class': 'all',
type: 'substr', type: 'substr',
desc: 'Recording Title contains', desc: 'Recording Title contains',
def: 'Enter text here...' def: 'Enter text here...'
}, },
synopsis: { synopsis: {
'class': 'all',
type: 'substr', type: 'substr',
desc: 'Synopsis contains', desc: 'Synopsis contains',
def: 'Enter text here...' def: 'Enter text here...'
}, },
guidance: { guidance: {
'class': 'all',
type: 'substr', type: 'substr',
desc: 'Guidance Text contains', desc: 'Guidance Text contains',
def: 'Enter text here...' def: 'Enter text here...'
}, },
genre: { genre: {
'class': 'all',
type: 'select', type: 'select',
desc: 'Recording Genre', desc: 'Recording Genre',
select: { select: {
@ -67,6 +78,7 @@ var schema = {
def: 'Unclassified' def: 'Unclassified'
}, },
definition: { definition: {
'class': 'all',
type: 'select', type: 'select',
desc: 'Recording Definition', desc: 'Recording Definition',
select: { select: {
@ -76,6 +88,7 @@ var schema = {
def: 'SD' def: 'SD'
}, },
flag: { flag: {
'class': 'all',
type: 'select', type: 'select',
desc: 'Recording Flagged as', desc: 'Recording Flagged as',
select: { select: {
@ -90,13 +103,21 @@ var schema = {
def: 'New' def: 'New'
}, },
lock: { lock: {
'class': 'all',
type: 'select', type: 'select',
desc: 'Change Recording Lock', desc: 'Change Recording Lock',
select: { select: {
1: 'Lock Recording', 1: 'Lock Recording',
0: 'Unlock Recording' 0: 'Unlock Recording'
}, },
def: 1 def: 1,
deprecated: true
},
fflag: {
'class': 'folder',
type: 'string',
desc: 'Folder flagged as',
def: 'noflatten'
} }
}, },
action: { action: {
@ -104,50 +125,224 @@ var schema = {
'class': 'all', 'class': 'all',
argtype: 'none', argtype: 'none',
desc: 'Continue on to next rule', desc: 'Continue on to next rule',
showcont: false continues: true
}, },
stop: { stop: {
'class': 'all', 'class': 'all',
argtype: 'none', argtype: 'none',
desc: 'Do nothing and stop processing rules', desc: 'Do nothing and stop processing rules',
showcont: false continues: false
}, },
move: { move: {
'class': 'all', 'class': 'all',
argtype: 'folder', argtype: 'folder',
desc: 'Move recording to folder...', desc: 'Move recording to folder...',
showcont: true continues: false
},
lock: {
'class': 'all',
argtype: 'none',
desc: 'Lock recordings',
continues: true
},
unlock: {
'class': 'all',
argtype: 'none',
desc: 'Unlock recordings',
continues: true
}, },
movecreate: { movecreate: {
'class': 'all', 'class': 'all',
argtype: 'folder', argtype: 'folder',
desc: 'Move recording to folder (creating if necessary)', desc: 'Move recording to folder (creating if necessary)',
showcont: true continues: false
}, },
fileunder: { fileunder: {
'class': 'folder', 'class': 'folder',
argtype: 'folder', argtype: 'folder',
desc: 'Merge into first folder of same name found under...', desc: 'Merge into first folder of same name found under...',
showcont: true continues: false
}, },
fileundercreate: { fileundercreate: {
'class': 'folder', 'class': 'folder',
argtype: 'folder', argtype: 'folder',
desc: 'Merge into or create folder of ' + desc: 'Merge into or create folder of ' +
'same name found under...', 'same name found under...',
showcont: true continues: false
} }
} }
}; };
var select_criteria = {}; var macros = {
seriesfiler: {
desc: 'Series-Filer',
rules: [
{
"raw": "action {fileunder &quot;&quot;}",
"name": "Emulate Seriesfiler",
"type": "folder",
"enabled": "1",
"criteria": [],
"action": {
"cmd": "fileunder",
"arg": ""
}
}
]
},
example: {
desc: 'Example rules',
rules: [
{
"raw": "lcn {&gt;= 70} lcn {&lt;= 79} duration {&gt;= 90} action {move Children/Films}",
"name": "Move any Children's films (by length)",
"type": "file",
"enabled": "0",
"criteria": [
{
"cmd": "lcn",
"arg": "&gt;= 70"
},
{
"cmd": "lcn",
"arg": "&lt;= 79"
},
{
"cmd": "duration",
"arg": "&gt;= 90"
}
],
"action": {
"cmd": "move",
"arg": "Children/Films"
}
},
{
"raw": "lcn {&gt;= 70} lcn {&lt;= 79} genre Film action {move Children/Films}",
"name": "Move any Children's films (by genre)",
"type": "file",
"enabled": "0",
"criteria": [
{
"cmd": "lcn",
"arg": "&gt;= 70"
},
{
"cmd": "lcn",
"arg": "&lt;= 79"
},
{
"cmd": "genre",
"arg": "Film"
}
],
"action": {
"cmd": "move",
"arg": "Children/Films"
}
},
{
"raw": "lcn {&gt;= 70} lcn {&lt;= 79} action {move Children/Miscellaneous}",
"name": "Move anything else recorded from a children's channel",
"type": "file",
"enabled": "0",
"criteria": [
{
"cmd": "lcn",
"arg": "&gt;= 70"
},
{
"cmd": "lcn",
"arg": "&lt;= 79"
}
],
"action": {
"cmd": "move",
"arg": "Children/Miscellaneous"
}
},
{
"raw": "lcn {&gt;= 70} lcn {&lt;= 79} action {fileundercreate Children}",
"name": "Move any series recordings from a Children's channel (folder rule)",
"type": "folder",
"enabled": "0",
"criteria": [
{
"cmd": "lcn",
"arg": "&gt;= 70"
},
{
"cmd": "lcn",
"arg": "&lt;= 79"
}
],
"action": {
"cmd": "fileundercreate",
"arg": "Children"
}
},
{
"raw": "title {Formula 1} action {move F1}",
"name": "Move any one-off Formula 1 recordings into the F1 folder",
"type": "file",
"enabled": "0",
"criteria": [
{
"cmd": "title",
"arg": "Formula 1"
}
],
"action": {
"cmd": "move",
"arg": "F1"
}
},
{
"raw": "age {&gt; 120} action {movecreate Misc}",
"name": "Move any one-off recordings into a folder called Misc after a while",
"type": "file",
"enabled": "0",
"criteria": [
{
"cmd": "age",
"arg": "&gt; 120"
}
],
"action": {
"cmd": "movecreate",
"arg": "Misc"
}
}
]
}
};
var select_file_criteria = {};
var select_folder_criteria = {};
$.each(schema.criterion, function(k, v) { $.each(schema.criterion, function(k, v) {
select_criteria[k] = v.desc; if (v.deprecated)
return;
switch (v['class'])
{
case 'file':
select_file_criteria[k] = v.desc;
break;
case 'folder':
select_folder_criteria[k] = v.desc;
break;
case 'all':
select_file_criteria[k] = v.desc;
select_folder_criteria[k] = v.desc;
break;
}
}); });
var select_file_actions = {}; var select_file_actions = {};
var select_folder_actions = {}; var select_folder_actions = {};
$.each(schema.action, function(k, v) { $.each(schema.action, function(k, v) {
if (v.deprecated)
return;
switch (v['class']) switch (v['class'])
{ {
case 'file': case 'file':

View File

@ -6,7 +6,7 @@ function quot(str)
if (!str) if (!str)
return '""'; return '""';
if (str.indexOf(" ") == -1) if (str.indexOf(" ") == -1 && str.indexOf('"') == -1)
return str; return str;
return '{' + str + '}'; return '{' + str + '}';
} }
@ -15,6 +15,11 @@ function fixupdown()
{ {
$('#ruleset a.uprule').enable().filter(':first').disable(); $('#ruleset a.uprule').enable().filter(':first').disable();
$('#ruleset a.downrule').enable().filter(':last').disable(); $('#ruleset a.downrule').enable().filter(':last').disable();
$('#ruleset a.uprule img').attr('src', '/img/nav/up.png')
.first().attr('src', '/img/nav/up-grey.png');
$('#ruleset a.downrule img').attr('src', '/img/nav/down.png')
.last().attr('src', '/img/nav/down-grey.png');
} }
function changed(c) function changed(c)
@ -23,12 +28,12 @@ function changed(c)
return; return;
if (c) if (c)
{ {
$('#b_save,#b_revert').enable(); $('#b_save,#b_revert').button('enable');
$('#pendingnote').show(); $('#pendingnote').show();
} }
else else
{ {
$('#b_save,#b_revert').disable(); $('#b_save,#b_revert').button('disable');
$('#pendingnote').hide(); $('#pendingnote').hide();
} }
changed.last = c; changed.last = c;
@ -52,6 +57,9 @@ var setters = {
substr: function(cmd, a) { substr: function(cmd, a) {
return a; return a;
}, },
'string': function(cmd, a) {
return a;
},
select: function(cmd, a) { select: function(cmd, a) {
return schema.criterion[cmd].select[a]; return schema.criterion[cmd].select[a];
} }
@ -70,6 +78,9 @@ var getters = {
substr: function(cmd, a) { substr: function(cmd, a) {
return a; return a;
}, },
'string': function(cmd, a) {
return a;
},
select: function(cmd, a) { select: function(cmd, a) {
var c = schema.criterion[cmd].select; var c = schema.criterion[cmd].select;
for (key in c) for (key in c)
@ -83,11 +94,13 @@ function ruleconf(rule)
{ {
var s = ''; var s = '';
if ($(rule).attr('type') == 'folder') if (rule.hasClass('ruledisabled'))
s += '## ';
if (rule.attr('type') == 'folder')
s += 'folder '; s += 'folder ';
$(rule).find('tr.clause').each(function(i) { rule.find('tr.clause').each(function(i) {
cmd = $(this).find('th').attr('cmd'); cmd = $(this).find('th.cmd').attr('cmd');
c = schema.criterion[cmd]; c = schema.criterion[cmd];
val = $.trim($(this).find('td.val').text()); val = $.trim($(this).find('td.val').text());
if (getters[c.type]) if (getters[c.type])
@ -96,12 +109,17 @@ function ruleconf(rule)
s += cmd + ' ' + quot(val) + ' '; s += cmd + ' ' + quot(val) + ' ';
}); });
act = $(rule).find('tr.action th').attr('cmd'); act = rule.find('tr.action th.cmd').attr('cmd');
arg = $.trim($(rule).find('tr.action td.val').text()); arg = $.trim(rule.find('tr.action td.val').text());
if (schema.action[act].argtype == 'folder' && arg == mroot) if (schema.action[act].argtype == 'folder' && arg == mroot)
arg = ''; arg = '';
if (act != 'continue') if (act != 'continue')
s += 'action {' + act + ' ' + quot(arg) + '}'; {
if (schema.action[act].argtype == 'none')
s += 'action ' + act;
else
s += 'action {' + act + ' ' + quot(arg) + '}';
}
return s; return s;
} }
@ -120,12 +138,22 @@ function conffile()
function rulerefresh(rule) function rulerefresh(rule)
{ {
if (!showraw)
rule.find('.raw').hide();
rule.find('.raw').html(ruleconf(rule)); rule.find('.raw').html(ruleconf(rule));
rule.find('tr.clause td.title').text('And:').first().text('If:'); rule.find('tr.clause th.title').text('And:').first().text('If:');
if (rule.find('tr.clause').length < 1) if (rule.find('tr.clause').length < 1)
rule.find('tr.action td.title').text('Always:'); {
rule.find('tr.action th.title').text('Always:');
rule.find('tr.otherwise').hide();
rule.find('div.criteria,div.arrow').hide();
}
else else
rule.find('tr.action td.title').text('Then:'); {
rule.find('tr.action th.title').text('Then:');
rule.find('tr.otherwise').show();
rule.find('div.criteria,div.arrow').show();
}
} }
function criterion(data) function criterion(data)
@ -133,8 +161,9 @@ function criterion(data)
var c = schema.criterion[data.cmd]; var c = schema.criterion[data.cmd];
var s; var s;
s = '<tr class=clause><td class=title>And:</td>' + s = '<tr class=clause><th class=title>And:</th>' +
'<th cmd=' + data.cmd + '>' + c.desc + '</th><td class=val>'; '<th class=cmd cmd=' + data.cmd + '>' +
c.desc + '</th><td class=val>';
if (setters[c.type]) if (setters[c.type])
s += setters[c.type](data.cmd, data.arg); s += setters[c.type](data.cmd, data.arg);
@ -160,16 +189,26 @@ function action(data)
data.arg = mroot; data.arg = mroot;
s = '<tr class=action>' + s = '<tr class=action>' +
'<td class=title>Then:</td>' + '<th class=title>Then:</th>' +
'<th cmd=' + data.cmd + '>' + c.desc + '</th>' + '<th class=cmd cmd=' + data.cmd + '>' + c.desc + '</th>' +
'<td class=val>' + data.arg + '</td>' + '<td class=val>' + data.arg + '</td>' +
'<td><a class=editaction href=#>' + '<td><a class=editaction href=#>' +
'<img src=/img/context/edit.png></a></td>' + '<img src=/img/context/edit.png></a></td>' +
'</tr>'; '</tr>';
if (c.showcont) if (data.cmd != 'continue')
s += '<tr><td class=title>Else:</td>' + {
'<th>Continue on to next rule.</th></tr>'; if (c.continues)
s += '<tr><th class=title>And:</th><td class=title>' +
'Continue to next rule.</td></tr>';
else if (data.cmd != 'stop')
s += '<tr><th class=title>And:</th><td class=title>' +
'Stop processing rules.</td></tr>';
s += '<tr class=blank><td>&nbsp;</th></tr>' +
'<tr class=otherwise><th class=title>Otherwise:</th>' +
'<td class=title>Continue to next rule.</td></tr>';
}
return s; return s;
} }
@ -196,16 +235,25 @@ function addrule(id, data)
var $c = rule.find('table.criteria'); var $c = rule.find('table.criteria');
$.each(data.criteria, function(key, val) { $.each(data.criteria, function(key, val) {
if (val.cmd == 'lock' && (
data.action.cmd == 'lock' || data.action.cmd == 'unlock'))
return;
$c.find('tbody').append(criterion(val)); $c.find('tbody').append(criterion(val));
}); });
$c.find('td.title:first').text('If:'); $c.find('th.title:first').text('If:');
rule.find('table.action tbody').append(action(data.action)); rule.find('table.action tbody').append(action(data.action));
if (data.enabled == 0)
{
rule.addClass('ruledisabled');
rule.find('span.disabledtext').removeClass('hidden');
}
$('#ruleset').append(rule); $('#ruleset').append(rule);
rule.find('button.addcriterion') // rule.find('button.addcriterion')
.button({icons: { primary: "ui-icon-plus"}}); // .button({icons: { primary: "ui-icon-plus"}});
rulerefresh(rule); rulerefresh(rule);
@ -241,6 +289,8 @@ function edit_text(obj, title, text, callback)
} }
} }
}); });
if (text == 'Enter text here...')
$('#edit_text_field').focus().select();
} }
function edit_int(obj, title, op, val, callback) function edit_int(obj, title, op, val, callback)
@ -310,10 +360,10 @@ function edit_select(obj, title, options, val, callback)
function edit_clause(obj) function edit_clause(obj)
{ {
var cmd = $(obj).find('th').attr('cmd'); var cmd = $(obj).find('th.cmd').attr('cmd');
var target = $(obj).find('td.val'); var target = $(obj).find('td.val');
var val = $.trim($(target).text()); var val = $.trim($(target).text());
var title = $(obj).find('th').html(); var title = $(obj).find('th.cmd').html();
if (!schema.criterion[cmd]) if (!schema.criterion[cmd])
{ {
alert('Unhandled command (' + cmd + ')'); alert('Unhandled command (' + cmd + ')');
@ -364,7 +414,7 @@ function loadrules(dir)
if (!data.length) if (!data.length)
{ {
$('#ruleset').attr('empty', true) $('#ruleset').attr('empty', true)
.html('<i>No rules defined for this folder.</i>'); .html($('#empty_rulebase').html());
return; return;
} }
$.each(data, function(key, val) { $.each(data, function(key, val) {
@ -412,18 +462,18 @@ $('#b_add').button({icons: {primary: "ui-icon-plus"}})
} }
}); });
$('#newrule_type_file').prop('checked', true); $('#newrule_type_file').prop('checked', true);
$('#newrule_comment').focus().val('').val('Unnamed rule'); $('#newrule_comment').focus().val('').val('Unnamed rule').select();
}); });
$('#b_save').button({icons: {primary: "ui-icon-disk"}}) $('#b_save').button({icons: {primary: "ui-icon-disk"}});
.on('click', function(e) { $('#buttons')
.on('click', '#b_save', function(e) {
e.preventDefault(); e.preventDefault();
$(this).dojConfirmAction({ $(this).dojConfirmAction({
question: 'Save changes?', question: 'Save changes?',
yesAnswer: 'Yes', yesAnswer: 'Yes',
cancelAnswer: 'No' cancelAnswer: 'No'
}, function(el) { }, function(el) {
$('.jcaquestion').fadeOut(300);
$.post('save.jim', { $.post('save.jim', {
dir: $('span.dir').text(), dir: $('span.dir').text(),
data: conffile() data: conffile()
@ -435,15 +485,15 @@ $('#b_save').button({icons: {primary: "ui-icon-disk"}})
}); });
}); });
$('#b_revert').button({icons: {primary: "ui-icon-arrowrefresh-1-w"}}) $('#b_revert').button({icons: {primary: "ui-icon-arrowrefresh-1-w"}});
.on('click', function(e) { $('#buttons')
.on('click', '#b_revert', function(e) {
e.preventDefault(); e.preventDefault();
$(this).dojConfirmAction({ $(this).dojConfirmAction({
question: 'Discard changes?', question: 'Discard changes?',
yesAnswer: 'Yes', yesAnswer: 'Yes',
cancelAnswer: 'No' cancelAnswer: 'No'
}, function(el) { }, function(el) {
$('.jcaquestion').fadeOut(300);
loadrules($('span.dir').text()); loadrules($('span.dir').text());
changed(0); changed(0);
}); });
@ -470,6 +520,32 @@ $('#b_show').button({icons: {primary: "ui-icon-clipboard"}})
$('#b_raw').button({icons: {primary: "ui-icon-gear"}}) $('#b_raw').button({icons: {primary: "ui-icon-gear"}})
.on('click', function(e) { .on('click', function(e) {
$('#ruleset div.raw').toggle('slow'); $('#ruleset div.raw').toggle('slow');
showraw = !showraw;
$.get('save.jim?act=raw&val=' + (showraw ? 1 : 0));
});
$('#b_macro').button({disabled: true, icons: {primary: "ui-icon-plus"}});
$('#macros').on('click', '#b_macro', function(e) {
e.preventDefault();
var set = $('#macroselect').val();
if (!set) return;
var desc = macros[set].desc;
$(this).dojConfirmAction({
question: 'Add ' + desc + '?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$('.jcaquestion').remove();
$.each(macros[set].rules, function(k, v) {
addrule(last_ruleid + 1, v).hide().addClass('hl');
});
$('.hl').slideDown('slow', function() {
$(this).removeClass('hl');
});
fixupdown();
changed(1);
$('#macroselect').val(0);
});
}); });
changed(0); changed(0);
@ -479,11 +555,17 @@ $('#ruleset')
e.preventDefault(); e.preventDefault();
edit_clause($(this).closest('tr.clause')); edit_clause($(this).closest('tr.clause'));
}) })
.on('click', 'button.addcriterion', function(e) { .on('click', 'a.addcriterion', function(e) {
e.preventDefault(); e.preventDefault();
var rule = $(this).closest('div.rule'); var rule = $(this).closest('div.rule');
edit_select(rule, 'Select new criterion', var type = rule.attr('type');
select_criteria, '', function(rule, val) {
if (type == 'folder')
options = select_folder_criteria;
else
options = select_file_criteria;
edit_select(rule, 'Add new condition',
options, '', function(rule, val) {
var id = rule.attr('id'); var id = rule.attr('id');
rule.find('table.criteria tbody') rule.find('table.criteria tbody')
.append(criterion({ .append(criterion({
@ -537,6 +619,35 @@ $('#ruleset')
}); });
}); });
}) })
.on('click', 'a.cprule', function(e) {
e.preventDefault();
$(this).dojConfirmAction({
question: 'Duplicate rule?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$('.jcaquestion').remove();
var rule = $(el).closest('div.rule');
rule.clone()
.attr('id', 'rule_' + ++last_ruleid)
.addClass('hl').hide()
.insertAfter(rule)
.slideDown('slow', function() {
$(this).removeClass('hl');
changed(1);
})
.find('span.comment').append(' (copy)');
});
})
.on('click', 'a.enadisrule', function(e) {
e.preventDefault();
var rule = $(this).closest('div.rule')
.toggleClass('ruledisabled');
rule.find('span.disabledtext').toggleClass('hidden',
!rule.hasClass('ruledisabled'));
rulerefresh(rule);
changed(1);
})
.on('click', 'a.editaction', function(e) { .on('click', 'a.editaction', function(e) {
e.preventDefault(); e.preventDefault();
@ -555,7 +666,7 @@ $('#ruleset')
); );
}); });
var cmd = rule.find('tr.action th').attr('cmd'); var cmd = rule.find('tr.action th.cmd').attr('cmd');
var arg = rule.find('tr.action td.val').html(); var arg = rule.find('tr.action td.val').html();
if (schema.action[cmd].argtype == 'none') if (schema.action[cmd].argtype == 'none')
@ -596,11 +707,10 @@ $('#ruleset')
.on('click', 'a.delclause', function(e) { .on('click', 'a.delclause', function(e) {
e.preventDefault(); e.preventDefault();
$(this).dojConfirmAction({ $(this).dojConfirmAction({
question: 'Delete criterion?', question: 'Delete condition?',
yesAnswer: 'Yes', yesAnswer: 'Yes',
cancelAnswer: 'No' cancelAnswer: 'No'
}, function(el) { }, function(el) {
$('.jcaquestion').fadeOut(300);
var rule = $(el).closest('div.rule'); var rule = $(el).closest('div.rule');
$(el).closest('tr.clause').fadeOut('slow', function() { $(el).closest('tr.clause').fadeOut('slow', function() {
$(this).remove(); $(this).remove();
@ -616,19 +726,33 @@ $('#ruleset')
yesAnswer: 'Yes', yesAnswer: 'Yes',
cancelAnswer: 'No' cancelAnswer: 'No'
}, function(el) { }, function(el) {
$('.jcaquestion').fadeOut(300); $(el).closest('div.rule')
$(el).closest('div.rule').slideUp('slow', function() { .addClass('hl')
.slideUp('slow', function() {
$(this).remove(); $(this).remove();
changed(1); changed(1);
fixupdown(); fixupdown();
if ($('div.rule').length < 1) if ($('div.rule').length < 1)
$('#ruleset').attr('empty', true) $('#ruleset').attr('empty', true)
.html('<i>No rules defined for this ' + .html($('#empty_rulebase').html());
'folder.</i>');
}); });
}); });
}); });
// Set up macros
$.each(macros, function(key, val) {
$('#macroselect').append(
$('<option></option>').attr('value', key).text(val.desc)
);
});
$('#macroselect').on('change', function(el) {
if ($(this).val() == "0")
$('#b_macro').button('disable');
else
$('#b_macro').button('enable');
});
loadrules($('span.dir').text()); loadrules($('span.dir').text());
}); });

View File

@ -1,4 +1,9 @@
a
{
outline: 0;
}
span.ruleicon img span.ruleicon img
{ {
height: 21px; height: 21px;
@ -20,15 +25,30 @@ img
background: yellow; background: yellow;
} }
.ruledisabled
{
background: #663436 url(img/redstripes.gif) repeat;
}
div.action div.action
{ {
padding-left: 1em;
float: left; float: left;
} }
a.addcriterion
{
padding: 4px 2px 0 0;
float: left;
clear: left;
}
div.criteria div.criteria
{ {
clear: left; float: left;
}
div.arrow
{
float: left; float: left;
} }
@ -47,11 +67,9 @@ div#buttons
padding-top: 1em; padding-top: 1em;
} }
td.title,th.title div#macros
{ {
line-height: 1em; padding-top: 1em;
text-align: left !important;
background: #eee !important;
} }
div.rule div.rule
@ -86,3 +104,47 @@ span.legendright
background: transparent; background: transparent;
} }
table
{
border: 0;
}
table th
{
background: #ccff99;
font-weight: bold;
text-align: right;
color: black;
xpadding: 0.5em;
}
table td
{
background: #ffffcc;
color: black;
}
td.title,th.title
{
line-height: 1em;
background: #eee !important;
font-style: italic;
}
td.title
{
text-align: left !important;
}
th.title
{
font-weight: normal;
text-align: right !important;
}
tr.blank td
{
background: transparent;
line-height: 7px;
}