top: 0;
background-color: white; /* kludge, see above */
text-align: left; }
-.stickycontainer { overflow-y: clip; } /* attach to heading's container */
+.stickycontainer { overflow-y: clip; } /* attach to heading's container */
+.stickyfooting { position: sticky; /* attach to footer */
+ bottom: 0;
+ background-color: white; /* kludge, see above */
+ text-align: right; }
search_path The requested search_path
sql Text of the sql command(s)
result_rows List of SQLResult objects
+ show_spaces HTML attribute of input element indicating checked checkbox
+ of the "Show spaces" checkbox
</%doc>
status_result = []
%>
- <% stmt_break = '<hr>' %>
- % for result_row in result_rows:
- ${stmt_break | n}
- <% stmt_break = '<hr class="innerbreak">' %>
-
- % if result_row.statusmessage is not None:
- <p class="sql_status">
- ${result_row.statusmessage.data.split(' ')[0]}</p>
- % endif
-
- % if result_row.rows:
- <table class="stickycontainer">
- % if result_row.heading is not None:
- ${self.render_heading(result_row.heading.data)}
- % endif
- <tbody>
- % for row in result_row.rows:
- ${self.render_row(row.data)}
- % endfor
- </tbody>
- </table>
- % endif
-
- <p>${result_row.rowcount.data}</p>
- % endfor
- <hr>
+ <div class="stickycontainer">
+ <% stmt_break = '<hr>' %>
+ % for result_row in result_rows:
+ ${stmt_break | n}
+ <% stmt_break = '<hr class="innerbreak">' %>
+
+ % if result_row.statusmessage is not None:
+ <p class="sql_status">
+ ${result_row.statusmessage.data.split(' ')[0]}</p>
+ % endif
+
+ % if result_row.rows:
+ <table class="stickycontainer">
+ % if result_row.heading is not None:
+ ${self.render_heading(result_row.heading.data)}
+ % endif
+ <tbody>
+ % for row in result_row.rows:
+ ${self.render_row(row.data)}
+ % endfor
+ </tbody>
+ </table>
+ % endif
+
+ <p>${result_row.rowcount.data}</p>
+ % endfor
+ <div class="stickyfooting">
+ <hr>
+ <self.lib:td_label for_id="show_spaces_id">
+ Show spaces
+ </self.lib:td_label>
+ <self.lib:td_input tab_index="${tab_index}">
+ <input name="show_spaces"
+ tabindex="${tab_index.val}"
+ type="checkbox"
+ ${show_spaces | n}
+ onchange="whitespaceDisplay(this.checked)"
+ />
+ </self.lib:td_input>
+ </div>
+ </div>
</%def>
<%def name="result_form(tab_index)">
+ ',top=' + window.screenTop)
.focus();
}
+ function whitespaceDisplay(checked) {
+ const tdSqltextSheet = document.adoptedStyleSheets[0];
+
+ if (checked) {
+ tdSqltextSheet.disabled = false;
+ } else {
+ tdSqltextSheet.disabled = true;
+ }
+ }
+
+ // See: https://web.dev/articles/constructable-stylesheets
+ function setupCSS() {
+ tdSqltextSheet = new CSSStyleSheet(disabled=true);
+ tdSqltextSheet.replaceSync(
+ 'td.sqltext {
+ text-decoration-line: underline;
+ text-decoration-color: silver;
+ text-decoration-style: double; }');
+ document.adoptedStyleSheets = [tdSqltextSheet];
+ }
+
+ if (document.readyState === "loading") {
+ // Loading hasn't finished yet
+ document.addEventListener("DOMContentLoaded", setupCSS);
+ } else {
+ // `DOMContentLoaded` has already fired
+ setupCSS();
+ }
</script>
<noscript>
import psycopg.errors
import pyramid.response
import tempfile
+import wtforms.fields
import pgwui_core.core
import pgwui_core.utils
from pgwui_common.view import auth_base_view
from pgwui_sql import exceptions as sql_ex
-from pgwui_core.constants import CSV
+from pgwui_core.constants import (
+ CSV,
+ CHECKED,
+ UNCHECKED)
from pgwui_sql.constants import MANY_FILES
log = logging.getLogger(__name__)
+@attrs.define(slots=False)
+class SQLInitialPost(pgwui_sql.views.base.SQLBaseInitialPost):
+ show_spaces = attrs.field(default=False)
+
+
+class SQLWTForm(pgwui_sql.views.base.SQLBaseWTForm):
+ '''The wtform used to connect to the db to execute SQL.'''
+ # We don't actually use the labels, wanting the template to
+ # look (and render) like html, but I'll define them anyway
+ # just to keep my hand in.
+ show_spaces = wtforms.fields.BooleanField(
+ 'Show spaces', id='show_spaces_id')
+
+
+@attrs.define(slots=False)
+class SQLForm(pgwui_sql.views.base.SQLBaseForm):
+ '''
+ Acts like a dict, but with extra methods.
+
+ Attributes:
+ uh The UploadHandler instance using the form
+ '''
+ show_spaces = attrs.field(default=False)
+
+ def read(self):
+ '''
+ Read form data from the client
+ '''
+ # Read parent's data
+ super().read()
+
+ # Read our own data
+ self['show_spaces'] = self._form.show_spaces.data
+
+ def write(self, result, errors):
+ '''
+ Produces the dict pyramid will use to render the form.
+ '''
+ response = super().write(result, errors)
+ if self['show_spaces']:
+ response['show_spaces'] = CHECKED
+ else:
+ response['show_spaces'] = UNCHECKED
+ return response
+
+
@attrs.define()
class SQLResult():
rows = attrs.field(factory=list)
Attributes:
request A pyramid request instance
- uf A SQLBaseForm instance
+ uf A SQLForm instance
session A pyramid session instance
ue
cur
tfile = attrs.field(default=None)
def make_form(self):
- return pgwui_sql.views.base.SQLBaseForm().build(
- self, ip=pgwui_sql.views.base.SQLBaseInitialPost(),
- fc=pgwui_sql.views.base.SQLBaseWTForm)
+ return SQLForm().build(self, ip=SQLInitialPost(), fc=SQLWTForm)
def read(self):
super().read()
self.data = tuple()
def format_detail(self, err, stmt_text):
+ # The textarea HTML spec has changed from receiving textarea
+ # data with cr+lf as EOF to just cr. See:
+ # https://github.com/whatwg/html/issues/6647
detail = []
if err.diag.message_detail is not None:
detail.append(markupsafe.escape(err.diag.message_detail))