hydrus/hydrus/client/gui/networking/ClientGUIHydrusNetwork.py

1376 lines
48 KiB
Python

import os
import typing
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusExceptions
from hydrus.core import HydrusGlobals as HG
from hydrus.core.networking import HydrusNetwork
from hydrus.core.networking import HydrusNetworking
from hydrus.client import ClientConstants as CC
from hydrus.client.gui import ClientGUIAsync
from hydrus.client.gui import ClientGUIDialogs
from hydrus.client.gui import ClientGUIDialogsQuick
from hydrus.client.gui import ClientGUIFunctions
from hydrus.client.gui import ClientGUIScrolledPanels
from hydrus.client.gui import ClientGUITime
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
from hydrus.client.gui import QtPorting as QP
from hydrus.client.gui.lists import ClientGUIListBoxes
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
from hydrus.client.gui.lists import ClientGUIListCtrl
from hydrus.client.gui.widgets import ClientGUICommon
from hydrus.client.gui.widgets import ClientGUIControls
class EditAccountTypePanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent: QW.QWidget, service_type: int, account_type: HydrusNetwork.AccountType ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
self._account_type_key = account_type.GetAccountTypeKey()
title = account_type.GetTitle()
permissions = account_type.GetPermissions()
bandwidth_rules = account_type.GetBandwidthRules()
auto_create_velocity = account_type.GetAutoCreateAccountVelocity()
self._auto_create_history = account_type.GetAutoCreateAccountHistory()
self._title = QW.QLineEdit( self )
permission_choices = self._GeneratePermissionChoices( service_type )
self._permission_controls = []
self._permissions_panel = ClientGUICommon.StaticBox( self, 'permissions' )
gridbox_rows = []
for ( content_type, action_rows ) in permission_choices:
choice_control = ClientGUICommon.BetterChoice( self._permissions_panel )
for ( label, action ) in action_rows:
choice_control.addItem( label, ( content_type, action ) )
if content_type in permissions:
selection_row = ( content_type, permissions[ content_type ] )
else:
selection_row = ( content_type, None )
try:
choice_control.SetValue( selection_row )
except:
choice_control.SetValue( ( content_type, None ) )
self._permission_controls.append( choice_control )
gridbox_label = HC.content_type_string_lookup[ content_type ]
gridbox_rows.append( ( gridbox_label, choice_control ) )
gridbox = ClientGUICommon.WrapInGrid( self._permissions_panel, gridbox_rows )
self._bandwidth_rules_control = ClientGUIControls.BandwidthRulesCtrl( self, bandwidth_rules )
self._auto_creation_box = ClientGUICommon.StaticBox( self, 'automatic account creation' )
min_unit_value = 0
max_unit_value = 65565
min_time_delta = 60 * 60
self._auto_create_velocity_control = ClientGUITime.VelocityCtrl( self._auto_creation_box, min_unit_value, max_unit_value, min_time_delta, days = True, hours = True, unit = 'accounts' )
self._auto_create_history_st = ClientGUICommon.BetterStaticText( self._auto_creation_box, label = 'initialising' )
#
self._title.setText( title )
self._auto_create_velocity_control.SetValue( auto_create_velocity )
#
intro = 'If you wish, you can allow new users to create their own accounts. They will be limited to a certain number over a particular time.'
intro += os.linesep * 2
intro += 'Set to 0 to disable auto-creation.'
st = ClientGUICommon.BetterStaticText( self._auto_creation_box, label = intro )
st.setWordWrap( True )
self._auto_creation_box.Add( st, CC.FLAGS_EXPAND_PERPENDICULAR )
self._auto_creation_box.Add( self._auto_create_history_st, CC.FLAGS_EXPAND_PERPENDICULAR )
self._auto_creation_box.Add( self._auto_create_velocity_control, CC.FLAGS_EXPAND_PERPENDICULAR )
t_hbox = ClientGUICommon.WrapInText( self._title, self, 'title: ' )
self._permissions_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, t_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, self._permissions_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._bandwidth_rules_control, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._auto_creation_box, CC.FLAGS_EXPAND_PERPENDICULAR )
self.widget().setLayout( vbox )
#
self._UpdateAutoCreationHistoryText()
self._auto_create_velocity_control.velocityChanged.connect( self._UpdateAutoCreationHistoryText )
def _GeneratePermissionChoices( self, service_type ):
possible_permissions = HydrusNetwork.GetPossiblePermissions( service_type )
permission_choices = []
for ( content_type, possible_actions ) in possible_permissions:
choices = []
for action in possible_actions:
choices.append( ( HC.permission_pair_string_lookup[ ( content_type, action ) ], action ) )
permission_choices.append( ( content_type, choices ) )
return permission_choices
def _UpdateAutoCreationHistoryText( self ):
( accounts_per_time_delta, time_delta ) = self._auto_create_velocity_control.GetValue()
text = ''
if accounts_per_time_delta == 0:
text = 'Auto-creation disabled. '
num_created = self._auto_create_history.GetUsage( HC.BANDWIDTH_TYPE_DATA, time_delta )
text += '{} auto-created in the past {}.'.format( HydrusData.ToHumanInt( num_created ), HydrusData.TimeDeltaToPrettyTimeDelta( time_delta ) )
self._auto_create_history_st.setText( text )
def GetValue( self ) -> HydrusNetwork.AccountType:
title = self._title.text()
permissions = {}
for permission_control in self._permission_controls:
( content_type, action ) = permission_control.GetValue()
if action is not None:
permissions[ content_type ] = action
bandwidth_rules = self._bandwidth_rules_control.GetValue()
auto_creation_velocity = self._auto_create_velocity_control.GetValue()
return HydrusNetwork.AccountType(
account_type_key = self._account_type_key,
title = title,
permissions = permissions,
bandwidth_rules = bandwidth_rules,
auto_creation_velocity = auto_creation_velocity,
auto_creation_history = self._auto_create_history
)
class EditAccountTypesPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, service_type, account_types ):
self._service_type = service_type
self._original_account_types = account_types
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
self._deletee_account_type_keys_to_new_account_type_keys = {}
self._account_types_listctrl = ClientGUIListCtrl.BetterListCtrl( self, CGLC.COLUMN_LIST_ACCOUNT_TYPES.ID, 20, self._ConvertAccountTypeToTuples, delete_key_callback = self._Delete, activation_callback = self._Edit )
self._add_button = ClientGUICommon.BetterButton( self, 'add', self._Add )
self._edit_button = ClientGUICommon.BetterButton( self, 'edit', self._Edit )
self._delete_button = ClientGUICommon.BetterButton( self, 'delete', self._Delete )
self._account_types_listctrl.AddDatas( self._original_account_types )
hbox = QP.HBoxLayout()
QP.AddToLayout( hbox, self._add_button, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._edit_button, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._delete_button, CC.FLAGS_CENTER_PERPENDICULAR )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, self._account_types_listctrl, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, hbox, CC.FLAGS_ON_RIGHT )
self.widget().setLayout( vbox )
def _Add( self ):
title = 'new account type'
permissions = {}
bandwidth_rules = HydrusNetworking.BandwidthRules()
account_type = HydrusNetwork.AccountType.GenerateNewAccountType( title, permissions, bandwidth_rules )
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit account type' ) as dlg_edit:
panel = EditAccountTypePanel( dlg_edit, self._service_type, account_type )
dlg_edit.SetPanel( panel )
if dlg_edit.exec() == QW.QDialog.Accepted:
new_account_type = panel.GetValue()
self._account_types_listctrl.AddDatas( ( new_account_type, ) )
def _ConvertAccountTypeToTuples( self, account_type: HydrusNetwork.AccountType ):
title = account_type.GetTitle()
if account_type.IsNullAccount():
title = '{} (cannot be edited)'.format( title )
display_tuple = ( title, )
sort_tuple = ( title, )
return ( display_tuple, sort_tuple )
def _Delete( self ):
result = ClientGUIDialogsQuick.GetYesNo( self, 'Remove all selected?' )
if result == QW.QDialog.Accepted:
account_types_about_to_delete = self._account_types_listctrl.GetData( only_selected = True )
if True in ( at.IsNullAccount() for at in account_types_about_to_delete ):
QW.QMessageBox.critical( self, 'Error', 'You cannot delete the null account type!' )
return
all_real_account_types = set( [ at for at in self._account_types_listctrl.GetData() if not at.IsNullAccount() ] )
account_types_can_move_to = all_real_account_types.difference( account_types_about_to_delete )
if len( account_types_can_move_to ) == 0:
QW.QMessageBox.critical( self, 'Error', 'You cannot delete every account type!' )
return
for deletee_account_type in account_types_about_to_delete:
if len( account_types_can_move_to ) > 1:
deletee_title = deletee_account_type.GetTitle()
choice_tuples = [ ( account_type.GetTitle(), account_type ) for account_type in account_types_can_move_to ]
try:
new_account_type = ClientGUIDialogsQuick.SelectFromList( self, 'what should deleted ' + deletee_title + ' accounts become?', choice_tuples )
except HydrusExceptions.CancelledException:
return
else:
( new_account_type, ) = account_types_can_move_to
deletee_account_type_key = deletee_account_type.GetAccountTypeKey()
new_account_type_key = new_account_type.GetAccountTypeKey()
self._deletee_account_type_keys_to_new_account_type_keys[ deletee_account_type_key ] = new_account_type_key
self._account_types_listctrl.DeleteSelected()
def _Edit( self ):
datas = self._account_types_listctrl.GetData( only_selected = True )
if True in ( at.IsNullAccount() for at in datas ):
QW.QMessageBox.critical( self, 'Error', 'You cannot edit the null account type!' )
return
for account_type in datas:
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit account type' ) as dlg_edit:
panel = EditAccountTypePanel( dlg_edit, self._service_type, account_type )
dlg_edit.SetPanel( panel )
if dlg_edit.exec() == QW.QDialog.Accepted:
edited_account_type = panel.GetValue()
self._account_types_listctrl.ReplaceData( account_type, edited_account_type )
else:
return
def GetValue( self ):
account_types = [ at for at in self._original_account_types if at.IsNullAccount() ]
account_types.extend( [ at for at in self._account_types_listctrl.GetData() if not at.IsNullAccount() ] )
def key_transfer_not_collapsed():
keys = set( self._deletee_account_type_keys_to_new_account_type_keys.keys() )
values = set( self._deletee_account_type_keys_to_new_account_type_keys.values() )
return HydrusData.SetsIntersect( keys, values )
while key_transfer_not_collapsed():
# some deletees are going to other deletees, so lets collapse
deletee_account_type_keys = set( self._deletee_account_type_keys_to_new_account_type_keys.keys() )
account_type_keys_tuples = list(self._deletee_account_type_keys_to_new_account_type_keys.items())
for ( deletee_account_type_key, new_account_type_key ) in account_type_keys_tuples:
if new_account_type_key in deletee_account_type_keys:
better_new_account_type_key = self._deletee_account_type_keys_to_new_account_type_keys[ new_account_type_key ]
self._deletee_account_type_keys_to_new_account_type_keys[ deletee_account_type_key ] = better_new_account_type_key
return ( account_types, self._deletee_account_type_keys_to_new_account_type_keys )
class ListAccountsPanel( ClientGUIScrolledPanels.ReviewPanel ):
def __init__( self, parent: QW.QWidget, service_key: bytes, accounts: typing.Collection[ HydrusNetwork.Account ] ):
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )
self._service_key = service_key
self._service = HG.client_controller.services_manager.GetService( self._service_key )
self._accounts = accounts
self._accounts_box = ClientGUICommon.StaticBox( self, 'accounts' )
self._account_list = ClientGUIListBoxes.BetterQListWidget( self._accounts_box )
self._account_list.setSelectionMode( QW.QListWidget.ExtendedSelection )
( min_width, min_height ) = ClientGUIFunctions.ConvertTextToPixels( self._account_list, ( 74, 16 ) )
self._account_list.setMinimumSize( min_width, min_height )
modify_button = ClientGUICommon.BetterButton( self._accounts_box, 'modify selected', self._ModifyAccounts )
#
my_admin_account_key = self._service.GetAccount().GetAccountKey()
accounts.sort( key = lambda a: ( a.GetAccountType().GetTitle(), a.GetAccountKey().hex() ) )
for account in accounts:
item = QW.QListWidgetItem()
account_key = account.GetAccountKey()
text = account.GetSingleLineTitle()
if account_key == my_admin_account_key:
text = 'THIS IS YOU: {}'.format( text )
item.setText( text )
item.setData( QC.Qt.UserRole, account )
self._account_list.addItem( item )
#
self._accounts_box.Add( self._account_list, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, self._accounts_box, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, modify_button, CC.FLAGS_EXPAND_PERPENDICULAR )
self.widget().setLayout( vbox )
#
self._account_list.itemDoubleClicked.connect( self._ModifyAccounts )
def _ModifyAccounts( self ):
accounts = self._account_list.GetData( only_selected = True )
if len( accounts ) > 0:
subject_account_identifiers = [ HydrusNetwork.AccountIdentifier( account_key = account.GetAccountKey() ) for account in accounts ]
frame = ClientGUITopLevelWindowsPanels.FrameThatTakesScrollablePanel( self.window().parentWidget(), 'manage accounts' )
panel = ModifyAccountsPanel( frame, self._service_key, subject_account_identifiers )
frame.SetPanel( panel )
class ReviewAccountsPanel( QW.QWidget ):
accountsFetchFinished = QC.Signal()
accountsFetchStarted = QC.Signal()
def __init__( self, parent: QW.QWidget, service_key: bytes, account_identifiers: typing.Collection[ HydrusNetwork.AccountIdentifier ] ):
QW.QWidget.__init__( self, parent )
self._service_key = service_key
self._service = HG.client_controller.services_manager.GetService( self._service_key )
self._account_identifiers = account_identifiers
self._done_first_fetch = False
self._accounts_loaded = False
self._account_keys_to_accounts = {}
self._account_keys_to_account_info = {}
self._accounts_box = ClientGUICommon.StaticBox( self, 'accounts' )
self._status_st = ClientGUICommon.BetterStaticText( self._accounts_box )
self._account_list = ClientGUICommon.BetterCheckBoxList( self._accounts_box )
self._account_list.setSelectionMode( QW.QAbstractItemView.SingleSelection )
( min_width, min_height ) = ClientGUIFunctions.ConvertTextToPixels( self._account_list, ( 74, 6 ) )
self._account_list.setMinimumSize( min_width, min_height )
self._account_info_box = QW.QTextEdit( self._accounts_box )
self._account_info_box.setReadOnly( True )
( min_width, min_height ) = ClientGUIFunctions.ConvertTextToPixels( self._account_info_box, ( 16, 8 ) )
self._account_info_box.setMinimumHeight( min_height )
self._copy_checked_account_keys_button = ClientGUICommon.BetterButton( self._accounts_box, 'copy checked account ids', self._CopyCheckedAccountKeys )
#
self._accounts_box.Add( self._status_st, CC.FLAGS_EXPAND_PERPENDICULAR )
self._accounts_box.Add( self._account_list, CC.FLAGS_EXPAND_BOTH_WAYS )
self._accounts_box.Add( self._account_info_box, CC.FLAGS_EXPAND_BOTH_WAYS )
self._accounts_box.Add( self._copy_checked_account_keys_button, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, self._accounts_box, CC.FLAGS_EXPAND_BOTH_WAYS )
self.setLayout( vbox )
#
self._account_list.itemClicked.connect( self._AccountClicked )
def _AccountClicked( self ):
selected_indices = sorted( self._account_list.GetSelectedIndices() )
if len( selected_indices ) > 0:
item = self._account_list.item( selected_indices[0] )
account_info_components = []
account_key = item.data( QC.Qt.UserRole )
my_admin_account = self._service.GetAccount()
if account_key == my_admin_account.GetAccountKey():
account_info_components.append( 'THIS IS YOU' )
if account_key in self._account_keys_to_accounts:
account = self._account_keys_to_accounts[ account_key ]
account_info_components.append( account.ToString() )
account_info_components.append( account.GetExpiresString() )
account_info_components.append( account.GetStatusInfo()[1] )
( message, message_created ) = account.GetMessageAndTimestamp()
if message != '':
account_info_components.append( 'Message: {}'.format( message ) )
else:
account_info_components.append( 'No message set.' )
if account_key in self._account_keys_to_account_info:
account_info_components.append( self._account_keys_to_account_info[ account_key ] )
else:
account_info_components.append( 'Could not find info for this account!' )
else:
account_info_components.append( 'Could not find this account!' )
joiner = os.linesep * 2
account_info = joiner.join( account_info_components )
else:
account_info = ''
self._account_info_box.setText( account_info )
def _CopyCheckedAccountKeys( self ):
checked_account_keys = self.GetCheckedAccountKeys()
if len( checked_account_keys ) > 0:
account_keys_text = os.linesep.join( ( account_key.hex() for account_key in checked_account_keys ) )
HG.client_controller.pub( 'clipboard', 'text', account_keys_text )
def _RefreshAccounts( self ):
# TODO: so, rework this guy, and modifyaccounts parent, to not hold account_identifiers, but account_keys. have an async lookup convert contents to account keys before launching this guy
account_identifiers = self._account_identifiers
service = self._service
selected_account_keys = self.GetCheckedAccountKeys()
def work_callable():
account_errors = set()
account_keys_to_accounts = {}
account_keys_to_account_info = {}
for account_identifier in account_identifiers:
try:
result = service.Request( HC.GET, 'other_account', { 'subject_identifier' : account_identifier } )
except Exception as e:
account_errors.add( str( e ) )
continue
if 'account' in result:
account = result[ 'account' ]
subject_account_key = account.GetAccountKey()
if subject_account_key in account_keys_to_accounts:
continue
account_keys_to_accounts[ subject_account_key ] = account
try:
response = self._service.Request( HC.GET, 'account_info', { 'subject_account_key' : subject_account_key } )
except Exception as e:
HydrusData.PrintException( e )
continue
account_string = str( response[ 'account_info' ] )
account_keys_to_account_info[ subject_account_key ] = account_string
return ( account_keys_to_accounts, account_keys_to_account_info, account_errors )
def publish_callable( result ):
( self._account_keys_to_accounts, self._account_keys_to_account_info, account_errors ) = result
if len( account_errors ) > 0:
account_errors = sorted( account_errors )
QW.QMessageBox.information( self, 'Information', 'Errors were encountered during account fetch:{}{}'.format( os.linesep * 2, os.linesep.join( account_errors ) ) )
if not self._done_first_fetch:
# if we launched with CPU-expensive mapping identifiers, let's move to nice account ids for future refreshes
self._account_identifiers = [ HydrusNetwork.AccountIdentifier( account_key = account_key ) for account_key in self._account_keys_to_accounts.keys() ]
#
account_keys_sorted = sorted(
list( self._account_keys_to_accounts.keys() ),
key = lambda sak: ( self._account_keys_to_accounts[ sak ].GetAccountType().GetTitle(), sak.hex() )
)
my_admin_account = self._service.GetAccount()
my_admin_account_key = my_admin_account.GetAccountKey()
for account_key in account_keys_sorted:
item = QW.QListWidgetItem()
item.setFlags( item.flags() | QC.Qt.ItemIsUserCheckable )
account = self._account_keys_to_accounts[ account_key ]
text = account.GetSingleLineTitle()
if account_key == my_admin_account_key:
text = 'THIS IS YOU: {}'.format( text )
item.setText( text )
if not self._done_first_fetch or account_key in selected_account_keys:
item.setCheckState( QC.Qt.Checked )
else:
item.setCheckState( QC.Qt.Unchecked )
item.setData( QC.Qt.UserRole, account_key )
self._account_list.addItem( item )
#
self._status_st.setVisible( False )
self._status_st.setText( '' )
if self._account_list.count() > 0:
self._account_list.item( 0 ).setSelected( True )
self._AccountClicked()
self._accounts_loaded = True
self._done_first_fetch = True
self.accountsFetchFinished.emit()
self._status_st.setVisible( True )
self._status_st.setText( 'fetching accounts\u2026' )
self._accounts_loaded = False
self._account_list.clear()
self._account_info_box.clear()
self._account_keys_to_accounts = {}
self._account_keys_to_account_info = {}
self.accountsFetchStarted.emit()
job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
job.start()
def AccountsLoaded( self ):
return self._accounts_loaded
def GetCheckedAccountKeys( self ):
account_keys = set()
for i in range( self._account_list.count() ):
item = self._account_list.item( i )
if item.checkState() == QC.Qt.Checked:
account_keys.add( item.data( QC.Qt.UserRole ) )
return account_keys
def GetCheckedAccounts( self ):
checked_account_keys = self.GetCheckedAccountKeys()
return { self._account_keys_to_accounts[ account_key ] for account_key in checked_account_keys }
def RefreshAccounts( self ):
self._RefreshAccounts()
def UncheckAccountKey( self, account_key: bytes ):
for i in range( self._account_list.count() ):
item = self._account_list.item( i )
checked_account_key = item.data( QC.Qt.UserRole )
if checked_account_key == account_key:
item.setCheckState( QC.Qt.Unchecked )
return
def UncheckNullAccount( self ):
for i in range( self._account_list.count() ):
item = self._account_list.item( i )
account_key = item.data( QC.Qt.UserRole )
account = self._account_keys_to_accounts[ account_key ]
if account.IsNullAccount():
item.setCheckState( QC.Qt.Unchecked )
return
class ModifyAccountsPanel( ClientGUIScrolledPanels.ReviewPanel ):
def __init__( self, parent: QW.QWidget, service_key: bytes, subject_identifiers: typing.Collection[ HydrusNetwork.AccountIdentifier ] ):
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )
self._service_key = service_key
self._service = HG.client_controller.services_manager.GetService( service_key )
self._subject_identifiers = subject_identifiers
self._account_types = []
#
self._account_panel = ReviewAccountsPanel( self, service_key, subject_identifiers )
#
self._message_panel = ClientGUICommon.StaticBox( self, 'message' )
self._message_text = QW.QLineEdit( self._message_panel )
self._set_message_button = ClientGUICommon.BetterButton( self._message_panel, 'set message', self._DoSetMessage )
#
self._expiration_panel = ClientGUICommon.StaticBox( self, 'change expiration' )
self._add_to_expires = ClientGUICommon.BetterChoice( self._expiration_panel )
for ( label, value ) in HC.lifetimes:
if value is not None:
self._add_to_expires.addItem( label, value )
self._add_to_expires_button = ClientGUICommon.BetterButton( self._expiration_panel, 'ok', self._AddToExpires )
self._add_to_expires_button.SetYesNoText( 'Add time to account expiration?' )
self._set_expires = ClientGUICommon.BetterChoice( self._expiration_panel )
for ( label, value ) in HC.lifetimes:
self._set_expires.addItem( label, value )
self._set_expires_button = ClientGUICommon.BetterButton( self._expiration_panel, 'ok', self._SetExpires )
self._set_expires_button.SetYesNoText( 'Set new account expiration?' )
#
self._account_types_panel = ClientGUICommon.StaticBox( self, 'account types' )
self._account_types_choice = ClientGUICommon.BetterChoice( self._account_types_panel )
self._account_types_button = ClientGUICommon.BetterButton( self._account_types_panel, 'ok', self._DoAccountType )
self._account_types_button.SetYesNoText( 'Set new account type?' )
#
self._ban_panel = ClientGUICommon.StaticBox( self, 'bans' )
self._ban_reason = QW.QLineEdit( self._ban_panel )
self._ban_expires = ClientGUICommon.BetterChoice( self._ban_panel )
for ( label, value ) in HC.lifetimes:
self._ban_expires.addItem( label, value )
self._ban_button = ClientGUICommon.BetterButton( self._ban_panel, 'ban account', self._DoBan )
self._unban_button = ClientGUICommon.BetterButton( self._ban_panel, 'unban account', self._DoUnban )
self._unban_button.SetYesNoText( 'Unban the account(s)?' )
#
self._add_to_expires.SetValue( 3 * 31 * 86400 ) # three months
self._set_expires.SetValue( 3 * 31 * 86400 ) # three months
#
gridbox_rows = []
gridbox_rows.append( ( 'message:', self._message_text ) )
gridbox = ClientGUICommon.WrapInGrid( self._message_panel, gridbox_rows )
self._message_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._message_panel.Add( self._set_message_button, CC.FLAGS_EXPAND_PERPENDICULAR )
add_to_expires_box = QP.HBoxLayout()
QP.AddToLayout( add_to_expires_box, QW.QLabel( 'add to expires: ', self._expiration_panel ), CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( add_to_expires_box, self._add_to_expires, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( add_to_expires_box, self._add_to_expires_button, CC.FLAGS_CENTER_PERPENDICULAR )
set_expires_box = QP.HBoxLayout()
QP.AddToLayout( set_expires_box, QW.QLabel( 'set expires to: ', self._expiration_panel ), CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( set_expires_box, self._set_expires, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( set_expires_box, self._set_expires_button, CC.FLAGS_CENTER_PERPENDICULAR )
self._expiration_panel.Add( add_to_expires_box, CC.FLAGS_EXPAND_PERPENDICULAR )
self._expiration_panel.Add( set_expires_box, CC.FLAGS_EXPAND_PERPENDICULAR )
account_types_hbox = QP.HBoxLayout()
QP.AddToLayout( account_types_hbox, self._account_types_choice, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( account_types_hbox, self._account_types_button, CC.FLAGS_CENTER_PERPENDICULAR )
self._account_types_panel.Add( account_types_hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
gridbox_rows = []
gridbox_rows.append( ( 'ban reason:', self._ban_reason ) )
gridbox_rows.append( ( 'ban expiration:', self._ban_expires ) )
gridbox = ClientGUICommon.WrapInGrid( self._ban_panel, gridbox_rows )
self._ban_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._ban_panel.Add( self._ban_button, CC.FLAGS_EXPAND_PERPENDICULAR )
self._ban_panel.Add( self._unban_button, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox = QP.VBoxLayout()
self._modification_status = ClientGUICommon.BetterStaticText( self )
QP.AddToLayout( vbox, self._modification_status, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._account_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, self._message_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._account_types_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._expiration_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._ban_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
self.widget().setLayout( vbox )
#
self._account_panel.accountsFetchStarted.connect( self._DisableUIForRefresh )
self._account_panel.accountsFetchFinished.connect( self._EnableUIAfterRefresh )
#
self._InitialiseAccountTypes()
self._account_panel.RefreshAccounts()
def _AddToExpires( self ):
expires_delta = self._add_to_expires.GetValue()
self._account_panel.UncheckNullAccount()
subject_accounts = self._account_panel.GetCheckedAccounts()
num_unchecked = 0
for subject_account in subject_accounts:
if subject_account.GetExpires() is None:
self._account_panel.UncheckAccountKey( subject_account.GetAccountKey() )
num_unchecked += 1
QW.QMessageBox.information( self, 'Information', '{} accounts do not expire, so could not have time added!'.format( HydrusData.ToHumanInt( num_unchecked ) ) )
subject_accounts = self._account_panel.GetCheckedAccounts()
subject_account_keys_and_current_expires = [ ( subject_account.GetAccountKey(), subject_account.GetExpires() ) for subject_account in subject_accounts ]
subject_account_keys_and_new_expires = [ ( subject_account_key, current_expires + expires_delta ) for ( subject_account_key, current_expires ) in subject_account_keys_and_current_expires ]
self._DoExpires( subject_account_keys_and_new_expires )
def _DisableUIForJob( self, message ):
self.setEnabled( False )
self._modification_status.setVisible( True )
self._modification_status.setText( message )
def _DisableUIForRefresh( self ):
self._DisableUIForJob( 'refreshing accounts\u2026' )
def _DoAccountType( self ):
self._account_panel.UncheckNullAccount()
subject_account_keys = self._account_panel.GetCheckedAccountKeys()
if len( subject_account_keys ) == 0:
QW.QMessageBox.information( self, 'Information', 'No accounts selected for action!' )
return
service = self._service
account_type = self._account_types_choice.GetValue()
account_type_key = account_type.GetAccountTypeKey()
def work_callable():
for subject_account_key in subject_account_keys:
service.Request( HC.POST, 'modify_account_account_type', { 'subject_account_key' : subject_account_key, 'account_type_key' : account_type_key } )
return 1
def publish_callable( gumpf ):
QW.QMessageBox.information( self, 'Information', 'Done!' )
self._account_panel.RefreshAccounts()
self._DisableUIForJob( 'setting new account type\u2026' )
job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
job.start()
def _DoBan( self ):
self._account_panel.UncheckNullAccount()
subject_accounts = self._account_panel.GetCheckedAccounts()
if len( subject_accounts ) == 0:
QW.QMessageBox.information( self, 'Information', 'No accounts selected for action!' )
return
some_are_banned = True in ( subject_account.IsBanned() for subject_account in subject_accounts )
if some_are_banned:
message = 'Some of these selected accounts are already banned. Sure you want to overwrite the bans?'
result = ClientGUIDialogsQuick.GetYesNo( self, message )
if result != QW.QDialog.Accepted:
return
subject_account_keys = [ subject_account.GetAccountKey() for subject_account in subject_accounts ]
reason = self._ban_reason.text()
if reason == '':
QW.QMessageBox.information( self, 'Information', 'The ban reason is empty!' )
return
message = 'Ban these user(s)? All of their pending petitions will be deleted serverside.'
result = ClientGUIDialogsQuick.GetYesNo( self, message )
if result != QW.QDialog.Accepted:
return
expires = self._ban_expires.GetValue()
if expires is not None:
expires += HydrusData.GetNow()
service = self._service
def work_callable():
for subject_account_key in subject_account_keys:
service.Request( HC.POST, 'modify_account_ban', { 'subject_account_key' : subject_account_key, 'reason' : reason, 'expires' : expires } )
return 1
def publish_callable( gumpf ):
QW.QMessageBox.information( self, 'Information', 'Done!' )
self._account_panel.RefreshAccounts()
self._DisableUIForJob( 'banning\u2026' )
job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
job.start()
def _DoExpires( self, subject_account_keys_and_new_expires ):
if len( subject_account_keys_and_new_expires ) == 0:
QW.QMessageBox.information( self, 'Information', 'No accounts selected for action!' )
return
service = self._service
def work_callable():
for ( subject_account_key, new_expires ) in subject_account_keys_and_new_expires:
service.Request( HC.POST, 'modify_account_expires', { 'subject_account_key' : subject_account_key, 'expires' : new_expires } )
return 1
def publish_callable( gumpf ):
QW.QMessageBox.information( self, 'Information', 'Done!' )
self._account_panel.RefreshAccounts()
self._DisableUIForJob( 'setting new expiry\u2026' )
job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
job.start()
def _DoSetMessage( self ):
self._account_panel.UncheckNullAccount()
subject_accounts = self._account_panel.GetCheckedAccounts()
if len( subject_accounts ) == 0:
QW.QMessageBox.information( self, 'Information', 'No accounts selected for action!' )
return
message = self._message_text.text()
if message == '':
yn_message = 'Clear message for the selected accounts?'
else:
yn_message = 'Set this message for the selected accounts?'
result = ClientGUIDialogsQuick.GetYesNo( self, yn_message )
if result != QW.QDialog.Accepted:
return
subject_account_keys = [ subject_account.GetAccountKey() for subject_account in subject_accounts ]
service = self._service
def work_callable():
for subject_account_key in subject_account_keys:
service.Request( HC.POST, 'modify_account_set_message', { 'subject_account_key' : subject_account_key, 'message': message } )
return 1
def publish_callable( gumpf ):
QW.QMessageBox.information( self, 'Information', 'Done!' )
self._account_panel.RefreshAccounts()
self._DisableUIForJob( 'setting message\u2026' )
job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
job.start()
def _DoUnban( self ):
self._account_panel.UncheckNullAccount()
subject_accounts = self._account_panel.GetCheckedAccounts()
if len( subject_accounts ) == 0:
QW.QMessageBox.information( self, 'Information', 'No accounts selected for action!' )
return
subject_accounts = [ subject_account for subject_account in subject_accounts if subject_account.IsBanned() ]
if len( subject_accounts ) == 0:
QW.QMessageBox.information( self, 'Information', 'None of the selected accounts are banned!' )
return
subject_account_keys = [ subject_account.GetAccountKey() for subject_account in subject_accounts ]
service = self._service
def work_callable():
for subject_account_key in subject_account_keys:
service.Request( HC.POST, 'modify_account_unban', { 'subject_account_key' : subject_account_key } )
return 1
def publish_callable( gumpf ):
QW.QMessageBox.information( self, 'Information', 'Done!' )
self._account_panel.RefreshAccounts()
self._DisableUIForJob( 'unbanning\u2026' )
job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
job.start()
def _EnableUIAfterRefresh( self ):
self._modification_status.setVisible( False )
self._modification_status.setText( '' )
self.setEnabled( True )
def _InitialiseAccountTypes( self ):
service = self._service
def work_callable():
response = service.Request( HC.GET, 'account_types' )
account_types = response[ 'account_types' ]
return account_types
def publish_callable( result ):
self._account_types = result
self._account_types_choice.setEnabled( True )
self._account_types_button.setEnabled( True )
self._account_types.sort( key = lambda at: str( at ) )
for account_type in self._account_types:
self._account_types_choice.addItem( str( account_type ), account_type )
self._account_types_choice.setEnabled( False )
self._account_types_button.setEnabled( False )
job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
job.start()
def _SetExpires( self ):
self._account_panel.UncheckNullAccount()
expires = self._set_expires.GetValue()
if expires is not None:
expires += HydrusData.GetNow()
subject_account_keys_and_new_expires = [ ( subject_account_key, expires ) for subject_account_key in self._account_panel.GetCheckedAccountKeys() ]
self._DoExpires( subject_account_keys_and_new_expires )