Boa noite prezados ...
consegui o mesmo efeito WINDOWS / LINUX .. ficou legal !!
quem quiser compilar em linux .. está funcionando com letodbf
Código: Selecionar todos
#include "hwgui.ch"
#include "rddleto.ch"
REQUEST LETO
REQUEST DBFCDX
REQUEST DBFFPT
// =====================================================
// CADASTRO DE USUÁRIOS (PSW) + LOGIN (GTK/Linux)
// - Versão SEM ADDPROPERTY() (compatível com seu build)
// - Estado do formulário em STATICs: g_cModo, g_nRecno, g_cUsuOrig
// =====================================================
// -------------------------------------------------
// Objetos/controles principais
// -------------------------------------------------
STATIC ThisForm
STATIC oTab, oBrw, oEdBusca
STATIC oEdUsuario, oEdSenhaOld, oEdSenhaNew, oEdSenhaConf
STATIC oBtnInclui, oBtnAltera, oBtnExcluir, oBtnSalvar, oBtnCancelar, oBtnSair
// -------------------------------------------------
// Buffers do formulário
// -------------------------------------------------
STATIC cUsuarioForm
STATIC cSenhaOldForm
STATIC cSenhaNewForm
STATIC cSenhaConfForm
STATIC cBusca
// -------------------------------------------------
// “Propriedades” do formulário (substitui ADDPROPERTY)
// -------------------------------------------------
STATIC g_cModo := "view"
STATIC g_nRecno := 0
STATIC g_cUsuOrig := ""
// -------------------------------------------------
// Estado de login (opcional)
// -------------------------------------------------
STATIC g_cOperador := ""
STATIC g_nPswRec := 0
// Fonte padrão
PUBLIC oFontPSW
PUBLIC cPathLeto
PUBLIC CONEXAO_LETO
// =====================================================
// PONTO DE ENTRADA
// =====================================================
PROCEDURE MAIN()
LOCAL cIP := "127.0.0.1"
LOCAL cPort := "2830"
LOCAL nTimeOut := 10000
LOCAL nConectarLeto, nRes
// Fonte padrão
PUBLIC oFontPSW
// monta string de conexão
cPathLeto := "//" + cIP + ":" + cPort + "/"
CONEXAO_LETO := ""
// conecta
nConectarLeto := leto_Connect( cPathLeto ,,, nTimeOut )
IF nConectarLeto == -1
nRes := leto_Connect_Err()
IF nRes == LETO_ERR_LOGIN
hwg_MsgStop( "Falla al logarse", "Conexión" )
ELSEIF nRes == LETO_ERR_RECV
hwg_MsgStop( "Erro al conectarse: " + Str(nConectarLeto), "Conexión" )
ELSEIF nRes == LETO_ERR_SEND
hwg_MsgStop( "Erro de envio", "Conexión" )
ELSE
hwg_MsgStop( "Servidor no encontrado! ", cIP + ":" + cPort )
ENDIF
QUIT
ENDIF
CONEXAO_LETO := LETO_GETCURRENTCONNECTION()
// cria/abre PSW no servidor + garante índice
IF ! EnsurePSW_LETO()
hwg_MsgStop( "No se pudo crear/abrir PSW en el servidor.", "Error" )
QUIT
ENDIF
// (Opcional) LOG local
EnsureLOG()
// (Opcional) cria usuário padrão
CRIAUSUARIOPADRAOPSW()
IF TELA_LOGIN( NIL )
CAD_USUARIO()
ENDIF
RETURN
// =====================================================
// EnsurePSW_LETO()
// - Cria PSW.DBF no servidor se não existir
// - Abre PSW via RDD LETO
// - Garante CDX/TAGs (pswcx_01 / pswcx_02)
// - Deixa ORDER ativo em pswcx_01 (necessário p/ DBSEEK)
// =====================================================
STATIC FUNCTION EnsurePSW_LETO()
LOCAL aStru := {}
LOCAL lOk := .T.
IF Empty( cPathLeto )
RETURN .F.
ENDIF
BEGIN SEQUENCE
// 1) cria DBF se não existir
IF ! leto_file( cPathLeto + "psw.dbf" )
AAdd( aStru, { "USUARIO", "C", 10, 0 } )
AAdd( aStru, { "SENHA", "C", 10, 0 } )
AAdd( aStru, { "GRUPO", "C", 20, 0 } )
AAdd( aStru, { "ACESSO", "C", 200,0 } )
AAdd( aStru, { "CODLOJ", "C", 2, 0 } )
AAdd( aStru, { "CAIXA", "C", 4, 0 } )
dbCreate( cPathLeto + "psw.dbf", aStru, "LETO" )
ENDIF
// 2) abre PSW (sempre via LETO)
IF ! Used( "PSW" )
USE ( cPathLeto + "psw" ) VIA "LETO" SHARED NEW ALIAS "PSW"
ELSE
DbSelectArea( "PSW" )
ENDIF
// 3) garante CDX/TAGs (se não existir, cria)
IF ! leto_file( cPathLeto + "psw" + indexext() )
INDEX ON USUARIO TAG pswcx_01
INDEX ON SENHA TAG pswcx_02
ENDIF
// 4) garante ORDER ativo
SET ORDER TO TAG pswcx_01
RECOVER
lOk := .F.
END SEQUENCE
RETURN lOk
// =====================================================
// EnsureLOG() - (opcional) cria/abre LOG.DBF local
// =====================================================
STATIC FUNCTION ensurelog()
LOCAL cDbf := hb_DirBase() + "log.dbf"
LOCAL aStru
IF ! hb_FileExists( cDbf )
aStru := {}
AAdd( aStru, { "CD_DATA", "D", 8, 0 } )
AAdd( aStru, { "CC_TIME", "C", 8, 0 } )
AAdd( aStru, { "CC_USUARIO","C", 20, 0 } )
AAdd( aStru, { "CC_OPERAC", "C", 60, 0 } )
BEGIN SEQUENCE
DbCreate( cDbf, aStru, "DBFCDX" )
RECOVER
RETURN .F.
END SEQUENCE
ENDIF
IF Select( "LOG" ) == 0
BEGIN SEQUENCE
USE ( cDbf ) ALIAS "LOG" NEW SHARED VIA "DBFCDX"
RECOVER
RETURN .F.
END SEQUENCE
ENDIF
RETURN .T.
// =====================================================
// CAD_USUARIO()
// =====================================================
FUNCTION CAD_USUARIO()
LOCAL oDlg
// Buffers do formulário
cUsuarioForm := Space(10) // PSW->USUARIO é C(10)
cSenhaOldForm := ""
cSenhaNewForm := ""
cSenhaConfForm := ""
cBusca := Space(40)
IF Select("PSW") == 0
hwg_MsgStop( "PSW no está abierto.", "Error" )
RETURN NIL
ENDIF
// Estado inicial
g_cModo := "view"
g_nRecno := 0
g_cUsuOrig := ""
INIT DIALOG oDlg ;
TITLE "Mantenimiento de Usuários" ;
AT 0,0 ;
SIZE 720, 460 ;
STYLE WS_POPUP + WS_CAPTION + WS_SYSMENU + DS_CENTER ;
FONT oFontPSW ;
CLIPPER NOEXIT ;
ON INIT {|| ;
ThisForm := oDlg, ;
OnDlgInitCad(), ;
BrwInitPSW(), ;
AtualizaTela(), ;
SetInitialFocus( oBrw ),;
oTab:bChange := { |o,nPage| IIF( g_cModo $ "novo|editar", .F., TabChangePSW( nPage ) ) } ;
}
ThisForm := oDlg
// ESC global do diálogo
hwg_SetDlgKey( oDlg, 0, 27, {|| CadUsuario_OnEsc( oDlg ) }, .F. )
// BARRA DE BOTÕES
@ 15,11 BUTTON oBtnInclui CAPTION "Nuevo" SIZE 90,30 OF oDlg ;
ON CLICK {|| Habilitar("novo") }
@ 110,11 BUTTON oBtnAltera CAPTION "Editar" SIZE 90,30 OF oDlg ;
ON CLICK {|| Habilitar("editar") }
@ 205,11 BUTTON oBtnExcluir CAPTION "Eliminar" SIZE 90,30 OF oDlg ;
ON CLICK {|| ExcluirRegistro() }
@ 300,11 BUTTON oBtnSalvar CAPTION "Guardar" SIZE 90,30 OF oDlg ;
ON CLICK {|| Gravar("salvar") }
@ 395,11 BUTTON oBtnCancelar CAPTION "Cancelar" SIZE 90,30 OF oDlg ;
ON CLICK {|| Gravar("cancelar") }
@ 490,11 BUTTON oBtnSair CAPTION "Salir" SIZE 90,30 OF oDlg ;
ON CLICK {|| hwg_EndDialog( oDlg:handle ) }
// TAB
@ 10,50 TAB oTab ITEMS { } SIZE 690,360 OF oDlg ;
ON CHANGE { |o,nPage| IIF( g_cModo $ "novo|editar", .F., TabChangePSW( nPage ) ) } ;
STYLE 0
// ABA CONSULTA
BEGIN PAGE "Consulta" OF oTab
@ 15,50 BROWSE oBrw DATABASE OF oTab SIZE 650,210 ;
STYLE WS_BORDER + WS_VSCROLL + WS_HSCROLL + WS_TABSTOP ;
FONT HFont():Add( "Courier New", 0, -13, 400 )
@ 15,310 SAY "&Buscar:" SIZE 60,16 OF oTab TRANSPARENT
@ 80,308 GET oEdBusca VAR cBusca PICTURE "@!" SIZE 200,22 OF oTab ;
VALID {|| PesquisaUsuarios( oEdBusca:GetText(), oBrw ), SetInitialFocus( oBrw ), .T. }
// GET fantasma (stub)
AddPhantomGet( oTab )
END PAGE OF oTab
// ABA DADOS
BEGIN PAGE "Datos" OF oTab
@ 10,25 GROUPBOX "Datos del Usuario" SIZE 520,200 OF oTab
@ 20,60 SAY "Usuario:" SIZE 90,18 OF oTab TRANSPARENT
@ 115,58 GET oEdUsuario VAR cUsuarioForm PICTURE "@!" SIZE 360,22 OF oTab ;
STYLE WS_DISABLED ;
VALID {|| ValidUsuario() }
@ 20,95 SAY "Anterior:" SIZE 90,18 OF oTab TRANSPARENT
@ 115,93 GET oEdSenhaOld VAR cSenhaOldForm PICTURE "@!" SIZE 240,22 OF oTab ;
STYLE WS_DISABLED
@ 20,130 SAY "Nueva:" SIZE 90,18 OF oTab TRANSPARENT
@ 115,128 GET oEdSenhaNew VAR cSenhaNewForm PICTURE "@!" SIZE 240,22 OF oTab ;
STYLE WS_DISABLED
@ 20,165 SAY "Confirmar:" SIZE 90,18 OF oTab TRANSPARENT
@ 115,163 GET oEdSenhaConf VAR cSenhaConfForm PICTURE "@!" SIZE 240,22 OF oTab ;
STYLE WS_DISABLED
END PAGE OF oTab
// BROWSE – EVENTOS
oBrw:Alias := "PSW"
oBrw:bKeyDown := { |o, k| BrwKeyDownPSW( oDlg, k ) }
oBrw:bPosChanged := {|| BrwSyncFromBrowse() }
ACTIVATE DIALOG oDlg CENTER
RETURN NIL
STATIC FUNCTION CadUsuario_OnEsc( oDlg )
IF g_cModo == "novo" .OR. g_cModo == "editar"
IF hwg_MsgYesNo( "¿Desea abandonar sin guardar?", "Confirmar" )
Gravar( "cancelar" )
ENDIF
RETURN .T.
ENDIF
hwg_EndDialog( oDlg:handle )
RETURN .T.
STATIC FUNCTION BrwKeyDownPSW( oDlg, k )
HB_SYMBOL_UNUSED( oDlg )
DO CASE
CASE k == Asc("n") .OR. k == Asc("N")
Habilitar( "novo" )
RETURN .F.
CASE k == Asc("e") .OR. k == Asc("E")
Habilitar( "editar" )
RETURN .F.
CASE k == Asc("r") .OR. k == Asc("R")
ExcluirRegistro()
RETURN .F.
CASE k == Asc("b") .OR. k == Asc("B")
cBusca := ""
SafeSetText( oEdBusca, "" )
SafeSetFocus( oEdBusca )
RETURN .F.
ENDCASE
RETURN .T.
STATIC FUNCTION OnDlgInitCad()
LOCAL nOld := Select()
DbSelectArea( "PSW" )
// garante ordem “real” antes de montar filtros temporários
__PSW_GARANTE_ORDEM()
BrwFiltroBasePSW( PSW->( RecNo() ) )
IF PSW->( LastRec() ) > 0
PSW->( DbGoTop() )
g_nRecno := PSW->( RecNo() )
ENDIF
DbSelectArea( nOld )
RETURN NIL
STATIC FUNCTION BrwInitPSW()
oBrw:Alias := "PSW"
oBrw:aColumns := {}
oBrw:AddColumn( HColumn():New( "Usuário", FieldBlock("USUARIO"), "C", 10, 0 ) )
oBrw:oStyleHead := HStyle():New( ;
{ 0x003A3A3A, 0x005E5E5E }, ;
1,, ;
0.4, ;
0x00EDEDED )
oBrw:headColor := 16777215
oBrw:Refresh()
RETURN NIL
STATIC FUNCTION BrwSyncFromBrowse()
LOCAL nOld := Select()
IF g_cModo == "novo" .OR. g_cModo == "editar"
RETURN NIL
ENDIF
DbSelectArea( "PSW" )
IF ! PSW->( EOF() )
g_nRecno := PSW->( RecNo() )
ENDIF
DbSelectArea( nOld )
AtualizaTela()
RETURN NIL
STATIC FUNCTION TabChangePSW( nPage )
LOCAL nOld := Select()
IF g_cModo $ "novo|editar"
RETURN NIL
ENDIF
DbSelectArea( "PSW" )
DO CASE
CASE nPage == 1
SetInitialFocus( oBrw )
CASE nPage == 2
IF PSW->( LastRec() ) > 0 .AND. ! PSW->( EOF() )
cUsuarioForm := PSW->USUARIO
ELSE
cUsuarioForm := ""
ENDIF
SafeSetText( oEdUsuario, cUsuarioForm )
SafeRefresh( oEdUsuario )
cSenhaOldForm := ""
cSenhaNewForm := ""
cSenhaConfForm := ""
SafeSetText( oEdSenhaOld, "" ) ; SafeRefresh( oEdSenhaOld )
SafeSetText( oEdSenhaNew, "" ) ; SafeRefresh( oEdSenhaNew )
SafeSetText( oEdSenhaConf, "" ) ; SafeRefresh( oEdSenhaConf )
ENDCASE
DbSelectArea( nOld )
RETURN NIL
STATIC FUNCTION AtualizaTela()
LOCAL nOld := Select()
LOCAL lTemReg := .F.
DbSelectArea( "PSW" )
lTemReg := ( PSW->( LastRec() ) > 0 .AND. ! PSW->( EOF() ) )
IF g_cModo <> "novo" .AND. g_cModo <> "editar"
g_cModo := "view"
ENDIF
oTab:Page(1):Enable()
IF lTemReg
cUsuarioForm := PSW->USUARIO
ELSE
cUsuarioForm := ""
ENDIF
cSenhaOldForm := ""
cSenhaNewForm := ""
cSenhaConfForm := ""
SafeSetText( oEdSenhaOld, "" ) ; SafeRefresh( oEdSenhaOld )
SafeSetText( oEdSenhaNew, "" ) ; SafeRefresh( oEdSenhaNew )
SafeSetText( oEdSenhaConf, "" ) ; SafeRefresh( oEdSenhaConf )
SafeDisable( oEdUsuario )
SafeDisable( oEdSenhaOld )
SafeDisable( oEdSenhaNew )
SafeDisable( oEdSenhaConf )
SetBtnEnabled( oBtnInclui, .T. )
SetBtnEnabled( oBtnAltera, lTemReg )
SetBtnEnabled( oBtnExcluir, lTemReg )
SetBtnEnabled( oBtnSalvar, .F. )
SetBtnEnabled( oBtnCancelar, .F. )
SetBtnEnabled( oBtnSair, .T. )
DbSelectArea( nOld )
SetTabPage( oTab, 1 )
RETURN NIL
STATIC FUNCTION Habilitar( cModo )
LOCAL nOld := Select()
DbSelectArea( "PSW" )
__PSW_GARANTE_ORDEM()
IF cModo == "editar" .AND. ( PSW->( LastRec() ) <= 0 .OR. PSW->( EOF() ) )
DbSelectArea( nOld )
RETURN NIL
ENDIF
IF cModo == "editar" .AND. Upper(AllTrim(PSW->USUARIO)) == "CONTROLE"
DbSelectArea( nOld )
hwg_MsgStop( "El usuario del sistema no puede ser modificado.", "Atención" )
RETURN NIL
ENDIF
g_cModo := cModo
g_nRecno := IIF( PSW->( LastRec() ) > 0 .AND. !PSW->( EOF() ), PSW->( RecNo() ), 0 )
oTab:Page(1):Disable()
cSenhaOldForm := ""
cSenhaNewForm := ""
cSenhaConfForm := ""
SafeSetText( oEdSenhaOld, "" ) ; SafeRefresh( oEdSenhaOld )
SafeSetText( oEdSenhaNew, "" ) ; SafeRefresh( oEdSenhaNew )
SafeSetText( oEdSenhaConf, "" ) ; SafeRefresh( oEdSenhaConf )
SafeEnable( oEdUsuario )
ForceGetPassword( oEdSenhaOld, "*", 10 )
ForceGetPassword( oEdSenhaNew, "*", 10 )
ForceGetPassword( oEdSenhaConf, "*", 10 )
IF cModo == "novo"
g_cUsuOrig := ""
SafeDisable( oEdSenhaOld )
SafeEnable( oEdSenhaNew )
SafeEnable( oEdSenhaConf )
cUsuarioForm := ""
SafeSetText( oEdUsuario, cUsuarioForm )
SafeRefresh( oEdUsuario )
ELSE
g_cUsuOrig := Upper( AllTrim( PSW->USUARIO ) )
SafeEnable( oEdSenhaOld )
SafeEnable( oEdSenhaNew )
SafeEnable( oEdSenhaConf )
cUsuarioForm := PSW->USUARIO
SafeSetText( oEdUsuario, cUsuarioForm )
SafeRefresh( oEdUsuario )
ENDIF
SetTabPage( oTab, 2 )
SafeSetFocus( oEdUsuario )
SetBtnEnabled( oBtnInclui, .F. )
SetBtnEnabled( oBtnAltera, .F. )
SetBtnEnabled( oBtnExcluir, .F. )
SetBtnEnabled( oBtnSalvar, .T. )
SetBtnEnabled( oBtnCancelar, .T. )
SetBtnEnabled( oBtnSair, .F. )
DbSelectArea( nOld )
RETURN NIL
STATIC FUNCTION Gravar( cAcao )
LOCAL nOld := Select()
LOCAL cUsu, cOld, cNew, cConf
LOCAL nRecSalvo := 0
DbSelectArea( "PSW" )
__PSW_GARANTE_ORDEM()
IF cAcao == "cancelar"
IF g_nRecno > 0 .AND. PSW->( LastRec() ) > 0
PSW->( DbGoTo( g_nRecno ) )
ELSEIF PSW->( LastRec() ) > 0
PSW->( DbGoTop() )
ENDIF
BrwFiltroBasePSW( PSW->( RecNo() ) )
DbSelectArea( nOld )
g_cModo := "view"
AtualizaTela()
oBrw:Refresh()
SetInitialFocus( oBrw )
RETURN NIL
ENDIF
cUsu := Upper( AllTrim( cUsuarioForm ) )
cOld := AllTrim( cSenhaOldForm )
cNew := AllTrim( cSenhaNewForm )
cConf := AllTrim( cSenhaConfForm )
IF " " $ cUsu
DbSelectArea( nOld )
hwg_MsgStop( "El usuario no puede contener espacios.", "Error" )
SafeSetFocus( oEdUsuario )
RETURN NIL
ENDIF
IF Empty( cUsu )
DbSelectArea( nOld )
hwg_MsgStop( "El usuario no puede quedar vacío.", "Error" )
SafeSetFocus( oEdUsuario )
RETURN NIL
ENDIF
IF cUsu == "CONTROLE"
DbSelectArea( nOld )
hwg_MsgStop( "El usuario CONTROLE está reservado para el sistema.", "Atención" )
SafeSetFocus( oEdUsuario )
RETURN NIL
ENDIF
// SENHA
IF g_cModo == "novo"
IF Empty( cNew )
DbSelectArea( nOld )
hwg_MsgStop( "La contraseña no puede quedar vacía.", "Error" )
SafeSetFocus( oEdSenhaNew )
RETURN NIL
ENDIF
IF Empty( cConf )
DbSelectArea( nOld )
hwg_MsgStop( "Confirme la contraseña.", "Error" )
SafeSetFocus( oEdSenhaConf )
RETURN NIL
ENDIF
IF cNew <> cConf
DbSelectArea( nOld )
hwg_MsgStop( "Las contraseñas no coinciden.", "Error" )
cSenhaConfForm := ""
SafeSetText( oEdSenhaConf, "" )
SafeRefresh( oEdSenhaConf )
SafeSetFocus( oEdSenhaConf )
RETURN NIL
ENDIF
ELSEIF g_cModo == "editar"
IF ! Empty( cOld ) .OR. ! Empty( cNew ) .OR. ! Empty( cConf )
IF Empty( cOld )
DbSelectArea( nOld )
hwg_MsgStop( "Informe la contraseña anterior.", "Error" )
SafeSetFocus( oEdSenhaOld )
RETURN NIL
ENDIF
IF Empty( cNew )
DbSelectArea( nOld )
hwg_MsgStop( "Informe la nueva contraseña.", "Error" )
SafeSetFocus( oEdSenhaNew )
RETURN NIL
ENDIF
IF Empty( cConf )
DbSelectArea( nOld )
hwg_MsgStop( "Confirme la nueva contraseña.", "Error" )
SafeSetFocus( oEdSenhaConf )
RETURN NIL
ENDIF
IF cNew <> cConf
DbSelectArea( nOld )
hwg_MsgStop( "Las contraseñas no coinciden.", "Error" )
cSenhaConfForm := ""
SafeSetText( oEdSenhaConf, "" )
SafeRefresh( oEdSenhaConf )
SafeSetFocus( oEdSenhaConf )
RETURN NIL
ENDIF
IF Empty( g_cUsuOrig )
g_cUsuOrig := Upper( AllTrim( PSW->USUARIO ) )
ENDIF
IF ! PswCheck( g_cUsuOrig, cOld, PSW->SENHA )
DbSelectArea( nOld )
hwg_MsgStop( "Contraseña anterior inválida.", "Error" )
cSenhaOldForm := ""
SafeSetText( oEdSenhaOld, "" )
SafeRefresh( oEdSenhaOld )
SafeSetFocus( oEdSenhaOld )
RETURN NIL
ENDIF
ENDIF
ENDIF
// duplicidade (usa ordem correta)
IF UsuarioDuplicadoPSW( cUsu, IIF( g_cModo == "editar", g_nRecno, 0 ) )
hwg_MsgStop( "Ya existe un usuario con ese nombre.", "Atención" )
SafeSetFocus( oEdUsuario )
DbSelectArea( nOld )
RETURN NIL
ENDIF
// Gravação
IF g_cModo == "novo"
PSW->( DbAppend() )
IF ! RLOCK()
PSW->( DbDelete() )
DbSelectArea( nOld )
RETURN NIL
ENDIF
PSW->USUARIO := PadR( cUsu, Len( PSW->USUARIO ) )
PSW->SENHA := PswHash( cUsu, cNew, Len( PSW->SENHA ) )
nRecSalvo := PSW->( RecNo() )
g_nRecno := nRecSalvo
DBUNLOCK()
ELSE
IF g_nRecno > 0
PSW->( DbGoTo( g_nRecno ) )
ENDIF
IF ! RLOCK()
DbSelectArea( nOld )
RETURN NIL
ENDIF
PSW->USUARIO := PadR( cUsu, Len( PSW->USUARIO ) )
IF ! Empty( cOld ) .OR. ! Empty( cNew ) .OR. ! Empty( cConf )
PSW->SENHA := PswHash( cUsu, cNew, Len( PSW->SENHA ) )
ENDIF
nRecSalvo := PSW->( RecNo() )
g_nRecno := nRecSalvo
DBUNLOCK()
ENDIF
PSW->( dbCommit() )
PSW->( dbCommitAll() )
BrwFiltroBasePSW( nRecSalvo )
DbSelectArea( nOld )
g_cModo := "view"
AtualizaTela()
oBrw:Refresh()
SetInitialFocus( oBrw )
RETURN NIL
STATIC FUNCTION ExcluirRegistro()
LOCAL nOld := Select()
LOCAL nRecAtual := 0
LOCAL cKeyAtual := ""
LOCAL lDel := .F.
DbSelectArea( "PSW" )
__PSW_GARANTE_ORDEM()
IF PSW->( LastRec() ) <= 0 .OR. PSW->( EOF() )
DbSelectArea( nOld )
RETURN NIL
ENDIF
IF Upper( AllTrim( PSW->USUARIO ) ) == "CONTROLE"
DbSelectArea( nOld )
hwg_MsgStop( "El usuario del sistema no puede ser eliminado.", "Atención" )
RETURN NIL
ENDIF
nRecAtual := PSW->( RecNo() )
cKeyAtual := PSW->USUARIO
IF hwg_MsgYesNo( "¿Desea eliminar este usuario?", "Confirmar" )
IF RLOCK()
DBDELETE()
DBUNLOCK()
PSW->( dbCommit() )
PSW->( dbCommitAll() )
lDel := .T.
ENDIF
ENDIF
BrwFiltroBasePSW( PSW->( RecNo() ) )
IF PSW->( LastRec() ) > 0
__PSW_GARANTE_ORDEM()
IF ! Empty( cKeyAtual )
DBSEEK( cKeyAtual, .T. )
ENDIF
IF ! lDel
IF nRecAtual > 0 .AND. PSW->( LastRec() ) >= nRecAtual
PSW->( DbGoTo( nRecAtual ) )
ENDIF
ENDIF
IF PSW->( EOF() )
PSW->( DbGoBottom() )
ENDIF
ENDIF
DbSelectArea( nOld )
AtualizaTela()
oBrw:Refresh()
RETURN NIL
FUNCTION PesquisaUsuarios( cFiltro, oBrw )
LOCAL cQuery := "", cParte := "", nParte := 0, aList
STATIC nIndiceOriginal := 0
LOCAL nOld := Select()
DbSelectArea( "PSW" )
__PSW_GARANTE_ORDEM()
IF ! Empty( AllTrim( cFiltro ) )
IF nIndiceOriginal == 0
nIndiceOriginal := PSW->( OrdNumber() )
ENDIF
aList := hb_RegExSplit( " ", AllTrim( cFiltro ) )
FOR EACH cParte IN aList
cParte := AllTrim( cParte )
IF Empty( cParte )
LOOP
ENDIF
IF ++nParte > 1
cQuery += " .AND. "
ENDIF
cQuery += " ( '" + Upper( cParte ) + "' $ Upper(USUARIO) )"
NEXT
IF ! Empty( cQuery )
cQuery := "(" + cQuery + ") .AND. Upper(AllTrim(USUARIO)) <> 'CONTROLE'"
ELSE
cQuery := "Upper(AllTrim(USUARIO)) <> 'CONTROLE'"
ENDIF
IF PSW->( OrdNumber( "TMPFILTRO_PSW" ) ) > 0
PSW->( OrdDestroy( "TMPFILTRO_PSW" ) )
ENDIF
INDEX ON USUARIO TAG TMPFILTRO_PSW FOR &cQuery TEMPORARY
ELSE
BrwLimpaPesquisaPSW( oBrw )
DbSelectArea( nOld )
RETURN .T.
ENDIF
PSW->( DbGoTop() )
oBrw:Refresh()
DbSelectArea( nOld )
RETURN .T.
STATIC FUNCTION ValidUsuario()
LOCAL cU := AllTrim( cUsuarioForm )
IF Empty( cU )
RETURN .F.
ENDIF
IF " " $ cU
hwg_MsgStop( "El usuario no puede contener espacios.", "Error" )
cUsuarioForm := StrTran( cU, " ", "" )
SafeSetText( oEdUsuario, cUsuarioForm )
SafeRefresh( oEdUsuario )
SafeSetFocus( oEdUsuario )
RETURN .F.
ENDIF
RETURN .T.
STATIC FUNCTION UsuarioDuplicadoPSW( cUsu, nRecAtual )
LOCAL nOldSel := Select()
LOCAL nOldOrd := 0
LOCAL lDup := .F.
LOCAL cKey := Upper( AllTrim( cUsu ) )
LOCAL nLenUser := 10
IF Empty( cKey )
RETURN .F.
ENDIF
DbSelectArea( "PSW" )
BEGIN SEQUENCE
nOldOrd := PSW->( OrdNumber() )
RECOVER
nOldOrd := 0
END SEQUENCE
BEGIN SEQUENCE
nLenUser := FieldLen( "USUARIO" )
RECOVER
nLenUser := 10
END SEQUENCE
IF nLenUser <= 0
nLenUser := 10
ENDIF
__PSW_GARANTE_ORDEM()
IF PSW->( DBSEEK( PadR( cKey, nLenUser ), .T. ) )
IF Upper( AllTrim( PSW->USUARIO ) ) == cKey
IF PSW->( RecNo() ) <> nRecAtual
lDup := .T.
ENDIF
ENDIF
ENDIF
IF nOldOrd > 0
BEGIN SEQUENCE
PSW->( OrdSetFocus( nOldOrd ) )
RECOVER
END SEQUENCE
ENDIF
DbSelectArea( nOldSel )
RETURN lDup
STATIC FUNCTION BrwFiltroBasePSW( nRecPos )
Hb_Default( @nRecPos, 0 )
IF nRecPos <= 0
nRecPos := PSW->( RecNo() )
ENDIF
IF PSW->( OrdNumber( "TMP_NO_CONTROLE" ) ) > 0
PSW->( OrdDestroy( "TMP_NO_CONTROLE" ) )
ENDIF
INDEX ON USUARIO TAG TMP_NO_CONTROLE ;
FOR Upper( AllTrim( USUARIO ) ) <> "CONTROLE" TEMPORARY
BEGIN SEQUENCE
SET ORDER TO TAG TMP_NO_CONTROLE
RECOVER
END SEQUENCE
IF nRecPos > 0 .AND. PSW->( LastRec() ) >= nRecPos
PSW->( DbGoTo( nRecPos ) )
ENDIF
IF PSW->( EOF() ) .AND. PSW->( LastRec() ) > 0
PSW->( DbGoBottom() )
ENDIF
RETURN NIL
STATIC FUNCTION BrwLimpaPesquisaPSW( oBrw )
IF PSW->( OrdNumber( "TMPFILTRO_PSW" ) ) > 0
PSW->( OrdDestroy( "TMPFILTRO_PSW" ) )
ENDIF
BrwFiltroBasePSW( PSW->( RecNo() ) )
oBrw:Refresh()
RETURN NIL
// =====================================================
// Garantia central de ORDER p/ evitar LETO/1201
// =====================================================
STATIC FUNCTION __PSW_GARANTE_ORDEM()
IF ! Used( "PSW" )
ensurePSW_LETO()
RETURN NIL
ENDIF
BEGIN SEQUENCE
SET ORDER TO TAG pswcx_01
RECOVER
EnsurePSW_LETO()
BEGIN SEQUENCE
SET ORDER TO TAG pswcx_01
RECOVER
END SEQUENCE
END SEQUENCE
RETURN NIL
FUNCTION SETDLGKEY( hDlg, nKey, bAction )
HB_SYMBOL_UNUSED( hDlg )
HB_SYMBOL_UNUSED( nKey )
HB_SYMBOL_UNUSED( bAction )
RETURN .F.
FUNCTION CRIAUSUARIOPADRAOPSW()
LOCAL nOldArea := Select()
LOCAL nOldRec := 0
LOCAL nOldOrd := 0
LOCAL lOk := .T.
LOCAL cUsuPadrao := "CONTROLE"
LOCAL cSenhaPadrao := "CTRL"
LOCAL cHashNovo
LOCAL nLenUser := 10
IF ! Used( "PSW" )
RETURN .F.
ENDIF
DbSelectArea( "PSW" )
BEGIN SEQUENCE
nLenUser := FieldLen( "USUARIO" )
RECOVER
nLenUser := 10
END SEQUENCE
IF nLenUser <= 0
nLenUser := 10
ENDIF
nOldRec := PSW->( RecNo() )
BEGIN SEQUENCE
nOldOrd := OrdNumber()
RECOVER
nOldOrd := 0
END SEQUENCE
__PSW_GARANTE_ORDEM()
cHashNovo := PswHash( cUsuPadrao, cSenhaPadrao, Len( PSW->SENHA ) )
IF ! DBSEEK( PadR( cUsuPadrao, nLenUser ), .T. )
PSW->( DbAppend() )
IF RLOCK()
PSW->USUARIO := PadR( cUsuPadrao, Len( PSW->USUARIO ) )
PSW->SENHA := cHashNovo
DBUNLOCK()
PSW->( dbCommit() )
PSW->( dbCommitAll() )
ELSE
lOk := .F.
ENDIF
ELSE
IF AllTrim( PSW->SENHA ) <> AllTrim( cHashNovo )
IF RLOCK()
PSW->SENHA := cHashNovo
DBUNLOCK()
PSW->( dbCommit() )
PSW->( dbCommitAll() )
ELSE
lOk := .F.
ENDIF
ENDIF
ENDIF
IF nOldRec > 0
PSW->( DbGoTo( nOldRec ) )
ENDIF
IF nOldOrd > 0
BEGIN SEQUENCE
OrdSetFocus( nOldOrd )
RECOVER
END SEQUENCE
ENDIF
DbSelectArea( nOldArea )
RETURN lOk
FUNCTION TELA_LOGIN( oMainWnd )
LOCAL oLoginWnd, oBmp
LOCAL oGetUsuario, oGetSenha
LOCAL oBtnEntrar, oBtnCancelar
LOCAL oPhantom
LOCAL cPhantom := ""
LOCAL nLenUser := 10
LOCAL cUsuario := ""
LOCAL cSenha := ""
LOCAL lOK := .F.
HB_SYMBOL_UNUSED( oMainWnd )
HB_SYMBOL_UNUSED( cPhantom )
BEGIN SEQUENCE
nLenUser := FieldLen( "USUARIO" )
RECOVER
nLenUser := 10
END SEQUENCE
IF nLenUser <= 0
nLenUser := 10
ENDIF
cUsuario := PadR( ULTIMOUSUARIO_GET(), nLenUser )
INIT DIALOG oLoginWnd ;
TITLE "Inicio de sesión" ;
AT 0,0 ;
SIZE 351, 190 ;
FONT oFontPSW ;
STYLE WS_POPUP + WS_CAPTION + WS_SYSMENU + DS_CENTER ;
CLIPPER NOEXIT
BEGIN SEQUENCE
oBmp := HBitmap():AddFile( "lock.bmp" )
@ 20, 20 BITMAP oBmp OF oLoginWnd SIZE 72, 87 TRANSPARENT
RECOVER
END SEQUENCE
@ 110, 20 SAY "Nombre de usuario" OF oLoginWnd SIZE 200, 15 TRANSPARENT
@ 110, 40 GET oGetUsuario VAR cUsuario PICTURE "@!" ;
SIZE 200, 20 MAXLENGTH nLenUser OF oLoginWnd ;
VALID {|| ;
IF( hwg_GetFocus() == oBtnCancelar:handle, .T., ;
IF( EMPTY( ALLTRIM( cUsuario ) ), ;
( oGetUsuario:SetFocus(), .F. ), ;
( oGetSenha:SetFocus(), .T. ) ) ) }
@ 110, 70 SAY "Contraseña" OF oLoginWnd SIZE 150, 15 TRANSPARENT
@ 110, 90 GET oGetSenha VAR cSenha ;
PASSWORD;
SIZE 150, 20 OF oLoginWnd ;
VALID {|| ;
IF( hwg_GetFocus() == oBtnCancelar:handle, .T., ;
( ;
IF( __objHasMsg( oBtnEntrar, "bClick" ) .AND. ;
ValType( oBtnEntrar:bClick ) == "B", ;
Eval( oBtnEntrar:bClick ), ;
NIL ), ;
.T. ) ) }
oPhantom := AddPhantomGet( oLoginWnd )
HB_SYMBOL_UNUSED( oPhantom )
@ 60, 140 BUTTON oBtnEntrar CAPTION "Ingresar" SIZE 100, 28 OF oLoginWnd ;
ON CLICK {|| ;
IF( VER_SENHA( ALLTRIM( cUsuario ), ALLTRIM( cSenha ), oMainWnd ), ;
( LOG_ACESSO_SISTEMA( ALLTRIM( cUsuario ) ), ;
lOK := .T., ;
hwg_EndDialog( oLoginWnd:handle ), ;
.T. ), ;
( hwg_MsgStop( "Usuario o contraseña inválidos.", "Inicio de sesión" ), ;
cSenha := "", ;
SafeSetText( oGetSenha, "" ), ;
SafeRefresh( oGetSenha ), ;
SelTextoGet( oGetUsuario, cUsuario, .T. ), ;
.F. ) ) }
@ 190, 140 BUTTON oBtnCancelar CAPTION "Cancelar" SIZE 100, 28 OF oLoginWnd ;
ON CLICK {|| lOK := .F., hwg_EndDialog( oLoginWnd:handle ), .T. }
oLoginWnd:bInit := {|| ;
cSenha := "", ;
SafeSetText( oGetSenha, "" ), ;
SafeRefresh( oGetSenha ), ;
ArmGetSenha( oGetSenha ), ;
SelTextoGet( oGetUsuario, @cUsuario, .T. ) }
ACTIVATE DIALOG oLoginWnd CENTER
RETURN lOK
FUNCTION ULTIMOUSUARIO_GET()
LOCAL cFile := hb_DirBase() + "lastuser.ini"
LOCAL cUser := ""
IF hb_FileExists( cFile )
cUser := AllTrim( MemoRead( cFile ) )
ENDIF
RETURN Upper( cUser )
FUNCTION ULTIMOUSUARIO_SET( cUser )
LOCAL cFile := hb_DirBase() + "lastuser.ini"
LOCAL cOut := Upper( AllTrim( cUser ) )
IF Empty( cOut )
RETURN .F.
ENDIF
MemoWrit( cFile, cOut )
RETURN .T.
FUNCTION VER_SENHA( cUsuario, cSenha, oMainWnd )
LOCAL cNome := Upper( AllTrim( cUsuario ) )
LOCAL cPass := AllTrim( cSenha )
LOCAL lOk := .F.
LOCAL nOldSel := Select()
LOCAL nOldRec := 0
LOCAL lOldSoft := Set( _SET_SOFTSEEK )
LOCAL nOldOrd := 0
LOCAL nLenUser := 10
LOCAL cKeySeek := ""
HB_SYMBOL_UNUSED( oMainWnd )
IF Empty( cNome ) .OR. Empty( cPass )
RETURN .F.
ENDIF
IF ! Used( "PSW" )
hwg_MsgStop( "El archivo PSW no está abierto.", "Inicio de sesión" )
RETURN .F.
ENDIF
DbSelectArea( "PSW" )
nOldRec := IIF( RecCount() > 0, RecNo(), 0 )
BEGIN SEQUENCE
nOldOrd := OrdNumber()
RECOVER
nOldOrd := 0
END SEQUENCE
BEGIN SEQUENCE
nLenUser := FieldLen( "USUARIO" )
RECOVER
nLenUser := 10
END SEQUENCE
IF nLenUser <= 0
nLenUser := 10
ENDIF
cKeySeek := PadR( cNome, nLenUser )
BEGIN SEQUENCE
// GARANTE ORDEM ANTES DO DBSEEK (resolve LETO/1201)
__PSW_GARANTE_ORDEM()
SET SOFTSEEK OFF
IF ! DBSEEK( cKeySeek, .F. )
BREAK
ENDIF
IF ! Found()
BREAK
ENDIF
IF PadR( Upper( AllTrim( PSW->USUARIO ) ), nLenUser ) <> cKeySeek
BREAK
ENDIF
IF PswCheck( cNome, cPass, PSW->SENHA )
lOk := .T.
g_cOperador := RTrim( PSW->USUARIO )
g_nPswRec := RecNo()
ULTIMOUSUARIO_SET( cNome )
ENDIF
END SEQUENCE
IF lOldSoft
SET SOFTSEEK ON
ELSE
SET SOFTSEEK OFF
ENDIF
IF nOldRec > 0 .AND. nOldRec <= RecCount()
DbGoTo( nOldRec )
ENDIF
BEGIN SEQUENCE
IF nOldOrd > 0
OrdSetFocus( nOldOrd )
ENDIF
RECOVER
END SEQUENCE
DbSelectArea( nOldSel )
RETURN lOk
FUNCTION log_acesso_sistema( cUsuario )
LOCAL nOldSel := Select()
cUsuario := Upper( AllTrim( cUsuario ) )
IF Empty( cUsuario )
RETURN .F.
ENDIF
IF ! Used( "LOG" )
DbSelectArea( nOldSel )
RETURN .F.
ENDIF
DbSelectArea( "LOG" )
BEGIN SEQUENCE
ADIC_REG()
IF ! Bloq_reg()
BREAK
ENDIF
REPLACE cd_data WITH Date(), ;
cc_time WITH Time(), ;
cc_usuario WITH cUsuario, ;
cc_operac WITH "Acessou o Sistema"
DBCOMMIT()
UNLOCK
END SEQUENCE
DbSelectArea( nOldSel )
RETURN .T.
// =====================================================
// ---------------- STUBS / HELPERS --------------------
// =====================================================
// =====================================================
// AddPhantomGet()
// Cria um GET fantasma (0,0 tamanho 0) para destravar ENTER
// no "último GET" (ex: campo Buscar).
// Retorna o objeto GET.
// =====================================================
FUNCTION AddPhantomGet( oOwner )
LOCAL oPhantom
LOCAL cPhantom := ""
IF ValType( oOwner ) <> "O"
RETURN NIL
ENDIF
// OF pode ser oTab (page) ou oDlg — use o mesmo "OF" do seu GET
@ 0,0 GET oPhantom VAR cPhantom SIZE 0,0 OF oOwner
RETURN oPhantom
// =====================================================
// Foco inicial genérico (ideal para usar no ON INIT)
// Prioridade: lInfocus -> SetFocus() -> hwg_SetFocus(handle)
// =====================================================
FUNCTION SetInitialFocus( oCtrl )
IF ValType( oCtrl ) <> "O"
RETURN .F.
ENDIF
// 1) HWGUI (muito estável no ON INIT)
IF __objHasMsg( oCtrl, "lInfocus" )
oCtrl:lInfocus := .T.
RETURN .T.
ENDIF
// 2) Método SetFocus
IF __objHasMethod( oCtrl, "SetFocus" )
oCtrl:SetFocus()
RETURN .T.
ENDIF
// 3) Fallback pelo handle
IF __objHasMsg( oCtrl, "handle" ) .AND. ! Empty( oCtrl:handle )
hwg_SetFocus( oCtrl:handle )
RETURN .T.
ENDIF
RETURN .F.
// =====================================================
// Troca aba do TAB (compatível com HWGUI antigas e novas)
// Uso: SetTabPage( oTab, 1 )
// =====================================================
FUNCTION SetTabPage( oTb, nPage )
#ifndef TCM_SETCURSEL
#define TCM_SETCURSEL 0x130C
#endif
IF ValType( oTb ) <> "O"
RETURN NIL
ENDIF
// Algumas builds possuem DATA nTab
IF __objHasMsg( oTb, "nTab" )
oTb:nTab := nPage
RETURN NIL
ENDIF
// Outras usam método SetTab()
IF __objHasMethod( oTb, "SetTab" )
oTb:SetTab( nPage )
RETURN NIL
ENDIF
// Outras usam método SetPage()
IF __objHasMethod( oTb, "SetPage" )
oTb:SetPage( nPage )
RETURN NIL
ENDIF
// Fallback WinAPI (funciona sempre)
IF __objHasMsg( oTb, "handle" ) .AND. ! Empty( oTb:handle )
hwg_SendMessage( oTb:handle, TCM_SETCURSEL, nPage - 1, 0 )
hwg_InvalidateRect( oTb:handle, 0, .T. )
ENDIF
RETURN NIL
// ===============================
// Habilita/Desabilita OWNERBUTTON com cor diferente
// ===============================
FUNCTION SetBtnEnabled( oBtn, lEnable )
IF ValType( oBtn ) <> "O"
RETURN NIL
ENDIF
IF lEnable
oBtn:Enable()
// Use SetHStyles (plural) - mais compatível
IF __objHasMethod( oBtn, "SetHStyles" )
oBtn:SetHStyles( oStyleNormal, oStylePressed, oStyleOver )
ENDIF
ELSE
oBtn:Disable()
// Para “desabilitado”, repete o mesmo estilo nas 3 posições
IF __objHasMethod( oBtn, "SetHStyles" )
oBtn:SetHStyles( oStyleDisabled, oStyleDisabled, oStyleDisabled )
ENDIF
ENDIF
oBtn:Refresh()
RETURN NIL
#define EM_SETPASSWORDCHAR 0x00CC
#define EM_LIMITTEXT 0x00C5
FUNCTION ForceGetPassword( oGet, cMaskChar, nMax )
LOCAL hEd
LOCAL nChar
IF ValType( oGet ) <> "O" .OR. Empty( oGet:handle )
RETURN .F.
ENDIF
hEd := oGet:handle
IF ValType( cMaskChar ) <> "C" .OR. Empty( cMaskChar )
cMaskChar := "*"
ENDIF
nChar := Asc( Left( cMaskChar, 1 ) )
IF ValType( nMax ) <> "N" .OR. nMax <= 0
nMax := 15
ENDIF
hwg_SendMessage( hEd, EM_LIMITTEXT, nMax, 0 )
hwg_SendMessage( hEd, EM_SETPASSWORDCHAR, nChar, 0 )
oGet:SetText( "" )
oGet:Refresh()
RETURN .T.
// Wrapper opcional (pra ficar padrão no sistema)
FUNCTION ArmGetSenha( oGet )
RETURN ForceGetPassword( oGet, "*", 15 )
//---------------------------------------------------//
FUNCTION SelTextoGet( oGet, cTexto, lSetFocus )
//---------------------------------------------------//
LOCAL hEd, nLen
Hb_Default( @lSetFocus, .T. )
IF ValType( oGet ) <> "O"
RETURN .F.
ENDIF
// Se não passar cTexto, tenta pegar do próprio controle
IF ValType( cTexto ) <> "C"
BEGIN SEQUENCE
cTexto := oGet:GetText()
RECOVER
cTexto := ""
END SEQUENCE
ENDIF
nLen := Len( RTrim( cTexto ) )
IF lSetFocus
oGet:SetFocus()
ENDIF
hEd := oGet:handle
IF Empty( hEd )
RETURN .F.
ENDIF
// Seleciona somente o que tem texto (sem área vazia)
hwg_SendMessage( hEd, EM_SETSEL, 0, nLen )
RETURN .T.
STATIC FUNCTION safesettext( oObj, cTxt )
IF ValType( oObj ) == "O"
BEGIN SEQUENCE
oObj:SetText( cTxt )
RECOVER
END SEQUENCE
ENDIF
RETURN NIL
STATIC FUNCTION SafeRefresh( oObj )
IF ValType( oObj ) == "O"
BEGIN SEQUENCE
oObj:Refresh()
RECOVER
END SEQUENCE
ENDIF
RETURN NIL
STATIC FUNCTION SafeSetFocus( oObj )
IF ValType( oObj ) == "O"
BEGIN SEQUENCE
oObj:SetFocus()
RECOVER
END SEQUENCE
ENDIF
RETURN NIL
STATIC FUNCTION SafeEnable( oObj )
IF ValType( oObj ) == "O"
BEGIN SEQUENCE
oObj:Enable()
RECOVER
BEGIN SEQUENCE
oObj:Enabled := .T.
RECOVER
END SEQUENCE
END SEQUENCE
ENDIF
RETURN NIL
STATIC FUNCTION SafeDisable( oObj )
IF ValType( oObj ) == "O"
BEGIN SEQUENCE
oObj:Disable()
RECOVER
BEGIN SEQUENCE
oObj:Enabled := .F.
RECOVER
END SEQUENCE
END SEQUENCE
ENDIF
RETURN NIL
STATIC FUNCTION Bloq_reg()
RETURN RLOCK()
STATIC FUNCTION ADIC_REG()
DbAppend()
RETURN NIL
// =====================================================
// HASH SIMPLES (teste)
// =====================================================
FUNCTION PswHash( cUsu, cSenha, nLenCampo )
LOCAL cBase := Upper(AllTrim(cUsu)) + ":" + AllTrim(cSenha)
LOCAL nCrc := hb_Crc32( cBase )
LOCAL cOut := StrZero( nCrc, 10 )
Hb_Default( @nLenCampo, 10 )
cOut := PadR( cOut, nLenCampo )
RETURN cOut
FUNCTION PswCheck( cUsu, cSenha, cHashDBF )
LOCAL cCalc := PswHash( cUsu, cSenha, Len( cHashDBF ) )
RETURN ( AllTrim( cCalc ) == AllTrim( cHashDBF ) )
testem ai .. o comando é o mesmo para ambas plataformas ..