예제 #1
0
def execute(macro, args):
    request = macro.request
    _ = request.getText

    if args:
        args = [x.strip() for x in args.split(',')]
    # Wrong number of arguments
    if not args or len(args) not in [1, 2]:
        return render_error( _("GetMetaData: Need to specify page, or page and key"))

    # Get all non-empty args
    args = [x for x in args if x]

    # If not page specified, defaulting to current page
    if len(args) == 1:
        page = request.page.page_name
        key = args[0]
    elif len(args) == 2:
        page = args[0]
        key = args[1]
    # Faulty args
    else:
        return render_error(_("GetMetaData: Need to specify page, or page and key"))

    vals = get_metas(request, page, [key], formatLinks=True)

    val = ', '.join(vals[key])

    return format_wikitext(request, val)
예제 #2
0
def execute(macro, args):
    request = macro.request
    _ = request.getText

    if args:
        args = [x.strip() for x in args.split(',')]
    # Wrong number of arguments
    if not args or len(args) not in [1, 2]:
        return render_error(
            _("GetMetaData: Need to specify page, or page and key"))

    # Get all non-empty args
    args = [x for x in args if x]

    # If not page specified, defaulting to current page
    if len(args) == 1:
        page = request.page.page_name
        key = args[0]
    elif len(args) == 2:
        page = args[0]
        key = args[1]
    # Faulty args
    else:
        return render_error(
            _("GetMetaData: Need to specify page, or page and key"))

    vals = get_metas(request, page, [key], formatLinks=True)

    val = ', '.join(vals[key])

    return format_wikitext(request, val)
예제 #3
0
def execute(macro, args):
    tset = set(['score', 'vector'])
    request = macro.request
    _ = request.getText

    if args:
        args = [x.strip() for x in args.split(',')]
    # Wrong number of arguments
    if len(args) not in [0, 1, 2]:
        return render_error(
            _("CVSS: Need to specify a page or page and " +
              "type (score|vector)."))

    # Get all non-empty args
    args = [x for x in args if x]

    # If page is not specified, defaulting to current page
    if len(args) == 0:
        page = request.page.page_name
        type = "score"
    elif len(args) == 1:
        page = args[0]
        type = "score"
    else:
        page = args[0]
        type = args[1]
        if type not in tset:
            return render_error(
                _("CVSS: The type needs to be either score " + "or vector."))

    base_metas = get_metas(request, page, [
        "Access Vector", "Access Complexity", "Authentication",
        "Confidentiality", "Integrity", "Availability"
    ])
    vector = buildVector(base_metas)
    if vector is not None:
        if type == "vector":
            return format_wikitext(request, vector)
        cvss = parse_cvss(vector)
        bscore = basescore(cvss)
        bstring = "%s" % bscore
        return format_wikitext(request, bstring)
    else:
        return render_error(_("CVSS: Invalid value(s) in Base Metrics."))
예제 #4
0
def execute(macro, args):
    tset = set(['score', 'vector'])
    request = macro.request
    _ = request.getText

    if args:
        args = [x.strip() for x in args.split(',')]
    # Wrong number of arguments
    if len(args) not in [0, 1, 2]:
        return render_error(_("CVSS: Need to specify a page or page and " +
                              "type (score|vector)."))

    # Get all non-empty args
    args = [x for x in args if x]

    # If page is not specified, defaulting to current page
    if len(args) == 0:
        page = request.page.page_name
        type = "score"
    elif len(args) == 1:
        page = args[0]
        type = "score"
    else:
        page = args[0]
        type = args[1]
        if type not in tset:
            return render_error(_("CVSS: The type needs to be either score " +
                                  "or vector."))

    base_metas = get_metas(request, page, ["Access Vector", "Access Complexity", "Authentication", "Confidentiality", "Integrity", "Availability"])
    vector = buildVector(base_metas)
    if vector is not None:
        if type == "vector":
            return format_wikitext(request, vector)
        cvss = parse_cvss(vector)
        bscore = basescore(cvss)
        bstring = "%s" % bscore
        return format_wikitext(request, bstring)
    else:
        return render_error(_("CVSS: Invalid value(s) in Base Metrics."))
예제 #5
0
def show_editform(wr, request, pagename, args):
    formatter = request.formatter

    # Note that metatable_parseargs handles read permission checks
    pagelist, metakeys, _ = metatable_parseargs(request, args,
                                                get_all_keys=True)

    uneditable_pages = list()
    # See that the user can write each page
    for page in pagelist:
        if not request.user.may.write(page):
            uneditable_pages.append(page)
    for page in uneditable_pages:
        pagelist.remove(page)

    _ = request.getText

    if uneditable_pages:
        reason = _("No save permission to some pages (%s)" %
                   ','.join(uneditable_pages))
        request.write(render_warning(reason))

    if not pagelist:
        reason = _("No pages to edit.")
        request.write(render_error(reason))
        return

    wr(u'<form method="POST" action="%s" enctype="multipart/form-data">\n',
       actionname(request))
    wr(u'<input type="hidden" name="action" value="%s">\n', action_name)
    wr(formatter.table(1))
    wr(formatter.table_row(1, {'rowclass': 'meta_header'}))
    wr(formatter.table_cell(1, {'class': 'meta_page'}))

    form = values_to_form(request.values)

    template = form.get('template', [''])[0]
    if template:
        wr('<input type="hidden" name="template" value="%s">', template)

    # Filter out uneditables, such as inlinks
    metakeys = editable_p(metakeys)

    _ = request.getText

    for key in metakeys + ['']:
        wr(formatter.table_cell(1, {'class': 'meta_header'}))
        wr(u'<input class="metakey" type="text" name="%s" value="%s">',
           u':: %s' % key, key)
    wr(formatter.table_row(0))

    values = dict()
    valnos = dict()

    for frompage in pagelist:
        values[frompage] = dict()

        for key in metakeys + ['']:
            values[frompage][key] = list()
            # If the page has no values for this key, it'll
            # have one in the table to add a value
            if not valnos.has_key(frompage):
                valnos[frompage] = 1

            keydata = get_metas(request, frompage, [key],
                                abs_attach=False, includeGenerated=False)

            for i, val in enumerate(keydata[key]):
                values[frompage][key].append(val)
                # Enumerate starts from 0: #values++
                # One to add a value: #values++
                if valnos[frompage] < i + 2:
                    valnos[frompage] = i + 2

            values[frompage][key].append('')


    for frompage in pagelist:
        wr(formatter.table_row(1))
        wr(formatter.table_cell(1, {'class': 'meta_page',
                                    'rowspan': str(valnos[frompage])}))
        wr(u'%s', frompage)

        for i in range(valnos[frompage]):
            # Add <tr>:s for additional values also
            if i > 0:
                wr(formatter.table_row(1))

            for key in metakeys + ['']:
                inputname = frompage + SEPARATOR + key

                if len(values[frompage][key]) >= (i + 1):
                    val = values[frompage][key][i]
                else:
                    val = ''

                # Skip default labels
                if key == 'label' and val == frompage:
                    val = ''

                wr(formatter.table_cell(1, {'class': 'meta_cell'}))
                wr(u'<textarea class="metavalue dynamic"  rows="1" name="%s">%s</textarea>',
                   inputname, val)

                #print frompage, key, inputname, values, '<br>'
            wr(formatter.table_row(0))

        wr(formatter.table_row(1))
        wr(formatter.table_cell(1, {'class': 'meta_cell'}))
        for key in metakeys + ['']:
            inputname = frompage + SEPARATOR + key

            if len(values[frompage][key]) >= (i + 1):
                val = values[frompage][key][i]
            else:
                val = ''

            # Skip default labels
            if key == 'label' and val == frompage:
                val = ''

            wr(formatter.table_cell(1))
            wr(u'<input class="metavalue" type="file" name="%s">\n', inputname)

        wr(formatter.table_row(0))

# Proto JS code to warn on leaving an empty key name
# <script language="JavaScript" type="text/javascript">
#    function myvalid(me) {
#      if (me.form.subject.value == "") {
#        if (confirm("Empty subject, send anyway?"))
#          return true;
#        else
#          return false;
#      }
#      return true;
#    }
# </script>
# <input type="submit" name="send" value="Send" class="button1"tabindex="7"
# onClick="return myvalid(this);" />

    wr(formatter.table(0))
    wr(u'<input type="submit" name="save" value="%s">\n', _('Save'))
    wr(u'<input type="submit" name="cancel" value="%s">\n', _('Cancel'))
    wr(u'</form>\n')
def draw_topology(request, args, key):
    args = [x.strip() for x in args.split(',')]

    topology, flowfile, color = '', '', ''
    rotate, width = '', ''
    graph = GraphShower(request.page.page_name, request)

    # take flow file specification from arguments as flow=k.csv,
    # otherwise assume that the argument specifies the topology
    for arg in args:
        if '=' in arg:
            key, val = [x.strip() for x in arg.split('=', 1)]
            if key == 'color':
                color = val
            if key == 'flow':
                flowfile = val
            if key == 'rotate':
                rotate = True
            if key == 'width':
                try:
                    width = float(val)
                except ValueError:
                    pass
        else:
            topology = arg

    _ = request.getText

    # Get all containers
    args = 'CategoryContainer, %s=/.+/' % (topology)

    # Note, metatable_parseargs deals with permissions
    pagelist, metakeys, styles = metatable_parseargs(request, args,
                                                     get_all_keys=True)

    if not pagelist:
        return (False, "", render_error("%s: %s" %
                (_("No such topology or empty topology"), topology)))

    coords = dict()
    images = dict()
    aliases = dict()
    areas = dict()
    colors = dict()

    # Make a context to calculate font sizes with
    # There must be a better way to do this, I just don't know it!
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)

    ctx = cairo.Context(surface)
    ctx.select_font_face("Times-Roman", cairo.FONT_SLANT_NORMAL,
                         cairo.FONT_WEIGHT_BOLD)
    ctx.set_font_size(12)

    allcoords = list()
    for page in pagelist:
        data = get_metas(request, page,
                         [topology, 'gwikishapefile', 'tia-name', color],
                         checkAccess=False, formatLinks=True)

        crds = [x.split(',') for x in data.get(topology, list)]

        if not crds:
            continue
        crds = [x.strip() for x in crds[0]]
        if not len(crds) == 2:
            continue

        try:
            start_x, start_y = int(crds[0]), int(crds[1])
        except ValueError:
            continue

        coords[page] = start_x, start_y
        allcoords.append((start_x, start_y))

        img = data.get('gwikishapefile', list())
        if color:
            clr = data.get(color, list())
            if clr:
                colors[page] = clr[0]

        alias = data.get('tia-name', list())
        # Newer versions of analyzer do not use aliases anymore
        if not alias:
            alias = [page]

        aliases[page] = alias[0]

        if img:
            # Get attachment name, name of the page it's on, strip
            # link artifacts, find filesys name
            img = img[0].split(':')[1]
            pname = '/'.join(img.split('/')[:-1])
            img = img.split('/')[-1]
            img = img.split('|')[0]
            img = img.rstrip('}').rstrip(']]')
            imgname = AttachFile.getFilename(request, pname, img)
            try:
                images[page] = cairo.ImageSurface.create_from_png(imgname)
                end_x = start_x + images[page].get_width()
                end_y = start_y + images[page].get_height()
            except cairo.Error:
                end_x = start_x
                end_y = start_y
                pass

        text_len = ctx.text_extents(aliases[page])[4]
        text_end = start_x + text_len
        if text_end > end_x:
            end_x = text_end

        # If there was no image or a problem with loading the image
        if page not in images:
            # Lack of image -> black 10x10 rectangle is drawn
            end_x, end_y = start_x + 10, start_y + 10

        allcoords.append((end_x, end_y))

    if flowfile:
        flowcoords = list()
        flowname = AttachFile.getFilename(request, topology, flowfile)
        try:
            flows = csv.reader(file(flowname, 'r').readlines(), delimiter=';')
        except IOError:
            return (False, "", render_error("%s: %s" %
                    (_("No such flowfile as attachment on topology page"),
                     flowfile)))

        flows.next()
        for line in flows:
            if not line:
                continue
            try:
                flowcoords.append((line[0], line[6]))
            except IndexError:
                # Pasted broken lines?
                pass

    max_x = max([x[0] for x in allcoords])
    min_x = min([x[0] for x in allcoords])
    max_y = max([x[1] for x in allcoords])
    min_y = min([x[1] for x in allcoords])

    # Make room for text under pictures
    if rotate:
        surface_y = max_y - min_y
        surface_x = max_x - min_x + 25
    else:
        surface_y = max_y - min_y + 25
        surface_x = max_x - min_x

    try:
        # Get background image, if any
        toponame = AttachFile.getFilename(request, topology, 'shapefile.png')
        background = cairo.ImageSurface.create_from_png(toponame)

        h = background.get_height()
        w = background.get_width()
        diff_x = w - surface_x
        diff_y = h - surface_y

        if diff_x > 0:
            surface_x = w
        else:
            diff_x = 0

        if diff_y > 0:
            surface_y = h
        else:
            diff_y = 0

    except cairo.Error:
        background = None
        diff_x = 0
        diff_y = 0
        pass

    # Setup Cairo
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
                                 int(surface_x), int(surface_y))

    # request.write(repr([surface_x, surface_y]))
    ctx = cairo.Context(surface)
    ctx.select_font_face("Times-Roman", cairo.FONT_SLANT_NORMAL,
                         cairo.FONT_WEIGHT_BOLD)
    ctx.set_font_size(12)

    ctx.set_source_rgb(1.0, 1.0, 1.0)
    ctx.rectangle(0, 0, surface_x, surface_y)
    ctx.fill()

    if background:
        # Center background if not large. Again, I'm just guessing
        # where the background image should be, and trying to mimic
        # analyzer.
        h = background.get_height()
        w = background.get_width()
        start_x, start_y = 0, 0
        if w < surface_x:
            start_x = start_x - min_x - w/2
        if h < surface_y:
            start_y = start_y - min_y - h/2
        ctx.set_source_surface(background, start_x, start_y)
        ctx.rectangle(start_x, start_y, w, h)
        ctx.fill()

    midcoords = dict()
    for page in pagelist:
        if page not in coords:
            continue

        x, y = coords[page]

        # FIXME need more data to align different backgrounds
        # correctly, this is just guessing
        start_x = x - min_x + (diff_x / 2)
        start_y = y - min_y + (diff_y / 3)

        w, h = 10, 10
        if page not in images:
            ctx.set_source_rgb(0, 0, 0)
        else:
            h = images[page].get_height()
            w = images[page].get_width()
            if page in colors:
                clr = graph.hashcolor(colors[page])
                r, g, b = [int(''.join(i), 16) / 255.0 for i in
                           zip(clr[1::2], clr[2::2])]
                ctx.set_source_rgb(r, g, b)
            else:
                ctx.set_source_rgb(1, 1, 1)

        midcoords[page] = (start_x + w / 2, start_y + h / 2)
        ctx.rectangle(start_x, start_y, w, h)
        ctx.fill()

        if page in images:
            ctx.set_source_surface(images[page], start_x, start_y)
            ctx.rectangle(start_x, start_y, w, h)
            ctx.fill()

        text = make_tooltip(request, page)

        areas["%s,%s,%s,%s" % (start_x, start_y, start_x + w, start_y + h)] = \
            [page, text, 'rect']

        if page in aliases:
            ctx.set_source_rgb(0, 0, 0)
            if rotate:
                ctx.move_to(start_x + w + 10, start_y + h)
            else:
                ctx.move_to(start_x, start_y + h + 10)

            # FIXME, should parse links more nicely, now just removes
            # square brackets
            text = aliases[page].lstrip('[').rstrip(']')

            if rotate:
                ctx.rotate(-90.0*math.pi/180.0)

            ctx.show_text(text)

            if rotate:
                ctx.rotate(90.0*math.pi/180.0)

    if flowfile:
        ctx.set_line_width(1)
        ctx.set_source_rgb(0, 0, 0)
        for start, end in flowcoords:
            if (start not in midcoords) or (end not in midcoords):
                continue

            sx, sy = midcoords[start]
            ex, ey = midcoords[end]
            ctx.move_to(sx, sy)
            ctx.line_to(ex, ey)
            ctx.stroke()

    s2 = surface

    if width:
        # For scaling
        new_surface_y = width
        factor = surface_y/new_surface_y
        new_surface_x = surface_x / factor

        # Recalculate image map data
        newareas = dict()
        for coords, data in areas.iteritems():
            corners = [float(i) for i in coords.split(',')]
            corners = tuple(i / factor for i in corners)
            newareas['%s,%s,%s,%s' % corners] = data
        areas = newareas
    else:
        new_surface_y = surface_y
        new_surface_x = surface_x

    if rotate:
        temp = new_surface_x
        new_surface_x = new_surface_y
        new_surface_y = temp
        temp = surface_x
        surface_x = surface_y
        surface_y = temp

    s2 = cairo.ImageSurface(cairo.FORMAT_ARGB32,
                            int(new_surface_x), int(new_surface_y))

    ctx = cairo.Context(s2)

    if rotate:
        ctx.rotate(90.0*math.pi/180.0)

        # Recalculate image map data
        newareas = dict()
        for coords, data in areas.iteritems():
            corners = coords.split(',')
            corners = [float(i) for i in coords.split(',')]
            corners = tuple([new_surface_x - corners[1], corners[0],
                             new_surface_x - corners[3], corners[2]])
            newareas['%s,%s,%s,%s' % corners] = data
        areas = newareas

    if width:
        ctx.scale(new_surface_x/surface_x, new_surface_y/surface_y)

    if rotate:
        ctx.translate(0, -surface_x)

    ctx.set_source_surface(surface, 0, 0)
    ctx.paint()

    data = cairo_surface_to_png(s2)

    map = ''
    for coords in areas:
        name, text, shape = areas[coords]
        pagelink = request.script_root + u'/' + name

        tooltip = "%s\n%s" % (name, text)

        map += u'<area href="%s" shape="%s" coords="%s" title="%s">\n' % \
            (form_escape(pagelink), shape, coords, tooltip)

    return True, data, map
예제 #7
0
def execute(pagename, request):
    _ = request.getText

    # Start content - IMPORTANT - without content div, there is no
    # direction support!
    if not hasattr(request, 'formatter'):
        formatter = HtmlFormatter(request)
    else:
        formatter = request.formatter
    request.page.formatter = formatter
    formatter.setPage(request.page)

    # This action generate data using the user language
    request.setContentLanguage(request.lang)

    request.theme.send_title(request.getText('Search by metadata'),
                             pagename=pagename)

    request.write(formatter.startContent("content"))

    q = ''
    mtabq = ''
    if request.values.has_key('q'):
        if request.values.has_key('mtab'):
            mtabq = ''.join(request.values.getlist('q'))
        else:
            q = ''.join(request.values.getlist('q'))

    request.write(u'<form method="GET" action="%s">\n' %
                  actionname(request, pagename))
    request.write(u'<input type=hidden name=action value="%s">' %
                  'MetaSearch')

    request.write(u'<input type="text" name="q" size=50 value="%s"> ' %
                  (form_escape(q)))
    request.write(u'<input type="submit" name="mtab" value="Search as MetaTable">')
    request.write(u'<input type="submit" value="' + _('Search') +
                  '">' + u'\n</form>\n')

    if q:
        if regexp_re.match(q):
            try:
                page_re = re.compile(q[1:-1])
                q = ''
            except re.error:
                request.write(render_error(_("Bad regexp!")))

        graphdata = request.graphdata
        graphdata.reverse_meta()
        keys_on_pages = graphdata.keys_on_pages
        vals_on_pages = graphdata.vals_on_pages

        keyhits = set([])
        keys = set([])
        for key in keys_on_pages:
            if q:
                if key == q:
                    keyhits.update(keys_on_pages[key])
                    keys.add(key)
            else:
                if page_re.match(key):
                    keyhits.update(keys_on_pages[key])
                    keys.add(key)

        valhits = set([])
        vals = set([])
        for val in vals_on_pages:
            if q:
                if val == q:
                    valhits.update(vals_on_pages[val])
                    vals.add(val)
            else:
                if page_re.match(val):
                    valhits.update(vals_on_pages[val])
                    vals.add(val)

        if not q:
            elemlist(request, formatter, keys, _('keys'))
            elemlist(request, formatter, vals, _('values'))

        request.write(formatter.paragraph(1))
        request.write(formatter.text(_("Found as key in following pages")))
        request.write(formatter.paragraph(0))

        request.write(formatter.bullet_list(1))
        for page in sorted(keyhits):
            # Do not include revisions etc so far, enabling this as per request
            if not graphdata[page].has_key(u'saved'):
                continue

            request.write(formatter.listitem(1))
            request.write(formatter.pagelink(1, page))
            request.write(formatter.text(page))
            request.write(formatter.pagelink(0))
            request.write(formatter.listitem(0))

        request.write(formatter.bullet_list(0))

        request.write(formatter.paragraph(1))
        request.write(formatter.text(_("Found as value in following pages")))
        request.write(formatter.paragraph(0))
        request.write(formatter.bullet_list(1))
        for page in sorted(valhits):
            # Do not include revisions etc so far, enabling this as per request
            if not graphdata[page].has_key(u'saved'):
                continue

            request.write(formatter.listitem(1))
            request.write(formatter.pagelink(1, page))
            request.write(formatter.text(page))
            request.write(formatter.pagelink(0))
            request.write(formatter.listitem(0))

        request.write(formatter.bullet_list(0))

    if mtabq:
        metatab = wikiutil.importPlugin(request.cfg, 'macro', 'MetaTable')
        request.write("<br>")
        # Poor but sufficient emulation of macro object
        mtabHTML = metatab(request.page, mtabq)
        request.write(mtabHTML)

    # End content
    request.write(formatter.endContent()) # end content div

    # Footer
    request.theme.send_footer(pagename)

    request.theme.send_closing_html()
예제 #8
0
def show_editform(wr, request, pagename, args):
    formatter = request.formatter

    # Note that metatable_parseargs handles read permission checks
    pagelist, metakeys, _ = metatable_parseargs(request,
                                                args,
                                                get_all_keys=True)

    uneditable_pages = list()
    # See that the user can write each page
    for page in pagelist:
        if not request.user.may.write(page):
            uneditable_pages.append(page)
    for page in uneditable_pages:
        pagelist.remove(page)

    _ = request.getText

    if uneditable_pages:
        reason = _("No save permission to some pages (%s)" %
                   ','.join(uneditable_pages))
        request.write(render_warning(reason))

    if not pagelist:
        reason = _("No pages to edit.")
        request.write(render_error(reason))
        return

    wr(u'<form method="POST" action="%s" enctype="multipart/form-data">\n',
       actionname(request))
    wr(u'<input type="hidden" name="action" value="%s">\n', action_name)
    wr(formatter.table(1))
    wr(formatter.table_row(1, {'rowclass': 'meta_header'}))
    wr(formatter.table_cell(1, {'class': 'meta_page'}))

    form = values_to_form(request.values)

    template = form.get('template', [''])[0]
    if template:
        wr('<input type="hidden" name="template" value="%s">', template)

    # Filter out uneditables, such as inlinks
    metakeys = editable_p(metakeys)

    _ = request.getText

    for key in metakeys + ['']:
        wr(formatter.table_cell(1, {'class': 'meta_header'}))
        wr(u'<input class="metakey" type="text" name="%s" value="%s">',
           u':: %s' % key, key)
    wr(formatter.table_row(0))

    values = dict()
    valnos = dict()

    for frompage in pagelist:
        values[frompage] = dict()

        for key in metakeys + ['']:
            values[frompage][key] = list()
            # If the page has no values for this key, it'll
            # have one in the table to add a value
            if not valnos.has_key(frompage):
                valnos[frompage] = 1

            keydata = get_metas(request,
                                frompage, [key],
                                abs_attach=False,
                                includeGenerated=False)

            for i, val in enumerate(keydata[key]):
                values[frompage][key].append(val)
                # Enumerate starts from 0: #values++
                # One to add a value: #values++
                if valnos[frompage] < i + 2:
                    valnos[frompage] = i + 2

            values[frompage][key].append('')

    for frompage in pagelist:
        wr(formatter.table_row(1))
        wr(
            formatter.table_cell(1, {
                'class': 'meta_page',
                'rowspan': str(valnos[frompage])
            }))
        wr(u'%s', frompage)

        for i in range(valnos[frompage]):
            # Add <tr>:s for additional values also
            if i > 0:
                wr(formatter.table_row(1))

            for key in metakeys + ['']:
                inputname = frompage + SEPARATOR + key

                if len(values[frompage][key]) >= (i + 1):
                    val = values[frompage][key][i]
                else:
                    val = ''

                # Skip default labels
                if key == 'label' and val == frompage:
                    val = ''

                wr(formatter.table_cell(1, {'class': 'meta_cell'}))
                wr(
                    u'<textarea class="metavalue dynamic"  rows="1" name="%s">%s</textarea>',
                    inputname, val)

                #print frompage, key, inputname, values, '<br>'
            wr(formatter.table_row(0))

        wr(formatter.table_row(1))
        wr(formatter.table_cell(1, {'class': 'meta_cell'}))
        for key in metakeys + ['']:
            inputname = frompage + SEPARATOR + key

            if len(values[frompage][key]) >= (i + 1):
                val = values[frompage][key][i]
            else:
                val = ''

            # Skip default labels
            if key == 'label' and val == frompage:
                val = ''

            wr(formatter.table_cell(1))
            wr(u'<input class="metavalue" type="file" name="%s">\n', inputname)

        wr(formatter.table_row(0))


# Proto JS code to warn on leaving an empty key name
# <script language="JavaScript" type="text/javascript">
#    function myvalid(me) {
#      if (me.form.subject.value == "") {
#        if (confirm("Empty subject, send anyway?"))
#          return true;
#        else
#          return false;
#      }
#      return true;
#    }
# </script>
# <input type="submit" name="send" value="Send" class="button1"tabindex="7"
# onClick="return myvalid(this);" />

    wr(formatter.table(0))
    wr(u'<input type="submit" name="save" value="%s">\n', _('Save'))
    wr(u'<input type="submit" name="cancel" value="%s">\n', _('Cancel'))
    wr(u'</form>\n')