Code

Adding missing files from last commit (r4575) for fixing expressing filters
authorber <ber@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 9 Nov 2010 15:06:14 +0000 (15:06 +0000)
committerber <ber@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 9 Nov 2010 15:06:14 +0000 (15:06 +0000)
for multilinkgs, see issue2550648.

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4576 57a73879-2fb5-44c3-a270-3262357dd7e2

roundup/cgi/KeywordsExpr.py [new file with mode: 0644]
share/roundup/templates/classic/html/_generic.keywords_expr.html [new file with mode: 0644]

diff --git a/roundup/cgi/KeywordsExpr.py b/roundup/cgi/KeywordsExpr.py
new file mode 100644 (file)
index 0000000..a94bf7c
--- /dev/null
@@ -0,0 +1,273 @@
+# This module is free software, you may redistribute it
+# and/or modify under the same terms as Python.
+
+WINDOW_CONTENT = '''\
+<h3>Keyword Expression Editor:</h3>
+<hr/>
+<div id="content"></div>
+<script type="text/javascript">
+<!--
+
+var NOT_OP = "-2";
+var AND_OP = "-3";
+var OR_OP  = "-4";
+
+var original = "%(original)s";
+var current = original;
+var undo = [];
+
+var KEYWORDS = [
+    %(keywords)s
+];
+
+function find_keyword(x) {
+    for (var i = 0; i < KEYWORDS.length; ++i) {
+        if (KEYWORDS[i][0] == x) {
+            return KEYWORDS[i][1];
+        }
+    }
+    return "unknown";
+}
+
+function Equals(x) {
+    this.x = x;
+    this.brackets = false;
+
+    this.infix = function() {
+        return find_keyword(this.x);
+    }
+
+    this.postfix = function() {
+        return this.x;
+    }
+}
+
+function Not(x) {
+    this.x = x;
+    this.brackets = false;
+
+    this.infix = function() {
+        return this.x.brackets 
+            ? "NOT(" + this.x.infix() + ")"
+            : "NOT " + this.x.infix();
+    }
+
+    this.postfix = function() {
+        return this.x.postfix() + "," + NOT_OP;
+    }
+}
+
+function And(x, y) {
+    this.x = x;
+    this.y = y;
+    this.brackets = true;
+
+    this.infix = function() {
+        var a = this.x.brackets ? "(" + this.x.infix() + ")" : this.x.infix();
+        var b = this.y.brackets ? "(" + this.y.infix() + ")" : this.y.infix();
+        return a + " AND " + b;
+    }
+    this.postfix = function() {
+        return this.x.postfix() + "," + this.y.postfix() + "," + AND_OP;
+    }
+}
+
+function Or(x, y) {
+    this.x = x;
+    this.y = y;
+    this.brackets = true;
+
+    this.infix = function() {
+        var a = this.x.brackets ? "(" + this.x.infix() + ")" : this.x.infix();
+        var b = this.y.brackets ? "(" + this.y.infix() + ")" : this.y.infix();
+        return a + " OR " + b;
+    }
+
+    this.postfix = function() {
+        return this.x.postfix() + "," + this.y.postfix() + "," + OR_OP;
+    }
+}
+
+function trim(s) {
+    return s.replace (/^\s+/, '').replace(/\s+$/, '');
+}
+
+function parse(s) {
+    var operators = s.split(",");
+    var stack = [];
+    for (var i = 0; i < operators.length; ++i) {
+        var operator = trim(operators[i]);
+        if (operator == "") continue;
+        if (operator == NOT_OP) {
+            stack.push(new Not(stack.pop()));
+        }
+        else if (operator == AND_OP) {
+            var a = stack.pop();
+            var b = stack.pop();
+            stack.push(new And(b, a));
+        }
+        else if (operator == OR_OP) {
+            var a = stack.pop();
+            var b = stack.pop();
+            stack.push(new Or(b, a));
+        }
+        else {
+            stack.push(new Equals(operator));
+        }
+    }
+    return stack.length > 0 ? stack.pop() : null;
+}
+
+function render_select(handler) {
+    var out = '<select name="keyword" id="keyword"';
+    if (handler != null) {
+        out += ' onchange="' + handler + '"';
+    }
+    out += '>';
+    out += '<option value="-1"><\/option>';
+    for (var i = 0; i < KEYWORDS.length; ++i) {
+        out += '<option value="' + KEYWORDS[i][0] + 
+               '">' + KEYWORDS[i][1] + "<\/option>";
+    }
+    out += '<\/select>';
+    return out;
+}
+
+function first_select() {
+    var value = document.getElementById("keyword").value;
+    current = value;
+    set_content();
+}
+
+function not_clicked() {
+    var expr = parse(current);
+    if (expr == null) return;
+    undo.push(current);
+    current = expr instanceof Not
+        ? expr.x.postfix()
+        : new Not(expr).postfix();
+    set_content();
+}
+
+function not_b_wrap(expr) {
+    var value = document.getElementById("not_b").checked;
+    return value ? new Not(expr) : expr;
+}
+
+function and_clicked() {
+    var expr = parse(current);
+    if (expr == null) return;
+    var value = document.getElementById("keyword").value;
+    if (value == "-1") return;
+    undo.push(current);
+    current = new And(expr, not_b_wrap(new Equals(value))).postfix();
+    set_content();
+}
+
+function or_clicked() {
+    var expr = parse(current);
+    if (expr == null) return;
+    var value = document.getElementById("keyword").value;
+    if (value == "-1") return;
+    undo.push(current);
+    current = new Or(expr, not_b_wrap(new Equals(value))).postfix();
+    set_content();
+}
+
+function undo_clicked() {
+    current = undo.length > 0 
+        ? undo.pop()
+        : original;
+    set_content();
+}
+
+function enable_and_or() {
+    var value = document.getElementById("keyword").value;
+    value = value == "-1";
+    document.getElementById("and").disabled = value;
+    document.getElementById("or").disabled = value;
+    document.getElementById("not_b").disabled = value;
+}
+
+function create() {
+    var expr = parse(current);
+    var out = "";
+    if (expr == null) {
+        out += "Keyword: ";
+        out += render_select("first_select();");
+    }
+    else {
+        out += '<table><tr>'
+        out += '<td><input type="button" name="not" onclick="not_clicked();" value="NOT"\/><\/td>';
+        out += "<td><tt><strong>" + expr.infix() + "<\/strong><\/tt><\/td>";
+        out += '<td><table>';
+        out += '<tr><td><input type="button" id="and" name="and" onclick="and_clicked();"'
+            +  ' value="AND" disabled="disabled"\/><\/td><\/tr>';
+        out += '<tr><td><input type="button" id="or" name="or" onclick="or_clicked();"'
+            +  ' value="OR" disabled="disabled"\/><\/td><\/tr>';
+        out += '<\/table><\/td>';
+        out += '<td><label for="not_b">NOT<\/label><br/>'
+            +  '<input type="checkbox" name="not_b" id="not_b" disabled="disabled"\/><\/td>';
+        out += '<td>' + render_select("enable_and_or();") + '<\/td>';
+        out += '<\/tr><\/table>'
+    }
+    out += '<hr\/>';
+    if (undo.length > 0 || (undo.length == 0 && current != original)) {
+        out += '<input type="button" onclick="undo_clicked();" value="Undo"\/>';
+    }
+    out += '<input type="button" onclick="modify_main();" value="Apply"\/>'
+        +  '<input type="button" onclick="window.close();" value="Close Window"\/>';
+    return out;
+}
+
+function main_content() {
+    var out = '';
+    out += '<input type="hidden" name="%(prop)s" value="' + current + '"\/>';
+    out += parse(current).infix();
+    return out;
+}
+
+function modify_main() {
+    main = window.opener.document.getElementById("keywords_%(prop)s");
+    main.innerHTML = main_content();
+}
+
+function set_content() {
+    document.getElementById("content").innerHTML = create();
+}
+
+set_content();
+//-->
+</script>
+'''
+
+def list_nodes(request):
+    prop = request.form.getfirst("property")
+    cls = request.client.db.getclass(prop)
+    items = []
+    for nodeid in cls.getnodeids():
+        l = cls.getnode(nodeid).items()
+        l = dict([x for x in l if len(x) == 2])
+        try:
+            items.append((l['id'], l['name']))
+        except KeyError:
+            pass
+    items.sort(key=lambda x: int(x[0]))
+    return items
+
+def items_to_keywords(items):
+    return ',\n    '.join(['["%s", "%s"]' % x for x in items])
+   
+
+def render_keywords_expression_editor(request):
+    prop = request.form.getfirst("property")
+
+    window_content = WINDOW_CONTENT % {
+        'prop'    : prop,
+        'keywords': items_to_keywords(list_nodes(request)),
+        'original': ''
+    }
+
+    return window_content
+
+# vim: set et sts=4 sw=4 :
diff --git a/share/roundup/templates/classic/html/_generic.keywords_expr.html b/share/roundup/templates/classic/html/_generic.keywords_expr.html
new file mode 100644 (file)
index 0000000..4ff1dc2
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <head>
+  <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8;" />
+  <title tal:content="string:Roundup Keywords Expression Editor"></title>
+ </head>
+ <body class="body"
+       tal:content="structure python:utils.keywords_expressions(request)">
+ </body>
+</html>