diff --git a/barkshark_web/component/application.py b/barkshark_web/component/application.py
index 9acf612..6f392ea 100644
--- a/barkshark_web/component/application.py
+++ b/barkshark_web/component/application.py
@@ -6,6 +6,7 @@ from .. import dbus
from .. import __version__ as version
from ..config import var
from ..database import db
+from ..exceptions import AccountNotFoundError, NoAccountsError
class Application(Gtk.Application):
@@ -28,6 +29,75 @@ class Application(Gtk.Application):
self.connect('startup', self.handle_startup)
+ def create_tabs(self):
+ with db.session as s:
+ for row in s.fetch('tabs', orderby='order'):
+ self.window.new_tab(row=row)
+
+ for url in self.startup_urls:
+ self.window.new_tab(url, switch=row.active)
+
+ if len(self.window.tabdata.keys()) < 1:
+ self.window.new_tab(None, switch=False)
+
+ self.window.startup = False
+
+
+ def get_account_by_handle(self, username, domain=None):
+ if not len(self.accounts):
+ raise NoAccountsError('No accounts')
+
+ for acct in self.accounts:
+ if not domain and username == acct.handle:
+ return acct
+
+ elif domain and username == acct.handle and domain == acct.domain:
+ return acct
+
+ raise AccountNotFoundError('Cannot find account')
+
+
+ def get_account_by_id(self, id):
+ if not len(self.accounts):
+ raise NoAccountsError('No accounts')
+
+ for acct in self.accounts:
+ if acct.id == id:
+ return acct
+
+ raise AccountNotFoundError('Cannot find account')
+
+
+ def get_default_account(self):
+ if not len(self.accounts):
+ raise NoAccountsError('No accounts')
+
+ with db.session as s:
+ default = s.get_config('active_acct')
+
+ for acct in self.accounts:
+ if default == acct.id:
+ return acct
+
+ s.put_config('active_acct', self.accounts[0].id)
+
+ return self.accounts[0]
+
+
+ def set_clipboard_text(self, *text):
+ self.clipboard.set_text(' '.join(text), -1)
+
+
+ def quit(self, *args):
+ db.unregister_all_callbacks()
+
+ if self.window:
+ #self.window.passwords.db.disconnect()
+ self.window.handle_window_close()
+
+ super().quit()
+
+
def handle_activate(self, *args):
self.window = Window(self)
self.dbus = dbus.Server(self.window)
@@ -37,6 +107,12 @@ class Application(Gtk.Application):
self.window.show()
+ def handle_clipboard_clear_password(self, password):
+ if self.clipboard.wait_for_text() == password:
+ logging.debug('Clear clipboard text')
+ self.clipboard.set_text('', 0)
+
+
def handle_startup(self, *args):
Gtk.Application.do_startup(self)
accel = Gio.SimpleAction.new('accel', GLib.VariantType.new('s'))
@@ -82,79 +158,6 @@ class Application(Gtk.Application):
self.accounts = s.fetch('accounts').all()
- def quit(self, *args):
- db.unregister_all_callbacks()
-
- if self.window:
- #self.window.passwords.db.disconnect()
- self.window.handle_window_close()
-
- super().quit()
-
-
- def create_tabs(self):
- with db.session as s:
- for row in s.fetch('tabs', orderby='order'):
- self.window.new_tab(row=row)
-
- for url in self.startup_urls:
- self.window.new_tab(url, switch=row.active)
-
- if len(self.window.tabdata.keys()) < 1:
- self.window.new_tab(None, switch=False)
-
- self.window.startup = False
-
-
- def get_account_by_handle(self, username, domain=None):
- if not len(self.accounts):
- raise IndexError('No accounts')
-
- for acct in self.accounts:
- if not domain and username == acct.handle:
- return acct
-
- elif domain and username == acct.handle and domain == acct.domain:
- return acct
-
- raise IndexError('Cannot find account')
-
-
- def get_account_by_id(self, id):
- if not len(self.accounts):
- raise IndexError('No accounts')
-
- for acct in self.accounts:
- if acct.id == id:
- return acct
-
- raise IndexError('Cannot find account')
-
-
- def get_default_account(self):
- if not len(self.accounts):
- raise IndexError('No accounts')
-
- with db.session as s:
- default = s.get_config('active_acct')
-
- for acct in self.accounts:
- if default == acct.id:
- return acct
-
- return self.accounts[0]
-
-
- def set_clipboard_text(self, *text):
- self.clipboard.set_text(' '.join(text), -1)
-
-
- def handle_clipboard_clear_password(self, password):
- if self.clipboard.wait_for_text() == password:
- logging.debug('Clear clipboard text')
- self.clipboard.clear()
-
-
def handle_accel(self, signal, action):
action = action.get_string()
tab = self.window.active_tab
diff --git a/barkshark_web/component/status_bar.py b/barkshark_web/component/status_bar.py
index 2fea8f3..53faf24 100644
--- a/barkshark_web/component/status_bar.py
+++ b/barkshark_web/component/status_bar.py
@@ -1,5 +1,6 @@
from .. import fediverse
from ..database import db, default_permissions
+from ..exceptions import AccountNotFoundError, NoAccountsError
from ..functions import SignalBlock, connect, get_buffer_text
from ..objects.login_rows import SavedLoginRow
from ..passwords import passdb
@@ -8,12 +9,13 @@ from ..passwords import passdb
class StatusBar:
def __init__(self, window):
self.window = window
+ self.app = window.app
self.siteoptions_handler_ids = []
self.bookmark_row = None
self.theme_enabled = False
- self.toot_account = {}
+ self.toot_acct = None
self.toot_post = None
self.toot_max_len = 500
@@ -85,10 +87,10 @@ class StatusBar:
def toot_set_account(self):
- with db.session as s:
- self.toot_account = s.get_account()
+ try:
+ self.toot_account = self.app.get_default_account()
- if not self.toot_account:
+ except NoAccountsError:
self.window.notification('No active fedi accounts', 'error')
return False
@@ -171,6 +173,7 @@ class StatusBar:
#self.window.active_tab.editing_action('select')
#self.window.active_tab.editing_action('copy')
self.window.notification('Merp!', 'INFO', 0)
+ print(self.window.context.get_cookie_manager().delete_cookies_for_domain(self.window.active_tab.url.domain))
#if self.window.themes.current_theme:
#self.window.themes.UnloadTheme()
@@ -240,21 +243,21 @@ class StatusBar:
def handle_bookmark_button(self, name):
- if name == 'save':
- data = self.bookmark_get_data()
+ with db.session as s:
+ if name == 'save':
+ data = self.bookmark_get_data()
- with db.session as s:
if self.bookmark_row:
- data['id'] = self.bookmark_row.id
+ s.update_row(self.bookmark_row, **data)
- s.put_bookmark(**data)
+ else:
+ s.put_bookmark(**data)
- elif name == 'delete':
- if self.bookmark_row:
- s.remove_row(self.bookmark_row)
+ elif name == 'delete':
+ if self.bookmark_row:
+ s.remove_row(self.bookmark_row)
self.window['statusbar-bookmark-popover'].popdown()
- self.window.bookmarks.table.refresh()
def handle_fedi_button(self, name):
@@ -346,7 +349,7 @@ class StatusBar:
for child in login_list.get_children():
child.destroy()
- for row in passdb.fetch(domain=self.window.active_tab.url.domain):
+ for row in passdb.fetch(domain=self.window.active_tab.url.hostname()):
login_list.add(SavedLoginRow(row, self.window.active_tab.url)['container'])
diff --git a/barkshark_web/component/web_settings.py b/barkshark_web/component/web_settings.py
index e9e4016..06a713d 100644
--- a/barkshark_web/component/web_settings.py
+++ b/barkshark_web/component/web_settings.py
@@ -282,6 +282,11 @@ default_settings = {
'name': 'Serif font',
'description': 'Font to use when the page does not specify a font for pictograph fonts'
},
+ 'user-agent': {
+ 'default': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0',
+ 'name': 'User Agent',
+ 'description': 'How the browser identifies itself when connecting to web servers'
+ },
'zoom-text-only': {
'default': False,
'name': 'Only zoom text',
@@ -302,7 +307,7 @@ class WebSettings(WebKit2.Settings):
super().__init__()
self.tab = tab
- self.set_user_agent_with_application_details('pyWeb', version)
+ #self.set_user_agent_with_application_details('pyWeb', version)
for k,v in default_settings.items():
self[k] = v['default']
diff --git a/barkshark_web/config.py b/barkshark_web/config.py
index c7a5c7a..89c4cae 100644
--- a/barkshark_web/config.py
+++ b/barkshark_web/config.py
@@ -88,9 +88,6 @@ var.template.update_env({
logging.set_config('level', var.loglevel)
-if var.loglevel in ['DEBUG', 'VERBOSE']:
- izzylog.set_config('level', 'VERBOSE')
-
sys.path.insert(-1, dirs.extensions)
if dirs.data.join('database.sqlite3').exists():
diff --git a/barkshark_web/database/base.py b/barkshark_web/database/base.py
index 4a46eec..7197d57 100644
--- a/barkshark_web/database/base.py
+++ b/barkshark_web/database/base.py
@@ -120,7 +120,7 @@ default_config = {
'default_search': ('ddg', 'str'),
'detach_inspector': (False, 'bool'),
'download_dir': (Path('~/Downloads').expanduser(), 'path'),
- 'enable_autocomplete': (True, 'bool'),
+ 'enable_autocomplete': (False, 'bool'),
'fullscreen': (False, 'bool'),
'homepage': (var.local + '/', 'str'),
'https_force': (True, 'bool'),
@@ -334,12 +334,13 @@ class CustomSession(Session):
data = DotDict(
name = name,
url = url,
- description = description,
category = category or 'Misc',
lastupdate = datetime.now()
-
)
+ if description:
+ data['description'] = description
+
return self.insert('bookmarks', **data)
diff --git a/barkshark_web/database/rows.py b/barkshark_web/database/rows.py
index 35b83c2..622f4b1 100644
--- a/barkshark_web/database/rows.py
+++ b/barkshark_web/database/rows.py
@@ -38,11 +38,6 @@ class Search(Row):
class Account(Row):
_api = None
_emojis = None
- _db = None
-
-
- def __run__(self, session):
- self._db = session.db
@property
@@ -53,6 +48,12 @@ class Account(Row):
return self._api
+ @property
+ def active(self):
+ with self._db.session as s:
+ return s.get_config('active_acct') == self.id
+
+
@property
def avatar(self):
return dirs.avatars.join(f'{self.id}.png')
@@ -68,12 +69,6 @@ class Account(Row):
return
- @property
- def active(self):
- with self._db.session as s:
- return s.get_config('active_acct') == self.id
-
-
@property
def emojis(self):
if not self._emojis:
@@ -186,8 +181,3 @@ class Account(Row):
def set_active(self):
with self._db.session as s:
s.put_config('active_acct', self.id)
-
-
- def test(self):
- string = '30+ Transfem Sergal :trans_furr_white: :nbdab: :nbdab:\n\nDoes lots of Python crimes'
- return self.replace_emojis(string)
diff --git a/barkshark_web/exceptions.py b/barkshark_web/exceptions.py
new file mode 100644
index 0000000..80bd903
--- /dev/null
+++ b/barkshark_web/exceptions.py
@@ -0,0 +1,5 @@
+class NoAccountsError(Exception):
+ 'Raise when doing an action that requires a fediverse account, but none exist'
+
+class AccountNotFoundError(Exception):
+ 'Raise when a specific account is not found'
diff --git a/barkshark_web/localweb/js/functions.js b/barkshark_web/localweb/js/functions.js
index 04b1c70..bab116c 100644
--- a/barkshark_web/localweb/js/functions.js
+++ b/barkshark_web/localweb/js/functions.js
@@ -1,38 +1,34 @@
// General
function connect_event(name, signal, callback) {
- element = document.getElementById(name);
+ const element = document.getElementById(name);
element.addEventListener(signal, callback);
}
function delete_item(base_url, id) {
- request(`${base_url}/${id}`, function(xhr) {
- if (xhr.status != 200) {return;}
+ request(`${base_url}/${id}`, (response, body) => {
+ if (response.status != 200) {return;}
- var element = document.getElementById(id);
+ const element = document.getElementById(id);
element.parentElement.removeChild(element);
- })
+ });
}
-function request(url, callback, timeout=5) {
- const xhr = new XMLHttpRequest();
- xhr.timeout = 5
- xhr.open('GET', url);
-
- if (callback != undefined) {
- xhr.onload = function(event) {
- callback(event.target);
+function request(url, callback) {
+ fetch(url).then((response) => {
+ if (callback != null) {
+ response.text().then((body) => {
+ callback(response, body)
+ });
}
- }
-
- xhr.send();
+ });
}
function toggle_all_details(class_name, state) {
- var elements = document.getElementsByClassName(class_name);
-
+ const elements = document.getElementsByClassName(class_name);
+
for (let element of elements) {
if (state && !element.hasAttribute('open')) {
element.setAttribute('open', null);
@@ -44,15 +40,15 @@ function toggle_all_details(class_name, state) {
function toggle_menu() {
- var menu = document.getElementById('main-menu');
- var show_text = menu.hasAttribute('show');
-
+ const menu = document.getElementById('main-menu');
+ const show_text = menu.hasAttribute('show');
+
if (show_text) {
menu.removeAttribute('show');
} else {
menu.setAttribute('show', null);
}
-
+
for (let item of document.getElementsByClassName('menu-item-text')) {
if (show_text) {
item.style.display = 'none'
@@ -71,28 +67,24 @@ function handle_key_enter(event) {
function handle_save_config(event) {
- var input = event.target;
+ const input = event.target;
+ let value = '';
if (input.type.toUpperCase() == 'CHECKBOX') {
if (input.checked) {
- var value = 'true';
-
+ value = 'true';
} else {
- var value = 'false';
-
+ value = 'false';
}
-
} else {
- var value = input.value;
-
+ value = input.value;
}
const url = new URL(input.form.action);
url.searchParams.set(input.id, value)
- request(url, function(xhr) {
- if (xhr.status == 200) {
- console.log(xhr.status, url);
+ request(url, (response, body) => {
+ if (response.status == 200) {
console.log(`Set config: ${input.id}=${value}`);
}
});
@@ -102,15 +94,15 @@ function handle_save_config(event) {
// Fediverse
function create_account_nodes(ids) {
for (let id of ids) {
- request(`/fediverse/acct_info/${id}`, function(xhr) {
- if (xhr.status != 200) {
- console.log(`Error %{xhr.status} when trying to fetch account: ${xhr.responseText}`);
+ request(`/fediverse/acct_info/${id}`, (response, body) => {
+ if (response.status != 200) {
+ console.log(`Error ${response.status} when trying to fetch account: ${body}`);
return;
}
const accts = document.getElementById('accounts');
const container = document.createElement('div');
- container.innerHTML = xhr.responseText;
+ container.innerHTML = body;
accts.appendChild(container.children[0]);
})}
@@ -118,8 +110,8 @@ function create_account_nodes(ids) {
function set_active(acctid) {
- request(`/fediverse/set_active/${acctid}`, function(xhr) {
- if (xhr.status != 200) {
+ request(`/fediverse/set_active/${acctid}`, (response, body) => {
+ if (response.status != 200) {
console.log(`Failed to set account active: ID ${acctid}`);
return;
}
@@ -127,11 +119,10 @@ function set_active(acctid) {
for (let acct of document.getElementsByClassName('account')) {
var active = acct.getElementsByClassName('active')[0];
- if (acct.id == `ACCT${acctid}`) {
- active.style.display = 'none'
-
+ if (acct.id == acctid) {
+ active.style.display = 'none';
} else {
- active.style.display = 'inline-block'
+ active.style.display = 'inline-block';
}
}
});
@@ -140,8 +131,8 @@ function set_active(acctid) {
// History
function delete_history(histid) {
- request(`/history/delete/${histid}`, function(xhr) {
- if (xhr.status != 200) {return;}
+ request(`/history/delete/${histid}`, (response, body) => {
+ if (response.status != 200) {return;}
const element = document.getElementById(histid);
const details = element.parentElement;
@@ -157,20 +148,19 @@ function delete_history(histid) {
// Search
function set_default_search(id) {
- request(`/search/default/${id}`, function(xhr) {
- if (xhr.status != 200) {
+ request(`/search/default/${id}`, (response, body) => {
+ if (response.status != 200) {
console.log(`Failed to set search engine active: ID ${id}`);
return;
}
-
+
for (let acct of document.getElementsByClassName('search-item')) {
- var active = acct.getElementsByClassName('default')[0];
-
+ const active = acct.getElementsByClassName('default')[0];
+
if (acct.id == `SEARCH${id}`) {
- active.style.display = 'none'
-
+ active.style.display = 'none';
} else {
- active.style.display = 'inline-block'
+ active.style.display = 'inline-block';
}
}
});
@@ -179,9 +169,8 @@ function set_default_search(id) {
// Passwords
function copy_password(id) {
- console.log(id);
- request(`/passwords/copy/${id}`, function(xhr) {
- if (xhr.status != 200) {
+ request(`/passwords/copy/${id}`, (response, body) => {
+ if (response.status != 200) {
console.log(`Error code when trying to copy password: ${xhr.status} ${xhr.statusText}`);
}
});
diff --git a/barkshark_web/objects/login_rows.py b/barkshark_web/objects/login_rows.py
index f7c3b0d..ad5e4bb 100644
--- a/barkshark_web/objects/login_rows.py
+++ b/barkshark_web/objects/login_rows.py
@@ -9,9 +9,14 @@ class LoginRowBase:
return self.ui.get_object(key)
+ @property
+ def app(self):
+ return Gio.Application.get_default()
+
+
@property
def window(self):
- return Gio.Application.get_default().window
+ return self.app.window
def connect(self, name, signal, callback, *args, original_args=False, **kwargs):
@@ -25,36 +30,39 @@ class LoginRowBase:
class SavedLoginRow(LoginRowBase):
def __init__(self, row, page_url):
self.ui = Gtk.Builder.new_from_file(dirs.resources.join('password_saved.ui'))
- self.passrow = row
- self.tab = self.window.active_tab
+ self.row = row
with db.session as s:
self.dbrow = s.get_passfield(page_url)
- self['username'].set_text(self.passrow['username'])
+ self['username'].set_text(self.row['username'])
- if self.passrow.url != page_url or not self.dbrow:
- self['fill'].set_sensitive(False)
+ #if self.row.url != page_url or not self.dbrow:
+ #self['fill'].set_sensitive(False)
self.connect('fill', 'clicked', self.handle_fill_password)
- self.connect('copy', 'clicked', self.handle_copy_password)
+ self.connect('copy-password', 'clicked', self.handle_copy_password)
+ self.connect('copy-username', 'clicked', self.row.copy_username)
self['container'].show_all()
- def handle_copy_password(self):
- password = self.passrow.password
+ @property
+ def tab(self):
+ return self.window.active_tab
- self.window.clipboard.set_text(password, -1)
- self.window.notification('Copied password to clipboard for 30 seconds')
- TimeoutCallback(30, run_in_gui_thread, self.window.passwords.handle_clear_clipboard, self.window.clipboard, password).start()
+
+ def handle_copy_password(self):
+ self.row.copy_password(60)
+ self.window.notification('Copied password to clipboard for 5 seconds')
self.window['statusbar-logins-popover'].popdown()
def handle_fill_password(self):
- self.tab.run_js(f'document.getElementsByName("{self.dbrow.userfield}")[0].value = "{self.passrow.username}"')
- self.tab.run_js(f'document.getElementsByName("{self.dbrow.passfield}")[0].value = "{self.passrow.password}"')
+ with dirs.resources.join('ext_js/autofill.js').open() as fd:
+ self.tab.run_js(fd.read().replace('USERNAME_VALUE', self.row.username).replace('PASSWORD_VALUE', self.row.password))
+
self.window['statusbar-logins-popover'].popdown()
diff --git a/barkshark_web/passwords.py b/barkshark_web/passwords.py
index f79380f..cabf67e 100644
--- a/barkshark_web/passwords.py
+++ b/barkshark_web/passwords.py
@@ -4,6 +4,8 @@ from datetime import datetime
from secretstorage.collection import Collection, get_collection_by_alias, create_collection
from secretstorage.exceptions import ItemNotFoundException, LockedException
+from .functions import TimeoutCallback, get_app, run_in_gui_thread
+
pass_store_keys = ['username', 'domain', 'url', 'note']
pass_keys = [*pass_store_keys, 'created', 'modified', 'label', 'password']
@@ -274,6 +276,18 @@ class PasswordItem:
return self.update(note=value)
+ def copy_password(self, timeout=60):
+ app = get_app()
+ app.set_clipboard_text(self.password)
+
+ timer = TimeoutCallback(timeout, run_in_gui_thread, app.handle_clipboard_clear_password, self.password)
+ timer.start()
+
+
+ def copy_username(self):
+ get_app().set_clipboard_text(self.username)
+
+
def as_dict(self):
return DotDict(
id = self.id,
diff --git a/barkshark_web/protocol/local.py b/barkshark_web/protocol/local.py
index b1af1f5..f89c380 100644
--- a/barkshark_web/protocol/local.py
+++ b/barkshark_web/protocol/local.py
@@ -178,7 +178,7 @@ def fediverse_refresh(handler, request, acctid):
return request.error(f'Account with ID not found: {acctid}', 404)
row.refresh()
- return request.redirect('/fediverse')
+ return request.ok_or_redirect(f'Refreshed account info: {row.fullhandle}')
@Local.route('/fediverse/set_active/{acctid:int}')
@@ -190,8 +190,7 @@ def fediverse_set_active(handler, request, acctid):
return request.error(f'Account with ID not found: {acctid}', 404)
row.set_active()
- logging.verbose('Set account as active:', row.fullhandle)
- return request.response('OK')
+ return request.ok_or_redirect(f'Account set as active: {row.fullhandle}')
@Local.route('/fediverse/logout/{acctid:int}')
@@ -349,10 +348,7 @@ def passwords_copy(handler, request, rowid):
except KeyError:
return request.error(f'Cannot find password: {rowid}', 404)
- handler.app.set_clipboard_text(row.password)
-
- timer = TimeoutCallback(60, run_in_gui_thread, handler.app.handle_clipboard_clear_password, row.password)
- timer.start()
+ row.copy_password()
return request.ok_or_redirect(f'Copied password for 60 seconds')
@@ -384,7 +380,7 @@ def preferences_home(handler, request):
@Local.route('/preferences/update')
def preferences_update(handler, request):
- if not len(request.query):
+ if not len([key for key in request.query.keys() if key != 'redir']):
return request.error('No key/value pairs provided', 400)
with db.session as s:
@@ -395,7 +391,7 @@ def preferences_update(handler, request):
row = s.put_config(key, value)
logging.verbose(f'Updated config: {row.key} = {row.value}')
- return request.ok_or_redirect('/preferences', 'Updated preferences')
+ return request.ok_or_redirect('Updated preferences')
### Search ###
diff --git a/barkshark_web/resources/ext_js/autofill.js b/barkshark_web/resources/ext_js/autofill.js
new file mode 100644
index 0000000..2fb774b
--- /dev/null
+++ b/barkshark_web/resources/ext_js/autofill.js
@@ -0,0 +1,28 @@
+function fetch_fields() {
+ var userfield = null;
+ var passfield = null;
+
+ Array.from(document.forms).forEach((form) => {
+ Array.from(form.getElementsByTagName('input')).forEach((input) => {
+ if (input.type == 'password') {
+ passfield = input;
+ } else if (input.name.includes('user')) {
+ userfield = input;
+ }
+
+ if (![userfield, passfield].includes(null)) {
+ return;
+ }
+ });
+ });
+
+ return [userfield, passfield];
+}
+
+
+fields = fetch_fields();
+
+if (!fields.includes(null)) {
+ fields[0].value = 'USERNAME_VALUE';
+ fields[1].value = 'PASSWORD_VALUE';
+}
diff --git a/barkshark_web/resources/main.ui b/barkshark_web/resources/main.ui
index c9d4a31..f65e888 100644
--- a/barkshark_web/resources/main.ui
+++ b/barkshark_web/resources/main.ui
@@ -898,7 +898,7 @@ AND CONDITIONS OF THIS LICENSE.
window-close