hydrus/hydrus/client/gui/ClientGUILogin.py

2136 lines
80 KiB
Python

from . import ClientConstants as CC
from . import ClientDefaults
from . import ClientGUICommon
from . import ClientGUIDialogs
from . import ClientGUIDialogsQuick
from . import ClientGUIControls
from . import ClientGUIFunctions
from . import ClientGUIListBoxes
from . import ClientGUIListCtrl
from . import ClientGUIParsing
from . import ClientGUIScrolledPanels
from . import ClientGUITopLevelWindows
from . import ClientImporting
from . import ClientNetworking
from . import ClientNetworkingBandwidth
from . import ClientNetworkingContexts
from . import ClientNetworkingLogin
from . import ClientNetworkingSessions
from . import ClientPaths
from . import HydrusConstants as HC
from . import HydrusData
from . import HydrusExceptions
from . import HydrusGlobals as HG
from . import HydrusSerialisable
import os
import traceback
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
from . import QtPorting as QP
class EditLoginCredentialsPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, credential_definitions, credentials ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
#
self._control_data = []
rows = []
credential_definitions = list( credential_definitions )
credential_definitions.sort( key = lambda cd: cd.ShouldHide() )
for credential_definition in credential_definitions:
if credential_definition.ShouldHide():
echo_mode = QW.QLineEdit.Password
else:
echo_mode = QW.QLineEdit.Normal
control = QW.QLineEdit( self )
control.setEchoMode( echo_mode )
name = credential_definition.GetName()
if name in credentials:
control.setText( credentials[ name] )
control.textChanged.connect( self._UpdateSts )
control_st = ClientGUICommon.BetterStaticText( self )
self._control_data.append( ( credential_definition, control, control_st ) )
hbox = QP.HBoxLayout()
QP.AddToLayout( hbox, control, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( hbox, control_st )
rows.append( ( credential_definition.GetName() + ': ', hbox ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows, add_stretch_at_end = False )
self.widget().setLayout( gridbox )
self._UpdateSts()
def _UpdateSts( self ):
for ( credential_definition, control, control_st ) in self._control_data:
value = control.text()
hydrus_text = 'invalid'
if value == '':
string_match = credential_definition.GetStringMatch()
if string_match is None:
st_label = ''
else:
st_label = string_match.ToString()
else:
try:
credential_definition.Test( value )
st_label = 'looks good \u2713'
hydrus_text = 'valid'
except Exception as e:
st_label = str( e )
control_st.setText( st_label )
control_st.setProperty( 'hydrus_text', hydrus_text )
control_st.style().polish( control_st )
def CanOK( self ):
veto_errors = []
for ( credential_definition, control, control_st ) in self._control_data:
name = credential_definition.GetName()
value = control.text()
if value == '':
veto_errors.append( 'Value for ' + name + ' is blank!' )
else:
try:
credential_definition.Test( value )
except Exception as e:
veto_errors.append( 'For ' + name + ': ' + str( e ) )
if len( veto_errors ) > 0:
message = 'These values are invalid--are you sure this is ok?'
message += os.linesep * 2
message += os.linesep.join( veto_errors )
result = ClientGUIDialogsQuick.GetYesNo( self, message )
if result != QW.QDialog.Accepted:
return False
return True
def GetValue( self ):
credentials = {}
for ( credential_definition, control, control_st ) in self._control_data:
name = credential_definition.GetName()
value = control.text()
credentials[ name ] = value
return credentials
class EditLoginCredentialDefinitionPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, credential_definition ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
#
self._name = QW.QLineEdit( self )
self._credential_type = ClientGUICommon.BetterChoice( self )
for credential_type in [ ClientNetworkingLogin.CREDENTIAL_TYPE_TEXT, ClientNetworkingLogin.CREDENTIAL_TYPE_PASS ]:
self._credential_type.addItem( ClientNetworkingLogin.credential_type_str_lookup[ credential_type], credential_type )
string_match = credential_definition.GetStringMatch()
self._string_match = ClientGUIControls.StringMatchButton( self, string_match )
#
self._name.setText( credential_definition.GetName() )
self._credential_type.SetValue( credential_definition.GetType() )
#
rows = []
rows.append( ( 'name: ', self._name ) )
rows.append( ( 'input type: ', self._credential_type ) )
rows.append( ( 'permitted input: ', self._string_match ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
self.widget().setLayout( gridbox )
def GetValue( self ):
name = self._name.text()
credential_type = self._credential_type.GetValue()
string_match = self._string_match.GetValue()
credential_definition = ClientNetworkingLogin.LoginCredentialDefinition( name = name, credential_type = credential_type, string_match = string_match )
return credential_definition
class EditLoginsPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, engine, login_scripts, domains_to_login_info ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
self._engine = engine
self._login_scripts = login_scripts
self._domains_to_login_after_ok = []
self._domains_and_login_info_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
columns = [ ( 'domain', 20 ), ( 'login script', -1 ), ( 'access given', 36 ), ( 'active?', 8 ), ( 'logged in now?', 28 ), ( 'validity', 28 ), ( 'recent error/delay?', 21 ) ]
self._domains_and_login_info = ClientGUIListCtrl.BetterListCtrl( self._domains_and_login_info_panel, 'domains_to_login_info', 8, 36, columns, self._ConvertDomainAndLoginInfoListCtrlTuples, use_simple_delete = True, activation_callback = self._EditCredentials )
self._domains_and_login_info_panel.SetListCtrl( self._domains_and_login_info )
self._domains_and_login_info_panel.AddButton( 'add', self._Add )
self._domains_and_login_info_panel.AddDeleteButton()
self._domains_and_login_info_panel.AddSeparator()
self._domains_and_login_info_panel.AddButton( 'edit credentials', self._EditCredentials, enabled_check_func = self._CanEditCreds )
self._domains_and_login_info_panel.AddButton( 'change login script', self._EditLoginScript, enabled_only_on_selection = True )
self._domains_and_login_info_panel.AddButton( 'flip active', self._FlipActive, enabled_only_on_selection = True )
self._domains_and_login_info_panel.AddSeparator()
self._domains_and_login_info_panel.AddButton( 'scrub invalidity', self._ScrubInvalidity, enabled_check_func = self._CanScrubInvalidity )
self._domains_and_login_info_panel.AddButton( 'scrub delays', self._ScrubDelays, enabled_check_func = self._CanScrubDelays )
self._domains_and_login_info_panel.NewButtonRow()
self._domains_and_login_info_panel.AddButton( 'do login now', self._DoLogin, enabled_check_func = self._CanDoLogin )
self._domains_and_login_info_panel.AddButton( 'reset login (delete cookies)', self._ClearSessions, enabled_only_on_selection = True )
#
listctrl_data = []
for ( login_domain, ( login_script_key_and_name, credentials, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) ) in list(domains_to_login_info.items()):
credentials_tuple = tuple( credentials.items() )
domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
listctrl_data.append( domain_and_login_info )
self._domains_and_login_info.AddDatas( listctrl_data )
self._domains_and_login_info.Sort( 0 )
#
vbox = QP.VBoxLayout()
warning = 'WARNING: Your credentials are stored in plaintext! For this and other reasons, I recommend you use throwaway accounts with hydrus!'
warning += os.linesep * 2
warning += 'If a login script does not work for you, or the site you want has a complicated captcha, check out the Hydrus Companion web browser add-on--it can copy login cookies to hydrus! Pixiv recently changed their login system and now require this!'
warning_st = ClientGUICommon.BetterStaticText( self, warning )
warning_st.setAlignment( QC.Qt.AlignHCenter | QC.Qt.AlignVCenter )
warning_st.setWordWrap( True )
warning_st.setObjectName( 'HydrusWarning' )
QP.AddToLayout( vbox, warning_st, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._domains_and_login_info_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.widget().setLayout( vbox )
def _Add( self ):
if len( self._login_scripts ) == 0:
QW.QMessageBox.critical( self, 'Error', 'You have no login scripts, so you cannot add a new login!' )
return
choice_tuples = [ ( login_script.GetName(), login_script ) for login_script in self._login_scripts ]
try:
login_script = ClientGUIDialogsQuick.SelectFromList( self, 'select the login script to use', choice_tuples )
except HydrusExceptions.CancelledException:
return
example_domains = set( login_script.GetExampleDomains() )
domains_in_use = { login_domain for ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) in self._domains_and_login_info.GetData() }
available_examples = list( example_domains.difference( domains_in_use ) )
available_examples.sort()
if len( available_examples ) > 0:
choice_tuples = [ ( login_domain, login_domain ) for login_domain in available_examples ]
choice_tuples.append( ( 'use other domain', None ) )
try:
login_domain = ClientGUIDialogsQuick.SelectFromList( self, 'select the domain to use', choice_tuples, sort_tuples = False )
except HydrusExceptions.CancelledException:
return
if login_domain is not None:
( login_access_type, login_access_text ) = login_script.GetExampleDomainInfo( login_domain )
else:
login_domain = None
if login_domain is None:
with ClientGUIDialogs.DialogTextEntry( self, 'enter the domain', placeholder = 'example.com', allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
login_domain = dlg.GetValue()
if login_domain in domains_in_use:
QW.QMessageBox.warning( self, 'Warning', 'That domain is already in use!' )
return
a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ]
choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ]
try:
login_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, sort_tuples = False )
except HydrusExceptions.CancelledException:
return
login_access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ login_access_type ]
with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = login_access_text, allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
login_access_text = dlg.GetValue()
else:
return
else:
return
credential_definitions = login_script.GetCredentialDefinitions()
credentials = {}
if len( credential_definitions ) > 0:
with ClientGUITopLevelWindows.DialogEdit( self, 'edit login' ) as dlg:
panel = EditLoginCredentialsPanel( dlg, credential_definitions, credentials )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
credentials = panel.GetValue()
else:
return
try:
login_script.CheckCanLogin( credentials )
validity = ClientNetworkingLogin.VALIDITY_UNTESTED
validity_error_text = ''
# hacky: if there are creds, is at least one not empty string?
creds_are_good = len( credentials ) == 0 or True in ( value != '' for value in list(credentials.values()) )
except HydrusExceptions.ValidationException as e:
validity = ClientNetworkingLogin.VALIDITY_INVALID
validity_error_text = str( e )
creds_are_good = False
active = False
if creds_are_good:
message = 'Activate this login script for this domain?'
result = ClientGUIDialogsQuick.GetYesNo( self, message )
if result == QW.QDialog.Accepted:
active = True
login_script_key_and_name = login_script.GetLoginScriptKeyAndName()
credentials_tuple = tuple( credentials.items() )
no_work_until = 0
no_work_until_reason = ''
domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
self._domains_and_login_info.AddDatas( ( domain_and_login_info, ) )
self._domains_and_login_info.Sort()
def _CanDoLogin( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
if not active:
continue
if validity == ClientNetworkingLogin.VALIDITY_INVALID:
continue
try:
login_script = self._GetLoginScript( login_script_key_and_name )
network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain )
logged_in = login_script.IsLoggedIn( self._engine, network_context )
if logged_in:
continue
except HydrusExceptions.DataMissing:
continue
return True
return False
def _CanEditCreds( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
try:
login_script = self._GetLoginScript( login_script_key_and_name )
if len( login_script.GetCredentialDefinitions() ) > 0:
return True
except HydrusExceptions.DataMissing:
continue
return False
def _CanScrubDelays( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
if not HydrusData.TimeHasPassed( no_work_until ) or no_work_until_reason != '':
return True
return False
def _CanScrubInvalidity( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
if validity == ClientNetworkingLogin.VALIDITY_INVALID:
return True
return False
def _ClearSessions( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
if len( domain_and_login_infos ) > 0:
message = 'Are you sure you want to clear these domains\' sessions? This will delete all their existing cookies and cannot be undone.'
result = ClientGUIDialogsQuick.GetYesNo( self, message )
if result != QW.QDialog.Accepted:
return
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain )
self._engine.session_manager.ClearSession( network_context )
self._domains_and_login_info.UpdateDatas()
self._domains_and_login_info_panel.UpdateButtons()
def _ConvertDomainAndLoginInfoListCtrlTuples( self, domain_and_login_info ):
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
login_expiry = None
try:
login_script = self._GetLoginScript( login_script_key_and_name )
sort_login_script = login_script.GetName()
network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain )
logged_in = login_script.IsLoggedIn( self._engine, network_context )
if logged_in:
login_expiry = login_script.GetLoginExpiry( self._engine, network_context )
except HydrusExceptions.DataMissing:
sort_login_script = 'login script not found'
logged_in = False
access = ClientNetworkingLogin.login_access_type_str_lookup[ login_access_type ] + ' - ' + login_access_text
if active:
sort_active = 'yes'
else:
sort_active = 'no'
sort_validity = ClientNetworkingLogin.validity_str_lookup[ validity ]
if len( validity_error_text ) > 0:
sort_validity += ' - ' + validity_error_text
if login_expiry is None:
sort_login_expiry = HydrusData.GetNow() + 45 * 60
else:
sort_login_expiry = login_expiry
sort_logged_in = ( logged_in, sort_login_expiry )
if HydrusData.TimeHasPassed( no_work_until ):
pretty_no_work_until = ''
else:
pretty_no_work_until = HydrusData.ConvertTimestampToPrettyExpires( no_work_until ) + ' - ' + no_work_until_reason
pretty_login_domain = login_domain
pretty_login_script = sort_login_script
pretty_access = access
pretty_active = sort_active
if active:
pretty_validity = sort_validity
else:
pretty_validity = ''
if logged_in:
if login_expiry is None:
pretty_login_expiry = 'session'
else:
pretty_login_expiry = HydrusData.ConvertTimestampToPrettyExpires( login_expiry )
pretty_logged_in = 'yes - ' + pretty_login_expiry
else:
pretty_logged_in = 'no'
display_tuple = ( pretty_login_domain, pretty_login_script, pretty_access, pretty_active, pretty_logged_in, pretty_validity, pretty_no_work_until )
sort_tuple = ( login_domain, sort_login_script, access, sort_active, sort_logged_in, sort_validity, no_work_until )
return ( display_tuple, sort_tuple )
def _DoLogin( self ):
domains_to_login = []
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
if not active:
continue
if validity == ClientNetworkingLogin.VALIDITY_INVALID:
continue
try:
login_script = self._GetLoginScript( login_script_key_and_name )
network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain )
logged_in = login_script.IsLoggedIn( self._engine, network_context )
if logged_in:
continue
except HydrusExceptions.DataMissing:
continue
domains_to_login.append( login_domain )
if len( domains_to_login ) == 0:
QW.QMessageBox.warning( self, 'Warning', 'Unfortunately, none of the selected domains appear able to log in. Do you need to activate or scrub something somewhere?' )
else:
domains_to_login.sort()
message = 'It looks like the following domains can log in:'
message += os.linesep * 2
message += os.linesep.join( domains_to_login )
message += os.linesep * 2
message += 'The dialog will ok and the login attempts will start. Is this ok?'
result = ClientGUIDialogsQuick.GetYesNo( self, message )
if result != QW.QDialog.Accepted:
return
self._domains_to_login_after_ok = domains_to_login
self.parentWidget().DoOK()
def _EditCredentials( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
try:
login_script = self._GetLoginScript( login_script_key_and_name )
except HydrusExceptions.DataMissing:
QW.QMessageBox.information( self, 'Information', 'Could not find a login script for "'+login_domain+'"! Please re-add the login script in the other dialog or update the entry here to a new one!' )
return
credential_definitions = login_script.GetCredentialDefinitions()
if len( credential_definitions ) > 0:
with ClientGUITopLevelWindows.DialogEdit( self, 'edit login' ) as dlg:
credentials = dict( credentials_tuple )
panel = EditLoginCredentialsPanel( dlg, credential_definitions, credentials )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
credentials = panel.GetValue()
else:
return
else:
continue
try:
login_script.CheckCanLogin( credentials )
validity = ClientNetworkingLogin.VALIDITY_UNTESTED
validity_error_text = ''
# hacky: if there are creds, is at least one not empty string?
creds_are_good = len( credentials ) == 0 or True in ( value != '' for value in list(credentials.values()) )
except HydrusExceptions.ValidationException as e:
validity = ClientNetworkingLogin.VALIDITY_INVALID
validity_error_text = str( e )
creds_are_good = False
credentials_tuple = tuple( credentials.items() )
if creds_are_good:
if not active:
message = 'Activate this login script for this domain?'
result = ClientGUIDialogsQuick.GetYesNo( self, message )
if result == QW.QDialog.Accepted:
active = True
else:
active = False
no_work_until = 0
no_work_until_reason = ''
edited_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) )
self._domains_and_login_info.AddDatas( ( edited_domain_and_login_info, ) )
self._domains_and_login_info.Sort()
def _EditLoginScript( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
try:
current_login_script = self._GetLoginScript( login_script_key_and_name )
except HydrusExceptions.DataMissing:
current_login_script = None
potential_login_scripts = list( self._login_scripts )
potential_login_scripts.sort( key = lambda ls: ls.GetName() )
matching_potential_login_scripts = [ login_script for login_script in potential_login_scripts if login_domain in login_script.GetExampleDomains() ]
unmatching_potential_login_scripts = [ login_script for login_script in potential_login_scripts if login_domain not in login_script.GetExampleDomains() ]
choice_tuples = [ ( login_script.GetName(), login_script ) for login_script in matching_potential_login_scripts ]
if len( matching_potential_login_scripts ) > 0 and len( unmatching_potential_login_scripts ) > 0:
choice_tuples.append( ( '------', None ) )
choice_tuples.extend( [ ( login_script.GetName(), login_script ) for login_script in unmatching_potential_login_scripts ] )
try:
login_script = ClientGUIDialogsQuick.SelectFromList( self, 'select the login script to use', choice_tuples, value_to_select = current_login_script, sort_tuples = False )
except HydrusExceptions.CancelledException:
break
if login_script is None:
break
if login_script == current_login_script:
break
login_script_key_and_name = login_script.GetLoginScriptKeyAndName()
try:
( login_access_type, login_access_text ) = login_script.GetExampleDomainInfo( login_domain )
except HydrusExceptions.DataMissing:
a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ]
choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ]
try:
login_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, sort_tuples = False )
except HydrusExceptions.CancelledException:
break
login_access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ login_access_type ]
with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = login_access_text, allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
login_access_text = dlg.GetValue()
else:
break
credentials = dict( credentials_tuple )
try:
login_script.CheckCanLogin( credentials )
validity = ClientNetworkingLogin.VALIDITY_UNTESTED
validity_error_text = ''
creds_are_good = True
except HydrusExceptions.ValidationException as e:
validity = ClientNetworkingLogin.VALIDITY_INVALID
validity_error_text = str( e )
creds_are_good = False
if not creds_are_good:
active = False
no_work_until = 0
no_work_until_reason = ''
edited_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) )
self._domains_and_login_info.AddDatas( ( edited_domain_and_login_info, ) )
self._domains_and_login_info.Sort()
def _FlipActive( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
active = not active
flipped_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) )
self._domains_and_login_info.AddDatas( ( flipped_domain_and_login_info, ) )
self._domains_and_login_info.Sort()
def _GetLoginScript( self, login_script_key_and_name ):
( login_script_key, login_script_name ) = login_script_key_and_name
for login_script in self._login_scripts:
if login_script.GetLoginScriptKey() == login_script_key:
return login_script
for login_script in self._login_scripts:
if login_script.GetName() == login_script_name:
return login_script
raise HydrusExceptions.DataMissing( 'No login script found!' )
def _ScrubDelays( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
no_work_until = 0
no_work_until_reason = ''
scrubbed_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) )
self._domains_and_login_info.AddDatas( ( scrubbed_domain_and_login_info, ) )
self._domains_and_login_info.Sort()
def _ScrubInvalidity( self ):
domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True )
for domain_and_login_info in domain_and_login_infos:
( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info
if validity != ClientNetworkingLogin.VALIDITY_INVALID:
continue
try:
try:
login_script = self._GetLoginScript( login_script_key_and_name )
except HydrusExceptions.DataMissing:
continue
credentials = dict( credentials_tuple )
login_script.CheckCanLogin( credentials )
validity = ClientNetworkingLogin.VALIDITY_UNTESTED
validity_error_text = ''
except HydrusExceptions.ValidationException as e:
validity = ClientNetworkingLogin.VALIDITY_INVALID
validity_error_text = str( e )
scrubbed_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) )
self._domains_and_login_info.AddDatas( ( scrubbed_domain_and_login_info, ) )
self._domains_and_login_info.Sort()
def GetDomainsToLoginAfterOK( self ):
return self._domains_to_login_after_ok
def GetValue( self ):
domains_to_login_info = dict()
for ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) in self._domains_and_login_info.GetData():
credentials = dict( credentials_tuple )
domains_to_login_info[ login_domain ] = ( login_script_key_and_name, credentials, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason )
return domains_to_login_info
def GenerateTestNetworkJobPresentationContextFactory( window, network_job_control ):
def network_job_presentation_context_factory( network_job ):
def qt_set_it( nj ):
if not window or not QP.isValid( window ):
return
network_job_control.SetNetworkJob( nj )
def enter_call():
QP.CallAfter( qt_set_it, network_job )
def exit_call():
QP.CallAfter( qt_set_it, None )
return ClientImporting.NetworkJobPresentationContext( enter_call, exit_call )
return network_job_presentation_context_factory
class ReviewTestResultPanel( ClientGUIScrolledPanels.ReviewPanel ):
def __init__( self, parent, test_result ):
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )
( name, url, body, self._downloaded_data, new_temp_strings, new_cookie_strings, result ) = test_result
self._name = ClientGUICommon.BetterStaticText( self, label = name )
self._url = QW.QLineEdit( self )
self._url.setReadOnly( True )
self._body = QW.QPlainTextEdit( self )
self._body.setReadOnly( True )
min_size = ClientGUIFunctions.ConvertTextToPixels( self._body, ( 64, 3 ) )
QP.SetMinClientSize( self._body, min_size )
self._data_preview = QW.QPlainTextEdit( self )
self._data_preview.setReadOnly( True )
min_size = ClientGUIFunctions.ConvertTextToPixels( self._data_preview, ( 64, 8 ) )
QP.SetMinClientSize( self._data_preview, min_size )
self._data_copy_button = ClientGUICommon.BetterBitmapButton( self, CC.global_pixmaps().copy, self._CopyData )
self._data_copy_button.setToolTip( 'Copy the current example data to the clipboard.' )
self._temp_variables = QW.QPlainTextEdit( self )
self._temp_variables.setReadOnly( True )
min_size = ClientGUIFunctions.ConvertTextToPixels( self._temp_variables, ( 64, 6 ) )
QP.SetMinClientSize( self._temp_variables, min_size )
self._cookies = QW.QPlainTextEdit( self )
self._cookies.setReadOnly( True )
min_size = ClientGUIFunctions.ConvertTextToPixels( self._cookies, ( 64, 6 ) )
QP.SetMinClientSize( self._cookies, min_size )
self._result = ClientGUICommon.BetterStaticText( self, label = result )
#
self._url.setText( url )
if body is not None:
try:
self._body.setPlainText( body )
except:
self._body.setPlainText( str( body ) )
self._data_preview.setPlainText( str( self._downloaded_data[:1024] ) )
self._temp_variables.setPlainText( os.linesep.join( new_temp_strings ) )
self._cookies.setPlainText( os.linesep.join( new_cookie_strings ) )
#
rows = []
rows.append( ( 'name: ', self._name ) )
rows.append( ( 'url: ', self._url ) )
rows.append( ( 'body (if set): ', self._body ) )
rows.append( ( 'data: ', self._data_preview ) )
rows.append( ( 'copy data: ', self._data_copy_button ) )
rows.append( ( 'new temp vars: ', self._temp_variables ) )
rows.append( ( 'new cookies: ', self._cookies ) )
rows.append( ( 'result: ', self._result ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
self.widget().setLayout( gridbox )
def _CopyData( self ):
HG.client_controller.pub( 'clipboard', 'text', self._downloaded_data )
class EditLoginScriptPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, login_script ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
self._original_login_script = login_script
self._currently_testing = False
self._test_domain = ''
self._test_credentials = {}
#
menu_items = []
page_func = HydrusData.Call( ClientPaths.LaunchPathInWebBrowser, os.path.join( HC.HELP_DIR, 'downloader_login.html' ) )
menu_items.append( ( 'normal', 'open the login scripts help', 'Open the help page for login scripts in your web browser.', page_func ) )
help_button = ClientGUICommon.MenuBitmapButton( self, CC.global_pixmaps().help, menu_items )
help_hbox = ClientGUICommon.WrapInText( help_button, self, 'help for this panel -->', QG.QColor( 0, 0, 255 ) )
#
self._name = QW.QLineEdit( self )
#
credential_definitions_box_panel = ClientGUICommon.StaticBox( self, 'credential definitions' )
credential_definitions_panel = ClientGUIListCtrl.BetterListCtrlPanel( credential_definitions_box_panel )
columns = [ ( 'name', -1 ), ( 'type', 10 ), ( 'value', 16 ) ]
self._credential_definitions = ClientGUIListCtrl.BetterListCtrl( credential_definitions_panel, 'credential_definitions', 4, 16, columns, self._ConvertCredentialDefinitionToListCtrlTuples, use_simple_delete = True, activation_callback = self._EditCredentialDefinitions )
credential_definitions_panel.SetListCtrl( self._credential_definitions )
credential_definitions_panel.AddButton( 'add', self._AddCredentialDefinition )
credential_definitions_panel.AddButton( 'edit', self._EditCredentialDefinitions, enabled_only_on_selection = True )
credential_definitions_panel.AddDeleteButton()
#
login_steps_box_panel = ClientGUICommon.StaticBox( self, 'login steps' )
columns = [ ( 'name', -1 ), ( 'url', 56 ) ]
self._login_steps = ClientGUIListBoxes.QueueListBox( login_steps_box_panel, 5, self._ConvertLoginStepToListBoxString, add_callable = self._AddLoginStep, edit_callable = self._EditLoginStep )
#
required_cookies_info_box_panel = ClientGUICommon.StaticBox( self, 'cookies required to consider session logged in' )
self._required_cookies_info = ClientGUIControls.StringMatchToStringMatchDictControl( required_cookies_info_box_panel, login_script.GetRequiredCookiesInfo(), min_height = 4, key_name = 'cookie name' )
#
example_domains_info_box_panel = ClientGUICommon.StaticBox( self, 'example domains' )
example_domains_info_panel = ClientGUIListCtrl.BetterListCtrlPanel( example_domains_info_box_panel )
columns = [ ( 'domain', -1 ), ( 'access type', 14 ), ( 'description', 40 ) ]
self._example_domains_info = ClientGUIListCtrl.BetterListCtrl( example_domains_info_panel, 'example_domains_info', 6, 16, columns, self._ConvertExampleDomainInfoToListCtrlTuples, use_simple_delete = True, activation_callback = self._EditExampleDomainsInfo )
example_domains_info_panel.SetListCtrl( self._example_domains_info )
example_domains_info_panel.AddButton( 'add', self._AddExampleDomainsInfo )
example_domains_info_panel.AddButton( 'edit', self._EditExampleDomainsInfo, enabled_only_on_selection = True )
example_domains_info_panel.AddDeleteButton()
#
test_panel = ClientGUICommon.StaticBox( self, 'testing' )
self._test_button = ClientGUICommon.BetterButton( test_panel, 'run test', self._DoTest )
self._test_network_job_control = ClientGUIControls.NetworkJobControl( test_panel )
test_listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( test_panel )
columns = [ ( 'step', -1 ), ( 'url', 36 ), ( 'result', 14 ) ]
self._test_listctrl = ClientGUIListCtrl.BetterListCtrl( test_listctrl_panel, 'test_login_script_results', 6, 20, columns, self._ConvertTestResultToListCtrlTuples, activation_callback = self._ReviewTestResult )
test_listctrl_panel.SetListCtrl( self._test_listctrl )
test_listctrl_panel.AddButton( 'review', self._ReviewTestResult, enabled_only_on_selection = True )
self._final_test_result = ClientGUICommon.BetterStaticText( test_panel )
#
self._name.setText( login_script.GetName() )
self._credential_definitions.SetData( login_script.GetCredentialDefinitions() )
self._login_steps.AddDatas( login_script.GetLoginSteps() )
self._example_domains_info.SetData( login_script.GetExampleDomainsInfo() )
#
credential_definitions_box_panel.Add( credential_definitions_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
login_steps_box_panel.Add( self._login_steps, CC.FLAGS_EXPAND_BOTH_WAYS )
required_cookies_info_box_panel.Add( self._required_cookies_info, CC.FLAGS_EXPAND_BOTH_WAYS )
example_domains_info_box_panel.Add( example_domains_info_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
test_panel.Add( self._test_button, CC.FLAGS_EXPAND_PERPENDICULAR )
test_panel.Add( self._test_network_job_control, CC.FLAGS_EXPAND_PERPENDICULAR )
test_panel.Add( test_listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
test_panel.Add( self._final_test_result, CC.FLAGS_EXPAND_PERPENDICULAR )
#
rows = []
rows.append( ( 'name: ', self._name ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, help_hbox, CC.FLAGS_BUTTON_SIZER )
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, credential_definitions_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, login_steps_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, required_cookies_info_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, example_domains_info_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
hbox = QP.HBoxLayout()
QP.AddToLayout( hbox, vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
QP.AddToLayout( hbox, test_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.widget().setLayout( hbox )
def _AddCredentialDefinition( self ):
new_credential_definition = ClientNetworkingLogin.LoginCredentialDefinition()
with ClientGUITopLevelWindows.DialogEdit( self, 'edit parser', frame_key = 'deeply_nested_dialog' ) as dlg_edit:
panel = EditLoginCredentialDefinitionPanel( dlg_edit, new_credential_definition )
dlg_edit.SetPanel( panel )
if dlg_edit.exec() == QW.QDialog.Accepted:
new_credential_definition = panel.GetValue()
HydrusSerialisable.SetNonDupeName( new_credential_definition, self._GetExistingCredentialDefinitionNames() )
self._credential_definitions.AddDatas( ( new_credential_definition, ) )
self._credential_definitions.Sort()
def _AddExampleDomainsInfo( self ):
( domain, access_type, access_text ) = ( 'example.com', ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.login_access_type_default_description_lookup[ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW ] )
with ClientGUIDialogs.DialogTextEntry( self, 'edit the domain', default = domain, allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
domain = dlg.GetValue()
else:
return
existing_domains = self._GetExistingDomains()
if domain in existing_domains:
QW.QMessageBox.critical( self, 'Error', 'That domain already exists!' )
return
a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ]
choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ]
try:
new_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, value_to_select = access_type, sort_tuples = False )
except HydrusExceptions.CancelledException:
return
if new_access_type != access_type:
access_type = new_access_type
access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ access_type ]
with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = access_text, allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
access_text = dlg.GetValue()
else:
return
example_domain_info = ( domain, access_type, access_text )
self._example_domains_info.AddDatas( ( example_domain_info, ) )
self._example_domains_info.Sort()
def _AddLoginStep( self ):
login_step = ClientNetworkingLogin.LoginStep()
return self._EditLoginStep( login_step )
def _ConvertCredentialDefinitionToListCtrlTuples( self, credential_definition ):
name = credential_definition.GetName()
credential_type = credential_definition.GetType()
type_string = ClientNetworkingLogin.credential_type_str_lookup[ credential_type ]
string_match = credential_definition.GetStringMatch()
value = string_match.ToString()
pretty_name = name
pretty_type_string = type_string
pretty_value = value
display_tuple = ( pretty_name, pretty_type_string, pretty_value )
sort_tuple = ( name, type_string, value )
return ( display_tuple, sort_tuple )
def _ConvertExampleDomainInfoToListCtrlTuples( self, example_domain_info ):
( domain, access_type, access_text ) = example_domain_info
pretty_domain = domain
pretty_access_type = ClientNetworkingLogin.login_access_type_str_lookup[ access_type ]
pretty_access_text = access_text
display_tuple = ( pretty_domain, pretty_access_type, pretty_access_text )
sort_tuple = ( domain, pretty_access_type, access_text )
return ( display_tuple, sort_tuple )
def _ConvertLoginStepToListBoxString( self, login_step ):
name = login_step.GetName()
return name
def _ConvertTestResultToListCtrlTuples( self, test_result ):
( name, url, body, downloaded_data, new_temp_strings, new_cookie_strings, result ) = test_result
pretty_name = name
pretty_url = url
pretty_result = result
display_tuple = ( pretty_name, pretty_url, pretty_result )
sort_tuple = ( name, url, result )
return ( display_tuple, sort_tuple )
def _EditCredentialDefinitions( self ):
credential_definitions = self._credential_definitions.GetData( only_selected = True )
for credential_definition in credential_definitions:
with ClientGUITopLevelWindows.DialogEdit( self, 'edit login script', frame_key = 'deeply_nested_dialog' ) as dlg:
panel = EditLoginCredentialDefinitionPanel( dlg, credential_definition )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
edited_credential_definition = panel.GetValue()
self._credential_definitions.DeleteDatas( ( credential_definition, ) )
HydrusSerialisable.SetNonDupeName( edited_credential_definition, self._GetExistingCredentialDefinitionNames() )
self._credential_definitions.AddDatas( ( edited_credential_definition, ) )
else:
break
self._credential_definitions.Sort()
def _DoTest( self ):
def qt_add_result( test_result ):
if not self or not QP.isValid( self ):
return
self._test_listctrl.AddDatas( ( test_result, ) )
def receive_result( test_result ):
QP.CallAfter( qt_add_result, test_result )
def clean_up( final_result ):
if not self or not QP.isValid( self ):
return
QW.QMessageBox.information( self, 'Information', final_result )
self._final_test_result.setText( final_result )
self._test_button.setEnabled( True )
self._currently_testing = False
def do_it( login_script, domain, credentials, network_job_presentation_context_factory ):
try:
login_result = 'login did not finish'
# a potential here is to properly inform the login manager of the domain map and hence read back the invalidation text
# but I am catching the info in the raised exception, so nbd really, I think
bandwidth_manager = ClientNetworkingBandwidth.NetworkBandwidthManager()
session_manager = ClientNetworkingSessions.NetworkSessionManager()
domain_manager = HG.client_controller.network_engine.domain_manager.Duplicate() # keep custom headers from current domain stuff
login_manager = ClientNetworkingLogin.NetworkLoginManager()
network_engine = ClientNetworking.NetworkEngine( HG.client_controller, bandwidth_manager, session_manager, domain_manager, login_manager )
HG.client_controller.CallToThreadLongRunning( network_engine.MainLoop )
network_context = ClientNetworkingContexts.NetworkContext.STATICGenerateForDomain( domain )
login_result = login_script.Start( network_engine, network_context, credentials, network_job_presentation_context_factory = network_job_presentation_context_factory, test_result_callable = qt_add_result )
except Exception as e:
login_result = str( e )
HydrusData.ShowException( e )
finally:
network_engine.Shutdown()
QP.CallAfter( clean_up, login_result )
if self._currently_testing:
QW.QMessageBox.warning( self, 'Warning', 'Currently testing already! Please cancel current job!' )
return
try:
login_script = self.GetValue()
except HydrusExceptions.VetoException:
return
if self._test_domain == '':
example_domains = list( login_script.GetExampleDomains() )
example_domains.sort()
if len( example_domains ) > 0:
self._test_domain = example_domains[0]
with ClientGUIDialogs.DialogTextEntry( self, 'edit the domain', default = self._test_domain, allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
self._test_domain = dlg.GetValue()
else:
return
credential_definitions = login_script.GetCredentialDefinitions()
if len( credential_definitions ) > 0:
with ClientGUITopLevelWindows.DialogEdit( self, 'edit login' ) as dlg:
panel = EditLoginCredentialsPanel( dlg, credential_definitions, self._test_credentials )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
self._test_credentials = panel.GetValue()
else:
return
else:
self._test_credentials = {}
self._test_listctrl.DeleteDatas( self._test_listctrl.GetData() )
self._test_button.setEnabled( False )
network_job_presentation_context_factory = GenerateTestNetworkJobPresentationContextFactory( self, self._test_network_job_control )
self._currently_testing = True
HG.client_controller.CallToThread( do_it, login_script, self._test_domain, self._test_credentials, network_job_presentation_context_factory )
def _EditExampleDomainsInfo( self ):
selected_example_domains_info = self._example_domains_info.GetData( only_selected = True )
for example_domain_info in selected_example_domains_info:
( original_domain, access_type, access_text ) = example_domain_info
with ClientGUIDialogs.DialogTextEntry( self, 'edit the domain', default = original_domain, allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
domain = dlg.GetValue()
else:
break
existing_domains = self._GetExistingDomains()
if domain != original_domain and domain in existing_domains:
QW.QMessageBox.critical( self, 'Error', 'That domain already exists!' )
break
a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ]
choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ]
try:
new_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, value_to_select = access_type, sort_tuples = False )
except HydrusExceptions.CancelledException:
break
if new_access_type != access_type:
access_type = new_access_type
access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ access_type ]
with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = access_text, allow_blank = False ) as dlg:
if dlg.exec() == QW.QDialog.Accepted:
access_text = dlg.GetValue()
else:
break
self._example_domains_info.DeleteDatas( ( example_domain_info, ) )
edited_example_domain_info = ( domain, access_type, access_text )
self._example_domains_info.AddDatas( ( edited_example_domain_info, ) )
self._example_domains_info.Sort()
def _EditLoginStep( self, login_step ):
with ClientGUITopLevelWindows.DialogEdit( self, 'edit login step' ) as dlg:
panel = EditLoginStepPanel( dlg, login_step )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
login_step = panel.GetValue()
return login_step
else:
raise HydrusExceptions.VetoException()
def _GetExistingCredentialDefinitionNames( self ):
return { credential_definition.GetName() for credential_definition in self._credential_definitions.GetData() }
def _GetExistingDomains( self ):
return { domain for ( domain, access_type, access_text ) in self._example_domains_info.GetData() }
def _ReviewTestResult( self ):
for test_result in self._test_listctrl.GetData( only_selected = True ):
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, 'login test result' )
panel = ReviewTestResultPanel( frame, test_result )
frame.SetPanel( panel )
def GetValue( self ):
if self._currently_testing:
raise HydrusExceptions.VetoException( 'Currently testing! Please cancel it first!' )
name = self._name.text()
login_script_key = self._original_login_script.GetLoginScriptKey()
required_cookies_info = self._required_cookies_info.GetValue()
credential_definitions = self._credential_definitions.GetData()
login_steps = self._login_steps.GetData()
example_domains_info = self._example_domains_info.GetData()
credential_names = { credential_definition.GetName() for credential_definition in credential_definitions }
login_script = ClientNetworkingLogin.LoginScriptDomain( name = name, login_script_key = login_script_key, required_cookies_info = required_cookies_info, credential_definitions = credential_definitions, login_steps = login_steps, example_domains_info = example_domains_info )
login_script.SetLoginScriptKey( login_script_key )
try:
login_script.CheckIsValid()
except HydrusExceptions.ValidationException as e:
message = 'There is a problem with this script. The reason is:'
message += os.linesep * 2
message += str( e )
message += os.linesep * 2
message += 'Do you want to proceed with this invalid script, or go back and fix it?'
result = ClientGUIDialogsQuick.GetYesNo( self, message, yes_label = 'ok as invalid', no_label = 'go back' )
if result != QW.QDialog.Accepted:
raise HydrusExceptions.VetoException( 'The ok event has been cancelled!' )
return login_script
class EditLoginScriptsPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, login_scripts ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
login_scripts_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
columns = [ ( 'name', -1 ), ( 'example domains', 40 ) ]
self._login_scripts = ClientGUIListCtrl.BetterListCtrl( login_scripts_panel, 'login_scripts', 20, 24, columns, self._ConvertLoginScriptToListCtrlTuples, use_simple_delete = True, activation_callback = self._Edit )
login_scripts_panel.SetListCtrl( self._login_scripts )
login_scripts_panel.AddButton( 'add', self._Add )
login_scripts_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
login_scripts_panel.AddDeleteButton()
login_scripts_panel.AddSeparator()
login_scripts_panel.AddImportExportButtons( ( ClientNetworkingLogin.LoginScriptDomain, ), self._AddLoginScript )
login_scripts_panel.AddSeparator()
login_scripts_panel.AddDefaultsButton( ClientDefaults.GetDefaultLoginScripts, self._AddLoginScript )
#
self._login_scripts.AddDatas( login_scripts )
#
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, login_scripts_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.widget().setLayout( vbox )
def _Add( self ):
new_login_script = ClientNetworkingLogin.LoginScriptDomain()
with ClientGUITopLevelWindows.DialogEdit( self, 'edit login script', frame_key = 'deeply_nested_dialog' ) as dlg_edit:
panel = EditLoginScriptPanel( dlg_edit, new_login_script )
dlg_edit.SetPanel( panel )
if dlg_edit.exec() == QW.QDialog.Accepted:
new_login_script = panel.GetValue()
self._AddLoginScript( new_login_script )
self._login_scripts.Sort()
def _AddLoginScript( self, login_script ):
HydrusSerialisable.SetNonDupeName( login_script, self._GetExistingNames() )
login_script.RegenerateLoginScriptKey()
self._login_scripts.AddDatas( ( login_script, ) )
def _ConvertLoginScriptToListCtrlTuples( self, login_script ):
name = login_script.GetName()
example_domains = list( login_script.GetExampleDomains() )
example_domains.sort()
pretty_name = name
pretty_example_domains = ', '.join( example_domains )
display_tuple = ( pretty_name, pretty_example_domains )
sort_tuple = ( name, example_domains )
return ( display_tuple, sort_tuple )
def _Edit( self ):
login_scripts = self._login_scripts.GetData( only_selected = True )
for login_script in login_scripts:
with ClientGUITopLevelWindows.DialogEdit( self, 'edit login script', frame_key = 'deeply_nested_dialog' ) as dlg:
panel = EditLoginScriptPanel( dlg, login_script )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
edited_login_script = panel.GetValue()
self._login_scripts.DeleteDatas( ( login_script, ) )
HydrusSerialisable.SetNonDupeName( edited_login_script, self._GetExistingNames() )
self._login_scripts.AddDatas( ( edited_login_script, ) )
else:
break
self._login_scripts.Sort()
def _GetExistingNames( self ):
names = { login_script.GetName() for login_script in self._login_scripts.GetData() }
return names
def GetValue( self ):
return self._login_scripts.GetData()
class EditLoginStepPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, login_step ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
#
name = login_step.GetName()
( scheme, method, subdomain, path, required_credentials, static_args, temp_args, required_cookies_info, content_parsers ) = login_step.ToTuple()
#
self._name = QW.QLineEdit( self )
self._method = ClientGUICommon.BetterChoice( self )
self._method.addItem( 'GET', 'GET' )
self._method.addItem( 'POST', 'POST' )
self._scheme = ClientGUICommon.BetterChoice( self )
self._scheme.addItem( 'http', 'http' )
self._scheme.addItem( 'https', 'https' )
self._subdomain = ClientGUICommon.NoneableTextCtrl( self, none_phrase = 'none' )
self._path = QW.QLineEdit( self )
required_credentials_panel = ClientGUICommon.StaticBox( self, 'credentials to send' )
self._required_credentials = ClientGUIControls.StringToStringDictControl( required_credentials_panel, required_credentials, min_height = 4, key_name = 'credential name', value_name = 'parameter name' )
#
static_args_panel = ClientGUICommon.StaticBox( self, 'static variables to send' )
self._static_args = ClientGUIControls.StringToStringDictControl( static_args_panel, static_args, min_height = 4, key_name = 'parameter name', value_name = 'value' )
#
temp_args_panel = ClientGUICommon.StaticBox( self, 'temporary variables to send' )
self._temp_args = ClientGUIControls.StringToStringDictControl( temp_args_panel, temp_args, min_height = 4, key_name = 'temp variable name', value_name = 'parameter name' )
#
required_cookies_info_box_panel = ClientGUICommon.StaticBox( self, 'cookies required to consider step successful' )
self._required_cookies_info = ClientGUIControls.StringMatchToStringMatchDictControl( required_cookies_info_box_panel, required_cookies_info, min_height = 4, key_name = 'cookie name' )
#
content_parsers_panel = ClientGUICommon.StaticBox( self, 'content parsers' )
test_context_callable = lambda: ( {}, '' )
permitted_content_types = [ HC.CONTENT_TYPE_VARIABLE, HC.CONTENT_TYPE_VETO ]
self._content_parsers = ClientGUIParsing.EditContentParsersPanel( content_parsers_panel, test_context_callable, permitted_content_types )
# a test panel a la pageparsers
#
self._name.setText( name )
self._scheme.SetValue( scheme )
self._method.SetValue( method )
self._subdomain.SetValue( subdomain )
self._path.setText( path )
self._content_parsers.AddDatas( content_parsers )
#
required_credentials_panel.Add( self._required_credentials, CC.FLAGS_EXPAND_BOTH_WAYS )
static_args_panel.Add( self._static_args, CC.FLAGS_EXPAND_BOTH_WAYS )
temp_args_panel.Add( self._temp_args, CC.FLAGS_EXPAND_BOTH_WAYS )
required_cookies_info_box_panel.Add( self._required_cookies_info, CC.FLAGS_EXPAND_BOTH_WAYS )
content_parsers_panel.Add( self._content_parsers, CC.FLAGS_EXPAND_BOTH_WAYS )
#
rows = []
rows.append( ( 'name: ', self._name ) )
rows.append( ( 'scheme: ', self._scheme ) )
rows.append( ( 'method: ', self._method ) )
rows.append( ( 'subdomain (replaces www, if present): ', self._subdomain ) )
rows.append( ( 'path: ', self._path ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, required_credentials_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, static_args_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, temp_args_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, required_cookies_info_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, content_parsers_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.widget().setLayout( vbox )
def GetValue( self ):
name = self._name.text()
scheme = self._scheme.GetValue()
method = self._method.GetValue()
subdomain = self._subdomain.GetValue()
path = self._path.text()
required_credentials = self._required_credentials.GetValue()
static_args = self._static_args.GetValue()
temp_args = self._temp_args.GetValue()
required_cookies_info = self._required_cookies_info.GetValue()
content_parsers = self._content_parsers.GetData()
if subdomain == '':
subdomain = None
login_step = ClientNetworkingLogin.LoginStep( name = name, scheme = scheme, method = method, subdomain = subdomain, path = path )
login_step.SetComplicatedVariables( required_credentials, static_args, temp_args, required_cookies_info, content_parsers )
return login_step