From af3ee52058bcf51718e1d27192b3cd6ab6466f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez?= Date: Thu, 19 Jul 2012 23:47:54 +0000 Subject: [PATCH] Initial release --- client/administration/UdsAdmin.sln | 30 + .../UdsAdmin/Images.Designer.cs | 224 + client/administration/UdsAdmin/Images.resx | 191 + client/administration/UdsAdmin/Program.cs | 62 + .../UdsAdmin/Properties/AssemblyInfo.cs | 38 + .../UdsAdmin/Properties/Resources.Designer.cs | 63 + .../UdsAdmin/Properties/Resources.resx | 120 + .../UdsAdmin/Properties/Settings.Designer.cs | 122 + .../UdsAdmin/Properties/Settings.settings | 30 + .../UdsAdmin/Properties/app.manifest | 47 + .../UdsAdmin/Resources/Image1.png | Bin 0 -> 490 bytes .../UdsAdmin/Resources/apply16.png | Bin 0 -> 1108 bytes .../UdsAdmin/Resources/assignedServices16.png | Bin 0 -> 1255 bytes .../UdsAdmin/Resources/authenticators16.png | Bin 0 -> 825 bytes .../UdsAdmin/Resources/cache16.png | Bin 0 -> 940 bytes .../UdsAdmin/Resources/cancel16.png | Bin 0 -> 1259 bytes .../UdsAdmin/Resources/connectivity16.png | Bin 0 -> 741 bytes .../UdsAdmin/Resources/delete16.png | Bin 0 -> 535 bytes .../UdsAdmin/Resources/deployedService16.png | Bin 0 -> 1206 bytes .../UdsAdmin/Resources/deployedServices16.ico | Bin 0 -> 1406 bytes .../UdsAdmin/Resources/deployedServices16.png | Bin 0 -> 804 bytes .../UdsAdmin/Resources/downarrow16.png | Bin 0 -> 555 bytes .../UdsAdmin/Resources/empty16.png | Bin 0 -> 187 bytes .../UdsAdmin/Resources/find16.png | Bin 0 -> 1226 bytes .../UdsAdmin/Resources/groups16.ico | Bin 0 -> 1150 bytes .../UdsAdmin/Resources/groups16.png | Bin 0 -> 821 bytes .../UdsAdmin/Resources/networks16.png | Bin 0 -> 2531 bytes .../UdsAdmin/Resources/new16.png | Bin 0 -> 1141 bytes .../UdsAdmin/Resources/osmanagers16.png | Bin 0 -> 1226 bytes .../UdsAdmin/Resources/publications16.png | Bin 0 -> 1213 bytes .../UdsAdmin/Resources/serviceProviders16.png | Bin 0 -> 986 bytes .../UdsAdmin/Resources/services16.png | Bin 0 -> 657 bytes .../UdsAdmin/Resources/stats16.png | Bin 0 -> 722 bytes .../UdsAdmin/Resources/transports16.png | Bin 0 -> 1258 bytes .../UdsAdmin/Resources/uparrow16.png | Bin 0 -> 531 bytes .../UdsAdmin/Resources/users16.ico | Bin 0 -> 1406 bytes .../UdsAdmin/Resources/users16.png | Bin 0 -> 1128 bytes .../UdsAdmin/Strings.Designer.cs | 1215 + .../UdsAdmin/Strings.de.Designer.cs | 0 .../administration/UdsAdmin/Strings.de.resx | 475 + .../UdsAdmin/Strings.es.Designer.cs | 0 .../administration/UdsAdmin/Strings.es.resx | 495 + .../UdsAdmin/Strings.fr.Designer.cs | 0 .../administration/UdsAdmin/Strings.fr.resx | 475 + client/administration/UdsAdmin/Strings.resx | 504 + .../administration/UdsAdmin/UdsAdmin.csproj | 847 + .../UdsAdmin/UdsAdmin_TemporaryKey.pfx | Bin 0 -> 1676 bytes client/administration/UdsAdmin/app.config | 36 + .../UdsAdmin/controls/ListEditor.Designer.cs | 57 + .../UdsAdmin/controls/ListEditor.cs | 93 + .../UdsAdmin/controls/ListEditor.de.resx | 165 + .../UdsAdmin/controls/ListEditor.es.resx | 165 + .../UdsAdmin/controls/ListEditor.fr.resx | 165 + .../UdsAdmin/controls/ListEditor.resx | 165 + .../controls/forms/ListEditorForm.Designer.cs | 118 + .../UdsAdmin/controls/forms/ListEditorForm.cs | 111 + .../controls/forms/ListEditorForm.de.resx | 135 + .../controls/forms/ListEditorForm.es.resx | 132 + .../controls/forms/ListEditorForm.fr.resx | 135 + .../controls/forms/ListEditorForm.resx | 330 + .../controls/panel/AuthsPanel.Designer.cs | 93 + .../UdsAdmin/controls/panel/AuthsPanel.cs | 105 + .../controls/panel/AuthsPanel.de.resx | 132 + .../controls/panel/AuthsPanel.es.resx | 132 + .../controls/panel/AuthsPanel.fr.resx | 132 + .../UdsAdmin/controls/panel/AuthsPanel.resx | 207 + .../panel/DeployedGroupsPanel.Designer.cs | 95 + .../controls/panel/DeployedGroupsPanel.cs | 177 + .../panel/DeployedGroupsPanel.de.resx | 132 + .../panel/DeployedGroupsPanel.es.resx | 132 + .../panel/DeployedGroupsPanel.fr.resx | 132 + .../controls/panel/DeployedGroupsPanel.resx | 204 + .../controls/panel/DeployedPanel.Designer.cs | 106 + .../UdsAdmin/controls/panel/DeployedPanel.cs | 212 + .../controls/panel/DeployedPanel.de.resx | 192 + .../controls/panel/DeployedPanel.es.resx | 192 + .../controls/panel/DeployedPanel.fr.resx | 192 + .../controls/panel/DeployedPanel.resx | 237 + .../panel/DeployedServicePanel.Designer.cs | 232 + .../controls/panel/DeployedServicePanel.cs | 72 + .../panel/DeployedServicePanel.de.resx | 783 + .../panel/DeployedServicePanel.es.resx | 783 + .../panel/DeployedServicePanel.fr.resx | 783 + .../controls/panel/DeployedServicePanel.resx | 852 + .../panel/DeployedServicesPanel.Designer.cs | 94 + .../controls/panel/DeployedServicesPanel.cs | 107 + .../panel/DeployedServicesPanel.de.resx | 204 + .../panel/DeployedServicesPanel.es.resx | 204 + .../panel/DeployedServicesPanel.fr.resx | 204 + .../controls/panel/DeployedServicesPanel.resx | 204 + .../controls/panel/GroupsPanel.Designer.cs | 88 + .../UdsAdmin/controls/panel/GroupsPanel.cs | 189 + .../controls/panel/GroupsPanel.de.resx | 192 + .../controls/panel/GroupsPanel.es.resx | 192 + .../controls/panel/GroupsPanel.fr.resx | 192 + .../UdsAdmin/controls/panel/GroupsPanel.resx | 192 + .../controls/panel/NetworksPanel.Designer.cs | 88 + .../UdsAdmin/controls/panel/NetworksPanel.cs | 151 + .../controls/panel/NetworksPanel.de.resx | 192 + .../controls/panel/NetworksPanel.es.resx | 192 + .../controls/panel/NetworksPanel.fr.resx | 192 + .../controls/panel/NetworksPanel.resx | 192 + .../panel/OsManagersPanel.Designer.cs | 86 + .../controls/panel/OsManagersPanel.cs | 105 + .../controls/panel/OsManagersPanel.de.resx | 195 + .../controls/panel/OsManagersPanel.es.resx | 195 + .../controls/panel/OsManagersPanel.fr.resx | 195 + .../controls/panel/OsManagersPanel.resx | 195 + .../controls/panel/PanelEmpty.Designer.cs | 56 + .../UdsAdmin/controls/panel/PanelEmpty.cs | 55 + .../controls/panel/PanelEmpty.de.resx | 165 + .../controls/panel/PanelEmpty.es.resx | 165 + .../controls/panel/PanelEmpty.fr.resx | 165 + .../UdsAdmin/controls/panel/PanelEmpty.resx | 165 + .../panel/PublicationsPanel.Designer.cs | 94 + .../controls/panel/PublicationsPanel.cs | 154 + .../controls/panel/PublicationsPanel.de.resx | 213 + .../controls/panel/PublicationsPanel.es.resx | 213 + .../controls/panel/PublicationsPanel.fr.resx | 213 + .../controls/panel/PublicationsPanel.resx | 213 + .../controls/panel/ServicePanel.Designer.cs | 86 + .../UdsAdmin/controls/panel/ServicePanel.cs | 108 + .../controls/panel/ServicePanel.de.resx | 195 + .../controls/panel/ServicePanel.es.resx | 195 + .../controls/panel/ServicePanel.fr.resx | 195 + .../UdsAdmin/controls/panel/ServicePanel.resx | 195 + .../panel/ServiceProvidersPanel.Designer.cs | 86 + .../controls/panel/ServiceProvidersPanel.cs | 105 + .../panel/ServiceProvidersPanel.de.resx | 195 + .../panel/ServiceProvidersPanel.es.resx | 195 + .../panel/ServiceProvidersPanel.fr.resx | 195 + .../controls/panel/ServiceProvidersPanel.resx | 195 + .../controls/panel/ServicesPanel.Designer.cs | 86 + .../UdsAdmin/controls/panel/ServicesPanel.cs | 108 + .../controls/panel/ServicesPanel.de.resx | 195 + .../controls/panel/ServicesPanel.es.resx | 195 + .../controls/panel/ServicesPanel.fr.resx | 195 + .../controls/panel/ServicesPanel.resx | 195 + .../panel/TransportsPanel.Designer.cs | 94 + .../controls/panel/TransportsPanel.cs | 156 + .../controls/panel/TransportsPanel.de.resx | 132 + .../controls/panel/TransportsPanel.es.resx | 198 + .../controls/panel/TransportsPanel.fr.resx | 198 + .../controls/panel/TransportsPanel.resx | 207 + .../controls/panel/UsersPanel.Designer.cs | 103 + .../UdsAdmin/controls/panel/UsersPanel.cs | 261 + .../controls/panel/UsersPanel.de.resx | 210 + .../controls/panel/UsersPanel.es.resx | 210 + .../controls/panel/UsersPanel.fr.resx | 210 + .../UdsAdmin/controls/panel/UsersPanel.resx | 210 + .../exceptions/AuthenticationException.cs | 45 + .../exceptions/CommunicationException.cs | 45 + .../exceptions/NewVersionRequiredException.cs | 51 + .../UdsAdmin/exceptions/UdsException.cs | 45 + .../UdsAdmin/forms/AboutBoxForm.Designer.cs | 129 + .../UdsAdmin/forms/AboutBoxForm.cs | 133 + .../UdsAdmin/forms/AboutBoxForm.de.resx | 890 + .../UdsAdmin/forms/AboutBoxForm.es.resx | 627 + .../UdsAdmin/forms/AboutBoxForm.fr.resx | 890 + .../UdsAdmin/forms/AboutBoxForm.resx | 890 + .../UdsAdmin/forms/AssignDeployed.Designer.cs | 146 + .../UdsAdmin/forms/AssignDeployed.cs | 119 + .../UdsAdmin/forms/AssignDeployed.de.resx | 139 + .../UdsAdmin/forms/AssignDeployed.es.resx | 135 + .../UdsAdmin/forms/AssignDeployed.fr.resx | 135 + .../UdsAdmin/forms/AssignDeployed.resx | 417 + .../forms/AuthenticatorForm.Designer.cs | 207 + .../UdsAdmin/forms/AuthenticatorForm.cs | 137 + .../UdsAdmin/forms/AuthenticatorForm.de.resx | 147 + .../UdsAdmin/forms/AuthenticatorForm.es.resx | 154 + .../UdsAdmin/forms/AuthenticatorForm.fr.resx | 154 + .../UdsAdmin/forms/AuthenticatorForm.resx | 561 + .../forms/ConfigurationForm.Designer.cs | 88 + .../UdsAdmin/forms/ConfigurationForm.cs | 141 + .../UdsAdmin/forms/ConfigurationForm.de.resx | 183 + .../UdsAdmin/forms/ConfigurationForm.es.resx | 183 + .../UdsAdmin/forms/ConfigurationForm.fr.resx | 183 + .../UdsAdmin/forms/ConfigurationForm.resx | 258 + .../forms/DeployedGroupForm.Designer.cs | 127 + .../UdsAdmin/forms/DeployedGroupForm.cs | 94 + .../UdsAdmin/forms/DeployedGroupForm.de.resx | 210 + .../UdsAdmin/forms/DeployedGroupForm.es.resx | 210 + .../UdsAdmin/forms/DeployedGroupForm.fr.resx | 210 + .../UdsAdmin/forms/DeployedGroupForm.resx | 366 + .../forms/DeployedServiceForm.Designer.cs | 325 + .../UdsAdmin/forms/DeployedServiceForm.cs | 229 + .../forms/DeployedServiceForm.de.resx | 194 + .../forms/DeployedServiceForm.es.resx | 551 + .../forms/DeployedServiceForm.fr.resx | 575 + .../UdsAdmin/forms/DeployedServiceForm.resx | 1015 + .../forms/DeployedTransportForm.Designer.cs | 108 + .../UdsAdmin/forms/DeployedTransportForm.cs | 83 + .../forms/DeployedTransportForm.de.resx | 210 + .../forms/DeployedTransportForm.es.resx | 210 + .../forms/DeployedTransportForm.fr.resx | 210 + .../UdsAdmin/forms/DeployedTransportForm.resx | 315 + .../UdsAdmin/forms/FileDownloader.Designer.cs | 92 + .../UdsAdmin/forms/FileDownloader.cs | 142 + .../UdsAdmin/forms/FileDownloader.de.resx | 133 + .../UdsAdmin/forms/FileDownloader.es.resx | 258 + .../UdsAdmin/forms/FileDownloader.fr.resx | 258 + .../UdsAdmin/forms/FileDownloader.resx | 261 + .../UdsAdmin/forms/GroupForm.Designer.cs | 175 + .../UdsAdmin/forms/GroupForm.cs | 109 + .../UdsAdmin/forms/GroupForm.de.resx | 166 + .../UdsAdmin/forms/GroupForm.es.resx | 331 + .../UdsAdmin/forms/GroupForm.fr.resx | 331 + .../UdsAdmin/forms/GroupForm.resx | 543 + .../UdsAdmin/forms/LoginForm.Designer.cs | 157 + .../UdsAdmin/forms/LoginForm.cs | 199 + .../UdsAdmin/forms/LoginForm.de.resx | 457 + .../UdsAdmin/forms/LoginForm.es.resx | 151 + .../UdsAdmin/forms/LoginForm.fr.resx | 457 + .../UdsAdmin/forms/LoginForm.resx | 457 + .../UdsAdmin/forms/MainForm.Designer.cs | 278 + .../administration/UdsAdmin/forms/MainForm.cs | 677 + .../UdsAdmin/forms/MainForm.de.resx | 287 + .../UdsAdmin/forms/MainForm.es.resx | 247 + .../UdsAdmin/forms/MainForm.fr.resx | 247 + .../UdsAdmin/forms/MainForm.resx | 531 + .../UdsAdmin/forms/NetworkForm.Designer.cs | 138 + .../UdsAdmin/forms/NetworkForm.cs | 124 + .../UdsAdmin/forms/NetworkForm.de.resx | 417 + .../UdsAdmin/forms/NetworkForm.es.resx | 417 + .../UdsAdmin/forms/NetworkForm.fr.resx | 417 + .../UdsAdmin/forms/NetworkForm.resx | 420 + .../UdsAdmin/forms/OSManagerForm.Designer.cs | 174 + .../UdsAdmin/forms/OSManagerForm.cs | 137 + .../UdsAdmin/forms/OSManagerForm.de.resx | 306 + .../UdsAdmin/forms/OSManagerForm.es.resx | 306 + .../UdsAdmin/forms/OSManagerForm.fr.resx | 306 + .../UdsAdmin/forms/OSManagerForm.resx | 507 + .../UdsAdmin/forms/SearchForm.Designer.cs | 146 + .../UdsAdmin/forms/SearchForm.cs | 113 + .../UdsAdmin/forms/SearchForm.de.resx | 399 + .../UdsAdmin/forms/SearchForm.es.resx | 399 + .../UdsAdmin/forms/SearchForm.fr.resx | 399 + .../UdsAdmin/forms/SearchForm.resx | 399 + .../UdsAdmin/forms/ServiceForm.Designer.cs | 154 + .../UdsAdmin/forms/ServiceForm.cs | 127 + .../UdsAdmin/forms/ServiceForm.de.resx | 279 + .../UdsAdmin/forms/ServiceForm.es.resx | 279 + .../UdsAdmin/forms/ServiceForm.fr.resx | 279 + .../UdsAdmin/forms/ServiceForm.resx | 450 + .../forms/ServiceProviderForm.Designer.cs | 174 + .../UdsAdmin/forms/ServiceProviderForm.cs | 126 + .../forms/ServiceProviderForm.de.resx | 504 + .../forms/ServiceProviderForm.es.resx | 151 + .../forms/ServiceProviderForm.fr.resx | 504 + .../UdsAdmin/forms/ServiceProviderForm.resx | 507 + .../UdsAdmin/forms/TransportForm.Designer.cs | 251 + .../UdsAdmin/forms/TransportForm.cs | 158 + .../UdsAdmin/forms/TransportForm.de.resx | 672 + .../UdsAdmin/forms/TransportForm.es.resx | 672 + .../UdsAdmin/forms/TransportForm.fr.resx | 672 + .../UdsAdmin/forms/TransportForm.resx | 861 + .../UdsAdmin/forms/UserForm.Designer.cs | 284 + .../administration/UdsAdmin/forms/UserForm.cs | 192 + .../UdsAdmin/forms/UserForm.de.resx | 494 + .../UdsAdmin/forms/UserForm.es.resx | 494 + .../UdsAdmin/forms/UserForm.fr.resx | 494 + .../UdsAdmin/forms/UserForm.resx | 859 + .../forms/UserPreferencesForm.Designer.cs | 88 + .../UdsAdmin/forms/UserPreferencesForm.cs | 108 + .../forms/UserPreferencesForm.de.resx | 183 + .../forms/UserPreferencesForm.es.resx | 183 + .../forms/UserPreferencesForm.fr.resx | 183 + .../UdsAdmin/forms/UserPreferencesForm.resx | 255 + .../administration/UdsAdmin/gui/ActionTree.cs | 577 + client/administration/UdsAdmin/gui/Colors.cs | 78 + .../UdsAdmin/gui/DinamycFieldsManager.cs | 661 + client/administration/UdsAdmin/gui/Helpers.cs | 102 + .../UdsAdmin/gui/ListViewSorter.cs | 162 + .../UdsAdmin/gui/MenusManager.cs | 239 + .../UdsAdmin/gui/UserNotifier.cs | 63 + .../obj/x86/Debug/UdsAdmin.TrustInfo.xml | 12 + .../UdsAdmin/xmlrpc/Constants.cs | 64 + .../UdsAdmin/xmlrpc/ExceptionExplainer.cs | 115 + .../UdsAdmin/xmlrpc/IUDSAdmin.cs | 338 + .../UdsAdmin/xmlrpc/UDSAdminService.cs | 727 + client/administration/UdsAdmin/xmlrpc/Util.cs | 76 + .../administration/UdsAdmin/xmlrpc/structs.cs | 474 + .../installer/UDSAdminInstaller/.project | 11 + .../UDSAdminInstaller/UDSAdminSetup.exe | Bin 0 -> 520046 bytes .../installer/UDSAdminInstaller/license.txt | 27 + .../installer/UDSAdminInstaller/udsadmin.nsi | 163 + .../xmlrpc/CookComputing.XmlRpcV2.dll | Bin 0 -> 122880 bytes linuxActor/.project | 17 + linuxActor/.pydevproject | 10 + .../org.eclipse.core.resources.prefs | 8 + .../org.eclipse.mylyn.tasks.ui.prefs | 4 + linuxActor/src/Makefile | 26 + linuxActor/src/actor.py | 97 + linuxActor/src/debian/changelog | 3 + linuxActor/src/debian/compat | 1 + linuxActor/src/debian/config | 46 + linuxActor/src/debian/control | 14 + linuxActor/src/debian/copyright | 26 + linuxActor/src/debian/dirs | 2 + linuxActor/src/debian/docs | 1 + linuxActor/src/debian/files | 1 + linuxActor/src/debian/init | 21 + linuxActor/src/debian/po/POTFILES.in | 1 + linuxActor/src/debian/po/templates.pot | 58 + linuxActor/src/debian/postinst | 38 + linuxActor/src/debian/rules | 44 + linuxActor/src/debian/templates | 19 + linuxActor/src/readme.txt | 1 + linuxActor/src/runActor.sh | 4 + linuxActor/src/uds/__init__.py | 0 linuxActor/src/uds/actor/__init__.py | 26 + linuxActor/src/uds/actor/config.py | 50 + linuxActor/src/uds/actor/daemon.py | 132 + linuxActor/src/uds/actor/net.py | 62 + linuxActor/src/uds/actor/renamer/__init__.py | 23 + linuxActor/src/uds/actor/renamer/debian.py | 35 + linuxActor/src/uds/actor/rpc.py | 89 + linuxActor/src/udsactor.cfg | 13 + linuxActorNX/.project | 11 + .../org.eclipse.mylyn.tasks.ui.prefs | 4 + linuxActorNX/src/Makefile | 13 + linuxActorNX/src/debian/changelog | 3 + linuxActorNX/src/debian/compat | 1 + linuxActorNX/src/debian/control | 14 + linuxActorNX/src/debian/copyright | 26 + linuxActorNX/src/debian/dirs | 0 linuxActorNX/src/debian/docs | 1 + linuxActorNX/src/debian/files | 1 + linuxActorNX/src/debian/postinst | 37 + linuxActorNX/src/debian/postrm | 28 + linuxActorNX/src/debian/rules | 41 + linuxActorNX/src/readme.txt | 1 + linuxActorNX/src/udsnxstart.sh | 3 + linuxActorNX/src/udsnxstop.sh | 3 + nxtransport/.classpath | 6 + nxtransport/.project | 17 + .../org.eclipse.mylyn.tasks.ui.prefs | 4 + nxtransport/description.jardesc | 19 + nxtransport/jar/nxtransport.jar | Bin 0 -> 24418 bytes nxtransport/src/NxTransportApplet.java | 135 + .../src/es/virtualcable/nx/LinuxApplet.java | 104 + .../src/es/virtualcable/nx/MacApplet.java | 104 + .../src/es/virtualcable/nx/NXPassword.java | 122 + .../src/es/virtualcable/nx/NxFile.java | 192 + .../src/es/virtualcable/nx/OsApplet.java | 15 + .../src/es/virtualcable/nx/WindowsApplet.java | 86 + .../es/virtualcable/windows/WinRegistry.java | 392 + nxtransport/src/manifest | 3 + nxtuntransport/.classpath | 6 + nxtuntransport/.project | 17 + .../org.eclipse.mylyn.tasks.ui.prefs | 4 + nxtuntransport/description.jardesc | 19 + nxtuntransport/jar/nxtuntransport.jar | Bin 0 -> 28785 bytes nxtuntransport/src/NxTunTransportApplet.java | 135 + .../es/virtualcable/nx/FreePortFinder.java | 40 + .../src/es/virtualcable/nx/LinuxApplet.java | 130 + .../src/es/virtualcable/nx/MacApplet.java | 130 + .../src/es/virtualcable/nx/NXPassword.java | 122 + .../src/es/virtualcable/nx/NxFile.java | 192 + .../src/es/virtualcable/nx/OsApplet.java | 15 + .../src/es/virtualcable/nx/WindowsApplet.java | 116 + .../src/es/virtualcable/nx/util.java | 49 + .../es/virtualcable/windows/WinRegistry.java | 392 + nxtuntransport/src/manifest | 3 + rdptransport/Debug/rdppass.dll | Bin 0 -> 31744 bytes rdptransport/Release/rdppass.dll | Bin 0 -> 40448 bytes rdptransport/java/.classpath | 6 + rdptransport/java/.project | 17 + .../org.eclipse.core.resources.prefs | 3 + .../org.eclipse.mylyn.tasks.ui.prefs | 4 + rdptransport/java/descrdp.jardesc | 21 + rdptransport/java/description.jardesc | 21 + rdptransport/java/jar/rdp.jar | Bin 0 -> 24270 bytes rdptransport/java/src/RdpApplet.java | 135 + .../es/virtualcable/rdp/FreePortFinder.java | 40 + .../src/es/virtualcable/rdp/LinuxApplet.java | 187 + .../src/es/virtualcable/rdp/MacApplet.java | 206 + .../src/es/virtualcable/rdp/OsApplet.java | 15 + .../src/es/virtualcable/rdp/WinRdpFile.java | 94 + .../es/virtualcable/rdp/WindowsApplet.java | 133 + .../java/src/es/virtualcable/rdp/util.java | 49 + rdptransport/java/src/manifest | 3 + .../src/net/sourceforge/jdpapi/DPAPI.java | 27 + .../sourceforge/jdpapi/DPAPIException.java | 28 + .../net/sourceforge/jdpapi/DataProtector.java | 75 + rdptransport/rdppass.sln | 26 + rdptransport/rdppass.suo | Bin 0 -> 15872 bytes rdptransport/rdppass/ReadMe.txt | 54 + rdptransport/rdppass/dllmain.cpp | 19 + rdptransport/rdppass/dpapi.cpp | 133 + .../rdppass/net_sourceforge_jdpapi_DPAPI.h | 29 + rdptransport/rdppass/rdppass.cpp | 6 + rdptransport/rdppass/rdppass.vcxproj | 107 + rdptransport/rdppass/rdppass.vcxproj.filters | 45 + rdptransport/rdppass/rdppass.vcxproj.user | 3 + rdptransport/rdppass/stdafx.cpp | 8 + rdptransport/rdppass/stdafx.h | 16 + rdptransport/rdppass/targetver.h | 8 + rdptransport/testPass/ReadMe.txt | 46 + rdptransport/testPass/stdafx.cpp | 8 + rdptransport/testPass/stdafx.h | 15 + rdptransport/testPass/targetver.h | 8 + rdptransport/testPass/testPass.cpp | 115 + rdptransport/testPass/testPass.vcxproj | 93 + .../testPass/testPass.vcxproj.filters | 36 + rdptransport/testPass/testPass.vcxproj.user | 3 + server/.project | 18 + server/.pydevproject | 16 + .../org.eclipse.core.resources.prefs | 168 + .../org.eclipse.ltk.core.refactoring.prefs | 3 + .../org.eclipse.mylyn.tasks.ui.prefs | 4 + .../.settings/org.eclipse.wst.css.core.prefs | 3 + .../.settings/org.eclipse.wst.html.core.prefs | 3 + server/documentation/Makefile | 153 + .../_downloads/samples/auths/SampleAuth.py | 307 + .../_downloads/samples/auths/__init__.py | 40 + .../_downloads/samples/auths/auth.png | Bin 0 -> 1225 bytes .../samples/services/SampleProvider.py | 196 + .../samples/services/SamplePublication.py | 272 + .../samples/services/SampleService.py | 236 + .../services/SampleUserDeploymentOne.py | 373 + .../services/SampleUserDeploymentTwo.py | 469 + .../_downloads/samples/services/__init__.py | 44 + .../_downloads/samples/services/provider.png | Bin 0 -> 491 bytes .../_downloads/samples/services/service.png | Bin 0 -> 578 bytes server/documentation/_images/LogoUDS.png | Bin 0 -> 9441 bytes server/documentation/api/index.rst | 9 + server/documentation/api/models.rst | 24 + .../api/models/authentication.rst | 25 + server/documentation/api/models/other.rst | 41 + server/documentation/api/models/services.rst | 33 + server/documentation/api/models/transport.rst | 18 + server/documentation/api/modules.rst | 18 + .../api/modules/AuthenticatorModule.rst | 27 + .../documentation/api/modules/BaseModule.rst | 57 + .../documentation/api/modules/FormFields.rst | 33 + .../api/modules/ServiceModules.rst | 53 + .../api/modules/auths/Authenticator.rst | 15 + .../api/modules/services/Exceptions.rst | 9 + .../api/modules/services/Provider.rst | 27 + .../api/modules/services/Publication.rst | 30 + .../api/modules/services/Service.rst | 25 + .../api/modules/services/UserDeployment.rst | 23 + server/documentation/conf.py | 256 + .../development/architecture.rst | 36 + .../development/contributing.rst | 3 + .../documentation/development/repository.rst | 3 + .../samples/auths/Authenticator.rst | 15 + .../development/samples/samples.rst | 100 + .../samples/services/DeployedServiceOne.rst | 20 + .../samples/services/DeployedServiceTwo.rst | 20 + .../development/samples/services/Provider.rst | 19 + .../samples/services/Publication.rst | 23 + .../development/samples/services/Service.rst | 15 + .../samples/services/whatisneeded.rst | 32 + server/documentation/index.rst | 71 + server/documentation/intro/install.rst | 46 + server/documentation/intro/overview.rst | 32 + server/documentation/make.bat | 190 + server/src/manage.py | 9 + server/src/server/__init__.py | 0 server/src/server/settings.py.sample | 302 + server/src/server/urls.py | 14 + server/src/server/wsgi.py | 34 + server/src/uds/__init__.py | 61 + server/src/uds/auths/IP/Authenticator.py | 112 + server/src/uds/auths/IP/__init__.py | 40 + server/src/uds/auths/IP/auth.png | Bin 0 -> 1193 bytes .../src/uds/auths/InternalDB/Authenticator.py | 101 + server/src/uds/auths/InternalDB/__init__.py | 36 + server/src/uds/auths/InternalDB/auth.png | Bin 0 -> 1193 bytes .../src/uds/auths/RegexLdap/Authenticator.py | 381 + server/src/uds/auths/RegexLdap/__init__.py | 36 + server/src/uds/auths/RegexLdap/auth.png | Bin 0 -> 3322 bytes server/src/uds/auths/Sample/SampleAuth.py | 307 + server/src/uds/auths/Sample/__init__.py | 38 + server/src/uds/auths/Sample/auth.png | Bin 0 -> 1225 bytes .../src/uds/auths/SimpleLDAP/Authenticator.py | 424 + server/src/uds/auths/SimpleLDAP/__init__.py | 36 + server/src/uds/auths/SimpleLDAP/auth.png | Bin 0 -> 3322 bytes server/src/uds/auths/__init__.py | 62 + server/src/uds/core/BaseModule.py | 266 + server/src/uds/core/Environment.py | 205 + server/src/uds/core/Serializable.py | 94 + server/src/uds/core/__init__.py | 41 + server/src/uds/core/auths/AuthsFactory.py | 72 + .../src/uds/core/auths/BaseAuthenticator.py | 495 + server/src/uds/core/auths/Exceptions.py | 50 + server/src/uds/core/auths/Group.py | 63 + server/src/uds/core/auths/GroupsManager.py | 145 + server/src/uds/core/auths/User.py | 101 + server/src/uds/core/auths/__init__.py | 58 + server/src/uds/core/auths/auth.png | Bin 0 -> 1225 bytes server/src/uds/core/auths/auth.py | 204 + server/src/uds/core/jobs/DelayedTask.py | 57 + server/src/uds/core/jobs/DelayedTaskRunner.py | 146 + server/src/uds/core/jobs/Job.py | 62 + server/src/uds/core/jobs/JobsFactory.py | 82 + server/src/uds/core/jobs/Scheduler.py | 143 + server/src/uds/core/jobs/__init__.py | 0 server/src/uds/core/managers/CryptoManager.py | 76 + .../src/uds/core/managers/DownloadsManager.py | 95 + .../uds/core/managers/PublicationManager.py | 193 + server/src/uds/core/managers/TaskManager.py | 134 + .../src/uds/core/managers/UserPrefsManager.py | 268 + .../uds/core/managers/UserServiceManager.py | 451 + server/src/uds/core/managers/__init__.py | 0 .../src/uds/core/osmanagers/BaseOsManager.py | 128 + .../uds/core/osmanagers/OSManagersFactory.py | 60 + server/src/uds/core/osmanagers/__init__.py | 0 server/src/uds/core/osmanagers/osmanager.png | Bin 0 -> 1253 bytes server/src/uds/core/services/BaseDeployed.py | 572 + .../src/uds/core/services/BasePublication.py | 269 + server/src/uds/core/services/BaseService.py | 206 + .../uds/core/services/BaseServiceProvider.py | 162 + server/src/uds/core/services/Exceptions.py | 74 + .../core/services/ServiceProviderFactory.py | 109 + server/src/uds/core/services/__init__.py | 57 + server/src/uds/core/services/provider.png | Bin 0 -> 491 bytes server/src/uds/core/services/service.png | Bin 0 -> 578 bytes .../src/uds/core/transports/BaseTransport.py | 125 + .../uds/core/transports/TransportsFactory.py | 60 + server/src/uds/core/transports/__init__.py | 0 server/src/uds/core/transports/transport.png | Bin 0 -> 1258 bytes server/src/uds/core/ui/UserInterface.py | 779 + server/src/uds/core/ui/__init__.py | 8 + server/src/uds/core/util/AutoAttributes.py | 108 + server/src/uds/core/util/Cache.py | 127 + server/src/uds/core/util/Config.py | 249 + server/src/uds/core/util/Decorators.py | 28 + server/src/uds/core/util/Log.py | 42 + server/src/uds/core/util/OsDetector.py | 59 + server/src/uds/core/util/State.py | 135 + server/src/uds/core/util/StateQueue.py | 76 + server/src/uds/core/util/Storage.py | 109 + server/src/uds/core/util/UniqueIDGenerator.py | 108 + .../src/uds/core/util/UniqueMacGenerator.py | 59 + .../src/uds/core/util/UniqueNameGenerator.py | 56 + server/src/uds/core/util/__init__.py | 0 server/src/uds/core/util/connection.py | 49 + server/src/uds/core/util/db/LockingManager.py | 73 + server/src/uds/core/util/db/__init__.py | 0 server/src/uds/core/util/modfinder.py | 54 + .../src/uds/core/workers/AssignedAndUnused.py | 57 + server/src/uds/core/workers/CacheCleaner.py | 52 + .../core/workers/DeployedServiceCleaner.py | 113 + .../uds/core/workers/PublicationCleaner.py | 72 + .../uds/core/workers/ServiceCacheUpdater.py | 245 + .../uds/core/workers/UserServiceCleaner.py | 74 + server/src/uds/core/workers/__init__.py | 0 server/src/uds/dispatchers/__init__.py | 0 server/src/uds/dispatchers/pam/__init__.py | 0 server/src/uds/dispatchers/pam/urls.py | 17 + server/src/uds/dispatchers/pam/views.py | 43 + .../src/uds/locale/de/LC_MESSAGES/django.mo | Bin 0 -> 27889 bytes .../src/uds/locale/de/LC_MESSAGES/django.po | 1543 + .../src/uds/locale/es/LC_MESSAGES/django.po | 1531 + .../src/uds/locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 28203 bytes .../src/uds/locale/fr/LC_MESSAGES/django.po | 1546 + server/src/uds/management/__init__.py | 0 .../src/uds/management/commands/__init__.py | 0 server/src/uds/management/commands/config.py | 59 + .../uds/management/commands/taskManager.py | 101 + server/src/uds/migrations/0001_initial.py | 537 + ...__del_unique_userpreference_name_module.py | 200 + ...del_field_deployedservice_authenticator.py | 199 + ...o__add_field_deployedservice_state_date.py | 200 + .../0005_auto__add_field_config_crypt.py | 201 + server/src/uds/migrations/__init__.py | 0 server/src/uds/models.py | 1652 + .../LinuxOsManager/LinuxOsManager.py | 156 + .../uds/osmanagers/LinuxOsManager/__init__.py | 45 + .../LinuxOsManager/files/udsactor_1.0_all.deb | Bin 0 -> 9072 bytes .../osmanagers/LinuxOsManager/losmanager.png | Bin 0 -> 1203 bytes .../uds/osmanagers/NoneOsManager/Manager.py | 72 + .../uds/osmanagers/NoneOsManager/__init__.py | 38 + .../osmanagers/NoneOsManager/osmanager.png | Bin 0 -> 1248 bytes .../WindowsOsManager/WinDomainOsManager.py | 85 + .../WindowsOsManager/WindowsOsManager.py | 158 + .../osmanagers/WindowsOsManager/__init__.py | 25 + .../WindowsOsManager/files/UDSActorSetup.exe | Bin 0 -> 323233 bytes .../WindowsOsManager/wosmanager.png | Bin 0 -> 6211 bytes server/src/uds/osmanagers/__init__.py | 41 + .../PhysicalMachines/IPMachineDeployed.py | 100 + .../PhysicalMachines/IPMachinesService.py | 106 + .../PhysicalMachines/ServiceProvider.py | 71 + .../uds/services/PhysicalMachines/__init__.py | 38 + .../uds/services/PhysicalMachines/machine.png | Bin 0 -> 1111 bytes .../services/PhysicalMachines/provider.png | Bin 0 -> 696 bytes .../src/uds/services/Sample/SampleProvider.py | 196 + .../uds/services/Sample/SamplePublication.py | 272 + .../src/uds/services/Sample/SampleService.py | 236 + .../Sample/SampleUserDeploymentOne.py | 373 + .../Sample/SampleUserDeploymentTwo.py | 469 + server/src/uds/services/Sample/__init__.py | 44 + server/src/uds/services/Sample/provider.png | Bin 0 -> 491 bytes server/src/uds/services/Sample/service.png | Bin 0 -> 578 bytes server/src/uds/services/__init__.py | 63 + server/src/uds/static/css/reset.css | 46 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 159 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../smoothness/jquery-ui-1.8.17.custom.css | 307 + server/src/uds/static/css/uds.css | 228 + server/src/uds/static/img/2downarrow.png | Bin 0 -> 587 bytes server/src/uds/static/img/2uparrow.png | Bin 0 -> 656 bytes server/src/uds/static/img/access.png | Bin 0 -> 1265 bytes server/src/uds/static/img/bg_barra_pie.png | Bin 0 -> 227 bytes .../src/uds/static/img/bg_barra_superior2.png | Bin 0 -> 207 bytes server/src/uds/static/img/bg_uds.png | Bin 0 -> 65759 bytes server/src/uds/static/img/down.png | Bin 0 -> 198 bytes server/src/uds/static/img/exit.png | Bin 0 -> 1252 bytes server/src/uds/static/img/favicon.ico | Bin 0 -> 1366 bytes server/src/uds/static/img/favicon.png | Bin 0 -> 600 bytes server/src/uds/static/img/flags/de.png | Bin 0 -> 209 bytes server/src/uds/static/img/flags/en.png | Bin 0 -> 251 bytes server/src/uds/static/img/flags/es.png | Bin 0 -> 232 bytes server/src/uds/static/img/flags/fr.png | Bin 0 -> 208 bytes server/src/uds/static/img/ico-mas.png | Bin 0 -> 149 bytes server/src/uds/static/img/ico-menos.png | Bin 0 -> 125 bytes server/src/uds/static/img/mas.png | Bin 0 -> 221 bytes server/src/uds/static/img/menos.png | Bin 0 -> 163 bytes server/src/uds/static/img/unknown.png | Bin 0 -> 1189 bytes server/src/uds/static/img/up.png | Bin 0 -> 184 bytes server/src/uds/static/img/volver.png | Bin 0 -> 163 bytes server/src/uds/static/js/PluginDetect_Java.js | 7 + server/src/uds/static/js/jquery-1.7.1.js | 9266 +++++ server/src/uds/static/js/jquery-1.7.1.min.js | 4 + .../static/js/jquery-ui-1.8.17.custom.min.js | 42 + server/src/uds/static/other/getJavaInfo.jar | Bin 0 -> 587 bytes server/src/uds/templates/404.html | 10 + server/src/uds/templates/uds/base.html | 49 + server/src/uds/templates/uds/detectJava.html | 40 + server/src/uds/templates/uds/downloads.html | 25 + server/src/uds/templates/uds/error.html | 16 + server/src/uds/templates/uds/index.html | 85 + .../src/uds/templates/uds/internal_page.html | 44 + server/src/uds/templates/uds/login.html | 98 + server/src/uds/templates/uds/prefs.html | 21 + .../uds/templates/uds/service_not_ready.html | 8 + .../src/uds/templates/uds/show_transport.html | 13 + .../templates/uds/snippets/admin_user.html | 10 + .../templates/uds/snippets/back_to_list.html | 4 + .../src/uds/templates/uds/snippets/lang.html | 21 + server/src/uds/tests.py | 66 + server/src/uds/transports/NX/NXTransport.py | 179 + server/src/uds/transports/NX/__init__.py | 50 + .../uds/transports/NX/applet/nxtransport.jar | Bin 0 -> 24418 bytes .../NX/files/udsactor-nx_1.0_all.deb | Bin 0 -> 2584 bytes server/src/uds/transports/NX/nx.png | Bin 0 -> 1027 bytes server/src/uds/transports/NX/web.py | 90 + server/src/uds/transports/RDP/RDPTransport.py | 141 + .../src/uds/transports/RDP/TSRDPTransport.py | 164 + server/src/uds/transports/RDP/__init__.py | 24 + .../uds/transports/RDP/applet/launcher.jar | Bin 0 -> 316188 bytes server/src/uds/transports/RDP/applet/rdp.jar | Bin 0 -> 24270 bytes .../src/uds/transports/RDP/applet/rdppass.dll | Bin 0 -> 40448 bytes server/src/uds/transports/RDP/rdp.png | Bin 0 -> 1054 bytes server/src/uds/transports/RDP/web.py | 101 + .../src/uds/transports/TSNX/TSNXTransport.py | 200 + server/src/uds/transports/TSNX/__init__.py | 45 + .../uds/transports/TSNX/applet/launcher.jar | Bin 0 -> 316188 bytes .../transports/TSNX/applet/nxtuntransport.jar | Bin 0 -> 28785 bytes server/src/uds/transports/TSNX/tsnx.png | Bin 0 -> 1027 bytes server/src/uds/transports/TSNX/web.py | 90 + server/src/uds/transports/__init__.py | 41 + server/src/uds/urls.py | 65 + server/src/uds/views.py | 33 + server/src/uds/web/__init__.py | 0 server/src/uds/web/errors.py | 106 + server/src/uds/web/forms/LoginForm.py | 81 + server/src/uds/web/forms/__init__.py | 0 server/src/uds/web/transformers.py | 66 + server/src/uds/web/views.py | 326 + server/src/uds/xmlrpc/__init__.py | 0 server/src/uds/xmlrpc/actor/Actor.py | 86 + server/src/uds/xmlrpc/actor/__init__.py | 3 + server/src/uds/xmlrpc/auths/AdminAuth.py | 163 + server/src/uds/xmlrpc/auths/Authenticators.py | 257 + server/src/uds/xmlrpc/auths/Groups.py | 114 + .../src/uds/xmlrpc/auths/UserPreferences.py | 59 + server/src/uds/xmlrpc/auths/Users.py | 196 + server/src/uds/xmlrpc/auths/__init__.py | 3 + .../src/uds/xmlrpc/osmanagers/OSManagers.py | 184 + server/src/uds/xmlrpc/osmanagers/__init__.py | 0 .../uds/xmlrpc/services/DeployedServices.py | 296 + .../src/uds/xmlrpc/services/Publications.py | 89 + .../uds/xmlrpc/services/ServiceProviders.py | 207 + server/src/uds/xmlrpc/services/Services.py | 214 + .../xmlrpc/services/UserDeployedServices.py | 157 + server/src/uds/xmlrpc/services/__init__.py | 0 server/src/uds/xmlrpc/tools/Cache.py | 48 + server/src/uds/xmlrpc/tools/Config.py | 65 + server/src/uds/xmlrpc/tools/__init__.py | 0 server/src/uds/xmlrpc/transports/Networks.py | 113 + .../src/uds/xmlrpc/transports/Transports.py | 162 + server/src/uds/xmlrpc/transports/__init__.py | 0 server/src/uds/xmlrpc/util/Callbacks.py | 50 + server/src/uds/xmlrpc/util/Exceptions.py | 82 + server/src/uds/xmlrpc/util/Helpers.py | 56 + server/src/uds/xmlrpc/util/TestTransport.py | 60 + server/src/uds/xmlrpc/util/__init__.py | 0 server/src/uds/xmlrpc/views.py | 110 + ssh-tunnel/pam-http/.autotools | 42 + ssh-tunnel/pam-http/.cproject | 507 + ssh-tunnel/pam-http/.project | 76 + .../org.eclipse.cdt.managedbuilder.core.prefs | 8 + ssh-tunnel/pam-http/AUTHORS | 1 + ssh-tunnel/pam-http/COPYING | 1 + ssh-tunnel/pam-http/ChangeLog | 0 ssh-tunnel/pam-http/INSTALL | 365 + ssh-tunnel/pam-http/Makefile.am | 2 + ssh-tunnel/pam-http/Makefile.in | 710 + ssh-tunnel/pam-http/NEWS | 1 + ssh-tunnel/pam-http/README | 1 + ssh-tunnel/pam-http/aclocal.m4 | 1151 + ssh-tunnel/pam-http/compile | 143 + ssh-tunnel/pam-http/config.guess | 1517 + ssh-tunnel/pam-http/config.sub | 1760 + ssh-tunnel/pam-http/configure | 13978 +++++++ ssh-tunnel/pam-http/configure.ac | 26 + ssh-tunnel/pam-http/depcomp | 630 + ssh-tunnel/pam-http/doc/requests.txt | 18 + ssh-tunnel/pam-http/doc/setup.txt | 35 + ssh-tunnel/pam-http/install-sh | 520 + ssh-tunnel/pam-http/ltmain.sh | 9661 +++++ ssh-tunnel/pam-http/missing | 376 + ssh-tunnel/pam-http/src/Makefile.am | 17 + ssh-tunnel/pam-http/src/Makefile.in | 671 + ssh-tunnel/pam-http/src/group.c | 42 + ssh-tunnel/pam-http/src/http.c | 120 + ssh-tunnel/pam-http/src/http.h | 13 + ssh-tunnel/pam-http/src/pam_uds.c | 109 + ssh-tunnel/pam-http/src/passwd.c | 104 + ssh-tunnel/pam-http/src/shadow.c | 49 + ssh-tunnel/pam-http/src/test.c | 29 + ssh-tunnel/tunnelLaucher/.classpath | 6 + ssh-tunnel/tunnelLaucher/.project | 17 + .../.settings/org.eclipse.jdt.core.prefs | 12 + ssh-tunnel/tunnelLaucher/description.jardesc | 19 + ssh-tunnel/tunnelLaucher/jar/launcher.jar | Bin 0 -> 316188 bytes .../src/com/jcraft/jsch/Buffer.java | 254 + .../src/com/jcraft/jsch/Channel.java | 665 + .../jcraft/jsch/ChannelAgentForwarding.java | 227 + .../com/jcraft/jsch/ChannelDirectTCPIP.java | 194 + .../src/com/jcraft/jsch/ChannelExec.java | 83 + .../jcraft/jsch/ChannelForwardedTCPIP.java | 311 + .../src/com/jcraft/jsch/ChannelSession.java | 276 + .../src/com/jcraft/jsch/ChannelSftp.java | 2651 ++ .../src/com/jcraft/jsch/ChannelShell.java | 70 + .../src/com/jcraft/jsch/ChannelSubsystem.java | 83 + .../src/com/jcraft/jsch/ChannelX11.java | 273 + .../src/com/jcraft/jsch/Cipher.java | 40 + .../src/com/jcraft/jsch/CipherNone.java | 42 + .../src/com/jcraft/jsch/Compression.java | 38 + .../tunnelLaucher/src/com/jcraft/jsch/DH.java | 39 + .../src/com/jcraft/jsch/DHG1.java | 310 + .../src/com/jcraft/jsch/DHG14.java | 310 + .../src/com/jcraft/jsch/DHGEX.java | 340 + .../com/jcraft/jsch/ForwardedTCPIPDaemon.java | 36 + .../src/com/jcraft/jsch/GSSContext.java | 38 + .../src/com/jcraft/jsch/HASH.java | 37 + .../src/com/jcraft/jsch/HostKey.java | 104 + .../com/jcraft/jsch/HostKeyRepository.java | 44 + .../tunnelLaucher/src/com/jcraft/jsch/IO.java | 132 + .../src/com/jcraft/jsch/Identity.java | 41 + .../src/com/jcraft/jsch/IdentityFile.java | 918 + .../src/com/jcraft/jsch/JSch.java | 301 + .../jcraft/jsch/JSchAuthCancelException.java | 45 + .../src/com/jcraft/jsch/JSchException.java | 48 + .../jcraft/jsch/JSchPartialAuthException.java | 45 + .../src/com/jcraft/jsch/KeyExchange.java | 159 + .../src/com/jcraft/jsch/KeyPair.java | 729 + .../src/com/jcraft/jsch/KeyPairDSA.java | 221 + .../src/com/jcraft/jsch/KeyPairGenDSA.java | 39 + .../src/com/jcraft/jsch/KeyPairGenRSA.java | 43 + .../src/com/jcraft/jsch/KeyPairRSA.java | 320 + .../src/com/jcraft/jsch/KnownHosts.java | 506 + .../src/com/jcraft/jsch/Logger.java | 54 + .../src/com/jcraft/jsch/MAC.java | 39 + .../src/com/jcraft/jsch/Packet.java | 115 + .../src/com/jcraft/jsch/PortWatcher.java | 194 + .../src/com/jcraft/jsch/Proxy.java | 40 + .../src/com/jcraft/jsch/ProxyHTTP.java | 180 + .../src/com/jcraft/jsch/ProxySOCKS4.java | 212 + .../src/com/jcraft/jsch/ProxySOCKS5.java | 349 + .../src/com/jcraft/jsch/Random.java | 34 + .../src/com/jcraft/jsch/Request.java | 69 + .../jcraft/jsch/RequestAgentForwarding.java | 53 + .../src/com/jcraft/jsch/RequestEnv.java | 54 + .../src/com/jcraft/jsch/RequestExec.java | 58 + .../src/com/jcraft/jsch/RequestPtyReq.java | 78 + .../src/com/jcraft/jsch/RequestSftp.java | 49 + .../src/com/jcraft/jsch/RequestShell.java | 51 + .../src/com/jcraft/jsch/RequestSignal.java | 49 + .../src/com/jcraft/jsch/RequestSubsystem.java | 53 + .../com/jcraft/jsch/RequestWindowChange.java | 68 + .../src/com/jcraft/jsch/RequestX11.java | 63 + .../com/jcraft/jsch/ServerSocketFactory.java | 37 + .../src/com/jcraft/jsch/Session.java | 2030 ++ .../src/com/jcraft/jsch/SftpATTRS.java | 263 + .../src/com/jcraft/jsch/SftpException.java | 51 + .../com/jcraft/jsch/SftpProgressMonitor.java | 39 + .../src/com/jcraft/jsch/SignatureDSA.java | 39 + .../src/com/jcraft/jsch/SignatureRSA.java | 39 + .../src/com/jcraft/jsch/SocketFactory.java | 40 + .../jcraft/jsch/UIKeyboardInteractive.java | 38 + .../src/com/jcraft/jsch/UserAuth.java | 53 + .../jcraft/jsch/UserAuthGSSAPIWithMIC.java | 227 + .../jsch/UserAuthKeyboardInteractive.java | 197 + .../src/com/jcraft/jsch/UserAuthNone.java | 129 + .../src/com/jcraft/jsch/UserAuthPassword.java | 187 + .../com/jcraft/jsch/UserAuthPublicKey.java | 222 + .../src/com/jcraft/jsch/UserInfo.java | 39 + .../src/com/jcraft/jsch/Util.java | 474 + .../src/com/jcraft/jsch/jce/AES128CBC.java | 73 + .../src/com/jcraft/jsch/jce/AES128CTR.java | 73 + .../src/com/jcraft/jsch/jce/AES192CBC.java | 71 + .../src/com/jcraft/jsch/jce/AES192CTR.java | 71 + .../src/com/jcraft/jsch/jce/AES256CBC.java | 71 + .../src/com/jcraft/jsch/jce/AES256CTR.java | 71 + .../src/com/jcraft/jsch/jce/ARCFOUR.java | 68 + .../src/com/jcraft/jsch/jce/ARCFOUR128.java | 71 + .../src/com/jcraft/jsch/jce/ARCFOUR256.java | 71 + .../src/com/jcraft/jsch/jce/BlowfishCBC.java | 71 + .../src/com/jcraft/jsch/jce/DH.java | 91 + .../src/com/jcraft/jsch/jce/HMACMD5.java | 75 + .../src/com/jcraft/jsch/jce/HMACMD596.java | 77 + .../src/com/jcraft/jsch/jce/HMACSHA1.java | 75 + .../src/com/jcraft/jsch/jce/HMACSHA196.java | 76 + .../com/jcraft/jsch/jce/KeyPairGenDSA.java | 62 + .../com/jcraft/jsch/jce/KeyPairGenRSA.java | 72 + .../src/com/jcraft/jsch/jce/MD5.java | 51 + .../src/com/jcraft/jsch/jce/Random.java | 81 + .../src/com/jcraft/jsch/jce/SHA1.java | 51 + .../src/com/jcraft/jsch/jce/SignatureDSA.java | 147 + .../src/com/jcraft/jsch/jce/SignatureRSA.java | 83 + .../src/com/jcraft/jsch/jce/TripleDESCBC.java | 84 + .../src/com/jcraft/jsch/jce/TripleDESCTR.java | 84 + .../com/jcraft/jsch/jcraft/Compression.java | 140 + .../src/com/jcraft/jsch/jcraft/HMAC.java | 107 + .../src/com/jcraft/jsch/jcraft/HMACMD5.java | 51 + .../src/com/jcraft/jsch/jcraft/HMACMD596.java | 50 + .../src/com/jcraft/jsch/jcraft/HMACSHA1.java | 51 + .../com/jcraft/jsch/jcraft/HMACSHA196.java | 50 + .../com/jcraft/jsch/jgss/GSSContextKrb5.java | 177 + .../src/com/jcraft/jzlib/Adler32.java | 121 + .../src/com/jcraft/jzlib/CRC32.java | 174 + .../src/com/jcraft/jzlib/Checksum.java | 43 + .../src/com/jcraft/jzlib/Deflate.java | 1751 + .../src/com/jcraft/jzlib/Deflater.java | 147 + .../jcraft/jzlib/DeflaterOutputStream.java | 181 + .../src/com/jcraft/jzlib/GZIPException.java | 44 + .../src/com/jcraft/jzlib/GZIPHeader.java | 184 + .../src/com/jcraft/jzlib/GZIPInputStream.java | 145 + .../com/jcraft/jzlib/GZIPOutputStream.java | 90 + .../src/com/jcraft/jzlib/InfBlocks.java | 614 + .../src/com/jcraft/jzlib/InfCodes.java | 605 + .../src/com/jcraft/jzlib/InfTree.java | 520 + .../src/com/jcraft/jzlib/Inflate.java | 711 + .../src/com/jcraft/jzlib/Inflater.java | 133 + .../com/jcraft/jzlib/InflaterInputStream.java | 238 + .../src/com/jcraft/jzlib/JZlib.java | 78 + .../src/com/jcraft/jzlib/StaticTree.java | 149 + .../src/com/jcraft/jzlib/Tree.java | 365 + .../src/com/jcraft/jzlib/ZInputStream.java | 126 + .../src/com/jcraft/jzlib/ZOutputStream.java | 159 + .../src/com/jcraft/jzlib/ZStream.java | 336 + .../com/jcraft/jzlib/ZStreamException.java | 44 + .../es/virtualcable/sshtunnel/Laucher.java | 302 + ssh-tunnel/tunnelLaucher/src/manifest | 4 + .../installer/USDActorInstaller/.project | 11 + .../USDActorInstaller/UDSActorSetup.exe | Bin 0 -> 321460 bytes .../installer/USDActorInstaller/license.txt | 27 + .../installer/USDActorInstaller/udsactor.nsi | 188 + udsService/log4net/2.0/log4net.dll | Bin 0 -> 299008 bytes udsService/log4net/2.0/log4net.xml | 30205 ++++++++++++++++ udsService/log4net/3.5/log4net.dll | Bin 0 -> 299008 bytes udsService/log4net/3.5/log4net.xml | 30205 ++++++++++++++++ udsService/rpc/IUDS.cs | 17 + udsService/rpc/Info/Computer.cs | 89 + udsService/rpc/Info/DomainInfo.cs | 79 + udsService/rpc/Info/OsInfo.cs | 111 + udsService/rpc/Operation.cs | 145 + udsService/rpc/Properties/AssemblyInfo.cs | 38 + udsService/rpc/config.cs | 73 + udsService/rpc/rpc.cs | 212 + udsService/rpc/tools.csproj | 88 + udsService/udsService.sln | 66 + udsService/udsService/Application.cs | 87 + .../udsService/Properties/AssemblyInfo.cs | 40 + .../udsService/Sens/EventSystemRegistrar.cs | 334 + udsService/udsService/Service.cs | 336 + udsService/udsService/ServiceInstaller.cs | 171 + udsService/udsService/app.config | 7 + udsService/udsService/logging.cfg | 16 + udsService/udsService/udsService.csproj | 139 + udsService/udsService/udsService.manifest | 11 + udsService/udsgui/Lang.Designer.cs | 108 + udsService/udsgui/Lang.de.resx | 135 + udsService/udsgui/Lang.es.resx | 135 + udsService/udsgui/Lang.fr.resx | 135 + udsService/udsgui/Lang.resx | 135 + udsService/udsgui/Properties/AssemblyInfo.cs | 38 + udsService/udsgui/forms/Config.Designer.cs | 115 + udsService/udsgui/forms/Config.cs | 58 + udsService/udsgui/forms/Config.de.resx | 153 + udsService/udsgui/forms/Config.es.resx | 153 + udsService/udsgui/forms/Config.fr.resx | 153 + udsService/udsgui/forms/Config.resx | 330 + udsService/udsgui/gui.cs | 20 + udsService/udsgui/udsgui.csproj | 114 + udsService/xmlrpc/CookComputing.XmlRpcV2.dll | Bin 0 -> 122880 bytes 923 files changed, 231017 insertions(+) create mode 100644 client/administration/UdsAdmin.sln create mode 100644 client/administration/UdsAdmin/Images.Designer.cs create mode 100644 client/administration/UdsAdmin/Images.resx create mode 100644 client/administration/UdsAdmin/Program.cs create mode 100644 client/administration/UdsAdmin/Properties/AssemblyInfo.cs create mode 100644 client/administration/UdsAdmin/Properties/Resources.Designer.cs create mode 100644 client/administration/UdsAdmin/Properties/Resources.resx create mode 100644 client/administration/UdsAdmin/Properties/Settings.Designer.cs create mode 100644 client/administration/UdsAdmin/Properties/Settings.settings create mode 100644 client/administration/UdsAdmin/Properties/app.manifest create mode 100644 client/administration/UdsAdmin/Resources/Image1.png create mode 100644 client/administration/UdsAdmin/Resources/apply16.png create mode 100644 client/administration/UdsAdmin/Resources/assignedServices16.png create mode 100644 client/administration/UdsAdmin/Resources/authenticators16.png create mode 100644 client/administration/UdsAdmin/Resources/cache16.png create mode 100644 client/administration/UdsAdmin/Resources/cancel16.png create mode 100644 client/administration/UdsAdmin/Resources/connectivity16.png create mode 100644 client/administration/UdsAdmin/Resources/delete16.png create mode 100644 client/administration/UdsAdmin/Resources/deployedService16.png create mode 100644 client/administration/UdsAdmin/Resources/deployedServices16.ico create mode 100644 client/administration/UdsAdmin/Resources/deployedServices16.png create mode 100644 client/administration/UdsAdmin/Resources/downarrow16.png create mode 100644 client/administration/UdsAdmin/Resources/empty16.png create mode 100644 client/administration/UdsAdmin/Resources/find16.png create mode 100644 client/administration/UdsAdmin/Resources/groups16.ico create mode 100644 client/administration/UdsAdmin/Resources/groups16.png create mode 100644 client/administration/UdsAdmin/Resources/networks16.png create mode 100644 client/administration/UdsAdmin/Resources/new16.png create mode 100644 client/administration/UdsAdmin/Resources/osmanagers16.png create mode 100644 client/administration/UdsAdmin/Resources/publications16.png create mode 100644 client/administration/UdsAdmin/Resources/serviceProviders16.png create mode 100644 client/administration/UdsAdmin/Resources/services16.png create mode 100644 client/administration/UdsAdmin/Resources/stats16.png create mode 100644 client/administration/UdsAdmin/Resources/transports16.png create mode 100644 client/administration/UdsAdmin/Resources/uparrow16.png create mode 100644 client/administration/UdsAdmin/Resources/users16.ico create mode 100644 client/administration/UdsAdmin/Resources/users16.png create mode 100644 client/administration/UdsAdmin/Strings.Designer.cs create mode 100644 client/administration/UdsAdmin/Strings.de.Designer.cs create mode 100644 client/administration/UdsAdmin/Strings.de.resx create mode 100644 client/administration/UdsAdmin/Strings.es.Designer.cs create mode 100644 client/administration/UdsAdmin/Strings.es.resx create mode 100644 client/administration/UdsAdmin/Strings.fr.Designer.cs create mode 100644 client/administration/UdsAdmin/Strings.fr.resx create mode 100644 client/administration/UdsAdmin/Strings.resx create mode 100644 client/administration/UdsAdmin/UdsAdmin.csproj create mode 100644 client/administration/UdsAdmin/UdsAdmin_TemporaryKey.pfx create mode 100644 client/administration/UdsAdmin/app.config create mode 100644 client/administration/UdsAdmin/controls/ListEditor.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/ListEditor.cs create mode 100644 client/administration/UdsAdmin/controls/ListEditor.de.resx create mode 100644 client/administration/UdsAdmin/controls/ListEditor.es.resx create mode 100644 client/administration/UdsAdmin/controls/ListEditor.fr.resx create mode 100644 client/administration/UdsAdmin/controls/ListEditor.resx create mode 100644 client/administration/UdsAdmin/controls/forms/ListEditorForm.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/forms/ListEditorForm.cs create mode 100644 client/administration/UdsAdmin/controls/forms/ListEditorForm.de.resx create mode 100644 client/administration/UdsAdmin/controls/forms/ListEditorForm.es.resx create mode 100644 client/administration/UdsAdmin/controls/forms/ListEditorForm.fr.resx create mode 100644 client/administration/UdsAdmin/controls/forms/ListEditorForm.resx create mode 100644 client/administration/UdsAdmin/controls/panel/AuthsPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/AuthsPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/AuthsPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/AuthsPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/AuthsPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/AuthsPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicePanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicePanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicePanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicePanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicePanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicePanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/GroupsPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/GroupsPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/GroupsPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/GroupsPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/GroupsPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/GroupsPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/NetworksPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/NetworksPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/NetworksPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/NetworksPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/NetworksPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/NetworksPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/OsManagersPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/OsManagersPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/OsManagersPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/OsManagersPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/OsManagersPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/OsManagersPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PanelEmpty.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/PanelEmpty.cs create mode 100644 client/administration/UdsAdmin/controls/panel/PanelEmpty.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PanelEmpty.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PanelEmpty.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PanelEmpty.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PublicationsPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/PublicationsPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/PublicationsPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PublicationsPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PublicationsPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/PublicationsPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicePanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/ServicePanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/ServicePanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicePanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicePanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicePanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicesPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/ServicesPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/ServicesPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicesPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicesPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/ServicesPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/TransportsPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/TransportsPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/TransportsPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/TransportsPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/TransportsPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/TransportsPanel.resx create mode 100644 client/administration/UdsAdmin/controls/panel/UsersPanel.Designer.cs create mode 100644 client/administration/UdsAdmin/controls/panel/UsersPanel.cs create mode 100644 client/administration/UdsAdmin/controls/panel/UsersPanel.de.resx create mode 100644 client/administration/UdsAdmin/controls/panel/UsersPanel.es.resx create mode 100644 client/administration/UdsAdmin/controls/panel/UsersPanel.fr.resx create mode 100644 client/administration/UdsAdmin/controls/panel/UsersPanel.resx create mode 100644 client/administration/UdsAdmin/exceptions/AuthenticationException.cs create mode 100644 client/administration/UdsAdmin/exceptions/CommunicationException.cs create mode 100644 client/administration/UdsAdmin/exceptions/NewVersionRequiredException.cs create mode 100644 client/administration/UdsAdmin/exceptions/UdsException.cs create mode 100644 client/administration/UdsAdmin/forms/AboutBoxForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/AboutBoxForm.cs create mode 100644 client/administration/UdsAdmin/forms/AboutBoxForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/AboutBoxForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/AboutBoxForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/AboutBoxForm.resx create mode 100644 client/administration/UdsAdmin/forms/AssignDeployed.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/AssignDeployed.cs create mode 100644 client/administration/UdsAdmin/forms/AssignDeployed.de.resx create mode 100644 client/administration/UdsAdmin/forms/AssignDeployed.es.resx create mode 100644 client/administration/UdsAdmin/forms/AssignDeployed.fr.resx create mode 100644 client/administration/UdsAdmin/forms/AssignDeployed.resx create mode 100644 client/administration/UdsAdmin/forms/AuthenticatorForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/AuthenticatorForm.cs create mode 100644 client/administration/UdsAdmin/forms/AuthenticatorForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/AuthenticatorForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/AuthenticatorForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/AuthenticatorForm.resx create mode 100644 client/administration/UdsAdmin/forms/ConfigurationForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/ConfigurationForm.cs create mode 100644 client/administration/UdsAdmin/forms/ConfigurationForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/ConfigurationForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/ConfigurationForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/ConfigurationForm.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedGroupForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/DeployedGroupForm.cs create mode 100644 client/administration/UdsAdmin/forms/DeployedGroupForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedGroupForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedGroupForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedGroupForm.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedServiceForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/DeployedServiceForm.cs create mode 100644 client/administration/UdsAdmin/forms/DeployedServiceForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedServiceForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedServiceForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedServiceForm.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedTransportForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/DeployedTransportForm.cs create mode 100644 client/administration/UdsAdmin/forms/DeployedTransportForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedTransportForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedTransportForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/DeployedTransportForm.resx create mode 100644 client/administration/UdsAdmin/forms/FileDownloader.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/FileDownloader.cs create mode 100644 client/administration/UdsAdmin/forms/FileDownloader.de.resx create mode 100644 client/administration/UdsAdmin/forms/FileDownloader.es.resx create mode 100644 client/administration/UdsAdmin/forms/FileDownloader.fr.resx create mode 100644 client/administration/UdsAdmin/forms/FileDownloader.resx create mode 100644 client/administration/UdsAdmin/forms/GroupForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/GroupForm.cs create mode 100644 client/administration/UdsAdmin/forms/GroupForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/GroupForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/GroupForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/GroupForm.resx create mode 100644 client/administration/UdsAdmin/forms/LoginForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/LoginForm.cs create mode 100644 client/administration/UdsAdmin/forms/LoginForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/LoginForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/LoginForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/LoginForm.resx create mode 100644 client/administration/UdsAdmin/forms/MainForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/MainForm.cs create mode 100644 client/administration/UdsAdmin/forms/MainForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/MainForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/MainForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/MainForm.resx create mode 100644 client/administration/UdsAdmin/forms/NetworkForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/NetworkForm.cs create mode 100644 client/administration/UdsAdmin/forms/NetworkForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/NetworkForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/NetworkForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/NetworkForm.resx create mode 100644 client/administration/UdsAdmin/forms/OSManagerForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/OSManagerForm.cs create mode 100644 client/administration/UdsAdmin/forms/OSManagerForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/OSManagerForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/OSManagerForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/OSManagerForm.resx create mode 100644 client/administration/UdsAdmin/forms/SearchForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/SearchForm.cs create mode 100644 client/administration/UdsAdmin/forms/SearchForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/SearchForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/SearchForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/SearchForm.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/ServiceForm.cs create mode 100644 client/administration/UdsAdmin/forms/ServiceForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceForm.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceProviderForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/ServiceProviderForm.cs create mode 100644 client/administration/UdsAdmin/forms/ServiceProviderForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceProviderForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceProviderForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/ServiceProviderForm.resx create mode 100644 client/administration/UdsAdmin/forms/TransportForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/TransportForm.cs create mode 100644 client/administration/UdsAdmin/forms/TransportForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/TransportForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/TransportForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/TransportForm.resx create mode 100644 client/administration/UdsAdmin/forms/UserForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/UserForm.cs create mode 100644 client/administration/UdsAdmin/forms/UserForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/UserForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/UserForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/UserForm.resx create mode 100644 client/administration/UdsAdmin/forms/UserPreferencesForm.Designer.cs create mode 100644 client/administration/UdsAdmin/forms/UserPreferencesForm.cs create mode 100644 client/administration/UdsAdmin/forms/UserPreferencesForm.de.resx create mode 100644 client/administration/UdsAdmin/forms/UserPreferencesForm.es.resx create mode 100644 client/administration/UdsAdmin/forms/UserPreferencesForm.fr.resx create mode 100644 client/administration/UdsAdmin/forms/UserPreferencesForm.resx create mode 100644 client/administration/UdsAdmin/gui/ActionTree.cs create mode 100644 client/administration/UdsAdmin/gui/Colors.cs create mode 100644 client/administration/UdsAdmin/gui/DinamycFieldsManager.cs create mode 100644 client/administration/UdsAdmin/gui/Helpers.cs create mode 100644 client/administration/UdsAdmin/gui/ListViewSorter.cs create mode 100644 client/administration/UdsAdmin/gui/MenusManager.cs create mode 100644 client/administration/UdsAdmin/gui/UserNotifier.cs create mode 100644 client/administration/UdsAdmin/obj/x86/Debug/UdsAdmin.TrustInfo.xml create mode 100644 client/administration/UdsAdmin/xmlrpc/Constants.cs create mode 100644 client/administration/UdsAdmin/xmlrpc/ExceptionExplainer.cs create mode 100644 client/administration/UdsAdmin/xmlrpc/IUDSAdmin.cs create mode 100644 client/administration/UdsAdmin/xmlrpc/UDSAdminService.cs create mode 100644 client/administration/UdsAdmin/xmlrpc/Util.cs create mode 100644 client/administration/UdsAdmin/xmlrpc/structs.cs create mode 100644 client/administration/installer/UDSAdminInstaller/.project create mode 100644 client/administration/installer/UDSAdminInstaller/UDSAdminSetup.exe create mode 100644 client/administration/installer/UDSAdminInstaller/license.txt create mode 100644 client/administration/installer/UDSAdminInstaller/udsadmin.nsi create mode 100644 client/administration/xmlrpc/CookComputing.XmlRpcV2.dll create mode 100644 linuxActor/.project create mode 100644 linuxActor/.pydevproject create mode 100644 linuxActor/.settings/org.eclipse.core.resources.prefs create mode 100644 linuxActor/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 linuxActor/src/Makefile create mode 100644 linuxActor/src/actor.py create mode 100644 linuxActor/src/debian/changelog create mode 100644 linuxActor/src/debian/compat create mode 100644 linuxActor/src/debian/config create mode 100644 linuxActor/src/debian/control create mode 100644 linuxActor/src/debian/copyright create mode 100644 linuxActor/src/debian/dirs create mode 100644 linuxActor/src/debian/docs create mode 100644 linuxActor/src/debian/files create mode 100755 linuxActor/src/debian/init create mode 100644 linuxActor/src/debian/po/POTFILES.in create mode 100644 linuxActor/src/debian/po/templates.pot create mode 100644 linuxActor/src/debian/postinst create mode 100755 linuxActor/src/debian/rules create mode 100644 linuxActor/src/debian/templates create mode 100644 linuxActor/src/readme.txt create mode 100644 linuxActor/src/runActor.sh create mode 100644 linuxActor/src/uds/__init__.py create mode 100644 linuxActor/src/uds/actor/__init__.py create mode 100644 linuxActor/src/uds/actor/config.py create mode 100644 linuxActor/src/uds/actor/daemon.py create mode 100644 linuxActor/src/uds/actor/net.py create mode 100644 linuxActor/src/uds/actor/renamer/__init__.py create mode 100644 linuxActor/src/uds/actor/renamer/debian.py create mode 100644 linuxActor/src/uds/actor/rpc.py create mode 100644 linuxActor/src/udsactor.cfg create mode 100644 linuxActorNX/.project create mode 100644 linuxActorNX/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 linuxActorNX/src/Makefile create mode 100644 linuxActorNX/src/debian/changelog create mode 100644 linuxActorNX/src/debian/compat create mode 100644 linuxActorNX/src/debian/control create mode 100644 linuxActorNX/src/debian/copyright create mode 100644 linuxActorNX/src/debian/dirs create mode 100644 linuxActorNX/src/debian/docs create mode 100644 linuxActorNX/src/debian/files create mode 100644 linuxActorNX/src/debian/postinst create mode 100644 linuxActorNX/src/debian/postrm create mode 100755 linuxActorNX/src/debian/rules create mode 100644 linuxActorNX/src/readme.txt create mode 100644 linuxActorNX/src/udsnxstart.sh create mode 100644 linuxActorNX/src/udsnxstop.sh create mode 100644 nxtransport/.classpath create mode 100644 nxtransport/.project create mode 100644 nxtransport/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 nxtransport/description.jardesc create mode 100644 nxtransport/jar/nxtransport.jar create mode 100644 nxtransport/src/NxTransportApplet.java create mode 100644 nxtransport/src/es/virtualcable/nx/LinuxApplet.java create mode 100644 nxtransport/src/es/virtualcable/nx/MacApplet.java create mode 100644 nxtransport/src/es/virtualcable/nx/NXPassword.java create mode 100644 nxtransport/src/es/virtualcable/nx/NxFile.java create mode 100644 nxtransport/src/es/virtualcable/nx/OsApplet.java create mode 100644 nxtransport/src/es/virtualcable/nx/WindowsApplet.java create mode 100644 nxtransport/src/es/virtualcable/windows/WinRegistry.java create mode 100644 nxtransport/src/manifest create mode 100644 nxtuntransport/.classpath create mode 100644 nxtuntransport/.project create mode 100644 nxtuntransport/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 nxtuntransport/description.jardesc create mode 100644 nxtuntransport/jar/nxtuntransport.jar create mode 100644 nxtuntransport/src/NxTunTransportApplet.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/FreePortFinder.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/LinuxApplet.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/MacApplet.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/NXPassword.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/NxFile.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/OsApplet.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/WindowsApplet.java create mode 100644 nxtuntransport/src/es/virtualcable/nx/util.java create mode 100644 nxtuntransport/src/es/virtualcable/windows/WinRegistry.java create mode 100644 nxtuntransport/src/manifest create mode 100644 rdptransport/Debug/rdppass.dll create mode 100644 rdptransport/Release/rdppass.dll create mode 100644 rdptransport/java/.classpath create mode 100644 rdptransport/java/.project create mode 100644 rdptransport/java/.settings/org.eclipse.core.resources.prefs create mode 100644 rdptransport/java/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 rdptransport/java/descrdp.jardesc create mode 100644 rdptransport/java/description.jardesc create mode 100644 rdptransport/java/jar/rdp.jar create mode 100644 rdptransport/java/src/RdpApplet.java create mode 100644 rdptransport/java/src/es/virtualcable/rdp/FreePortFinder.java create mode 100644 rdptransport/java/src/es/virtualcable/rdp/LinuxApplet.java create mode 100644 rdptransport/java/src/es/virtualcable/rdp/MacApplet.java create mode 100644 rdptransport/java/src/es/virtualcable/rdp/OsApplet.java create mode 100644 rdptransport/java/src/es/virtualcable/rdp/WinRdpFile.java create mode 100644 rdptransport/java/src/es/virtualcable/rdp/WindowsApplet.java create mode 100644 rdptransport/java/src/es/virtualcable/rdp/util.java create mode 100644 rdptransport/java/src/manifest create mode 100644 rdptransport/java/src/net/sourceforge/jdpapi/DPAPI.java create mode 100644 rdptransport/java/src/net/sourceforge/jdpapi/DPAPIException.java create mode 100644 rdptransport/java/src/net/sourceforge/jdpapi/DataProtector.java create mode 100644 rdptransport/rdppass.sln create mode 100644 rdptransport/rdppass.suo create mode 100644 rdptransport/rdppass/ReadMe.txt create mode 100644 rdptransport/rdppass/dllmain.cpp create mode 100644 rdptransport/rdppass/dpapi.cpp create mode 100644 rdptransport/rdppass/net_sourceforge_jdpapi_DPAPI.h create mode 100644 rdptransport/rdppass/rdppass.cpp create mode 100644 rdptransport/rdppass/rdppass.vcxproj create mode 100644 rdptransport/rdppass/rdppass.vcxproj.filters create mode 100644 rdptransport/rdppass/rdppass.vcxproj.user create mode 100644 rdptransport/rdppass/stdafx.cpp create mode 100644 rdptransport/rdppass/stdafx.h create mode 100644 rdptransport/rdppass/targetver.h create mode 100644 rdptransport/testPass/ReadMe.txt create mode 100644 rdptransport/testPass/stdafx.cpp create mode 100644 rdptransport/testPass/stdafx.h create mode 100644 rdptransport/testPass/targetver.h create mode 100644 rdptransport/testPass/testPass.cpp create mode 100644 rdptransport/testPass/testPass.vcxproj create mode 100644 rdptransport/testPass/testPass.vcxproj.filters create mode 100644 rdptransport/testPass/testPass.vcxproj.user create mode 100644 server/.project create mode 100644 server/.pydevproject create mode 100644 server/.settings/org.eclipse.core.resources.prefs create mode 100644 server/.settings/org.eclipse.ltk.core.refactoring.prefs create mode 100644 server/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 server/.settings/org.eclipse.wst.css.core.prefs create mode 100644 server/.settings/org.eclipse.wst.html.core.prefs create mode 100644 server/documentation/Makefile create mode 100644 server/documentation/_downloads/samples/auths/SampleAuth.py create mode 100644 server/documentation/_downloads/samples/auths/__init__.py create mode 100644 server/documentation/_downloads/samples/auths/auth.png create mode 100644 server/documentation/_downloads/samples/services/SampleProvider.py create mode 100644 server/documentation/_downloads/samples/services/SamplePublication.py create mode 100644 server/documentation/_downloads/samples/services/SampleService.py create mode 100644 server/documentation/_downloads/samples/services/SampleUserDeploymentOne.py create mode 100644 server/documentation/_downloads/samples/services/SampleUserDeploymentTwo.py create mode 100644 server/documentation/_downloads/samples/services/__init__.py create mode 100644 server/documentation/_downloads/samples/services/provider.png create mode 100644 server/documentation/_downloads/samples/services/service.png create mode 100644 server/documentation/_images/LogoUDS.png create mode 100644 server/documentation/api/index.rst create mode 100644 server/documentation/api/models.rst create mode 100644 server/documentation/api/models/authentication.rst create mode 100644 server/documentation/api/models/other.rst create mode 100644 server/documentation/api/models/services.rst create mode 100644 server/documentation/api/models/transport.rst create mode 100644 server/documentation/api/modules.rst create mode 100644 server/documentation/api/modules/AuthenticatorModule.rst create mode 100644 server/documentation/api/modules/BaseModule.rst create mode 100644 server/documentation/api/modules/FormFields.rst create mode 100644 server/documentation/api/modules/ServiceModules.rst create mode 100644 server/documentation/api/modules/auths/Authenticator.rst create mode 100644 server/documentation/api/modules/services/Exceptions.rst create mode 100644 server/documentation/api/modules/services/Provider.rst create mode 100644 server/documentation/api/modules/services/Publication.rst create mode 100644 server/documentation/api/modules/services/Service.rst create mode 100644 server/documentation/api/modules/services/UserDeployment.rst create mode 100644 server/documentation/conf.py create mode 100644 server/documentation/development/architecture.rst create mode 100644 server/documentation/development/contributing.rst create mode 100644 server/documentation/development/repository.rst create mode 100644 server/documentation/development/samples/auths/Authenticator.rst create mode 100644 server/documentation/development/samples/samples.rst create mode 100644 server/documentation/development/samples/services/DeployedServiceOne.rst create mode 100644 server/documentation/development/samples/services/DeployedServiceTwo.rst create mode 100644 server/documentation/development/samples/services/Provider.rst create mode 100644 server/documentation/development/samples/services/Publication.rst create mode 100644 server/documentation/development/samples/services/Service.rst create mode 100644 server/documentation/development/samples/services/whatisneeded.rst create mode 100644 server/documentation/index.rst create mode 100644 server/documentation/intro/install.rst create mode 100644 server/documentation/intro/overview.rst create mode 100644 server/documentation/make.bat create mode 100755 server/src/manage.py create mode 100644 server/src/server/__init__.py create mode 100644 server/src/server/settings.py.sample create mode 100644 server/src/server/urls.py create mode 100644 server/src/server/wsgi.py create mode 100644 server/src/uds/__init__.py create mode 100644 server/src/uds/auths/IP/Authenticator.py create mode 100644 server/src/uds/auths/IP/__init__.py create mode 100644 server/src/uds/auths/IP/auth.png create mode 100644 server/src/uds/auths/InternalDB/Authenticator.py create mode 100644 server/src/uds/auths/InternalDB/__init__.py create mode 100644 server/src/uds/auths/InternalDB/auth.png create mode 100644 server/src/uds/auths/RegexLdap/Authenticator.py create mode 100644 server/src/uds/auths/RegexLdap/__init__.py create mode 100644 server/src/uds/auths/RegexLdap/auth.png create mode 100644 server/src/uds/auths/Sample/SampleAuth.py create mode 100644 server/src/uds/auths/Sample/__init__.py create mode 100644 server/src/uds/auths/Sample/auth.png create mode 100644 server/src/uds/auths/SimpleLDAP/Authenticator.py create mode 100644 server/src/uds/auths/SimpleLDAP/__init__.py create mode 100644 server/src/uds/auths/SimpleLDAP/auth.png create mode 100644 server/src/uds/auths/__init__.py create mode 100644 server/src/uds/core/BaseModule.py create mode 100644 server/src/uds/core/Environment.py create mode 100644 server/src/uds/core/Serializable.py create mode 100644 server/src/uds/core/__init__.py create mode 100644 server/src/uds/core/auths/AuthsFactory.py create mode 100644 server/src/uds/core/auths/BaseAuthenticator.py create mode 100644 server/src/uds/core/auths/Exceptions.py create mode 100644 server/src/uds/core/auths/Group.py create mode 100644 server/src/uds/core/auths/GroupsManager.py create mode 100644 server/src/uds/core/auths/User.py create mode 100644 server/src/uds/core/auths/__init__.py create mode 100644 server/src/uds/core/auths/auth.png create mode 100644 server/src/uds/core/auths/auth.py create mode 100644 server/src/uds/core/jobs/DelayedTask.py create mode 100644 server/src/uds/core/jobs/DelayedTaskRunner.py create mode 100644 server/src/uds/core/jobs/Job.py create mode 100644 server/src/uds/core/jobs/JobsFactory.py create mode 100644 server/src/uds/core/jobs/Scheduler.py create mode 100644 server/src/uds/core/jobs/__init__.py create mode 100644 server/src/uds/core/managers/CryptoManager.py create mode 100644 server/src/uds/core/managers/DownloadsManager.py create mode 100644 server/src/uds/core/managers/PublicationManager.py create mode 100644 server/src/uds/core/managers/TaskManager.py create mode 100644 server/src/uds/core/managers/UserPrefsManager.py create mode 100644 server/src/uds/core/managers/UserServiceManager.py create mode 100644 server/src/uds/core/managers/__init__.py create mode 100644 server/src/uds/core/osmanagers/BaseOsManager.py create mode 100644 server/src/uds/core/osmanagers/OSManagersFactory.py create mode 100644 server/src/uds/core/osmanagers/__init__.py create mode 100644 server/src/uds/core/osmanagers/osmanager.png create mode 100644 server/src/uds/core/services/BaseDeployed.py create mode 100644 server/src/uds/core/services/BasePublication.py create mode 100644 server/src/uds/core/services/BaseService.py create mode 100644 server/src/uds/core/services/BaseServiceProvider.py create mode 100644 server/src/uds/core/services/Exceptions.py create mode 100644 server/src/uds/core/services/ServiceProviderFactory.py create mode 100644 server/src/uds/core/services/__init__.py create mode 100644 server/src/uds/core/services/provider.png create mode 100644 server/src/uds/core/services/service.png create mode 100644 server/src/uds/core/transports/BaseTransport.py create mode 100644 server/src/uds/core/transports/TransportsFactory.py create mode 100644 server/src/uds/core/transports/__init__.py create mode 100644 server/src/uds/core/transports/transport.png create mode 100644 server/src/uds/core/ui/UserInterface.py create mode 100644 server/src/uds/core/ui/__init__.py create mode 100644 server/src/uds/core/util/AutoAttributes.py create mode 100644 server/src/uds/core/util/Cache.py create mode 100644 server/src/uds/core/util/Config.py create mode 100644 server/src/uds/core/util/Decorators.py create mode 100644 server/src/uds/core/util/Log.py create mode 100644 server/src/uds/core/util/OsDetector.py create mode 100644 server/src/uds/core/util/State.py create mode 100644 server/src/uds/core/util/StateQueue.py create mode 100644 server/src/uds/core/util/Storage.py create mode 100644 server/src/uds/core/util/UniqueIDGenerator.py create mode 100644 server/src/uds/core/util/UniqueMacGenerator.py create mode 100644 server/src/uds/core/util/UniqueNameGenerator.py create mode 100644 server/src/uds/core/util/__init__.py create mode 100644 server/src/uds/core/util/connection.py create mode 100644 server/src/uds/core/util/db/LockingManager.py create mode 100644 server/src/uds/core/util/db/__init__.py create mode 100644 server/src/uds/core/util/modfinder.py create mode 100644 server/src/uds/core/workers/AssignedAndUnused.py create mode 100644 server/src/uds/core/workers/CacheCleaner.py create mode 100644 server/src/uds/core/workers/DeployedServiceCleaner.py create mode 100644 server/src/uds/core/workers/PublicationCleaner.py create mode 100644 server/src/uds/core/workers/ServiceCacheUpdater.py create mode 100644 server/src/uds/core/workers/UserServiceCleaner.py create mode 100644 server/src/uds/core/workers/__init__.py create mode 100644 server/src/uds/dispatchers/__init__.py create mode 100644 server/src/uds/dispatchers/pam/__init__.py create mode 100644 server/src/uds/dispatchers/pam/urls.py create mode 100644 server/src/uds/dispatchers/pam/views.py create mode 100644 server/src/uds/locale/de/LC_MESSAGES/django.mo create mode 100644 server/src/uds/locale/de/LC_MESSAGES/django.po create mode 100644 server/src/uds/locale/es/LC_MESSAGES/django.po create mode 100644 server/src/uds/locale/fr/LC_MESSAGES/django.mo create mode 100644 server/src/uds/locale/fr/LC_MESSAGES/django.po create mode 100644 server/src/uds/management/__init__.py create mode 100644 server/src/uds/management/commands/__init__.py create mode 100644 server/src/uds/management/commands/config.py create mode 100644 server/src/uds/management/commands/taskManager.py create mode 100644 server/src/uds/migrations/0001_initial.py create mode 100644 server/src/uds/migrations/0002_auto__del_unique_userpreference_name_module.py create mode 100644 server/src/uds/migrations/0003_auto__del_field_deployedservice_authenticator.py create mode 100644 server/src/uds/migrations/0004_auto__add_field_deployedservice_state_date.py create mode 100644 server/src/uds/migrations/0005_auto__add_field_config_crypt.py create mode 100644 server/src/uds/migrations/__init__.py create mode 100644 server/src/uds/models.py create mode 100644 server/src/uds/osmanagers/LinuxOsManager/LinuxOsManager.py create mode 100644 server/src/uds/osmanagers/LinuxOsManager/__init__.py create mode 100644 server/src/uds/osmanagers/LinuxOsManager/files/udsactor_1.0_all.deb create mode 100644 server/src/uds/osmanagers/LinuxOsManager/losmanager.png create mode 100644 server/src/uds/osmanagers/NoneOsManager/Manager.py create mode 100644 server/src/uds/osmanagers/NoneOsManager/__init__.py create mode 100644 server/src/uds/osmanagers/NoneOsManager/osmanager.png create mode 100644 server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py create mode 100644 server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py create mode 100644 server/src/uds/osmanagers/WindowsOsManager/__init__.py create mode 100644 server/src/uds/osmanagers/WindowsOsManager/files/UDSActorSetup.exe create mode 100644 server/src/uds/osmanagers/WindowsOsManager/wosmanager.png create mode 100644 server/src/uds/osmanagers/__init__.py create mode 100644 server/src/uds/services/PhysicalMachines/IPMachineDeployed.py create mode 100644 server/src/uds/services/PhysicalMachines/IPMachinesService.py create mode 100644 server/src/uds/services/PhysicalMachines/ServiceProvider.py create mode 100644 server/src/uds/services/PhysicalMachines/__init__.py create mode 100644 server/src/uds/services/PhysicalMachines/machine.png create mode 100644 server/src/uds/services/PhysicalMachines/provider.png create mode 100644 server/src/uds/services/Sample/SampleProvider.py create mode 100644 server/src/uds/services/Sample/SamplePublication.py create mode 100644 server/src/uds/services/Sample/SampleService.py create mode 100644 server/src/uds/services/Sample/SampleUserDeploymentOne.py create mode 100644 server/src/uds/services/Sample/SampleUserDeploymentTwo.py create mode 100644 server/src/uds/services/Sample/__init__.py create mode 100644 server/src/uds/services/Sample/provider.png create mode 100644 server/src/uds/services/Sample/service.png create mode 100644 server/src/uds/services/__init__.py create mode 100644 server/src/uds/static/css/reset.css create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-icons_222222_256x240.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-icons_2e83ff_256x240.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-icons_454545_256x240.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-icons_888888_256x240.png create mode 100644 server/src/uds/static/css/smoothness/images/ui-icons_cd0a0a_256x240.png create mode 100644 server/src/uds/static/css/smoothness/jquery-ui-1.8.17.custom.css create mode 100644 server/src/uds/static/css/uds.css create mode 100644 server/src/uds/static/img/2downarrow.png create mode 100644 server/src/uds/static/img/2uparrow.png create mode 100644 server/src/uds/static/img/access.png create mode 100644 server/src/uds/static/img/bg_barra_pie.png create mode 100644 server/src/uds/static/img/bg_barra_superior2.png create mode 100644 server/src/uds/static/img/bg_uds.png create mode 100644 server/src/uds/static/img/down.png create mode 100644 server/src/uds/static/img/exit.png create mode 100644 server/src/uds/static/img/favicon.ico create mode 100644 server/src/uds/static/img/favicon.png create mode 100644 server/src/uds/static/img/flags/de.png create mode 100644 server/src/uds/static/img/flags/en.png create mode 100644 server/src/uds/static/img/flags/es.png create mode 100644 server/src/uds/static/img/flags/fr.png create mode 100644 server/src/uds/static/img/ico-mas.png create mode 100644 server/src/uds/static/img/ico-menos.png create mode 100644 server/src/uds/static/img/mas.png create mode 100644 server/src/uds/static/img/menos.png create mode 100644 server/src/uds/static/img/unknown.png create mode 100644 server/src/uds/static/img/up.png create mode 100644 server/src/uds/static/img/volver.png create mode 100644 server/src/uds/static/js/PluginDetect_Java.js create mode 100644 server/src/uds/static/js/jquery-1.7.1.js create mode 100644 server/src/uds/static/js/jquery-1.7.1.min.js create mode 100644 server/src/uds/static/js/jquery-ui-1.8.17.custom.min.js create mode 100644 server/src/uds/static/other/getJavaInfo.jar create mode 100644 server/src/uds/templates/404.html create mode 100644 server/src/uds/templates/uds/base.html create mode 100644 server/src/uds/templates/uds/detectJava.html create mode 100644 server/src/uds/templates/uds/downloads.html create mode 100644 server/src/uds/templates/uds/error.html create mode 100644 server/src/uds/templates/uds/index.html create mode 100644 server/src/uds/templates/uds/internal_page.html create mode 100644 server/src/uds/templates/uds/login.html create mode 100644 server/src/uds/templates/uds/prefs.html create mode 100644 server/src/uds/templates/uds/service_not_ready.html create mode 100644 server/src/uds/templates/uds/show_transport.html create mode 100644 server/src/uds/templates/uds/snippets/admin_user.html create mode 100644 server/src/uds/templates/uds/snippets/back_to_list.html create mode 100644 server/src/uds/templates/uds/snippets/lang.html create mode 100644 server/src/uds/tests.py create mode 100644 server/src/uds/transports/NX/NXTransport.py create mode 100644 server/src/uds/transports/NX/__init__.py create mode 100644 server/src/uds/transports/NX/applet/nxtransport.jar create mode 100644 server/src/uds/transports/NX/files/udsactor-nx_1.0_all.deb create mode 100644 server/src/uds/transports/NX/nx.png create mode 100644 server/src/uds/transports/NX/web.py create mode 100644 server/src/uds/transports/RDP/RDPTransport.py create mode 100644 server/src/uds/transports/RDP/TSRDPTransport.py create mode 100644 server/src/uds/transports/RDP/__init__.py create mode 100644 server/src/uds/transports/RDP/applet/launcher.jar create mode 100644 server/src/uds/transports/RDP/applet/rdp.jar create mode 100644 server/src/uds/transports/RDP/applet/rdppass.dll create mode 100644 server/src/uds/transports/RDP/rdp.png create mode 100644 server/src/uds/transports/RDP/web.py create mode 100644 server/src/uds/transports/TSNX/TSNXTransport.py create mode 100644 server/src/uds/transports/TSNX/__init__.py create mode 100644 server/src/uds/transports/TSNX/applet/launcher.jar create mode 100644 server/src/uds/transports/TSNX/applet/nxtuntransport.jar create mode 100644 server/src/uds/transports/TSNX/tsnx.png create mode 100644 server/src/uds/transports/TSNX/web.py create mode 100644 server/src/uds/transports/__init__.py create mode 100644 server/src/uds/urls.py create mode 100644 server/src/uds/views.py create mode 100644 server/src/uds/web/__init__.py create mode 100644 server/src/uds/web/errors.py create mode 100644 server/src/uds/web/forms/LoginForm.py create mode 100644 server/src/uds/web/forms/__init__.py create mode 100644 server/src/uds/web/transformers.py create mode 100644 server/src/uds/web/views.py create mode 100644 server/src/uds/xmlrpc/__init__.py create mode 100644 server/src/uds/xmlrpc/actor/Actor.py create mode 100644 server/src/uds/xmlrpc/actor/__init__.py create mode 100644 server/src/uds/xmlrpc/auths/AdminAuth.py create mode 100644 server/src/uds/xmlrpc/auths/Authenticators.py create mode 100644 server/src/uds/xmlrpc/auths/Groups.py create mode 100644 server/src/uds/xmlrpc/auths/UserPreferences.py create mode 100644 server/src/uds/xmlrpc/auths/Users.py create mode 100644 server/src/uds/xmlrpc/auths/__init__.py create mode 100644 server/src/uds/xmlrpc/osmanagers/OSManagers.py create mode 100644 server/src/uds/xmlrpc/osmanagers/__init__.py create mode 100644 server/src/uds/xmlrpc/services/DeployedServices.py create mode 100644 server/src/uds/xmlrpc/services/Publications.py create mode 100644 server/src/uds/xmlrpc/services/ServiceProviders.py create mode 100644 server/src/uds/xmlrpc/services/Services.py create mode 100644 server/src/uds/xmlrpc/services/UserDeployedServices.py create mode 100644 server/src/uds/xmlrpc/services/__init__.py create mode 100644 server/src/uds/xmlrpc/tools/Cache.py create mode 100644 server/src/uds/xmlrpc/tools/Config.py create mode 100644 server/src/uds/xmlrpc/tools/__init__.py create mode 100644 server/src/uds/xmlrpc/transports/Networks.py create mode 100644 server/src/uds/xmlrpc/transports/Transports.py create mode 100644 server/src/uds/xmlrpc/transports/__init__.py create mode 100644 server/src/uds/xmlrpc/util/Callbacks.py create mode 100644 server/src/uds/xmlrpc/util/Exceptions.py create mode 100644 server/src/uds/xmlrpc/util/Helpers.py create mode 100644 server/src/uds/xmlrpc/util/TestTransport.py create mode 100644 server/src/uds/xmlrpc/util/__init__.py create mode 100644 server/src/uds/xmlrpc/views.py create mode 100644 ssh-tunnel/pam-http/.autotools create mode 100644 ssh-tunnel/pam-http/.cproject create mode 100644 ssh-tunnel/pam-http/.project create mode 100644 ssh-tunnel/pam-http/.settings/org.eclipse.cdt.managedbuilder.core.prefs create mode 100644 ssh-tunnel/pam-http/AUTHORS create mode 100644 ssh-tunnel/pam-http/COPYING create mode 100644 ssh-tunnel/pam-http/ChangeLog create mode 100644 ssh-tunnel/pam-http/INSTALL create mode 100644 ssh-tunnel/pam-http/Makefile.am create mode 100644 ssh-tunnel/pam-http/Makefile.in create mode 100644 ssh-tunnel/pam-http/NEWS create mode 100644 ssh-tunnel/pam-http/README create mode 100644 ssh-tunnel/pam-http/aclocal.m4 create mode 100755 ssh-tunnel/pam-http/compile create mode 100755 ssh-tunnel/pam-http/config.guess create mode 100755 ssh-tunnel/pam-http/config.sub create mode 100755 ssh-tunnel/pam-http/configure create mode 100644 ssh-tunnel/pam-http/configure.ac create mode 100755 ssh-tunnel/pam-http/depcomp create mode 100644 ssh-tunnel/pam-http/doc/requests.txt create mode 100644 ssh-tunnel/pam-http/doc/setup.txt create mode 100755 ssh-tunnel/pam-http/install-sh create mode 100644 ssh-tunnel/pam-http/ltmain.sh create mode 100755 ssh-tunnel/pam-http/missing create mode 100644 ssh-tunnel/pam-http/src/Makefile.am create mode 100644 ssh-tunnel/pam-http/src/Makefile.in create mode 100644 ssh-tunnel/pam-http/src/group.c create mode 100644 ssh-tunnel/pam-http/src/http.c create mode 100644 ssh-tunnel/pam-http/src/http.h create mode 100644 ssh-tunnel/pam-http/src/pam_uds.c create mode 100644 ssh-tunnel/pam-http/src/passwd.c create mode 100644 ssh-tunnel/pam-http/src/shadow.c create mode 100644 ssh-tunnel/pam-http/src/test.c create mode 100644 ssh-tunnel/tunnelLaucher/.classpath create mode 100644 ssh-tunnel/tunnelLaucher/.project create mode 100644 ssh-tunnel/tunnelLaucher/.settings/org.eclipse.jdt.core.prefs create mode 100644 ssh-tunnel/tunnelLaucher/description.jardesc create mode 100644 ssh-tunnel/tunnelLaucher/jar/launcher.jar create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Buffer.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Channel.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelAgentForwarding.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelDirectTCPIP.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelExec.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelForwardedTCPIP.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelSession.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelSftp.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelShell.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelSubsystem.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ChannelX11.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Cipher.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/CipherNone.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Compression.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/DH.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/DHG1.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/DHG14.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/DHGEX.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ForwardedTCPIPDaemon.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/GSSContext.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/HASH.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/HostKey.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/HostKeyRepository.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/IO.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Identity.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/IdentityFile.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/JSch.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/JSchAuthCancelException.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/JSchException.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/JSchPartialAuthException.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/KeyExchange.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/KeyPair.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/KeyPairDSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/KeyPairGenDSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/KeyPairGenRSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/KeyPairRSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/KnownHosts.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Logger.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/MAC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Packet.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/PortWatcher.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Proxy.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ProxyHTTP.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ProxySOCKS4.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ProxySOCKS5.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Random.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Request.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestAgentForwarding.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestEnv.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestExec.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestPtyReq.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestSftp.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestShell.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestSignal.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestSubsystem.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestWindowChange.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/RequestX11.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/ServerSocketFactory.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Session.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/SftpATTRS.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/SftpException.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/SftpProgressMonitor.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/SignatureDSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/SignatureRSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/SocketFactory.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UIKeyboardInteractive.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UserAuth.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UserAuthKeyboardInteractive.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UserAuthNone.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UserAuthPassword.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UserAuthPublicKey.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/UserInfo.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/Util.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/AES128CBC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/AES128CTR.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/AES192CBC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/AES192CTR.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/AES256CBC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/AES256CTR.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/ARCFOUR.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/ARCFOUR128.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/ARCFOUR256.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/BlowfishCBC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/DH.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/HMACMD5.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/HMACMD596.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/HMACSHA1.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/HMACSHA196.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/KeyPairGenDSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/KeyPairGenRSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/MD5.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/Random.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/SHA1.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/SignatureDSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/SignatureRSA.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/TripleDESCBC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jce/TripleDESCTR.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jcraft/Compression.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jcraft/HMAC.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jcraft/HMACMD5.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jcraft/HMACMD596.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jcraft/HMACSHA1.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jcraft/HMACSHA196.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jsch/jgss/GSSContextKrb5.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/Adler32.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/CRC32.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/Checksum.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/Deflate.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/Deflater.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/DeflaterOutputStream.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/GZIPException.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/GZIPHeader.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/GZIPInputStream.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/GZIPOutputStream.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/InfBlocks.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/InfCodes.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/InfTree.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/Inflate.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/Inflater.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/InflaterInputStream.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/JZlib.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/StaticTree.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/Tree.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/ZInputStream.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/ZOutputStream.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/ZStream.java create mode 100644 ssh-tunnel/tunnelLaucher/src/com/jcraft/jzlib/ZStreamException.java create mode 100644 ssh-tunnel/tunnelLaucher/src/es/virtualcable/sshtunnel/Laucher.java create mode 100644 ssh-tunnel/tunnelLaucher/src/manifest create mode 100644 udsService/installer/USDActorInstaller/.project create mode 100644 udsService/installer/USDActorInstaller/UDSActorSetup.exe create mode 100644 udsService/installer/USDActorInstaller/license.txt create mode 100644 udsService/installer/USDActorInstaller/udsactor.nsi create mode 100644 udsService/log4net/2.0/log4net.dll create mode 100644 udsService/log4net/2.0/log4net.xml create mode 100644 udsService/log4net/3.5/log4net.dll create mode 100644 udsService/log4net/3.5/log4net.xml create mode 100644 udsService/rpc/IUDS.cs create mode 100644 udsService/rpc/Info/Computer.cs create mode 100644 udsService/rpc/Info/DomainInfo.cs create mode 100644 udsService/rpc/Info/OsInfo.cs create mode 100644 udsService/rpc/Operation.cs create mode 100644 udsService/rpc/Properties/AssemblyInfo.cs create mode 100644 udsService/rpc/config.cs create mode 100644 udsService/rpc/rpc.cs create mode 100644 udsService/rpc/tools.csproj create mode 100644 udsService/udsService.sln create mode 100644 udsService/udsService/Application.cs create mode 100644 udsService/udsService/Properties/AssemblyInfo.cs create mode 100644 udsService/udsService/Sens/EventSystemRegistrar.cs create mode 100644 udsService/udsService/Service.cs create mode 100644 udsService/udsService/ServiceInstaller.cs create mode 100644 udsService/udsService/app.config create mode 100644 udsService/udsService/logging.cfg create mode 100644 udsService/udsService/udsService.csproj create mode 100644 udsService/udsService/udsService.manifest create mode 100644 udsService/udsgui/Lang.Designer.cs create mode 100644 udsService/udsgui/Lang.de.resx create mode 100644 udsService/udsgui/Lang.es.resx create mode 100644 udsService/udsgui/Lang.fr.resx create mode 100644 udsService/udsgui/Lang.resx create mode 100644 udsService/udsgui/Properties/AssemblyInfo.cs create mode 100644 udsService/udsgui/forms/Config.Designer.cs create mode 100644 udsService/udsgui/forms/Config.cs create mode 100644 udsService/udsgui/forms/Config.de.resx create mode 100644 udsService/udsgui/forms/Config.es.resx create mode 100644 udsService/udsgui/forms/Config.fr.resx create mode 100644 udsService/udsgui/forms/Config.resx create mode 100644 udsService/udsgui/gui.cs create mode 100644 udsService/udsgui/udsgui.csproj create mode 100644 udsService/xmlrpc/CookComputing.XmlRpcV2.dll diff --git a/client/administration/UdsAdmin.sln b/client/administration/UdsAdmin.sln new file mode 100644 index 000000000..6cb017487 --- /dev/null +++ b/client/administration/UdsAdmin.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UdsAdmin", "UdsAdmin\UdsAdmin.csproj", "{F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Debug|Any CPU.ActiveCfg = Debug|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Debug|x86.ActiveCfg = Debug|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Debug|x86.Build.0 = Debug|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Release|Any CPU.ActiveCfg = Release|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Release|Mixed Platforms.Build.0 = Release|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Release|x86.ActiveCfg = Release|x86 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/client/administration/UdsAdmin/Images.Designer.cs b/client/administration/UdsAdmin/Images.Designer.cs new file mode 100644 index 000000000..2427d5da0 --- /dev/null +++ b/client/administration/UdsAdmin/Images.Designer.cs @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------------ +// +// Este código fue generado por una herramienta. +// Versión de runtime:4.0.30319.239 +// +// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si +// se vuelve a generar el código. +// +//------------------------------------------------------------------------------ + +namespace UdsAdmin { + using System; + + + /// + /// Clase de recurso con establecimiento inflexible de tipos, para buscar cadenas traducidas, etc. + /// + // StronglyTypedResourceBuilder generó automáticamente esta clase + // a través de una herramienta como ResGen o Visual Studio. + // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen + // con la opción /str o vuelva a generar su proyecto de VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Images { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Images() { + } + + /// + /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UdsAdmin.Images", typeof(Images).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las + /// búsquedas de recursos mediante esta clase de recurso con establecimiento inflexible de tipos. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap apply16 { + get { + object obj = ResourceManager.GetObject("apply16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap assignedServices16 { + get { + object obj = ResourceManager.GetObject("assignedServices16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap authenticators16 { + get { + object obj = ResourceManager.GetObject("authenticators16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap cache16 { + get { + object obj = ResourceManager.GetObject("cache16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap cancel16 { + get { + object obj = ResourceManager.GetObject("cancel16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap connectivity16 { + get { + object obj = ResourceManager.GetObject("connectivity16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap delete16 { + get { + object obj = ResourceManager.GetObject("delete16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap deployedService16 { + get { + object obj = ResourceManager.GetObject("deployedService16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap deployedServices16 { + get { + object obj = ResourceManager.GetObject("deployedServices16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap downarrow16 { + get { + object obj = ResourceManager.GetObject("downarrow16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap empty16 { + get { + object obj = ResourceManager.GetObject("empty16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap find16 { + get { + object obj = ResourceManager.GetObject("find16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap groups16 { + get { + object obj = ResourceManager.GetObject("groups16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Icon iconDeployedServices16 { + get { + object obj = ResourceManager.GetObject("iconDeployedServices16", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Bitmap networks16 { + get { + object obj = ResourceManager.GetObject("networks16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap new16 { + get { + object obj = ResourceManager.GetObject("new16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap osmanagers16 { + get { + object obj = ResourceManager.GetObject("osmanagers16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap publications16 { + get { + object obj = ResourceManager.GetObject("publications16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap serviceProviders16 { + get { + object obj = ResourceManager.GetObject("serviceProviders16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap services16 { + get { + object obj = ResourceManager.GetObject("services16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap transports16 { + get { + object obj = ResourceManager.GetObject("transports16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap uparrow16 { + get { + object obj = ResourceManager.GetObject("uparrow16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap users16 { + get { + object obj = ResourceManager.GetObject("users16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/client/administration/UdsAdmin/Images.resx b/client/administration/UdsAdmin/Images.resx new file mode 100644 index 000000000..ce559c08e --- /dev/null +++ b/client/administration/UdsAdmin/Images.resx @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Resources\apply16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\assignedServices16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\authenticators16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\cache16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\cancel16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\connectivity16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\delete16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\deployedService16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\deployedServices16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\downarrow16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + + Resources\empty16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\find16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\groups16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\deployedServices16.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\networks16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\new16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\osmanagers16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\publications16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\serviceProviders16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\services16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\transports16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\uparrow16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\users16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/Program.cs b/client/administration/UdsAdmin/Program.cs new file mode 100644 index 000000000..d320101e1 --- /dev/null +++ b/client/administration/UdsAdmin/Program.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using System.Globalization; +using System.Threading; + +namespace UdsAdmin +{ + static class Program + { + /// + /// Punto de entrada principal para la aplicación. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + CultureInfo culture = new CultureInfo(UdsAdmin.Properties.Settings.Default.Locale); + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + forms.LoginForm logForm = new forms.LoginForm(); + DialogResult res = logForm.ShowDialog(); + if (res == DialogResult.Cancel) + { + return; + } + Application.Run(new forms.MainForm()); + Properties.Settings.Default.Save(); + } + } +} diff --git a/client/administration/UdsAdmin/Properties/AssemblyInfo.cs b/client/administration/UdsAdmin/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..5686525ed --- /dev/null +++ b/client/administration/UdsAdmin/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Resources; + +// La información general sobre un ensamblado se controla mediante el siguiente +// conjunto de atributos. Cambie estos atributos para modificar la información +// asociada con un ensamblado. +[assembly: AssemblyTitle("UDS Administration Client")] +[assembly: AssemblyDescription("(c) 2012 Virtual Cable S.L.\nXML-RPC.NET Copyright (c) 2006 Charles Cook")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Virtual Cable S.L.")] +[assembly: AssemblyProduct("UDS Administration Client")] +[assembly: AssemblyCopyright("Copyright © Virtual Cable 2012")] +[assembly: AssemblyTrademark("UDS Admin")] +[assembly: AssemblyCulture("")] + +// Si establece ComVisible como false, los tipos de este ensamblado no estarán visibles +// para los componentes COM. Si necesita obtener acceso a un tipo de este ensamblado desde +// COM, establezca el atributo ComVisible como true en este tipo. +[assembly: ComVisible(false)] + +// El siguiente GUID sirve como identificador de typelib si este proyecto se expone a COM +[assembly: Guid("203d7325-5e07-455f-a27c-09d2c31d00a2")] + +// La información de versión de un ensamblado consta de los cuatro valores siguientes: +// +// Versión principal +// Versión secundaria +// Número de compilación +// Revisión +// +// Puede especificar todos los valores o establecer como predeterminados los números de versión de compilación y de revisión +// mediante el asterisco ('*'), como se muestra a continuación: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.7.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: NeutralResourcesLanguageAttribute("en")] diff --git a/client/administration/UdsAdmin/Properties/Resources.Designer.cs b/client/administration/UdsAdmin/Properties/Resources.Designer.cs new file mode 100644 index 000000000..8076a5335 --- /dev/null +++ b/client/administration/UdsAdmin/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Este código fue generado por una herramienta. +// Versión de runtime:4.0.30319.225 +// +// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si +// se vuelve a generar el código. +// +//------------------------------------------------------------------------------ + +namespace UdsAdmin.Properties { + using System; + + + /// + /// Clase de recurso con establecimiento inflexible de tipos, para buscar cadenas traducidas, etc. + /// + // StronglyTypedResourceBuilder generó automáticamente esta clase + // a través de una herramienta como ResGen o Visual Studio. + // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen + // con la opción /str o vuelva a generar su proyecto de VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UdsAdmin.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las + /// búsquedas de recursos mediante esta clase de recurso con establecimiento inflexible de tipos. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/client/administration/UdsAdmin/Properties/Resources.resx b/client/administration/UdsAdmin/Properties/Resources.resx new file mode 100644 index 000000000..5ea0895e3 --- /dev/null +++ b/client/administration/UdsAdmin/Properties/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/Properties/Settings.Designer.cs b/client/administration/UdsAdmin/Properties/Settings.Designer.cs new file mode 100644 index 000000000..8a67fa8cf --- /dev/null +++ b/client/administration/UdsAdmin/Properties/Settings.Designer.cs @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +// +// Este código fue generado por una herramienta. +// Versión de runtime:4.0.30319.261 +// +// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si +// se vuelve a generar el código. +// +//------------------------------------------------------------------------------ + +namespace UdsAdmin.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("en-US")] + public string Locale { + get { + return ((string)(this["Locale"])); + } + set { + this["Locale"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("20000")] + public int TimeOut { + get { + return ((int)(this["TimeOut"])); + } + set { + this["TimeOut"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("93")] + public int wUsernameCol { + get { + return ((int)(this["wUsernameCol"])); + } + set { + this["wUsernameCol"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("220")] + public int wNameCol { + get { + return ((int)(this["wNameCol"])); + } + set { + this["wNameCol"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("81")] + public int wStateCol { + get { + return ((int)(this["wStateCol"])); + } + set { + this["wStateCol"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("93")] + public int wLastAccessCol { + get { + return ((int)(this["wLastAccessCol"])); + } + set { + this["wLastAccessCol"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("250")] + public int wCommentsCol { + get { + return ((int)(this["wCommentsCol"])); + } + set { + this["wCommentsCol"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("320")] + public int MaxControlWidth { + get { + return ((int)(this["MaxControlWidth"])); + } + set { + this["MaxControlWidth"] = value; + } + } + } +} diff --git a/client/administration/UdsAdmin/Properties/Settings.settings b/client/administration/UdsAdmin/Properties/Settings.settings new file mode 100644 index 000000000..3296cf724 --- /dev/null +++ b/client/administration/UdsAdmin/Properties/Settings.settings @@ -0,0 +1,30 @@ + + + + + + en-US + + + 20000 + + + 93 + + + 220 + + + 81 + + + 93 + + + 250 + + + 320 + + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/Properties/app.manifest b/client/administration/UdsAdmin/Properties/app.manifest new file mode 100644 index 000000000..54b397fb3 --- /dev/null +++ b/client/administration/UdsAdmin/Properties/app.manifest @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/Resources/Image1.png b/client/administration/UdsAdmin/Resources/Image1.png new file mode 100644 index 0000000000000000000000000000000000000000..c0525fbb16697be117dd96d57fc0283365546e84 GIT binary patch literal 490 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10g0sLQ zvY3H^?+6GpPSxg<1{xmh>EalY;ruq@VBsMH0h_??Os5ioe^V6Zc?xPe{M#iE%ouV& zdzrZFw2j9Zxtqm=Z8n=P?tfx?_uV`bW$owAX)@iC*X6v_8~>M0;Jd&Wz_?97LxE>G z^M;lLCMyOmj>9h*bQEVa@HH?UDzGXz6~1WO?)E7@2alM|*vlNWzccu7*2?>r?_Stu zwS3i``=*aiZ78zT&yy+MZPnqRaWYGNndbW5OBF$TcYf&Iw(pNxkb$ApdG&zE<*vKW z^=v49>68Di>wxSGiN?-z2ORfJ^O}-%yj^7RaT`|SoAI2=`&MR^S10Swzo{#xIU`-r z;@^ptYjvYfyNQ3ATW)+fba_IX(b9VkvU*?gi-nKoi|oH=edM2reP5@@s#iZReye%R zuV!^*eXQ)Zy4y-qv7Bm>l;X5GI#QA~&mp?zm&-}UVVA0mEvw&gF;OXk; Jvd$@?2>=u#x`_Y) literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/apply16.png b/client/administration/UdsAdmin/Resources/apply16.png new file mode 100644 index 0000000000000000000000000000000000000000..fc9aa52497f4688cf99915a8a2473d686b1ac83d GIT binary patch literal 1108 zcmYjQdrVVj6hHUgwzQ??(L(0h0^)GG87o-a+?XI*gS@(xnLXkr&dcLG)#Q-`4JHx^aacNDu*!%c7Gg zMF%x6_+6Wj(P&s*LWH3XsF&W*-cGx6zn?t&De)sYfJr2$2CmFz8L$?Fas1}gYXd~kR(A6Jj=6yc#>ohULh86Vsbq1 zWoTKdG`X`V&Zr-lzQg=F)k7-clr6?O!*G~Q7>Xo-p#0ndKfN%e zNNBnnREW@?rs zU4C!n2gz>M4Hw7G`mWFS801p9EkTg@bDp|rvxA;y?=m-mndAlb&WUpijzGD+eVkP( z)DgKOT0I#1`+1c@qw4ltL91kIxPr7|88^;eLI96m)vSMIVsV_a3m<}bN+x(b_@F%? z36xV5us*++Ae00yd&Wx8w!Qu8?$ed;Fb?{HBYR4;MyTU9E!(1`fmY6-Nq$=VFA_SV z1P@0?+c{0JjG6(K!%pGAnuY@*(@*bKo7z;muenQ6F4Nr|8<2OI?bFFt%>ciDbU)*| zJlJnGBktomw|}8T``R(Vi=y*62V8tVLm20fb6AN>+&iqgd=~|BtQDTZtu*< zKrGp1HGB4P3^SW^EY_91IV$o*9nt0uMDDzjGAk zh4Y|@izj2_V|ck-4vEAPaM4a;*6TnXj~9GTd?C{Tc>q2ssse>v8A8L--e~#3ddg>B d`KKhZLBWNZmRxm0`o++5{uYD2b5q%Y{{X6J@*e;I literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/assignedServices16.png b/client/administration/UdsAdmin/Resources/assignedServices16.png new file mode 100644 index 0000000000000000000000000000000000000000..6a761a26ce148e11e2790bc71bc5c7245441375d GIT binary patch literal 1255 zcmYk6Sxi({7=X_?cbo094#NykK$fs5s~SN{i7*DS(YBPArb*Kc(uX!p)0d`cwDv(` zI*DtwQQSalO)Fc|kc!AAT>wF$Agc_@FfcP0oMDzbckLZXBj@Bl=k3e)|L6bD@iz}v z(waC80D!LAXRcQ6h)}3VFLQG_o@M=-9%AhxpMceaqQ1YD?B{U`jcmhEH zWf95>V?$^IMF}JbBchKo0cVX?M~@q#jb#ku#0`cheBHeS3^Z{P_~z3n$(_x*T)-B9 zE|LU|9LHg%1Xg^J+eVoljST6uYH{8W8>{cWKM4B&9_A~{cQK8(+T^_SIH|lSC*HQ? z03?(+5cHB>V}S>fbW$+Dx_4~L-fVU4r&~VT!sD|?zxm=&S7!%m`lEmHZg2m9)47J* z7ZdcxNIHJWu_m}2E4UD5^5-R}KaqIR!-@!UGLT+cq0-&EDkYO6V!4qKl--9fh=2&(6tW#6KXJ4vVV;l$kd_WW-f|Bx_ zn-Y9#U0j}KAH}9BF=vRvi>g#g4C#x2s0C7N=L`W<))jBaT&?4v~5kv zY$GWgBM5x6)y#r*V0>|XtJ^QN5d=o9tp}WdqHQE1YHBA}EJ%pklpwy>TmiQvB`9=BNm8KnG${l*f3gPW-;0P3$2=Yn z*!fze@9Nd-H=xL;QyMLpo1L2vwYn0*ODn`WSAMKx|Mju>Tk5 UXJ?q{P}M-y-h<|*^7p^{4;1-7$^ZZW literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/authenticators16.png b/client/administration/UdsAdmin/Resources/authenticators16.png new file mode 100644 index 0000000000000000000000000000000000000000..346f21e7bdab6f68ef31c1ee6e6db224fdeab0c2 GIT binary patch literal 825 zcmV-91IGM`P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iOAw z4j&`Bv(#Sz00Oy5L_t(|+KrJ(XcJ)=hTnfOnXOq(+GH{x) z@PF_7eSAMb8AmoPWH^rBCRR9B3!5t@1vZuiD2SfdV$ncF_ro_o>3ADVZ)m6|t!?dq z;0CdADCQMhy?Cw~fX~t(%3v@GkJnQ>vD$AL?O^R3I9>oJNQgAfD21b(@&G-r5Bj_= z049hfTGXww00zYZfREz_ke*SXd_?4&eagS&n$!w}| z4o(eE-ptzwOhd)@FQ0Jt&iy^s+t2^{MnzHX_77xGV^9C)D&6k zayz~KpTEDjdgH+p{qZStOu)QojOot`E4M}FBKi9V)I3G7ekM8FOAh#_w;WW(3crJ8 zV0c4_trD(05v1*aiLf&8A_8!GbdDuc=iLR(~V(GM=B9Vib__$0T_i|MjlotWwQ!&qfk=O zG{-1XUxUr_eE=unL$ZkHeFHE>4+ukYypcXe8fGmL1A%Zyv?aQ{Zc1PV&+`Ea>M|*K zYbzUTV`N>|GnZffPW7?V!U{y5(~_^Hp}xZd&-9)@^};4LQ{9l000000NkvXXu0mjf DyzOz> literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/cache16.png b/client/administration/UdsAdmin/Resources/cache16.png new file mode 100644 index 0000000000000000000000000000000000000000..8651deba72fe6f2452bc5d98444e2873dc839e4f GIT binary patch literal 940 zcmYL{aZD3e9LH-;I#-T4;x-SQt4SAS*|L^0WG3BC8pKr*7AHiqXba3@mMr320-I&e zf|8M5!!C;m&UpVYHJfp1%?z2!6OQpTS^?rL@o9vss_wthW zeed)8y>I{f^*f7Hr7DF&QDl0rW-nf!&yF=pJgXDXqEHaWOf}W}y3Vyq05tKk1VX^$ zf10{IQ&dhL(Oz!-NK8g(FWTHk^9VA6FSh*n4EMOX&{ptQr5OMQSeE^p#@7L~wrp22 z3<+|k-SWB1+i>grhu_$H&)DsARRrNh%D&EqlKnH4gNt`A-L#4@FpQmA0_#7wk%|GW zAvE)$keNHyv1o8x#4-#eM1i7Yo!%{HAQ^MQjiz^s>H+~0Ad?CD*Zh(#1)yg`jGI%h z(-4VJv_X5=H3n~bJ>wpan5wEEUcbxn4fQUYxL`1uCZ*@RJPI&%0o1aLy;*SM(N&o6 z;zH!|1LT5sQOrd7wRI#JRF~6+L>zh-qN`^Q&VDC&{4rcJ>gdiHdn%~mAq=we?VRw# z@oJ8jT9%idsEOZ#K@!vltY5#lGPx-}|JiECiCf2&wHJ_oJcnx>Jz7S z{Jyh`^1}^Qa$# zkUb&LU;0YVT^Eu`?T=y{Ayi(j)5$yT=Uc1lMGVO>=v}(I%%4tKR98nfdC|e$kHB^x zb%m!+BHA!N>Gwb7Sk_)VlPxaSQydA67k~jY_JS?lBu*s+JxT7phDx^WX&y6tajnt! zn%mCypNiAFCfY1HpHWqF0pT_sdh~(*`j$^h-#R_uVD0Qq$I`vCy;b+jU#>4$N}{s9 zEF6Bu_$*OdySWcYl0l(!N5g~G75I2wTDv9rkE(U9P4bCAulw1m#L63XO+X1n4a>5$ zxBz@~nhb61_KtSmzWKoXXJC4G3=XA(p4c-7D*g}Gh!3Cy3%~{^ArXG~QbdgoixcCc zr<^PsUQcM6CP}RP3-$f@_es+H%K1)FQ+i{tar*l>q~HyZ<#Zw%#aRaGO1lLsZ=gJW zk+b8dr#LnDllzZ~0$(Gwt>9Jp)51Htj>6qKoy*-;nLA7zt0Nn;RR1=?y9oUSji;BMZxG!CKJsO8INv z;iIosT4XV%QOY>e>*%-tE^SR2nOCCSO*mbbU)gw1{la4W=1mowC*mnCxlW_YJ!QzRr_o!HJa!ZIXP%jlx{u7=?v#&4@F?E0+$Gkt!^a~4NFef!ZGg&B5|&z zC?!e2fQf1^eVGIZ9-xYH1|yV$H4Meex!P6rjbKJk5F%OdsuFUaIx=&CnQ{0~9zQH>EZ^ z5jWSbDXI9MWmYS(LHu@TlpP$`JiZFg{b(jtv=<-|%z24q@OeZq8j+QQE4XP+yMt~e z;)983^y7FWa#~cC`C_}B7J>^Klz~B9REkB6@V^hGO5#jtd8rj*3c6fH)LK%)B5`q0 zmE~^2m`ZiJwNy-Ok13gXn+i{5~Zl3e|nlS8V)n-uLxpHg@l}(`sIth<(8> z8tT10=PzBo{ApkNkqP8(mq!MC-QQif($@K<_hDoEuNPKDsY_`a)D2esM$%9|aD?K0 Vk7j=Nk6A$=@;7%hU2p99@qbaxBpv_& literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/connectivity16.png b/client/administration/UdsAdmin/Resources/connectivity16.png new file mode 100644 index 0000000000000000000000000000000000000000..5058e5c68f2e64e083719a136adaa59aea09a0d9 GIT binary patch literal 741 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2ipq| z1STi?L;08h00L!6L_t(I%f*vlNK%MD@GT6gBuE&27NMB#%J z^nE&f;e7bQ_Z;AVPs)bq>gqbBR;$e=B_*8O?f$lHfL^cXT3TAv9LHUe$#SNAzBhAQ znQS(jPNUJBrD>WGZY(_>pQF9K{X#=SgD6|pJA9+^t+(j#p#zkF2hwYEQ)*Zdv zo#prYJIc$;^LU;|TbpG+^((oy_IzhSzWlnmHipbnGaxDi);%+sYYk!>Q>dfsW{7XUzAte5Rknkfym}){sbI8~o5ov5}Ow#m`{?ycc$AIHv z;qQP_IxR06&%{pRgD~+Ch)n{C2(Z3_Lirk=b~?_4Vg+LWy8;9t*f4s%-W^t}wb|l6 zr%DRsu=R}qaX+x+#ncxAx_!-<`>Y^KYtnbX#LDIuTrQVbrBdBw7-q1ksi`I-b9ai< z=>#`DiqrHwlF@0{XEdn0bQLe=c2QY-r9p;y<=O;jwc5by>Z(zv(}^4o2YPyX;_s&( zj^C@9$V*H31tj(z^NR#S;|nkvjgUyhhV*o45zD^r2LNhd;7O>mvO;08bj0oUz6XIo z(h7)z_b-O`NK%(gktmUQf)XyF)ECR;8F%aJ>#0a20*>N=|Jz`(7K)~Um z{NcG;Tb6DWS=KXS;*Gz2GWYk)jN)In?egzKwl1gZIURm#G_7(=u{t_A_wG4ykOeAd z4stda@HkwIUnrKy&Sz+boP zO=L-nf}>EFke$-f1ef}?HPc(F=NTPc{yzFQ&d{ap>b=Y^Xs7E4FQ(|mmL3%O7P3GZ f6hju8XmQ5Oz*4a5iqM-q4ImYsu6{1-oD!M<*?Z;; literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/deployedService16.png b/client/administration/UdsAdmin/Resources/deployedService16.png new file mode 100644 index 0000000000000000000000000000000000000000..0dae987f14f458468ce366e5001ba3a124fbc84e GIT binary patch literal 1206 zcmYk53rtg27{|Z+EG>OmpsQ$rC@UM_1cDfF8;?*Pfe}z7+Z>D738*8>X51VEi^{8+ z7~>WnQ@~{!r#O_13{b~=b1=taW-QB?)iMM!N-2ew_CC&T(M-eVF-&?HGmu z$1$K>yTR+lErr$hzdv@6AP7J_i)b%@P=x7W!F(GqjD^J+6YSTg3RFO71x0$V+Z@M< z1M8&VY7~iO6pkUeOzEox@2FR8%kryBmf!E=gou!M`*IFHJJ_ZzfB^%JsVT78=AWYW zxR$tkMY7lr3)dFdzKBQXGdo!f=<%iOi4wVK|P*UDSF^rtkr=WDZva#O8SWYD2-sjq$t# zIB)~cEpQm(1Wy<)IOA8@<5?cD41;-7G|5vqjuRMQ$O$~7WJPp0&1D&zuqzc% zEZ?Ck#hvNVFAsKp^=?gC{Hcx|`%-)D(RafCRd~Dw54FfDBBZ*W5Rt^u=%YJ^uQ&YV zJ5CnW8-vfBZn4}@qEPIc1Lx*yKORsWo@u#$^4<^nhQMbEc1bFN1O?mTjxMfgMft7A z)sM9*vd8OCKz(26gJ0L{L*0Ko3cb`Fk&{fPln*ux_5Jqc?G4mNZK}41&3C$D(eqG9 zUT9)Z*o%ucNJ&kB@xM(lF*yfboP(oij+maR`_^G$aj`ff7vtC9xXaBhxqp5zZ!N7s zsA%=8dwO3OK#OQFKQV)ou?vMLAc$FzON0(!O|n^Y(^Zl5`Z!6dCppGC<93=xgiCW6 zCK3)o2-(vjPl)hM7;Unqhu!Gx9cHBZH&hoctMh*GE~)=|i(#iO+qSoEmnEsV`bdA< z(D{68d3Z?sKtuGpeIjK}5K$$hf z+|B1ud|wxmqVqgA`2uI3d-JuxduLOQh>ageiz|#q<6~2oLCUi1v%ivnO-&t@6D31w P9$30AE4evo_p$!~dBqd@ literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/deployedServices16.ico b/client/administration/UdsAdmin/Resources/deployedServices16.ico new file mode 100644 index 0000000000000000000000000000000000000000..0979e3d9eabb3af0d7487aee51b1a3805a93c411 GIT binary patch literal 1406 zcmeH{+fNc;7{(t<%Z_3RVW1+Inx~NRl;%0eP$(f!K)$Qi+R6*N&^24F+OmVIv|QrG zaIIP`(~IWj1!f5d8VG25q0rypWveawa6`NCwkzLn&-1>&XZvp6Z~K5Sb~qd`kHnP- zfCnIziA4}G1@n!nMHG#eL=LW}a?zF+gZ4}=Ze;P$o+Utsl!u$yT*zcHbjXCbl@o`% zd3@Z?jl;cs0qz&_@t|0Uhb6I00z4`cp{qOronG8s=5V#wukJW-^hw^{_H zQVEkL9%{83&uXPG*GurcE(fo5V)WPL;f+2K{S5_pp)bPw3rYBJQG&rs61;3Kg|SJC zPc12UbyDiV5rT2FP%zwdyC;SR-vb-2Yr2gFdB_8nM^R7%@`OM zz{lYsjEsz6baWIJiv?q2W3Y^W#`wfnSglsrY&J~TZLl)g?RMBEnH-aFI2^D$9dJ6G zn3|fx^z<}bE*HMJU2wVGa5K%!%)sOEz~lA8>+`|KG&?(sxw$#a&(C9FVF8Pai&$D( zg5U2)AQ0H-SvK>{4g8l4P_TATEX9*>b!L`ZkXLv-zc4q%q^_^1sIRRIadnMatF2Xq zkVva*Xw+$HLP1H>dA-4)2_ca}-`v=EdW{Q;>&{h{o>*fx6s^M1aAN7EG;2-d$@zEmzW8zIu8lzl9L*hka zf(aKB4;sV4gYpuhyfns&N(ePXtUTHV+hTXu?za1IhZsVd2$P+i`M#O?&F}Y7r!Vw* z`S8A;#OEJP`Jby#DOYXZ+J`F;?|R%Sa@?&?@8=mMSGc|Y{r*fW_7ED6c{@`%>HOmC zLej&r*GSpPKad+6<64OYb3I#FO$;A= zBnBAZ@}+hqvsOcri>oJf-3EGD10V#-ntt>^b0{@8zbsH9fTpMbg!Sg~zxV&z5M#yJ zB>8pcf~Xby)XxElb{6f6MTzBTyoyjiR6r>oAmHHd@O z>jUaWCxjpv)^=q;B@&5%r4==!e%y)-@NJ-2EC#R#iT)4392y!LOsCV|d_EuQM*!3g i8mj=5$oK8TXnz4IU`^szIRFL#0000HC>B_w9Wz^6=01=fA(p_w@X)GyL=abN9pl@8|Q((fR*x zf6eDU)6@4C#l&=c|NH+xPft$|&woa+JDMsV{@?$f=ggirKKJA5{vFmnd|XO$h5ES< zZ=cJz{r~^puwb*HagJ44^TV^p^G{doys`h^@Bi~VCr$D z{!~Bu79Cp_wrqYISJUtC7Fj4l!jGBZQ}-ixFQ*7rkc6kJpUXO@geCwSUk}p& literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/empty16.png b/client/administration/UdsAdmin/Resources/empty16.png new file mode 100644 index 0000000000000000000000000000000000000000..679b980fa700b046e4ab699ac6c645c6b83ca809 GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg)9w|OOkx;8QZ-GLxnIRD+&iT2y zsd*(pE(3#eQEFmIYKlU6W=V#EyQgnJie4%^P+Z8<#WBR*55F9wrV31{MJZ T##Z}PMIddSu6{1-oD!M<+k-7p literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/find16.png b/client/administration/UdsAdmin/Resources/find16.png new file mode 100644 index 0000000000000000000000000000000000000000..88ae58a943d4cab1b4a0c64bab75832a7b0d054c GIT binary patch literal 1226 zcmYk4eN0t#7{{OUJ1_U#!@0=?f=ht+8ZIO&dFe$WB#`%t%-Ae6M6*`pB*Cr1N^7ok z1Gl!e6m4sB)0zT9-O5=&B9OoaItdVvmkV6D7w(1oe(rhsb*_c++kU_2kKglr_B_w` zdtzI0zK(m00{~rNLC$tHh6XRosISq5_W)4e73O4>e0p~8{$DQbn~sc-urRpko9n*H zgo7@paHV?rcv4D=Ec!&t702$(rbAV}(kQE~tIdxymQ5oW!L0*oOGS}#B z$+JKA&kJ;z!E}LA{JbcNv?v6yS}D;QE)Z}n?0h9HapJY@Z&tV@5s?TfYAZk!#3~ep zSX?r?;Es-tPyXI*or($s1eu~WYNi5;tWe;0a}*bPzO{RxB6i){te7}sYakFHRff!J zH22ZB0+|LH{4H=wh4X>Olm{O2`vv6HfXRF6t(2EoN>N?^%4XDs11p$QBBR)Zhvy{sbd!# z8geL_6&^LjA;Dv(T}}@yExBbxvwpk9oSU5*W4Lvr@3*dwZj$GD+3WR!vi!hM{|!i4 zr|o3wvoC&uaVH)g8u<|O`W}wsY#Ppm31TkXYcoIb^2X3eL#XEkgV9ihy2+EW>N0`` zQ9;V8uF&Y%i2T9(`v^`O@@3kSrWzw*k>r`a4PYJI$hpSINZM0BJTjdzF>XJ!B_qA= z>E}~-&Cbt@3l0bEa5`6^D7)PO9qsLS*Uq<)f|$-VJm2ZISzvbF26OWZAj=9&PMN9X z*s zfztKYU8LS?8EGkBZOzYXbS^FD-RbMQ9{k691;LhJZp9cI)2eDc-o*(;`?C97y84x8x zmLyD)NL5)BDcVg7f)e8KKzLh6?`ctz<0Hc!dFKI&gpR|VnP EA9^|{zyJUM literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/groups16.ico b/client/administration/UdsAdmin/Resources/groups16.ico new file mode 100644 index 0000000000000000000000000000000000000000..d8afba2d3c697e7173bdecd76533b8928a3d48db GIT binary patch literal 1150 zcmaKsdrVYU5XKJ=mHI-{it8V1fY?d}AJ~Z6*tpmRw1|&bF{Q15O&e$vMgPD`%0pum zwNxHL8^i)>#h_7x3T(q}C6-#n4R%ooj|N;N!4QE0YV_{i`|H_Vr8aJRZ@zPKznO35 z&dkXX!b`r>rU|(Qiez6Q0)!AD5|WaPo0pr%2N0g1Y5(vVnnxd}?%_kbFq``@8vC(W zhUxEDES};yxqFZMFyFsQ)t}YWb=5QSWJDw+TqY!>mYA3u?AXysZf-Ay$9gfF22kvp zP#lUlZM#HD%YHt#e$9$2AE7@T!IYzV3`J3F-D)B*u$s8In`C74P*QTAJ-gdkz4i~8 z(p`i8?At{Bx{c)a9M&|YFt<9A2?qmMl^;z_&0P``n+Om8MaF6&HMO0M@ttg5Z^Tma zEjKE@VR@;6#dVvAx}3oL+BLlLot~(3TR7Q#3TL0Hs_yXVHY;KCe`d*|YL4zK zuELsWz?%9NpQO(rxG0R*js(+q@0zPNhr=OMRdc~+8zpq!1wzA2_)RUP^xXwqi3^}% zosRF<&cG{qG7}5+_#f8O{h$Zcp$aGLc86=W#l_9&bmdI9ev-1O-*HAgqGi;!%FszL&Gh#_`)1u{2(* z=gW>9W`8w@$$J7wugGxe6}uwqcjv!-yOZ?v-(?Rfsr;#t^C|J1S?Why+ERMku9H`H z0)6Hie0F{(x7uz!tN-8Ua$sQe^Nx-lMu&zt{^qL`1o=`ItE2y7fy=+OwUxo)!T03H zOXt}+i|71sPZ#Bjr&9R(B#MH4xSpAW9M+iqMc?Nd=kA=PrpmrPeJYv{C1Xwqr#Q%) z?)nQZy{ahUzqDg!A*hw*S=%anL63h-^2UmqfOg Y5Dh*rPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0?J85K~y+Tg_BE2 zR8bhmfA`+GHkmr(I69N5lcS+M?Hv_Cy8>I(rZz!~HmxE83u1X(AFs-kHMBV$SK-s@)IZJ(?zcx6?qTHqeFj@wF=f>{W1Jd&3R_VrNlalFL&T$>aWV>b zI$CLa-|FUAj*U8x69|RKq*6?O`^Jrv4YYUtz+YCxhyEGPUwgs1#ui3mFVoiA|dAT`^dV+3KMZTB8i7>f7L1?p%KfH_sTMuB%>aIYxJBp@} ziN{fMJXro>e(pPoZ_iHF)%vO3SV%?nPQrns_|!1Eq7zGvp{eqZ$mJ3Yx3w|a+=4Di z(upJ`Wy`5nr*ZQ0SmN~)UKyvnv=Aei#LSuHQuk?Yci(aewBy^iH^v@5WV*AHP~ApC zt80k-=w`)m2Z8llh!!U4^_-z7GDuA%%CZ8Tu8~iqG6wdXhF0(KG)}&LO?blwd{tHC zS67p=J$Pfo9K6!NuDVjL4_%*2y49WDYw3dVLI4EW!Atu_}(M*#i!5~X3D$t9I7ahIt zsZj=v0pf`Xazz0<%6FsubDaN}VS$_zxfY9GwBAZvWi9l-00000NkvXXu0mjfS$u=W literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/networks16.png b/client/administration/UdsAdmin/Resources/networks16.png new file mode 100644 index 0000000000000000000000000000000000000000..e74a67f444fafe56efc2f6a51ca18bd304ede532 GIT binary patch literal 2531 zcmV<92^{u`P)4}ZAe8(6d{q6Br0hWr4end_ESIfQ!7PLk`L)d zT1BcVMI}|0iqa6=xFL`Q(S%f@Kn;f225h{H$IFaozjtQt{x0X7ei(ubV;9s%dZbt9 zmCpZnp7WgZJV!X^_#oyS$d7+l1G}WwxA#PPuczcT2QD*quGKcDtVw@~bJN*#Uo--c zQoQfH;)4K)kN#T$Pahf|>iOe6+Y0+{8p(^%z8pDU0?>*r3)MCk7n=6WY~#(j<@UFo z%O1_1{k@jb|2M?@0^}b!a;Q=Y{%Ze@;>f|B6?Ts0DdjaifkrEd6Od(=b|<6OO1Zq) z;+3;iUVf`~d40Y4W%G-N9tXHlFyxJd|K`XAE$)5%pDVrLcRq4paOAgta~t;_7@&~z zh~5?#a&uy>BvGiT6nrK}d%5SpAotxlykm5v_+9n!!{7SmQ=8(cfEP~J7%YYeDOhc!)SB6*H!{{+mPR|J z(={Z$qC& zqJ&_rnKCt+=1Oz*F3N+aP!BEb-#0OYPRdObJY(;p~h1ZsU?YVG-At2 zGi7xnK`Y7b?LG8{n$@+0W}LCQ5p(i#gK8tj7>F$_HZu0^D6-Kq96P(l+4&~xt&AWF zICi@F;s2RwO>Bw4KRj1E&}ellp(iOs9z&%7r4{v-WzSHK+a?NJTIq6prcNhYXJ)z0 zSbvUk-eYF5gUK8%V_9vc{Kiy?T07;$rTXSYWdxp$*xi5k%{>?21u$Pr z4_afE`3ZW*Dk~3F3oSN$}RP?!JB+oA|)oFJf zAKul6FC9l`8{|Swy=l4UKtFTK4UWInK}y4!tMT?N5%9Dal~N#`#8;C2+wd(kzyf4 z`;x%(aYE2Fj$Gsc5c!JOSb{*YZ#++_N1>M_1H};QAWjXfIHQ#qv?mZ<5D3OcdMV_* zc1*3wrB7#b`lLa1|F0Q^wBK0MSr-Achz0-VKU7UwKVY~Bq@2?mM* zeZ_z%^!Ui`G6u;{POkIFFIHG>WprYT?7Ou$e(37%b=5G1`0Xj*0N}7J#qc zm#a>VmwsBR6hLhjZ(}VGT>XiuSp0Ozoo0c)|=5ei9~ z7~ZNTtaUSb^B#Vr2t3Vd(-3JvyJP5f65_-V1&ZA}irhZcN4{9H^~^tft>(J`{_LaC z%eRadjtq_TA?0QXHrA3E$67PSW`=q@;fYt)m|cwVoFvU0ao1uqi^MTG5HMc$=+ArX z8p*MJFr-xQA1mFAXPX;G$0I*rC4}at9U!I&8S|1oGqdbL{0s##5`bY_H%+}dx zCe%9#jc&?DBW0tRkfw&cqd7jdugGV9ZGf@<97`K9oy5{VSf*GhuRit{fBqHTW7hy| z7WbJ;`r?aEJ^Q!2Cx#drDp4u~43t9JiDkYPBZS5|h>fG0SbPPQyk^g6L^-bzPSA=C z)n>wAIbzq&A}mc;CByw=AmY* zt(I0ckU}7oLP$kE&yF7OKk#HRglap(Nku;L5CW1ULzs*taR{mC#Kykx z!ke!@^}U}x)2yz|6M7vS%jocRmR_25Yd^Esk3jRRp}cn$SOk0-hN|0+mw)vA=l)~* z%*@1XckbCgHa<9Bsf4|SNXf_-I6yc-Y(&=X#ElCV<}bbS(&>|@-k83SBuR%bNN{!o z#3EU;ey#Wx0M|L?#N?MYK%56sgxKx{+C)*1)z_NmPMMCv zS{n6Eytr7~Sh!lPEzVc#8|#fulndK&H*F+IR>g@cNO1*|HQ##*wsghr!o!)E{L(Ve z7ElvXj7cT?J>M&ZkzdeCHJo$H#yTaXl-4?zWyV-zGp$t;Mt;}#bzMohCIqX%Jg|&) zZ8!6G_I&}~hA_Z}-u0EXKr9I%dWE1YgeXcW3QEe*_p~2`o|dU}TB}TJ8A~ZTLWl-} zbp-1m>O#=5(+_9Y)43tQuPi(JmnpEm`_qqX01HCkA@Bju;k0!q=WuLhbq1twdjgo| tiLbry%l8Alj^AYt5Z8WQGw}bg{{_YmjtWofDm?%I002ovPDHLkV1i}n@(KU| literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/new16.png b/client/administration/UdsAdmin/Resources/new16.png new file mode 100644 index 0000000000000000000000000000000000000000..e04cfc1b355c4b7cdd8ad0a364584fbc78b93454 GIT binary patch literal 1141 zcmYk53rtg27{^b0Z%Yd;y)Ca%U=*a&j`wRLh@oXAP!$3)9OA|%;9vs7C^ir}hJ^9b zMVRF=H^^Wcim=LK%RoRVY8@aoWK$Z~p~4VQ2yf`?-n&*{F(>DIIm!9{-}!#|f8{aJ z5p;95IRJnziWD9(M#Qu!WaH_)hMxcc4iO0j@8^CG%LSHvAA~<1THo3643W)(LVJvMfH}R z!?%YtX4_?VQ=?9!_;iStMDljc97?F>UyyU^go}f-ea=~as_w@x7rSJ6sjW@FarVbp z7QE^f2*52Ih6%)cJ<#mY5jF`pFfD*$1kA<^Pz?|u&`5v`87O*07pT8t@I^N<7{zcs zhH31zM8*npW(Qm^-3X2^w>w_3kJoadabW;ByX>R5+TMBQ^;6CBuTLWIJ?*ssc2zF* z?TS+%sqJjv8%G-?6ML4>?&w*DsTH>eJ@i2z8pLObbqk@#gR?6c?Wq;KGY%gz<|;d^ z?z`5G@s3UwJa*0~j^z&czL!US#Eo~+sTDgQ4zc)r8fmch@Wq709=s>0kaV)~lwoz; zQ{VOQXXW*P+`290_+5KhODVE9?fli+PqRk6%aaO8;%PQB;mg;EFco&?%s*GXAqu2N zF_esJa6oh;*e(YzL}@l~Eg<4V@)B8ZeAk5P40;^LiAHP~Othdwplnf;hn zhMJ*3ySxQRjugO%x3asB_8&AX8vsT^A?h+^!Nu&YtaarEFvr4JI-eA~QgYSy#7qv>e zu$FXb%%fLuVYx5MI2{oGoBf|77EbO1Q@xU#DUd+Tm7h18n!mo)tZGfJskz{8>rjP` z4(lXwUANoGoPQY96$KGi=OMeadGMN$#ZbF`)o>CjWN{1ZA=gJ_EH9p+Zl>R4u5l{^ZSXp zhsOmmg-DXb1VMrzB%%2E7oeuP4#ZRv!r=%YL_sVTgDitVAw5Nr9alT$SUi5so@ZOG zDla>d0onu6WylRTc_0mNmHbe%LYnW7YqbRQ3BcT4`P#XwN_7Whv@sX z9J)slJL{33Q*S6NT|xLWA(gb0RKBuQyrrytOJTtUy-pWnlr2^>VrUxf4UfRrm##yu zHD^5v-`_h3yonvV=jkS>va2h$-5Ebzzi?iqQaSkiVz4_l4VnzP2N+?r90-^-YRyzI z80xtBPjNN~WmazpuCN?zcG>%Q_VOw>x=w)cpv4?fSR&(1H4MMU}?QiUyC?bn9Ld%=2gmr_Jbe1?Z-r1!N zk9T34Vq!j(Nk)mL3pr$~26QJQ!AFq9`(vn``9?iwfTjhY%Vc?>~i*_#0rvX!OQ|X%xZREYR{n zGtdkTvVfrbVjnEJ=d%SNWs@X{lSGkIoUJvQviDSMEwAHqt?}=!bRJ125`Wk?=KqyO z(JrS8999QJl>?5=j)P(!g5xcINfQ0CBtc4f|Dk}N3ZO literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/publications16.png b/client/administration/UdsAdmin/Resources/publications16.png new file mode 100644 index 0000000000000000000000000000000000000000..c2a06dcd949a49ba03142a4a56c914527a140aa5 GIT binary patch literal 1213 zcmYk+YfM{J902hDef32PT?>7|cnlawS#V%P9fpB09>Q3JMU9&iqMLr1#;J2=i4mvC zmifV$jF1>ZoQci_m1Tl4uz@RE*6|w47<7dSQZ@=Lh1=fV-rjqT7ZXEIa$Y(A{LaaF zojtI>MyIi906o>DXA(t5}ZUvyv)KyiqeEeT-a2)$ z;73>gsrNhPg-eGczh-zo3_lt_qCMX*GJ>%r-5DGllnKBXD&QI>UGnnA`H$xd3owkxtYnmUsCwzZ&ocz)?IpQWsibP1-42z?P>X%Qv zrpavV;5U%=;lR1pS-10Ny}f3CTKbM$ogs_oxah);RE^<=@w^Gy1FNk2Fo*8n{Ks zTs$SWs}Fs2G)k6sGTf^0aMgdQsI2svgZp;pTJ#J=qa2L8{Ls~XvscZ}T-;mV(vImQ zeb3pJl10z`#Vv|V&&jhUKhgNPK^a?R%%&m3uCl`od$Mya3A{AFqwU4Xo|uDA_5$u{UH%j zjcN#T5{!CSnDYrtSHB1SHvFh@abbFdLL?Zh#SL+k)@aEm3BeoSF_Udggm+%dLMeI` zc!M!G*E0_Rnc8kjfR&I)&@@4+wcAXrL}MWo5E+%FV2h%aYK2HD7?h|XIgAPD8(o1Y z(6B904VxF`l`@D>NP(v#VW7D6#lPdIDM!y`b54#Sm+#^&R>`72DQLpEx zx;6j3>syq3CKT!H{b5KD;oaKf7rxf%4XGZtYv87Wcu@RQzCz$7cI@`dlEdM;W>hA< zt>RfA2{9z$h*D-Ym-7D`l$$_O6pbVaWfhfISGKmFG}>}3Sm2x}?7i~XGc)R47Ws)A zzx$}r@(U;?SVQc>gc#$z7{>ztQEg|AweCE66JvyqwY42R&=)Ljne6)q@k`6$?8|xm g!xy~r|FbzM1)uc{cYWHL%WwAUs`pp@Qu#*vzeRB-Hvj+t literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/serviceProviders16.png b/client/administration/UdsAdmin/Resources/serviceProviders16.png new file mode 100644 index 0000000000000000000000000000000000000000..76f08a8515bf7d088c80dfd46031f1e36aa34ee2 GIT binary patch literal 986 zcmV<0110>4P)*LJ)H8|L+rv3DoK;(CN%qmo9ZSa-0$< zDi2gD7SEPIQIQUOz6ZV=H%@K^$b9&XfTlIeSFQ5g6vYgw(G)w(W<7A6DhF076(E<( zz+%xuCL@QYrX!C=MsC+3Y+Ck%z;5@nsZ=GE27^x6vEwh;uweyQtwq2vN(5yw(?F60 zwkQ2eSui-*`)qnTH6r^}`tto|vq{*ycO&YM58d5i@Os~14uHYHLvgVdWHJ&o8U|dh zH8e#TYyAGl9XW!sGWX^3@;^2;G^~@cECUZ82C>)Yvw=YOTd()(;bd}-s;Q~9aU6~9 zP$1>`c~ybe`z%3KR&Hyt+1Bqf8r4uzqJ{SML3r`v)vV9gdM=fEcMJIgqL_5Z<#OQp z?^=wFO=2}!sIG1}OxbL%eL9^AN=pTZ$J5Z?KLnkfEf+;G`i}(GXiQFzXLo&Jp%TF) zM5A#C21k)34wIAdG?_?@bka1P6DpAqkz^7?@x33xeLP>X`q;6S`*n4HmhyZa%*|!s z_3KgS>kEV5-<#^`xptC#`tiQ(aYPp49&_b7|SUe5CjT0@~ga@f3iEoCyDZ+5xX>GXO9uq*)z1&jSl zcFyBH0z#pCt?_vHHdZ+%+qv_=8N0p0BnYaUKpe-A4o5kw)#l+85->G2i?*^@eik}9 zLXgc;FflQuiA4OZNbp(K)6;f`Wd*0z>L`-{g1})VB!Wp89{z;AhS8NM#9}k>@nZ_# z!!R%qNKZ`kOCrY?Mw!&DT)FF(%~n~xXV1oLRh5+*7>Ghw*IRh`GK@rT(i}&9h{a-L zIz3H9BKOgy@w*6&{M-fHGK)e{xW#BRC=MMu->g>W<1hs!wZDc!|D75i54Yj9xrO}^ zF{EWmE?&kw3`Vba9IUUukgTme9%C4x3D1^E(=L5q2g>o!RXzU9YTcHi$~9*k$L zh39!K02fe{hyRx)42k9qM!uuydqTt7U;rD}HsZ?yc-M$B)6o68Buop$} zeUA|Acc0JkOaIPQ>QnAaJCByAT4kS`9%VQVRGS3zGsInMeVwHQ4<07CPR> z+4(v?Y<$LzYfES>T*Unc4-tkT&uXo~rlo-IvFqzs&vAEo5lfd_*cxtQ>-!E?u3y5P zD`)WXMTbo&>7gTVtnqk^=CNI@eO$+npCjz00j)0rh$vagsr611S{|6?c;pbPgdD&rtCW zrV{|1;P;;xjrs{_W00$yqDp8bps7ox%O%e4fD`zHC`yo~DJ%&XlM`7I*?|%XUB*AW zgZ(`WX?X#1SeQaYyCWEkb~8x-H2VEMs@19iAed+^3$4(iY!pS7+^v+-Dy6J*p|g^X zvAtf8yAYY}JnO7EbK;f)w=*GZ6vsyjxaxMh{BL#j=>~v`<4p;;VKGfC-5mylfqeYr r(YpyUWA-|GjH9o$CVZuNiHZ6PI{ai^lyLx000000NkvXXu0mjfCleZK literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/stats16.png b/client/administration/UdsAdmin/Resources/stats16.png new file mode 100644 index 0000000000000000000000000000000000000000..ec6c4374757877f03d08a70564668a3f7265fb2b GIT binary patch literal 722 zcmV;@0xkWCP)o!` z1TmQ4!M_3yL=XM}8ohWSp1c?}h=~Ey5+sFKq74a#v;-m4gtp4E-EO<%^0A~?Kc3{5 znaSjRW-^mk5<(zJk_%|y0Ez!20L`u~yEq`6&1Tb<5XiE;A6t4XfHUszdm0&bRx@_)g6L9H$IPx`}d-Rf?jYW_UL$A)hFD3u*uAl zL4BPc)g2;_LZp-={_rLK(*e2%2l0l(NV1H>(d={DWOAtyogjI$iGt~+tJOtjdXn_>X zJ}u62t|`mH+zF5s+!V-s{lK%ZACb#(%a-fGAf1Imtw1jt2sJ{^1bpW?bv8p?#~hnG z1|840P)w6qyVyvl39PTz3W6tpBDqwGz7kU86+ccaKzW|G_KlW@C9W^ZL}P12g&^A3 z2Oy3Ky1T**^~8Cx_70CUN2tC?eZ`CQ-GhO_>*gHV6)!i3#|gBw;2>9jciNrz**Z@EX0nnDu=S8tt6s1y0n5HSp<+7+$ zD#EfXq3gOxrBXuEG%+u^{GcEB zV2lZBlmz1k3>XuQi4kJbL_mYQR-mMS)Pg*0(NemlyYnhLkDd2*s}c6Yy*KCPo?rgw z+;h%HPi-$%2d3Qfp1&$MH_gCKo-7bT) zAOPPEYOhE`L;{;juqcjVHj3DVx-yw-$jH|N;)@U<>{tEQZACra+}OHr)`9$sqHo>2 zv3JHXCrnWowC>uxp{YA5EEXbKI^&o&!y|V?=v(bEt?p)#4-oLGQ4ALgEd4D?a5d6B zR22Br6~#&eFIoy|`m7*jzp2>NY8sG>8A%wz3?BzB+QBB}GNL>ZN6}*##thFfCs_{F zqf%04NDSjbvLU@)pO}0>Nb#6Q_R*>rZjF=a2bcuUc>451aj17EI0jTKYja~Jd4T}H zLxik4RTQEL1Q@aa9H5ip;V|$&gmZfO{KXr(u_JG(7N-m>jw0HfY-g;iKZycAO=GUe zaXd=$sd`nBUws-;@q;8HBqi`#XjC${<&HJHXMyU30g!Pm&Dzs|G%tH6hgC%{-EkE~ zd(Bo$qoJIuPrIRNZdt1)XRx^h&Xt8s;tCh>#Zd=)srI;r@s*QzAlFdkfXa)*-Y!kmK8w;28 zbg0hRL%9@~Z0ek&Hi*)HDEd^N_+F1dpS05-^fQFC_A*;P-n)F)ub(^{%uKr z9|zw$f#yHF=hNZ0cda?UXUE2!k0`GmI{4!Dv${*&uL72dRtS{Stm)1&ws0UcaR+vk zUJ_JdC5RZ9p-l=zWff)fO=IK5VysN{P!d|8mhbEfeYZX`qdYaoRBSIuaz)p5%UpP2 zxJlwyFwNZzv^^D?=S38bcO+Qir}YzGT0~#!+RV1Xv7>7tgkRKH|Cf0f!>{FR_H!E(~0xB^xT%pOv-^k{?bq5ihGBK-#Mq6*Ou2Yc$_}j?AGZFc5#(h#v*_y3<#Ytl8#Gtyg|T(hvF+Zh zESKw0jrrCttz^&t`S1Y$=at`wGTCfil-r*h|7&cjVyHzSCAK9nid@fYgA_0vOTX5U zZ|{%7@L)}keIEsVv)Xs*+^H^gYGR~@f!*^LKOY6vY0TnFa$~d6HE)3|q>>S4+20t9 zhqZEXgiEFA#+H^K48!<7imvFDZPzbf1hq+0y9Iz{*g;(@D~7HCglL{m;xU02_*%bT sVOh4BVzBF&Gk=JZ)TEV4vo3@nknfzvb6?)vG;6PaVpIPQkMDT@AB#dT<^TWy literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Resources/uparrow16.png b/client/administration/UdsAdmin/Resources/uparrow16.png new file mode 100644 index 0000000000000000000000000000000000000000..55737e4610372b68ad2445fe9636bceeb32e7115 GIT binary patch literal 531 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKV7%q&;uumfC+W}s|MtwPKyilu z|C1JZm@_jogV~F2fW&};n2bZ&2loH}KlA_J|NY^-U^Ry{f4;Y878X{19Ixhfto{cN zBScv1G|BRD^xxs3Z4lbKHv-9KoeXsMs_TQ=7 z-}&(RcKJUtXO@LYJu*)$I`Il(RMHQiJ7xa={XYNY|NsAarq8RH>J?q__hJA2|NrX$ z{Q6vf>dD^i>+Nck*#bZJb4K!m&2BRO_xJaCpw9ZX@cH$dMb}kU{5ibc9%#V-|Br## zJ`SjLf5ESJhx6^~Qd?L2JH#Q&u zIs_CaOH7O*ra5)C{^#N0`Tx&wgG0oP1NCfbYRl|jSIzKBw~mzP!ma97)^{W07KCoF~)euCe{s!e*nM9chCJ!Zf0>O86bc> z6a}(|pgjnP1du==6=Vdq-)Nk{Knuk|6BUYPLK~03=fV|9p;O2~moOaNG=(lY0t}&t z=A$o$hdwb6JrV&8rs2Y^q2bX+ z;i686Q(8Huk~r{?y+=>pqnBe)FT+@}1Xm0qoHHmek|M+9R1qf9#BisnaKjjlt40YX z(q$OVP+>AN2DdV4+{qT>agG4DbESA>lHx%@ET+w|n6rp6Zcf27s|+_SdQ96C@Ys@Z zzfg<$A~|MC6nJWn$1A%WbERs`lxwh99*4yWC7x93aJ?!AZ>tmVqDF@|4h`Np)c8=V z#zK7(7M)tWcIxq|QHL+B2F$jb@Y!X=bC((Ooi?m?r(?3C3Cq1%c-ZT}+Ub0(o-x5Y zV8R-~d$s^;Ll$_4tR$^iaTj69U5sUxVLO&b?D#fPg0C0K;2o>Pil+|WCu(r-(kK?j zkK_H+7(Tw7-{`Y$_M12GUpK(8lfzUp<&6B#o(UF~);2hu^$iuV=pa>efvdf_rM0E0 z%_?P2X!DL9X*_tS?!f-ylrFZYVhW4)TFiTPrAM+_<50(Fvhxl41Tm}ZP^tDZqae&D z>>3`ICEDs&hM`Tn|J5X2M)hHTu_KC~w$HE3wxAGYZi&C(1Z)YFCHpF&PmS6@BjV3 zFW;#?awt10a%&_2AS&lTW*&3$f-547*(FOz3jm&!lc{>y*Z@?Z3$bV#0ydikUd*ip zH>`I7pC8Hgc_|dVU0A^rKCTRyX3oE$ISwc?*6P9&ze4Y{29cmhEQo_h%W^zcK~Hg?`-Mn35%M4BRH4UsC#z-*$~%N zYKhx-)2JqB=3VfSzi(IUfLz}5c4Kw5CqW>P2~gQrMCox0HyG>iW0{qRONi=)A{Yp? zoAM9LKViKPaVuT_2sN0LsNE=>Fc^{**IQ|Qz&p!fEEfJB_p)UehD|f2H+OcND~q<8 z)$(Q>yJZz;?`S$fyu}eDgv%JFrmU$`=i8RoODE)n z&dBQ=-EKhn5b%3_Xk~hag@=WKfNvFq3nYN{3si_&R$U=4+ z=i>x>2JdqD_8-NEua(3{l|3vlzbb_xl~83tXJBY}T(a;tYX|e^*a22J@KZF+Vf$eO zPQh>v1vf+nAYXuJv5X6G{{+jsIM3<7^H)lcWrF!mT4u_0uR}jI_j{YTsem{+=#=ez zGA3z&=`u+=eUgigoymdKf#S+&>vzQJ>80VG^uqUAQWOfsn!;L}A%sY<0RTW@E11 z-XU3Xx}U|T5_WGCNu^;Mczia_!2+w^m6h44KWFdX9m1zaI-@r&-pf$Ng`9-UV4zo9 U+FP*qgZ$uK&hv*dThtoUKdw<8F8}}l literal 0 HcmV?d00001 diff --git a/client/administration/UdsAdmin/Strings.Designer.cs b/client/administration/UdsAdmin/Strings.Designer.cs new file mode 100644 index 000000000..66049aad1 --- /dev/null +++ b/client/administration/UdsAdmin/Strings.Designer.cs @@ -0,0 +1,1215 @@ +//------------------------------------------------------------------------------ +// +// Este código fue generado por una herramienta. +// Versión de runtime:4.0.30319.269 +// +// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si +// se vuelve a generar el código. +// +//------------------------------------------------------------------------------ + +namespace UdsAdmin { + using System; + + + /// + /// Clase de recurso con establecimiento inflexible de tipos, para buscar cadenas traducidas, etc. + /// + // StronglyTypedResourceBuilder generó automáticamente esta clase + // a través de una herramienta como ResGen o Visual Studio. + // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen + // con la opción /str o vuelva a generar su proyecto de VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UdsAdmin.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las + /// búsquedas de recursos mediante esta clase de recurso con establecimiento inflexible de tipos. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Busca una cadena traducida similar a Enabled. + /// + internal static string active { + get { + return ResourceManager.GetString("active", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Allowed Groups. + /// + internal static string allowedGroups { + get { + return ResourceManager.GetString("allowedGroups", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Groups allowed to access this service. + /// + internal static string allowedGroupsToolTip { + get { + return ResourceManager.GetString("allowedGroupsToolTip", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a The application will exit now. + /// + internal static string appWillTerminate { + get { + return ResourceManager.GetString("appWillTerminate", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Assigned Services. + /// + internal static string assignedServices { + get { + return ResourceManager.GetString("assignedServices", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Shows services of this kind currently assigned to users. + /// + internal static string assignedServicesToolTip { + get { + return ResourceManager.GetString("assignedServicesToolTip", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Assign Service to user. + /// + internal static string assignToUser { + get { + return ResourceManager.GetString("assignToUser", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Authenticators. + /// + internal static string authenticators { + get { + return ResourceManager.GetString("authenticators", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a The credentials provided are no longer valid. + /// + internal static string authFailed { + get { + return ResourceManager.GetString("authFailed", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Blocked. + /// + internal static string blocked { + get { + return ResourceManager.GetString("blocked", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Cache. + /// + internal static string cache { + get { + return ResourceManager.GetString("cache", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a The cache has been flushed. + /// + internal static string cacheFlushed { + get { + return ResourceManager.GetString("cacheFlushed", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Cache Level. + /// + internal static string cacheLevel { + get { + return ResourceManager.GetString("cacheLevel", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Services currently in cache. + /// + internal static string cacheServicesToolTip { + get { + return ResourceManager.GetString("cacheServicesToolTip", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Cancel. + /// + internal static string cancel { + get { + return ResourceManager.GetString("cancel", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Can't Connect to server. Please, check server and retry. + /// + internal static string cantConnect { + get { + return ResourceManager.GetString("cantConnect", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a This authenticator do not allows the creation of new groups at administration interface. + /// + internal static string cantCreateGroups { + get { + return ResourceManager.GetString("cantCreateGroups", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a This authenticator do not allows the creation of new users at administration interface. + /// + internal static string cantCreateUsers { + get { + return ResourceManager.GetString("cantCreateUsers", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a This authenticator do not allows the modification of users at administration interface. + /// + internal static string cantModifyUsers { + get { + return ResourceManager.GetString("cantModifyUsers", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Do you really want to change current interface language?. Changes will take effect on next restart. + /// + internal static string changeLanguage { + get { + return ResourceManager.GetString("changeLanguage", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Check authenticator. + /// + internal static string checkAuthenticator { + get { + return ResourceManager.GetString("checkAuthenticator", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Check OS Manager. + /// + internal static string checkOSManager { + get { + return ResourceManager.GetString("checkOSManager", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Check service provider. + /// + internal static string checkServiceProvider { + get { + return ResourceManager.GetString("checkServiceProvider", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Connectivity. + /// + internal static string connectivity { + get { + return ResourceManager.GetString("connectivity", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Delete. + /// + internal static string deleteItem { + get { + return ResourceManager.GetString("deleteItem", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Deletion failed. + /// + internal static string deletionFailed { + get { + return ResourceManager.GetString("deletionFailed", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Deployed Service. + /// + internal static string deployedService { + get { + return ResourceManager.GetString("deployedService", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Deployed Services. + /// + internal static string deployedServices { + get { + return ResourceManager.GetString("deployedServices", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Disable. + /// + internal static string disable { + get { + return ResourceManager.GetString("disable", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Downloaded {0} KB of {1} KB at {2:0.00} KB/s. + /// + internal static string downloadInfo { + get { + return ResourceManager.GetString("downloadInfo", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Do you want to download and install the new version?. + /// + internal static string downloadQuery { + get { + return ResourceManager.GetString("downloadQuery", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Duplicated item. + /// + internal static string duplicatedItem { + get { + return ResourceManager.GetString("duplicatedItem", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Enable. + /// + internal static string enable { + get { + return ResourceManager.GetString("enable", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Error. + /// + internal static string error { + get { + return ResourceManager.GetString("error", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Get Error Information. + /// + internal static string errorInfo { + get { + return ResourceManager.GetString("errorInfo", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Exit. + /// + internal static string exit { + get { + return ResourceManager.GetString("exit", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Field '{0}' is required. + /// + internal static string fieldRequired { + get { + return ResourceManager.GetString("fieldRequired", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a The item do not exists anymore. Please, refresh gui (F5). + /// + internal static string findFailed { + get { + return ResourceManager.GetString("findFailed", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Group. + /// + internal static string group { + get { + return ResourceManager.GetString("group", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to select a group. + /// + internal static string groupRequired { + get { + return ResourceManager.GetString("groupRequired", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Groups. + /// + internal static string groups { + get { + return ResourceManager.GetString("groups", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Id. + /// + internal static string id { + get { + return ResourceManager.GetString("id", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Import text file. + /// + internal static string import { + get { + return ResourceManager.GetString("import", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Text Files|*.txt|All files|*.*. + /// + internal static string importFilter { + get { + return ResourceManager.GetString("importFilter", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Disabled. + /// + internal static string inactive { + get { + return ResourceManager.GetString("inactive", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Invalid credentials. + /// + internal static string invalidCredentials { + get { + return ResourceManager.GetString("invalidCredentials", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Invalid Ip Address. + /// + internal static string invalidIpAddress { + get { + return ResourceManager.GetString("invalidIpAddress", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Language. + /// + internal static string language { + get { + return ResourceManager.GetString("language", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Manage groups for this authenticator. + /// + internal static string manageGroups { + get { + return ResourceManager.GetString("manageGroups", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Manage users for this authenticator. + /// + internal static string manageUsers { + get { + return ResourceManager.GetString("manageUsers", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Modification failed. + /// + internal static string modificationFailed { + get { + return ResourceManager.GetString("modificationFailed", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Modifying. + /// + internal static string modifying { + get { + return ResourceManager.GetString("modifying", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Modify. + /// + internal static string modifyItem { + get { + return ResourceManager.GetString("modifyItem", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a The field 'name' is required. + /// + internal static string nameRequired { + get { + return ResourceManager.GetString("nameRequired", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to set up at least an Authenticator before using this.. + /// + internal static string needsAuthenticators { + get { + return ResourceManager.GetString("needsAuthenticators", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to set up at least an OS Manager before using this.. + /// + internal static string needsOsManagers { + get { + return ResourceManager.GetString("needsOsManagers", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to set up at least a Service before using this.. + /// + internal static string needsServices { + get { + return ResourceManager.GetString("needsServices", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You should set up at least a transport before using this.. + /// + internal static string needsTransports { + get { + return ResourceManager.GetString("needsTransports", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Transport not active for selected networks. + /// + internal static string negativeNetCheck { + get { + return ResourceManager.GetString("negativeNetCheck", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Network End. + /// + internal static string netEnd { + get { + return ResourceManager.GetString("netEnd", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Network end must be greater than network start. + /// + internal static string netRangeError { + get { + return ResourceManager.GetString("netRangeError", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Network Start. + /// + internal static string netStart { + get { + return ResourceManager.GetString("netStart", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Neworks. + /// + internal static string networks { + get { + return ResourceManager.GetString("networks", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a New. + /// + internal static string newItem { + get { + return ResourceManager.GetString("newItem", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a New Service Provider. + /// + internal static string newServiceProvider { + get { + return ResourceManager.GetString("newServiceProvider", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a A new version of UDS Admin is required to communicate with server. + /// + internal static string newVersionRequired { + get { + return ResourceManager.GetString("newVersionRequired", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a No. + /// + internal static string no { + get { + return ResourceManager.GetString("no", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Occupied. + /// + internal static string occopied { + get { + return ResourceManager.GetString("occopied", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a OS Manager. + /// + internal static string osManager { + get { + return ResourceManager.GetString("osManager", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a OS Managers. + /// + internal static string osManagers { + get { + return ResourceManager.GetString("osManagers", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Owner. + /// + internal static string owner { + get { + return ResourceManager.GetString("owner", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Transport active for selected networks. + /// + internal static string positiveNetCheck { + get { + return ResourceManager.GetString("positiveNetCheck", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Publication Failed. + /// + internal static string publicationFailed { + get { + return ResourceManager.GetString("publicationFailed", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Publications. + /// + internal static string publications { + get { + return ResourceManager.GetString("publications", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Publications done from this deployed service. + /// + internal static string publicationsToolTip { + get { + return ResourceManager.GetString("publicationsToolTip", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Publish. + /// + internal static string publish { + get { + return ResourceManager.GetString("publish", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Generate a new publication for this service?. + /// + internal static string publishQuestion { + get { + return ResourceManager.GetString("publishQuestion", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Are you sure do you want to remove this item?. + /// + internal static string removeQuestion { + get { + return ResourceManager.GetString("removeQuestion", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Test unsuccessful. + /// + internal static string resultTestError { + get { + return ResourceManager.GetString("resultTestError", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Test successful. + /// + internal static string resultTestOk { + get { + return ResourceManager.GetString("resultTestOk", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Search Group. + /// + internal static string searchGroup { + get { + return ResourceManager.GetString("searchGroup", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Search User. + /// + internal static string searchUser { + get { + return ResourceManager.GetString("searchUser", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Select only one item to perform this operation.. + /// + internal static string selectOnlyOne { + get { + return ResourceManager.GetString("selectOnlyOne", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Service Providers. + /// + internal static string serviceProviders { + get { + return ResourceManager.GetString("serviceProviders", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to select a service. + /// + internal static string serviceRequired { + get { + return ResourceManager.GetString("serviceRequired", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Services. + /// + internal static string services { + get { + return ResourceManager.GetString("services", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to specify the authenticator to be used. + /// + internal static string specifyAuthenticator { + get { + return ResourceManager.GetString("specifyAuthenticator", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to specify the service to be used. + /// + internal static string specifyBaseService { + get { + return ResourceManager.GetString("specifyBaseService", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to specify the OS Manager to be used. + /// + internal static string specifyOsManager { + get { + return ResourceManager.GetString("specifyOsManager", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Active. + /// + internal static string stateActive { + get { + return ResourceManager.GetString("stateActive", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Blocked. + /// + internal static string stateBlocked { + get { + return ResourceManager.GetString("stateBlocked", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Canceled. + /// + internal static string stateCanceled { + get { + return ResourceManager.GetString("stateCanceled", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Canceling. + /// + internal static string stateCanceling { + get { + return ResourceManager.GetString("stateCanceling", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Error. + /// + internal static string stateError { + get { + return ResourceManager.GetString("stateError", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Inactive. + /// + internal static string stateInactive { + get { + return ResourceManager.GetString("stateInactive", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Launching publication. + /// + internal static string stateLaunching { + get { + return ResourceManager.GetString("stateLaunching", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Generating. + /// + internal static string statePreparing { + get { + return ResourceManager.GetString("statePreparing", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Waiting for removal. + /// + internal static string stateRemovable { + get { + return ResourceManager.GetString("stateRemovable", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Removed. + /// + internal static string stateRemoved { + get { + return ResourceManager.GetString("stateRemoved", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Removing. + /// + internal static string stateRemoving { + get { + return ResourceManager.GetString("stateRemoving", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Unknown. + /// + internal static string stateUnknown { + get { + return ResourceManager.GetString("stateUnknown", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Ready. + /// + internal static string stateUsable { + get { + return ResourceManager.GetString("stateUsable", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Assign New Group. + /// + internal static string titleAssignNewGroup { + get { + return ResourceManager.GetString("titleAssignNewGroup", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Assign Service. + /// + internal static string titleAssignService { + get { + return ResourceManager.GetString("titleAssignService", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Authenticator. + /// + internal static string titleAuthenticator { + get { + return ResourceManager.GetString("titleAuthenticator", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Configuration. + /// + internal static string titleConfiguration { + get { + return ResourceManager.GetString("titleConfiguration", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Deployed Service. + /// + internal static string titleDeployedService { + get { + return ResourceManager.GetString("titleDeployedService", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Downloader. + /// + internal static string titleDownloader { + get { + return ResourceManager.GetString("titleDownloader", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Group. + /// + internal static string titleGroup { + get { + return ResourceManager.GetString("titleGroup", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Login to UDS Administration. + /// + internal static string titleLogin { + get { + return ResourceManager.GetString("titleLogin", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a UDS Administration Client. + /// + internal static string titleMain { + get { + return ResourceManager.GetString("titleMain", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Network. + /// + internal static string titleNetwork { + get { + return ResourceManager.GetString("titleNetwork", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a OS Manager. + /// + internal static string titleOsManager { + get { + return ResourceManager.GetString("titleOsManager", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Service. + /// + internal static string titleService { + get { + return ResourceManager.GetString("titleService", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Service Provider. + /// + internal static string titleServiceProvider { + get { + return ResourceManager.GetString("titleServiceProvider", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Transport. + /// + internal static string titleTransport { + get { + return ResourceManager.GetString("titleTransport", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a User. + /// + internal static string titleUser { + get { + return ResourceManager.GetString("titleUser", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a User Preferences. + /// + internal static string titleUserPreferences { + get { + return ResourceManager.GetString("titleUserPreferences", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to select a transport. + /// + internal static string transportRequired { + get { + return ResourceManager.GetString("transportRequired", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Transports. + /// + internal static string transports { + get { + return ResourceManager.GetString("transports", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a User. + /// + internal static string user { + get { + return ResourceManager.GetString("user", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a User Preferences. + /// + internal static string userPreferences { + get { + return ResourceManager.GetString("userPreferences", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a You need to select an user. + /// + internal static string userRequired { + get { + return ResourceManager.GetString("userRequired", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Users. + /// + internal static string users { + get { + return ResourceManager.GetString("users", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a There are errors in the data provided. + /// + internal static string validationFailed { + get { + return ResourceManager.GetString("validationFailed", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a View. + /// + internal static string view { + get { + return ResourceManager.GetString("view", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Waiting OS To Get Ready. + /// + internal static string waitingOsReady { + get { + return ResourceManager.GetString("waitingOsReady", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Yes. + /// + internal static string yes { + get { + return ResourceManager.GetString("yes", resourceCulture); + } + } + } +} diff --git a/client/administration/UdsAdmin/Strings.de.Designer.cs b/client/administration/UdsAdmin/Strings.de.Designer.cs new file mode 100644 index 000000000..e69de29bb diff --git a/client/administration/UdsAdmin/Strings.de.resx b/client/administration/UdsAdmin/Strings.de.resx new file mode 100644 index 000000000..4a2630edf --- /dev/null +++ b/client/administration/UdsAdmin/Strings.de.resx @@ -0,0 +1,475 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Aktiviert + + + Gruppen erlaubt + + + Gruppen auf diesen Dienst zugreifen + + + Zugeordneten Services + + + Dienstleistungen dieser Art, die Benutzer derzeit zugewiesen zeigt + + + Blockiert + + + Cache + + + Dienstleistungen derzeit im cache + + + Kann keine Verbindung zum Server herstellen. Bitte überprüfen Sie Server und wiederholen + + + Wollen Sie wirklich aktuellen Sprache der Benutzeroberfläche ändern?. Änderungen werden beim nächsten Neustart wirksam + + + Kontrollkästchen Authentifikator + + + Überprüfen Sie OS-Manager + + + Prüfen Sie Service-provider + + + Löschen + + + Löschen ist fehlgeschlagen + + + Publikationen + + + Publikationen von diesem bereitgestellten Dienst getan + + + Deaktivieren + + + Doppelte Element + + + Aktivieren + + + Fehler + + + Gruppen + + + Behinderte + + + Sprache + + + Verwalten von Gruppen für diese Authentifikator + + + Verwalten von Benutzern für diese Authentifikator + + + Ändern + + + Ändern + + + Sie müssen mindestens ein Authentifikator einrichten, bevor Sie diese verwenden. + + + Sie müssen Sie mindestens eine OS-Manager festlegen, bevor mit diesem. + + + Sie müssen mindestens einen Dienst einrichten, bevor mit diesem. + + + Sie sollten mindestens einen Transport einrichten, bevor Sie mit diesem. + + + Neu + + + Neue Dienstleister + + + Eine neue Version der UDS-Admin wird zur Kommunikation mit Server benötigt + + + Dienstleistungen + + + Sie müssen den Authentifikator verwendet werden angeben + + + Sie müssen den Dienst zu verwendende angeben + + + Sie müssen den OS-Manager verwendet werden angeben + + + Benutzer + + + Ansicht + + + Cache-Stufe + + + ID + + + Eigentümer + + + Veröffentlichen + + + Veröffentlichung fehlgeschlagen + + + Generieren Sie eine neue Publikation für diesen Dienst? + + + Abbrechen + + + Wählen Sie nur ein Element, um diesen Vorgang auszuführen. + + + Bereitgestellte Dienst + + + Nr. + + + Besetzt + + + Sind Sie sicher wollen Sie dieses Element entfernen? + + + Ja + + + OS-Manager + + + Die Anwendung wird nun beendet. + + + Die bereitgestellten Anmeldeinformationen sind nicht mehr gültig + + + Das Element-Do ist nicht mehr vorhanden. Bitte aktualisieren Sie gui (F5) + + + Ungültiger Anmeldeinformationen + + + Fehler sind in den Angaben + + + Aktive + + + Blockiert + + + Abgebrochen + + + Abbrechen + + + Fehler + + + Inaktiv + + + Generieren + + + Warten auf Entfernung + + + Entfernt + + + Entfernen + + + Unbekannt + + + Bereit + + + Personen, die auf OS bereit + + + Benutzer Service zuweisen + + + Service-Provider + + + Feld '{0}' muss ausgefüllt werden + + + Das Feld 'Name' ist erforderlich + + + Sie müssen eine Gruppe auswählen + + + Sie müssen einen Dienst auswählen + + + Sie müssen einen Benutzer auswählen + + + Benutzereinstellungen + + + Transporte + + + Neworks + + + Ungültige IP-Adresse + + + Netzwerk-Ende + + + Netzwerk-Start + + + Netzwerk Ende muss größer als Netzwerk Start sein. + + + Verkehr nicht aktiv für ausgewählten Netzen + + + Transport für ausgewählten Netzwerken aktiv + + + Test nicht erfolgreich + + + Test erfolgreich + + + Authentifikatoren + + + Konnektivität + + + Bereitgestellten Dienste + + + OS-Managers + + + Informieren Sie sich Fehler + + + Möchten Sie die neue Version herunterladen und installieren? + + + Heruntergeladene {0} KB von {1} KB am {2:0.00} KB/s + + + Ausfahrt + + + Text Files|*.txt|Alle-Dateien| *. * + + + Textdatei importieren + + + Gruppe + + + Suche Gruppe + + + Benutzer suchen + + + Benutzer + + + Änderung ist fehlgeschlagen + + + Weisen Sie Service + + + Neue Gruppe zuweisen + + + Authentifikator + + + Bereitgestellte Dienst + + + Downloader + + + Gruppe + + + Anmeldung für UDS Administration + + + UDS-Verwaltungsclient + + + Netzwerk + + + OS-Manager + + + Service + + + Service-Provider + + + Verkehr + + + Benutzer + + + Benutzereinstellungen + + + Der Cache geleert worden ist + + + Start der Veröffentlichung + + + Sie müssen einen Transport wählen + + + Konfiguration + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/Strings.es.Designer.cs b/client/administration/UdsAdmin/Strings.es.Designer.cs new file mode 100644 index 000000000..e69de29bb diff --git a/client/administration/UdsAdmin/Strings.es.resx b/client/administration/UdsAdmin/Strings.es.resx new file mode 100644 index 000000000..9788dabaf --- /dev/null +++ b/client/administration/UdsAdmin/Strings.es.resx @@ -0,0 +1,495 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Activo + + + Grupos permitidos + + + Grupos con acceso a este servicio + + + Servicios Asignados + + + Muestra los Servicios de este tipo que estan asignados a usuarios en la actualidad + + + Bloqueado + + + Caché + + + Servicios actualmente en el caché + + + No se puede conectar al servidor. Por favor, compruebe el servidor y vuelva a intentarlo + + + ¿Realmente desea cambiar el idioma de la interfaz actual?. Los cambios surtirán efecto en el próximo reinicio + + + Verificar autenticador + + + Verificar OS Manager + + + Verificar proveedor de servicios + + + Borrar + + + Borrado fallido + + + Deshabilitar + + + Elemento duplicado + + + Habilitar + + + Error + + + Grupos + + + Desactivado + + + Idioma + + + Administrar grupos para este autenticador + + + Administrar los usuarios de este autenticador + + + Modificando + + + Modificar + + + Debe configurar al menos un autenticador antes de usar esto. + + + Debe instalar al menos un Manager OS antes de utilizar esta. + + + Debe configurar al menos un servicio antes de usar esto. + + + Debe configurar al menos un transporte antes de usar esto. + + + Nuevo + + + Nuevo proveedor de servicios + + + Se requiere una nueva versión de UDS Admin para comunicarse con el servidor + + + Publicaciones + + + Publicaciones desde este servicio desplegado + + + Servicios + + + Es necesario especificar el autenticador a utilizar + + + Es necesario especificar el servicio a utilizar + + + Es necesario especificar el OS Manager a utilizar + + + Usuarios + + + Ver + + + Nivel de caché + + + ID. + + + Propietario + + + Publicar + + + Error de publicación + + + ¿Generar una nueva publicación para este servicio? + + + Cancelar + + + Seleccione sólo un artículo para realizar esta operación. + + + Servicio desplegado + + + No + + + Ocupado + + + ¿Está seguro que desea eliminar este elemento? + + + + + + OS Manager + + + La aplicación se cerrará ahora + + + Las credenciales proporcionadas no son válidas + + + El elemento do no existe ya. Por favor, actualice el gui (F5) + + + Credenciales no válidas + + + Hay errores en los datos proporcionados + + + Activo + + + Bloqueado + + + Cancelado + + + Cancelando + + + Error + + + Inactivo + + + Generando + + + A la espera de la eliminación + + + Eliminado + + + Eliminando + + + Desconocido + + + Listo + + + Esperando al OS + + + Asignar el servicio al usuario + + + Proveedores de servicios + + + Se requiere el campo '{0}' + + + Se requiere el campo 'nombre' + + + Debe seleccionar un grupo + + + Debe seleccionar un servicio + + + Debe seleccionar un usuario + + + Preferencias de usuario + + + Transportes + + + Redes + + + Dirección Ip no válida + + + Final de red + + + Inicio de red + + + El final de red debe ser mayor que el inicio de la red + + + Transporte inactivo para redes seleccionadas + + + Transporte activo para redes seleccionadas + + + Prueba fallida + + + Prueba exitosa + + + Autenticadores + + + Conectividad + + + Servicios implementados + + + OS administradores + + + Obtener información de Error + + + ¿Desea descargar e instalar la nueva versión? + + + Descargado {0} KB de {1} KB en {2:0.00} KB/s + + + Salir + + + Texto Files|*.txt|Todos files| *. * + + + Importar archivo de texto + + + Grupo + + + Grupo de búsqueda + + + Buscar Usuario + + + Usuario + + + Error de modificación + + + Asignar servicio + + + Asignar nuevo grupo + + + Autenticador + + + Servicio desplegado + + + Downloader + + + Grupo + + + Inicio de sesión en el Administrador UDS + + + Cliente de administración de UDS + + + Red + + + OS Manager + + + Servicio + + + Proveedor de servicios + + + Transporte + + + Usuario + + + Preferencias de usuario + + + La caché ha sido vaciada + + + Lanzando publicación + + + Debe seleccionar un transporte + + + Configuración + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/Strings.fr.Designer.cs b/client/administration/UdsAdmin/Strings.fr.Designer.cs new file mode 100644 index 000000000..e69de29bb diff --git a/client/administration/UdsAdmin/Strings.fr.resx b/client/administration/UdsAdmin/Strings.fr.resx new file mode 100644 index 000000000..8dc1fc118 --- /dev/null +++ b/client/administration/UdsAdmin/Strings.fr.resx @@ -0,0 +1,475 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Activée + + + Accueilli des groupes + + + Groupes autorisés à accéder à ce service + + + Services assignées + + + Indique les services de ce type actuellement assigné aux utilisateurs + + + Bloqué + + + Cache + + + Services actuellement dans le cache + + + Impossible de se connecter au serveur. Vérifiez svp, serveur et réessayer + + + Vous voulez vraiment changer la langue d'interface cours?. Modifications prendront effet au prochain redémarrage + + + Vérifiez l'authentificateur + + + Vérifiez le gestionnaire de l'OS + + + Fournisseur de services de vérification + + + Supprimer + + + Suppression a échoué + + + Publications + + + Publications faites de ce service déployé + + + Désactiver + + + Élément dupliqué + + + Enable + + + Erreur + + + Groupes + + + Handicapés + + + Langue + + + Gérer des groupes pour cette authentificateur + + + Gestion des utilisateurs pour cette authentificateur + + + Modifiant + + + Modifier + + + Vous devez configurer au moins un authentificateur avant d'utiliser ce. + + + Vous devez définir le gestionnaire d'au moins un OS avant d'utiliser ce. + + + Vous devez configurer au moins un Service avant d'utiliser ce. + + + Vous devez définir jusqu'à au moins un transport avant d'utiliser ce. + + + Nouveau + + + Nouveau fournisseur de services + + + Une nouvelle version de l'UDS Admin est obligée de communiquer avec le serveur + + + Services + + + Vous devez spécifier l'authentificateur à utiliser + + + Vous devez spécifier le service à utiliser + + + Vous devez spécifier le OS Manager à utiliser + + + Utilisateurs + + + Avis + + + Niveau de cache + + + ID + + + Propriétaire + + + Publier + + + Publication a échoué + + + Générer une nouvelle publication pour ce service ? + + + Annuler + + + Sélectionner un seul élément pour effectuer cette opération. + + + Service déployée + + + Aucun + + + Occupée + + + Vous êtes certain que vous voulez supprimer cet élément ? + + + Oui + + + Gestionnaire de l'OS + + + L'application sera sortie maintenant + + + Les informations d'identification fournies ne sont plus valides + + + L'élément do il existe non plus. Veuillez actualiser gui (F5) + + + Informations d'identification non valides + + + Il y a des erreurs dans les données fournies + + + Active + + + Bloqué + + + Annulée + + + Annulation + + + Erreur + + + Inactif + + + Générant + + + Attente d'enlèvement + + + Supprimé + + + Suppression + + + Inconnu + + + Prêt + + + OS pour se préparer en attente + + + Assigner un Service à l'utilisateur + + + Fournisseurs de services + + + Champ « {0} » est requis + + + Le champ « nom » est requis + + + Vous devez sélectionner un groupe + + + Vous devez sélectionner un service + + + Vous devez sélectionner un utilisateur + + + Préférences de l'utilisateur + + + Transports + + + Réseaux + + + Adresse Ip non valide + + + Fin de réseau + + + Début du réseau + + + Fin du réseau doit être supérieure à démarrer réseau + + + Transport non active pour les réseaux sélectionnés + + + Transport actif pour les réseaux sélectionnés + + + Essai infructueux + + + Test réussie + + + Authentificateurs + + + Connectivité + + + Services de déploiements + + + Gestionnaires des OS + + + Obtenir des informations sur l'erreur + + + Vous voulez télécharger et installer la nouvelle version ? + + + Téléchargé {0} Ko {1} ko à {2:0.00} KB/s. + + + Sortie + + + Texte Files|*.txt|Tous les fichiers| *. * + + + Importer un fichier texte + + + Groupe + + + Groupe de recherche + + + Utilisateur de recherche + + + Utilisateur + + + Modification a échoué + + + Affecter le Service + + + Assigner le nouveau groupe + + + Authentificateur + + + Service de déploiement + + + Téléchargeur + + + Groupe + + + Connexion à l'Administration de l'UDS + + + UDS Administration Client + + + Réseau + + + Gestionnaire de l'OS + + + Service + + + Fournisseur de services + + + Transport + + + Utilisateur + + + Préférences de l'utilisateur + + + Le cache a été vidé. + + + Lancement de la publication + + + Vous devez sélectionner un transport + + + Configuration + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/Strings.resx b/client/administration/UdsAdmin/Strings.resx new file mode 100644 index 000000000..49364f565 --- /dev/null +++ b/client/administration/UdsAdmin/Strings.resx @@ -0,0 +1,504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Enabled + + + Allowed Groups + + + Groups allowed to access this service + + + Assigned Services + + + Shows services of this kind currently assigned to users + + + Blocked + + + Cache + + + Services currently in cache + + + Can't Connect to server. Please, check server and retry + + + Do you really want to change current interface language?. Changes will take effect on next restart + + + Check authenticator + + + Check OS Manager + + + Check service provider + + + Delete + + + Deletion failed + + + Publications + + + Publications done from this deployed service + + + Disable + + + Duplicated item + + + Enable + + + Error + + + Groups + + + Disabled + + + Language + + + Manage groups for this authenticator + + + Manage users for this authenticator + + + Modifying + + + Modify + + + You need to set up at least an Authenticator before using this. + + + You need to set up at least an OS Manager before using this. + + + You need to set up at least a Service before using this. + + + You should set up at least a transport before using this. + + + New + + + New Service Provider + + + A new version of UDS Admin is required to communicate with server + + + Services + + + You need to specify the authenticator to be used + + + You need to specify the service to be used + + + You need to specify the OS Manager to be used + + + Users + + + View + + + Cache Level + + + Id + + + Owner + + + Publish + + + Publication Failed + + + Generate a new publication for this service? + + + Cancel + + + Select only one item to perform this operation. + + + Deployed Service + + + No + + + Occupied + + + Are you sure do you want to remove this item? + + + Yes + + + OS Manager + + + The application will exit now + + + The credentials provided are no longer valid + + + The item do not exists anymore. Please, refresh gui (F5) + + + Invalid credentials + + + There are errors in the data provided + + + Active + + + Blocked + + + Canceled + + + Canceling + + + Error + + + Inactive + + + Generating + + + Waiting for removal + + + Removed + + + Removing + + + Unknown + + + Ready + + + Waiting OS To Get Ready + + + Assign Service to user + + + Service Providers + + + Field '{0}' is required + + + The field 'name' is required + + + You need to select a group + + + You need to select a service + + + You need to select an user + + + User Preferences + + + Transports + + + Neworks + + + Invalid Ip Address + + + Network End + + + Network Start + + + Network end must be greater than network start + + + Transport not active for selected networks + + + Transport active for selected networks + + + Test unsuccessful + + + Test successful + + + Authenticators + + + Connectivity + + + Deployed Services + + + OS Managers + + + Get Error Information + + + Do you want to download and install the new version? + + + Downloaded {0} KB of {1} KB at {2:0.00} KB/s + + + Exit + + + Text Files|*.txt|All files|*.* + + + Import text file + + + Group + + + Search Group + + + Search User + + + User + + + Modification failed + + + Assign Service + + + Assign New Group + + + Authenticator + + + Deployed Service + + + Downloader + + + Group + + + Login to UDS Administration + + + UDS Administration Client + + + Network + + + OS Manager + + + Service + + + Service Provider + + + Transport + + + User + + + User Preferences + + + The cache has been flushed + + + Launching publication + + + You need to select a transport + + + Configuration + + + This authenticator do not allows the creation of new groups at administration interface + + + This authenticator do not allows the creation of new users at administration interface + + + This authenticator do not allows the modification of users at administration interface + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/UdsAdmin.csproj b/client/administration/UdsAdmin/UdsAdmin.csproj new file mode 100644 index 000000000..89a3fcb29 --- /dev/null +++ b/client/administration/UdsAdmin/UdsAdmin.csproj @@ -0,0 +1,847 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {F8DBFEC2-6B52-4A89-AD0B-1886B2ABC11D} + WinExe + Properties + UdsAdmin + UdsAdmin + 512 + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 1 + 1.0.0.%2a + false + true + true + v3.5 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + UdsAdmin.Program + + + 4D3CEB1F26E78F248C53715BFC18F75507F77C07 + + + UdsAdmin_TemporaryKey.pfx + + + true + + + true + + + Properties\app.manifest + + + LocalIntranet + + + + ..\xmlrpc\CookComputing.XmlRpcV2.dll + + + + + + + + + + + + + UserControl + + + ListEditor.cs + + + UserControl + + + AuthsPanel.cs + + + UserControl + + + NetworksPanel.cs + + + UserControl + + + ServicePanel.cs + + + UserControl + + + ServicesPanel.cs + + + UserControl + + + ServiceProvidersPanel.cs + + + UserControl + + + OsManagersPanel.cs + + + UserControl + + + TransportsPanel.cs + + + UserControl + + + DeployedServicesPanel.cs + + + UserControl + + + DeployedGroupsPanel.cs + + + UserControl + + + DeployedServicePanel.cs + + + UserControl + + + PublicationsPanel.cs + + + UserControl + + + DeployedPanel.cs + + + UserControl + + + GroupsPanel.cs + + + UserControl + + + PanelEmpty.cs + + + UserControl + + + UsersPanel.cs + + + + + + + Form + + + AboutBoxForm.cs + + + Form + + + ListEditorForm.cs + + + Form + + + AssignDeployed.cs + + + Form + + + AuthenticatorForm.cs + + + Form + + + ConfigurationForm.cs + + + Form + + + DeployedTransportForm.cs + + + Form + + + DeployedGroupForm.cs + + + Form + + + DeployedServiceForm.cs + + + Form + + + FileDownloader.cs + + + Form + + + NetworkForm.cs + + + Form + + + SearchForm.cs + + + Form + + + TransportForm.cs + + + Form + + + OSManagerForm.cs + + + Form + + + UserForm.cs + + + Form + + + GroupForm.cs + + + Form + + + ServiceForm.cs + + + Form + + + LoginForm.cs + + + Form + + + ServiceProviderForm.cs + + + Form + + + MainForm.cs + + + Form + + + UserPreferencesForm.cs + + + + + + + + + + True + True + Images.resx + + + True + True + Strings.de.resx + + + True + True + Strings.resx + + + True + True + Strings.es.resx + + + True + True + Strings.fr.resx + + + + + + + + + + + ListEditorForm.cs + + + ListEditorForm.cs + + + ListEditorForm.cs + + + ListEditor.cs + + + ListEditor.cs + + + ListEditor.cs + + + ListEditor.cs + + + AuthsPanel.cs + + + AuthsPanel.cs + + + AuthsPanel.cs + + + AuthsPanel.cs + + + DeployedGroupsPanel.cs + + + DeployedGroupsPanel.cs + + + DeployedGroupsPanel.cs + + + DeployedPanel.cs + + + DeployedPanel.cs + + + DeployedPanel.cs + + + DeployedServicePanel.cs + + + DeployedServicePanel.cs + + + DeployedServicePanel.cs + + + DeployedServicesPanel.cs + + + DeployedServicesPanel.cs + + + DeployedServicesPanel.cs + + + GroupsPanel.cs + + + GroupsPanel.cs + + + GroupsPanel.cs + + + NetworksPanel.cs + + + NetworksPanel.cs + + + NetworksPanel.cs + + + NetworksPanel.cs + + + OsManagersPanel.cs + + + OsManagersPanel.cs + + + OsManagersPanel.cs + + + PanelEmpty.cs + + + PanelEmpty.cs + + + PanelEmpty.cs + + + PublicationsPanel.cs + + + PublicationsPanel.cs + + + PublicationsPanel.cs + + + ServicePanel.cs + + + ServicePanel.cs + + + ServicePanel.cs + + + ServicePanel.cs + + + ServiceProvidersPanel.cs + + + ServiceProvidersPanel.cs + + + ServiceProvidersPanel.cs + + + ServicesPanel.cs + + + ServicesPanel.cs + + + ServicesPanel.cs + + + ServicesPanel.cs + + + ServiceProvidersPanel.cs + + + OsManagersPanel.cs + + + TransportsPanel.cs + + + TransportsPanel.cs + + + TransportsPanel.cs + + + TransportsPanel.cs + + + DeployedServicesPanel.cs + + + DeployedGroupsPanel.cs + + + DeployedServicePanel.cs + + + PublicationsPanel.cs + + + DeployedPanel.cs + + + GroupsPanel.cs + + + PanelEmpty.cs + + + UsersPanel.cs + + + UsersPanel.cs + + + UsersPanel.cs + + + UsersPanel.cs + + + AboutBoxForm.cs + + + AboutBoxForm.cs + + + AboutBoxForm.cs + + + AboutBoxForm.cs + + + ListEditorForm.cs + + + AssignDeployed.cs + + + AssignDeployed.cs + + + AssignDeployed.cs + + + AssignDeployed.cs + + + AuthenticatorForm.cs + + + AuthenticatorForm.cs + + + AuthenticatorForm.cs + + + AuthenticatorForm.cs + + + ConfigurationForm.cs + + + ConfigurationForm.cs + + + ConfigurationForm.cs + + + ConfigurationForm.cs + + + DeployedServiceForm.cs + + + DeployedTransportForm.cs + + + DeployedTransportForm.cs + + + DeployedTransportForm.cs + + + DeployedTransportForm.cs + + + DeployedGroupForm.cs + + + DeployedGroupForm.cs + + + DeployedGroupForm.cs + + + DeployedGroupForm.cs + + + DeployedServiceForm.cs + + + DeployedServiceForm.cs + + + DeployedServiceForm.cs + + + FileDownloader.cs + + + FileDownloader.cs + + + FileDownloader.cs + + + FileDownloader.cs + + + GroupForm.cs + + + GroupForm.cs + + + GroupForm.cs + + + LoginForm.cs + + + LoginForm.cs + + + MainForm.cs + + + MainForm.cs + + + MainForm.cs + + + NetworkForm.cs + + + NetworkForm.cs + + + NetworkForm.cs + + + NetworkForm.cs + + + OSManagerForm.cs + + + OSManagerForm.cs + + + OSManagerForm.cs + + + SearchForm.cs + + + ServiceForm.cs + + + ServiceForm.cs + + + ServiceForm.cs + + + ServiceProviderForm.cs + + + ServiceProviderForm.cs + + + ServiceProviderForm.cs + + + TransportForm.cs + + + OSManagerForm.cs + + + UserForm.cs + + + UserForm.cs + + + UserForm.cs + + + UserForm.cs + + + GroupForm.cs + + + ServiceForm.cs + + + LoginForm.cs + + + LoginForm.cs + + + ServiceProviderForm.cs + + + MainForm.cs + + + UserPreferencesForm.cs + + + UserPreferencesForm.cs + + + UserPreferencesForm.cs + + + UserPreferencesForm.cs + + + ResXFileCodeGenerator + Images.Designer.cs + + + ResXFileCodeGenerator + Strings.de.Designer.cs + + + ResXFileCodeGenerator + Strings.es.Designer.cs + Designer + + + ResXFileCodeGenerator + Strings.fr.Designer.cs + + + ResXFileCodeGenerator + Strings.Designer.cs + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/UdsAdmin_TemporaryKey.pfx b/client/administration/UdsAdmin/UdsAdmin_TemporaryKey.pfx new file mode 100644 index 0000000000000000000000000000000000000000..bb50d1f222ea100582c080f2c3b70c86359c722e GIT binary patch literal 1676 zcmY+Dc~sNa631_T35g(!OB9U~0+d&j?HAb_TGoI;LXlcR3d#e5P%1=e1Tm)IPl+f5 zWl^3$06`GS=0k`Esx2srMhHYHVvC7@EvSeHgdO|C)8+KsGk0b_Gjq@VW41Ug4(iF<%i-Iy0a$x(sRYn;>M9s0aHpS#7~ z21WX{IVS@2yHsPThI)+1N^)g{i-#66z1%N9~%F<&BPi|i9df^gOdN!oqo zEqwuVyV_gGbiYWPd#G32RBaYk-y2Q5bvF97%YbwpQ0>S_w|V8S;*>mn3Wz^Bs%muyGYLDSW;`bC>zKOJ8vUEC%F-`s=?y zHz}$(bc=aJEB5w_xxLpZ`QP|5OD8Q1_mPM0kOr?ARGW1h7W$u#Qq~oSgI#jU_t%9s zo}2ae)_*n||8S(mIp`{#ij!rX7r!}$6{YZ}iaUlq3)ok$V=e^FNITA$rlx#Zb#?}P zL_1#V=n|bfYMnj&y+zxShFZVkGPZ6&_TtSNjM_(eA!eDTdtOF)%c}T6-g>dRb2_Tz z|KhwGO0tt^e%r|SncexS!+l|o444}W;h4dxzNu2#sE6XF(%flPW}^waOCoG_7PamL zv;*%h_<}y2d}gX`@%9L3Vns#_W_-?*#U5Jq?)cglC!X$`Hy+rSz}L`mVfDN$?1)(W z><~B8IO47=&k9^WQF78#$)EeF?sr>|R%Rt+ebSj;mZMgePy@OV=O#r1>4|BGZ1LhJ z=>hzHitK5n$(@B%?{Y!g5xXX-R@*~SEi0jUi7R-|FiIqnzkePAL4%%IXS*4#I@(um zkzVb;f5b+*`p1cllCn%!-w?U_nqFy?#d7Et8r3TCLTv3~sX@CSr9?{8V8$<~j235~ zQ~S@;n&PO(d*Jndues(1DjtW!007|Ne;|?SNA?ALKn3=|4(vdW6QBc6U&N#G#LxM&^^!q7PS!xRPhf9OjFK@?LTjImugEn8fy@x{Z^|!T*NpoubD2C*_n-2xI-(85b%Bw)yG*8PW zMGAuWoq=skfQnL4{7o#>75;b=vA>0ry-T1;UgL9yM$$FJ&2xX4)NYrrUl(^Or0nBi zEyiVnL=xvJc-nY!Qv?)yN0hrv=Y;$6=n)(B;Fbl( zm-~gzk9MWi1Pxsa@3p*|)nh+L$=Pz*)EPA2Tdt}p`rrNV%=0lC%?C1|>0`R(yUnI7 zj0c2Vr5C5*uw$gcqw0QnQvHAfgy6gC>?=vhYtvMZh&AJv-fYrPGJ2EsayR^^Y9D)w zCp$LAGkWb>KJ#wy?e@b`N5hUduF9Ob?<0EiUiE+#G`H2BW6e_hcrQx*IMdZkVze08 zd3`Xd7oOT2HkxAW^uDfi#^UH&_pq$co(BgqekysgCzp&I?;x8vu!Wd0hCox4R#3gz zc}{9|_3LLFT7UWNXvPjv(oAo-@yn1fnqX)yoo!_6G@PpLQH_D4zkV#%-iaG;cyp?r z;q~*~X8o3=i6KLAYMaVDTXM%=;bUB$UgXx;&-FHUw|CmpcJN8J-L%B%8bm1_SranM z8OyJKLa2#Kp3N0TPMW|p*bpY*HOzIf5D7 + + + +
+ + + + + + en-US + + + 20000 + + + 93 + + + 220 + + + 81 + + + 93 + + + 250 + + + 320 + + + + diff --git a/client/administration/UdsAdmin/controls/ListEditor.Designer.cs b/client/administration/UdsAdmin/controls/ListEditor.Designer.cs new file mode 100644 index 000000000..ad86fc3fb --- /dev/null +++ b/client/administration/UdsAdmin/controls/ListEditor.Designer.cs @@ -0,0 +1,57 @@ +namespace UdsAdmin.controls +{ + public partial class ListEditor + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ListEditor)); + this.bntOpen = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // bntOpen + // + resources.ApplyResources(this.bntOpen, "bntOpen"); + this.bntOpen.Name = "bntOpen"; + this.bntOpen.UseVisualStyleBackColor = true; + this.bntOpen.Click += new System.EventHandler(this.bntOpen_Click); + // + // ListEditor + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.bntOpen); + this.Name = "ListEditor"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button bntOpen; + + } +} diff --git a/client/administration/UdsAdmin/controls/ListEditor.cs b/client/administration/UdsAdmin/controls/ListEditor.cs new file mode 100644 index 000000000..b0a1ff12d --- /dev/null +++ b/client/administration/UdsAdmin/controls/ListEditor.cs @@ -0,0 +1,93 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls +{ + public partial class ListEditor : UserControl + { + private forms.ListEditorForm _form; + public ListEditor() + { + InitializeComponent(); + _form = new forms.ListEditorForm(); + } + + private void bntOpen_Click(object sender, EventArgs e) + { + _form.ShowDialog(); + } + + private List readItems() + { + List res = new List(_form.Items.Count); + foreach (string v in _form.Items) + res.Add(v); + + return res; + } + + private void writeItems(List values) + { + _form.Items.Clear(); + foreach (string v in values) + _form.Items.Add(v); + } + + [Category("Design")] + [Description("Tittle of the opened list editor window")] + public override string Text + { + get + { + return base.Text; + } + set + { + _form.Text = value; + base.Text = value; + } + } + + public List Items + { + get { return readItems(); } + set { writeItems(value); } + } + + } +} diff --git a/client/administration/UdsAdmin/controls/ListEditor.de.resx b/client/administration/UdsAdmin/controls/ListEditor.de.resx new file mode 100644 index 000000000..de450cc6d --- /dev/null +++ b/client/administration/UdsAdmin/controls/ListEditor.de.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Fill + + + + 0, 0 + + + 185, 23 + + + + 0 + + + Öffnen Sie den editor + + + bntOpen + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 185, 23 + + + ListEditor + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/ListEditor.es.resx b/client/administration/UdsAdmin/controls/ListEditor.es.resx new file mode 100644 index 000000000..1e2384be9 --- /dev/null +++ b/client/administration/UdsAdmin/controls/ListEditor.es.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Fill + + + + 0, 0 + + + 185, 23 + + + + 0 + + + Abrir el editor + + + bntOpen + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 185, 23 + + + ListEditor + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/ListEditor.fr.resx b/client/administration/UdsAdmin/controls/ListEditor.fr.resx new file mode 100644 index 000000000..af1998595 --- /dev/null +++ b/client/administration/UdsAdmin/controls/ListEditor.fr.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Fill + + + + 0, 0 + + + 185, 23 + + + + 0 + + + Ouvrez l'éditeur + + + bntOpen + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 185, 23 + + + ListEditor + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/ListEditor.resx b/client/administration/UdsAdmin/controls/ListEditor.resx new file mode 100644 index 000000000..8e6b0dcf2 --- /dev/null +++ b/client/administration/UdsAdmin/controls/ListEditor.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Fill + + + + 0, 0 + + + 185, 23 + + + + 0 + + + Open the editor + + + bntOpen + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 185, 23 + + + ListEditor + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/forms/ListEditorForm.Designer.cs b/client/administration/UdsAdmin/controls/forms/ListEditorForm.Designer.cs new file mode 100644 index 000000000..5a9ca8a1a --- /dev/null +++ b/client/administration/UdsAdmin/controls/forms/ListEditorForm.Designer.cs @@ -0,0 +1,118 @@ +namespace UdsAdmin.controls.forms +{ + partial class ListEditorForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ListEditorForm)); + this.lstItems = new System.Windows.Forms.ListBox(); + this.textItem = new System.Windows.Forms.TextBox(); + this.btnImport = new System.Windows.Forms.Button(); + this.layout = new System.Windows.Forms.TableLayoutPanel(); + this.btnRemove = new System.Windows.Forms.Button(); + this.btnAdd = new System.Windows.Forms.Button(); + this.btnClose = new System.Windows.Forms.Button(); + this.layout.SuspendLayout(); + this.SuspendLayout(); + // + // lstItems + // + this.layout.SetColumnSpan(this.lstItems, 2); + resources.ApplyResources(this.lstItems, "lstItems"); + this.lstItems.FormattingEnabled = true; + this.lstItems.Name = "lstItems"; + // + // textItem + // + resources.ApplyResources(this.textItem, "textItem"); + this.textItem.Name = "textItem"; + this.textItem.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.textItem_KeyPress); + // + // btnImport + // + resources.ApplyResources(this.btnImport, "btnImport"); + this.btnImport.Name = "btnImport"; + this.btnImport.UseVisualStyleBackColor = true; + this.btnImport.Click += new System.EventHandler(this.btnImport_Click); + // + // layout + // + resources.ApplyResources(this.layout, "layout"); + this.layout.Controls.Add(this.lstItems, 0, 0); + this.layout.Controls.Add(this.textItem, 0, 1); + this.layout.Controls.Add(this.btnImport, 0, 2); + this.layout.Controls.Add(this.btnRemove, 1, 2); + this.layout.Controls.Add(this.btnAdd, 1, 1); + this.layout.Controls.Add(this.btnClose, 0, 3); + this.layout.Name = "layout"; + // + // btnRemove + // + resources.ApplyResources(this.btnRemove, "btnRemove"); + this.btnRemove.Name = "btnRemove"; + this.btnRemove.UseVisualStyleBackColor = true; + this.btnRemove.Click += new System.EventHandler(this.btnRemove_Click); + // + // btnAdd + // + resources.ApplyResources(this.btnAdd, "btnAdd"); + this.btnAdd.Name = "btnAdd"; + this.btnAdd.UseVisualStyleBackColor = true; + this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click); + // + // btnClose + // + this.layout.SetColumnSpan(this.btnClose, 2); + resources.ApplyResources(this.btnClose, "btnClose"); + this.btnClose.Name = "btnClose"; + this.btnClose.UseVisualStyleBackColor = true; + this.btnClose.Click += new System.EventHandler(this.btnClose_Click); + // + // ListEditorForm + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.layout); + this.Name = "ListEditorForm"; + this.Load += new System.EventHandler(this.ListEditorForm_Load); + this.layout.ResumeLayout(false); + this.layout.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListBox lstItems; + private System.Windows.Forms.TableLayoutPanel layout; + private System.Windows.Forms.TextBox textItem; + private System.Windows.Forms.Button btnImport; + private System.Windows.Forms.Button btnRemove; + private System.Windows.Forms.Button btnAdd; + private System.Windows.Forms.Button btnClose; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/forms/ListEditorForm.cs b/client/administration/UdsAdmin/controls/forms/ListEditorForm.cs new file mode 100644 index 000000000..9d924f9e2 --- /dev/null +++ b/client/administration/UdsAdmin/controls/forms/ListEditorForm.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; + +namespace UdsAdmin.controls.forms +{ + public partial class ListEditorForm : Form + { + public ListEditorForm() + { + InitializeComponent(); + } + + private void addTxtItem(string txt) + { + lstItems.Items.Add(txt); + } + + private void addCommaSeparatedValues(string txt) + { + foreach (string item in txt.Split(',')) + { + addTxtItem(item); + } + } + + public ListBox.ObjectCollection Items + { + get { return lstItems.Items; } + } + + private void processTxt() + { + string item = textItem.Text; + if (item.Length == 0) + return; + + if (item.Contains(',')) + addCommaSeparatedValues(item); + else + addTxtItem(item); + + textItem.Text = ""; + textItem.Focus(); + } + + + private void textItem_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == 13) + { + processTxt(); + } + } + + private void btnRemove_Click(object sender, EventArgs e) + { + if (lstItems.SelectedIndex != -1) + { + int sel = lstItems.SelectedIndex; + lstItems.Items.RemoveAt(sel); + if (sel > 0) + lstItems.SelectedIndex = sel - 1; + else if (lstItems.Items.Count > 0) + lstItems.SelectedIndex = 0; + } + } + + private void btnAdd_Click(object sender, EventArgs e) + { + processTxt(); + } + + private void btnClose_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.OK; + } + + private void ListEditorForm_Load(object sender, EventArgs e) + { + this.Location = System.Windows.Forms.Cursor.Position; + } + + private void btnImport_Click(object sender, EventArgs e) + { + OpenFileDialog dlg = new OpenFileDialog(); + dlg.Filter = Strings.importFilter; + dlg.DefaultExt = "txt"; + dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + StreamReader reader = File.OpenText(dlg.FileName); + string line; + while ((line = reader.ReadLine()) != null) + { + if( line.Contains(',') ) + addCommaSeparatedValues(line); + else + addTxtItem(line); + } + reader.Close(); + } + } + } +} diff --git a/client/administration/UdsAdmin/controls/forms/ListEditorForm.de.resx b/client/administration/UdsAdmin/controls/forms/ListEditorForm.de.resx new file mode 100644 index 000000000..10b18f87e --- /dev/null +++ b/client/administration/UdsAdmin/controls/forms/ListEditorForm.de.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ListEditorFormfr + + + Entfernen + + + Hinzufügen + + + Datei importieren + + + Editor schließen + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/forms/ListEditorForm.es.resx b/client/administration/UdsAdmin/controls/forms/ListEditorForm.es.resx new file mode 100644 index 000000000..616244895 --- /dev/null +++ b/client/administration/UdsAdmin/controls/forms/ListEditorForm.es.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Añadir + + + Quitar + + + Archivo de importación + + + Cerrar editor + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/forms/ListEditorForm.fr.resx b/client/administration/UdsAdmin/controls/forms/ListEditorForm.fr.resx new file mode 100644 index 000000000..6ebc089d5 --- /dev/null +++ b/client/administration/UdsAdmin/controls/forms/ListEditorForm.fr.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ListEditorFormfr + + + Supprimer + + + Ajouter + + + Fichier d'importation + + + Éditeur étroite + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/forms/ListEditorForm.resx b/client/administration/UdsAdmin/controls/forms/ListEditorForm.resx new file mode 100644 index 000000000..262c178d0 --- /dev/null +++ b/client/administration/UdsAdmin/controls/forms/ListEditorForm.resx @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + + Fill + + + + 3, 187 + + + 157, 20 + + + 1 + + + textItem + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + layout + + + 1 + + + 3, 213 + + + 75, 20 + + + 2 + + + Import file + + + btnImport + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + layout + + + 2 + + + Top, Right + + + 206, 213 + + + 75, 20 + + + 3 + + + Remove + + + btnRemove + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + layout + + + 3 + + + Top, Right + + + 206, 187 + + + 75, 20 + + + 4 + + + Add + + + btnAdd + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + layout + + + 4 + + + Fill + + + 3, 239 + + + 278, 20 + + + 5 + + + Close editor + + + btnClose + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + layout + + + 5 + + + Fill + + + 0, 0 + + + 4 + + + 284, 262 + + + 2 + + + layout + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="lstItems" Row="0" RowSpan="1" Column="0" ColumnSpan="2" /><Control Name="textItem" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="btnImport" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="btnRemove" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="btnAdd" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="btnClose" Row="3" RowSpan="1" Column="0" ColumnSpan="2" /></Controls><Columns Styles="Percent,57,45614,Percent,42,54386" /><Rows Styles="Percent,100,Absolute,26,Absolute,26,Absolute,26" /></TableLayoutSettings> + + + Fill + + + 3, 3 + + + 278, 178 + + + 0 + + + lstItems + + + System.Windows.Forms.ListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + layout + + + 0 + + + True + + + 6, 13 + + + 284, 262 + + + Manual + + + Editor + + + ListEditorForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/AuthsPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/AuthsPanel.Designer.cs new file mode 100644 index 000000000..552657e52 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/AuthsPanel.Designer.cs @@ -0,0 +1,93 @@ +namespace UdsAdmin.controls.panel +{ + partial class AuthsPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AuthsPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.priority = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.typeName, + this.comments, + this.priority}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // typeName + // + resources.ApplyResources(this.typeName, "typeName"); + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // priority + // + resources.ApplyResources(this.priority, "priority"); + // + // AuthsPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "AuthsPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader typeName; + private System.Windows.Forms.ColumnHeader priority; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/AuthsPanel.cs b/client/administration/UdsAdmin/controls/panel/AuthsPanel.cs new file mode 100644 index 000000000..6e6704cb3 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/AuthsPanel.cs @@ -0,0 +1,105 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class AuthsPanel : UserControl + { + gui.ListViewSorter _listSorter; + + public AuthsPanel() + { + InitializeComponent(); + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.Authenticator[] auths = xmlrpc.UdsAdminService.GetAuthenticators(); + List lst = new List(); + foreach (xmlrpc.Authenticator auth in auths) + { + ListViewItem itm = new ListViewItem(new string[] { auth.name, auth.typeName, auth.comments, auth.priority }); + itm.ForeColor = gui.Colors.ActiveColor; + itm.Tag = auth.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/AuthsPanel.de.resx b/client/administration/UdsAdmin/controls/panel/AuthsPanel.de.resx new file mode 100644 index 000000000..fcf0d1256 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/AuthsPanel.de.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Kommentare + + + Typ + + + Priorität + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/AuthsPanel.es.resx b/client/administration/UdsAdmin/controls/panel/AuthsPanel.es.resx new file mode 100644 index 000000000..f128d3fba --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/AuthsPanel.es.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Comentarios + + + Tipo + + + Prioridad + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/AuthsPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/AuthsPanel.fr.resx new file mode 100644 index 000000000..ba36abc6f --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/AuthsPanel.fr.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Commentaires + + + Type + + + Priorité + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/AuthsPanel.resx b/client/administration/UdsAdmin/controls/panel/AuthsPanel.resx new file mode 100644 index 000000000..18b826e66 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/AuthsPanel.resx @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Type + + + + 135 + + + Comments + + + Priority + + + 73 + + + + Fill + + + False + + + + 0, 0 + + + 699, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 699, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + priority + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + AuthsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.Designer.cs new file mode 100644 index 000000000..db0b68209 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.Designer.cs @@ -0,0 +1,95 @@ +namespace UdsAdmin.controls.panel +{ + partial class DeployedGroupsPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployedGroupsPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.state = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.auth = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.auth, + this.name, + this.state, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + this.listView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView_MouseUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // state + // + resources.ApplyResources(this.state, "state"); + this.state.Width = global::UdsAdmin.Properties.Settings.Default.wStateCol; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // auth + // + resources.ApplyResources(this.auth, "auth"); + // + // DeployedGroupsPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "DeployedGroupsPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader state; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader auth; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.cs b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.cs new file mode 100644 index 000000000..124befdad --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.cs @@ -0,0 +1,177 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class DeployedGroupsPanel : UserControl + { + private xmlrpc.DeployedService _ds; + ContextMenuStrip _selectedMenu; + ContextMenuStrip _unselectedMenu; + gui.ListViewSorter _listSorter; + + public DeployedGroupsPanel(xmlrpc.DeployedService dps) + { + _ds = dps; + InitializeComponent(); + + _selectedMenu = new ContextMenuStrip(); + _unselectedMenu = new ContextMenuStrip(); + + ToolStripMenuItem enable = new ToolStripMenuItem(Strings.enable); enable.Click += enableItem; enable.Image = Images.apply16; + ToolStripMenuItem disable = new ToolStripMenuItem(Strings.disable); disable.Click += disableItem; disable.Image = Images.cancel16; + ToolStripSeparator sep = new ToolStripSeparator(); + ToolStripMenuItem newG = new ToolStripMenuItem(Strings.newItem); newG.Click += newItem; newG.Image = Images.new16; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Click += deleteItem; delete.Image = Images.delete16; + + ToolStripMenuItem newG2 = new ToolStripMenuItem(Strings.newItem); newG2.Click += newItem; newG2.Image = Images.new16; + ToolStripMenuItem delete2 = new ToolStripMenuItem(Strings.deleteItem); delete2.Click += deleteItem; delete2.Image = Images.delete16; + + _selectedMenu.Items.AddRange(new ToolStripItem[] { enable, disable, sep, newG, delete }); + _unselectedMenu.Items.AddRange(new ToolStripItem[] { newG2, delete2 }); + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + xmlrpc.Group[] grps = xmlrpc.UdsAdminService.GetGroupsAssignedToDeployedService(_ds.id); + List lst = new List(); + foreach (xmlrpc.Group grp in grps) + { + ListViewItem itm = new ListViewItem(new string[]{grp.nameParent, grp.name, grp.active ? Strings.active : Strings.inactive , grp.comments}); + itm.ForeColor = grp.active ? gui.Colors.ActiveColor : gui.Colors.InactiveColor; + itm.Tag = grp.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + + private void newItem(object sender, EventArgs e) + { + UdsAdmin.forms.DeployedGroupForm form = new UdsAdmin.forms.DeployedGroupForm(_ds); + if (form.ShowDialog() == DialogResult.OK) + updateList(); + } + + private void setSelectedStates(bool newState) + { + if( listView.SelectedItems.Count == 0 ) + return; + string info = newState == true ? Strings.active : Strings.inactive; + Color col = newState == true ? gui.Colors.ActiveColor : gui.Colors.InactiveColor; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + i.SubItems[2].Text = info; + i.ForeColor = col; + } + xmlrpc.UdsAdminService.ChangeGroupsState(ids, newState); + + } + + private void enableItem(object sender, EventArgs e) + { + setSelectedStates(true); + } + + private void disableItem(object sender, EventArgs e) + { + setSelectedStates(false); + } + + private void deleteItem(object sender, EventArgs e) + { + if (listView.SelectedItems.Count == 0) + return; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + listView.Items.Remove(i); + } + xmlrpc.UdsAdminService.RemoveGroupsFromDeployedService(_ds.id, ids); + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + private void listView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + if (listView.SelectedItems.Count == 0) + _unselectedMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + else + _selectedMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.de.resx b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.de.resx new file mode 100644 index 000000000..d597a1b5f --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.de.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Staat + + + Kommentare + + + Authentifikator + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.es.resx b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.es.resx new file mode 100644 index 000000000..f1c9704a6 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.es.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Estado + + + Comentarios + + + Autenticador + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.fr.resx new file mode 100644 index 000000000..ff49e44c0 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.fr.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + État + + + Commentaires + + + Authentificateur + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.resx b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.resx new file mode 100644 index 000000000..a0a02743c --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedGroupsPanel.resx @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Authenticator + + + + 153 + + + Name + + + State + + + Comments + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + auth + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedGroupsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/DeployedPanel.Designer.cs new file mode 100644 index 000000000..0df4e5c95 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedPanel.Designer.cs @@ -0,0 +1,106 @@ +namespace UdsAdmin.controls.panel +{ + partial class DeployedPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployedPanel)); + this.Id = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.CreationDate = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.State = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.StatusDate = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.listView = new System.Windows.Forms.ListView(); + this.friendlyName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.Revision = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // Id + // + resources.ApplyResources(this.Id, "Id"); + // + // CreationDate + // + resources.ApplyResources(this.CreationDate, "CreationDate"); + // + // State + // + resources.ApplyResources(this.State, "State"); + // + // StatusDate + // + resources.ApplyResources(this.StatusDate, "StatusDate"); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.Id, + this.friendlyName, + this.Revision, + this.CreationDate, + this.State, + this.StatusDate}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + this.listView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView_MouseUp); + // + // friendlyName + // + resources.ApplyResources(this.friendlyName, "friendlyName"); + // + // Revision + // + resources.ApplyResources(this.Revision, "Revision"); + // + // DeployedPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "DeployedPanel"; + this.VisibleChanged += new System.EventHandler(this.DeployedPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader Id; + private System.Windows.Forms.ColumnHeader CreationDate; + private System.Windows.Forms.ColumnHeader State; + private System.Windows.Forms.ColumnHeader StatusDate; + private System.Windows.Forms.ColumnHeader Revision; + private System.Windows.Forms.ColumnHeader friendlyName; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedPanel.cs b/client/administration/UdsAdmin/controls/panel/DeployedPanel.cs new file mode 100644 index 000000000..31f385e89 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedPanel.cs @@ -0,0 +1,212 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class DeployedPanel : UserControl + { + private xmlrpc.DeployedService _parent; + private bool _cache; + ContextMenuStrip _deleteMenu; + ContextMenuStrip _assignMenu; + ContextMenuStrip _infoMenu; + gui.ListViewSorter _listSorter; + + public DeployedPanel(xmlrpc.DeployedService parent, bool cache = false) + { + _parent = parent; + _cache = cache; + _deleteMenu = new ContextMenuStrip(); + _assignMenu = new ContextMenuStrip(); + _infoMenu = new ContextMenuStrip(); + InitializeComponent(); + + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Click += deleteItem; delete.Image = Images.delete16; + _deleteMenu.Items.AddRange(new ToolStripItem[] { delete }); + + ToolStripMenuItem info = new ToolStripMenuItem(Strings.errorInfo); info.Click += infoItem; info.Image = Images.find16; + _infoMenu.Items.AddRange(new ToolStripItem[] { info }); + + ToolStripMenuItem assign = new ToolStripMenuItem(Strings.assignToUser); assign.Click += assignToUser; assign.Image = Images.new16; + _assignMenu.Items.AddRange(new ToolStripItem[] { assign }); + + // Adapt listview to cache or users + if (cache) + { + cacheHeaders(); + } + else + { + assignedHeaders(); + } + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView, new int[] { 3, 5 } ); + + updateList(); + } + + private void DeployedPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void cacheHeaders() + { + ColumnHeader he = new ColumnHeader(); + he.Text = Strings.cacheLevel; he.TextAlign = HorizontalAlignment.Left; he.Width = 128; + listView.Columns.Add(he); + } + + private void assignedHeaders() + { + ColumnHeader userHeader = new ColumnHeader(); userHeader.Text = Strings.owner; userHeader.TextAlign = HorizontalAlignment.Center; + ColumnHeader usedHeader = new ColumnHeader(); usedHeader.Text = Strings.occopied; usedHeader.TextAlign = HorizontalAlignment.Center; + listView.Columns.AddRange(new ColumnHeader[]{ userHeader, usedHeader}); + } + + private ListViewItem getListViewItemFrom(xmlrpc.UserDeployedService uds) + { + if (_cache == true) + return new ListViewItem(new string[] { uds.uniqueId, uds.friendlyName, uds.revision, uds.creationDate.ToString(), + xmlrpc.Util.GetStringFromState(uds.state, uds.osState), uds.stateDate.ToString(), + ((xmlrpc.CachedDeployedService)uds).cacheLevel}); + xmlrpc.AssignedDeployedService udss = (xmlrpc.AssignedDeployedService)uds; + return new ListViewItem(new string[] { uds.uniqueId, uds.friendlyName, uds.revision, uds.creationDate.ToString(), + xmlrpc.Util.GetStringFromState(uds.state, uds.osState), uds.stateDate.ToString(), udss.user, udss.inUse ? Strings.yes : Strings.no} ); + } + + private void updateList() + { + + xmlrpc.UserDeployedService[] servs; + if (_cache == true) + servs = xmlrpc.UdsAdminService.GetCachedDeployedServices(_parent); + else + servs = xmlrpc.UdsAdminService.GetAssignedDeployedServices(_parent); + + List lst = new List(); + foreach (xmlrpc.UserDeployedService uds in servs) + { + ListViewItem itm = getListViewItemFrom(uds); + itm.Tag = uds.id; + itm.ForeColor = gui.Colors.getColorForState(uds.state); + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + + private void assignToUser(object sender, EventArgs e) + { + UdsAdmin.forms.AssignDeployed form = new UdsAdmin.forms.AssignDeployed(_parent); + if (form.ShowDialog() == DialogResult.OK) + updateList(); + } + + private void infoItem(object sender, EventArgs e) + { + string id = (string)listView.SelectedItems[0].Tag; + string error = xmlrpc.UdsAdminService.GetUserDeployedServiceError(id); + MessageBox.Show(error, Strings.error, MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void deleteItem(object sender, EventArgs e) + { + if (listView.SelectedItems.Count == 0) + return; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + } + try + { + xmlrpc.UdsAdminService.RemoveUserService(ids); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + updateList(); + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + private void listView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + if (listView.SelectedItems.Count == 0) + { + if (_parent.info.mustAssignManually) + _assignMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + else + { + if (listView.SelectedItems.Count == 1 && listView.SelectedItems[0].SubItems[4].Text == xmlrpc.Util.GetStringFromState(xmlrpc.Constants.STATE_ERROR)) + _infoMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + else + _deleteMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + } + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedPanel.de.resx b/client/administration/UdsAdmin/controls/panel/DeployedPanel.de.resx new file mode 100644 index 000000000..4f6eef408 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedPanel.de.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ID + + + Erstellungsdatum + + + Staat + + + Statusdatum + + + Angezeigter Name + + + Überarbeitung + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + StatusDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + friendlyName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedPanel.es.resx b/client/administration/UdsAdmin/controls/panel/DeployedPanel.es.resx new file mode 100644 index 000000000..67c7f484d --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedPanel.es.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ID + + + Fecha de creación + + + Estado + + + Fecha de Estado + + + Nombre descriptivo + + + Revisión + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + StatusDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + friendlyName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/DeployedPanel.fr.resx new file mode 100644 index 000000000..0ebabed54 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedPanel.fr.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ID + + + Date de création + + + État + + + Date d'État + + + Nom convivial + + + Révision + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + StatusDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + friendlyName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedPanel.resx b/client/administration/UdsAdmin/controls/panel/DeployedPanel.resx new file mode 100644 index 000000000..823ac3567 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedPanel.resx @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Id + + + + 125 + + + Creation Date + + + 150 + + + State + + + 120 + + + Status Date + + + 143 + + + Friendly Name + + + 142 + + + Revision + + + 109 + + + + Fill + + + False + + + + 0, 0 + + + 784, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 784, 279 + + + Id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + StatusDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + friendlyName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.Designer.cs new file mode 100644 index 000000000..23cef6d69 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.Designer.cs @@ -0,0 +1,232 @@ +namespace UdsAdmin.controls.panel +{ + partial class DeployedServicePanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployedServicePanel)); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.groupLabel = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.lName = new System.Windows.Forms.Label(); + this.lComments = new System.Windows.Forms.Label(); + this.lBaseService = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.lOsManager = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.lInitial = new System.Windows.Forms.Label(); + this.cacheLabel = new System.Windows.Forms.Label(); + this.lCache = new System.Windows.Forms.Label(); + this.cacheL2Label = new System.Windows.Forms.Label(); + this.lL2Cache = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.lMax = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.lState = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.groupLabel, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.lName, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.lComments, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.lBaseService, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.lOsManager, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.lInitial, 1, 4); + this.tableLayoutPanel1.Controls.Add(this.cacheLabel, 0, 5); + this.tableLayoutPanel1.Controls.Add(this.lCache, 1, 5); + this.tableLayoutPanel1.Controls.Add(this.cacheL2Label, 0, 6); + this.tableLayoutPanel1.Controls.Add(this.lL2Cache, 1, 6); + this.tableLayoutPanel1.Controls.Add(this.lMax, 1, 7); + this.tableLayoutPanel1.Controls.Add(this.label6, 0, 8); + this.tableLayoutPanel1.Controls.Add(this.lState, 1, 8); + this.tableLayoutPanel1.Controls.Add(this.label10, 0, 4); + this.tableLayoutPanel1.Controls.Add(this.label7, 0, 7); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // groupLabel + // + resources.ApplyResources(this.groupLabel, "groupLabel"); + this.groupLabel.CausesValidation = false; + this.groupLabel.Name = "groupLabel"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.CausesValidation = false; + this.label2.Name = "label2"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.CausesValidation = false; + this.label1.Name = "label1"; + // + // lName + // + resources.ApplyResources(this.lName, "lName"); + this.lName.BackColor = System.Drawing.SystemColors.Window; + this.lName.Name = "lName"; + // + // lComments + // + resources.ApplyResources(this.lComments, "lComments"); + this.lComments.BackColor = System.Drawing.SystemColors.Window; + this.lComments.Name = "lComments"; + // + // lBaseService + // + resources.ApplyResources(this.lBaseService, "lBaseService"); + this.lBaseService.BackColor = System.Drawing.SystemColors.Window; + this.lBaseService.Name = "lBaseService"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.CausesValidation = false; + this.label3.Name = "label3"; + // + // lOsManager + // + resources.ApplyResources(this.lOsManager, "lOsManager"); + this.lOsManager.BackColor = System.Drawing.SystemColors.Window; + this.lOsManager.Name = "lOsManager"; + // + // label10 + // + resources.ApplyResources(this.label10, "label10"); + this.label10.CausesValidation = false; + this.label10.Name = "label10"; + // + // lInitial + // + resources.ApplyResources(this.lInitial, "lInitial"); + this.lInitial.BackColor = System.Drawing.SystemColors.Window; + this.lInitial.Name = "lInitial"; + // + // cacheLabel + // + resources.ApplyResources(this.cacheLabel, "cacheLabel"); + this.cacheLabel.CausesValidation = false; + this.cacheLabel.Name = "cacheLabel"; + // + // lCache + // + resources.ApplyResources(this.lCache, "lCache"); + this.lCache.BackColor = System.Drawing.SystemColors.Window; + this.lCache.Name = "lCache"; + // + // cacheL2Label + // + resources.ApplyResources(this.cacheL2Label, "cacheL2Label"); + this.cacheL2Label.CausesValidation = false; + this.cacheL2Label.Name = "cacheL2Label"; + // + // lL2Cache + // + resources.ApplyResources(this.lL2Cache, "lL2Cache"); + this.lL2Cache.BackColor = System.Drawing.SystemColors.Window; + this.lL2Cache.Name = "lL2Cache"; + // + // label7 + // + resources.ApplyResources(this.label7, "label7"); + this.label7.CausesValidation = false; + this.label7.Name = "label7"; + // + // lMax + // + resources.ApplyResources(this.lMax, "lMax"); + this.lMax.BackColor = System.Drawing.SystemColors.Window; + this.lMax.Name = "lMax"; + // + // label6 + // + resources.ApplyResources(this.label6, "label6"); + this.label6.CausesValidation = false; + this.label6.Name = "label6"; + // + // lState + // + resources.ApplyResources(this.lState, "lState"); + this.lState.BackColor = System.Drawing.SystemColors.Window; + this.lState.Name = "lState"; + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + this.label4.UseMnemonic = false; + // + // DeployedServicePanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.SystemColors.Window; + this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.Controls.Add(this.label4); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "DeployedServicePanel"; + this.VisibleChanged += new System.EventHandler(this.DeployedServicePanel_VisibleChanged); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label groupLabel; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Label cacheLabel; + private System.Windows.Forms.Label cacheL2Label; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label lName; + private System.Windows.Forms.Label lComments; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label lBaseService; + private System.Windows.Forms.Label lOsManager; + private System.Windows.Forms.Label lInitial; + private System.Windows.Forms.Label lCache; + private System.Windows.Forms.Label lL2Cache; + private System.Windows.Forms.Label lState; + private System.Windows.Forms.Label lMax; + private System.Windows.Forms.Label label4; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.cs b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.cs new file mode 100644 index 000000000..b05ee18c2 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.cs @@ -0,0 +1,72 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class DeployedServicePanel : UserControl + { + private string _dsId; + public DeployedServicePanel(xmlrpc.DeployedService ds) + { + _dsId = ds.id; + InitializeComponent(); + + updateData(); + } + + private void DeployedServicePanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + updateData(); + } + + private void updateData() + { + xmlrpc.DeployedService ds = xmlrpc.UdsAdminService.GetDeployedService(_dsId); + lName.Text = ds.name; + lComments.Text = ds.comments; + lInitial.Text = ds.initialServices.ToString(); + lCache.Text = ds.cacheL1.ToString(); + lL2Cache.Text = ds.cacheL2.ToString(); + lMax.Text = ds.maxServices.ToString(); + lState.Text = ds.state; + lBaseService.Text = ds.serviceName; + lOsManager.Text = ds.osManagerName; + } + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.de.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.de.resx new file mode 100644 index 000000000..c74b70016 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.de.resx @@ -0,0 +1,783 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + True + + + + 3, 6 + + + + 3, 6, 3, 0 + + + 35, 13 + + + 2 + + + Name + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 27 + + + 3, 6, 3, 0 + + + 56, 13 + + + 4 + + + Kommentare + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 48 + + + 3, 6, 3, 0 + + + 68, 13 + + + 3 + + + Basisdienst + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + True + + + 3, 69 + + + 3, 6, 3, 0 + + + 70, 13 + + + 6 + + + Authentifikator + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + True + + + 3, 90 + + + 3, 6, 3, 0 + + + 65, 13 + + + 5 + + + OS-Manager + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + Left + + + True + + + NoControl + + + 3, 109 + + + 126, 13 + + + 12 + + + Erste Services Availables + + + label10 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Left + + + True + + + NoControl + + + 3, 130 + + + 131, 13 + + + 13 + + + Dienstleistungen im Cache zu behalten + + + cacheLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + Left + + + True + + + NoControl + + + 3, 151 + + + 146, 13 + + + 14 + + + Dienstleistungen im L2-Cache zu behalten + + + cacheL2Label + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + Left + + + True + + + NoControl + + + 3, 172 + + + 143, 13 + + + 15 + + + Maximale Anzahl von Dienstleistungen + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Left + + + NoControl + + + 184, 3 + + + 200, 15 + + + 16 + + + + + + lName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Left + + + NoControl + + + 184, 24 + + + 200, 15 + + + 18 + + + + + + lComments + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + Left + + + True + + + NoControl + + + 3, 195 + + + 32, 13 + + + 17 + + + Staat + + + label6 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + Left + + + NoControl + + + 184, 45 + + + 200, 15 + + + 19 + + + + + + lBaseService + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + Left + + + NoControl + + + 184, 66 + + + 200, 15 + + + 20 + + + + + + lAuthenticator + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + Left + + + NoControl + + + 184, 87 + + + 200, 15 + + + 21 + + + + + + lOsManager + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + Left + + + NoControl + + + 184, 108 + + + 200, 15 + + + 22 + + + + + + lInitial + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 15 + + + Left + + + NoControl + + + 184, 129 + + + 200, 15 + + + 23 + + + + + + lCache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 16 + + + Left + + + NoControl + + + 184, 150 + + + 200, 15 + + + 24 + + + + + + lL2Cache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 17 + + + Left + + + NoControl + + + 184, 194 + + + 200, 15 + + + 25 + + + + + + lState + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 18 + + + Left + + + NoControl + + + 184, 171 + + + 200, 15 + + + 26 + + + + + + lMax + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 19 + + + 4, 4 + + + 10 + + + 394, 215 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label4" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label3" Row="4" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label10" Row="5" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheLabel" Row="6" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheL2Label" Row="7" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label7" Row="8" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lComments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label6" Row="9" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lBaseService" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lAuthenticator" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lOsManager" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lInitial" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lCache" Row="6" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lL2Cache" Row="7" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lState" Row="9" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lMax" Row="8" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,45,961,Percent,54,039" /><Rows Styles="Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 401, 275 + + + DeployedServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.es.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.es.resx new file mode 100644 index 000000000..2ac131a87 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.es.resx @@ -0,0 +1,783 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + True + + + + 3, 6 + + + + 3, 6, 3, 0 + + + 35, 13 + + + 2 + + + Nombre + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 27 + + + 3, 6, 3, 0 + + + 56, 13 + + + 4 + + + Comentarios + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 48 + + + 3, 6, 3, 0 + + + 68, 13 + + + 3 + + + Servicio base + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + True + + + 3, 69 + + + 3, 6, 3, 0 + + + 70, 13 + + + 6 + + + Autenticador + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + True + + + 3, 90 + + + 3, 6, 3, 0 + + + 65, 13 + + + 5 + + + OS Manager + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + Left + + + True + + + NoControl + + + 3, 109 + + + 126, 13 + + + 12 + + + Servicios iniciales disponibles + + + label10 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Left + + + True + + + NoControl + + + 3, 130 + + + 131, 13 + + + 13 + + + Servicios para mantener en la memoria caché + + + cacheLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + Left + + + True + + + NoControl + + + 3, 151 + + + 146, 13 + + + 14 + + + Servicios para mantener en la memoria caché L2 + + + cacheL2Label + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + Left + + + True + + + NoControl + + + 3, 172 + + + 143, 13 + + + 15 + + + Número máximo de servicios + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Left + + + NoControl + + + 184, 3 + + + 200, 15 + + + 16 + + + + + + lName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Left + + + NoControl + + + 184, 24 + + + 200, 15 + + + 18 + + + + + + lComments + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + Left + + + True + + + NoControl + + + 3, 195 + + + 32, 13 + + + 17 + + + Estado + + + label6 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + Left + + + NoControl + + + 184, 45 + + + 200, 15 + + + 19 + + + + + + lBaseService + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + Left + + + NoControl + + + 184, 66 + + + 200, 15 + + + 20 + + + + + + lAuthenticator + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + Left + + + NoControl + + + 184, 87 + + + 200, 15 + + + 21 + + + + + + lOsManager + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + Left + + + NoControl + + + 184, 108 + + + 200, 15 + + + 22 + + + + + + lInitial + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 15 + + + Left + + + NoControl + + + 184, 129 + + + 200, 15 + + + 23 + + + + + + lCache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 16 + + + Left + + + NoControl + + + 184, 150 + + + 200, 15 + + + 24 + + + + + + lL2Cache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 17 + + + Left + + + NoControl + + + 184, 194 + + + 200, 15 + + + 25 + + + + + + lState + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 18 + + + Left + + + NoControl + + + 184, 171 + + + 200, 15 + + + 26 + + + + + + lMax + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 19 + + + 4, 4 + + + 10 + + + 394, 215 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label4" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label3" Row="4" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label10" Row="5" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheLabel" Row="6" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheL2Label" Row="7" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label7" Row="8" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lComments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label6" Row="9" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lBaseService" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lAuthenticator" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lOsManager" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lInitial" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lCache" Row="6" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lL2Cache" Row="7" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lState" Row="9" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lMax" Row="8" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,45,961,Percent,54,039" /><Rows Styles="Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 401, 275 + + + DeployedServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.fr.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.fr.resx new file mode 100644 index 000000000..69bc34392 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.fr.resx @@ -0,0 +1,783 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + True + + + + 3, 6 + + + + 3, 6, 3, 0 + + + 35, 13 + + + 2 + + + Nom + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 27 + + + 3, 6, 3, 0 + + + 56, 13 + + + 4 + + + Commentaires + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 48 + + + 3, 6, 3, 0 + + + 68, 13 + + + 3 + + + Service de base + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + True + + + 3, 69 + + + 3, 6, 3, 0 + + + 70, 13 + + + 6 + + + Authentificateur + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + True + + + 3, 90 + + + 3, 6, 3, 0 + + + 65, 13 + + + 5 + + + OS Manager + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + Left + + + True + + + NoControl + + + 3, 109 + + + 126, 13 + + + 12 + + + Initial Services disponibles + + + label10 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Left + + + True + + + NoControl + + + 3, 130 + + + 131, 13 + + + 13 + + + Services de garder dans le cache + + + cacheLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + Left + + + True + + + NoControl + + + 3, 151 + + + 146, 13 + + + 14 + + + Services de garder en mémoire cache L2 + + + cacheL2Label + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + Left + + + True + + + NoControl + + + 3, 172 + + + 143, 13 + + + 15 + + + Nombre maximal de services + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Left + + + NoControl + + + 184, 3 + + + 200, 15 + + + 16 + + + + + + lName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Left + + + NoControl + + + 184, 24 + + + 200, 15 + + + 18 + + + + + + lComments + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + Left + + + True + + + NoControl + + + 3, 195 + + + 32, 13 + + + 17 + + + État + + + label6 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + Left + + + NoControl + + + 184, 45 + + + 200, 15 + + + 19 + + + + + + lBaseService + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + Left + + + NoControl + + + 184, 66 + + + 200, 15 + + + 20 + + + + + + lAuthenticator + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + Left + + + NoControl + + + 184, 87 + + + 200, 15 + + + 21 + + + + + + lOsManager + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + Left + + + NoControl + + + 184, 108 + + + 200, 15 + + + 22 + + + + + + lInitial + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 15 + + + Left + + + NoControl + + + 184, 129 + + + 200, 15 + + + 23 + + + + + + lCache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 16 + + + Left + + + NoControl + + + 184, 150 + + + 200, 15 + + + 24 + + + + + + lL2Cache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 17 + + + Left + + + NoControl + + + 184, 194 + + + 200, 15 + + + 25 + + + + + + lState + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 18 + + + Left + + + NoControl + + + 184, 171 + + + 200, 15 + + + 26 + + + + + + lMax + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 19 + + + 4, 4 + + + 10 + + + 394, 215 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label4" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label3" Row="4" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label10" Row="5" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheLabel" Row="6" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheL2Label" Row="7" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label7" Row="8" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lComments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label6" Row="9" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lBaseService" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lAuthenticator" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lOsManager" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lInitial" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lCache" Row="6" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lL2Cache" Row="7" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lState" Row="9" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lMax" Row="8" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,45,961,Percent,54,039" /><Rows Styles="Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 401, 275 + + + DeployedServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.resx new file mode 100644 index 000000000..ac4ed506f --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicePanel.resx @@ -0,0 +1,852 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + True + + + + Microsoft Sans Serif, 8.25pt + + + 3, 6 + + + + 3, 6, 3, 0 + + + 35, 13 + + + 2 + + + Name + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + Microsoft Sans Serif, 8.25pt + + + 3, 27 + + + 3, 6, 3, 0 + + + 56, 13 + + + 4 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + Microsoft Sans Serif, 8.25pt + + + 3, 48 + + + 3, 6, 3, 0 + + + 68, 13 + + + 3 + + + Base service + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 4 + + + 11, 13 + + + 16 + + + + + + lName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 25 + + + 11, 13 + + + 18 + + + + + + lComments + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 46 + + + 11, 13 + + + 19 + + + + + + lBaseService + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + True + + + Microsoft Sans Serif, 8.25pt + + + 3, 69 + + + 3, 6, 3, 0 + + + 65, 13 + + + 5 + + + Os Manager + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 67 + + + 11, 13 + + + 21 + + + + + + lOsManager + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 88 + + + 11, 13 + + + 22 + + + + + + lInitial + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt + + + NoControl + + + 3, 112 + + + 3, 6, 3, 0 + + + 131, 13 + + + 13 + + + Services to keep in cache + + + cacheLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 109 + + + 11, 13 + + + 23 + + + + + + lCache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt + + + NoControl + + + 3, 133 + + + 3, 6, 3, 0 + + + 146, 13 + + + 14 + + + Services to keep in L2 cache + + + cacheL2Label + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 130 + + + 11, 13 + + + 24 + + + + + + lL2Cache + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 151 + + + 11, 13 + + + 26 + + + + + + lMax + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt + + + NoControl + + + 3, 175 + + + 3, 6, 3, 0 + + + 32, 13 + + + 17 + + + State + + + label6 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt, style=Bold + + + NoControl + + + 155, 172 + + + 11, 13 + + + 25 + + + + + + lState + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 15 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt + + + NoControl + + + 3, 91 + + + 3, 6, 3, 0 + + + 126, 13 + + + 12 + + + Initial Services Availables + + + label10 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 16 + + + Left + + + True + + + Microsoft Sans Serif, 8.25pt + + + NoControl + + + 3, 154 + + + 3, 6, 3, 0 + + + 143, 13 + + + 15 + + + Maximum number of services + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 17 + + + 17, 25 + + + 10 + + + 367, 215 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lComments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lBaseService" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label3" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lOsManager" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lInitial" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="cacheLabel" Row="5" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lCache" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="cacheL2Label" Row="6" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lL2Cache" Row="6" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="lMax" Row="7" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label6" Row="8" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="lState" Row="8" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label10" Row="4" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label7" Row="7" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,AutoSize,0" /><Rows Styles="Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10,Percent,10" /></TableLayoutSettings> + + + True + + + Microsoft Sans Serif, 8.25pt + + + 186, 6 + + + 3, 6, 3, 0 + + + 59, 13 + + + 1 + + + Information + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 442, 272 + + + DeployedServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.Designer.cs new file mode 100644 index 000000000..61f3a41d0 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.Designer.cs @@ -0,0 +1,94 @@ +namespace UdsAdmin.controls.panel +{ + partial class DeployedServicesPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployedServicesPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.kind = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.state = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.kind, + this.state, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // kind + // + resources.ApplyResources(this.kind, "kind"); + // + // state + // + resources.ApplyResources(this.state, "state"); + this.state.Width = global::UdsAdmin.Properties.Settings.Default.wStateCol; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // DeployedServicesPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "DeployedServicesPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader state; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader kind; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.cs b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.cs new file mode 100644 index 000000000..b39f46f3b --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.cs @@ -0,0 +1,107 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class DeployedServicesPanel : UserControl + { + gui.ListViewSorter _listSorter; + + public DeployedServicesPanel() + { + InitializeComponent(); + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.DeployedService[] dps = xmlrpc.UdsAdminService.GetDeployedServices(true); + List lst = new List(); + foreach (xmlrpc.DeployedService ser in dps) + { + ListViewItem itm = new ListViewItem(new string[]{ser.name, ser.info.typeName, xmlrpc.Util.GetStringFromState(ser.state), ser.comments}); + itm.ForeColor = gui.Colors.getColorForState(ser.state); + itm.Tag = ser.id; + lst.Add(itm); + } + listView.BeginUpdate(); + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + listView.EndUpdate(); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.de.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.de.resx new file mode 100644 index 000000000..97ff12aab --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.de.resx @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Typ + + + + 104 + + + Staat + + + Kommentare + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + kind + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.es.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.es.resx new file mode 100644 index 000000000..845662c71 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.es.resx @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Tipo + + + + 104 + + + Estado + + + Comentarios + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + kind + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.fr.resx new file mode 100644 index 000000000..4ed10e9fd --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.fr.resx @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Type + + + + 104 + + + État + + + Commentaires + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + kind + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.resx b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.resx new file mode 100644 index 000000000..dbff7c4c1 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/DeployedServicesPanel.resx @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Type + + + + 137 + + + State + + + Comments + + + + Fill + + + False + + + + 0, 0 + + + 660, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 660, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + kind + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployedServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/GroupsPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/GroupsPanel.Designer.cs new file mode 100644 index 000000000..58814c5d0 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/GroupsPanel.Designer.cs @@ -0,0 +1,88 @@ +namespace UdsAdmin.controls.panel +{ + partial class GroupsPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GroupsPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.state = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.state, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + this.listView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView_MouseUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // state + // + resources.ApplyResources(this.state, "state"); + this.state.Width = global::UdsAdmin.Properties.Settings.Default.wStateCol; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // GroupsPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "GroupsPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader state; + private System.Windows.Forms.ColumnHeader comments; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/GroupsPanel.cs b/client/administration/UdsAdmin/controls/panel/GroupsPanel.cs new file mode 100644 index 000000000..04b23839b --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/GroupsPanel.cs @@ -0,0 +1,189 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class GroupsPanel : UserControl + { + private xmlrpc.Authenticator _auth; + private xmlrpc.AuthenticatorType _authType; + ContextMenuStrip _emptyMenu; + ContextMenuStrip _fullMenu; + gui.ListViewSorter _listSorter; + + + public GroupsPanel(xmlrpc.Authenticator auth, xmlrpc.AuthenticatorType authType) + { + _auth = auth; + _authType = authType; + InitializeComponent(); + + _emptyMenu = new ContextMenuStrip(); + _fullMenu = new ContextMenuStrip(); + + ToolStripMenuItem enable = new ToolStripMenuItem(Strings.enable); enable.Click += enableItem; enable.Image = Images.apply16; + ToolStripMenuItem disable = new ToolStripMenuItem(Strings.disable); disable.Click += disableItem; disable.Image = Images.cancel16; + ToolStripSeparator sep = new ToolStripSeparator(); + ToolStripMenuItem newG1 = new ToolStripMenuItem(Strings.newItem); newG1.Click += newItem; newG1.Image = Images.new16; + ToolStripMenuItem newG2 = new ToolStripMenuItem(Strings.newItem); newG2.Click += newItem; newG2.Image = Images.new16; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Click += deleteItem; delete.Image = Images.delete16; + + _emptyMenu.Items.Add(newG1); + _fullMenu.Items.AddRange(new ToolStripItem[] { enable, disable, sep, newG2, delete }); + + listView.Columns[0].Text = _authType.groupNameLabel; + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.Group[] grps = xmlrpc.UdsAdminService.GetGroups(_auth.id); + List lst = new List(); + foreach (xmlrpc.Group grp in grps) + { + ListViewItem itm = new ListViewItem(new string[]{grp.name, grp.active ? Strings.active : Strings.inactive , grp.comments}); + itm.ForeColor = grp.active ? gui.Colors.ActiveColor : gui.Colors.InactiveColor; + itm.Tag = grp.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void newItem(object sender, EventArgs e) + { + UdsAdmin.forms.GroupForm form = new UdsAdmin.forms.GroupForm(_auth, _authType); + if (form.ShowDialog() == DialogResult.OK) + { + updateList(); + } + } + + private void setSelectedStates(bool newState) + { + if( listView.SelectedItems.Count == 0 ) + return; + string info = newState == true ? Strings.active : Strings.inactive; + Color col = newState == true ? gui.Colors.ActiveColor : gui.Colors.InactiveColor; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + i.SubItems[1].Text = info; + i.ForeColor = col; + } + xmlrpc.UdsAdminService.ChangeGroupsState(ids, newState); + + } + + private void enableItem(object sender, EventArgs e) + { + setSelectedStates(true); + } + + private void disableItem(object sender, EventArgs e) + { + setSelectedStates(false); + } + + private void deleteItem(object sender, EventArgs e) + { + if (listView.SelectedItems.Count == 0) + return; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + listView.Items.Remove(i); + } + xmlrpc.UdsAdminService.RemoveGroups(ids); + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + private void listView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + if (listView.SelectedItems.Count == 0) + _emptyMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + else + _fullMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/GroupsPanel.de.resx b/client/administration/UdsAdmin/controls/panel/GroupsPanel.de.resx new file mode 100644 index 000000000..2e1d51f55 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/GroupsPanel.de.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Staat + + + Kommentare + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + GroupsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/GroupsPanel.es.resx b/client/administration/UdsAdmin/controls/panel/GroupsPanel.es.resx new file mode 100644 index 000000000..76de946e9 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/GroupsPanel.es.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Estado + + + Comentarios + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + GroupsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/GroupsPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/GroupsPanel.fr.resx new file mode 100644 index 000000000..b8d28e224 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/GroupsPanel.fr.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + État + + + Commentaires + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + GroupsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/GroupsPanel.resx b/client/administration/UdsAdmin/controls/panel/GroupsPanel.resx new file mode 100644 index 000000000..1b31cfb1f --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/GroupsPanel.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + State + + + Comments + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + GroupsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/NetworksPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/NetworksPanel.Designer.cs new file mode 100644 index 000000000..6bf3d7c64 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/NetworksPanel.Designer.cs @@ -0,0 +1,88 @@ +namespace UdsAdmin.controls.panel +{ + partial class NetworksPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(NetworksPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.rangeStart = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.rangeEnd = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.rangeStart, + this.rangeEnd}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + this.listView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView_MouseUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // rangeStart + // + resources.ApplyResources(this.rangeStart, "rangeStart"); + this.rangeStart.Width = global::UdsAdmin.Properties.Settings.Default.wStateCol; + // + // rangeEnd + // + resources.ApplyResources(this.rangeEnd, "rangeEnd"); + this.rangeEnd.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // NetworksPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "NetworksPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader rangeStart; + private System.Windows.Forms.ColumnHeader rangeEnd; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/NetworksPanel.cs b/client/administration/UdsAdmin/controls/panel/NetworksPanel.cs new file mode 100644 index 000000000..c2d3ac81a --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/NetworksPanel.cs @@ -0,0 +1,151 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class NetworksPanel : UserControl + { + ContextMenuStrip _emptyMenu; + ContextMenuStrip _fullMenu; + gui.ListViewSorter _listSorter; + + public NetworksPanel() + { + InitializeComponent(); + + _emptyMenu = new ContextMenuStrip(); + _fullMenu = new ContextMenuStrip(); + + ToolStripMenuItem newG1 = new ToolStripMenuItem(Strings.newItem); newG1.Click += newItem; newG1.Image = Images.new16; + ToolStripMenuItem newG2 = new ToolStripMenuItem(Strings.newItem); newG2.Click += newItem; newG2.Image = Images.new16; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Click += deleteItem; delete.Image = Images.delete16; + + _emptyMenu.Items.Add(newG1); + _fullMenu.Items.AddRange(new ToolStripItem[] { newG2, delete }); + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.Network[] nets = xmlrpc.UdsAdminService.GetNetworks(); + List lst = new List(); + foreach (xmlrpc.Network net in nets) + { + ListViewItem itm = new ListViewItem(new string[]{net.name, net.netStart, net.netEnd}); + itm.ForeColor = gui.Colors.ActiveColor; + itm.Tag = net; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void newItem(object sender, EventArgs e) + { + UdsAdmin.forms.NetworkForm form = new UdsAdmin.forms.NetworkForm(); + if (form.ShowDialog() == DialogResult.OK) + { + updateList(); + } + } + + private void deleteItem(object sender, EventArgs e) + { + if (listView.SelectedItems.Count == 0) + return; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = ((xmlrpc.Network)i.Tag).id; + listView.Items.Remove(i); + } + xmlrpc.UdsAdminService.RemoveNetworks(ids); + } + + private void listView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + if (listView.SelectedItems.Count == 0) + _emptyMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + else + _fullMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/NetworksPanel.de.resx b/client/administration/UdsAdmin/controls/panel/NetworksPanel.de.resx new file mode 100644 index 000000000..96f748d45 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/NetworksPanel.de.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Anfang des Bereichs + + + Bereichsende + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeStart + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeEnd + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + NetworksPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/NetworksPanel.es.resx b/client/administration/UdsAdmin/controls/panel/NetworksPanel.es.resx new file mode 100644 index 000000000..199942d42 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/NetworksPanel.es.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Intervalo inicio + + + Fin de intervalo + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeStart + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeEnd + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + NetworksPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/NetworksPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/NetworksPanel.fr.resx new file mode 100644 index 000000000..ea1114bc7 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/NetworksPanel.fr.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Début de la gamme + + + Fin de la gamme + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeStart + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeEnd + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + NetworksPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/NetworksPanel.resx b/client/administration/UdsAdmin/controls/panel/NetworksPanel.resx new file mode 100644 index 000000000..b4ea11a54 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/NetworksPanel.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Range Start + + + Range End + + + + Fill + + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeStart + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rangeEnd + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + NetworksPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/OsManagersPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.Designer.cs new file mode 100644 index 000000000..bcd1f17b4 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.Designer.cs @@ -0,0 +1,86 @@ +namespace UdsAdmin.controls.panel +{ + partial class OsManagersPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OsManagersPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.typeName, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // typeName + // + resources.ApplyResources(this.typeName, "typeName"); + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // OsManagersPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "OsManagersPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader typeName; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/OsManagersPanel.cs b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.cs new file mode 100644 index 000000000..037992394 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.cs @@ -0,0 +1,105 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class OsManagersPanel : UserControl + { + gui.ListViewSorter _listSorter; + + public OsManagersPanel() + { + InitializeComponent(); + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.OSManager[] osms = xmlrpc.UdsAdminService.GetOSManagers(); + List lst = new List(); + foreach (xmlrpc.OSManager osm in osms) + { + ListViewItem itm = new ListViewItem(new string[]{osm.name, osm.typeName, osm.comments}); + itm.ForeColor = gui.Colors.ActiveColor; + itm.Tag = osm.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/OsManagersPanel.de.resx b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.de.resx new file mode 100644 index 000000000..e725d0132 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.de.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Typ + + + + 157 + + + Kommentare + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OsManagersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/OsManagersPanel.es.resx b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.es.resx new file mode 100644 index 000000000..8f248852d --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.es.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Tipo + + + + 157 + + + Comentarios + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OsManagersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/OsManagersPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.fr.resx new file mode 100644 index 000000000..5e9902e88 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.fr.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Type + + + + 157 + + + Commentaires + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OsManagersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/OsManagersPanel.resx b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.resx new file mode 100644 index 000000000..1f301bbd8 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/OsManagersPanel.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Type + + + + 157 + + + Comments + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OsManagersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PanelEmpty.Designer.cs b/client/administration/UdsAdmin/controls/panel/PanelEmpty.Designer.cs new file mode 100644 index 000000000..49d2d1004 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PanelEmpty.Designer.cs @@ -0,0 +1,56 @@ +namespace UdsAdmin.controls.panel +{ + partial class PanelEmpty + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PanelEmpty)); + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // PanelEmpty + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.SystemColors.Window; + this.Controls.Add(this.label1); + this.Name = "PanelEmpty"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/PanelEmpty.cs b/client/administration/UdsAdmin/controls/panel/PanelEmpty.cs new file mode 100644 index 000000000..428c89f6e --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PanelEmpty.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class PanelEmpty : UserControl + { + public PanelEmpty(string labelText) + { + InitializeComponent(); + Text = labelText; + } + + public override string Text + { + set { label1.Text = value; } + get { return label1.Text; } + } + } +} diff --git a/client/administration/UdsAdmin/controls/panel/PanelEmpty.de.resx b/client/administration/UdsAdmin/controls/panel/PanelEmpty.de.resx new file mode 100644 index 000000000..de6d81017 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PanelEmpty.de.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + Top + + + + 0, 0 + + + 66, 13 + + + 0 + + + Leere Panel + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + PanelEmpty + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PanelEmpty.es.resx b/client/administration/UdsAdmin/controls/panel/PanelEmpty.es.resx new file mode 100644 index 000000000..a23fb7cbf --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PanelEmpty.es.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + Top + + + + 0, 0 + + + 66, 13 + + + 0 + + + Panel vacío + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + PanelEmpty + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PanelEmpty.fr.resx b/client/administration/UdsAdmin/controls/panel/PanelEmpty.fr.resx new file mode 100644 index 000000000..184b2e2ca --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PanelEmpty.fr.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + Top + + + + 0, 0 + + + 66, 13 + + + 0 + + + Panneau vide + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + PanelEmpty + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PanelEmpty.resx b/client/administration/UdsAdmin/controls/panel/PanelEmpty.resx new file mode 100644 index 000000000..122b66449 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PanelEmpty.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + Top + + + + 0, 0 + + + 66, 13 + + + 0 + + + Empty Panel + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + PanelEmpty + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PublicationsPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.Designer.cs new file mode 100644 index 000000000..7b1aa0165 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.Designer.cs @@ -0,0 +1,94 @@ +namespace UdsAdmin.controls.panel +{ + partial class PublicationsPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PublicationsPanel)); + this.CreationDate = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.State = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.listView = new System.Windows.Forms.ListView(); + this.Revision = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.Reason = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // CreationDate + // + resources.ApplyResources(this.CreationDate, "CreationDate"); + // + // State + // + resources.ApplyResources(this.State, "State"); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.CausesValidation = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.Revision, + this.CreationDate, + this.State, + this.Reason}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.MultiSelect = false; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + this.listView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView_MouseUp); + // + // Revision + // + resources.ApplyResources(this.Revision, "Revision"); + // + // Reason + // + resources.ApplyResources(this.Reason, "Reason"); + // + // PublicationsPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "PublicationsPanel"; + this.VisibleChanged += new System.EventHandler(this.DeployedPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader CreationDate; + private System.Windows.Forms.ColumnHeader State; + private System.Windows.Forms.ColumnHeader Reason; + private System.Windows.Forms.ColumnHeader Revision; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/PublicationsPanel.cs b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.cs new file mode 100644 index 000000000..07db674a1 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.cs @@ -0,0 +1,154 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class PublicationsPanel : UserControl + { + private xmlrpc.DeployedService _parent; + ContextMenuStrip _fullMenu; + gui.ListViewSorter _listSorter; + + public PublicationsPanel(xmlrpc.DeployedService parent) + { + _parent = parent; + _fullMenu = new ContextMenuStrip(); + InitializeComponent(); + + ToolStripMenuItem cancel = new ToolStripMenuItem(Strings.cancel); cancel.Click += cancelPublication; cancel.Image = Images.cancel16; + + _fullMenu.Items.AddRange(new ToolStripItem[] { cancel }); + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView, new int[] {1}); + + updateList(); + } + + private void DeployedPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + xmlrpc.DeployedServicePublication[] pubs = UdsAdmin.xmlrpc.UdsAdminService.getPublications(_parent); + List lst = new List(); + foreach (xmlrpc.DeployedServicePublication pub in pubs) + { + ListViewItem itm = new ListViewItem(new string[] { pub.revision, pub.publishDate.ToString(), xmlrpc.Util.GetStringFromState(pub.state), pub.reason }); + itm.Tag = pub.id; + switch (pub.state) + { + case xmlrpc.Constants.STATE_USABLE: + itm.ForeColor = gui.Colors.ActiveColor; + break; + case xmlrpc.Constants.STATE_ERROR: + itm.ForeColor = gui.Colors.ErrorColor; + break; + case xmlrpc.Constants.STATE_PREPARING: + itm.ForeColor = gui.Colors.RunningColor; + break; + default: + itm.ForeColor = gui.Colors.InactiveColor; + break; + } + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + + private void cancelPublication(object sender, EventArgs e) + { + if (listView.SelectedItems.Count == 0) + return; + if (listView.SelectedItems.Count > 1) + { + MessageBox.Show(Strings.selectOnlyOne, Strings.error, MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + ListViewItem itm = listView.SelectedItems[0]; + string id = (string)itm.Tag; + try + { + xmlrpc.UdsAdminService.CancelPublication(id); + updateList(); + + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + private void listView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + if (listView.SelectedItems.Count == 0) + return; + else + _fullMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/PublicationsPanel.de.resx b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.de.resx new file mode 100644 index 000000000..1678985bc --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.de.resx @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Erstellungsdatum + + + + 144 + + + Staat + + + + Center + + + 71 + + + Überarbeitung + + + Grund + + + 339 + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Reason + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PublicationsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PublicationsPanel.es.resx b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.es.resx new file mode 100644 index 000000000..d005895b1 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.es.resx @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fecha de creación + + + + 144 + + + Estado + + + + Center + + + 71 + + + Revisión + + + Razón + + + 339 + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Reason + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PublicationsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PublicationsPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.fr.resx new file mode 100644 index 000000000..fedd8f6b1 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.fr.resx @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Date de création + + + + 144 + + + État + + + + Center + + + 71 + + + Révision + + + Raison + + + 339 + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Reason + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PublicationsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/PublicationsPanel.resx b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.resx new file mode 100644 index 000000000..1b3102e9d --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/PublicationsPanel.resx @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Creation Date + + + + 144 + + + State + + + + Center + + + 71 + + + Revision + + + Reason + + + 339 + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + CreationDate + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + State + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Revision + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Reason + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PublicationsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicePanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/ServicePanel.Designer.cs new file mode 100644 index 000000000..3ced594cf --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicePanel.Designer.cs @@ -0,0 +1,86 @@ +namespace UdsAdmin.controls.panel +{ + partial class ServicePanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ServicePanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.typeName, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // typeName + // + resources.ApplyResources(this.typeName, "typeName"); + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // ServicePanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "ServicePanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader typeName; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/ServicePanel.cs b/client/administration/UdsAdmin/controls/panel/ServicePanel.cs new file mode 100644 index 000000000..8143309d3 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicePanel.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class ServicePanel : UserControl + { + private string _idParent; + gui.ListViewSorter _listSorter; + + public ServicePanel(xmlrpc.Service parent) + { + InitializeComponent(); + + _idParent = parent.id; + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.Service[] servs = xmlrpc.UdsAdminService.GetServices(_idParent); + List lst = new List(); + foreach (xmlrpc.Service serv in servs) + { + ListViewItem itm = new ListViewItem(new string[] { serv.name, serv.typeName, serv.comments }); + itm.ForeColor = gui.Colors.ActiveColor; + itm.Tag = serv.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/ServicePanel.de.resx b/client/administration/UdsAdmin/controls/panel/ServicePanel.de.resx new file mode 100644 index 000000000..33b8d9a92 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicePanel.de.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Typ + + + + 135 + + + Kommentare + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicePanel.es.resx b/client/administration/UdsAdmin/controls/panel/ServicePanel.es.resx new file mode 100644 index 000000000..381b7ee0e --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicePanel.es.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Tipo + + + + 135 + + + Comentarios + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicePanel.fr.resx b/client/administration/UdsAdmin/controls/panel/ServicePanel.fr.resx new file mode 100644 index 000000000..736dc61ed --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicePanel.fr.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Type + + + + 135 + + + Commentaires + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicePanel.resx b/client/administration/UdsAdmin/controls/panel/ServicePanel.resx new file mode 100644 index 000000000..96a0eeb4e --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicePanel.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Type + + + + 135 + + + Comments + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicePanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.Designer.cs new file mode 100644 index 000000000..af4367235 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.Designer.cs @@ -0,0 +1,86 @@ +namespace UdsAdmin.controls.panel +{ + partial class ServiceProvidersPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ServiceProvidersPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.typeName, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // typeName + // + resources.ApplyResources(this.typeName, "typeName"); + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // ServiceProvidersPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "ServiceProvidersPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader typeName; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.cs b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.cs new file mode 100644 index 000000000..487cc0e6f --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.cs @@ -0,0 +1,105 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class ServiceProvidersPanel : UserControl + { + gui.ListViewSorter _listSorter; + + public ServiceProvidersPanel() + { + InitializeComponent(); + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.ServiceProvider[] sps = xmlrpc.UdsAdminService.GetServiceProviders(); + List lst = new List(); + foreach (xmlrpc.ServiceProvider sp in sps) + { + ListViewItem itm = new ListViewItem(new string[] { sp.name, sp.typeName, sp.comments }); + itm.ForeColor = gui.Colors.ActiveColor; + itm.Tag = sp.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.de.resx b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.de.resx new file mode 100644 index 000000000..4a7b5b4f1 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.de.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Typ + + + + 135 + + + Kommentare + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServiceProvidersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.es.resx b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.es.resx new file mode 100644 index 000000000..342209772 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.es.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Tipo + + + + 135 + + + Comentarios + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServiceProvidersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.fr.resx new file mode 100644 index 000000000..3d1386094 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.fr.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Type + + + + 135 + + + Commentaires + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServiceProvidersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.resx b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.resx new file mode 100644 index 000000000..7fa13f5dd --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServiceProvidersPanel.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Type + + + + 135 + + + Comments + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServiceProvidersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicesPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/ServicesPanel.Designer.cs new file mode 100644 index 000000000..63d42125a --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicesPanel.Designer.cs @@ -0,0 +1,86 @@ +namespace UdsAdmin.controls.panel +{ + partial class ServicesPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ServicesPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.typeName, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // typeName + // + resources.ApplyResources(this.typeName, "typeName"); + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // ServicesPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "ServicesPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader typeName; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/ServicesPanel.cs b/client/administration/UdsAdmin/controls/panel/ServicesPanel.cs new file mode 100644 index 000000000..bd2bdad5b --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicesPanel.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class ServicesPanel : UserControl + { + private string _idParent; + gui.ListViewSorter _listSorter; + + public ServicesPanel(xmlrpc.ServiceProvider parent) + { + InitializeComponent(); + + _idParent = parent.id; + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.Service[] servs = xmlrpc.UdsAdminService.GetServices(_idParent); + List lst = new List(); + foreach (xmlrpc.Service serv in servs) + { + ListViewItem itm = new ListViewItem(new string[] { serv.name, serv.typeName, serv.comments }); + itm.ForeColor = gui.Colors.ActiveColor; + itm.Tag = serv.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/ServicesPanel.de.resx b/client/administration/UdsAdmin/controls/panel/ServicesPanel.de.resx new file mode 100644 index 000000000..b7984c339 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicesPanel.de.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Typ + + + + 135 + + + Kommentare + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicesPanel.es.resx b/client/administration/UdsAdmin/controls/panel/ServicesPanel.es.resx new file mode 100644 index 000000000..f334a740a --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicesPanel.es.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Tipo + + + + 135 + + + Comentarios + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicesPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/ServicesPanel.fr.resx new file mode 100644 index 000000000..991f8a513 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicesPanel.fr.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Type + + + + 135 + + + Commentaires + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/ServicesPanel.resx b/client/administration/UdsAdmin/controls/panel/ServicesPanel.resx new file mode 100644 index 000000000..9e0062e18 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/ServicesPanel.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Type + + + + 135 + + + Comments + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ServicesPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/TransportsPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/TransportsPanel.Designer.cs new file mode 100644 index 000000000..3249b698f --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/TransportsPanel.Designer.cs @@ -0,0 +1,94 @@ +namespace UdsAdmin.controls.panel +{ + partial class TransportsPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TransportsPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.priority = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.AutoArrange = false; + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.name, + this.typeName, + this.comments, + this.priority}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + this.listView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView_MouseUp); + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // typeName + // + resources.ApplyResources(this.typeName, "typeName"); + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Width = global::UdsAdmin.Properties.Settings.Default.wCommentsCol; + // + // priority + // + resources.ApplyResources(this.priority, "priority"); + // + // TransportsPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "TransportsPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader comments; + private System.Windows.Forms.ColumnHeader typeName; + private System.Windows.Forms.ColumnHeader priority; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/TransportsPanel.cs b/client/administration/UdsAdmin/controls/panel/TransportsPanel.cs new file mode 100644 index 000000000..002b1127d --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/TransportsPanel.cs @@ -0,0 +1,156 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class TransportsPanel : UserControl + { + gui.ListViewSorter _listSorter; + xmlrpc.DeployedService _ds; + ContextMenuStrip _emptyMenu; + ContextMenuStrip _fullMenu; + + public TransportsPanel(xmlrpc.DeployedService ds = null) + { + InitializeComponent(); + + _emptyMenu = new ContextMenuStrip(); + _fullMenu = new ContextMenuStrip(); + + ToolStripMenuItem addTransport = new ToolStripMenuItem(Strings.newItem); addTransport.Click += addTransportItem; addTransport.Image = Images.new16; + ToolStripMenuItem addTransport2 = new ToolStripMenuItem(Strings.newItem); addTransport2.Click += addTransportItem; addTransport2.Image = Images.new16; + ToolStripMenuItem delTransport = new ToolStripMenuItem(Strings.deleteItem); delTransport.Click += delTransportItem; delTransport.Image = Images.delete16; + + _fullMenu.Items.AddRange( new ToolStripMenuItem[] { addTransport, delTransport }); + _emptyMenu.Items.Add(addTransport2); + + _ds = ds; + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + private void updateList() + { + try + { + xmlrpc.Transport[] trans; + if (_ds == null) + trans = xmlrpc.UdsAdminService.GetTransports(); + else + trans = xmlrpc.UdsAdminService.GetTransportsAssignedToDeployedService(_ds.id); + + List lst = new List(); + foreach (xmlrpc.Transport tran in trans) + { + ListViewItem itm = new ListViewItem(new string[]{tran.name, tran.typeName, tran.comments, tran.priority}); + itm.ForeColor = gui.Colors.ActiveColor; + itm.Tag = tran.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + private void addTransportItem(object sender, EventArgs e) + { + UdsAdmin.forms.DeployedTransportForm form = new UdsAdmin.forms.DeployedTransportForm(_ds); + if (form.ShowDialog() == DialogResult.OK) + updateList(); + } + + private void delTransportItem(object sender, EventArgs e) + { + if (listView.SelectedItems.Count == 0) + return; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + listView.Items.Remove(i); + } + xmlrpc.UdsAdminService.RemoveTransportFromDeployedService(_ds.id, ids); + } + + private void listView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Right && _ds != null ) + { + if (listView.SelectedItems.Count == 0) + _emptyMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + else + _fullMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/TransportsPanel.de.resx b/client/administration/UdsAdmin/controls/panel/TransportsPanel.de.resx new file mode 100644 index 000000000..6ba232023 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/TransportsPanel.de.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Typ + + + Kommentare + + + Name + + + Priorität + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/TransportsPanel.es.resx b/client/administration/UdsAdmin/controls/panel/TransportsPanel.es.resx new file mode 100644 index 000000000..0c6f80875 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/TransportsPanel.es.resx @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre + + + Tipo + + + + 141 + + + Comentarios + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + TransportsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Prioridad + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/TransportsPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/TransportsPanel.fr.resx new file mode 100644 index 000000000..820a47a92 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/TransportsPanel.fr.resx @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom + + + Type + + + + 141 + + + Commentaires + + + + Fill + + + False + + + + 0, 0 + + + 583, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 583, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + TransportsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Priorité + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/TransportsPanel.resx b/client/administration/UdsAdmin/controls/panel/TransportsPanel.resx new file mode 100644 index 000000000..39d9627f6 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/TransportsPanel.resx @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Type + + + + 141 + + + Comments + + + Priority + + + 120 + + + + Fill + + + False + + + + 0, 0 + + + 675, 279 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 675, 279 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + typeName + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + priority + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + TransportsPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/UsersPanel.Designer.cs b/client/administration/UdsAdmin/controls/panel/UsersPanel.Designer.cs new file mode 100644 index 000000000..801c98355 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/UsersPanel.Designer.cs @@ -0,0 +1,103 @@ +namespace UdsAdmin.controls.panel +{ + partial class UsersPanel + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de componentes + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UsersPanel)); + this.listView = new System.Windows.Forms.ListView(); + this.username = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.state = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.lastAccess = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.comments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // listView + // + this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.username, + this.name, + this.state, + this.lastAccess, + this.comments}); + resources.ApplyResources(this.listView, "listView"); + this.listView.FullRowSelect = true; + this.listView.GridLines = true; + this.listView.Name = "listView"; + this.listView.UseCompatibleStateImageBehavior = false; + this.listView.View = System.Windows.Forms.View.Details; + this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick); + this.listView.DoubleClick += new System.EventHandler(this.modifyItem); + this.listView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listView_KeyUp); + this.listView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView_MouseUp); + // + // username + // + resources.ApplyResources(this.username, "username"); + this.username.Width = global::UdsAdmin.Properties.Settings.Default.wUsernameCol; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Width = global::UdsAdmin.Properties.Settings.Default.wNameCol; + // + // state + // + resources.ApplyResources(this.state, "state"); + this.state.Width = global::UdsAdmin.Properties.Settings.Default.wStateCol; + // + // lastAccess + // + resources.ApplyResources(this.lastAccess, "lastAccess"); + this.lastAccess.Width = global::UdsAdmin.Properties.Settings.Default.wLastAccessCol; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + // + // UsersPanel + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.listView); + this.Name = "UsersPanel"; + this.VisibleChanged += new System.EventHandler(this.UsersPanel_VisibleChanged); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView; + private System.Windows.Forms.ColumnHeader username; + private System.Windows.Forms.ColumnHeader name; + private System.Windows.Forms.ColumnHeader state; + private System.Windows.Forms.ColumnHeader lastAccess; + private System.Windows.Forms.ColumnHeader comments; + } +} diff --git a/client/administration/UdsAdmin/controls/panel/UsersPanel.cs b/client/administration/UdsAdmin/controls/panel/UsersPanel.cs new file mode 100644 index 000000000..4a068e732 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/UsersPanel.cs @@ -0,0 +1,261 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.controls.panel +{ + public partial class UsersPanel : UserControl + { + private xmlrpc.Authenticator _auth; + private xmlrpc.AuthenticatorType _authType; + ContextMenuStrip _emptyMenu; + ContextMenuStrip _fullMenu; + gui.ListViewSorter _listSorter; + + public UsersPanel(xmlrpc.Authenticator auth, xmlrpc.AuthenticatorType authType) + { + _auth = auth; + _authType = authType; + InitializeComponent(); + + _emptyMenu = new ContextMenuStrip(); + _fullMenu = new ContextMenuStrip(); + + + ToolStripMenuItem newU1 = new ToolStripMenuItem(Strings.newItem); newU1.Click += newItem; newU1.Image = Images.new16; + + ToolStripMenuItem modify = new ToolStripMenuItem(Strings.modifyItem); modify.Click += modifyItem; + ToolStripMenuItem prefs = new ToolStripMenuItem(Strings.userPreferences); prefs.Click += preferences; + ToolStripMenuItem enable = new ToolStripMenuItem(Strings.enable); enable.Click += enableItem; enable.Image = Images.apply16; + ToolStripMenuItem disable = new ToolStripMenuItem(Strings.disable); disable.Click += disableItem; disable.Image = Images.cancel16; + ToolStripMenuItem newU2 = new ToolStripMenuItem(Strings.newItem); newU2.Click += newItem; newU2.Image = Images.new16; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Click += deleteItem; delete.Image = Images.delete16; + + _fullMenu.Items.AddRange(new ToolStripItem[] { modify, new ToolStripSeparator(), enable, disable, new ToolStripSeparator(), + prefs, new ToolStripSeparator()}); + + if (_authType.canCreateUsers == true) + { + _emptyMenu.Items.Add(newU1); + _fullMenu.Items.Add(newU2); + } + + _fullMenu.Items.Add(delete); + + listView.Columns[0].Text = _authType.userNameLabel; + + listView.ListViewItemSorter = _listSorter = new gui.ListViewSorter(listView, new int[]{3}); + updateList(); + } + + private void UsersPanel_VisibleChanged(object sender, EventArgs e) + { + if (Visible == true) + { + updateList(); + } + } + + + private string getStateString(string state) + { + string res; + switch (state) + { + case xmlrpc.Constants.STATE_ACTIVE: + res = Strings.active; + break; + case xmlrpc.Constants.STATE_INACTIVE: + res = Strings.inactive; + break; + default: + res = Strings.blocked; + break; + } + return res; + } + + private Color getStateColor(string state) + { + Color color; + switch (state) + { + case xmlrpc.Constants.STATE_ACTIVE: + color = gui.Colors.ActiveColor; + break; + case xmlrpc.Constants.STATE_INACTIVE: + color = gui.Colors.InactiveColor; + break; + default: + color = gui.Colors.BlockedColor; + break; + } + return color; + } + + private void updateList() + { + try + { + xmlrpc.User[] usrs = xmlrpc.UdsAdminService.GetUsers(_auth.id); + List lst = new List(); + foreach (xmlrpc.User usr in usrs) + { + + ListViewItem itm = new ListViewItem(new string[] { usr.name, usr.realName, getStateString(usr.state), usr.lastAccess.ToString(), usr.comments }); + itm.ForeColor = getStateColor(usr.state); + itm.Tag = usr.id; + lst.Add(itm); + } + listView.Items.Clear(); + listView.Items.AddRange(lst.ToArray()); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void newItem(object sender, EventArgs e) + { + if (_authType.canCreateUsers == false) + { + gui.UserNotifier.notifyError(Strings.cantCreateUsers); + return; + } + UdsAdmin.forms.UserForm form = new UdsAdmin.forms.UserForm(_auth, _authType, new xmlrpc.User()); + if (form.ShowDialog() == DialogResult.OK) + { + updateList(); + } + } + + private void modifyItem(object sender, EventArgs e) + { + xmlrpc.User usr = xmlrpc.UdsAdminService.GetUser((string)(listView.SelectedItems[0].Tag)); + UdsAdmin.forms.UserForm form = new UdsAdmin.forms.UserForm(_auth, _authType, usr); + if (form.ShowDialog() == DialogResult.OK ) + { + updateList(); + } + } + + private void preferences(object sender, EventArgs e) + { + xmlrpc.User usr = xmlrpc.UdsAdminService.GetUser((string)(listView.SelectedItems[0].Tag)); + UdsAdmin.forms.UserPreferencesForm form = new UdsAdmin.forms.UserPreferencesForm(usr.id); + form.ShowDialog(); + } + + private void setSelectedStates(string newState) + { + if( listView.SelectedItems.Count == 0 ) + return; + string info = getStateString(newState); + Color col = getStateColor(newState); + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + i.SubItems[2].Text = info; + i.ForeColor = col; + } + xmlrpc.UdsAdminService.ChangeUsersState(ids, newState); + + } + + private void enableItem(object sender, EventArgs e) + { + setSelectedStates(xmlrpc.Constants.STATE_ACTIVE); + } + + private void disableItem(object sender, EventArgs e) + { + setSelectedStates(xmlrpc.Constants.STATE_INACTIVE); + } + + private void deleteItem(object sender, EventArgs e) + { + if (listView.SelectedItems.Count == 0) + return; + string[] ids = new string[listView.SelectedItems.Count]; + int n = 0; + foreach (ListViewItem i in listView.SelectedItems) + { + ids[n++] = (string)i.Tag; + listView.Items.Remove(i); + } + xmlrpc.UdsAdminService.RemoveUsers(ids); + } + + private void listView_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + private void listView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + if (listView.SelectedItems.Count == 0) + { + if( _emptyMenu.Items.Count > 0 ) + _emptyMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + else + _fullMenu.Show(Control.MousePosition.X, Control.MousePosition.Y); + } + } + + private void listView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F5: + updateList(); + break; + case Keys.E: + if (e.Modifiers == Keys.Control) + foreach (ListViewItem i in listView.Items) + i.Selected = true; + break; + } + } + + } +} diff --git a/client/administration/UdsAdmin/controls/panel/UsersPanel.de.resx b/client/administration/UdsAdmin/controls/panel/UsersPanel.de.resx new file mode 100644 index 000000000..359fe9840 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/UsersPanel.de.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Benutzername + + + Name + + + Staat + + + Letzter Zugriff + + + Kommentare + + + + Fill + + + + False + + + + 0, 0 + + + 597, 278 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 597, 278 + + + username + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + lastAccess + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UsersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/UsersPanel.es.resx b/client/administration/UdsAdmin/controls/panel/UsersPanel.es.resx new file mode 100644 index 000000000..07c9d7bf5 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/UsersPanel.es.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nombre de usuario + + + Nombre + + + Estado + + + Último acceso + + + Comentarios + + + + Fill + + + + False + + + + 0, 0 + + + 597, 278 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 597, 278 + + + username + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + lastAccess + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UsersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/UsersPanel.fr.resx b/client/administration/UdsAdmin/controls/panel/UsersPanel.fr.resx new file mode 100644 index 000000000..5704a2b19 --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/UsersPanel.fr.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nom d'utilisateur + + + Nom + + + État + + + Dernier accès + + + Commentaires + + + + Fill + + + + False + + + + 0, 0 + + + 597, 278 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 597, 278 + + + username + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + lastAccess + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UsersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/controls/panel/UsersPanel.resx b/client/administration/UdsAdmin/controls/panel/UsersPanel.resx new file mode 100644 index 000000000..cc68258db --- /dev/null +++ b/client/administration/UdsAdmin/controls/panel/UsersPanel.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Username + + + Name + + + State + + + Last Access + + + Comments + + + + Fill + + + + False + + + + 0, 0 + + + 597, 278 + + + 0 + + + listView + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 597, 278 + + + username + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + state + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + lastAccess + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + comments + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UsersPanel + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/exceptions/AuthenticationException.cs b/client/administration/UdsAdmin/exceptions/AuthenticationException.cs new file mode 100644 index 000000000..d0b5d8d85 --- /dev/null +++ b/client/administration/UdsAdmin/exceptions/AuthenticationException.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UdsAdmin.exceptions +{ + [Serializable] + public class AuthenticationException : UdsException + { + public AuthenticationException(string msg) + : base(msg) + { + } + } +} diff --git a/client/administration/UdsAdmin/exceptions/CommunicationException.cs b/client/administration/UdsAdmin/exceptions/CommunicationException.cs new file mode 100644 index 000000000..15b25c894 --- /dev/null +++ b/client/administration/UdsAdmin/exceptions/CommunicationException.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UdsAdmin.exceptions +{ + [Serializable] + public class CommunicationException : UdsException + { + public CommunicationException(string msg) + : base(msg) + { + } + } +} diff --git a/client/administration/UdsAdmin/exceptions/NewVersionRequiredException.cs b/client/administration/UdsAdmin/exceptions/NewVersionRequiredException.cs new file mode 100644 index 000000000..4fcde3760 --- /dev/null +++ b/client/administration/UdsAdmin/exceptions/NewVersionRequiredException.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UdsAdmin.exceptions +{ + [Serializable] + class NewVersionRequiredException : UdsException + { + private string _url; + public NewVersionRequiredException(string msg, string url) + : base(msg) + { + _url = url; + } + + public string Url { + get { return _url; } + } + } +} diff --git a/client/administration/UdsAdmin/exceptions/UdsException.cs b/client/administration/UdsAdmin/exceptions/UdsException.cs new file mode 100644 index 000000000..2a5a2815c --- /dev/null +++ b/client/administration/UdsAdmin/exceptions/UdsException.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UdsAdmin.exceptions +{ + [Serializable] + public class UdsException : Exception + { + public UdsException(string msg) + : base(msg) + { + } + } +} diff --git a/client/administration/UdsAdmin/forms/AboutBoxForm.Designer.cs b/client/administration/UdsAdmin/forms/AboutBoxForm.Designer.cs new file mode 100644 index 000000000..64335df4f --- /dev/null +++ b/client/administration/UdsAdmin/forms/AboutBoxForm.Designer.cs @@ -0,0 +1,129 @@ +namespace UdsAdmin.forms +{ + partial class AboutBoxForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutBoxForm)); + this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.logoPictureBox = new System.Windows.Forms.PictureBox(); + this.labelProductName = new System.Windows.Forms.Label(); + this.labelVersion = new System.Windows.Forms.Label(); + this.labelCopyright = new System.Windows.Forms.Label(); + this.labelCompanyName = new System.Windows.Forms.Label(); + this.textBoxDescription = new System.Windows.Forms.TextBox(); + this.okButton = new System.Windows.Forms.Button(); + this.tableLayoutPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel + // + resources.ApplyResources(this.tableLayoutPanel, "tableLayoutPanel"); + this.tableLayoutPanel.Controls.Add(this.logoPictureBox, 0, 0); + this.tableLayoutPanel.Controls.Add(this.labelProductName, 1, 0); + this.tableLayoutPanel.Controls.Add(this.labelVersion, 1, 1); + this.tableLayoutPanel.Controls.Add(this.labelCopyright, 1, 2); + this.tableLayoutPanel.Controls.Add(this.labelCompanyName, 1, 3); + this.tableLayoutPanel.Controls.Add(this.textBoxDescription, 1, 4); + this.tableLayoutPanel.Controls.Add(this.okButton, 1, 5); + this.tableLayoutPanel.Name = "tableLayoutPanel"; + // + // logoPictureBox + // + resources.ApplyResources(this.logoPictureBox, "logoPictureBox"); + this.logoPictureBox.Name = "logoPictureBox"; + this.tableLayoutPanel.SetRowSpan(this.logoPictureBox, 6); + this.logoPictureBox.TabStop = false; + // + // labelProductName + // + resources.ApplyResources(this.labelProductName, "labelProductName"); + this.labelProductName.MaximumSize = new System.Drawing.Size(0, 17); + this.labelProductName.Name = "labelProductName"; + // + // labelVersion + // + resources.ApplyResources(this.labelVersion, "labelVersion"); + this.labelVersion.MaximumSize = new System.Drawing.Size(0, 17); + this.labelVersion.Name = "labelVersion"; + // + // labelCopyright + // + resources.ApplyResources(this.labelCopyright, "labelCopyright"); + this.labelCopyright.MaximumSize = new System.Drawing.Size(0, 17); + this.labelCopyright.Name = "labelCopyright"; + // + // labelCompanyName + // + resources.ApplyResources(this.labelCompanyName, "labelCompanyName"); + this.labelCompanyName.MaximumSize = new System.Drawing.Size(0, 17); + this.labelCompanyName.Name = "labelCompanyName"; + // + // textBoxDescription + // + resources.ApplyResources(this.textBoxDescription, "textBoxDescription"); + this.textBoxDescription.Name = "textBoxDescription"; + this.textBoxDescription.ReadOnly = true; + this.textBoxDescription.TabStop = false; + // + // okButton + // + resources.ApplyResources(this.okButton, "okButton"); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.okButton.Name = "okButton"; + // + // AboutBoxForm + // + this.AcceptButton = this.okButton; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutBoxForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.tableLayoutPanel.ResumeLayout(false); + this.tableLayoutPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; + private System.Windows.Forms.PictureBox logoPictureBox; + private System.Windows.Forms.Label labelProductName; + private System.Windows.Forms.Label labelVersion; + private System.Windows.Forms.Label labelCopyright; + private System.Windows.Forms.Label labelCompanyName; + private System.Windows.Forms.TextBox textBoxDescription; + private System.Windows.Forms.Button okButton; + } +} diff --git a/client/administration/UdsAdmin/forms/AboutBoxForm.cs b/client/administration/UdsAdmin/forms/AboutBoxForm.cs new file mode 100644 index 000000000..bf78e470e --- /dev/null +++ b/client/administration/UdsAdmin/forms/AboutBoxForm.cs @@ -0,0 +1,133 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + partial class AboutBoxForm : Form + { + public AboutBoxForm() + { + InitializeComponent(); + this.Text = String.Format("Acerca de {0}", AssemblyTitle); + this.labelProductName.Text = AssemblyProduct; + this.labelVersion.Text = String.Format("Versión {0}", AssemblyVersion); + this.labelCopyright.Text = AssemblyCopyright; + this.labelCompanyName.Text = AssemblyCompany; + this.textBoxDescription.Text = AssemblyDescription; + } + + #region Descriptores de acceso de atributos de ensamblado + + public string AssemblyTitle + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + if (attributes.Length > 0) + { + AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; + if (titleAttribute.Title != "") + { + return titleAttribute.Title; + } + } + return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); + } + } + + public string AssemblyVersion + { + get + { + return Assembly.GetExecutingAssembly().GetName().Version.ToString(); + } + } + + public string AssemblyDescription + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyDescriptionAttribute)attributes[0]).Description; + } + } + + public string AssemblyProduct + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyProductAttribute)attributes[0]).Product; + } + } + + public string AssemblyCopyright + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyCopyrightAttribute)attributes[0]).Copyright; + } + } + + public string AssemblyCompany + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyCompanyAttribute)attributes[0]).Company; + } + } + #endregion + } +} diff --git a/client/administration/UdsAdmin/forms/AboutBoxForm.de.resx b/client/administration/UdsAdmin/forms/AboutBoxForm.de.resx new file mode 100644 index 000000000..d0324fcbd --- /dev/null +++ b/client/administration/UdsAdmin/forms/AboutBoxForm.de.resx @@ -0,0 +1,890 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + + Fill + + + + + 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 + + + + 3, 3 + + + 131, 259 + + + StretchImage + + + 12 + + + logoPictureBox + + + System.Windows.Forms.PictureBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 0 + + + Fill + + + 143, 0 + + + 6, 0, 3, 0 + + + 271, 17 + + + 19 + + + Nombre de producto + + + MiddleLeft + + + labelProductName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 1 + + + Fill + + + 143, 26 + + + 6, 0, 3, 0 + + + 271, 17 + + + 0 + + + Versión + + + MiddleLeft + + + labelVersion + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 2 + + + Fill + + + 143, 52 + + + 6, 0, 3, 0 + + + 271, 17 + + + 21 + + + Urheberrecht + + + MiddleLeft + + + labelCopyright + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 3 + + + Fill + + + 143, 78 + + + 6, 0, 3, 0 + + + 271, 17 + + + 22 + + + Nombre De La compañía + + + MiddleLeft + + + labelCompanyName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 4 + + + Fill + + + 143, 107 + + + 6, 3, 3, 3 + + + True + + + Both + + + 271, 126 + + + 23 + + + Descripción + + + textBoxDescription + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 5 + + + Bottom, Right + + + 339, 239 + + + 75, 23 + + + 24 + + + & Akzeptieren + + + okButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 6 + + + Fill + + + 9, 9 + + + 6 + + + 417, 265 + + + 0 + + + tableLayoutPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="logoPictureBox" Row="0" RowSpan="6" Column="0" ColumnSpan="1" /><Control Name="labelProductName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelVersion" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelCopyright" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelCompanyName" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="textBoxDescription" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="okButton" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,33,Percent,67" /><Rows Styles="Percent,10,Percent,10,Percent,10,Percent,10,Percent,50,Percent,10" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 435, 283 + + + 9, 9, 9, 9 + + + CenterParent + + + AboutBox + + + AboutBoxForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AboutBoxForm.es.resx b/client/administration/UdsAdmin/forms/AboutBoxForm.es.resx new file mode 100644 index 000000000..f00774b21 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AboutBoxForm.es.resx @@ -0,0 +1,627 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + 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 + + + + Nombre del producto + + + English + + + Derechos de autor + + + Empresa + + + Descripción + + + & Aceptar + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AboutBoxForm.fr.resx b/client/administration/UdsAdmin/forms/AboutBoxForm.fr.resx new file mode 100644 index 000000000..6a2e7bf38 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AboutBoxForm.fr.resx @@ -0,0 +1,890 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + + Fill + + + + + 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 + + + + 3, 3 + + + 131, 259 + + + StretchImage + + + 12 + + + logoPictureBox + + + System.Windows.Forms.PictureBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 0 + + + Fill + + + 143, 0 + + + 6, 0, 3, 0 + + + 271, 17 + + + 19 + + + Nom du produit + + + MiddleLeft + + + labelProductName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 1 + + + Fill + + + 143, 26 + + + 6, 0, 3, 0 + + + 271, 17 + + + 0 + + + Versión + + + MiddleLeft + + + labelVersion + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 2 + + + Fill + + + 143, 52 + + + 6, 0, 3, 0 + + + 271, 17 + + + 21 + + + Droit d'auteur + + + MiddleLeft + + + labelCopyright + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 3 + + + Fill + + + 143, 78 + + + 6, 0, 3, 0 + + + 271, 17 + + + 22 + + + Entreprise + + + MiddleLeft + + + labelCompanyName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 4 + + + Fill + + + 143, 107 + + + 6, 3, 3, 3 + + + True + + + Both + + + 271, 126 + + + 23 + + + Description + + + textBoxDescription + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 5 + + + Bottom, Right + + + 339, 239 + + + 75, 23 + + + 24 + + + & Accepter + + + okButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 6 + + + Fill + + + 9, 9 + + + 6 + + + 417, 265 + + + 0 + + + tableLayoutPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="logoPictureBox" Row="0" RowSpan="6" Column="0" ColumnSpan="1" /><Control Name="labelProductName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelVersion" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelCopyright" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelCompanyName" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="textBoxDescription" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="okButton" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,33,Percent,67" /><Rows Styles="Percent,10,Percent,10,Percent,10,Percent,10,Percent,50,Percent,10" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 435, 283 + + + 9, 9, 9, 9 + + + CenterParent + + + AboutBox + + + AboutBoxForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AboutBoxForm.resx b/client/administration/UdsAdmin/forms/AboutBoxForm.resx new file mode 100644 index 000000000..3149d4615 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AboutBoxForm.resx @@ -0,0 +1,890 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + + Fill + + + + + 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 + + + + 3, 3 + + + 131, 259 + + + StretchImage + + + 12 + + + logoPictureBox + + + System.Windows.Forms.PictureBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 0 + + + Fill + + + 143, 0 + + + 6, 0, 3, 0 + + + 271, 17 + + + 19 + + + Name of product + + + MiddleLeft + + + labelProductName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 1 + + + Fill + + + 143, 26 + + + 6, 0, 3, 0 + + + 271, 17 + + + 0 + + + Versión + + + MiddleLeft + + + labelVersion + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 2 + + + Fill + + + 143, 52 + + + 6, 0, 3, 0 + + + 271, 17 + + + 21 + + + Copyright + + + MiddleLeft + + + labelCopyright + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 3 + + + Fill + + + 143, 78 + + + 6, 0, 3, 0 + + + 271, 17 + + + 22 + + + Company + + + MiddleLeft + + + labelCompanyName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 4 + + + Fill + + + 143, 107 + + + 6, 3, 3, 3 + + + True + + + Both + + + 271, 126 + + + 23 + + + Description + + + textBoxDescription + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 5 + + + Bottom, Right + + + 339, 239 + + + 75, 23 + + + 24 + + + &Accept + + + okButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel + + + 6 + + + Fill + + + 9, 9 + + + 6 + + + 417, 265 + + + 0 + + + tableLayoutPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="logoPictureBox" Row="0" RowSpan="6" Column="0" ColumnSpan="1" /><Control Name="labelProductName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelVersion" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelCopyright" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="labelCompanyName" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="textBoxDescription" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="okButton" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,33,Percent,67" /><Rows Styles="Percent,10,Percent,10,Percent,10,Percent,10,Percent,50,Percent,10" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 435, 283 + + + 9, 9, 9, 9 + + + CenterParent + + + AboutBox + + + AboutBoxForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AssignDeployed.Designer.cs b/client/administration/UdsAdmin/forms/AssignDeployed.Designer.cs new file mode 100644 index 000000000..e35c523d3 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AssignDeployed.Designer.cs @@ -0,0 +1,146 @@ +namespace UdsAdmin.forms +{ + partial class AssignDeployed + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AssignDeployed)); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.serviceCombo = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.userCombo = new System.Windows.Forms.ComboBox(); + this.userLabel = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.authCombo = new System.Windows.Forms.ComboBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.serviceCombo, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.userCombo, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.userLabel, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.authCombo, 1, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // serviceCombo + // + this.serviceCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.serviceCombo.FormattingEnabled = true; + resources.ApplyResources(this.serviceCombo, "serviceCombo"); + this.serviceCombo.Name = "serviceCombo"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // userCombo + // + this.userCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.userCombo.FormattingEnabled = true; + resources.ApplyResources(this.userCombo, "userCombo"); + this.userCombo.Name = "userCombo"; + // + // userLabel + // + resources.ApplyResources(this.userLabel, "userLabel"); + this.userLabel.Name = "userLabel"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // authCombo + // + this.authCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.authCombo.FormattingEnabled = true; + resources.ApplyResources(this.authCombo, "authCombo"); + this.authCombo.Name = "authCombo"; + this.authCombo.SelectedIndexChanged += new System.EventHandler(this.authCombo_SelectedIndexChanged); + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // AssignDeployed + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.tableLayoutPanel2); + this.Name = "AssignDeployed"; + this.Load += new System.EventHandler(this.AssignDeployed_Load); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.tableLayoutPanel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label userLabel; + private System.Windows.Forms.ComboBox userCombo; + private System.Windows.Forms.ComboBox serviceCombo; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox authCombo; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AssignDeployed.cs b/client/administration/UdsAdmin/forms/AssignDeployed.cs new file mode 100644 index 000000000..0e8ac6669 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AssignDeployed.cs @@ -0,0 +1,119 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class AssignDeployed : Form + { + private xmlrpc.DeployedService _parent; + + public AssignDeployed(xmlrpc.DeployedService parent) + { + _parent = parent; + InitializeComponent(); + Text = Strings.titleAssignService; + } + + private void AssignDeployed_Load(object sender, EventArgs e) + { + xmlrpc.AssignableDeployedService[] services = xmlrpc.UdsAdminService.GetAssignableDeployedServices(_parent.id); + if (services.Length == 0) + { + MessageBox.Show(Strings.error, Strings.services, MessageBoxButtons.OK, MessageBoxIcon.Error); + Close(); + return; + } + + xmlrpc.Authenticator[] auths = xmlrpc.UdsAdminService.GetAuthenticators(); + + if (auths.Length == 0) + { + MessageBox.Show(Strings.error, Strings.authenticators, MessageBoxButtons.OK, MessageBoxIcon.Error); + Close(); + return; + } + + foreach (xmlrpc.Authenticator a in auths) + { + authCombo.Items.Add(a); + } + authCombo.SelectedIndex = 0; + serviceCombo.Items.AddRange(services); + } + + private void accept_Click(object sender, EventArgs e) + { + if (userCombo.SelectedItem == null) + { + gui.UserNotifier.notifyError(Strings.userRequired); + return; + } + + if (serviceCombo.SelectedItem == null) + { + gui.UserNotifier.notifyError(Strings.serviceRequired); + return; + } + + try { + xmlrpc.UdsAdminService.AssignDeployedService(_parent.id, + ((xmlrpc.AssignableDeployedService)serviceCombo.SelectedItem).id, + ((xmlrpc.User)userCombo.SelectedItem).id); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void cancel_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + + private void authCombo_SelectedIndexChanged(object sender, EventArgs e) + { + userCombo.Items.Clear(); + xmlrpc.User[] users = xmlrpc.UdsAdminService.GetUsers(((xmlrpc.Authenticator)authCombo.Items[0]).id); + userCombo.Items.AddRange(users); + + } + + } +} diff --git a/client/administration/UdsAdmin/forms/AssignDeployed.de.resx b/client/administration/UdsAdmin/forms/AssignDeployed.de.resx new file mode 100644 index 000000000..3876ef5d8 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AssignDeployed.de.resx @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 75, 13 + + + Benutzername + + + Akzeptieren + + + Abbrechen + + + Assign Service + + + Service + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AssignDeployed.es.resx b/client/administration/UdsAdmin/forms/AssignDeployed.es.resx new file mode 100644 index 000000000..d0d085def --- /dev/null +++ b/client/administration/UdsAdmin/forms/AssignDeployed.es.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Assign Service + + + Servicio + + + Aceptar + + + Cancelar + + + Nombre de usuario + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AssignDeployed.fr.resx b/client/administration/UdsAdmin/forms/AssignDeployed.fr.resx new file mode 100644 index 000000000..712b8a84f --- /dev/null +++ b/client/administration/UdsAdmin/forms/AssignDeployed.fr.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Assign Service + + + Service + + + Accepter + + + Annuler + + + Nom d'utilisateur + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AssignDeployed.resx b/client/administration/UdsAdmin/forms/AssignDeployed.resx new file mode 100644 index 000000000..767656925 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AssignDeployed.resx @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + + 91, 59 + + + 240, 21 + + + 4 + + + serviceCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 62 + + + + 3, 6, 3, 0 + + + 43, 13 + + + 3 + + + Service + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 91, 31 + + + 240, 21 + + + 2 + + + userCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + True + + + 3, 34 + + + 3, 6, 3, 0 + + + 60, 13 + + + 1 + + + User Name + + + userLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 70, 13 + + + 1 + + + Authenticator + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 91, 3 + + + 240, 21 + + + 2 + + + authCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + 13, 7 + + + 3 + + + 334, 86 + + + 10 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="serviceCombo" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="userCombo" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="userLabel" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="authCombo" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,26,49842,Percent,73,50158" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 101, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 252, 7 + + + 102, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 117 + + + 1 + + + 357, 33 + + + 9 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 357, 150 + + + CenterParent + + + Assign Service + + + AssignDeployed + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AuthenticatorForm.Designer.cs b/client/administration/UdsAdmin/forms/AuthenticatorForm.Designer.cs new file mode 100644 index 000000000..a04619535 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AuthenticatorForm.Designer.cs @@ -0,0 +1,207 @@ +namespace UdsAdmin.forms +{ + partial class AuthenticatorForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AuthenticatorForm)); + this.groupData = new System.Windows.Forms.GroupBox(); + this.dataPanel = new System.Windows.Forms.TableLayoutPanel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.label3 = new System.Windows.Forms.Label(); + this.comments = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.priority = new System.Windows.Forms.NumericUpDown(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.test = new System.Windows.Forms.Button(); + this.groupData.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.priority)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.SuspendLayout(); + // + // groupData + // + resources.ApplyResources(this.groupData, "groupData"); + this.groupData.Controls.Add(this.dataPanel); + this.groupData.Name = "groupData"; + this.groupData.TabStop = false; + // + // dataPanel + // + resources.ApplyResources(this.dataPanel, "dataPanel"); + this.dataPanel.Name = "dataPanel"; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.label3, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.comments, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.priority, 1, 2); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Name = "comments"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // priority + // + resources.ApplyResources(this.priority, "priority"); + this.priority.Maximum = new decimal(new int[] { + 10, + 0, + 0, + 0}); + this.priority.Minimum = new decimal(new int[] { + 10, + 0, + 0, + -2147483648}); + this.priority.Name = "priority"; + this.priority.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 1, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // tableLayoutPanel3 + // + resources.ApplyResources(this.tableLayoutPanel3, "tableLayoutPanel3"); + this.tableLayoutPanel3.Controls.Add(this.test, 1, 0); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + // + // test + // + resources.ApplyResources(this.test, "test"); + this.test.Name = "test"; + this.test.UseVisualStyleBackColor = true; + this.test.Click += new System.EventHandler(this.test_Click); + // + // AuthenticatorForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.groupData); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.tableLayoutPanel1); + this.DoubleBuffered = true; + this.Name = "AuthenticatorForm"; + this.Load += new System.EventHandler(this.Authenticator_Load); + this.groupData.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.priority)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupData; + private System.Windows.Forms.TableLayoutPanel dataPanel; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.TextBox comments; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox name; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Button test; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.NumericUpDown priority; + + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AuthenticatorForm.cs b/client/administration/UdsAdmin/forms/AuthenticatorForm.cs new file mode 100644 index 000000000..984b142b8 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AuthenticatorForm.cs @@ -0,0 +1,137 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class AuthenticatorForm : Form + { + string _id; + xmlrpc.GuiField[] _flds; + xmlrpc.GuiFieldValue[] _fldValues; + string _authenticatorName; + string _authenticatorType; + + public AuthenticatorForm(string providerName, string providerType, Icon icon) + { + InitializeComponent(); + _fldValues = null; + _id = null; + _flds = null; + _authenticatorName = providerName; + _authenticatorType = providerType; + Icon = icon; + Text = Strings.titleAuthenticator; + } + + public void setData(string name, string comments, string id, xmlrpc.GuiFieldValue[] data) + { + this.name.Text = name; + this.comments.Text = comments; + this._id = id; + this.priority.Value = Convert.ToInt32(xmlrpc.GuiFieldValue.getData(data, "priority")); + _fldValues = data; + } + + private void Authenticator_Load(object sender, EventArgs e) + { + _flds = xmlrpc.UdsAdminService.GetAuthenticatorGui(_authenticatorType); + Size sz = gui.DinamycFieldsManager.PutFields(dataPanel, _flds, _fldValues); + groupData.Size = new Size(groupData.Size.Width, 32 + sz.Height); + Size wSize = new Size(); + wSize.Width = Size.Width; + wSize.Height = groupData.Location.Y + tableLayoutPanel1.Size.Height + groupData.Size.Height + 48; + Size = MinimumSize = MaximumSize = wSize; + + if (_flds.Length == 0) + groupData.Visible = false; + Text = _authenticatorName; + //this.Location = System.Windows.Forms.Cursor.Position; + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + xmlrpc.GuiFieldValue[] data; + try { + data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + } + catch (gui.DinamycFieldsManager.ValidationError err) + { + gui.UserNotifier.notifyValidationException(err); + return; + } + + try { + if (_id == null) + xmlrpc.UdsAdminService.CreateAuthenticator(name.Text, comments.Text, _authenticatorType, Convert.ToInt32(priority.Value), data); + else + { + xmlrpc.UdsAdminService.ModifyAuthenticator(name.Text, comments.Text, Convert.ToInt32(priority.Value), _id, data); + } + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void test_Click(object sender, EventArgs e) + { + try + { + xmlrpc.GuiFieldValue[] data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + xmlrpc.ResultTest res = xmlrpc.UdsAdminService.TestAuthenticator(_authenticatorType, data); + gui.UserNotifier.notifyTestResult(res); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + catch (gui.DinamycFieldsManager.ValidationError ex) + { + gui.UserNotifier.notifyValidationException(ex); + } + } + } +} diff --git a/client/administration/UdsAdmin/forms/AuthenticatorForm.de.resx b/client/administration/UdsAdmin/forms/AuthenticatorForm.de.resx new file mode 100644 index 000000000..c33d1a7e5 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AuthenticatorForm.de.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Authenticator + + + Authentifikator Daten + + + Priorität + + + Kommentare + + + Name + + + Allgemeine Daten + + + Akzeptieren + + + Abbrechen + + + Test + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AuthenticatorForm.es.resx b/client/administration/UdsAdmin/forms/AuthenticatorForm.es.resx new file mode 100644 index 000000000..331480fd0 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AuthenticatorForm.es.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Datos del autenticador + + + + 65, 13 + + + Comentarios + + + 44, 13 + + + Nombre + + + Datos comunes + + + Aceptar + + + Cancelar + + + Probar + + + Autenticador + + + Prioridad + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AuthenticatorForm.fr.resx b/client/administration/UdsAdmin/forms/AuthenticatorForm.fr.resx new file mode 100644 index 000000000..0a85bb677 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AuthenticatorForm.fr.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Données authentificateur + + + Données communes + + + + 39, 13 + + + Priorité + + + 73, 13 + + + Commentaires + + + 29, 13 + + + Nom + + + Accepter + + + Annuler + + + Test + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/AuthenticatorForm.resx b/client/administration/UdsAdmin/forms/AuthenticatorForm.resx new file mode 100644 index 000000000..73fe3efc9 --- /dev/null +++ b/client/administration/UdsAdmin/forms/AuthenticatorForm.resx @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 2 + + + Fill + + + + 3, 16 + + + 2 + + + 443, 157 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 12, 125 + + + 449, 176 + + + 7 + + + Authenticator data + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Top, Left, Right + + + 2 + + + Left + + + True + + + NoControl + + + 3, 57 + + + 38, 13 + + + 4 + + + Priority + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + 82, 28 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Left + + + True + + + 3, 31 + + + 56, 13 + + + 1 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + Left + + + True + + + 3, 6 + + + 35, 13 + + + 0 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + 82, 53 + + + 63, 20 + + + 5 + + + priority + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + 12, 19 + + + 3 + + + 414, 77 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="priority" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + 12, 7 + + + 449, 112 + + + 6 + + + Common Data + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 136, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 334, 7 + + + 137, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 3 + + + Fill + + + 39, 3 + + + 103, 21 + + + 0 + + + Test + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + 145, 3 + + + 1 + + + 183, 27 + + + 2 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="test" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,20,Percent,60,Percent,20" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Bottom + + + 0, 307 + + + 1 + + + 474, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="tableLayoutPanel3" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 474, 340 + + + CenterParent + + + Authenticator + + + AuthenticatorForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ConfigurationForm.Designer.cs b/client/administration/UdsAdmin/forms/ConfigurationForm.Designer.cs new file mode 100644 index 000000000..e34896699 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ConfigurationForm.Designer.cs @@ -0,0 +1,88 @@ +namespace UdsAdmin.forms +{ + partial class ConfigurationForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ConfigurationForm)); + this.modTabs = new System.Windows.Forms.TabControl(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // modTabs + // + resources.ApplyResources(this.modTabs, "modTabs"); + this.modTabs.Name = "modTabs"; + this.modTabs.SelectedIndex = 0; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // ConfigurationForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel2); + this.Controls.Add(this.modTabs); + this.Name = "ConfigurationForm"; + this.Load += new System.EventHandler(this.ConfigurationForm_Load); + this.tableLayoutPanel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl modTabs; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ConfigurationForm.cs b/client/administration/UdsAdmin/forms/ConfigurationForm.cs new file mode 100644 index 000000000..2648ed890 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ConfigurationForm.cs @@ -0,0 +1,141 @@ +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class ConfigurationForm : Form + { + List panels = new List(); + + public ConfigurationForm() + { + InitializeComponent(); + Text = Strings.titleConfiguration; + } + + private void ConfigurationForm_Load(object sender, EventArgs e) + { + xmlrpc.Configuration[] configuration = xmlrpc.UdsAdminService.GetConfiguration(); + Dictionary> dict = new Dictionary>(); + // Create the different tabs + foreach (xmlrpc.Configuration cfg in configuration) + { + if (!dict.ContainsKey(cfg.section)) + { + dict.Add(cfg.section, new List()); + } + dict[cfg.section].Add(cfg); + } + + // Now we create the tabs with fields + foreach (KeyValuePair> entry in dict) + { + int rows = entry.Value.Count; + TableLayoutPanel pan = new TableLayoutPanel(); + pan.RowCount = rows; + pan.ColumnCount = 2; + pan.AutoSize = true; + pan.Left = 8; + pan.Top = 8; + pan.Dock = DockStyle.Fill; + pan.AutoScroll = true; + + int row = 0; + foreach (xmlrpc.Configuration cfg in entry.Value) + { + Label lab = new Label(); + lab.Text = cfg.key; + lab.Margin = new System.Windows.Forms.Padding(3, 6, 3, 0); + lab.AutoSize = true; + lab.Dock = DockStyle.Fill; + pan.Controls.Add(lab, 0, row); + TextBox txt = new TextBox(); + txt.Tag = cfg; + txt.Text = cfg.value; + txt.Width = 180; + txt.UseSystemPasswordChar = cfg.crypt; + txt.Dock = DockStyle.Fill; + pan.Controls.Add(txt, 1, row); + row++; + } + + // Set panel column and rows styles + TableLayoutColumnStyleCollection styles = pan.ColumnStyles; + + foreach (ColumnStyle style in styles) + { + style.SizeType = SizeType.AutoSize; + } + + TableLayoutRowStyleCollection stylesR = pan.RowStyles; + foreach (RowStyle style in stylesR) + { + style.SizeType = SizeType.AutoSize; + } + + TabPage page = new TabPage(entry.Key); + page.Controls.Add(pan); + panels.Add(pan); + modTabs.TabPages.Add(page); + } + + } + + private void accept_Click(object sender, EventArgs e) + { + // Locate changed items and submit them to save + List changed = new List(); + foreach (TableLayoutPanel pan in panels ) + { + foreach (Control ctrl in pan.Controls) + { + if (ctrl is TextBox) + { + TextBox txt = (TextBox)ctrl; + xmlrpc.Configuration cfg = (xmlrpc.Configuration)ctrl.Tag; + if( cfg.value != txt.Text ) + changed.Add( new xmlrpc.Configuration(cfg.section, cfg.key, txt.Text, cfg.crypt ) ); + } + } + } + + xmlrpc.UdsAdminService.UpdateConfiguration(changed.ToArray()); + + DialogResult = System.Windows.Forms.DialogResult.OK; + } + } +} diff --git a/client/administration/UdsAdmin/forms/ConfigurationForm.de.resx b/client/administration/UdsAdmin/forms/ConfigurationForm.de.resx new file mode 100644 index 000000000..97d9b06d3 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ConfigurationForm.de.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + UserPreferencesForm + + + UserPreferencesForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ConfigurationForm.es.resx b/client/administration/UdsAdmin/forms/ConfigurationForm.es.resx new file mode 100644 index 000000000..41809757f --- /dev/null +++ b/client/administration/UdsAdmin/forms/ConfigurationForm.es.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + UserPreferencesForm + + + UserPreferencesForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ConfigurationForm.fr.resx b/client/administration/UdsAdmin/forms/ConfigurationForm.fr.resx new file mode 100644 index 000000000..568042243 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ConfigurationForm.fr.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + UserPreferencesForm + + + UserPreferencesForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ConfigurationForm.resx b/client/administration/UdsAdmin/forms/ConfigurationForm.resx new file mode 100644 index 000000000..98aed7f06 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ConfigurationForm.resx @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Bottom, Left, Right + + + + 12, 12 + + + 368, 319 + + + + 0 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 111, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 276, 7 + + + 113, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 337 + + + 1 + + + 392, 33 + + + 8 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 392, 370 + + + 400, 400 + + + CenterParent + + + ConfigurationForm + + + ConfigurationForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedGroupForm.Designer.cs b/client/administration/UdsAdmin/forms/DeployedGroupForm.Designer.cs new file mode 100644 index 000000000..82fc2598a --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedGroupForm.Designer.cs @@ -0,0 +1,127 @@ +namespace UdsAdmin.forms +{ + partial class DeployedGroupForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployedGroupForm)); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.groupLabel = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.groupCombo = new System.Windows.Forms.ComboBox(); + this.authCombo = new System.Windows.Forms.ComboBox(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.groupLabel, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.groupCombo, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.authCombo, 1, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // groupLabel + // + resources.ApplyResources(this.groupLabel, "groupLabel"); + this.groupLabel.Name = "groupLabel"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // groupCombo + // + this.groupCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.groupCombo.FormattingEnabled = true; + resources.ApplyResources(this.groupCombo, "groupCombo"); + this.groupCombo.Name = "groupCombo"; + // + // authCombo + // + this.authCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.authCombo.FormattingEnabled = true; + resources.ApplyResources(this.authCombo, "authCombo"); + this.authCombo.Name = "authCombo"; + this.authCombo.SelectedIndexChanged += new System.EventHandler(this.authCombo_SelectedIndexChanged); + // + // DeployedGroupForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.tableLayoutPanel2); + this.Name = "DeployedGroupForm"; + this.Load += new System.EventHandler(this.DeployedGroupForm_Load); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label groupLabel; + private System.Windows.Forms.ComboBox groupCombo; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox authCombo; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedGroupForm.cs b/client/administration/UdsAdmin/forms/DeployedGroupForm.cs new file mode 100644 index 000000000..7492b947b --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedGroupForm.cs @@ -0,0 +1,94 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class DeployedGroupForm : Form + { + private xmlrpc.DeployedService _ds; + + public DeployedGroupForm(xmlrpc.DeployedService ds) + { + _ds = ds; + InitializeComponent(); + Text = Strings.titleAssignNewGroup; + } + + private void DeployedGroupForm_Load(object sender, EventArgs e) + { + xmlrpc.Authenticator[] auths = xmlrpc.UdsAdminService.GetAuthenticators(); + authCombo.Items.AddRange(auths); + + if (auths.Length > 0) + { + authCombo.SelectedIndex = 0; + } + else + { + MessageBox.Show(Strings.error, Strings.authenticators, MessageBoxButtons.OK, MessageBoxIcon.Error); + Close(); + } + + } + + private void accept_Click(object sender, EventArgs e) + { + if (groupCombo.SelectedItem != null) + { + xmlrpc.Group grp = (xmlrpc.Group)groupCombo.SelectedItem; + xmlrpc.UdsAdminService.AssignGroupToDeployedService(_ds.id, grp.id); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + else + gui.UserNotifier.notifyError(Strings.groupRequired); + + } + + private void authCombo_SelectedIndexChanged(object sender, EventArgs e) + { + groupCombo.Items.Clear(); + string id = ((xmlrpc.Authenticator)authCombo.SelectedItem).id; + xmlrpc.Group[] grps = xmlrpc.UdsAdminService.GetAuthenticatorGroups(id); + if (grps.Length > 0) + { + groupCombo.Items.AddRange(grps); + groupCombo.SelectedIndex = 0; + } + } + } +} diff --git a/client/administration/UdsAdmin/forms/DeployedGroupForm.de.resx b/client/administration/UdsAdmin/forms/DeployedGroupForm.de.resx new file mode 100644 index 000000000..ea10f3c8c --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedGroupForm.de.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Gruppenname + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + groupBox + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Assign new group + + + DeployedGroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedGroupForm.es.resx b/client/administration/UdsAdmin/forms/DeployedGroupForm.es.resx new file mode 100644 index 000000000..67c6b67f4 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedGroupForm.es.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Nombre del grupo + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + groupBox + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Assign new group + + + DeployedGroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedGroupForm.fr.resx b/client/administration/UdsAdmin/forms/DeployedGroupForm.fr.resx new file mode 100644 index 000000000..b3e4a4ef2 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedGroupForm.fr.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Nom du groupe + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + groupBox + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Assign new group + + + DeployedGroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedGroupForm.resx b/client/administration/UdsAdmin/forms/DeployedGroupForm.resx new file mode 100644 index 000000000..ff9221600 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedGroupForm.resx @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 101, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 253, 7 + + + 103, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 97 + + + 1 + + + 359, 33 + + + 7 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + 2 + + + True + + + 3, 37 + + + 3, 6, 3, 0 + + + 67, 13 + + + 1 + + + Group Name + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 70, 13 + + + 1 + + + Authenticator + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 91, 34 + + + 240, 21 + + + 2 + + + groupCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + 91, 3 + + + 240, 21 + + + 2 + + + authCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + 13, 13 + + + 2 + + + 334, 62 + + + 8 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="groupLabel" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="groupCombo" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="authCombo" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,26,49842,Percent,73,50158" /><Rows Styles="Percent,50,Percent,50,Absolute,20" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 359, 130 + + + CenterParent + + + Assign new group + + + DeployedGroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedServiceForm.Designer.cs b/client/administration/UdsAdmin/forms/DeployedServiceForm.Designer.cs new file mode 100644 index 000000000..c328bd81c --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedServiceForm.Designer.cs @@ -0,0 +1,325 @@ +namespace UdsAdmin.forms +{ + partial class DeployedServiceForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployedServiceForm)); + this.tabs = new System.Windows.Forms.TabControl(); + this.Service = new System.Windows.Forms.TabPage(); + this.publishOnSave = new System.Windows.Forms.CheckBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.label3 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.osManagerCombo = new System.Windows.Forms.ComboBox(); + this.baseServiceCombo = new System.Windows.Forms.ComboBox(); + this.label9 = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.nameBox = new System.Windows.Forms.TextBox(); + this.commentsBox = new System.Windows.Forms.TextBox(); + this.Transports = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.label4 = new System.Windows.Forms.Label(); + this.allowedTransports = new System.Windows.Forms.CheckedListBox(); + this.Cache = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); + this.cacheL2ServicesBox = new System.Windows.Forms.NumericUpDown(); + this.cacheL2Label = new System.Windows.Forms.Label(); + this.cacheServicesBox = new System.Windows.Forms.NumericUpDown(); + this.label5 = new System.Windows.Forms.Label(); + this.cacheLabel = new System.Windows.Forms.Label(); + this.initialServicesBox = new System.Windows.Forms.NumericUpDown(); + this.label7 = new System.Windows.Forms.Label(); + this.maxServicesBox = new System.Windows.Forms.NumericUpDown(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tabs.SuspendLayout(); + this.Service.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.Transports.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.Cache.SuspendLayout(); + this.tableLayoutPanel4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.cacheL2ServicesBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.cacheServicesBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.initialServicesBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.maxServicesBox)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tabs + // + resources.ApplyResources(this.tabs, "tabs"); + this.tabs.Controls.Add(this.Service); + this.tabs.Controls.Add(this.Transports); + this.tabs.Controls.Add(this.Cache); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + // + // Service + // + this.Service.BackColor = System.Drawing.Color.Transparent; + this.Service.Controls.Add(this.publishOnSave); + this.Service.Controls.Add(this.tableLayoutPanel2); + resources.ApplyResources(this.Service, "Service"); + this.Service.Name = "Service"; + this.Service.UseVisualStyleBackColor = true; + // + // publishOnSave + // + resources.ApplyResources(this.publishOnSave, "publishOnSave"); + this.publishOnSave.Name = "publishOnSave"; + this.publishOnSave.UseVisualStyleBackColor = true; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.label3, 0, 3); + this.tableLayoutPanel2.Controls.Add(this.label1, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.osManagerCombo, 1, 3); + this.tableLayoutPanel2.Controls.Add(this.baseServiceCombo, 1, 2); + this.tableLayoutPanel2.Controls.Add(this.label9, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.label8, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.nameBox, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.commentsBox, 1, 1); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // osManagerCombo + // + resources.ApplyResources(this.osManagerCombo, "osManagerCombo"); + this.osManagerCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.osManagerCombo.FormattingEnabled = true; + this.osManagerCombo.Name = "osManagerCombo"; + // + // baseServiceCombo + // + resources.ApplyResources(this.baseServiceCombo, "baseServiceCombo"); + this.baseServiceCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.baseServiceCombo.FormattingEnabled = true; + this.baseServiceCombo.Name = "baseServiceCombo"; + this.baseServiceCombo.SelectionChangeCommitted += new System.EventHandler(this.baseServiceCombo_SelectionChangeCommitted); + // + // label9 + // + resources.ApplyResources(this.label9, "label9"); + this.label9.Name = "label9"; + // + // label8 + // + resources.ApplyResources(this.label8, "label8"); + this.label8.Name = "label8"; + // + // nameBox + // + resources.ApplyResources(this.nameBox, "nameBox"); + this.nameBox.Name = "nameBox"; + // + // commentsBox + // + resources.ApplyResources(this.commentsBox, "commentsBox"); + this.commentsBox.Name = "commentsBox"; + // + // Transports + // + this.Transports.Controls.Add(this.tableLayoutPanel3); + resources.ApplyResources(this.Transports, "Transports"); + this.Transports.Name = "Transports"; + this.Transports.UseVisualStyleBackColor = true; + // + // tableLayoutPanel3 + // + resources.ApplyResources(this.tableLayoutPanel3, "tableLayoutPanel3"); + this.tableLayoutPanel3.Controls.Add(this.label4, 0, 0); + this.tableLayoutPanel3.Controls.Add(this.allowedTransports, 0, 1); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // allowedTransports + // + resources.ApplyResources(this.allowedTransports, "allowedTransports"); + this.allowedTransports.FormattingEnabled = true; + this.allowedTransports.Name = "allowedTransports"; + // + // Cache + // + this.Cache.Controls.Add(this.tableLayoutPanel4); + resources.ApplyResources(this.Cache, "Cache"); + this.Cache.Name = "Cache"; + this.Cache.UseVisualStyleBackColor = true; + // + // tableLayoutPanel4 + // + resources.ApplyResources(this.tableLayoutPanel4, "tableLayoutPanel4"); + this.tableLayoutPanel4.Controls.Add(this.cacheL2ServicesBox, 1, 2); + this.tableLayoutPanel4.Controls.Add(this.cacheL2Label, 0, 2); + this.tableLayoutPanel4.Controls.Add(this.cacheServicesBox, 1, 1); + this.tableLayoutPanel4.Controls.Add(this.label5, 0, 0); + this.tableLayoutPanel4.Controls.Add(this.cacheLabel, 0, 1); + this.tableLayoutPanel4.Controls.Add(this.initialServicesBox, 1, 0); + this.tableLayoutPanel4.Controls.Add(this.label7, 0, 3); + this.tableLayoutPanel4.Controls.Add(this.maxServicesBox, 1, 3); + this.tableLayoutPanel4.Name = "tableLayoutPanel4"; + // + // cacheL2ServicesBox + // + resources.ApplyResources(this.cacheL2ServicesBox, "cacheL2ServicesBox"); + this.cacheL2ServicesBox.Name = "cacheL2ServicesBox"; + // + // cacheL2Label + // + resources.ApplyResources(this.cacheL2Label, "cacheL2Label"); + this.cacheL2Label.Name = "cacheL2Label"; + // + // cacheServicesBox + // + resources.ApplyResources(this.cacheServicesBox, "cacheServicesBox"); + this.cacheServicesBox.Name = "cacheServicesBox"; + // + // label5 + // + resources.ApplyResources(this.label5, "label5"); + this.label5.Name = "label5"; + // + // cacheLabel + // + resources.ApplyResources(this.cacheLabel, "cacheLabel"); + this.cacheLabel.Name = "cacheLabel"; + // + // initialServicesBox + // + resources.ApplyResources(this.initialServicesBox, "initialServicesBox"); + this.initialServicesBox.Name = "initialServicesBox"; + // + // label7 + // + resources.ApplyResources(this.label7, "label7"); + this.label7.Name = "label7"; + // + // maxServicesBox + // + resources.ApplyResources(this.maxServicesBox, "maxServicesBox"); + this.maxServicesBox.Name = "maxServicesBox"; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // DeployedServiceForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.tabs); + this.Name = "DeployedServiceForm"; + this.Load += new System.EventHandler(this.DeployedServiceForm_Load); + this.tabs.ResumeLayout(false); + this.Service.ResumeLayout(false); + this.Service.PerformLayout(); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.Transports.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.tableLayoutPanel3.PerformLayout(); + this.Cache.ResumeLayout(false); + this.tableLayoutPanel4.ResumeLayout(false); + this.tableLayoutPanel4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.cacheL2ServicesBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.cacheServicesBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.initialServicesBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.maxServicesBox)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage Service; + private System.Windows.Forms.TabPage Transports; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.CheckedListBox allowedTransports; + private System.Windows.Forms.TabPage Cache; + private System.Windows.Forms.ComboBox osManagerCombo; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; + private System.Windows.Forms.NumericUpDown maxServicesBox; + private System.Windows.Forms.NumericUpDown cacheServicesBox; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label cacheLabel; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.NumericUpDown initialServicesBox; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ComboBox baseServiceCombo; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TextBox nameBox; + private System.Windows.Forms.TextBox commentsBox; + private System.Windows.Forms.NumericUpDown cacheL2ServicesBox; + private System.Windows.Forms.Label cacheL2Label; + private System.Windows.Forms.CheckBox publishOnSave; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedServiceForm.cs b/client/administration/UdsAdmin/forms/DeployedServiceForm.cs new file mode 100644 index 000000000..fa30cfcbf --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedServiceForm.cs @@ -0,0 +1,229 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class DeployedServiceForm : Form + { + private xmlrpc.DeployedService _dps; + + private xmlrpc.Service[] _services = null; + private xmlrpc.OSManager[] _osManagers = null; + private xmlrpc.Transport[] _transports; + private ToolTip tooltipCache = new ToolTip(); + private ToolTip tooltipCacheL2 = new ToolTip(); + + public DeployedServiceForm() + { + InitializeComponent(); + _dps = new xmlrpc.DeployedService(); + _dps.id = ""; + Text = Strings.titleDeployedService; + } + + public void setData(xmlrpc.DeployedService dps) + { + _dps = dps; + } + + private void DeployedServiceForm_Load(object sender, EventArgs e) + { + _services = xmlrpc.UdsAdminService.GetAllServices(); + _osManagers = xmlrpc.UdsAdminService.GetOSManagers(); + _transports = xmlrpc.UdsAdminService.GetTransports(); + + tooltipCache.SetToolTip(cacheLabel, ""); + tooltipCacheL2.SetToolTip(cacheL2Label, ""); + + Text = Strings.titleDeployedService; + + if (_services.Length == 0) + { + UdsAdmin.gui.UserNotifier.notifyError(Strings.needsServices); + Close(); + return; + } + + if (_osManagers.Length == 0) + { + UdsAdmin.gui.UserNotifier.notifyError(Strings.needsOsManagers); + Close(); + return; + } + + if (_transports.Length == 0 ) + { + UdsAdmin.gui.UserNotifier.notifyError(Strings.needsTransports); + } + + baseServiceCombo.Items.AddRange(_services); + osManagerCombo.Items.AddRange(_osManagers); + allowedTransports.Items.AddRange(_transports); + + // Modifying, update form to modify operation + if (_dps.id != "") + { + Text = Strings.modifying + " " + _dps.name; + + baseServiceCombo.Enabled = false; + osManagerCombo.Enabled = false; + publishOnSave.Enabled = false; + publishOnSave.Checked = false; + foreach (xmlrpc.OSManager osm in _osManagers) + if (osm.id == _dps.idOsManager) + { + osManagerCombo.SelectedItem = osm; + break; + } + foreach (xmlrpc.Service serv in _services) + if (serv.id == _dps.idService) + { + baseServiceCombo.SelectedItem = serv; + ((Control)tabs.TabPages["Cache"]).Enabled = serv.info.usesCache; + cacheL2Label.Enabled = cacheL2ServicesBox.Enabled = serv.info.usesCacheL2; + tooltipCache.SetToolTip(cacheLabel, serv.info.cacheTooltip); + tooltipCacheL2.SetToolTip(cacheL2Label, serv.info.cacheTooltipL2); + break; + } + foreach (xmlrpc.SimpleInfo trans in _dps.transports) + { + for (int i = 0; i < allowedTransports.Items.Count; i++) + { + xmlrpc.Transport tr = (xmlrpc.Transport)allowedTransports.Items[i]; + if (tr.id == trans.id) + allowedTransports.SetItemChecked(i, true); + } + } + nameBox.Text = _dps.name; + commentsBox.Text = _dps.comments; + initialServicesBox.Value = _dps.initialServices; + cacheServicesBox.Value = _dps.cacheL1; + cacheL2ServicesBox.Value = _dps.cacheL2; + maxServicesBox.Value = _dps.maxServices; + } + else + { + publishOnSave.Checked = true; + } + + //this.Location = System.Windows.Forms.Cursor.Position; + } + + private void baseServiceCombo_SelectionChangeCommitted(object sender, EventArgs e) + { + xmlrpc.Service selected = (xmlrpc.Service)baseServiceCombo.SelectedItem; + if (_dps.id == "") + { + osManagerCombo.Enabled = selected.info.needsManager; + } + publishOnSave.Enabled = selected.info.needsPublication; + ((Control)tabs.TabPages["Cache"]).Enabled = selected.info.usesCache; + cacheL2Label.Enabled = cacheL2ServicesBox.Enabled = selected.info.usesCacheL2; + tooltipCache.SetToolTip(cacheLabel, selected.info.cacheTooltip); + tooltipCacheL2.SetToolTip(cacheL2Label, selected.info.cacheTooltipL2); + } + + private void accept_Click(object sender, EventArgs e) + { + if (nameBox.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + + if( baseServiceCombo.SelectedItem == null ) + { + gui.UserNotifier.notifyError(Strings.specifyBaseService); + return; + } + + if( ((xmlrpc.Service)baseServiceCombo.SelectedItem).info.needsManager && osManagerCombo.SelectedItem == null ) + { + gui.UserNotifier.notifyError(Strings.specifyOsManager); + return; + } + + if (initialServicesBox.Value > maxServicesBox.Value) + maxServicesBox.Value = initialServicesBox.Value; + if (cacheServicesBox.Value > maxServicesBox.Value) + maxServicesBox.Value = cacheServicesBox.Value; + + _dps.initialServices = (int)initialServicesBox.Value; + _dps.cacheL1 = (int)cacheServicesBox.Value; + _dps.cacheL2 = (int)cacheL2ServicesBox.Value; + _dps.maxServices = (int)maxServicesBox.Value; + _dps.name = nameBox.Text; + _dps.comments = commentsBox.Text; + _dps.idService = ((xmlrpc.Service)baseServiceCombo.SelectedItem).id; + if (osManagerCombo.SelectedItem != null) + _dps.idOsManager = ((xmlrpc.OSManager)osManagerCombo.SelectedItem).id; + else + _dps.idOsManager = "-1"; + + xmlrpc.SimpleInfo[] transports = new xmlrpc.SimpleInfo[allowedTransports.CheckedItems.Count]; + int n = 0; + foreach( xmlrpc.Transport trns in allowedTransports.CheckedItems ) + transports[n++] = new xmlrpc.SimpleInfo(trns.id, trns.name); + + _dps.transports = transports; + + try + { + if (_dps.id == "") // Create a new deployed Service + { + _dps.state = xmlrpc.Constants.STATE_ACTIVE; + _dps.id = xmlrpc.UdsAdminService.CreateDeployedService(_dps); + if ( publishOnSave.Enabled && publishOnSave.Checked) + xmlrpc.UdsAdminService.PublishDeployedService(_dps); + } + else + { + xmlrpc.UdsAdminService.ModifyDeployedService(_dps); + } + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch( CookComputing.XmlRpc.XmlRpcFaultException ex ) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + + } +} diff --git a/client/administration/UdsAdmin/forms/DeployedServiceForm.de.resx b/client/administration/UdsAdmin/forms/DeployedServiceForm.de.resx new file mode 100644 index 000000000..48bae546a --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedServiceForm.de.resx @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Veröffentlichen automatisch auf Speichern + + + OS-Manager + + + Basisdienst + + + Kommentare + + + Zulässige Transporte + + + Transporte + + + Dienstleistungen im L2-Cache zu behalten (Experimental) + + + Erste Services Availables + + + Dienstleistungen im Cache zu behalten + + + Maximale Anzahl von Dienstleistungen + + + Akzeptieren + + + Abbrechen + + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABtIBEAbSEQAHIoFQB2KhQAdy0VAHkuFgB3LhsAezAWAHwxFQAyMjIAezIcAH41HQCCNxgAfzYdAIM4 + GwCGOxgAiD4cAIk/GQCJPxsAikEgAIxCHwCHQScAkUYbAItEJgCPRyIARkZGAI5HKwCVTCAAS0tLAJ1T + HgBPT08AkFIwAJ9XIwCTVjUApFsiAJpWNwClXCEAmlg5AJRcPACpYiQAq2MjAKxkIwCXX0AAmVpUALNt + JwCiZlsAqGxOAKZrWACscU4Aw4EsAK52XQDAh0sA0JU+ANGZSQCSkpIAmJiYAJmZmQCdnZ0An5+fAKCg + oACtr64AtbW1ALi4uAC8vLwAvb29ALy/vgC/wsEAw8PDAMTExADCxcQAw8XEAMXFxQDExsUAxMfGAMfH + xwDFyMcAyMjIAMnJyQDKysoAzMzMAMvNzADMzc0Azc3NAM7OzgDPz88Az9DQANDR0QDR0dEA0tLSANPT + 0wDV1dUA1tbWANra2gDb29sA3NzcAN3d3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYGBgYGAJYBlgHmAcYGBgYGBgYGA2OTc7Ojg7NmBgYGBgYGBgYE9XQ0NXUkRgYGBgYGBgYExN + T1RUUk5gYGBgYGBgYGBgVFtYWVtTTWBgYGBgYGBgP1peXF1dU2BgYGBgYGBgYGBHXF9ZWUpgYGBgYGBg + YGA5PlZVREA9YGBgYGBgYGBgYGBgPGBgYGBgYGBgYGBQYElgRUtIYEJgQVFgYGBgYGBgYGBgYGBgYGBG + YGBgYCZgYGBgIR9gYGAjKhFgYBASB2BgDRQTBWBgDhgXAxUnJA8BYBgpHQgAYCAoGwpgNSwMYGAwNCIE + YGAzMRYGKzIaYGBgLS4LYGBgLyUCYPqvAADwDwAA+A8AAPAfAAD4DwAA8B8AAPgfAADwHwAA/v8AANRT + AAD/+wAA3nEAAIwwAAAEEAAAjDAAABxxAAA= + + + + Service + + + Cache + + + Name + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedServiceForm.es.resx b/client/administration/UdsAdmin/forms/DeployedServiceForm.es.resx new file mode 100644 index 000000000..4a3075345 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedServiceForm.es.resx @@ -0,0 +1,551 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + cacheL2Label + + + 6 + + + allowedTransports + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + baseServiceCombo + + + 5 + + + maxServicesBox + + + Service + + + 0 + + + tableLayoutPanel2 + + + 0 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + tableLayoutPanel2 + + + tabs + + + Cache + + + tableLayoutPanel1 + + + osManagerCombo + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 1 + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + label1 + + + tabs + + + Transportes + + + Servicios + + + accept + + + Transports + + + Service + + + tabs + + + label7 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + Cancelar + + + tableLayoutPanel1 + + + tableLayoutPanel2 + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Servicios a mantener en la caché + + + tableLayoutPanel4 + + + Comentarios + + + tableLayoutPanel4 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 4 + + + Caché + + + 0 + + + Publicar automáticamente al guardar + + + 2 + + + DeployedServiceForm + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 0 + + + Servicios a mantener en la caché L2 (Experimental) + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + label5 + + + tableLayoutPanel2 + + + cacheServicesBox + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 2 + + + label9 + + + publishOnSave + + + 0 + + + 1 + + + 1 + + + Service + + + tableLayoutPanel4 + + + Aceptar + + + tableLayoutPanel2 + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + label4 + + + 0 + + + cacheLabel + + + Nº Maximo de servicios + + + Nombre + + + tableLayoutPanel4 + + + tableLayoutPanel2 + + + tableLayoutPanel4 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commentsBox + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + Cache + + + cancel + + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABtIBEAbSEQAHIoFQB2KhQAdy0VAHkuFgB3LhsAezAWAHwxFQAyMjIAezIcAH41HQCCNxgAfzYdAIM4 + GwCGOxgAiD4cAIk/GQCJPxsAikEgAIxCHwCHQScAkUYbAItEJgCPRyIARkZGAI5HKwCVTCAAS0tLAJ1T + HgBPT08AkFIwAJ9XIwCTVjUApFsiAJpWNwClXCEAmlg5AJRcPACpYiQAq2MjAKxkIwCXX0AAmVpUALNt + JwCiZlsAqGxOAKZrWACscU4Aw4EsAK52XQDAh0sA0JU+ANGZSQCSkpIAmJiYAJmZmQCdnZ0An5+fAKCg + oACtr64AtbW1ALi4uAC8vLwAvb29ALy/vgC/wsEAw8PDAMTExADCxcQAw8XEAMXFxQDExsUAxMfGAMfH + xwDFyMcAyMjIAMnJyQDKysoAzMzMAMvNzADMzc0Azc3NAM7OzgDPz88Az9DQANDR0QDR0dEA0tLSANPT + 0wDV1dUA1tbWANra2gDb29sA3NzcAN3d3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYGBgYGAJYBlgHmAcYGBgYGBgYGA2OTc7Ojg7NmBgYGBgYGBgYE9XQ0NXUkRgYGBgYGBgYExN + T1RUUk5gYGBgYGBgYGBgVFtYWVtTTWBgYGBgYGBgP1peXF1dU2BgYGBgYGBgYGBHXF9ZWUpgYGBgYGBg + YGA5PlZVREA9YGBgYGBgYGBgYGBgPGBgYGBgYGBgYGBQYElgRUtIYEJgQVFgYGBgYGBgYGBgYGBgYGBG + YGBgYCZgYGBgIR9gYGAjKhFgYBASB2BgDRQTBWBgDhgXAxUnJA8BYBgpHQgAYCAoGwpgNSwMYGAwNCIE + YGAzMRYGKzIaYGBgLS4LYGBgLyUCYPqvAADwDwAA+A8AAPAfAAD4DwAA8B8AAPgfAADwHwAA/v8AANRT + AAD/+wAA3nEAAIwwAAAEEAAAjDAAABxxAAA= + + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + nameBox + + + Transports + + + New Deployed Service + + + tabs + + + tableLayoutPanel1 + + + label8 + + + tableLayoutPanel3 + + + OS Manager + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 3 + + + cacheL2ServicesBox + + + Servicio Base + + + 1 + + + 2 + + + $this + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 6 + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 5 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 4 + + + 1 + + + 0 + + + tableLayoutPanel4 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 0 + + + 3 + + + tableLayoutPanel4 + + + 1 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 7 + + + initialServicesBox + + + Servicios Iniciales disponibles + + + tableLayoutPanel3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 0 + + + Transportes permitidos + + + label3 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedServiceForm.fr.resx b/client/administration/UdsAdmin/forms/DeployedServiceForm.fr.resx new file mode 100644 index 000000000..b75d93ff3 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedServiceForm.fr.resx @@ -0,0 +1,575 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Publier automatiquement d'enregistrer + + + publishOnSave + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Service + + + 0 + + + Gestionnaire de système d'exploitation + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Service de base + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + osManagerCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + authenticatorCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + baseServiceCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + Nom + + + label9 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 6 + + + Commentaires + + + label8 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 7 + + + nameBox + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 8 + + + commentsBox + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 9 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Service + + + 1 + + + Service + + + Service + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + Transports autorisés + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + allowedTransports + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 1 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Transports + + + 0 + + + Transports + + + Transports + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + cacheL2ServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 0 + + + Services de garder en mémoire cache L2 (Experimental) + + + cacheL2Label + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 1 + + + cacheServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 2 + + + Initial Services disponibles + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 3 + + + Services de garder dans le cache + + + cacheLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 4 + + + initialServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 5 + + + Nombre maximal de services + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 6 + + + maxServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 7 + + + tableLayoutPanel4 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cache + + + 0 + + + Cache + + + Cache + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 2 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABtIBEAbSEQAHIoFQB2KhQAdy0VAHkuFgB3LhsAezAWAHwxFQAyMjIAezIcAH41HQCCNxgAfzYdAIM4 + GwCGOxgAiD4cAIk/GQCJPxsAikEgAIxCHwCHQScAkUYbAItEJgCPRyIARkZGAI5HKwCVTCAAS0tLAJ1T + HgBPT08AkFIwAJ9XIwCTVjUApFsiAJpWNwClXCEAmlg5AJRcPACpYiQAq2MjAKxkIwCXX0AAmVpUALNt + JwCiZlsAqGxOAKZrWACscU4Aw4EsAK52XQDAh0sA0JU+ANGZSQCSkpIAmJiYAJmZmQCdnZ0An5+fAKCg + oACtr64AtbW1ALi4uAC8vLwAvb29ALy/vgC/wsEAw8PDAMTExADCxcQAw8XEAMXFxQDExsUAxMfGAMfH + xwDFyMcAyMjIAMnJyQDKysoAzMzMAMvNzADMzc0Azc3NAM7OzgDPz88Az9DQANDR0QDR0dEA0tLSANPT + 0wDV1dUA1tbWANra2gDb29sA3NzcAN3d3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYGBgYGAJYBlgHmAcYGBgYGBgYGA2OTc7Ojg7NmBgYGBgYGBgYE9XQ0NXUkRgYGBgYGBgYExN + T1RUUk5gYGBgYGBgYGBgVFtYWVtTTWBgYGBgYGBgP1peXF1dU2BgYGBgYGBgYGBHXF9ZWUpgYGBgYGBg + YGA5PlZVREA9YGBgYGBgYGBgYGBgPGBgYGBgYGBgYGBQYElgRUtIYEJgQVFgYGBgYGBgYGBgYGBgYGBG + YGBgYCZgYGBgIR9gYGAjKhFgYBASB2BgDRQTBWBgDhgXAxUnJA8BYBgpHQgAYCAoGwpgNSwMYGAwNCIE + YGAzMRYGKzIaYGBgLS4LYGBgLyUCYPqvAADwDwAA+A8AAPAfAAD4DwAA8B8AAPgfAADwHwAA/v8AANRT + AAD/+wAA3nEAAIwwAAAEEAAAjDAAABxxAAA= + + + + New Deployed Service + + + DeployedServiceForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedServiceForm.resx b/client/administration/UdsAdmin/forms/DeployedServiceForm.resx new file mode 100644 index 000000000..f5082d683 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedServiceForm.resx @@ -0,0 +1,1015 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Bottom, Left, Right + + + + True + + + + 3, 144 + + + 165, 17 + + + 2 + + + Publish automatically on save + + + publishOnSave + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Service + + + 0 + + + Top, Left, Right + + + 2 + + + Left + + + True + + + NoControl + + + 3, 106 + + + 67, 13 + + + 3 + + + OS Manager + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Left + + + True + + + 3, 73 + + + 70, 13 + + + 1 + + + Base Service + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Fill + + + 79, 99 + + + 248, 21 + + + 5 + + + osManagerCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Fill + + + 79, 67 + + + 248, 21 + + + 3 + + + baseServiceCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + Left + + + True + + + NoControl + + + 3, 9 + + + 35, 13 + + + 9 + + + Name + + + label9 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + Left + + + True + + + NoControl + + + 3, 41 + + + 56, 13 + + + 10 + + + Comments + + + label8 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + Fill + + + 79, 3 + + + 128 + + + 248, 20 + + + 1 + + + nameBox + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 6 + + + Fill + + + 79, 35 + + + 256 + + + 248, 20 + + + 2 + + + commentsBox + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 7 + + + 6, 6 + + + 3 + + + 330, 129 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Service + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="osManagerCombo" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="baseServiceCombo" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label9" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label8" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="nameBox" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="commentsBox" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,AutoSize,0" /><Rows Styles="Percent,25,Percent,25,Percent,25,Percent,25" /></TableLayoutSettings> + + + 4, 23 + + + 3, 3, 3, 3 + + + 342, 197 + + + 0 + + + Service + + + Service + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + Top, Bottom, Left, Right + + + 1 + + + Left + + + True + + + 3, 5 + + + 97, 13 + + + 2 + + + Allowed Transports + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + Fill + + + 3, 27 + + + 512, 154 + + + 9 + + + allowedTransports + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 1 + + + 7, 7 + + + 2 + + + 518, 184 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Transports + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label4" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="allowedTransports" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100,Absolute,20" /><Rows Styles="Absolute,24,Percent,100" /></TableLayoutSettings> + + + 4, 23 + + + 3, 3, 3, 3 + + + 440, 197 + + + 1 + + + Transports + + + Transports + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + Top, Left, Right + + + 2 + + + Fill + + + 224, 72 + + + 3, 6, 3, 3 + + + 201, 20 + + + 12 + + + cacheL2ServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 0 + + + Left + + + True + + + NoControl + + + 3, 76 + + + 215, 13 + + + 3 + + + Services to keep in L2 cache (Experimental) + + + cacheL2Label + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 1 + + + Fill + + + 224, 39 + + + 3, 6, 3, 3 + + + 201, 20 + + + 11 + + + cacheServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 2 + + + Left + + + True + + + NoControl + + + 3, 10 + + + 126, 13 + + + 1 + + + Initial Services Availables + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 3 + + + Left + + + True + + + NoControl + + + 3, 43 + + + 131, 13 + + + 2 + + + Services to keep in cache + + + cacheLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 4 + + + Fill + + + 224, 6 + + + 3, 6, 3, 3 + + + 201, 20 + + + 10 + + + initialServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 5 + + + Left + + + True + + + NoControl + + + 3, 110 + + + 143, 13 + + + 3 + + + Maximum number of services + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 6 + + + Fill + + + 224, 105 + + + 3, 6, 3, 3 + + + 201, 20 + + + 12 + + + maxServicesBox + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel4 + + + 7 + + + 6, 6 + + + 4 + + + 428, 135 + + + 2 + + + tableLayoutPanel4 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cache + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="cacheL2ServicesBox" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="cacheL2Label" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheServicesBox" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label5" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cacheLabel" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="initialServicesBox" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label7" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="maxServicesBox" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100" /><Rows Styles="Percent,25,Percent,25,Percent,25,Percent,25" /></TableLayoutSettings> + + + 4, 23 + + + 3, 3, 3, 3 + + + 440, 197 + + + 2 + + + Cache + + + Cache + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 2 + + + 12, 12 + + + 448, 224 + + + 6 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 135, 23 + + + 7 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 332, 7 + + + 137, 23 + + + 8 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Bottom + + + 0, 242 + + + 1 + + + 472, 33 + + + 6 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 472, 275 + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABtIBEAbSEQAHIoFQB2KhQAdy0VAHkuFgB3LhsAezAWAHwxFQAyMjIAezIcAH41HQCCNxgAfzYdAIM4 + GwCGOxgAiD4cAIk/GQCJPxsAikEgAIxCHwCHQScAkUYbAItEJgCPRyIARkZGAI5HKwCVTCAAS0tLAJ1T + HgBPT08AkFIwAJ9XIwCTVjUApFsiAJpWNwClXCEAmlg5AJRcPACpYiQAq2MjAKxkIwCXX0AAmVpUALNt + JwCiZlsAqGxOAKZrWACscU4Aw4EsAK52XQDAh0sA0JU+ANGZSQCSkpIAmJiYAJmZmQCdnZ0An5+fAKCg + oACtr64AtbW1ALi4uAC8vLwAvb29ALy/vgC/wsEAw8PDAMTExADCxcQAw8XEAMXFxQDExsUAxMfGAMfH + xwDFyMcAyMjIAMnJyQDKysoAzMzMAMvNzADMzc0Azc3NAM7OzgDPz88Az9DQANDR0QDR0dEA0tLSANPT + 0wDV1dUA1tbWANra2gDb29sA3NzcAN3d3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYGBgYGAJYBlgHmAcYGBgYGBgYGA2OTc7Ojg7NmBgYGBgYGBgYE9XQ0NXUkRgYGBgYGBgYExN + T1RUUk5gYGBgYGBgYGBgVFtYWVtTTWBgYGBgYGBgP1peXF1dU2BgYGBgYGBgYGBHXF9ZWUpgYGBgYGBg + YGA5PlZVREA9YGBgYGBgYGBgYGBgPGBgYGBgYGBgYGBQYElgRUtIYEJgQVFgYGBgYGBgYGBgYGBgYGBG + YGBgYCZgYGBgIR9gYGAjKhFgYBASB2BgDRQTBWBgDhgXAxUnJA8BYBgpHQgAYCAoGwpgNSwMYGAwNCIE + YGAzMRYGKzIaYGBgLS4LYGBgLyUCYPqvAADwDwAA+A8AAPAfAAD4DwAA8B8AAPgfAADwHwAA/v8AANRT + AAD/+wAA3nEAAIwwAAAEEAAAjDAAABxxAAA= + + + + CenterParent + + + New Deployed Service + + + DeployedServiceForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedTransportForm.Designer.cs b/client/administration/UdsAdmin/forms/DeployedTransportForm.Designer.cs new file mode 100644 index 000000000..c113b2400 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedTransportForm.Designer.cs @@ -0,0 +1,108 @@ +namespace UdsAdmin.forms +{ + partial class DeployedTransportForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployedTransportForm)); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.transCombo = new System.Windows.Forms.ComboBox(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.transCombo, 1, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // transCombo + // + this.transCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.transCombo.FormattingEnabled = true; + resources.ApplyResources(this.transCombo, "transCombo"); + this.transCombo.Name = "transCombo"; + // + // DeployedTransportForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.tableLayoutPanel2); + this.Name = "DeployedTransportForm"; + this.Load += new System.EventHandler(this.DeployedTransportForm_Load); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox transCombo; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedTransportForm.cs b/client/administration/UdsAdmin/forms/DeployedTransportForm.cs new file mode 100644 index 000000000..03f9d6371 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedTransportForm.cs @@ -0,0 +1,83 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class DeployedTransportForm : Form + { + private xmlrpc.DeployedService _ds; + + public DeployedTransportForm(xmlrpc.DeployedService ds) + { + _ds = ds; + InitializeComponent(); + Text = Strings.titleAssignNewGroup; + } + + private void DeployedTransportForm_Load(object sender, EventArgs e) + { + xmlrpc.Transport[] trans = xmlrpc.UdsAdminService.GetTransports(); + transCombo.Items.AddRange(trans); + + if (trans.Length > 0) + { + transCombo.SelectedIndex = 0; + } + else + { + MessageBox.Show(Strings.error, Strings.transports, MessageBoxButtons.OK, MessageBoxIcon.Error); + Close(); + } + + } + + private void accept_Click(object sender, EventArgs e) + { + if (transCombo.SelectedItem != null) + { + xmlrpc.Transport trans = (xmlrpc.Transport)transCombo.SelectedItem; + xmlrpc.UdsAdminService.AssignTransportToDeployedService(_ds.id, trans.id); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + else + gui.UserNotifier.notifyError(Strings.transportRequired); + + } + + } +} diff --git a/client/administration/UdsAdmin/forms/DeployedTransportForm.de.resx b/client/administration/UdsAdmin/forms/DeployedTransportForm.de.resx new file mode 100644 index 000000000..5e41acdb4 --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedTransportForm.de.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + groupBox + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Assign new group + + + DeployedGroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Verkehr + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedTransportForm.es.resx b/client/administration/UdsAdmin/forms/DeployedTransportForm.es.resx new file mode 100644 index 000000000..decb87edc --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedTransportForm.es.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + groupBox + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Assign new group + + + DeployedGroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Transporte + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedTransportForm.fr.resx b/client/administration/UdsAdmin/forms/DeployedTransportForm.fr.resx new file mode 100644 index 000000000..66b8fbffe --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedTransportForm.fr.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + groupBox + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Assign new group + + + DeployedGroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Transport + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/DeployedTransportForm.resx b/client/administration/UdsAdmin/forms/DeployedTransportForm.resx new file mode 100644 index 000000000..4a5bac44f --- /dev/null +++ b/client/administration/UdsAdmin/forms/DeployedTransportForm.resx @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 101, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 253, 7 + + + 103, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 67 + + + 1 + + + 359, 33 + + + 7 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + 2 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 52, 13 + + + 1 + + + Transport + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + 91, 3 + + + 240, 21 + + + 2 + + + transCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 13, 13 + + + 1 + + + 334, 32 + + + 8 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="transCombo" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,26,49842,Percent,73,50158" /><Rows Styles="Percent,50,Absolute,32" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 359, 100 + + + CenterParent + + + Assign new transport + + + DeployedTransportForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/FileDownloader.Designer.cs b/client/administration/UdsAdmin/forms/FileDownloader.Designer.cs new file mode 100644 index 000000000..282e9f4a4 --- /dev/null +++ b/client/administration/UdsAdmin/forms/FileDownloader.Designer.cs @@ -0,0 +1,92 @@ +namespace UdsAdmin.forms +{ + partial class FileDownloader + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FileDownloader)); + this.progress = new System.Windows.Forms.ProgressBar(); + this.info = new System.Windows.Forms.Label(); + this.cancel = new System.Windows.Forms.Button(); + this.bgTask = new System.ComponentModel.BackgroundWorker(); + this.install = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // progress + // + resources.ApplyResources(this.progress, "progress"); + this.progress.Name = "progress"; + this.progress.Style = System.Windows.Forms.ProgressBarStyle.Continuous; + // + // info + // + resources.ApplyResources(this.info, "info"); + this.info.Name = "info"; + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // bgTask + // + this.bgTask.WorkerSupportsCancellation = true; + this.bgTask.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bgTask_DoWork); + // + // install + // + resources.ApplyResources(this.install, "install"); + this.install.Name = "install"; + this.install.UseVisualStyleBackColor = true; + this.install.Click += new System.EventHandler(this.install_Click); + // + // FileDownloader + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.install); + this.Controls.Add(this.cancel); + this.Controls.Add(this.info); + this.Controls.Add(this.progress); + this.Name = "FileDownloader"; + this.Load += new System.EventHandler(this.FileDownloader_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ProgressBar progress; + private System.Windows.Forms.Label info; + private System.Windows.Forms.Button cancel; + private System.ComponentModel.BackgroundWorker bgTask; + private System.Windows.Forms.Button install; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/FileDownloader.cs b/client/administration/UdsAdmin/forms/FileDownloader.cs new file mode 100644 index 000000000..b959d40a3 --- /dev/null +++ b/client/administration/UdsAdmin/forms/FileDownloader.cs @@ -0,0 +1,142 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Net; +using System.Diagnostics; + +namespace UdsAdmin.forms +{ + public partial class FileDownloader : Form + { + private string _url; + private string _outFile; + private const int BUFFER_SIZE = 8192; + delegate void UpdateDialogCallback(int readed, int length, double speed); + + public FileDownloader(string url) + { + _url = url; + _outFile = System.IO.Path.GetTempPath() + "UDSAdminSetup.exe"; + InitializeComponent(); + Text = Strings.titleDownloader; + } + + private void FileDownloader_Load(object sender, EventArgs e) + { + install.Enabled = false; + bgTask.RunWorkerAsync(); + } + + private void UpdateProgress(int readed, int length, double speed) + { + progress.Maximum = length; + progress.Value = readed; + info.Text = String.Format(Strings.downloadInfo, readed / 1024, length / 1024, speed / 1024); + if (readed == length) + { + install.Enabled = true; + cancel.Text = Strings.exit; + } + } + + private void bgTask_DoWork(object sender, DoWorkEventArgs e) + { + HttpWebRequest req; + HttpWebResponse res; + try + { + req = (HttpWebRequest)WebRequest.Create(_url); + res = (HttpWebResponse)req.GetResponse(); + + int length = (int)res.ContentLength; + + System.IO.FileStream of = new System.IO.FileStream(_outFile, System.IO.FileMode.Create); + int nRead = 0; + + Stopwatch timer = new Stopwatch(); + + byte[] buffer = new byte[BUFFER_SIZE]; + UpdateDialogCallback d = new UpdateDialogCallback(UpdateProgress); + + timer.Start(); + while (true) + { + if (bgTask.CancellationPending) + break; + + int bytesReaded = res.GetResponseStream().Read(buffer, 0, BUFFER_SIZE); + + nRead += bytesReaded; + + double speed = 1000.0 * ((double)nRead) / timer.ElapsedMilliseconds; + + this.Invoke(d, new object[] { nRead, length, speed }); + + if (bytesReaded == 0) + break; + + of.Write(buffer, 0, bytesReaded); + + } + timer.Stop(); + + res.GetResponseStream().Close(); + of.Close(); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, Strings.error, MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + private void install_Click(object sender, EventArgs e) + { + Process install = new Process(); + install.StartInfo.FileName = _outFile; + install.Start(); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + + private void cancel_Click(object sender, EventArgs e) + { + bgTask.CancelAsync(); + DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + + } +} diff --git a/client/administration/UdsAdmin/forms/FileDownloader.de.resx b/client/administration/UdsAdmin/forms/FileDownloader.de.resx new file mode 100644 index 000000000..f136f85a3 --- /dev/null +++ b/client/administration/UdsAdmin/forms/FileDownloader.de.resx @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 39, 13 + + + Label1 + + + Abbrechen + + + Installieren + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/FileDownloader.es.resx b/client/administration/UdsAdmin/forms/FileDownloader.es.resx new file mode 100644 index 000000000..8acd0b4d2 --- /dev/null +++ b/client/administration/UdsAdmin/forms/FileDownloader.es.resx @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 12, 12 + + + 331, 23 + + + + 0 + + + progress + + + System.Windows.Forms.ProgressBar, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Top, Left, Right + + + True + + + 12, 38 + + + 35, 13 + + + 1 + + + Label1 + + + info + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 268, 54 + + + 75, 23 + + + 4 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 17, 17 + + + 15, 54 + + + 75, 23 + + + 5 + + + Instalar + + + install + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + True + + + 355, 89 + + + Downloader + + + bgTask + + + System.ComponentModel.BackgroundWorker, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + FileDownloader + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/FileDownloader.fr.resx b/client/administration/UdsAdmin/forms/FileDownloader.fr.resx new file mode 100644 index 000000000..d033b96fc --- /dev/null +++ b/client/administration/UdsAdmin/forms/FileDownloader.fr.resx @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 12, 12 + + + 331, 23 + + + + 0 + + + progress + + + System.Windows.Forms.ProgressBar, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Top, Left, Right + + + True + + + 12, 38 + + + 35, 13 + + + 1 + + + Label1 + + + info + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 268, 54 + + + 75, 23 + + + 4 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 17, 17 + + + 15, 54 + + + 75, 23 + + + 5 + + + Installer + + + install + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + True + + + 355, 89 + + + Downloader + + + bgTask + + + System.ComponentModel.BackgroundWorker, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + FileDownloader + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/FileDownloader.resx b/client/administration/UdsAdmin/forms/FileDownloader.resx new file mode 100644 index 000000000..a7da8f7a4 --- /dev/null +++ b/client/administration/UdsAdmin/forms/FileDownloader.resx @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 12, 12 + + + 331, 23 + + + + 0 + + + progress + + + System.Windows.Forms.ProgressBar, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Top, Left, Right + + + True + + + 12, 38 + + + 35, 13 + + + 1 + + + label1 + + + info + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 268, 54 + + + 75, 23 + + + 4 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 17, 17 + + + 15, 54 + + + 75, 23 + + + 5 + + + Install + + + install + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + True + + + 355, 89 + + + CenterParent + + + Downloader + + + bgTask + + + System.ComponentModel.BackgroundWorker, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + FileDownloader + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/GroupForm.Designer.cs b/client/administration/UdsAdmin/forms/GroupForm.Designer.cs new file mode 100644 index 000000000..6310134d2 --- /dev/null +++ b/client/administration/UdsAdmin/forms/GroupForm.Designer.cs @@ -0,0 +1,175 @@ +namespace UdsAdmin.forms +{ + partial class GroupForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GroupForm)); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.comments = new System.Windows.Forms.TextBox(); + this.searchButton = new System.Windows.Forms.Button(); + this.groupLabel = new System.Windows.Forms.Label(); + this.active = new System.Windows.Forms.CheckBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.check = new System.Windows.Forms.Button(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.comments, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.searchButton, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.groupLabel, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.active, 1, 2); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // comments + // + this.tableLayoutPanel1.SetColumnSpan(this.comments, 2); + resources.ApplyResources(this.comments, "comments"); + this.comments.Name = "comments"; + // + // searchButton + // + this.searchButton.Image = global::UdsAdmin.Images.find16; + resources.ApplyResources(this.searchButton, "searchButton"); + this.searchButton.Name = "searchButton"; + this.searchButton.UseVisualStyleBackColor = true; + this.searchButton.Click += new System.EventHandler(this.searchButton_Click); + // + // groupLabel + // + resources.ApplyResources(this.groupLabel, "groupLabel"); + this.groupLabel.Name = "groupLabel"; + // + // active + // + resources.ApplyResources(this.active, "active"); + this.active.Checked = true; + this.active.CheckState = System.Windows.Forms.CheckState.Checked; + this.tableLayoutPanel1.SetColumnSpan(this.active, 2); + this.active.Name = "active"; + this.active.UseVisualStyleBackColor = true; + this.active.CheckedChanged += new System.EventHandler(this.active_CheckedChanged); + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // tableLayoutPanel3 + // + resources.ApplyResources(this.tableLayoutPanel3, "tableLayoutPanel3"); + this.tableLayoutPanel3.Controls.Add(this.check, 1, 0); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + // + // check + // + resources.ApplyResources(this.check, "check"); + this.check.Name = "check"; + this.check.UseVisualStyleBackColor = true; + // + // GroupForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel2); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "GroupForm"; + this.Load += new System.EventHandler(this.GroupForm_Load); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label groupLabel; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button searchButton; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Button check; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.CheckBox active; + public System.Windows.Forms.TextBox name; + public System.Windows.Forms.TextBox comments; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/GroupForm.cs b/client/administration/UdsAdmin/forms/GroupForm.cs new file mode 100644 index 000000000..ea900236b --- /dev/null +++ b/client/administration/UdsAdmin/forms/GroupForm.cs @@ -0,0 +1,109 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class GroupForm : Form + { + private xmlrpc.Authenticator _auth; + private xmlrpc.AuthenticatorType _authType; + + public GroupForm(xmlrpc.Authenticator auth, xmlrpc.AuthenticatorType authType) + { + _auth = auth; + _authType = authType; + InitializeComponent(); + Text = Strings.titleGroup; + } + + private void GroupForm_Load(object sender, EventArgs e) + { + active.Checked = true; + active.Text = Strings.active; + if (_authType.canSearchGroups) + { + searchButton.Enabled = true; + check.Visible = true; + } + else + { + searchButton.Enabled = false; + check.Visible = false; + } + groupLabel.Text = _authType.groupNameLabel; + //this.Location = System.Windows.Forms.Cursor.Position; + } + + private void searchButton_Click(object sender, EventArgs e) + { + SearchForm form = new SearchForm(SearchForm.Type.groupSearch, _auth, name.Text); + if (form.ShowDialog() == System.Windows.Forms.DialogResult.Yes) + name.Text = form.selection; + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + xmlrpc.Group grp = new xmlrpc.Group(); + grp.idParent = _auth.id; grp.id = ""; grp.name = name.Text; grp.comments = comments.Text; grp.active = active.Checked; + try + { + xmlrpc.UdsAdminService.CreateGroup(grp); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void active_CheckedChanged(object sender, EventArgs e) + { + active.Text = active.Checked ? Strings.active : Strings.inactive; + } + + private void cancel_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + } +} diff --git a/client/administration/UdsAdmin/forms/GroupForm.de.resx b/client/administration/UdsAdmin/forms/GroupForm.de.resx new file mode 100644 index 000000000..c4266474e --- /dev/null +++ b/client/administration/UdsAdmin/forms/GroupForm.de.resx @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD+/v8A////AP///wD+//4A////AP///wD///8A////AP// + /wD///8A9PT7AL295/+4uOr/zMzx/+rq+sz///8A////AP///wD///8A////AP///wDj9uf/vejG/6nc + sf+327v/8vjyAF9fv/8oKLb/S0vL/29v2v+NjeT/k5Tk/7287f/6+/4A+v36AKrhtP9p0oT/XNF8/zzC + W/8dqjb/DY8d/0CVRv9eXrz/ICCx/1ZWz/99fd//mpro/3l32P9ITND/////AP///wAdrTr/RsNi/2jY + iP9Jymn/J7FB/weHFv9CkUf/tbXi/2Bgyv8xMb7/Xl7S/3Bw2P9RWtr/VU+4/8yajv/LoYz/OZxA/zK3 + U/9Gv1//LbZJ/wmXHf9Grlf/pc6n//7+/wCpqeD/Y2LR/ywtuf81MLH/j3Gf/8aCYf/ei0D/4YZH/7yQ + S/9ulEH/F4oe/wqRHf82tVD/n9Gj//7+/gD9/f4A////AMzM7f8vMLT/QDy4/3ZVif/VgD3/0n5C/9F+ + QP/RcDP/XXok/yKVLP8UiyL/xOPI/////wD9/f0A/Pz+APz8/gDh4fP/Kyqy/yssvP8IDpz/nEMu/8JW + Fv/AThj/mEwV/wFoDP8Kkx3/D4od/97s3//8/fwA/P38APv7/QD///8AlpbO/xgYoP8RD5L/AgSQ/4g/ + Sv/AVBX/uEEZ/4BgNv8CWQL/BWEG/wRyDf+SuJP/////APv8+wD6+vwA////AH9/vP8hIab/Liyh/42W + 8/+eboH/rTgF/8VqTf/EyLv/a9mI/x54JP8MeRb/eqF9/////wD6+/oA/f39AP///wDU1Nr/enrH/y8w + pv+mpMT/r2la/6s3CP/Wdjf/5NbJ/5C3nf8dfib/Za9x/9PW0/////8A/f3+AP///wD///8A////AO3t + 82bZ2d//8+/v/5spE/+SIQT/t00Y/+qzkv////8A1dXV/+7x7kT///8A////AP///wD///8A////AP7+ + /gD///8A////APHf2/+gMg7/kyUL/5UiA//JfmH///7+AP///wD///8A/v7+AP///wD///8A////AP// + /wD///8A/f3+AP78/AD8+fkAzolo/71fMf+WIQL/3ruy///+/gD8+voA/v7+AP///wD///8A////AP// + /wD///8A////AP///wD//v4A////APr29QDbtq7/58/K/////wD//v4A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD//v4A////AP///wD///8A////AP// + /wD///8A//8AAIfhAAABgAAAAYAAAAAAAACAAQAAwAMAAMADAADAAwAAwAMAAMADAADwLwAA+D8AAPw/ + AAD+fwAA//8AAA== + + + + Staat + + + Kommentare + + + Gruppenname + + + Aktive + + + Akzeptieren + + + Abbrechen + + + Überprüfen Sie Namen + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/GroupForm.es.resx b/client/administration/UdsAdmin/forms/GroupForm.es.resx new file mode 100644 index 000000000..1bb71712b --- /dev/null +++ b/client/administration/UdsAdmin/forms/GroupForm.es.resx @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Estado + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Comentarios + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + Nombre del grupo + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Activo + + + active + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Nombre del cheque + + + check + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD+/v8A////AP///wD+//4A////AP///wD///8A////AP// + /wD///8A9PT7AL295/+4uOr/zMzx/+rq+sz///8A////AP///wD///8A////AP///wDj9uf/vejG/6nc + sf+327v/8vjyAF9fv/8oKLb/S0vL/29v2v+NjeT/k5Tk/7287f/6+/4A+v36AKrhtP9p0oT/XNF8/zzC + W/8dqjb/DY8d/0CVRv9eXrz/ICCx/1ZWz/99fd//mpro/3l32P9ITND/////AP///wAdrTr/RsNi/2jY + iP9Jymn/J7FB/weHFv9CkUf/tbXi/2Bgyv8xMb7/Xl7S/3Bw2P9RWtr/VU+4/8yajv/LoYz/OZxA/zK3 + U/9Gv1//LbZJ/wmXHf9Grlf/pc6n//7+/wCpqeD/Y2LR/ywtuf81MLH/j3Gf/8aCYf/ei0D/4YZH/7yQ + S/9ulEH/F4oe/wqRHf82tVD/n9Gj//7+/gD9/f4A////AMzM7f8vMLT/QDy4/3ZVif/VgD3/0n5C/9F+ + QP/RcDP/XXok/yKVLP8UiyL/xOPI/////wD9/f0A/Pz+APz8/gDh4fP/Kyqy/yssvP8IDpz/nEMu/8JW + Fv/AThj/mEwV/wFoDP8Kkx3/D4od/97s3//8/fwA/P38APv7/QD///8AlpbO/xgYoP8RD5L/AgSQ/4g/ + Sv/AVBX/uEEZ/4BgNv8CWQL/BWEG/wRyDf+SuJP/////APv8+wD6+vwA////AH9/vP8hIab/Liyh/42W + 8/+eboH/rTgF/8VqTf/EyLv/a9mI/x54JP8MeRb/eqF9/////wD6+/oA/f39AP///wDU1Nr/enrH/y8w + pv+mpMT/r2la/6s3CP/Wdjf/5NbJ/5C3nf8dfib/Za9x/9PW0/////8A/f3+AP///wD///8A////AO3t + 82bZ2d//8+/v/5spE/+SIQT/t00Y/+qzkv////8A1dXV/+7x7kT///8A////AP///wD///8A////AP7+ + /gD///8A////APHf2/+gMg7/kyUL/5UiA//JfmH///7+AP///wD///8A/v7+AP///wD///8A////AP// + /wD///8A/f3+AP78/AD8+fkAzolo/71fMf+WIQL/3ruy///+/gD8+voA/v7+AP///wD///8A////AP// + /wD///8A////AP///wD//v4A////APr29QDbtq7/58/K/////wD//v4A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD//v4A////AP///wD///8A////AP// + /wD///8A//8AAIfhAAABgAAAAYAAAAAAAACAAQAAwAMAAMADAADAAwAAwAMAAMADAADwLwAA+D8AAPw/ + AAD+fwAA//8AAA== + + + + Groups + + + GroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/GroupForm.fr.resx b/client/administration/UdsAdmin/forms/GroupForm.fr.resx new file mode 100644 index 000000000..fe40ab76d --- /dev/null +++ b/client/administration/UdsAdmin/forms/GroupForm.fr.resx @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + État + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Commentaires + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + Nom du groupe + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Active + + + active + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Vérifiez le nom + + + check + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD+/v8A////AP///wD+//4A////AP///wD///8A////AP// + /wD///8A9PT7AL295/+4uOr/zMzx/+rq+sz///8A////AP///wD///8A////AP///wDj9uf/vejG/6nc + sf+327v/8vjyAF9fv/8oKLb/S0vL/29v2v+NjeT/k5Tk/7287f/6+/4A+v36AKrhtP9p0oT/XNF8/zzC + W/8dqjb/DY8d/0CVRv9eXrz/ICCx/1ZWz/99fd//mpro/3l32P9ITND/////AP///wAdrTr/RsNi/2jY + iP9Jymn/J7FB/weHFv9CkUf/tbXi/2Bgyv8xMb7/Xl7S/3Bw2P9RWtr/VU+4/8yajv/LoYz/OZxA/zK3 + U/9Gv1//LbZJ/wmXHf9Grlf/pc6n//7+/wCpqeD/Y2LR/ywtuf81MLH/j3Gf/8aCYf/ei0D/4YZH/7yQ + S/9ulEH/F4oe/wqRHf82tVD/n9Gj//7+/gD9/f4A////AMzM7f8vMLT/QDy4/3ZVif/VgD3/0n5C/9F+ + QP/RcDP/XXok/yKVLP8UiyL/xOPI/////wD9/f0A/Pz+APz8/gDh4fP/Kyqy/yssvP8IDpz/nEMu/8JW + Fv/AThj/mEwV/wFoDP8Kkx3/D4od/97s3//8/fwA/P38APv7/QD///8AlpbO/xgYoP8RD5L/AgSQ/4g/ + Sv/AVBX/uEEZ/4BgNv8CWQL/BWEG/wRyDf+SuJP/////APv8+wD6+vwA////AH9/vP8hIab/Liyh/42W + 8/+eboH/rTgF/8VqTf/EyLv/a9mI/x54JP8MeRb/eqF9/////wD6+/oA/f39AP///wDU1Nr/enrH/y8w + pv+mpMT/r2la/6s3CP/Wdjf/5NbJ/5C3nf8dfib/Za9x/9PW0/////8A/f3+AP///wD///8A////AO3t + 82bZ2d//8+/v/5spE/+SIQT/t00Y/+qzkv////8A1dXV/+7x7kT///8A////AP///wD///8A////AP7+ + /gD///8A////APHf2/+gMg7/kyUL/5UiA//JfmH///7+AP///wD///8A/v7+AP///wD///8A////AP// + /wD///8A/f3+AP78/AD8+fkAzolo/71fMf+WIQL/3ruy///+/gD8+voA/v7+AP///wD///8A////AP// + /wD///8A////AP///wD//v4A////APr29QDbtq7/58/K/////wD//v4A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD//v4A////AP///wD///8A////AP// + /wD///8A//8AAIfhAAABgAAAAYAAAAAAAACAAQAAwAMAAMADAADAAwAAwAMAAMADAADwLwAA+D8AAPw/ + AAD+fwAA//8AAA== + + + + Groups + + + GroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/GroupForm.resx b/client/administration/UdsAdmin/forms/GroupForm.resx new file mode 100644 index 000000000..3379184d9 --- /dev/null +++ b/client/administration/UdsAdmin/forms/GroupForm.resx @@ -0,0 +1,543 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 3 + + + True + + + + 3, 56 + + + 3, 6, 3, 0 + + + 32, 13 + + + 7 + + + State + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 31 + + + 3, 6, 3, 0 + + + 56, 13 + + + 1 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Fill + + + 71, 3 + + + 231, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Fill + + + 71, 28 + + + 299, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + 308, 3 + + + 30, 19 + + + 4 + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + True + + + 3, 6 + + + 3, 6, 3, 0 + + + 39, 19 + + + 0 + + + Group Name + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Button + + + True + + + Fill + + + 71, 56 + + + 3, 6, 3, 3 + + + 299, 26 + + + 6 + + + Active + + + MiddleCenter + + + active + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + 12, 12 + + + 3 + + + 373, 85 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="searchButton" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="active" Row="2" RowSpan="1" Column="1" ColumnSpan="2" /></Controls><Columns Styles="Percent,22,52252,Percent,77,47748,Absolute,67" /><Rows Styles="Percent,30,Percent,30,Percent,40" /></TableLayoutSettings> + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 113, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 280, 7 + + + 114, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + 3 + + + Fill + + + 33, 3 + + + 85, 21 + + + 0 + + + Check name + + + check + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + 122, 3 + + + 1 + + + 152, 27 + + + 2 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="check" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,20,Percent,60,Percent,20" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Bottom + + + 0, 131 + + + 1 + + + 397, 33 + + + 6 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="tableLayoutPanel3" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 397, 164 + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD+/v8A////AP///wD+//4A////AP///wD///8A////AP// + /wD///8A9PT7AL295/+4uOr/zMzx/+rq+sz///8A////AP///wD///8A////AP///wDj9uf/vejG/6nc + sf+327v/8vjyAF9fv/8oKLb/S0vL/29v2v+NjeT/k5Tk/7287f/6+/4A+v36AKrhtP9p0oT/XNF8/zzC + W/8dqjb/DY8d/0CVRv9eXrz/ICCx/1ZWz/99fd//mpro/3l32P9ITND/////AP///wAdrTr/RsNi/2jY + iP9Jymn/J7FB/weHFv9CkUf/tbXi/2Bgyv8xMb7/Xl7S/3Bw2P9RWtr/VU+4/8yajv/LoYz/OZxA/zK3 + U/9Gv1//LbZJ/wmXHf9Grlf/pc6n//7+/wCpqeD/Y2LR/ywtuf81MLH/j3Gf/8aCYf/ei0D/4YZH/7yQ + S/9ulEH/F4oe/wqRHf82tVD/n9Gj//7+/gD9/f4A////AMzM7f8vMLT/QDy4/3ZVif/VgD3/0n5C/9F+ + QP/RcDP/XXok/yKVLP8UiyL/xOPI/////wD9/f0A/Pz+APz8/gDh4fP/Kyqy/yssvP8IDpz/nEMu/8JW + Fv/AThj/mEwV/wFoDP8Kkx3/D4od/97s3//8/fwA/P38APv7/QD///8AlpbO/xgYoP8RD5L/AgSQ/4g/ + Sv/AVBX/uEEZ/4BgNv8CWQL/BWEG/wRyDf+SuJP/////APv8+wD6+vwA////AH9/vP8hIab/Liyh/42W + 8/+eboH/rTgF/8VqTf/EyLv/a9mI/x54JP8MeRb/eqF9/////wD6+/oA/f39AP///wDU1Nr/enrH/y8w + pv+mpMT/r2la/6s3CP/Wdjf/5NbJ/5C3nf8dfib/Za9x/9PW0/////8A/f3+AP///wD///8A////AO3t + 82bZ2d//8+/v/5spE/+SIQT/t00Y/+qzkv////8A1dXV/+7x7kT///8A////AP///wD///8A////AP7+ + /gD///8A////APHf2/+gMg7/kyUL/5UiA//JfmH///7+AP///wD///8A/v7+AP///wD///8A////AP// + /wD///8A/f3+AP78/AD8+fkAzolo/71fMf+WIQL/3ruy///+/gD8+voA/v7+AP///wD///8A////AP// + /wD///8A////AP///wD//v4A////APr29QDbtq7/58/K/////wD//v4A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD//v4A////AP///wD///8A////AP// + /wD///8A//8AAIfhAAABgAAAAYAAAAAAAACAAQAAwAMAAMADAADAAwAAwAMAAMADAADwLwAA+D8AAPw/ + AAD+fwAA//8AAA== + + + + CenterParent + + + Groups + + + GroupForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/LoginForm.Designer.cs b/client/administration/UdsAdmin/forms/LoginForm.Designer.cs new file mode 100644 index 000000000..9b414cbb6 --- /dev/null +++ b/client/administration/UdsAdmin/forms/LoginForm.Designer.cs @@ -0,0 +1,157 @@ +namespace UdsAdmin.forms +{ + partial class LoginForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LoginForm)); + this.label1 = new System.Windows.Forms.Label(); + this.serverCombo = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.username = new System.Windows.Forms.TextBox(); + this.passwordText = new System.Windows.Forms.TextBox(); + this.connectButton = new System.Windows.Forms.Button(); + this.exitButton = new System.Windows.Forms.Button(); + this.useSSLCheck = new System.Windows.Forms.CheckBox(); + this.extendButton = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.authenticator = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // serverCombo + // + this.serverCombo.FormattingEnabled = true; + resources.ApplyResources(this.serverCombo, "serverCombo"); + this.serverCombo.Name = "serverCombo"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // username + // + resources.ApplyResources(this.username, "username"); + this.username.Name = "username"; + // + // passwordText + // + resources.ApplyResources(this.passwordText, "passwordText"); + this.passwordText.Name = "passwordText"; + this.passwordText.UseSystemPasswordChar = true; + // + // connectButton + // + resources.ApplyResources(this.connectButton, "connectButton"); + this.connectButton.Name = "connectButton"; + this.connectButton.UseVisualStyleBackColor = true; + this.connectButton.Click += new System.EventHandler(this.button1_Click); + // + // exitButton + // + this.exitButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + resources.ApplyResources(this.exitButton, "exitButton"); + this.exitButton.Name = "exitButton"; + this.exitButton.UseVisualStyleBackColor = true; + this.exitButton.Click += new System.EventHandler(this.exit_Click); + // + // useSSLCheck + // + resources.ApplyResources(this.useSSLCheck, "useSSLCheck"); + this.useSSLCheck.Name = "useSSLCheck"; + this.useSSLCheck.UseVisualStyleBackColor = true; + // + // extendButton + // + resources.ApplyResources(this.extendButton, "extendButton"); + this.extendButton.Name = "extendButton"; + this.extendButton.UseVisualStyleBackColor = true; + this.extendButton.Click += new System.EventHandler(this.button2_Click); + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // authenticator + // + this.authenticator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.authenticator.FormattingEnabled = true; + resources.ApplyResources(this.authenticator, "authenticator"); + this.authenticator.Name = "authenticator"; + // + // LoginForm + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.authenticator); + this.Controls.Add(this.label4); + this.Controls.Add(this.extendButton); + this.Controls.Add(this.useSSLCheck); + this.Controls.Add(this.exitButton); + this.Controls.Add(this.connectButton); + this.Controls.Add(this.passwordText); + this.Controls.Add(this.username); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.serverCombo); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Name = "LoginForm"; + this.Load += new System.EventHandler(this.LoginForm_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox serverCombo; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox username; + private System.Windows.Forms.TextBox passwordText; + private System.Windows.Forms.Button connectButton; + private System.Windows.Forms.Button exitButton; + private System.Windows.Forms.CheckBox useSSLCheck; + private System.Windows.Forms.Button extendButton; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox authenticator; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/LoginForm.cs b/client/administration/UdsAdmin/forms/LoginForm.cs new file mode 100644 index 000000000..207ae37ef --- /dev/null +++ b/client/administration/UdsAdmin/forms/LoginForm.cs @@ -0,0 +1,199 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class LoginForm : Form + { + private const int HEIGHT_REDUCED = 80; + private const int HEIGHT_EXPANDED = 220; + + private string savedAuth = ""; + private string savedUser = ""; + + public LoginForm() + { + InitializeComponent(); + Height = HEIGHT_REDUCED; + Text = Strings.titleLogin; + } + + private void LoginForm_Load(object sender, EventArgs e) + { + LoadFormData(); + AcceptButton = extendButton; + } + + private void exit_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + + private void button1_Click(object sender, EventArgs e) + { + try + { + string auth = ""; + if (authenticator.SelectedItem != null) + auth = ((xmlrpc.SimpleInfo)authenticator.SelectedItem).id; + xmlrpc.UdsAdminService.Login(username.Text, passwordText.Text, auth); + SaveAuthUser(); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + if (ex.FaultCode == xmlrpc.ExceptionExplainer.AUTH_FAILED) + MessageBox.Show(Strings.invalidCredentials, Strings.error, MessageBoxButtons.OK, MessageBoxIcon.Error); + else + gui.UserNotifier.notifyRpcException(ex); + } + catch (exceptions.NewVersionRequiredException ex) + { + if (MessageBox.Show(Strings.newVersionRequired, Strings.downloadQuery, MessageBoxButtons.YesNo, MessageBoxIcon.Hand) == System.Windows.Forms.DialogResult.Yes) + { + string url = "http" + (useSSLCheck.Checked ? "s" : "") + "://" + serverCombo.Text + ex.Url; + (new FileDownloader(url)).ShowDialog(); + } + DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + } + + private void button2_Click(object sender, EventArgs e) + { + if (extendButton.Text == ">>>") + { + try + { + xmlrpc.SimpleInfo[] info = xmlrpc.UdsAdminService.GetAdminAuths(serverCombo.Text, useSSLCheck.Checked); + authenticator.Items.Clear(); + foreach( xmlrpc.SimpleInfo i in info) + { + authenticator.Items.Add(i); + } + if (info.Length > 0) + authenticator.SelectedIndex = 0; + SaveServerData(); + SetAuthUser(); + } + catch (exceptions.CommunicationException) + { + MessageBox.Show(Strings.cantConnect); + return; + } + extendButton.Text = "<<<"; + serverCombo.Enabled = false; + useSSLCheck.Enabled = false; + Height = HEIGHT_EXPANDED; + username.Focus(); + AcceptButton = connectButton; + } + else + { + extendButton.Text = ">>>"; + serverCombo.Enabled = true; + useSSLCheck.Enabled = true; + Height = HEIGHT_REDUCED; + AcceptButton = extendButton; + } + } + + private void SaveServerData() + { + List items = new List(); + bool addNewItem = true; + string value = serverCombo.Text; + foreach (object i in serverCombo.Items) + { + string s = i.ToString(); + items.Add(s); + if (s == value) + addNewItem = false; + } + if (addNewItem) + { + items.Add(value); + serverCombo.Items.Add(value); + } + Application.UserAppDataRegistry.SetValue("server", string.Join(",", items.ToArray())); + Application.UserAppDataRegistry.SetValue("ssl", useSSLCheck.Checked ? "y" : "n"); + } + + private void SetAuthUser() + { + if (savedAuth != "") + { + foreach (xmlrpc.SimpleInfo i in authenticator.Items) + { + if (i.id == savedAuth) + { + authenticator.SelectedItem = i; + break; + } + } + } + if (savedUser != "") + { + username.Text = savedUser; + } + } + + private void SaveAuthUser() + { + savedAuth = ((xmlrpc.SimpleInfo)authenticator.SelectedItem).id; + savedUser = username.Text; + Application.UserAppDataRegistry.SetValue("auth", savedAuth); + Application.UserAppDataRegistry.SetValue("user", savedUser); + } + + private void LoadFormData() + { + string server = (string)Application.UserAppDataRegistry.GetValue("server", ""); + if (server != "") + { + string[] items = server.Split(','); + serverCombo.Items.Clear(); + foreach (string i in items) + serverCombo.Items.Insert(0, i); + } + useSSLCheck.Checked = ((string)Application.UserAppDataRegistry.GetValue("ssl", "n")) == "y"; + savedAuth = (string)Application.UserAppDataRegistry.GetValue("auth", ""); + savedUser = (string)Application.UserAppDataRegistry.GetValue("user", ""); + } + + } +} diff --git a/client/administration/UdsAdmin/forms/LoginForm.de.resx b/client/administration/UdsAdmin/forms/LoginForm.de.resx new file mode 100644 index 000000000..980c8b30e --- /dev/null +++ b/client/administration/UdsAdmin/forms/LoginForm.de.resx @@ -0,0 +1,457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 12, 18 + + + 116, 18 + + + + 0 + + + Server + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 11 + + + 134, 4 + + + 258, 21 + + + 1 + + + serverCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 10 + + + 12, 91 + + + 116, 18 + + + 2 + + + Benutzername + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 9 + + + 12, 117 + + + 116, 18 + + + 3 + + + Passwort + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 8 + + + 134, 88 + + + 258, 20 + + + 4 + + + username + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + 135, 114 + + + 32 + + + 257, 20 + + + 5 + + + passwordText + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + 79, 152 + + + 75, 23 + + + 6 + + + Verbinden + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + 276, 152 + + + 75, 23 + + + 7 + + + Ausfahrt + + + exit + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + True + + + 134, 31 + + + 68, 17 + + + 9 + + + Verwendung SSL + + + useSSLCheck + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 362, 27 + + + 30, 23 + + + 10 + + + >>> + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + + NoControl + + + 12, 64 + + + 116, 18 + + + 11 + + + Authentifikator + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 135, 61 + + + 257, 21 + + + 12 + + + authenticator + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 402, 193 + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeBwAAZRAMAGMQDQBkEA0AZxINAGcUDwBrFhAAbRgMAG8YEQBxGxAAbxsTAHAbEwByGxQAdRwSAHUd + EgByHhUAex8TAH8iFAB3IxcAdiMZAIEkFQCGJRUAkiYOAHclGwCLKREAhyoaAKArDwCGKxsAlCwXAI0t + GAB8LCIAqi4MAJQtGACULxUAey8iAIEvIACQMB4AmjEZAIMxIwCJMiAAmTMZAJU0HQCGNCYAozUaAJ81 + HgCVNiAAiDcmAKU4HACpOBsArDsdALo8FQCrPh8AuEIfALNDIQC5RCEAw0UdAJJEMgDCRiAApEUvALlH + IwCURzAAsUksAMZKIgC8TCMAv00nAM5NIgDDTikAvFIrANRSJADUUyUAvVQtAKJVPADTVigAyFctANBZ + KwDXWSkA3VopAM1cLgDUXiwAz14vAOFgLQDkZjEAwGhCAONrNQDEa0QAxm1HAO1xNgClbGMA6XM6ALdz + WQDvfUAA7X5CAO5/QgDvf0IA7oBDAO+ERQDuhEYA74RGAOqGSgDohksA6YZLAOmITQDpiU0A8IlMAOuK + TwDukFQA6pRbAPGVVwCwjosAzZJ0ANuqkADeycYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAcHBwcHA8cFRwUnAicHBwcHBwBxhOWl1eXlxdUyEAbHBwcBpDa2hkZWZlY2dGH3BwcHAsP2pp + YF9hW1hRSzJvcHBwcFVwSUpIRURBOTYWcHBwcFdZKRwrO0AxLygdcHBwcHBwcHAQM01PNRUOcHBwcHBw + cHBwcBEkOhkEcHBwcHBwcHBwcHBwG0I9cHBwcHBwcHBwcHBwLi03VmJwcHBwcHBwcHBwChcUNFBwcHBw + cHBwcHBwcAYFCCU+THBwcHBwcHBwcHAMAQINIDBwcHBwcHBwcHBwKkcjAwsncHBwcHBwcHBwcBJubQkm + cHBwcHBwcHBwcHBwHjgPE3BwcHBwcPqvAADAAQAAwAMAAMABAADoAwAAwAcAAPgPAAD8HwAA/j8AAPwf + AAD4PwAA+B8AAPgfAAD4HwAA+D8AAPw/AAA= + + + + CenterScreen + + + UDS Administration + + + LoginForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/LoginForm.es.resx b/client/administration/UdsAdmin/forms/LoginForm.es.resx new file mode 100644 index 000000000..27af22c5b --- /dev/null +++ b/client/administration/UdsAdmin/forms/LoginForm.es.resx @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Servidor + + + Usuario + + + Contraseña + + + Conectar + + + Salir + + + + 71, 17 + + + Usar SSL + + + Administración de UDS + + + >>> + + + Autenticador + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/LoginForm.fr.resx b/client/administration/UdsAdmin/forms/LoginForm.fr.resx new file mode 100644 index 000000000..7df1d308a --- /dev/null +++ b/client/administration/UdsAdmin/forms/LoginForm.fr.resx @@ -0,0 +1,457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 12, 18 + + + 116, 18 + + + + 0 + + + Serveur + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 11 + + + 134, 4 + + + 258, 21 + + + 1 + + + serverCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 10 + + + 12, 91 + + + 116, 18 + + + 2 + + + Nom d'utilisateur + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 9 + + + 12, 117 + + + 116, 18 + + + 3 + + + Mot de passe + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 8 + + + 134, 88 + + + 258, 20 + + + 4 + + + username + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + 135, 114 + + + 32 + + + 257, 20 + + + 5 + + + passwordText + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + 79, 152 + + + 75, 23 + + + 6 + + + Se connecter + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + 276, 152 + + + 75, 23 + + + 7 + + + Sortie + + + exit + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + True + + + 134, 31 + + + 68, 17 + + + 9 + + + Utilisation SSL + + + useSSLCheck + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 362, 27 + + + 30, 23 + + + 10 + + + >>> + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + + NoControl + + + 12, 64 + + + 116, 18 + + + 11 + + + Authentificateur + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 135, 61 + + + 257, 21 + + + 12 + + + authenticator + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 402, 193 + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeBwAAZRAMAGMQDQBkEA0AZxINAGcUDwBrFhAAbRgMAG8YEQBxGxAAbxsTAHAbEwByGxQAdRwSAHUd + EgByHhUAex8TAH8iFAB3IxcAdiMZAIEkFQCGJRUAkiYOAHclGwCLKREAhyoaAKArDwCGKxsAlCwXAI0t + GAB8LCIAqi4MAJQtGACULxUAey8iAIEvIACQMB4AmjEZAIMxIwCJMiAAmTMZAJU0HQCGNCYAozUaAJ81 + HgCVNiAAiDcmAKU4HACpOBsArDsdALo8FQCrPh8AuEIfALNDIQC5RCEAw0UdAJJEMgDCRiAApEUvALlH + IwCURzAAsUksAMZKIgC8TCMAv00nAM5NIgDDTikAvFIrANRSJADUUyUAvVQtAKJVPADTVigAyFctANBZ + KwDXWSkA3VopAM1cLgDUXiwAz14vAOFgLQDkZjEAwGhCAONrNQDEa0QAxm1HAO1xNgClbGMA6XM6ALdz + WQDvfUAA7X5CAO5/QgDvf0IA7oBDAO+ERQDuhEYA74RGAOqGSgDohksA6YZLAOmITQDpiU0A8IlMAOuK + TwDukFQA6pRbAPGVVwCwjosAzZJ0ANuqkADeycYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAcHBwcHA8cFRwUnAicHBwcHBwBxhOWl1eXlxdUyEAbHBwcBpDa2hkZWZlY2dGH3BwcHAsP2pp + YF9hW1hRSzJvcHBwcFVwSUpIRURBOTYWcHBwcFdZKRwrO0AxLygdcHBwcHBwcHAQM01PNRUOcHBwcHBw + cHBwcBEkOhkEcHBwcHBwcHBwcHBwG0I9cHBwcHBwcHBwcHBwLi03VmJwcHBwcHBwcHBwChcUNFBwcHBw + cHBwcHBwcAYFCCU+THBwcHBwcHBwcHAMAQINIDBwcHBwcHBwcHBwKkcjAwsncHBwcHBwcHBwcBJubQkm + cHBwcHBwcHBwcHBwHjgPE3BwcHBwcPqvAADAAQAAwAMAAMABAADoAwAAwAcAAPgPAAD8HwAA/j8AAPwf + AAD4PwAA+B8AAPgfAAD4HwAA+D8AAPw/AAA= + + + + CenterScreen + + + UDS Administration + + + LoginForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/LoginForm.resx b/client/administration/UdsAdmin/forms/LoginForm.resx new file mode 100644 index 000000000..5080e1626 --- /dev/null +++ b/client/administration/UdsAdmin/forms/LoginForm.resx @@ -0,0 +1,457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 12, 18 + + + 116, 18 + + + + 0 + + + Server + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 11 + + + 134, 4 + + + 258, 21 + + + 1 + + + serverCombo + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 10 + + + 12, 91 + + + 116, 18 + + + 2 + + + Username + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 9 + + + 12, 117 + + + 116, 18 + + + 3 + + + Password + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 8 + + + 134, 88 + + + 258, 20 + + + 4 + + + username + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + 135, 114 + + + 32 + + + 257, 20 + + + 5 + + + passwordText + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + 79, 152 + + + 75, 23 + + + 6 + + + Connect + + + connectButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + 276, 152 + + + 75, 23 + + + 7 + + + Exit + + + exitButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + True + + + 134, 31 + + + 68, 17 + + + 9 + + + Use SSL + + + useSSLCheck + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 362, 27 + + + 30, 23 + + + 10 + + + >>> + + + extendButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + + NoControl + + + 12, 64 + + + 116, 18 + + + 11 + + + Authenticator + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 135, 61 + + + 257, 21 + + + 12 + + + authenticator + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 402, 193 + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeBwAAZRAMAGMQDQBkEA0AZxINAGcUDwBrFhAAbRgMAG8YEQBxGxAAbxsTAHAbEwByGxQAdRwSAHUd + EgByHhUAex8TAH8iFAB3IxcAdiMZAIEkFQCGJRUAkiYOAHclGwCLKREAhyoaAKArDwCGKxsAlCwXAI0t + GAB8LCIAqi4MAJQtGACULxUAey8iAIEvIACQMB4AmjEZAIMxIwCJMiAAmTMZAJU0HQCGNCYAozUaAJ81 + HgCVNiAAiDcmAKU4HACpOBsArDsdALo8FQCrPh8AuEIfALNDIQC5RCEAw0UdAJJEMgDCRiAApEUvALlH + IwCURzAAsUksAMZKIgC8TCMAv00nAM5NIgDDTikAvFIrANRSJADUUyUAvVQtAKJVPADTVigAyFctANBZ + KwDXWSkA3VopAM1cLgDUXiwAz14vAOFgLQDkZjEAwGhCAONrNQDEa0QAxm1HAO1xNgClbGMA6XM6ALdz + WQDvfUAA7X5CAO5/QgDvf0IA7oBDAO+ERQDuhEYA74RGAOqGSgDohksA6YZLAOmITQDpiU0A8IlMAOuK + TwDukFQA6pRbAPGVVwCwjosAzZJ0ANuqkADeycYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAcHBwcHA8cFRwUnAicHBwcHBwBxhOWl1eXlxdUyEAbHBwcBpDa2hkZWZlY2dGH3BwcHAsP2pp + YF9hW1hRSzJvcHBwcFVwSUpIRURBOTYWcHBwcFdZKRwrO0AxLygdcHBwcHBwcHAQM01PNRUOcHBwcHBw + cHBwcBEkOhkEcHBwcHBwcHBwcHBwG0I9cHBwcHBwcHBwcHBwLi03VmJwcHBwcHBwcHBwChcUNFBwcHBw + cHBwcHBwcAYFCCU+THBwcHBwcHBwcHAMAQINIDBwcHBwcHBwcHBwKkcjAwsncHBwcHBwcHBwcBJubQkm + cHBwcHBwcHBwcHBwHjgPE3BwcHBwcPqvAADAAQAAwAMAAMABAADoAwAAwAcAAPgPAAD8HwAA/j8AAPwf + AAD4PwAA+B8AAPgfAAD4HwAA+D8AAPw/AAA= + + + + CenterScreen + + + UDS Administration + + + LoginForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/MainForm.Designer.cs b/client/administration/UdsAdmin/forms/MainForm.Designer.cs new file mode 100644 index 000000000..269f5461a --- /dev/null +++ b/client/administration/UdsAdmin/forms/MainForm.Designer.cs @@ -0,0 +1,278 @@ +namespace UdsAdmin.forms +{ + partial class MainForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.treeActions = new System.Windows.Forms.TreeView(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolsToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.languageToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.englishToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.spanishToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.frenchToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.germanToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.configurationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.aboutToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.serviceProviderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.newServiceProviderMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.testToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.languageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.spanishToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.englishToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.toolStripButton1 = new System.Windows.Forms.ToolStripButton(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.menuStrip1.SuspendLayout(); + this.toolStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + resources.ApplyResources(this.splitContainer1, "splitContainer1"); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.treeActions); + // + // treeActions + // + resources.ApplyResources(this.treeActions, "treeActions"); + this.treeActions.HideSelection = false; + this.treeActions.MinimumSize = new System.Drawing.Size(200, 4); + this.treeActions.Name = "treeActions"; + this.treeActions.ShowNodeToolTips = true; + this.treeActions.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeActions_BeforeExpand); + this.treeActions.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeActions_AfterSelect); + this.treeActions.KeyUp += new System.Windows.Forms.KeyEventHandler(this.treeActions_KeyUp); + this.treeActions.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeActions_MouseDown); + // + // statusStrip1 + // + resources.ApplyResources(this.statusStrip1, "statusStrip1"); + this.statusStrip1.Name = "statusStrip1"; + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem, + this.toolsToolStripMenuItem1}); + resources.ApplyResources(this.menuStrip1, "menuStrip1"); + this.menuStrip1.Name = "menuStrip1"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.exitToolStripMenuItem}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + resources.ApplyResources(this.fileToolStripMenuItem, "fileToolStripMenuItem"); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + resources.ApplyResources(this.exitToolStripMenuItem, "exitToolStripMenuItem"); + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // toolsToolStripMenuItem1 + // + this.toolsToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.languageToolStripMenuItem1, + this.configurationToolStripMenuItem, + this.aboutToolStripMenuItem1}); + this.toolsToolStripMenuItem1.Name = "toolsToolStripMenuItem1"; + resources.ApplyResources(this.toolsToolStripMenuItem1, "toolsToolStripMenuItem1"); + // + // languageToolStripMenuItem1 + // + this.languageToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.englishToolStripMenuItem1, + this.spanishToolStripMenuItem1, + this.frenchToolStripMenuItem, + this.germanToolStripMenuItem}); + this.languageToolStripMenuItem1.Name = "languageToolStripMenuItem1"; + resources.ApplyResources(this.languageToolStripMenuItem1, "languageToolStripMenuItem1"); + // + // englishToolStripMenuItem1 + // + this.englishToolStripMenuItem1.Name = "englishToolStripMenuItem1"; + resources.ApplyResources(this.englishToolStripMenuItem1, "englishToolStripMenuItem1"); + this.englishToolStripMenuItem1.Click += new System.EventHandler(this.englishToolStripMenuItem_Click); + // + // spanishToolStripMenuItem1 + // + this.spanishToolStripMenuItem1.Name = "spanishToolStripMenuItem1"; + resources.ApplyResources(this.spanishToolStripMenuItem1, "spanishToolStripMenuItem1"); + this.spanishToolStripMenuItem1.Click += new System.EventHandler(this.spanishToolStripMenuItem_Click); + // + // frenchToolStripMenuItem + // + this.frenchToolStripMenuItem.Name = "frenchToolStripMenuItem"; + resources.ApplyResources(this.frenchToolStripMenuItem, "frenchToolStripMenuItem"); + this.frenchToolStripMenuItem.Click += new System.EventHandler(this.frenchToolStripMenuItem_Click); + // + // germanToolStripMenuItem + // + this.germanToolStripMenuItem.Name = "germanToolStripMenuItem"; + resources.ApplyResources(this.germanToolStripMenuItem, "germanToolStripMenuItem"); + this.germanToolStripMenuItem.Click += new System.EventHandler(this.germanToolStripMenuItem_Click); + // + // configurationToolStripMenuItem + // + this.configurationToolStripMenuItem.Name = "configurationToolStripMenuItem"; + resources.ApplyResources(this.configurationToolStripMenuItem, "configurationToolStripMenuItem"); + this.configurationToolStripMenuItem.Click += new System.EventHandler(this.configurationToolStripMenuItem_Click); + // + // aboutToolStripMenuItem1 + // + this.aboutToolStripMenuItem1.Name = "aboutToolStripMenuItem1"; + resources.ApplyResources(this.aboutToolStripMenuItem1, "aboutToolStripMenuItem1"); + // + // serviceProviderToolStripMenuItem + // + this.serviceProviderToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.newServiceProviderMenu, + this.testToolStripMenuItem}); + this.serviceProviderToolStripMenuItem.Name = "serviceProviderToolStripMenuItem"; + resources.ApplyResources(this.serviceProviderToolStripMenuItem, "serviceProviderToolStripMenuItem"); + // + // newServiceProviderMenu + // + this.newServiceProviderMenu.Name = "newServiceProviderMenu"; + resources.ApplyResources(this.newServiceProviderMenu, "newServiceProviderMenu"); + // + // testToolStripMenuItem + // + this.testToolStripMenuItem.Name = "testToolStripMenuItem"; + resources.ApplyResources(this.testToolStripMenuItem, "testToolStripMenuItem"); + // + // toolsToolStripMenuItem + // + this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem"; + resources.ApplyResources(this.toolsToolStripMenuItem, "toolsToolStripMenuItem"); + // + // languageToolStripMenuItem + // + this.languageToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.spanishToolStripMenuItem, + this.englishToolStripMenuItem}); + this.languageToolStripMenuItem.Name = "languageToolStripMenuItem"; + resources.ApplyResources(this.languageToolStripMenuItem, "languageToolStripMenuItem"); + // + // spanishToolStripMenuItem + // + this.spanishToolStripMenuItem.Name = "spanishToolStripMenuItem"; + resources.ApplyResources(this.spanishToolStripMenuItem, "spanishToolStripMenuItem"); + this.spanishToolStripMenuItem.Click += new System.EventHandler(this.spanishToolStripMenuItem_Click); + // + // englishToolStripMenuItem + // + this.englishToolStripMenuItem.Name = "englishToolStripMenuItem"; + resources.ApplyResources(this.englishToolStripMenuItem, "englishToolStripMenuItem"); + this.englishToolStripMenuItem.Click += new System.EventHandler(this.englishToolStripMenuItem_Click); + // + // aboutToolStripMenuItem + // + this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + resources.ApplyResources(this.aboutToolStripMenuItem, "aboutToolStripMenuItem"); + // + // toolStrip1 + // + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripButton1}); + resources.ApplyResources(this.toolStrip1, "toolStrip1"); + this.toolStrip1.Name = "toolStrip1"; + // + // toolStripButton1 + // + this.toolStripButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButton1.Image = global::UdsAdmin.Images.cache16; + resources.ApplyResources(this.toolStripButton1, "toolStripButton1"); + this.toolStripButton1.Name = "toolStripButton1"; + this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click); + // + // MainForm + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Controls.Add(this.toolStrip1); + this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.Name = "MainForm"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed); + this.Load += new System.EventHandler(this.MainForm_Load); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.ResumeLayout(false); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem serviceProviderToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem newServiceProviderMenu; + private System.Windows.Forms.ToolStrip toolStrip1; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.TreeView treeActions; + private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem languageToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem spanishToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem englishToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem testToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem languageToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem englishToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem spanishToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem frenchToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem germanToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem1; + private System.Windows.Forms.ToolStripButton toolStripButton1; + private System.Windows.Forms.ToolStripMenuItem configurationToolStripMenuItem; + + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/MainForm.cs b/client/administration/UdsAdmin/forms/MainForm.cs new file mode 100644 index 000000000..7e0fd6e68 --- /dev/null +++ b/client/administration/UdsAdmin/forms/MainForm.cs @@ -0,0 +1,677 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Globalization; +using System.Threading; + +namespace UdsAdmin.forms +{ + public partial class MainForm : Form + { + public xmlrpc.ServiceProviderType[] _serviceProvidersTypes = null; + public xmlrpc.AuthenticatorType[] _authenticatorsTypes = null; + public xmlrpc.OSManagerType[] _osManagersTypes = null; + public xmlrpc.TransportType[] _transportTypes = null; + + public MainForm form = null; + + private Icon getIcon( xmlrpc.BaseType[] bas, string type) + { + Icon icon = null; + foreach (xmlrpc.BaseType bt in bas) + { + if (bt.type == type) + { + icon = gui.Helpers.IconFromBase64(bt.icon); + break; + } + } + return icon; + } + + public MainForm() + { + InitializeComponent(); + form = this; + Text = Strings.titleMain; + } + + private void MainForm_Load(object sender, EventArgs e) + { + // Adjust split position + splitContainer1.SplitterDistance = 200; + + // Saves the providers known by this server + _serviceProvidersTypes = xmlrpc.UdsAdminService.GetServiceProvidersTypes(); + _authenticatorsTypes = xmlrpc.UdsAdminService.GetAuthenticatorsTypes(); + _osManagersTypes = xmlrpc.UdsAdminService.GetOSManagersTypes(); + _transportTypes = xmlrpc.UdsAdminService.GetTransportsTypes(); + + // Initialize the base images for the action tree + gui.ActionTree.InitializeImageList(treeActions); + // Fill the types of servers for service providers, authenticators + gui.ActionTree.addTypesImages(treeActions, _serviceProvidersTypes, _authenticatorsTypes, _osManagersTypes, _transportTypes); + + // Menus for top level items + ContextMenuStrip servicesProvidersContextMenu = new System.Windows.Forms.ContextMenuStrip(); + ContextMenuStrip authenticatorsContextMenu = new System.Windows.Forms.ContextMenuStrip(); + ContextMenuStrip osManagersContextMenu = new System.Windows.Forms.ContextMenuStrip(); + ContextMenuStrip transportsContextMenu = new System.Windows.Forms.ContextMenuStrip(); + ContextMenuStrip deployedServiceContextMenu = new System.Windows.Forms.ContextMenuStrip(); + ContextMenuStrip networkContexMenu = new System.Windows.Forms.ContextMenuStrip(); + + // Contextual menu for Services Providers + ToolStripMenuItem newService = new ToolStripMenuItem(Strings.newItem); + gui.MenusManager.InitProvidersMenu(newService, newServiceProviderMenu_Click, _serviceProvidersTypes); + servicesProvidersContextMenu.Items.Add(newService); + + // Contextual menu for authenticators + ToolStripMenuItem newAuth = new ToolStripMenuItem(Strings.newItem); + gui.MenusManager.InitAuthenticatorsMenu(newAuth, newAuthenticatorMenu_Click, _authenticatorsTypes); + authenticatorsContextMenu.Items.Add(newAuth); + + // Contextual menu for os managers + ToolStripMenuItem newOSManager = new ToolStripMenuItem(Strings.newItem); + gui.MenusManager.InitOSManagersMenu(newOSManager, newOSManagerMenu_Click, _osManagersTypes); + osManagersContextMenu.Items.Add(newOSManager); + + // Contextual menu for transports + ToolStripMenuItem newTransport = new ToolStripMenuItem(Strings.newItem); + gui.MenusManager.InitTransportsMenu(newTransport, newTransportMenu_Click, _transportTypes); + transportsContextMenu.Items.Add(newTransport); + + // Contextual menu for Deployed Services + ToolStripMenuItem newDeployed = new ToolStripMenuItem(Strings.newItem); + gui.MenusManager.InitDeployedServicesMenu(newDeployed, newDeployed_Click); + deployedServiceContextMenu.Items.Add(newDeployed); + + // Contextual menu for Networks + ToolStripMenuItem newNetwork = new ToolStripMenuItem(Strings.newItem); + newNetwork.Click += newNetwork_Click; + networkContexMenu.Items.Add(newNetwork); + + // Generate main tree + treeActions.Nodes.Add(gui.ActionTree.SERVICES_PROVIDERS, Strings.serviceProviders, + gui.ActionTree.SERVICES_PROVIDERS + gui.ActionTree.DIMMED_OUT, gui.ActionTree.SERVICES_PROVIDERS); + treeActions.Nodes.Add(gui.ActionTree.AUTHENTICATORS, Strings.authenticators, + gui.ActionTree.AUTHENTICATORS + gui.ActionTree.DIMMED_OUT, gui.ActionTree.AUTHENTICATORS); + treeActions.Nodes.Add(gui.ActionTree.OS_MANAGERS, Strings.osManagers, + gui.ActionTree.OS_MANAGERS + gui.ActionTree.DIMMED_OUT, gui.ActionTree.OS_MANAGERS); + + treeActions.Nodes.Add(gui.ActionTree.CONNECTIVITY, Strings.connectivity, + gui.ActionTree.CONNECTIVITY + gui.ActionTree.DIMMED_OUT, gui.ActionTree.CONNECTIVITY); + treeActions.Nodes[gui.ActionTree.CONNECTIVITY].Nodes.Add(gui.ActionTree.TRANSPORTS, Strings.transports, + gui.ActionTree.TRANSPORTS + gui.ActionTree.DIMMED_OUT, gui.ActionTree.TRANSPORTS); + treeActions.Nodes[gui.ActionTree.CONNECTIVITY].Nodes.Add(gui.ActionTree.NETWORKS, Strings.networks, + gui.ActionTree.NETWORKS + gui.ActionTree.DIMMED_OUT, gui.ActionTree.NETWORKS); + + treeActions.Nodes.Add(gui.ActionTree.DEPLOYED_SERVICES, Strings.deployedServices, + gui.ActionTree.DEPLOYED_SERVICES + gui.ActionTree.DIMMED_OUT, gui.ActionTree.DEPLOYED_SERVICES); + + // Add context menus + treeActions.Nodes[gui.ActionTree.SERVICES_PROVIDERS].ContextMenuStrip = servicesProvidersContextMenu; + treeActions.Nodes[gui.ActionTree.AUTHENTICATORS].ContextMenuStrip = authenticatorsContextMenu; + treeActions.Nodes[gui.ActionTree.OS_MANAGERS].ContextMenuStrip = osManagersContextMenu; + + treeActions.Nodes[gui.ActionTree.CONNECTIVITY].Nodes[gui.ActionTree.TRANSPORTS].ContextMenuStrip = transportsContextMenu; + treeActions.Nodes[gui.ActionTree.CONNECTIVITY].Nodes[gui.ActionTree.NETWORKS].ContextMenuStrip = networkContexMenu; + + treeActions.Nodes[gui.ActionTree.DEPLOYED_SERVICES].ContextMenuStrip = deployedServiceContextMenu; + + updateServicesProvidersTree(); + updateAuthenticatorsTree(); + updateOSManagersTree(); + updateTransportsTree(); + updateDeployedServicesTree(); + } + + private void changeLangTo(string lang) + { + if (MessageBox.Show(Strings.changeLanguage, Strings.language, MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + { + UdsAdmin.Properties.Settings.Default.Locale = lang; + //CultureInfo culture = new CultureInfo(UdsAdmin.Properties.Settings.Default.Locale); + //Thread.CurrentThread.CurrentCulture = culture; + //Thread.CurrentThread.CurrentUICulture = culture; + + //UdsAdmin.Properties.Settings.Default.Save(); done at Program.cs + } + } + + private void englishToolStripMenuItem_Click(object sender, EventArgs e) + { + changeLangTo("en-US"); + } + + private void spanishToolStripMenuItem_Click(object sender, EventArgs e) + { + changeLangTo("es-ES"); + } + + private void frenchToolStripMenuItem_Click(object sender, EventArgs e) + { + changeLangTo("fr-FR"); + } + + private void germanToolStripMenuItem_Click(object sender, EventArgs e) + { + changeLangTo("de-DE"); + } + + private void treeActions_AfterSelect(object sender, TreeViewEventArgs e) + { + if (treeActions.SelectedNode == null) + return; + try + { + gui.ActionTree.showAssociatedPanel(splitContainer1.Panel2, treeActions, this); + } + catch (Exception) + { + // Nothing done right now + } + + } + + private void newServiceProviderMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + ServiceProviderForm dlg = new ServiceProviderForm(s.Text, s.Name, getIcon(_serviceProvidersTypes, s.Name)); + dlg.ShowDialog(); + updateServicesProvidersTree(); + } + + private void newAuthenticatorMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + AuthenticatorForm dlg = new AuthenticatorForm(s.Text, s.Name, getIcon(_authenticatorsTypes, s.Name)); + dlg.ShowDialog(); + updateAuthenticatorsTree(); + } + + private void newOSManagerMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + OSManagerForm dlg = new OSManagerForm(s.Text, s.Name, getIcon(_osManagersTypes, s.Name)); + dlg.ShowDialog(); + updateOSManagersTree(); + } + + private void newTransportMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + TransportForm dlg = new TransportForm(s.Text, s.Name, getIcon(_transportTypes, s.Name)); + dlg.ShowDialog(); + updateTransportsTree(); + } + + private void newDeployed_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + DeployedServiceForm dlg = new DeployedServiceForm(); + DialogResult res = dlg.ShowDialog(); + updateDeployedServicesTree(); + } + + private void treeActions_BeforeExpand(object sender, TreeViewCancelEventArgs e) + { + // We can do a constant refresh here, but i think better not, F5 must be enought + /*if (e.Node.Name == gui.ActionTree.SERVICES_PROVIDERS) + updateServicesProvidersTree();*/ + switch( e.Node.Name ) + { + case gui.ActionTree.SERVICE_PROVIDER: + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)e.Node.Tag; + updateServicesTree(sp.id); + break; + } + } + + private void serviceProviderContextMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + + switch (s.Name) + { + case gui.ActionTree.NEW_ACTION: + { + // Selects the corresponding "Services" node + if( treeActions.SelectedNode.Name == gui.ActionTree.SERVICE_PROVIDER ) + treeActions.SelectedNode = treeActions.SelectedNode.Nodes[gui.ActionTree.SERVICES]; + treeActions.SelectedNode.Expand(); + xmlrpc.ServiceType st = (xmlrpc.ServiceType)s.Tag; + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)treeActions.SelectedNode.Parent.Tag; + + ServiceForm dlg = new ServiceForm(sp.id, st.name, st.type, gui.Helpers.IconFromBase64(st.icon)); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + updateServicesTree(sp.id); + break; + } + case gui.ActionTree.MODIFY_ACTION: + { + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)treeActions.SelectedNode.Tag; + ServiceProviderForm dlg = new ServiceProviderForm(sp.typeName, sp.type, getIcon(_serviceProvidersTypes, sp.type)); + dlg.setData(sp.name, sp.comments, sp.id, xmlrpc.UdsAdminService.GetServiceProvider(sp.id)); + if( dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK ) + updateServicesProvidersTree(); + break; + } + case gui.ActionTree.DELETE_ACTION: + { + try + { + if (MessageBox.Show(Strings.removeQuestion, Strings.serviceProviders, MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + { + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)treeActions.SelectedNode.Tag; + xmlrpc.UdsAdminService.RemoveServiceProvider(sp.id); + updateServicesProvidersTree(); + } + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + break; + } + case gui.ActionTree.CHECK_ACTION: + { + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)treeActions.SelectedNode.Tag; + MessageBox.Show(xmlrpc.UdsAdminService.CheckServiceProvider(sp.id)); + break; + } + } + } + + private void AuthenticatorContextMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + switch (s.Name) + { + case gui.ActionTree.MODIFY_ACTION: + { + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)treeActions.SelectedNode.Tag; + AuthenticatorForm dlg = new AuthenticatorForm(auth.typeName, auth.type, getIcon(_authenticatorsTypes, auth.type)); + dlg.setData(auth.name, auth.comments, auth.id, xmlrpc.UdsAdminService.GetAuthenticator(auth.id)); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + updateAuthenticatorsTree(); + break; + } + case gui.ActionTree.DELETE_ACTION: + { + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)treeActions.SelectedNode.Tag; + xmlrpc.UdsAdminService.RemoveAuthenticator(auth.id); + updateAuthenticatorsTree(); + break; + } + case gui.ActionTree.CHECK_ACTION: + { + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)treeActions.SelectedNode.Tag; + MessageBox.Show(xmlrpc.UdsAdminService.CheckAuthenticator(auth.id)); + break; + } + } + } + + + private void serviceContextMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + + switch (s.Name) + { + case gui.ActionTree.MODIFY_ACTION: + { + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)treeActions.SelectedNode.Parent.Parent.Tag; + xmlrpc.Service se = (xmlrpc.Service)treeActions.SelectedNode.Tag; + xmlrpc.ServiceType st = (xmlrpc.ServiceType)s.Tag; + ServiceForm dlg = new ServiceForm(sp.id, se.typeName, se.type, gui.Helpers.IconFromBase64(st.icon)); + dlg.setData(se.name, se.comments, se.id, xmlrpc.UdsAdminService.GetService(se.id)); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + updateServicesTree(sp.id); + break; + } + case gui.ActionTree.DELETE_ACTION: + { + try + { + if (MessageBox.Show(Strings.removeQuestion, Strings.services, MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + { + xmlrpc.Service ser = (xmlrpc.Service)treeActions.SelectedNode.Tag; + xmlrpc.UdsAdminService.RemoveService(ser.id); + updateServicesTree(ser.idParent); + } + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + break; + } + } + } + + private void osManagerContextMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + switch (s.Name) + { + case gui.ActionTree.MODIFY_ACTION: + { + xmlrpc.OSManager osm = (xmlrpc.OSManager)treeActions.SelectedNode.Tag; + OSManagerForm dlg = new OSManagerForm(osm.typeName, osm.type, getIcon(_osManagersTypes,osm.type)); + dlg.setData(osm.name, osm.comments, osm.id, xmlrpc.UdsAdminService.GetOSManager(osm.id)); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + updateOSManagersTree(); + break; + } + case gui.ActionTree.DELETE_ACTION: + { + xmlrpc.OSManager osm = (xmlrpc.OSManager)treeActions.SelectedNode.Tag; + if (MessageBox.Show(Strings.removeQuestion, Strings.osManager, MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + { + try + { + xmlrpc.UdsAdminService.RemoveOSManager(osm.id); + updateOSManagersTree(); + } + catch( CookComputing.XmlRpc.XmlRpcFaultException ex ) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + break; + } + case gui.ActionTree.CHECK_ACTION: + { + xmlrpc.OSManager osm = (xmlrpc.OSManager)treeActions.SelectedNode.Tag; + MessageBox.Show(xmlrpc.UdsAdminService.CheckOSManager(osm.id)); + break; + } + } + } + + private void transportContextMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + switch (s.Name) + { + case gui.ActionTree.MODIFY_ACTION: + { + xmlrpc.Transport trans = (xmlrpc.Transport)treeActions.SelectedNode.Tag; + TransportForm dlg = new TransportForm(trans.typeName, trans.type, getIcon(_transportTypes, trans.type)); + dlg.setData(trans.name, trans.comments, trans.id, xmlrpc.UdsAdminService.GetTransport(trans.id)); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + updateTransportsTree(); + break; + } + case gui.ActionTree.DELETE_ACTION: + { + xmlrpc.Transport trans = (xmlrpc.Transport)treeActions.SelectedNode.Tag; + xmlrpc.UdsAdminService.RemoveTransport(trans.id); + updateTransportsTree(); + break; + } + } + } + + private void deployedContextMenu_Click(object sender, EventArgs e) + { + ToolStripMenuItem s = (ToolStripMenuItem)sender; + switch (s.Name) + { + case gui.ActionTree.MODIFY_ACTION: + { + xmlrpc.DeployedService dps = (xmlrpc.DeployedService)treeActions.SelectedNode.Tag; + dps = xmlrpc.UdsAdminService.GetDeployedService(dps.id); + DeployedServiceForm dlg = new DeployedServiceForm(); + dlg.setData(dps); + if( dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK ) + updateDeployedServicesTree(); + break; + } + case gui.ActionTree.DELETE_ACTION: + { + xmlrpc.DeployedService dps = (xmlrpc.DeployedService)treeActions.SelectedNode.Tag; + if (MessageBox.Show(Strings.removeQuestion, Strings.deployedService, MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + { + xmlrpc.UdsAdminService.RemoveDeployedService(dps.id); + updateDeployedServicesTree(); + } + break; + } + case gui.ActionTree.PUBLISH_ACTION: + { + // This can be trigered from 2 different places + if (treeActions.SelectedNode.Name == gui.ActionTree.DEPLOYED_SERVICE) + treeActions.SelectedNode = treeActions.SelectedNode.Nodes[gui.ActionTree.PUBLICATIONS]; + xmlrpc.DeployedService dps = (xmlrpc.DeployedService)treeActions.SelectedNode.Parent.Tag; + if (MessageBox.Show(Strings.publishQuestion, Strings.publish, MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + try + { + xmlrpc.UdsAdminService.PublishDeployedService(dps); + treeActions_AfterSelect(null, null); + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + break; + } + } + } + + private void UserGroupsMenu_Click(object sender, EventArgs e) + { + bool user = treeActions.SelectedNode.Name == gui.ActionTree.USERS; + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)(treeActions.SelectedNode.Parent.Tag); + xmlrpc.AuthenticatorType type = gui.ActionTree.authType(auth, _authenticatorsTypes); + ToolStripMenuItem s = (ToolStripMenuItem)sender; + switch (s.Name) + { + case gui.ActionTree.NEW_ACTION: + { + if (user) + { + if (type.canCreateUsers == false) + { + gui.UserNotifier.notifyError(Strings.cantCreateUsers); + return; + } + forms.UserForm dlg = new UserForm(auth, type, new xmlrpc.User()); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + treeActions_AfterSelect(null, null); + } + } + else + { + forms.GroupForm dlg = new GroupForm(auth, type); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + treeActions_AfterSelect(null, null); + } + break; + } + } + + } + + private void newNetwork_Click(object sender, EventArgs e) + { + forms.NetworkForm dlg = new NetworkForm(); + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + treeActions_AfterSelect(null, null); + } + + + private void treeActions_MouseDown(object sender, MouseEventArgs e) + { + // We force the selected item to be the one at witch we right-clicked + if (e.Button == MouseButtons.Right) + { + treeActions.SelectedNode = treeActions.GetNodeAt(e.X, e.Y); + } + } + + private void treeActions_KeyUp(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.F5) + { + TreeNode node = treeActions.SelectedNode; + switch (node.Name) + { + case gui.ActionTree.SERVICES_PROVIDERS: + updateServicesProvidersTree(); + break; + case gui.ActionTree.SERVICE_PROVIDER: + { + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)node.Tag; + updateServicesTree(sp.id); + } + break; + case gui.ActionTree.AUTHENTICATORS: + updateAuthenticatorsTree(); + break; + case gui.ActionTree.AUTHENTICATOR: + // TODO: Make this update the auth tree + break; + case gui.ActionTree.OS_MANAGERS: + case gui.ActionTree.OS_MANAGER: + updateOSManagersTree(); + break; + case gui.ActionTree.TRANSPORTS: + case gui.ActionTree.TRANSPORT: + updateTransportsTree(); + break; + case gui.ActionTree.NETWORKS: + updateTransportsTree(); + break; + case gui.ActionTree.DEPLOYED_SERVICES: + case gui.ActionTree.DEPLOYED_SERVICE: + updateDeployedServicesTree(); + break; + case gui.ActionTree.PUBLICATIONS: + case gui.ActionTree.ASSIGNED_SERVICES: + case gui.ActionTree.CACHE: + case gui.ActionTree.USERS: + case gui.ActionTree.GROUPS: + treeActions_AfterSelect(null, null); + break; + + } + + } + } + + // Test form + private void testToolStripMenuItem1_Click(object sender, EventArgs e) + { + } + + + // Helper methods, Tree updaters + private void updateServicesProvidersTree() + { + gui.ActionTree.FillServicesProviders(treeActions.Nodes[gui.ActionTree.SERVICES_PROVIDERS], serviceProviderContextMenu_Click); + treeActions_AfterSelect(null, null); + } + + private void updateServicesTree(string idServiceProvider) + { + TreeNode nodeBase = treeActions.Nodes[gui.ActionTree.SERVICES_PROVIDERS]; + foreach (TreeNode node in nodeBase.Nodes) + { + if (node.Name == gui.ActionTree.SERVICE_PROVIDER) + { + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)node.Tag; + if (sp.id == idServiceProvider) + gui.ActionTree.FillServices(node, serviceContextMenu_Click); + } + } + treeActions_AfterSelect(null, null); + } + + private void updateAuthenticatorsTree() + { + gui.ActionTree.FillAuthenticators(treeActions.Nodes[gui.ActionTree.AUTHENTICATORS], _authenticatorsTypes, + AuthenticatorContextMenu_Click, UserGroupsMenu_Click); + treeActions_AfterSelect(null, null); + } + + private void updateOSManagersTree() + { + gui.ActionTree.FillOSManagers(treeActions.Nodes[gui.ActionTree.OS_MANAGERS], osManagerContextMenu_Click); + treeActions_AfterSelect(null, null); + } + + private void updateTransportsTree() + { + gui.ActionTree.FillTransports(treeActions.Nodes[gui.ActionTree.CONNECTIVITY].Nodes[gui.ActionTree.TRANSPORTS], + transportContextMenu_Click); + treeActions_AfterSelect(null, null); + } + + private void updateDeployedServicesTree() + { + gui.ActionTree.FillDeployedServices(treeActions.Nodes[gui.ActionTree.DEPLOYED_SERVICES], deployedContextMenu_Click); + treeActions_AfterSelect(this, null); + } + + private void MainForm_FormClosed(object sender, FormClosedEventArgs e) + { + xmlrpc.UdsAdminService.Logout(); + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + Close(); + } + + private void toolStripButton1_Click(object sender, EventArgs e) + { + xmlrpc.UdsAdminService.FlushCache(); + MessageBox.Show(Strings.cacheFlushed, Strings.cache, MessageBoxButtons.OK); + } + + private void configurationToolStripMenuItem_Click(object sender, EventArgs e) + { + new ConfigurationForm().ShowDialog(); + } + + } +} diff --git a/client/administration/UdsAdmin/forms/MainForm.de.resx b/client/administration/UdsAdmin/forms/MainForm.de.resx new file mode 100644 index 000000000..7972aad73 --- /dev/null +++ b/client/administration/UdsAdmin/forms/MainForm.de.resx @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAAE1NlcnZpY2VzIFByb3ZpZGVycyAGBAAAABFTZXJ2aWNl + c1Byb3ZpZGVycwD/////BgUAAAAA/////wkFAAAAAAAAAAkFAAAACw== + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAADkF1dGhlbnRpY2F0b3JzBgQAAAAOQXV0aGVudGljYXRv + cnMA/////wYFAAAAAP////8JBQAAAAAAAAAJBQAAAAs= + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQgAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQBAQAA + AQABAAEICAgCAAAABgMAAAALT1MgTWFuYWdlcnMGBAAAAApPU01hbmFnZXJzAP////8GBQAAAAD///// + CQUAAAAAAAAACw== + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQoAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQJY2hp + bGRyZW4wCWNoaWxkcmVuMQEBAAABAAEABAQBCAgIHVN5c3RlbS5XaW5kb3dzLkZvcm1zLlRyZWVOb2Rl + AgAAAB1TeXN0ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQIAAAACAAAABgMAAAAMQ29ubmVjdGl2aXR5 + BgQAAAAMQ29ubmVjdGl2aXR5AP////8GBQAAAAD/////CQUAAAACAAAACQYAAAAJBwAAAAUGAAAAHVN5 + c3RlbS5XaW5kb3dzLkZvcm1zLlRyZWVOb2RlCAAAAARUZXh0BE5hbWUJSXNDaGVja2VkCkltYWdlSW5k + ZXgISW1hZ2VLZXkSU2VsZWN0ZWRJbWFnZUluZGV4EFNlbGVjdGVkSW1hZ2VLZXkKQ2hpbGRDb3VudAEB + AAABAAEAAQgICAIAAAAGCAAAAApUcmFuc3BvcnRzBgkAAAAKVHJhbnNwb3J0cwD/////CQUAAAD///// + CQUAAAAAAAAABQcAAAAdU3lzdGVtLldpbmRvd3MuRm9ybXMuVHJlZU5vZGUIAAAABFRleHQETmFtZQlJ + c0NoZWNrZWQKSW1hZ2VJbmRleAhJbWFnZUtleRJTZWxlY3RlZEltYWdlSW5kZXgQU2VsZWN0ZWRJbWFn + ZUtleQpDaGlsZENvdW50AQEAAAEAAQABCAgIAgAAAAYLAAAACE5ldHdvcmtzBgwAAAAITmV0d29ya3MA + /////wkFAAAA/////wkFAAAAAAAAAAs= + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAAEURlcGxveWVkIFNlcnZpY2VzBgQAAAAQRGVwbG95ZWRT + ZXJ2aWNlcwD/////BgUAAAAA/////wkFAAAAAAAAAAkFAAAACw== + + + + + 136, 20 + + + Anbieter von Diensten + + + 96, 22 + + + Neu + + + 96, 22 + + + Test + + + 77, 20 + + + Werkzeuge + + + 116, 22 + + + Sprache + + + 135, 22 + + + Englisch + + + 135, 22 + + + Spanisch + + + 135, 22 + + + Französisch + + + 135, 22 + + + Deutsch + + + Service-Provider + + + 96, 22 + + + Neu + + + 96, 22 + + + Sprache + + + 121, 22 + + + Spanisch + + + 121, 22 + + + Englisch + + + Über + + + statusStrip1 + + + Ausfahrt + + + Datei + + + Über + + + topMenu + + + toolStrip1 + + + Cache leeren + + + Löscht den Cache + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/MainForm.es.resx b/client/administration/UdsAdmin/forms/MainForm.es.resx new file mode 100644 index 000000000..e249e498f --- /dev/null +++ b/client/administration/UdsAdmin/forms/MainForm.es.resx @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAAGFByb3ZlZWRvcmVzIGRlIHNlcnZpY2lvcwYEAAAAEVNl + cnZpY2VzUHJvdmlkZXJzAP////8GBQAAAAD/////CQUAAAAAAAAACQUAAAAL + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAADkF1dGVudGljYWRvcmVzBgQAAAAOQXV0aGVudGljYXRv + cnMA/////wYFAAAAAP////8JBQAAAAAAAAAJBQAAAAs= + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQgAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQBAQAA + AQABAAEICAgCAAAABgMAAAAQR2VzdG9yZXMgZGUgUy5PLgYEAAAACk9TTWFuYWdlcnMA/////wYFAAAA + AP////8JBQAAAAAAAAAL + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQoAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQJY2hp + bGRyZW4wCWNoaWxkcmVuMQEBAAABAAEABAQBCAgIHVN5c3RlbS5XaW5kb3dzLkZvcm1zLlRyZWVOb2Rl + AgAAAB1TeXN0ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQIAAAACAAAABgMAAAAMQ29uZWN0aXZpZGFk + BgQAAAAMQ29ubmVjdGl2aXR5AP////8GBQAAAAD/////CQUAAAACAAAACQYAAAAJBwAAAAUGAAAAHVN5 + c3RlbS5XaW5kb3dzLkZvcm1zLlRyZWVOb2RlCAAAAARUZXh0BE5hbWUJSXNDaGVja2VkCkltYWdlSW5k + ZXgISW1hZ2VLZXkSU2VsZWN0ZWRJbWFnZUluZGV4EFNlbGVjdGVkSW1hZ2VLZXkKQ2hpbGRDb3VudAEB + AAABAAEAAQgICAIAAAAGCAAAAAtUcmFuc3BvcnRlcwYJAAAAClRyYW5zcG9ydHMA/////wkFAAAA//// + /wkFAAAAAAAAAAUHAAAAHVN5c3RlbS5XaW5kb3dzLkZvcm1zLlRyZWVOb2RlCAAAAARUZXh0BE5hbWUJ + SXNDaGVja2VkCkltYWdlSW5kZXgISW1hZ2VLZXkSU2VsZWN0ZWRJbWFnZUluZGV4EFNlbGVjdGVkSW1h + Z2VLZXkKQ2hpbGRDb3VudAEBAAABAAEAAQgICAIAAAAGCwAAAAVSZWRlcwYMAAAACE5ldHdvcmtzAP// + //8JBQAAAP////8JBQAAAAAAAAAL + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAAFVNlcnZpY2lvcyBEZXNwbGVnYWRvcwYEAAAAEERlcGxv + eWVkU2VydmljZXMA/////wYFAAAAAP////8JBQAAAAAAAAAJBQAAAAs= + + + + Acerca de + + + Inglés + + + Inglés + + + Español + + + Español + + + statusStrip1 + + + Idioma + + + Idioma + + + Proveedor de servicios + + + Nuevo + + + Proveedores de servicios + + + Nuevo + + + topMenu + + + Herramientas + + + toolStrip1 + + + prueba + + + Francés + + + Alemán + + + Salir + + + Archivo + + + Acerca de + + + Vaciar la caché + + + Borra la caché + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/MainForm.fr.resx b/client/administration/UdsAdmin/forms/MainForm.fr.resx new file mode 100644 index 000000000..aa88e1a42 --- /dev/null +++ b/client/administration/UdsAdmin/forms/MainForm.fr.resx @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAAElNlcnZpY2VzIFByb3ZpZGVycwYEAAAAEVNlcnZpY2Vz + UHJvdmlkZXJzAP////8GBQAAAAD/////CQUAAAAAAAAACQUAAAAL + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAADkF1dGhlbnRpY2F0b3JzBgQAAAAOQXV0aGVudGljYXRv + cnMA/////wYFAAAAAP////8JBQAAAAAAAAAJBQAAAAs= + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQgAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQBAQAA + AQABAAEICAgCAAAABgMAAAALT1MgTWFuYWdlcnMGBAAAAApPU01hbmFnZXJzAP////8GBQAAAAD///// + CQUAAAAAAAAACw== + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQoAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQJY2hp + bGRyZW4wCWNoaWxkcmVuMQEBAAABAAEABAQBCAgIHVN5c3RlbS5XaW5kb3dzLkZvcm1zLlRyZWVOb2Rl + AgAAAB1TeXN0ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQIAAAACAAAABgMAAAAMQ29ubmVjdGl2aXR5 + BgQAAAAMQ29ubmVjdGl2aXR5AP////8GBQAAAAD/////CQUAAAACAAAACQYAAAAJBwAAAAUGAAAAHVN5 + c3RlbS5XaW5kb3dzLkZvcm1zLlRyZWVOb2RlCAAAAARUZXh0BE5hbWUJSXNDaGVja2VkCkltYWdlSW5k + ZXgISW1hZ2VLZXkSU2VsZWN0ZWRJbWFnZUluZGV4EFNlbGVjdGVkSW1hZ2VLZXkKQ2hpbGRDb3VudAEB + AAABAAEAAQgICAIAAAAGCAAAAApUcmFuc3BvcnRzBgkAAAAKVHJhbnNwb3J0cwD/////CQUAAAD///// + CQUAAAAAAAAABQcAAAAdU3lzdGVtLldpbmRvd3MuRm9ybXMuVHJlZU5vZGUIAAAABFRleHQETmFtZQlJ + c0NoZWNrZWQKSW1hZ2VJbmRleAhJbWFnZUtleRJTZWxlY3RlZEltYWdlSW5kZXgQU2VsZWN0ZWRJbWFn + ZUtleQpDaGlsZENvdW50AQEAAAEAAQABCAgIAgAAAAYLAAAACE5ldHdvcmtzBgwAAAAITmV0d29ya3MA + /////wkFAAAA/////wkFAAAAAAAAAAs= + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAB1TeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5UcmVlTm9kZQkAAAAEVGV4dAROYW1lCUlzQ2hlY2tlZApJbWFnZUluZGV4 + CEltYWdlS2V5ElNlbGVjdGVkSW1hZ2VJbmRleBBTZWxlY3RlZEltYWdlS2V5CkNoaWxkQ291bnQIVXNl + ckRhdGEBAQAAAQABAAEBCAgIAgAAAAYDAAAAEURlcGxveWVkIFNlcnZpY2VzBgQAAAAQRGVwbG95ZWRT + ZXJ2aWNlcwD/////BgUAAAAA/////wkFAAAAAAAAAAkFAAAACw== + + + + Sujet + + + Anglais + + + Anglais + + + Espagnol + + + Espagnol + + + statusStrip1 + + + Langue + + + Langue + + + Fournisseur de services + + + Nouveau + + + Fournisseurs de services + + + Nouveau + + + topMenu + + + Outils + + + toolStrip1 + + + test + + + Français + + + Allemand + + + Sortie + + + Fichier + + + Sujet + + + Vider le Cache + + + Efface le cache + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/MainForm.resx b/client/administration/UdsAdmin/forms/MainForm.resx new file mode 100644 index 000000000..29d70c845 --- /dev/null +++ b/client/administration/UdsAdmin/forms/MainForm.resx @@ -0,0 +1,531 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Fill + + + + 0, 50 + + + Fill + + + 0, 0 + + + 200, 376 + + + + 0 + + + treeActions + + + System.Windows.Forms.TreeView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + splitContainer1.Panel1 + + + 0 + + + splitContainer1.Panel1 + + + System.Windows.Forms.SplitterPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + splitContainer1 + + + 0 + + + 32 + + + splitContainer1.Panel2 + + + System.Windows.Forms.SplitterPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + splitContainer1 + + + 1 + + + 32 + + + 610, 376 + + + 146 + + + 3 + + + splitContainer1 + + + System.Windows.Forms.SplitContainer, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + 17, 17 + + + 0, 426 + + + 610, 22 + + + 0 + + + statusStrip1 + + + statusStrip1 + + + System.Windows.Forms.StatusStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + 133, 17 + + + 102, 22 + + + Exit + + + 42, 21 + + + File + + + 127, 22 + + + English + + + 127, 22 + + + Spanish + + + 127, 22 + + + French + + + 127, 22 + + + German + + + 165, 22 + + + Language + + + 165, 22 + + + Configuration + + + 165, 22 + + + About + + + 52, 21 + + + Tools + + + 0, 0 + + + 610, 25 + + + 1 + + + topMenu + + + menuStrip1 + + + System.Windows.Forms.MenuStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 106, 22 + + + New + + + 106, 22 + + + 103, 20 + + + Service Provider + + + 32, 19 + + + 126, 22 + + + Spanish + + + 126, 22 + + + English + + + 126, 22 + + + Language + + + 126, 22 + + + About + + + 248, 17 + + + Magenta + + + 23, 22 + + + Flush Cache + + + Clears the cache + + + 0, 25 + + + 610, 25 + + + 2 + + + toolStrip1 + + + toolStrip1 + + + System.Windows.Forms.ToolStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + True + + + 6, 13 + + + 610, 448 + + + UDS Administration + + + fileToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + exitToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolsToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + languageToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + englishToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + spanishToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + frenchToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + germanToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + configurationToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + aboutToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + serviceProviderToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + newServiceProviderMenu + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + testToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolsToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + languageToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + spanishToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + englishToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + aboutToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripButton1 + + + System.Windows.Forms.ToolStripButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + MainForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/NetworkForm.Designer.cs b/client/administration/UdsAdmin/forms/NetworkForm.Designer.cs new file mode 100644 index 000000000..0b58d77a2 --- /dev/null +++ b/client/administration/UdsAdmin/forms/NetworkForm.Designer.cs @@ -0,0 +1,138 @@ +namespace UdsAdmin.forms +{ + partial class NetworkForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(NetworkForm)); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.groupLabel = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.netStart = new System.Windows.Forms.TextBox(); + this.netEnd = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.groupLabel, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.netStart, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.netEnd, 1, 2); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // groupLabel + // + resources.ApplyResources(this.groupLabel, "groupLabel"); + this.groupLabel.Name = "groupLabel"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // netStart + // + resources.ApplyResources(this.netStart, "netStart"); + this.netStart.Name = "netStart"; + // + // netEnd + // + resources.ApplyResources(this.netEnd, "netEnd"); + this.netEnd.Name = "netEnd"; + // + // NetworkForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.tableLayoutPanel2); + this.Name = "NetworkForm"; + this.Load += new System.EventHandler(this.NetworkForm_Load); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label groupLabel; + private System.Windows.Forms.TextBox name; + private System.Windows.Forms.TextBox netStart; + private System.Windows.Forms.TextBox netEnd; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/NetworkForm.cs b/client/administration/UdsAdmin/forms/NetworkForm.cs new file mode 100644 index 000000000..f664ea3d7 --- /dev/null +++ b/client/administration/UdsAdmin/forms/NetworkForm.cs @@ -0,0 +1,124 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class NetworkForm : Form + { + private xmlrpc.Network _net; + + public NetworkForm(xmlrpc.Network net = null) + { + InitializeComponent(); + + if (net != null) + _net = net; + else + _net = new xmlrpc.Network(); + + netStart.KeyPress += HandleKeyPress; + netEnd.KeyPress += HandleKeyPress; + + Text = Strings.titleNetwork; + } + + private void NetworkForm_Load(object sender, EventArgs e) + { + if (_net == null) + { + name.Text = _net.name; + netStart.Text = _net.netStart; + netEnd.Text = _net.netEnd; + } + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + System.Net.IPAddress ip; + System.Net.IPAddress ip2; + if (System.Net.IPAddress.TryParse(netStart.Text, out ip) == false) + { + MessageBox.Show(Strings.invalidIpAddress, Strings.netStart, MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + if (System.Net.IPAddress.TryParse(netEnd.Text, out ip2) == false) + { + MessageBox.Show(Strings.invalidIpAddress, Strings.netEnd, MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + if (ip.Address > ip2.Address) + { + MessageBox.Show(Strings.netRangeError, Strings.netStart + "/" + Strings.netEnd, MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + _net.name = name.Text; + _net.netStart = netStart.Text; + _net.netEnd = netEnd.Text; + try { + if (_net.id == "") + { + xmlrpc.UdsAdminService.CreateNetwork(_net); + } + else + { + xmlrpc.UdsAdminService.ModifyNetwork(_net); + } + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void HandleKeyPress(object sender, KeyPressEventArgs e) + { + if (!(char.IsDigit(e.KeyChar) || char.IsControl(e.KeyChar)) && e.KeyChar != '.') + e.Handled = true; + } + + } +} diff --git a/client/administration/UdsAdmin/forms/NetworkForm.de.resx b/client/administration/UdsAdmin/forms/NetworkForm.de.resx new file mode 100644 index 000000000..eb0ad66b9 --- /dev/null +++ b/client/administration/UdsAdmin/forms/NetworkForm.de.resx @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 121, 23 + + + 3 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 300, 7 + + + 123, 23 + + + 4 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 117 + + + 1 + + + 426, 33 + + + 9 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Left, Right + + + 2 + + + True + + + 3, 62 + + + 3, 6, 3, 0 + + + 69, 13 + + + 7 + + + Netzwerk-Ende + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 34 + + + 3, 6, 3, 0 + + + 72, 13 + + + 1 + + + Netzwerk-Start + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 6 + + + 3, 6, 3, 0 + + + 113, 13 + + + 0 + + + Netzwerk-Bereich Name + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Fill + + + 147, 3 + + + 252, 20 + + + 0 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + 147, 31 + + + 119, 20 + + + 1 + + + netStart + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 147, 59 + + + 119, 20 + + + 2 + + + netEnd + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + 12, 12 + + + 3 + + + 402, 85 + + + 10 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netStart" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netEnd" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,35,95506,Percent,64,04494" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 426, 150 + + + Network + + + NetworkForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/NetworkForm.es.resx b/client/administration/UdsAdmin/forms/NetworkForm.es.resx new file mode 100644 index 000000000..097926fd0 --- /dev/null +++ b/client/administration/UdsAdmin/forms/NetworkForm.es.resx @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 121, 23 + + + 3 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 300, 7 + + + 123, 23 + + + 4 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 117 + + + 1 + + + 426, 33 + + + 9 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Left, Right + + + 2 + + + True + + + 3, 62 + + + 3, 6, 3, 0 + + + 69, 13 + + + 7 + + + Final de red + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 34 + + + 3, 6, 3, 0 + + + 72, 13 + + + 1 + + + Inicio de red + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 6 + + + 3, 6, 3, 0 + + + 113, 13 + + + 0 + + + Nombre de rango de red + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Fill + + + 147, 3 + + + 252, 20 + + + 0 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + 147, 31 + + + 119, 20 + + + 1 + + + netStart + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 147, 59 + + + 119, 20 + + + 2 + + + netEnd + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + 12, 12 + + + 3 + + + 402, 85 + + + 10 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netStart" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netEnd" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,35,95506,Percent,64,04494" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 426, 150 + + + Network + + + NetworkForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/NetworkForm.fr.resx b/client/administration/UdsAdmin/forms/NetworkForm.fr.resx new file mode 100644 index 000000000..f24c7df88 --- /dev/null +++ b/client/administration/UdsAdmin/forms/NetworkForm.fr.resx @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 121, 23 + + + 3 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 300, 7 + + + 123, 23 + + + 4 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 117 + + + 1 + + + 426, 33 + + + 9 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Left, Right + + + 2 + + + True + + + 3, 62 + + + 3, 6, 3, 0 + + + 69, 13 + + + 7 + + + Fin de réseau + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 34 + + + 3, 6, 3, 0 + + + 72, 13 + + + 1 + + + Début du réseau + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 6 + + + 3, 6, 3, 0 + + + 113, 13 + + + 0 + + + Nom de la plage réseau + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Fill + + + 147, 3 + + + 252, 20 + + + 0 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + 147, 31 + + + 119, 20 + + + 1 + + + netStart + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 147, 59 + + + 119, 20 + + + 2 + + + netEnd + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + 12, 12 + + + 3 + + + 402, 85 + + + 10 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netStart" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netEnd" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,35,95506,Percent,64,04494" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 426, 150 + + + Network + + + NetworkForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/NetworkForm.resx b/client/administration/UdsAdmin/forms/NetworkForm.resx new file mode 100644 index 000000000..564185acc --- /dev/null +++ b/client/administration/UdsAdmin/forms/NetworkForm.resx @@ -0,0 +1,420 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 121, 23 + + + 3 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 300, 7 + + + 123, 23 + + + 4 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 117 + + + 1 + + + 426, 33 + + + 9 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Left, Right + + + 2 + + + True + + + 3, 62 + + + 3, 6, 3, 0 + + + 69, 13 + + + 7 + + + Network End + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 34 + + + 3, 6, 3, 0 + + + 72, 13 + + + 1 + + + Network Start + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 6 + + + 3, 6, 3, 0 + + + 113, 13 + + + 0 + + + Network Range Name + + + groupLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Fill + + + 147, 3 + + + 252, 20 + + + 0 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + 147, 31 + + + 119, 20 + + + 1 + + + netStart + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 147, 59 + + + 119, 20 + + + 2 + + + netEnd + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + 12, 12 + + + 3 + + + 402, 85 + + + 10 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="groupLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netStart" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="netEnd" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,35,95506,Percent,64,04494" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 426, 150 + + + CenterParent + + + Network + + + NetworkForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/OSManagerForm.Designer.cs b/client/administration/UdsAdmin/forms/OSManagerForm.Designer.cs new file mode 100644 index 000000000..40c57ba1f --- /dev/null +++ b/client/administration/UdsAdmin/forms/OSManagerForm.Designer.cs @@ -0,0 +1,174 @@ +namespace UdsAdmin.forms +{ + partial class OSManagerForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OSManagerForm)); + this.groupData = new System.Windows.Forms.GroupBox(); + this.dataPanel = new System.Windows.Forms.TableLayoutPanel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.comments = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.test = new System.Windows.Forms.Button(); + this.groupData.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.SuspendLayout(); + // + // groupData + // + resources.ApplyResources(this.groupData, "groupData"); + this.groupData.Controls.Add(this.dataPanel); + this.groupData.Name = "groupData"; + this.groupData.TabStop = false; + // + // dataPanel + // + resources.ApplyResources(this.dataPanel, "dataPanel"); + this.dataPanel.Name = "dataPanel"; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.comments, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Name = "comments"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 1, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // tableLayoutPanel3 + // + resources.ApplyResources(this.tableLayoutPanel3, "tableLayoutPanel3"); + this.tableLayoutPanel3.Controls.Add(this.test, 1, 0); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + // + // test + // + resources.ApplyResources(this.test, "test"); + this.test.Name = "test"; + this.test.UseVisualStyleBackColor = true; + this.test.Click += new System.EventHandler(this.test_Click); + // + // OSManagerForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.groupData); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.tableLayoutPanel1); + this.DoubleBuffered = true; + this.Name = "OSManagerForm"; + this.Load += new System.EventHandler(this.OsManager_Load); + this.groupData.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupData; + private System.Windows.Forms.TableLayoutPanel dataPanel; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.TextBox comments; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox name; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Button test; + + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/OSManagerForm.cs b/client/administration/UdsAdmin/forms/OSManagerForm.cs new file mode 100644 index 000000000..30827ffc8 --- /dev/null +++ b/client/administration/UdsAdmin/forms/OSManagerForm.cs @@ -0,0 +1,137 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class OSManagerForm : Form + { + string _id; + xmlrpc.GuiField[] _flds; + xmlrpc.GuiFieldValue[] _fldValues; + string _osManagerName; + string _osManagerType; + + public OSManagerForm(string osManagerName, string osManagerType, Icon icon) + { + InitializeComponent(); + _fldValues = null; + _id = null; + _flds = null; + _osManagerName = osManagerName; + _osManagerType = osManagerType; + Icon = icon; + Text = Strings.titleOsManager; + } + + public void setData(string name, string comments, string id, xmlrpc.GuiFieldValue[] data) + { + this.name.Text = name; + this.comments.Text = comments; + this._id = id; + _fldValues = data; + } + + private void OsManager_Load(object sender, EventArgs e) + { + _flds = xmlrpc.UdsAdminService.GetOSManagerGui(_osManagerType); + if (_flds == null) + { + Close(); + return; + } + Size sz = gui.DinamycFieldsManager.PutFields(dataPanel, _flds, _fldValues); + groupData.Size = new Size(groupData.Size.Width, 32 + sz.Height); + Size wSize = new Size(); + wSize.Width = Size.Width; + wSize.Height = groupData.Location.Y + tableLayoutPanel1.Size.Height + groupData.Size.Height + 48; + Size = MinimumSize = MaximumSize = wSize; + + if (_flds.Length == 0) + groupData.Visible = false; + + Text = _osManagerName; + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + xmlrpc.GuiFieldValue[] data; + try + { + data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + } + catch (gui.DinamycFieldsManager.ValidationError err) + { + gui.UserNotifier.notifyValidationException(err); + return; + } + if (_id == null) + try { + xmlrpc.UdsAdminService.CreateOSManager(name.Text, comments.Text, _osManagerType, data); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + else + { + try { + xmlrpc.UdsAdminService.ModifyOSManager(name.Text, comments.Text, _id, data); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + } + + private void test_Click(object sender, EventArgs e) + { + xmlrpc.GuiFieldValue[] data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + xmlrpc.ResultTest res = xmlrpc.UdsAdminService.TestOsManager(_osManagerType, data); + gui.UserNotifier.notifyTestResult(res); + } + } +} diff --git a/client/administration/UdsAdmin/forms/OSManagerForm.de.resx b/client/administration/UdsAdmin/forms/OSManagerForm.de.resx new file mode 100644 index 000000000..cc683ba8c --- /dev/null +++ b/client/administration/UdsAdmin/forms/OSManagerForm.de.resx @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + Service-Anbieterdaten + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Kommentare + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Allgemeine Daten + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Test + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + ServiceProvider + + + OSManagerForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/OSManagerForm.es.resx b/client/administration/UdsAdmin/forms/OSManagerForm.es.resx new file mode 100644 index 000000000..15af18ebe --- /dev/null +++ b/client/administration/UdsAdmin/forms/OSManagerForm.es.resx @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + Datos del proveedor de servicio + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Comentarios + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Nombre + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Datos comunes + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Prueba + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + ServiceProvider + + + OSManagerForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/OSManagerForm.fr.resx b/client/administration/UdsAdmin/forms/OSManagerForm.fr.resx new file mode 100644 index 000000000..136a329be --- /dev/null +++ b/client/administration/UdsAdmin/forms/OSManagerForm.fr.resx @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + Données de fournisseur de service + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Commentaires + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Nom + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Données communes + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Test + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + ServiceProvider + + + OSManagerForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/OSManagerForm.resx b/client/administration/UdsAdmin/forms/OSManagerForm.resx new file mode 100644 index 000000000..c0bff3265 --- /dev/null +++ b/client/administration/UdsAdmin/forms/OSManagerForm.resx @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 2 + + + Fill + + + + 3, 16 + + + 2 + + + 439, 120 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 105 + + + 445, 139 + + + 7 + + + Service Provider Data + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Top, Left, Right + + + 2 + + + 82, 36 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Left + + + True + + + 3, 43 + + + 56, 13 + + + 1 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Left + + + True + + + 3, 10 + + + 35, 13 + + + 0 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + 12, 19 + + + 2 + + + 414, 67 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 7 + + + 445, 92 + + + 6 + + + Common Data + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 135, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 332, 7 + + + 135, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 3 + + + Fill + + + 39, 3 + + + 103, 21 + + + 0 + + + Test + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + 144, 3 + + + 1 + + + 182, 27 + + + 2 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="test" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,20,Percent,60,Percent,20" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Bottom + + + 0, 270 + + + 1 + + + 470, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="tableLayoutPanel3" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 470, 303 + + + CenterParent + + + Os Manager + + + OSManagerForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/SearchForm.Designer.cs b/client/administration/UdsAdmin/forms/SearchForm.Designer.cs new file mode 100644 index 000000000..12a201491 --- /dev/null +++ b/client/administration/UdsAdmin/forms/SearchForm.Designer.cs @@ -0,0 +1,146 @@ +namespace UdsAdmin.forms +{ + partial class SearchForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SearchForm)); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.close = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.searchLabel = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.searchText = new System.Windows.Forms.TextBox(); + this.searchButton = new System.Windows.Forms.Button(); + this.resultsList = new System.Windows.Forms.ListView(); + this.id = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.close, 2, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // close + // + resources.ApplyResources(this.close, "close"); + this.close.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.close.Name = "close"; + this.close.UseVisualStyleBackColor = true; + this.close.Click += new System.EventHandler(this.cancel_Click); + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.searchLabel, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.searchText, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.searchButton, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.resultsList, 1, 1); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // searchLabel + // + resources.ApplyResources(this.searchLabel, "searchLabel"); + this.searchLabel.Name = "searchLabel"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // searchText + // + resources.ApplyResources(this.searchText, "searchText"); + this.searchText.Name = "searchText"; + // + // searchButton + // + resources.ApplyResources(this.searchButton, "searchButton"); + this.searchButton.Name = "searchButton"; + this.searchButton.UseVisualStyleBackColor = true; + this.searchButton.Click += new System.EventHandler(this.searchButton_Click); + // + // resultsList + // + this.resultsList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.id, + this.name}); + this.tableLayoutPanel1.SetColumnSpan(this.resultsList, 2); + resources.ApplyResources(this.resultsList, "resultsList"); + this.resultsList.FullRowSelect = true; + this.resultsList.Name = "resultsList"; + this.resultsList.UseCompatibleStateImageBehavior = false; + this.resultsList.View = System.Windows.Forms.View.Details; + this.resultsList.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.resultsList_ColumnClick); + this.resultsList.DoubleClick += new System.EventHandler(this.resultsList_DoubleClick); + // + // id + // + resources.ApplyResources(this.id, "id"); + // + // name + // + resources.ApplyResources(this.name, "name"); + // + // SearchForm + // + this.AcceptButton = this.searchButton; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.close; + this.Controls.Add(this.tableLayoutPanel2); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "SearchForm"; + this.Load += new System.EventHandler(this.SearchForm_Load); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button close; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label searchLabel; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox searchText; + private System.Windows.Forms.Button searchButton; + private System.Windows.Forms.ListView resultsList; + private System.Windows.Forms.ColumnHeader id; + private System.Windows.Forms.ColumnHeader name; + + + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/SearchForm.cs b/client/administration/UdsAdmin/forms/SearchForm.cs new file mode 100644 index 000000000..3e4010562 --- /dev/null +++ b/client/administration/UdsAdmin/forms/SearchForm.cs @@ -0,0 +1,113 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class SearchForm : Form + { + public enum Type { userSearch, groupSearch }; + + private Type _type; + private string _authId; + gui.ListViewSorter _listSorter; + + public SearchForm(Type type, xmlrpc.Authenticator auth, string defText = null) + { + InitializeComponent(); + _type = type; + _authId = auth.id; + if (defText != null) + searchText.Text = defText; + + resultsList.ListViewItemSorter = _listSorter = new gui.ListViewSorter(resultsList); + } + + private void SearchForm_Load(object sender, EventArgs e) + { + if (_type == Type.userSearch) + { + Text = Strings.searchUser; + searchLabel.Text = Strings.user; + } + else + { + Text = Strings.searchGroup; + searchLabel.Text = Strings.group; + } + } + + public string selection + { + get { return resultsList.SelectedItems[0].SubItems[0].Text; } + } + + private void cancel_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.No; + } + + private void searchButton_Click(object sender, EventArgs e) + { + string srch = searchText.Text.Trim(); + try + { + xmlrpc.SimpleInfo[] res = xmlrpc.UdsAdminService.SearchAuthenticator(_authId, _type == Type.userSearch, srch); + resultsList.Items.Clear(); + foreach (xmlrpc.SimpleInfo r in res) + { + ListViewItem itm = new ListViewItem(new string[]{r.id, r.name}); + resultsList.Items.Add(itm); + } + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void resultsList_ColumnClick(object sender, ColumnClickEventArgs e) + { + _listSorter.ColumnClick(sender, e); + } + + private void resultsList_DoubleClick(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.Yes; + } + } +} diff --git a/client/administration/UdsAdmin/forms/SearchForm.de.resx b/client/administration/UdsAdmin/forms/SearchForm.de.resx new file mode 100644 index 000000000..c630dcc9b --- /dev/null +++ b/client/administration/UdsAdmin/forms/SearchForm.de.resx @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + NoControl + + + + 293, 7 + + + 119, 23 + + + 9 + + + Schließen + + + close + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom + + + 0, 234 + + + 1 + + + 415, 33 + + + 7 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="close" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + 3 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 63, 13 + + + 1 + + + Suchbegriff + + + searchLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + NoControl + + + 3, 35 + + + 3, 6, 3, 0 + + + 42, 13 + + + 2 + + + Ergebnisse + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 109, 3 + + + 178, 20 + + + 3 + + + searchText + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + 331, 3 + + + 64, 21 + + + 4 + + + Suche + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + Id + + + 85 + + + Name + + + 175 + + + Fill + + + 109, 32 + + + 300, 190 + + + 5 + + + resultsList + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 0, 3 + + + 2 + + + 412, 225 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="searchLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="searchText" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="searchButton" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="resultsList" Row="1" RowSpan="1" Column="1" ColumnSpan="2" /></Controls><Columns Styles="Percent,25,81699,Percent,53,92157,Percent,20" /><Rows Styles="Percent,12,88889,Percent,87,11111,Absolute,20" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 415, 267 + + + CenterParent + + + SearchForm + + + id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SearchForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/SearchForm.es.resx b/client/administration/UdsAdmin/forms/SearchForm.es.resx new file mode 100644 index 000000000..a55458e47 --- /dev/null +++ b/client/administration/UdsAdmin/forms/SearchForm.es.resx @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + NoControl + + + + 293, 7 + + + 119, 23 + + + 9 + + + Cerrar + + + close + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom + + + 0, 234 + + + 1 + + + 415, 33 + + + 7 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="close" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + 3 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 63, 13 + + + 1 + + + Elemento de búsqueda + + + searchLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + NoControl + + + 3, 35 + + + 3, 6, 3, 0 + + + 42, 13 + + + 2 + + + Resultados + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 109, 3 + + + 178, 20 + + + 3 + + + searchText + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + 331, 3 + + + 64, 21 + + + 4 + + + Búsqueda + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + Id + + + 85 + + + Nombre + + + 175 + + + Fill + + + 109, 32 + + + 300, 190 + + + 5 + + + resultsList + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 0, 3 + + + 2 + + + 412, 225 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="searchLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="searchText" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="searchButton" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="resultsList" Row="1" RowSpan="1" Column="1" ColumnSpan="2" /></Controls><Columns Styles="Percent,25,81699,Percent,53,92157,Percent,20" /><Rows Styles="Percent,12,88889,Percent,87,11111,Absolute,20" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 415, 267 + + + CenterParent + + + SearchForm + + + id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SearchForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/SearchForm.fr.resx b/client/administration/UdsAdmin/forms/SearchForm.fr.resx new file mode 100644 index 000000000..221b32631 --- /dev/null +++ b/client/administration/UdsAdmin/forms/SearchForm.fr.resx @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + NoControl + + + + 293, 7 + + + 119, 23 + + + 9 + + + Fermer + + + close + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom + + + 0, 234 + + + 1 + + + 415, 33 + + + 7 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="close" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + 3 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 63, 13 + + + 1 + + + Élément de recherche + + + searchLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + NoControl + + + 3, 35 + + + 3, 6, 3, 0 + + + 42, 13 + + + 2 + + + Résultats + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 109, 3 + + + 178, 20 + + + 3 + + + searchText + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + 331, 3 + + + 64, 21 + + + 4 + + + Recherche + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + Id + + + 85 + + + Nom + + + 175 + + + Fill + + + 109, 32 + + + 300, 190 + + + 5 + + + resultsList + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 0, 3 + + + 2 + + + 412, 225 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="searchLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="searchText" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="searchButton" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="resultsList" Row="1" RowSpan="1" Column="1" ColumnSpan="2" /></Controls><Columns Styles="Percent,25,81699,Percent,53,92157,Percent,20" /><Rows Styles="Percent,12,88889,Percent,87,11111,Absolute,20" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 415, 267 + + + CenterParent + + + SearchForm + + + id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SearchForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/SearchForm.resx b/client/administration/UdsAdmin/forms/SearchForm.resx new file mode 100644 index 000000000..3b91f4728 --- /dev/null +++ b/client/administration/UdsAdmin/forms/SearchForm.resx @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + NoControl + + + + 293, 7 + + + 119, 23 + + + 9 + + + Close + + + close + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom + + + 0, 234 + + + 1 + + + 415, 33 + + + 7 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="close" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + 3 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 63, 13 + + + 1 + + + Search item + + + searchLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + NoControl + + + 3, 35 + + + 3, 6, 3, 0 + + + 42, 13 + + + 2 + + + Results + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 109, 3 + + + 178, 20 + + + 3 + + + searchText + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + 331, 3 + + + 64, 21 + + + 4 + + + Search + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + Id + + + 85 + + + Name + + + 175 + + + Fill + + + 109, 32 + + + 300, 190 + + + 5 + + + resultsList + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + 0, 3 + + + 2 + + + 412, 225 + + + 0 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="searchLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label1" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="searchText" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="searchButton" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="resultsList" Row="1" RowSpan="1" Column="1" ColumnSpan="2" /></Controls><Columns Styles="Percent,25,81699,Percent,53,92157,Percent,20" /><Rows Styles="Percent,12,88889,Percent,87,11111,Absolute,20" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 415, 267 + + + CenterParent + + + SearchForm + + + id + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SearchForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceForm.Designer.cs b/client/administration/UdsAdmin/forms/ServiceForm.Designer.cs new file mode 100644 index 000000000..13fed3928 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceForm.Designer.cs @@ -0,0 +1,154 @@ +namespace UdsAdmin.forms +{ + partial class ServiceForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ServiceForm)); + this.groupData = new System.Windows.Forms.GroupBox(); + this.dataPanel = new System.Windows.Forms.TableLayoutPanel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.comments = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.groupData.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // groupData + // + resources.ApplyResources(this.groupData, "groupData"); + this.groupData.Controls.Add(this.dataPanel); + this.groupData.Name = "groupData"; + this.groupData.TabStop = false; + // + // dataPanel + // + resources.ApplyResources(this.dataPanel, "dataPanel"); + this.dataPanel.Name = "dataPanel"; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.comments, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Name = "comments"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // ServiceForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.groupData); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.tableLayoutPanel1); + this.DoubleBuffered = true; + this.Name = "ServiceForm"; + this.Load += new System.EventHandler(this.Service_Load); + this.groupData.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupData; + private System.Windows.Forms.TableLayoutPanel dataPanel; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.TextBox comments; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox name; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceForm.cs b/client/administration/UdsAdmin/forms/ServiceForm.cs new file mode 100644 index 000000000..2bb33ea38 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceForm.cs @@ -0,0 +1,127 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class ServiceForm : Form + { + string _id; + string _idParent; + xmlrpc.GuiField[] _flds; + xmlrpc.GuiFieldValue[] _fldValues; + string _serviceName; + string _serviceType; + + public ServiceForm(string idParent, string serviceName, string serviceType, Icon icon) + { + InitializeComponent(); + _id = null; + _idParent = idParent; + _serviceName = serviceName; + _serviceType = serviceType; + _flds = null; + _fldValues = null; + Icon = icon; + Text = Strings.titleService; + } + + public void setData(string name, string comments, string id, xmlrpc.GuiFieldValue[] data) + { + this.name.Text = name; + this.comments.Text = comments; + this._id = id; + _fldValues = data; + } + + private void Service_Load(object sender, EventArgs e) + { + _flds = xmlrpc.UdsAdminService.GetServiceGui(_idParent, _serviceType); + if (_flds == null) + { + Close(); + return; + } + Size sz = gui.DinamycFieldsManager.PutFields(dataPanel, _flds, _fldValues); + groupData.Size = new Size(groupData.Size.Width, 32 + sz.Height); + Size wSize = new Size(); + wSize.Width = Size.Width; + wSize.Height = groupData.Location.Y + tableLayoutPanel1.Size.Height + groupData.Size.Height + 48; + Size = MinimumSize = wSize; + wSize.Width = Screen.GetWorkingArea(this).Width; + MaximumSize = wSize; + if (_flds.Length == 0) + groupData.Visible = false; + Text = _serviceName; + //this.Location = System.Windows.Forms.Cursor.Position; + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + xmlrpc.GuiFieldValue[] data; + try { + data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + } + catch (gui.DinamycFieldsManager.ValidationError err) + { + gui.UserNotifier.notifyValidationException(err); + return; + } + + try { + if (_id == null) + { + xmlrpc.UdsAdminService.CreateService(_idParent, name.Text, comments.Text, _serviceType, data); + } + else + { + xmlrpc.UdsAdminService.ModifyService(name.Text, comments.Text, _id, data); + } + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + } +} diff --git a/client/administration/UdsAdmin/forms/ServiceForm.de.resx b/client/administration/UdsAdmin/forms/ServiceForm.de.resx new file mode 100644 index 000000000..7929850eb --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceForm.de.resx @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + Service-Daten + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Kommentare + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Allgemeine Daten + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + ServiceProvider + + + ServiceForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceForm.es.resx b/client/administration/UdsAdmin/forms/ServiceForm.es.resx new file mode 100644 index 000000000..c75eba7d2 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceForm.es.resx @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + Datos del servicio + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Comentarios + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Nombre + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Datos comunes + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + ServiceProvider + + + ServiceForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceForm.fr.resx b/client/administration/UdsAdmin/forms/ServiceForm.fr.resx new file mode 100644 index 000000000..3af469a50 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceForm.fr.resx @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + Données de service + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Commentaires + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Nom + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Données communes + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + ServiceProvider + + + ServiceForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceForm.resx b/client/administration/UdsAdmin/forms/ServiceForm.resx new file mode 100644 index 000000000..f36c15993 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceForm.resx @@ -0,0 +1,450 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 2 + + + Fill + + + + 3, 16 + + + 2 + + + 465, 140 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 105 + + + 471, 159 + + + 7 + + + Service Data + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Top, Left, Right + + + 2 + + + 90, 36 + + + 348, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Left + + + True + + + 3, 43 + + + 56, 13 + + + 1 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + 90, 3 + + + 348, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Left + + + True + + + 3, 10 + + + 35, 13 + + + 0 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + 12, 19 + + + 2 + + + 453, 67 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 7 + + + 471, 92 + + + 6 + + + Common Data + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 142, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 349, 7 + + + 144, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Bottom + + + 0, 270 + + + 1 + + + 496, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 496, 303 + + + CenterParent + + + Service + + + ServiceForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceProviderForm.Designer.cs b/client/administration/UdsAdmin/forms/ServiceProviderForm.Designer.cs new file mode 100644 index 000000000..55d00cf2b --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceProviderForm.Designer.cs @@ -0,0 +1,174 @@ +namespace UdsAdmin.forms +{ + partial class ServiceProviderForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ServiceProviderForm)); + this.groupData = new System.Windows.Forms.GroupBox(); + this.dataPanel = new System.Windows.Forms.TableLayoutPanel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.comments = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.test = new System.Windows.Forms.Button(); + this.groupData.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.SuspendLayout(); + // + // groupData + // + resources.ApplyResources(this.groupData, "groupData"); + this.groupData.Controls.Add(this.dataPanel); + this.groupData.Name = "groupData"; + this.groupData.TabStop = false; + // + // dataPanel + // + resources.ApplyResources(this.dataPanel, "dataPanel"); + this.dataPanel.Name = "dataPanel"; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.comments, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Name = "comments"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 1, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // tableLayoutPanel3 + // + resources.ApplyResources(this.tableLayoutPanel3, "tableLayoutPanel3"); + this.tableLayoutPanel3.Controls.Add(this.test, 1, 0); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + // + // test + // + resources.ApplyResources(this.test, "test"); + this.test.Name = "test"; + this.test.UseVisualStyleBackColor = true; + this.test.Click += new System.EventHandler(this.test_Click); + // + // ServiceProviderForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.groupData); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.tableLayoutPanel1); + this.DoubleBuffered = true; + this.Name = "ServiceProviderForm"; + this.Load += new System.EventHandler(this.ServiceProvider_Load); + this.groupData.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupData; + private System.Windows.Forms.TableLayoutPanel dataPanel; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.TextBox comments; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox name; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Button test; + + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceProviderForm.cs b/client/administration/UdsAdmin/forms/ServiceProviderForm.cs new file mode 100644 index 000000000..9d0c15b95 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceProviderForm.cs @@ -0,0 +1,126 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class ServiceProviderForm : Form + { + string _id; + xmlrpc.GuiField[] _flds; + xmlrpc.GuiFieldValue[] _fldValues; + string _providerName; + string _providerType; + + public ServiceProviderForm(string providerName, string providerType, Icon icon) + { + InitializeComponent(); + _fldValues = null; + _id = null; + _flds = null; + _providerName = providerName; + _providerType = providerType; + Icon = icon; + Text = Strings.titleServiceProvider; + } + + public void setData(string name, string comments, string id, xmlrpc.GuiFieldValue[] data) + { + this.name.Text = name; + this.comments.Text = comments; + this._id = id; + _fldValues = data; + } + + private void ServiceProvider_Load(object sender, EventArgs e) + { + _flds = xmlrpc.UdsAdminService.GetServiceProviderGui(_providerType); + if (_flds == null) + { + Close(); + return; + } + Size sz = gui.DinamycFieldsManager.PutFields(dataPanel, _flds, _fldValues); + groupData.Size = new Size(groupData.Size.Width, 32 + sz.Height); + Size wSize = new Size(); + wSize.Width = Size.Width; + wSize.Height = groupData.Location.Y + tableLayoutPanel1.Size.Height + groupData.Size.Height + 48; + Size = MinimumSize = MaximumSize = wSize; + if (_flds.Length == 0) + groupData.Visible = false; + Text = _providerName; + //this.Location = System.Windows.Forms.Cursor.Position; + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + xmlrpc.GuiFieldValue[] data; + try { + data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + } + catch (gui.DinamycFieldsManager.ValidationError err) + { + gui.UserNotifier.notifyValidationException(err); + return; + } + + try { + if (_id == null) + xmlrpc.UdsAdminService.CreateServiceProvider(name.Text, comments.Text, _providerType, data); + else + xmlrpc.UdsAdminService.ModifyServiceProvider(name.Text, comments.Text, _id, data); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void test_Click(object sender, EventArgs e) + { + xmlrpc.GuiFieldValue[] data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + xmlrpc.ResultTest res = xmlrpc.UdsAdminService.testServiceProvider(_providerType, data); + gui.UserNotifier.notifyTestResult(res); + } + } +} diff --git a/client/administration/UdsAdmin/forms/ServiceProviderForm.de.resx b/client/administration/UdsAdmin/forms/ServiceProviderForm.de.resx new file mode 100644 index 000000000..2ee0eabfa --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceProviderForm.de.resx @@ -0,0 +1,504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 2 + + + + 12, 19 + + + 2 + + + 200, 100 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 105 + + + 473, 139 + + + 7 + + + Service-Anbieterdaten + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Top, Left, Right + + + 2 + + + 82, 36 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Left + + + True + + + 3, 43 + + + 56, 13 + + + 1 + + + Kommentare + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Left + + + True + + + 3, 10 + + + 35, 13 + + + 0 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + 12, 19 + + + 2 + + + 414, 67 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 7 + + + 473, 92 + + + 6 + + + Allgemeine Daten + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 143, 23 + + + 0 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 351, 7 + + + 144, 23 + + + 1 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 3 + + + Fill + + + 39, 3 + + + 103, 21 + + + 0 + + + Test + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + 152, 3 + + + 1 + + + 182, 27 + + + 2 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="test" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,20,Percent,60,Percent,20" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Bottom + + + 0, 273 + + + 1 + + + 498, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="tableLayoutPanel3" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 498, 306 + + + Manual + + + ServiceProvider + + + ServiceProviderForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceProviderForm.es.resx b/client/administration/UdsAdmin/forms/ServiceProviderForm.es.resx new file mode 100644 index 000000000..bd5306cc0 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceProviderForm.es.resx @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Datos del proveedor de servicios + + + Datos Comunes + + + + 65, 13 + + + Comentarios + + + 44, 13 + + + Nombre + + + Aceptar + + + Cancelar + + + Probar + + + Proveedor de servicios + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceProviderForm.fr.resx b/client/administration/UdsAdmin/forms/ServiceProviderForm.fr.resx new file mode 100644 index 000000000..c43b7f5b4 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceProviderForm.fr.resx @@ -0,0 +1,504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 2 + + + + 12, 19 + + + 2 + + + 200, 100 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 105 + + + 473, 139 + + + 7 + + + Données de fournisseur de service + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Top, Left, Right + + + 2 + + + 82, 36 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Left + + + True + + + 3, 43 + + + 56, 13 + + + 1 + + + Commentaires + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Left + + + True + + + 3, 10 + + + 35, 13 + + + 0 + + + Nom + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + 12, 19 + + + 2 + + + 414, 67 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 7 + + + 473, 92 + + + 6 + + + Données communes + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 143, 23 + + + 0 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 351, 7 + + + 144, 23 + + + 1 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 3 + + + Fill + + + 39, 3 + + + 103, 21 + + + 0 + + + Test + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + 152, 3 + + + 1 + + + 182, 27 + + + 2 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="test" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,20,Percent,60,Percent,20" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Bottom + + + 0, 273 + + + 1 + + + 498, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="tableLayoutPanel3" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 498, 306 + + + Manual + + + ServiceProvider + + + ServiceProviderForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/ServiceProviderForm.resx b/client/administration/UdsAdmin/forms/ServiceProviderForm.resx new file mode 100644 index 000000000..1a0fa34d2 --- /dev/null +++ b/client/administration/UdsAdmin/forms/ServiceProviderForm.resx @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 2 + + + Fill + + + + 3, 16 + + + 2 + + + 467, 120 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 105 + + + 473, 139 + + + 7 + + + Service Provider Data + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Top, Left, Right + + + 2 + + + 82, 36 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Left + + + True + + + 3, 43 + + + 56, 13 + + + 1 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + Left + + + True + + + 3, 10 + + + 35, 13 + + + 0 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + 12, 19 + + + 2 + + + 414, 67 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 13, 7 + + + 473, 92 + + + 6 + + + Common Data + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 143, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 351, 7 + + + 144, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + 3 + + + Fill + + + 39, 3 + + + 103, 21 + + + 0 + + + Test + + + test + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + 152, 3 + + + 1 + + + 182, 27 + + + 2 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="test" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,20,Percent,60,Percent,20" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Bottom + + + 0, 273 + + + 1 + + + 498, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="tableLayoutPanel3" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 498, 306 + + + CenterParent + + + ServiceProvider + + + ServiceProviderForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/TransportForm.Designer.cs b/client/administration/UdsAdmin/forms/TransportForm.Designer.cs new file mode 100644 index 000000000..3eee1b940 --- /dev/null +++ b/client/administration/UdsAdmin/forms/TransportForm.Designer.cs @@ -0,0 +1,251 @@ +namespace UdsAdmin.forms +{ + partial class TransportForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TransportForm)); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tabs = new System.Windows.Forms.TabControl(); + this.commonPage = new System.Windows.Forms.TabPage(); + this.groupData = new System.Windows.Forms.GroupBox(); + this.dataPanel = new System.Windows.Forms.TableLayoutPanel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.label3 = new System.Windows.Forms.Label(); + this.comments = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.priority = new System.Windows.Forms.NumericUpDown(); + this.networks = new System.Windows.Forms.TabPage(); + this.positiveNets = new System.Windows.Forms.CheckBox(); + this.label4 = new System.Windows.Forms.Label(); + this.nets = new System.Windows.Forms.CheckedListBox(); + this.tableLayoutPanel1.SuspendLayout(); + this.tabs.SuspendLayout(); + this.commonPage.SuspendLayout(); + this.groupData.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.priority)).BeginInit(); + this.networks.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // tabs + // + resources.ApplyResources(this.tabs, "tabs"); + this.tabs.Controls.Add(this.commonPage); + this.tabs.Controls.Add(this.networks); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + // + // commonPage + // + this.commonPage.Controls.Add(this.groupData); + this.commonPage.Controls.Add(this.groupBox1); + resources.ApplyResources(this.commonPage, "commonPage"); + this.commonPage.Name = "commonPage"; + this.commonPage.UseVisualStyleBackColor = true; + // + // groupData + // + resources.ApplyResources(this.groupData, "groupData"); + this.groupData.Controls.Add(this.dataPanel); + this.groupData.Name = "groupData"; + this.groupData.TabStop = false; + // + // dataPanel + // + resources.ApplyResources(this.dataPanel, "dataPanel"); + this.dataPanel.Name = "dataPanel"; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.label3, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.comments, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.priority, 1, 2); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // comments + // + resources.ApplyResources(this.comments, "comments"); + this.comments.Name = "comments"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // priority + // + resources.ApplyResources(this.priority, "priority"); + this.priority.Maximum = new decimal(new int[] { + 10, + 0, + 0, + 0}); + this.priority.Minimum = new decimal(new int[] { + 10, + 0, + 0, + -2147483648}); + this.priority.Name = "priority"; + this.priority.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + // + // networks + // + this.networks.Controls.Add(this.positiveNets); + this.networks.Controls.Add(this.label4); + this.networks.Controls.Add(this.nets); + resources.ApplyResources(this.networks, "networks"); + this.networks.Name = "networks"; + this.networks.UseVisualStyleBackColor = true; + // + // positiveNets + // + resources.ApplyResources(this.positiveNets, "positiveNets"); + this.positiveNets.AutoEllipsis = true; + this.positiveNets.Checked = true; + this.positiveNets.CheckState = System.Windows.Forms.CheckState.Checked; + this.positiveNets.Name = "positiveNets"; + this.positiveNets.UseVisualStyleBackColor = true; + this.positiveNets.CheckedChanged += new System.EventHandler(this.positiveNets_CheckedChanged); + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // nets + // + resources.ApplyResources(this.nets, "nets"); + this.nets.FormattingEnabled = true; + this.nets.Name = "nets"; + // + // TransportForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tabs); + this.Controls.Add(this.tableLayoutPanel1); + this.DoubleBuffered = true; + this.Name = "TransportForm"; + this.Load += new System.EventHandler(this.Transport_Load); + this.tableLayoutPanel1.ResumeLayout(false); + this.tabs.ResumeLayout(false); + this.commonPage.ResumeLayout(false); + this.groupData.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.priority)).EndInit(); + this.networks.ResumeLayout(false); + this.networks.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage commonPage; + private System.Windows.Forms.GroupBox groupData; + private System.Windows.Forms.TableLayoutPanel dataPanel; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox comments; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox name; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.NumericUpDown priority; + private System.Windows.Forms.TabPage networks; + private System.Windows.Forms.CheckedListBox nets; + private System.Windows.Forms.CheckBox positiveNets; + private System.Windows.Forms.Label label4; + + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/TransportForm.cs b/client/administration/UdsAdmin/forms/TransportForm.cs new file mode 100644 index 000000000..1a5fa5669 --- /dev/null +++ b/client/administration/UdsAdmin/forms/TransportForm.cs @@ -0,0 +1,158 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class TransportForm : Form + { + string _id; + xmlrpc.GuiField[] _flds; + xmlrpc.GuiFieldValue[] _fldValues; + string _transportName; + string _transportType; + + public TransportForm(string TransportName, string transportType, Icon icon) + { + InitializeComponent(); + _fldValues = null; + _id = null; + _flds = null; + _transportName = TransportName; + _transportType = transportType; + Icon = icon; + // Read networks + nets.Items.AddRange(xmlrpc.UdsAdminService.GetNetworks()); + Text = Strings.titleTransport; + } + + public void setData(string name, string comments, string id, xmlrpc.GuiFieldValue[] data) + { + this.name.Text = name; + this.comments.Text = comments; + this.priority.Value = Convert.ToInt32(xmlrpc.GuiFieldValue.getData(data, "priority")); + this.positiveNets.Checked = xmlrpc.GuiFieldValue.getData(data, "positiveNet") == xmlrpc.Constants.TRUE; + this._id = id; + _fldValues = data; + // Fill networks + foreach( string netId in xmlrpc.UdsAdminService.GetNetworksForTransport(id)) + { + for( int i = 0; i < nets.Items.Count; i++ ) + { + xmlrpc.Network net = (xmlrpc.Network)nets.Items[i]; + if (net.id == netId) + nets.SetItemChecked(i, true); + } + } + } + + private void Transport_Load(object sender, EventArgs e) + { + _flds = xmlrpc.UdsAdminService.GetTransportGui(_transportType); + if (_flds == null) + { + Close(); + return; + } + Size sz = gui.DinamycFieldsManager.PutFields(dataPanel, _flds, _fldValues); + groupData.Size = new Size(groupData.Size.Width, 32 + sz.Height); + Size wSize = new Size(); + wSize.Width = Size.Width + 64; + wSize.Height = groupData.Location.Y + tableLayoutPanel1.Size.Height + groupData.Size.Height + 96; + Size = MinimumSize = MaximumSize = wSize; + if (_flds.Length == 0) + groupData.Visible = false; + Text = _transportName; + //this.Location = System.Windows.Forms.Cursor.Position; + + // Networks + positiveNets_CheckedChanged(null, null); + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + xmlrpc.GuiFieldValue[] data; + try { + data = gui.DinamycFieldsManager.ReadFields(dataPanel, _flds); + } + catch (gui.DinamycFieldsManager.ValidationError err) + { + gui.UserNotifier.notifyValidationException(err); + return; + } + try { + if (_id == null) + _id = xmlrpc.UdsAdminService.CreateTransport(name.Text, comments.Text, Convert.ToInt32(priority.Value), positiveNets.Checked, _transportType, data); + else + xmlrpc.UdsAdminService.ModifyTransport(name.Text, comments.Text, Convert.ToInt32(priority.Value), positiveNets.Checked, _id, data); + List ids = new List(); + foreach (xmlrpc.Network net in nets.CheckedItems) + ids.Add(net.id); + xmlrpc.UdsAdminService.SetNetworksForTransport(_id, ids.ToArray() ); + + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + + } + + private void positiveNets_CheckedChanged(object sender, EventArgs e) + { + if (positiveNets.Checked == true) + { + positiveNets.Text = Strings.positiveNetCheck; + positiveNets.BackColor = gui.Colors.ActiveBackColor; + positiveNets.ForeColor = gui.Colors.ActiveForeColor; + } + else + { + positiveNets.Text = Strings.negativeNetCheck; + positiveNets.BackColor = gui.Colors.InactiveBackColor; + positiveNets.ForeColor = gui.Colors.InactiveForeColor; + } + } + + } +} diff --git a/client/administration/UdsAdmin/forms/TransportForm.de.resx b/client/administration/UdsAdmin/forms/TransportForm.de.resx new file mode 100644 index 000000000..54f29b232 --- /dev/null +++ b/client/administration/UdsAdmin/forms/TransportForm.de.resx @@ -0,0 +1,672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 143, 23 + + + 0 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 350, 7 + + + 144, 23 + + + 1 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Bottom + + + 0, 331 + + + 1 + + + 497, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + Top, Left, Right + + + 2 + + + 12, 19 + + + 2 + + + 200, 100 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 5, 132 + + + 453, 139 + + + 9 + + + Transportdaten + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 0 + + + Top, Left, Right + + + 2 + + + Left + + + True + + + NoControl + + + 3, 57 + + + 38, 13 + + + 4 + + + Priorität + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + 82, 28 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Left + + + True + + + NoControl + + + 3, 31 + + + 56, 13 + + + 1 + + + Kommentare + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + Left + + + True + + + NoControl + + + 3, 6 + + + 35, 13 + + + 0 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + 82, 53 + + + 63, 20 + + + 5 + + + priority + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + 11, 19 + + + 3 + + + 414, 77 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="priority" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + 6, 6 + + + 452, 120 + + + 8 + + + Allgemeine Daten + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 1 + + + 4, 22 + + + 3, 3, 3, 3 + + + 464, 286 + + + 0 + + + Verkehr + + + commonPage + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + Bottom, Left, Right + + + Button + + + 7, 242 + + + 451, 23 + + + 2 + + + checkBox1 + + + MiddleCenter + + + positiveNets + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 0 + + + Bottom, Left + + + True + + + 7, 268 + + + 244, 13 + + + 1 + + + Hinweis: Keine Netze ausgewählt bedeutet alle Netzwerke + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 1 + + + Top, Bottom, Left, Right + + + 7, 7 + + + 451, 229 + + + 0 + + + nets + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 2 + + + 4, 22 + + + 3, 3, 3, 3 + + + 464, 286 + + + 1 + + + Damit verbundenen Netzwerke + + + networks + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + 13, 13 + + + 472, 312 + + + 1 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 497, 364 + + + Manual + + + Transport + + + TransportForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/TransportForm.es.resx b/client/administration/UdsAdmin/forms/TransportForm.es.resx new file mode 100644 index 000000000..64eafd06c --- /dev/null +++ b/client/administration/UdsAdmin/forms/TransportForm.es.resx @@ -0,0 +1,672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 143, 23 + + + 0 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 350, 7 + + + 144, 23 + + + 1 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Bottom + + + 0, 331 + + + 1 + + + 497, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + Top, Left, Right + + + 2 + + + 12, 19 + + + 2 + + + 200, 100 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 5, 132 + + + 453, 139 + + + 9 + + + Datos de transporte + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 0 + + + Top, Left, Right + + + 2 + + + Left + + + True + + + NoControl + + + 3, 57 + + + 38, 13 + + + 4 + + + Prioridad + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + 82, 28 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Left + + + True + + + NoControl + + + 3, 31 + + + 56, 13 + + + 1 + + + Comentarios + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + Left + + + True + + + NoControl + + + 3, 6 + + + 35, 13 + + + 0 + + + Nombre + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + 82, 53 + + + 63, 20 + + + 5 + + + priority + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + 11, 19 + + + 3 + + + 414, 77 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="priority" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + 6, 6 + + + 452, 120 + + + 8 + + + Datos comunes + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 1 + + + 4, 22 + + + 3, 3, 3, 3 + + + 464, 286 + + + 0 + + + Transporte + + + commonPage + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + Bottom, Left, Right + + + Button + + + 7, 242 + + + 451, 23 + + + 2 + + + checkBox1 + + + MiddleCenter + + + positiveNets + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 0 + + + Bottom, Left + + + True + + + 7, 268 + + + 244, 13 + + + 1 + + + Nota: Ninguno seleccionadas de redes significa todas las redes + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 1 + + + Top, Bottom, Left, Right + + + 7, 7 + + + 451, 229 + + + 0 + + + nets + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 2 + + + 4, 22 + + + 3, 3, 3, 3 + + + 464, 286 + + + 1 + + + Redes asociadas + + + networks + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + 13, 13 + + + 472, 312 + + + 1 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 497, 364 + + + Manual + + + Transport + + + TransportForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/TransportForm.fr.resx b/client/administration/UdsAdmin/forms/TransportForm.fr.resx new file mode 100644 index 000000000..d7ff8ca7e --- /dev/null +++ b/client/administration/UdsAdmin/forms/TransportForm.fr.resx @@ -0,0 +1,672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 143, 23 + + + 0 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 350, 7 + + + 144, 23 + + + 1 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Bottom + + + 0, 331 + + + 1 + + + 497, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + Top, Left, Right + + + 2 + + + 12, 19 + + + 2 + + + 200, 100 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 5, 132 + + + 453, 139 + + + 9 + + + Données de transport + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 0 + + + Top, Left, Right + + + 2 + + + Left + + + True + + + NoControl + + + 3, 57 + + + 38, 13 + + + 4 + + + Priorité + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + 82, 28 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Left + + + True + + + NoControl + + + 3, 31 + + + 56, 13 + + + 1 + + + Commentaires + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + Left + + + True + + + NoControl + + + 3, 6 + + + 35, 13 + + + 0 + + + Nom + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + 82, 53 + + + 63, 20 + + + 5 + + + priority + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + 11, 19 + + + 3 + + + 414, 77 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="priority" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + 6, 6 + + + 452, 120 + + + 8 + + + Données communes + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 1 + + + 4, 22 + + + 3, 3, 3, 3 + + + 464, 286 + + + 0 + + + Transport + + + commonPage + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + Bottom, Left, Right + + + Button + + + 7, 242 + + + 451, 23 + + + 2 + + + checkBox1 + + + MiddleCenter + + + positiveNets + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 0 + + + Bottom, Left + + + True + + + 7, 268 + + + 244, 13 + + + 1 + + + Note : Aucun réseaux sélectionnés signifie tous les réseaux + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 1 + + + Top, Bottom, Left, Right + + + 7, 7 + + + 451, 229 + + + 0 + + + nets + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 2 + + + 4, 22 + + + 3, 3, 3, 3 + + + 464, 286 + + + 1 + + + Réseaux associés + + + networks + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + 13, 13 + + + 472, 312 + + + 1 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 497, 364 + + + Manual + + + Transport + + + TransportForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/TransportForm.resx b/client/administration/UdsAdmin/forms/TransportForm.resx new file mode 100644 index 000000000..7af6b84da --- /dev/null +++ b/client/administration/UdsAdmin/forms/TransportForm.resx @@ -0,0 +1,861 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 135, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Bottom, Left, Right + + + 332, 7 + + + 135, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Bottom + + + 0, 331 + + + 1 + + + 470, 33 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Bottom, Left, Right + + + commonPage + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + networks + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + 13, 13 + + + 445, 312 + + + 1 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 0 + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 1 + + + 4, 23 + + + 3, 3, 3, 3 + + + 437, 285 + + + 0 + + + Transport + + + commonPage + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + Top, Left, Right + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 5, 132 + + + 426, 139 + + + 9 + + + Transport Data + + + groupData + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 0 + + + 2 + + + Fill + + + 3, 16 + + + 2 + + + 420, 120 + + + 0 + + + dataPanel + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupData + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Percent,50,Percent,50" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + Top, Left, Right + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="priority" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + 6, 6 + + + 425, 120 + + + 8 + + + Common Data + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + commonPage + + + 1 + + + 2 + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + priority + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + 11, 19 + + + 3 + + + 414, 77 + + + 3 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label3" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="comments" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="priority" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,19,29825,Percent,80,70175" /><Rows Styles="Percent,33,Percent,33,Percent,34" /></TableLayoutSettings> + + + Left + + + True + + + NoControl + + + 3, 57 + + + 38, 13 + + + 4 + + + Priority + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + 82, 28 + + + 320, 20 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Left + + + True + + + NoControl + + + 3, 31 + + + 56, 13 + + + 1 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + 82, 3 + + + 320, 20 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 3 + + + Left + + + True + + + NoControl + + + 3, 6 + + + 35, 13 + + + 0 + + + Name + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 4 + + + 82, 53 + + + 63, 20 + + + 5 + + + priority + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 5 + + + positiveNets + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 0 + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 1 + + + nets + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 2 + + + 4, 23 + + + 3, 3, 3, 3 + + + 437, 285 + + + 1 + + + Associated Networks + + + networks + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + Bottom, Left, Right + + + Button + + + 7, 242 + + + 451, 23 + + + 2 + + + checkBox1 + + + MiddleCenter + + + positiveNets + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 0 + + + Bottom, Left + + + True + + + 7, 268 + + + 244, 13 + + + 1 + + + Note: None networks selected means all networks + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 1 + + + Top, Bottom, Left, Right + + + 7, 7 + + + 451, 229 + + + 0 + + + nets + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + networks + + + 2 + + + True + + + 6, 13 + + + 470, 364 + + + CenterParent + + + Transport + + + TransportForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserForm.Designer.cs b/client/administration/UdsAdmin/forms/UserForm.Designer.cs new file mode 100644 index 000000000..9b60d48fe --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserForm.Designer.cs @@ -0,0 +1,284 @@ +namespace UdsAdmin.forms +{ + partial class UserForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UserForm)); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.check = new System.Windows.Forms.Button(); + this.tabs = new System.Windows.Forms.TabControl(); + this.user = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.realName = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.comments = new System.Windows.Forms.TextBox(); + this.searchButton = new System.Windows.Forms.Button(); + this.userNameLabel = new System.Windows.Forms.Label(); + this.state = new System.Windows.Forms.ComboBox(); + this.passwordLabel = new System.Windows.Forms.Label(); + this.staffMemberLabel = new System.Windows.Forms.Label(); + this.adminLabel = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.password = new System.Windows.Forms.TextBox(); + this.staffMember = new System.Windows.Forms.CheckBox(); + this.admin = new System.Windows.Forms.CheckBox(); + this.group = new System.Windows.Forms.TabPage(); + this.groupsList = new System.Windows.Forms.CheckedListBox(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.tabs.SuspendLayout(); + this.user.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.group.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // tableLayoutPanel3 + // + resources.ApplyResources(this.tableLayoutPanel3, "tableLayoutPanel3"); + this.tableLayoutPanel3.Controls.Add(this.check, 1, 0); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + // + // check + // + resources.ApplyResources(this.check, "check"); + this.check.Name = "check"; + this.check.UseVisualStyleBackColor = true; + // + // tabs + // + resources.ApplyResources(this.tabs, "tabs"); + this.tabs.Controls.Add(this.user); + this.tabs.Controls.Add(this.group); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + // + // user + // + this.user.Controls.Add(this.tableLayoutPanel1); + resources.ApplyResources(this.user, "user"); + this.user.Name = "user"; + this.user.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.realName, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.label4, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.name, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.comments, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.searchButton, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.userNameLabel, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.state, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.passwordLabel, 0, 6); + this.tableLayoutPanel1.Controls.Add(this.staffMemberLabel, 0, 4); + this.tableLayoutPanel1.Controls.Add(this.adminLabel, 0, 5); + this.tableLayoutPanel1.Controls.Add(this.label5, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.password, 1, 6); + this.tableLayoutPanel1.Controls.Add(this.staffMember, 1, 4); + this.tableLayoutPanel1.Controls.Add(this.admin, 1, 5); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // realName + // + this.tableLayoutPanel1.SetColumnSpan(this.realName, 2); + resources.ApplyResources(this.realName, "realName"); + this.realName.Name = "realName"; + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // name + // + resources.ApplyResources(this.name, "name"); + this.name.Name = "name"; + // + // comments + // + this.tableLayoutPanel1.SetColumnSpan(this.comments, 2); + resources.ApplyResources(this.comments, "comments"); + this.comments.Name = "comments"; + // + // searchButton + // + this.searchButton.Image = global::UdsAdmin.Images.find16; + resources.ApplyResources(this.searchButton, "searchButton"); + this.searchButton.Name = "searchButton"; + this.searchButton.UseVisualStyleBackColor = true; + this.searchButton.Click += new System.EventHandler(this.searchButton_Click_1); + // + // userNameLabel + // + resources.ApplyResources(this.userNameLabel, "userNameLabel"); + this.userNameLabel.Name = "userNameLabel"; + // + // state + // + this.tableLayoutPanel1.SetColumnSpan(this.state, 2); + resources.ApplyResources(this.state, "state"); + this.state.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.state.FormattingEnabled = true; + this.state.Name = "state"; + // + // passwordLabel + // + resources.ApplyResources(this.passwordLabel, "passwordLabel"); + this.passwordLabel.Name = "passwordLabel"; + // + // staffMemberLabel + // + resources.ApplyResources(this.staffMemberLabel, "staffMemberLabel"); + this.staffMemberLabel.Name = "staffMemberLabel"; + // + // adminLabel + // + resources.ApplyResources(this.adminLabel, "adminLabel"); + this.adminLabel.Name = "adminLabel"; + // + // label5 + // + resources.ApplyResources(this.label5, "label5"); + this.label5.Name = "label5"; + // + // password + // + this.tableLayoutPanel1.SetColumnSpan(this.password, 2); + resources.ApplyResources(this.password, "password"); + this.password.Name = "password"; + this.password.UseSystemPasswordChar = true; + // + // staffMember + // + resources.ApplyResources(this.staffMember, "staffMember"); + this.staffMember.Name = "staffMember"; + this.staffMember.UseVisualStyleBackColor = true; + // + // admin + // + resources.ApplyResources(this.admin, "admin"); + this.admin.Name = "admin"; + this.admin.UseVisualStyleBackColor = true; + // + // group + // + this.group.Controls.Add(this.groupsList); + resources.ApplyResources(this.group, "group"); + this.group.Name = "group"; + this.group.UseVisualStyleBackColor = true; + // + // groupsList + // + this.groupsList.FormattingEnabled = true; + resources.ApplyResources(this.groupsList, "groupsList"); + this.groupsList.Name = "groupsList"; + // + // UserForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tabs); + this.Controls.Add(this.tableLayoutPanel2); + this.Name = "UserForm"; + this.Load += new System.EventHandler(this.UserForm_Load); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.tabs.ResumeLayout(false); + this.user.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.group.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Button check; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage user; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label staffMemberLabel; + private System.Windows.Forms.Label label2; + public System.Windows.Forms.TextBox name; + public System.Windows.Forms.TextBox comments; + private System.Windows.Forms.Button searchButton; + private System.Windows.Forms.TabPage group; + private System.Windows.Forms.CheckedListBox groupsList; + public System.Windows.Forms.TextBox realName; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox state; + public System.Windows.Forms.TextBox password; + private System.Windows.Forms.Label passwordLabel; + private System.Windows.Forms.Label adminLabel; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.CheckBox staffMember; + private System.Windows.Forms.CheckBox admin; + private System.Windows.Forms.Label userNameLabel; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserForm.cs b/client/administration/UdsAdmin/forms/UserForm.cs new file mode 100644 index 000000000..d24b8d762 --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserForm.cs @@ -0,0 +1,192 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class UserForm : Form + { + private xmlrpc.Authenticator _auth; + private xmlrpc.AuthenticatorType _authType; + private xmlrpc.User _user; + private string[] states = new string[] { xmlrpc.Constants.STATE_ACTIVE, xmlrpc.Constants.STATE_INACTIVE, xmlrpc.Constants.STATE_BLOCKED }; + + public UserForm(xmlrpc.Authenticator auth, xmlrpc.AuthenticatorType authType, xmlrpc.User user) + { + _auth = auth; + _authType = authType; + _user = user; + + InitializeComponent(); + Text = Strings.user; + } + + private void UserForm_Load(object sender, EventArgs e) + { + state.Items.AddRange(new string[] { Strings.active, Strings.inactive, Strings.blocked }); + state.SelectedIndex = 0; + + if (_authType.canSearchUsers) + { + searchButton.Enabled = true; + check.Visible = true; + } + else + { + searchButton.Enabled = false; + check.Visible = false; + } + + if (_authType.needsPassword) + { + password.Visible = passwordLabel.Visible = true; + } + else + { + password.Visible = passwordLabel.Visible = false; + } + + userNameLabel.Text = _authType.userNameLabel; + passwordLabel.Text = _authType.passwordLabel; + + + xmlrpc.Group[] groups = new xmlrpc.Group[0]; + if (_user.id != null) + { + groups = _user.groups; // xmlrpc.UdsAdminService.GetUserGroups(_user.id); + name.Text = _user.name; realName.Text = _user.realName; + comments.Text = _user.comments; password.Text = _user.password; + name.ReadOnly = true; + searchButton.Enabled = false; + for( int i = 0; i < states.Length; i++ ) + if( states[i] == _user.state ) + { + state.SelectedIndex = i; + break; + } + _user.comments = comments.Text; + + } + + if (_authType.isExternalSource ) + { + groupsList.Enabled = false; + if (_user.id != null) + foreach (xmlrpc.Group grp in groups) + groupsList.Items.Add(grp, true); + } + else + { + (tabs.TabPages["group"] as Control).Enabled = true; + try + { + xmlrpc.Group[] grps = xmlrpc.UdsAdminService.GetAuthenticatorGroups(_auth.id); + foreach (xmlrpc.Group grp in grps) + { + bool active = groups.Length > 0 && Array.Find(groups, g => g.id == grp.id).active; + groupsList.Items.Add(grp, active); + } + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + staffMember.Enabled = staffMemberLabel.Enabled = admin.Enabled = adminLabel.Enabled = xmlrpc.UdsAdminService.isAdmin; + staffMember.Checked = _user.staffMember; + admin.Checked = _user.isAdmin; + + //this.Location = System.Windows.Forms.Cursor.Position; + } + + private void accept_Click(object sender, EventArgs e) + { + if (name.Text.Trim().Length == 0) + { + gui.UserNotifier.notifyError(Strings.nameRequired); + return; + } + try + { + if (_authType.isExternalSource == false) + { + _user.groups = new xmlrpc.Group[groupsList.CheckedItems.Count]; + int n = 0; + foreach (xmlrpc.Group grp in groupsList.CheckedItems) + { + _user.groups[n++] = grp; + } + } + _user.idParent = _auth.id; _user.name = name.Text; _user.realName = realName.Text; + _user.comments = comments.Text; _user.state = states[state.SelectedIndex]; + _user.password = password.Text; + // If the user is not admin, server will ignore these parameters + _user.isAdmin = admin.Checked; + _user.staffMember = staffMember.Checked; + if (_user.id == null || _user.id == "") + { + // New user + _user.id = ""; + _user.oldPassword = ""; + xmlrpc.UdsAdminService.CreateUser(_user); + } + else + { + xmlrpc.UdsAdminService.ModifyUser(_user); + } + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (CookComputing.XmlRpc.XmlRpcFaultException ex) + { + gui.UserNotifier.notifyRpcException(ex); + } + } + + private void cancel_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + + private void searchButton_Click_1(object sender, EventArgs e) + { + SearchForm form = new SearchForm(SearchForm.Type.userSearch, _auth, name.Text); + if (form.ShowDialog() == System.Windows.Forms.DialogResult.Yes) + name.Text = form.selection; + } + } +} diff --git a/client/administration/UdsAdmin/forms/UserForm.de.resx b/client/administration/UdsAdmin/forms/UserForm.de.resx new file mode 100644 index 000000000..815e72afd --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserForm.de.resx @@ -0,0 +1,494 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Überprüfen Sie Namen + + + check + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + realName + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Richtiger Name + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Kommentare + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Benutzername + + + userNameLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + state + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + Passwort + + + passwordLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Mitarbeiter + + + staffMemberLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Admin + + + adminLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + Staat + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + password + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + staffMember + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + admin + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + user + + + 0 + + + Benutzer + + + user + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + groupsList + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + group + + + 0 + + + Gruppen + + + group + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeBwAAZRAMAGMQDQBkEA0AZxINAGcUDwBrFhAAbRgMAG8YEQBxGxAAbxsTAHAbEwByGxQAdRwSAHUd + EgByHhUAex8TAH8iFAB3IxcAdiMZAIEkFQCGJRUAkiYOAHclGwCLKREAhyoaAKArDwCGKxsAlCwXAI0t + GAB8LCIAqi4MAJQtGACULxUAey8iAIEvIACQMB4AmjEZAIMxIwCJMiAAmTMZAJU0HQCGNCYAozUaAJ81 + HgCVNiAAiDcmAKU4HACpOBsArDsdALo8FQCrPh8AuEIfALNDIQC5RCEAw0UdAJJEMgDCRiAApEUvALlH + IwCURzAAsUksAMZKIgC8TCMAv00nAM5NIgDDTikAvFIrANRSJADUUyUAvVQtAKJVPADTVigAyFctANBZ + KwDXWSkA3VopAM1cLgDUXiwAz14vAOFgLQDkZjEAwGhCAONrNQDEa0QAxm1HAO1xNgClbGMA6XM6ALdz + WQDvfUAA7X5CAO5/QgDvf0IA7oBDAO+ERQDuhEYA74RGAOqGSgDohksA6YZLAOmITQDpiU0A8IlMAOuK + TwDukFQA6pRbAPGVVwCwjosAzZJ0ANuqkADeycYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAcHBwcHA8cFRwUnAicHBwcHBwBxhOWl1eXlxdUyEAbHBwcBpDa2hkZWZlY2dGH3BwcHAsP2pp + YF9hW1hRSzJvcHBwcFVwSUpIRURBOTYWcHBwcFdZKRwrO0AxLygdcHBwcHBwcHAQM01PNRUOcHBwcHBw + cHBwcBEkOhkEcHBwcHBwcHBwcHBwG0I9cHBwcHBwcHBwcHBwLi03VmJwcHBwcHBwcHBwChcUNFBwcHBw + cHBwcHBwcAYFCCU+THBwcHBwcHBwcHAMAQINIDBwcHBwcHBwcHBwKkcjAwsncHBwcHBwcHBwcBJubQkm + cHBwcHBwcHBwcHBwHjgPE3BwcHBwcPqvAADAAQAAwAMAAMABAADoAwAAwAcAAPgPAAD8HwAA/j8AAPwf + AAD4PwAA+B8AAPgfAAD4HwAA+D8AAPw/AAA= + + + + Users + + + UserForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserForm.es.resx b/client/administration/UdsAdmin/forms/UserForm.es.resx new file mode 100644 index 000000000..2a549bf28 --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserForm.es.resx @@ -0,0 +1,494 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Nombre del cheque + + + check + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + realName + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + Nombre real + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Comentarios + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Nombre de usuario + + + userNameLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + state + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + Contraseña + + + passwordLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Personal + + + staffMemberLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Admin + + + adminLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + Estado + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + password + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + staffMember + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + admin + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + user + + + 0 + + + Usuario + + + user + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + groupsList + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + group + + + 0 + + + Grupos + + + group + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeBwAAZRAMAGMQDQBkEA0AZxINAGcUDwBrFhAAbRgMAG8YEQBxGxAAbxsTAHAbEwByGxQAdRwSAHUd + EgByHhUAex8TAH8iFAB3IxcAdiMZAIEkFQCGJRUAkiYOAHclGwCLKREAhyoaAKArDwCGKxsAlCwXAI0t + GAB8LCIAqi4MAJQtGACULxUAey8iAIEvIACQMB4AmjEZAIMxIwCJMiAAmTMZAJU0HQCGNCYAozUaAJ81 + HgCVNiAAiDcmAKU4HACpOBsArDsdALo8FQCrPh8AuEIfALNDIQC5RCEAw0UdAJJEMgDCRiAApEUvALlH + IwCURzAAsUksAMZKIgC8TCMAv00nAM5NIgDDTikAvFIrANRSJADUUyUAvVQtAKJVPADTVigAyFctANBZ + KwDXWSkA3VopAM1cLgDUXiwAz14vAOFgLQDkZjEAwGhCAONrNQDEa0QAxm1HAO1xNgClbGMA6XM6ALdz + WQDvfUAA7X5CAO5/QgDvf0IA7oBDAO+ERQDuhEYA74RGAOqGSgDohksA6YZLAOmITQDpiU0A8IlMAOuK + TwDukFQA6pRbAPGVVwCwjosAzZJ0ANuqkADeycYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAcHBwcHA8cFRwUnAicHBwcHBwBxhOWl1eXlxdUyEAbHBwcBpDa2hkZWZlY2dGH3BwcHAsP2pp + YF9hW1hRSzJvcHBwcFVwSUpIRURBOTYWcHBwcFdZKRwrO0AxLygdcHBwcHBwcHAQM01PNRUOcHBwcHBw + cHBwcBEkOhkEcHBwcHBwcHBwcHBwG0I9cHBwcHBwcHBwcHBwLi03VmJwcHBwcHBwcHBwChcUNFBwcHBw + cHBwcHBwcAYFCCU+THBwcHBwcHBwcHAMAQINIDBwcHBwcHBwcHBwKkcjAwsncHBwcHBwcHBwcBJubQkm + cHBwcHBwcHBwcHBwHjgPE3BwcHBwcPqvAADAAQAAwAMAAMABAADoAwAAwAcAAPgPAAD8HwAA/j8AAPwf + AAD4PwAA+B8AAPgfAAD4HwAA+D8AAPw/AAA= + + + + Users + + + UserForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserForm.fr.resx b/client/administration/UdsAdmin/forms/UserForm.fr.resx new file mode 100644 index 000000000..68e33b885 --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserForm.fr.resx @@ -0,0 +1,494 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Vérifiez le nom + + + check + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + realName + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + De son vrai nom + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Commentaires + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Nom d'utilisateur + + + userNameLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + state + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + Mot de passe + + + passwordLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Personnel + + + staffMemberLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Admin + + + adminLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + État + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + password + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + staffMember + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + admin + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + user + + + 0 + + + Utilisateur + + + user + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + groupsList + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + group + + + 0 + + + Groupes + + + group + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeBwAAZRAMAGMQDQBkEA0AZxINAGcUDwBrFhAAbRgMAG8YEQBxGxAAbxsTAHAbEwByGxQAdRwSAHUd + EgByHhUAex8TAH8iFAB3IxcAdiMZAIEkFQCGJRUAkiYOAHclGwCLKREAhyoaAKArDwCGKxsAlCwXAI0t + GAB8LCIAqi4MAJQtGACULxUAey8iAIEvIACQMB4AmjEZAIMxIwCJMiAAmTMZAJU0HQCGNCYAozUaAJ81 + HgCVNiAAiDcmAKU4HACpOBsArDsdALo8FQCrPh8AuEIfALNDIQC5RCEAw0UdAJJEMgDCRiAApEUvALlH + IwCURzAAsUksAMZKIgC8TCMAv00nAM5NIgDDTikAvFIrANRSJADUUyUAvVQtAKJVPADTVigAyFctANBZ + KwDXWSkA3VopAM1cLgDUXiwAz14vAOFgLQDkZjEAwGhCAONrNQDEa0QAxm1HAO1xNgClbGMA6XM6ALdz + WQDvfUAA7X5CAO5/QgDvf0IA7oBDAO+ERQDuhEYA74RGAOqGSgDohksA6YZLAOmITQDpiU0A8IlMAOuK + TwDukFQA6pRbAPGVVwCwjosAzZJ0ANuqkADeycYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAcHBwcHA8cFRwUnAicHBwcHBwBxhOWl1eXlxdUyEAbHBwcBpDa2hkZWZlY2dGH3BwcHAsP2pp + YF9hW1hRSzJvcHBwcFVwSUpIRURBOTYWcHBwcFdZKRwrO0AxLygdcHBwcHBwcHAQM01PNRUOcHBwcHBw + cHBwcBEkOhkEcHBwcHBwcHBwcHBwG0I9cHBwcHBwcHBwcHBwLi03VmJwcHBwcHBwcHBwChcUNFBwcHBw + cHBwcHBwcAYFCCU+THBwcHBwcHBwcHAMAQINIDBwcHBwcHBwcHBwKkcjAwsncHBwcHBwcHBwcBJubQkm + cHBwcHBwcHBwcHBwHjgPE3BwcHBwcPqvAADAAQAAwAMAAMABAADoAwAAwAcAAPgPAAD8HwAA/j8AAPwf + AAD4PwAA+B8AAPgfAAD4HwAA+D8AAPw/AAA= + + + + Users + + + UserForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserForm.resx b/client/administration/UdsAdmin/forms/UserForm.resx new file mode 100644 index 000000000..77354cee2 --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserForm.resx @@ -0,0 +1,859 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3 + + + + Bottom, Left, Right + + + + 3, 7 + + + 119, 23 + + + 8 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 295, 7 + + + 120, 23 + + + 9 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + 3 + + + Fill + + + 33, 3 + + + 85, 21 + + + 7 + + + Check name + + + check + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel3 + + + 0 + + + 128, 3 + + + 1 + + + 152, 27 + + + 2 + + + tableLayoutPanel3 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="check" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,20,Percent,60,Percent,20" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Bottom + + + 0, 260 + + + 1 + + + 418, 33 + + + 6 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="tableLayoutPanel3" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + Top, Left, Right + + + Top, Left, Right + + + 3 + + + Fill + + + 91, 32 + + + 279, 20 + + + 3 + + + realName + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + True + + + 3, 35 + + + 3, 6, 3, 0 + + + 60, 13 + + + 8 + + + Real Name + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + 3, 64 + + + 3, 6, 3, 0 + + + 56, 13 + + + 1 + + + Comments + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Fill + + + 91, 3 + + + 227, 20 + + + 1 + + + name + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + Fill + + + 91, 61 + + + 279, 20 + + + 4 + + + comments + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + NoControl + + + 324, 3 + + + 30, 20 + + + 2 + + + searchButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + True + + + NoControl + + + 3, 6 + + + 3, 6, 3, 0 + + + 60, 13 + + + 0 + + + User Name + + + userNameLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + Fill + + + 91, 88 + + + 279, 21 + + + 5 + + + state + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + True + + + 3, 172 + + + 3, 6, 3, 0 + + + 53, 13 + + + 11 + + + Password + + + passwordLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + True + + + 3, 118 + + + 3, 6, 3, 0 + + + 70, 13 + + + 7 + + + Staff Member + + + staffMemberLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + True + + + 3, 145 + + + 3, 6, 3, 0 + + + 36, 13 + + + 12 + + + Admin + + + adminLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + True + + + 3, 91 + + + 3, 6, 3, 0 + + + 32, 13 + + + 13 + + + State + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + Fill + + + 91, 169 + + + 279, 20 + + + 6 + + + password + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + True + + + Fill + + + 91, 115 + + + 227, 21 + + + 14 + + + staffMember + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + True + + + Fill + + + 91, 142 + + + 227, 21 + + + 15 + + + admin + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + 6, 6 + + + 7 + + + 373, 195 + + + 1 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + user + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="realName" Row="1" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="label4" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label2" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="name" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="comments" Row="2" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="searchButton" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="userNameLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="state" Row="3" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="passwordLabel" Row="6" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="staffMemberLabel" Row="4" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="adminLabel" Row="5" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="label5" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="password" Row="6" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="staffMember" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="admin" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Percent,27,54098,Percent,72,45902,Absolute,51" /><Rows Styles="Percent,15,Percent,15,Percent,14,Percent,14,Percent,14,Percent,14,Percent,14,Absolute,20" /></TableLayoutSettings> + + + 4, 23 + + + 3, 3, 3, 3 + + + 385, 210 + + + 0 + + + User + + + user + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 0 + + + 7, 7 + + + 372, 184 + + + 0 + + + groupsList + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + group + + + 0 + + + 4, 23 + + + 3, 3, 3, 3 + + + 385, 210 + + + 1 + + + Groups + + + group + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabs + + + 1 + + + 13, 13 + + + 393, 237 + + + 10 + + + tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 418, 293 + + + + AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeBwAAZRAMAGMQDQBkEA0AZxINAGcUDwBrFhAAbRgMAG8YEQBxGxAAbxsTAHAbEwByGxQAdRwSAHUd + EgByHhUAex8TAH8iFAB3IxcAdiMZAIEkFQCGJRUAkiYOAHclGwCLKREAhyoaAKArDwCGKxsAlCwXAI0t + GAB8LCIAqi4MAJQtGACULxUAey8iAIEvIACQMB4AmjEZAIMxIwCJMiAAmTMZAJU0HQCGNCYAozUaAJ81 + HgCVNiAAiDcmAKU4HACpOBsArDsdALo8FQCrPh8AuEIfALNDIQC5RCEAw0UdAJJEMgDCRiAApEUvALlH + IwCURzAAsUksAMZKIgC8TCMAv00nAM5NIgDDTikAvFIrANRSJADUUyUAvVQtAKJVPADTVigAyFctANBZ + KwDXWSkA3VopAM1cLgDUXiwAz14vAOFgLQDkZjEAwGhCAONrNQDEa0QAxm1HAO1xNgClbGMA6XM6ALdz + WQDvfUAA7X5CAO5/QgDvf0IA7oBDAO+ERQDuhEYA74RGAOqGSgDohksA6YZLAOmITQDpiU0A8IlMAOuK + TwDukFQA6pRbAPGVVwCwjosAzZJ0ANuqkADeycYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAcHBwcHA8cFRwUnAicHBwcHBwBxhOWl1eXlxdUyEAbHBwcBpDa2hkZWZlY2dGH3BwcHAsP2pp + YF9hW1hRSzJvcHBwcFVwSUpIRURBOTYWcHBwcFdZKRwrO0AxLygdcHBwcHBwcHAQM01PNRUOcHBwcHBw + cHBwcBEkOhkEcHBwcHBwcHBwcHBwG0I9cHBwcHBwcHBwcHBwLi03VmJwcHBwcHBwcHBwChcUNFBwcHBw + cHBwcHBwcAYFCCU+THBwcHBwcHBwcHAMAQINIDBwcHBwcHBwcHBwKkcjAwsncHBwcHBwcHBwcBJubQkm + cHBwcHBwcHBwcHBwHjgPE3BwcHBwcPqvAADAAQAAwAMAAMABAADoAwAAwAcAAPgPAAD8HwAA/j8AAPwf + AAD4PwAA+B8AAPgfAAD4HwAA+D8AAPw/AAA= + + + + CenterParent + + + Users + + + UserForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserPreferencesForm.Designer.cs b/client/administration/UdsAdmin/forms/UserPreferencesForm.Designer.cs new file mode 100644 index 000000000..afb542219 --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserPreferencesForm.Designer.cs @@ -0,0 +1,88 @@ +namespace UdsAdmin.forms +{ + partial class UserPreferencesForm + { + /// + /// Variable del diseñador requerida. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Limpiar los recursos que se estén utilizando. + /// + /// true si los recursos administrados se deben eliminar; false en caso contrario, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Código generado por el Diseñador de Windows Forms + + /// + /// Método necesario para admitir el Diseñador. No se puede modificar + /// el contenido del método con el editor de código. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UserPreferencesForm)); + this.modTabs = new System.Windows.Forms.TabControl(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.accept = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // modTabs + // + resources.ApplyResources(this.modTabs, "modTabs"); + this.modTabs.Name = "modTabs"; + this.modTabs.SelectedIndex = 0; + // + // tableLayoutPanel2 + // + resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2"); + this.tableLayoutPanel2.Controls.Add(this.accept, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cancel, 2, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + // + // accept + // + resources.ApplyResources(this.accept, "accept"); + this.accept.Name = "accept"; + this.accept.UseVisualStyleBackColor = true; + this.accept.Click += new System.EventHandler(this.accept_Click); + // + // cancel + // + resources.ApplyResources(this.cancel, "cancel"); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Name = "cancel"; + this.cancel.UseVisualStyleBackColor = true; + // + // UserPreferencesForm + // + this.AcceptButton = this.accept; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; + this.Controls.Add(this.tableLayoutPanel2); + this.Controls.Add(this.modTabs); + this.Name = "UserPreferencesForm"; + this.Load += new System.EventHandler(this.UserPreferencesForm_Load); + this.tableLayoutPanel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl modTabs; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button accept; + private System.Windows.Forms.Button cancel; + } +} \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserPreferencesForm.cs b/client/administration/UdsAdmin/forms/UserPreferencesForm.cs new file mode 100644 index 000000000..2d66f922c --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserPreferencesForm.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.forms +{ + public partial class UserPreferencesForm : Form + { + private struct PageData + { + public xmlrpc.GuiField[] fields; + public TableLayoutPanel panel; + + public PageData(xmlrpc.GuiField[] flds, TableLayoutPanel tbl) + { + fields = flds; + panel = tbl; + } + } + + private string _userId; + private List _data = new List(); + + public UserPreferencesForm(string userId) + { + _userId = userId; + InitializeComponent(); + Text = Strings.titleUserPreferences; + } + + private void UserPreferencesForm_Load(object sender, EventArgs e) + { + xmlrpc.PrefGroup[] prefs = xmlrpc.UdsAdminService.GetPrefsForUser(_userId); + Size max = new Size(0, 0); + foreach (xmlrpc.PrefGroup p in prefs) + { + TabPage page = new TabPage(p.moduleLabel); + TableLayoutPanel table = new TableLayoutPanel(); + page.Controls.Add(table); + modTabs.TabPages.Add(page); + _data.Add(new PageData(p.prefs, table)); + + Size sz = gui.DinamycFieldsManager.PutFields(table, p.prefs, null); + if (max.Width < sz.Width) + max.Width = sz.Width; + if (max.Height < sz.Height) + max.Height = sz.Height; + } + max.Height += 120; + max.Width += 56; + Size = max; + } + + private void accept_Click(object sender, EventArgs e) + { + List data = new List(); + try + { + foreach( PageData p in _data ) + { + xmlrpc.GuiFieldValue[] partial = gui.DinamycFieldsManager.ReadFields(p.panel, p.fields); + data.AddRange(partial); + } + xmlrpc.UdsAdminService.SetPrefsForUser(_userId, data.ToArray()); + DialogResult = System.Windows.Forms.DialogResult.OK; + } + catch (gui.DinamycFieldsManager.ValidationError err) + { + gui.UserNotifier.notifyValidationException(err); + return; + } + } + } +} diff --git a/client/administration/UdsAdmin/forms/UserPreferencesForm.de.resx b/client/administration/UdsAdmin/forms/UserPreferencesForm.de.resx new file mode 100644 index 000000000..97d9b06d3 --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserPreferencesForm.de.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Akzeptieren + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Abbrechen + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + UserPreferencesForm + + + UserPreferencesForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserPreferencesForm.es.resx b/client/administration/UdsAdmin/forms/UserPreferencesForm.es.resx new file mode 100644 index 000000000..41809757f --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserPreferencesForm.es.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Aceptar + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Cancelar + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + UserPreferencesForm + + + UserPreferencesForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserPreferencesForm.fr.resx b/client/administration/UdsAdmin/forms/UserPreferencesForm.fr.resx new file mode 100644 index 000000000..568042243 --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserPreferencesForm.fr.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Accepter + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Annuler + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + UserPreferencesForm + + + UserPreferencesForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/forms/UserPreferencesForm.resx b/client/administration/UdsAdmin/forms/UserPreferencesForm.resx new file mode 100644 index 000000000..1e15c992b --- /dev/null +++ b/client/administration/UdsAdmin/forms/UserPreferencesForm.resx @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Bottom, Left, Right + + + + 12, 12 + + + 344, 218 + + + + 0 + + + modTabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 3 + + + Bottom, Left, Right + + + 3, 7 + + + 104, 23 + + + 0 + + + Accept + + + accept + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 0 + + + Bottom, Left, Right + + + 260, 7 + + + 105, 23 + + + 1 + + + Cancel + + + cancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel2 + + + 1 + + + Bottom + + + 0, 236 + + + 1 + + + 368, 33 + + + 8 + + + tableLayoutPanel2 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="accept" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="cancel" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="Percent,30,Percent,40,Percent,30" /><Rows Styles="Percent,100" /></TableLayoutSettings> + + + True + + + 6, 13 + + + 368, 269 + + + CenterParent + + + UserPreferencesForm + + + UserPreferencesForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/client/administration/UdsAdmin/gui/ActionTree.cs b/client/administration/UdsAdmin/gui/ActionTree.cs new file mode 100644 index 000000000..0cccdde25 --- /dev/null +++ b/client/administration/UdsAdmin/gui/ActionTree.cs @@ -0,0 +1,577 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Drawing; + +namespace UdsAdmin.gui +{ + class ActionTree + { + // Plurar forms must be same literals as designated in actionTree in MainForm + public const string SERVICES_PROVIDERS = "ServicesProviders"; + public const string SERVICE_PROVIDER = "ServiceProvider"; + public const string DEPLOYED_SERVICES = "DeployedServices"; + public const string DEPLOYED_SERVICE = "DeployedService"; + public const string AUTHENTICATORS = "Authenticators"; + public const string AUTHENTICATOR = "Authenticator"; + public const string OS_MANAGERS = "OSManagers"; + public const string OS_MANAGER = "OSManager"; + public const string CONNECTIVITY = "Connectivity"; + public const string TRANSPORTS = "Transports"; + public const string TRANSPORT = "Transport"; + public const string NETWORKS = "Networks"; + public const string SERVICES = "Services"; + public const string SERVICE = "Service"; + public const string USERS = "Users"; + public const string GROUPS = "Groups"; + public const string ALLOWED_GROUPS = "AllowGroups"; + public const string ASSIGNED_SERVICES = "AssignService"; + public const string ASSIGNED_TRANSPORTS = "AssignedTransports"; + public const string PUBLICATIONS = "Deployments"; + public const string CACHE = "Cache"; + + + public const string DIMMED_OUT = "_2"; + + public const float UNSELECTED_OPACITY = 0.8f; + + public const string NEW_ACTION = "new"; + public const string MODIFY_ACTION = "modify"; + public const string DELETE_ACTION = "delete"; + public const string CHECK_ACTION = "test"; + public const string PUBLISH_ACTION = "publish"; + + private static Dictionary stCache = new Dictionary(); + + static public void FillServicesProviders(TreeNode node, EventHandler action) + { + ImageList lst = node.TreeView.ImageList; + node.Nodes.Clear(); + xmlrpc.ServiceProvider[] providers = xmlrpc.UdsAdminService.GetServiceProviders(); + foreach (xmlrpc.ServiceProvider prov in providers) + { + // We check if already knows about this type + if (!stCache.ContainsKey(prov.type)) + stCache.Add(prov.type, xmlrpc.UdsAdminService.GetOffersFromServiceProvider(prov.type)); + + xmlrpc.ServiceType[] offers = stCache[prov.type]; + + // Add Icons of service types to image list if they don't already exists + foreach (xmlrpc.ServiceType st in offers) + { + if (!lst.Images.ContainsKey(st.type)) + { + lst.Images.Add(st.type + DIMMED_OUT, Helpers.SetImageOpacity(Helpers.ImageFromBase64(st.icon), UNSELECTED_OPACITY)); + lst.Images.Add(st.type, Helpers.ImageFromBase64(st.icon)); + } + } + + // Menu for right-click on service provider + ContextMenuStrip menu = MenusManager.ServicesMenu(action, offers, true); + + TreeNode sp = new TreeNode(prov.name); + sp.Name = SERVICE_PROVIDER; + sp.Tag = prov; // We keep provider at tag, so we can easyly modify/delete or add new services for that provider + sp.ToolTipText = prov.typeName + ".\n" + prov.comments; + sp.ImageKey = prov.type + DIMMED_OUT; + sp.SelectedImageKey = prov.type; + sp.ContextMenuStrip = menu; + + // Meno for right-click on "Services" inside service provider + ContextMenuStrip menu2 = MenusManager.ServicesMenu(action, offers, false); + + TreeNode spChild = new TreeNode(Strings.services); + spChild.Name = SERVICES; + spChild.ToolTipText = ""; + spChild.ImageKey = SERVICES + DIMMED_OUT; + spChild.SelectedImageKey = SERVICES; + spChild.ContextMenuStrip = menu2; + sp.Nodes.Add(spChild); + + // Add to tree + node.Nodes.Add(sp); + } + } + + static public void FillServices(TreeNode node, EventHandler action) + { + TreeNode servicesTree = node.Nodes[SERVICES]; + xmlrpc.ServiceProvider sp = (xmlrpc.ServiceProvider)node.Tag; + xmlrpc.Service[] services = xmlrpc.UdsAdminService.GetServices(sp.id); + + servicesTree.Nodes.Clear(); + foreach (xmlrpc.Service s in services) + { + TreeNode se = new TreeNode(s.name); + se.Name = SERVICE; + se.Tag = s; + se.ToolTipText = s.typeName + ".\n" + s.comments; + se.ImageKey = s.type + DIMMED_OUT; + se.SelectedImageKey = s.type; + xmlrpc.ServiceType[] offers = stCache[sp.type]; + xmlrpc.ServiceType type = new xmlrpc.ServiceType(); + foreach( xmlrpc.ServiceType st in offers ) + { + if( st.type == s.type ) + { + type = st; + break; + } + } + se.ContextMenuStrip = MenusManager.ServiceMenu(action, s, type); + + servicesTree.Nodes.Add(se); + } + } + + static public void FillAuthenticators(TreeNode node, xmlrpc.AuthenticatorType[] authsTypes, EventHandler actionOnAuthClick, EventHandler actionOnUsersGroupsClick) + { + ImageList lst = node.TreeView.ImageList; + node.Nodes.Clear(); + xmlrpc.Authenticator[] auths = xmlrpc.UdsAdminService.GetAuthenticators(); + + ContextMenuStrip newMenu = MenusManager.TreeUsersGroupsMenu(actionOnUsersGroupsClick); + + foreach (xmlrpc.Authenticator auth in auths) + { + // Menu for right-click on service provider + ContextMenuStrip menu = MenusManager.AuthMenu(actionOnAuthClick); + + // authenticator type + xmlrpc.AuthenticatorType authType = gui.ActionTree.authType(auth, authsTypes); + + TreeNode a = new TreeNode(auth.name); + a.Name = AUTHENTICATOR; + a.Tag = auth; // We keep provider at tag, so we can easyly modify/delete or add new services for that provider + a.ToolTipText = auth.typeName + ".\n" + auth.comments; + a.ImageKey = auth.type + DIMMED_OUT; + a.SelectedImageKey = auth.type; + a.ContextMenuStrip = menu; + + TreeNode users = new TreeNode(Strings.users); + users.Name = USERS; + users.ToolTipText = Strings.manageUsers; + users.ImageKey = USERS + DIMMED_OUT; + users.SelectedImageKey = USERS; + if( authType.canCreateUsers ) + users.ContextMenuStrip = newMenu; + + // Udpate users for this authenticator + + a.Nodes.Add(users); + + TreeNode groups = new TreeNode(Strings.groups); + groups.Name = GROUPS; + groups.ToolTipText = Strings.manageGroups; + groups.ImageKey = GROUPS + DIMMED_OUT; + groups.SelectedImageKey = GROUPS; + groups.ContextMenuStrip = newMenu; + a.Nodes.Add(groups); + + + // Add to tree + node.Nodes.Add(a); + } + } + + static public void FillOSManagers(TreeNode node, EventHandler action) + { + xmlrpc.OSManager[] osManagers = xmlrpc.UdsAdminService.GetOSManagers(); + + node.Nodes.Clear(); + foreach (xmlrpc.OSManager osm in osManagers) + { + TreeNode se = new TreeNode(osm.name); + se.Name = OS_MANAGER; + se.Tag = osm; + se.ToolTipText = osm.typeName + ".\n" + osm.comments; + se.ImageKey = osm.type + DIMMED_OUT; + se.SelectedImageKey = osm.type; + se.ContextMenuStrip = MenusManager.OSManagerMenu(action, osm); + + node.Nodes.Add(se); + } + } + + static public void FillTransports(TreeNode transNode, EventHandler actionTransports) + { + xmlrpc.Transport[] transports = xmlrpc.UdsAdminService.GetTransports(); + + + transNode.Nodes.Clear(); + foreach (xmlrpc.Transport trans in transports) + { + TreeNode se = new TreeNode(trans.name); + se.Name = TRANSPORT; + se.Tag = trans; + se.ToolTipText = trans.typeName + ".\n" + trans.comments; + se.ImageKey = trans.type + DIMMED_OUT; + se.SelectedImageKey = trans.type; + se.ContextMenuStrip = MenusManager.TransportMenu(actionTransports, trans); + + transNode.Nodes.Add(se); + } + + + } + + static public void FillDeployedServices(TreeNode node, EventHandler action) + { + xmlrpc.DeployedService[] depServices = xmlrpc.UdsAdminService.GetDeployedServices(); + + node.Nodes.Clear(); + foreach (xmlrpc.DeployedService ds in depServices) + { + TreeNode se = new TreeNode(ds.name); + se.Name = DEPLOYED_SERVICE; + se.Tag = ds; + se.ToolTipText = ds.comments; + se.ImageKey = DEPLOYED_SERVICE + DIMMED_OUT; + se.SelectedImageKey = DEPLOYED_SERVICE; + se.ContextMenuStrip = MenusManager.DeployedServiceMenu(action, ds); + + // Add constant child nodes, that are Allowed Groups, Deployments (this only if needed), Assigned Services & Cache (also if needed) + TreeNode a = new TreeNode(Strings.assignedServices); + a.Name = ASSIGNED_SERVICES; a.Tag = ds; a.ToolTipText = Strings.assignedServicesToolTip; + a.ImageKey = ASSIGNED_SERVICES + DIMMED_OUT; a.SelectedImageKey = ASSIGNED_SERVICES; + se.Nodes.Add(a); + + if (ds.info.usesCache) + { + a = new TreeNode(Strings.cache); + a.Name = CACHE; a.Tag = ds; a.ToolTipText = Strings.cacheServicesToolTip; + a.ImageKey = CACHE + DIMMED_OUT; a.SelectedImageKey = CACHE; + se.Nodes.Add(a); + } + + if (ds.info.mustAssignManually == false) + { + a = new TreeNode(Strings.allowedGroups); + a.Name = ALLOWED_GROUPS; a.Tag = ds; a.ToolTipText = Strings.allowedGroupsToolTip; + a.ImageKey = GROUPS + DIMMED_OUT; a.SelectedImageKey = GROUPS; + se.Nodes.Add(a); + } + + // Transports are always associated with deployed services + a = new TreeNode(Strings.transports); + a.Name = ASSIGNED_TRANSPORTS; a.Tag = ds; a.ToolTipText = ""; + a.ImageKey = TRANSPORTS + DIMMED_OUT; a.SelectedImageKey = TRANSPORTS; + se.Nodes.Add(a); + + if (ds.info.needsPublication) + { + a = new TreeNode(Strings.publications); + a.Name = PUBLICATIONS; a.Tag = ds; a.ToolTipText = Strings.publicationsToolTip; + a.ImageKey = PUBLICATIONS + DIMMED_OUT; a.SelectedImageKey = PUBLICATIONS; + // Now the menu for publication + a.ContextMenuStrip = MenusManager.PublicationMenu(action); + se.Nodes.Add(a); + } + + node.Nodes.Add(se); + } + } + + static public void InitializeImageList(TreeView tree) + { + tree.ImageList = new ImageList(); + + tree.ImageList.Images.Add(SERVICES_PROVIDERS, Images.serviceProviders16); + tree.ImageList.Images.Add(SERVICES_PROVIDERS + DIMMED_OUT, Helpers.SetImageOpacity(Images.serviceProviders16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(DEPLOYED_SERVICES, Images.deployedServices16); + tree.ImageList.Images.Add(DEPLOYED_SERVICES + DIMMED_OUT, Helpers.SetImageOpacity(Images.deployedServices16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(AUTHENTICATORS, Images.authenticators16); + tree.ImageList.Images.Add(AUTHENTICATORS + DIMMED_OUT, Helpers.SetImageOpacity(Images.authenticators16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(OS_MANAGERS, Images.osmanagers16); + tree.ImageList.Images.Add(OS_MANAGERS + DIMMED_OUT, Helpers.SetImageOpacity(Images.osmanagers16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(TRANSPORTS, Images.transports16); + tree.ImageList.Images.Add(TRANSPORTS + DIMMED_OUT, Helpers.SetImageOpacity(Images.transports16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(SERVICES, Images.services16); + tree.ImageList.Images.Add(SERVICES + DIMMED_OUT, Helpers.SetImageOpacity(Images.services16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(DEPLOYED_SERVICE, Images.deployedService16); + tree.ImageList.Images.Add(DEPLOYED_SERVICE + DIMMED_OUT, Helpers.SetImageOpacity(Images.deployedService16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(ASSIGNED_SERVICES, Images.assignedServices16); + tree.ImageList.Images.Add(ASSIGNED_SERVICES + DIMMED_OUT, Helpers.SetImageOpacity(Images.assignedServices16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(CACHE, Images.cache16); + tree.ImageList.Images.Add(CACHE + DIMMED_OUT, Helpers.SetImageOpacity(Images.cache16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(PUBLICATIONS, Images.publications16); + tree.ImageList.Images.Add(PUBLICATIONS + DIMMED_OUT, Helpers.SetImageOpacity(Images.publications16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(USERS, Images.users16); + tree.ImageList.Images.Add(USERS + DIMMED_OUT, Helpers.SetImageOpacity(Images.users16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(GROUPS, Images.groups16); + tree.ImageList.Images.Add(GROUPS + DIMMED_OUT, Helpers.SetImageOpacity(Images.groups16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(NETWORKS, Images.networks16); + tree.ImageList.Images.Add(NETWORKS + DIMMED_OUT, Helpers.SetImageOpacity(Images.networks16, UNSELECTED_OPACITY)); + + tree.ImageList.Images.Add(CONNECTIVITY, Images.connectivity16); + tree.ImageList.Images.Add(CONNECTIVITY + DIMMED_OUT, Helpers.SetImageOpacity(Images.connectivity16, UNSELECTED_OPACITY)); + } + + static public void addTypesImages(TreeView tree, xmlrpc.ServiceProviderType[] providersTypes, + xmlrpc.AuthenticatorType[] authsTypes, xmlrpc.OSManagerType[] osManagerTypes, xmlrpc.TransportType[] transportTypes) + { + ImageList lst = tree.ImageList; + foreach (xmlrpc.ServiceProviderType spt in providersTypes) + { + lst.Images.Add(spt.type, Helpers.ImageFromBase64(spt.icon)); + lst.Images.Add(spt.type + DIMMED_OUT, Helpers.SetImageOpacity(Helpers.ImageFromBase64(spt.icon), UNSELECTED_OPACITY)); + } + foreach (xmlrpc.AuthenticatorType at in authsTypes) + { + lst.Images.Add(at.type, Helpers.ImageFromBase64(at.icon)); + lst.Images.Add(at.type + DIMMED_OUT, Helpers.SetImageOpacity(Helpers.ImageFromBase64(at.icon), UNSELECTED_OPACITY)); + } + foreach (xmlrpc.OSManagerType osm in osManagerTypes) + { + lst.Images.Add(osm.type, Helpers.ImageFromBase64(osm.icon)); + lst.Images.Add(osm.type + DIMMED_OUT, Helpers.SetImageOpacity(Helpers.ImageFromBase64(osm.icon), UNSELECTED_OPACITY)); + } + foreach (xmlrpc.TransportType trans in transportTypes) + { + lst.Images.Add(trans.type, Helpers.ImageFromBase64(trans.icon)); + lst.Images.Add(trans.type + DIMMED_OUT, Helpers.SetImageOpacity(Helpers.ImageFromBase64(trans.icon), UNSELECTED_OPACITY)); + } + + } + + static private string getKey(TreeNode node) + { + switch (node.Name) + { + case USERS: + { + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)(node.Parent.Tag); + return USERS + auth.id; + } + case GROUPS: + { + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)(node.Parent.Tag); + return GROUPS + auth.id; + } + case SERVICES: + { + xmlrpc.ServiceProvider prov = (xmlrpc.ServiceProvider)(node.Parent.Tag); + return SERVICE_PROVIDER + prov.id; + } + case SERVICE: + { + xmlrpc.Service serv = (xmlrpc.Service)node.Tag; + return SERVICE + serv.id; + } + case SERVICE_PROVIDER: + { + xmlrpc.ServiceProvider prov = (xmlrpc.ServiceProvider)(node.Tag); + return SERVICE_PROVIDER + prov.id; + } + case DEPLOYED_SERVICE: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(node.Tag); + return DEPLOYED_SERVICE + ds.id; + } + case CACHE: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(node.Tag); + return CACHE + ds.id; + } + case ASSIGNED_SERVICES: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(node.Tag); + return ASSIGNED_SERVICES + ds.id; + } + case ASSIGNED_TRANSPORTS: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(node.Tag); + return ASSIGNED_TRANSPORTS + ds.id; + } + case PUBLICATIONS: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(node.Tag); + return PUBLICATIONS + ds.id; + } + case ALLOWED_GROUPS: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(node.Tag); + return ALLOWED_GROUPS + ds.id; + } + + default: + return node.Name; + } + } + + static public xmlrpc.AuthenticatorType authType(xmlrpc.Authenticator auth, xmlrpc.AuthenticatorType[] types) + { + xmlrpc.AuthenticatorType type = new xmlrpc.AuthenticatorType(); + foreach (xmlrpc.AuthenticatorType a in types) + { + if (a.type == auth.type) + { + type = a; + break; + } + } + return type; + } + + static public void showAssociatedPanel(SplitterPanel panel, TreeView view, forms.MainForm mainForm) + { + TreeNode selected = view.SelectedNode; + + // Hides all visible controls + foreach (Control ctrl in panel.Controls) + ctrl.Hide(); + + string key = getKey(selected); + if (panel.Controls.ContainsKey(key)) + panel.Controls[key].Show(); + else // Don't exists, creates a new panel associated with the tree view and initializes it + { + Control ctrl; + switch( selected.Name ) + { + case USERS: + { + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)(selected.Parent.Tag); + xmlrpc.AuthenticatorType type = authType(auth, mainForm._authenticatorsTypes); + ctrl = new controls.panel.UsersPanel(auth, type); + break; + } + case GROUPS: + { + xmlrpc.Authenticator auth = (xmlrpc.Authenticator)(selected.Parent.Tag); + xmlrpc.AuthenticatorType type = authType(auth, mainForm._authenticatorsTypes); + ctrl = new controls.panel.GroupsPanel(auth, type); + break; + } + case TRANSPORTS: + { + ctrl = new controls.panel.TransportsPanel(); + break; + } + case OS_MANAGERS: + { + ctrl = new controls.panel.OsManagersPanel(); + break; + } + case AUTHENTICATORS: + { + ctrl = new controls.panel.AuthsPanel(); + break; + } + case SERVICES_PROVIDERS: + { + ctrl = new controls.panel.ServiceProvidersPanel(); + break; + } + case SERVICE_PROVIDER: + { + ctrl = new controls.panel.ServicesPanel(((xmlrpc.ServiceProvider)selected.Tag)); + break; + } + case SERVICES: + { + ctrl = new controls.panel.ServicesPanel(((xmlrpc.ServiceProvider)selected.Parent.Tag)); + break; + } + case DEPLOYED_SERVICE: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(selected.Tag); + ctrl = new controls.panel.DeployedServicePanel(ds); + break; + } + case DEPLOYED_SERVICES: + { + ctrl = new controls.panel.DeployedServicesPanel(); + break; + } + case CACHE: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(selected.Tag); + ctrl = new controls.panel.DeployedPanel(ds, true); + break; + } + case ASSIGNED_SERVICES: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(selected.Tag); + ctrl = new controls.panel.DeployedPanel(ds, false); + break; + } + case ASSIGNED_TRANSPORTS: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(selected.Tag); + ctrl = new controls.panel.TransportsPanel(ds); + break; + } + case PUBLICATIONS: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(selected.Tag); + ctrl = new controls.panel.PublicationsPanel(ds); + break; + } + case ALLOWED_GROUPS: + { + xmlrpc.DeployedService ds = (xmlrpc.DeployedService)(selected.Tag); + ctrl = new controls.panel.DeployedGroupsPanel(ds); + break; + } + case NETWORKS: + { + ctrl = new controls.panel.NetworksPanel(); + break; + } + default: + ctrl = new controls.panel.PanelEmpty(""); + break; + + } + ctrl.Name = key; + ctrl.Dock = DockStyle.Fill; + panel.Controls.Add(ctrl); + } + } + + } +} diff --git a/client/administration/UdsAdmin/gui/Colors.cs b/client/administration/UdsAdmin/gui/Colors.cs new file mode 100644 index 000000000..d9d885f86 --- /dev/null +++ b/client/administration/UdsAdmin/gui/Colors.cs @@ -0,0 +1,78 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; + +namespace UdsAdmin.gui +{ + public class Colors + { + public static Color BlackColor = Color.Black; + public static Color ActiveColor = Color.Black; + public static Color InactiveColor = Color.Orange; + public static Color RemovingColor = Color.DarkGray; + public static Color RemovedColor = Color.Gray; + public static Color BlockedColor = Color.Red; + public static Color RunningColor = Color.Green; + public static Color ErrorColor = Color.Red; + public static Color InactiveBackColor = Color.Red; + public static Color InactiveForeColor = Color.Yellow; + public static Color ActiveBackColor = Color.Green; + public static Color ActiveForeColor = Color.White; + + public static Color getColorForState(string state) + { + switch (state) + { + case xmlrpc.Constants.STATE_USABLE: + case xmlrpc.Constants.STATE_ACTIVE: + return ActiveColor; + case xmlrpc.Constants.STATE_ERROR: + return ErrorColor; + case xmlrpc.Constants.STATE_PREPARING: + return RunningColor; + case xmlrpc.Constants.STATE_INACTIVE: + return InactiveColor; + case xmlrpc.Constants.STATE_REMOVABLE: + case xmlrpc.Constants.STATE_REMOVING: + case xmlrpc.Constants.STATE_CANCELING: + return RemovingColor; + case xmlrpc.Constants.STATE_REMOVED: + case xmlrpc.Constants.STATE_CANCELED: + return RemovedColor; + default: + return BlackColor; + } + } + } +} diff --git a/client/administration/UdsAdmin/gui/DinamycFieldsManager.cs b/client/administration/UdsAdmin/gui/DinamycFieldsManager.cs new file mode 100644 index 000000000..188a5430e --- /dev/null +++ b/client/administration/UdsAdmin/gui/DinamycFieldsManager.cs @@ -0,0 +1,661 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Drawing; + +namespace UdsAdmin.gui +{ + public class DinamycFieldsManager + { + const int DEF_FLD_HEIGHT = 22; + const int DEF_FLD_WIDTH = 12; + + public class ValidationError : Exception + { + private string _msg; + + public ValidationError(string err) : base() + { + _msg = err; + } + + public ValidationError(xmlrpc.GuiField fld) : base() + { + _msg = string.Format(Strings.fieldRequired, fld.gui.label); + } + + public override string Message + { + get + { + return _msg; + } + } + + } + + // I'll try to explain what i want to do with this struture here :-): + // First, we need a translation table so we can easyly know how every single type behabiors. + // This behabiors must include: + // - Generate the control itself, with default data if provided (Textbox, Combobox, ...) + // - Read data from controls (Text from textbox, selected item id from comboboc, selected itemS idS from listbox, ...) + // - Refill controls with data (used for callback invocation, on return it must refill som items) + // - Select values of controls, without refilling data (for "modify" operations, so we can recover the previous state) + // - Calculate the size of the control (so we can adjust the Panel acordly) + // With all of this, we use the next items in every case: + // - PutFields, First, it calculates the size of all cells, done via "sizeCalculator". + // Then, it creates the controls with the specified value/values (value for text controls, values for choices and multichoices) + // This is done via "ctrlGenerator" method + // When all controls r created, it starts, in order, to set values indicated, invoking callbacks if needed + // (this way, if we need to recover the state of something, we can use same callbacks that we used at creation + // to allow modify). This is done via the "dataSelector". + // - Callbacks, It uses the information provided on the callback to; + // * Invoke the remote procedure with indicated parameters (even hidden fields :-)) + // * Receive data from callback and, using "dataWriter", refill the specified fields received on response. + // Choice and Multichoice uses the "values" field, and text ones uses the "value" field + // - ReadFields. It simply reads the previously created controls, fills the pairs "name" "value" in case of single select value controls + // (text, choice) and the keys "name" "values" in case of multiselection controls (multichoice), returning this list. + + struct FldTypeData { + public Func ctrlGenerator; + public Func dataExtractor; + public Action dataWriter; + public Action dataSelector; + public Func sizeCalculator; + + public FldTypeData(Func generator, + Func dataExtractor, + Action dataWriter, + Action dataSelector, + Func sizeCalculator, Size size) + { + this.ctrlGenerator = generator; + this.dataExtractor = dataExtractor; + this.dataWriter = dataWriter; + this.dataSelector = dataSelector; + this.sizeCalculator = sizeCalculator; + } + + public FldTypeData(Func generator, + Func dataExtractor, + Action dataWriter, + Action dataSelector, + Func sizeCalculator) + { + this.ctrlGenerator = generator; + this.dataExtractor = dataExtractor; + this.dataWriter = dataWriter; + this.dataSelector = dataSelector; + this.sizeCalculator = sizeCalculator; + } + + public FldTypeData(Func generator, + Func dataExtractor, + Action dataWriter, + Action dataSelector) + { + this.ctrlGenerator = generator; + this.dataExtractor = dataExtractor; + this.dataWriter = dataWriter; + this.dataSelector = dataSelector; + this.sizeCalculator = DefaultSizeCalculator; + } + + public Size size() + { + return new Size(DEF_FLD_WIDTH, DEF_FLD_HEIGHT); + } + + }; + + + private static Dictionary ctrlTypeInfo = + new Dictionary() + { + { xmlrpc.Constants.TEXT_TYPE, new FldTypeData(CreateTextBox, TextDataExtractor, TextDataWriter, TextSelector) }, + { xmlrpc.Constants.PASSWORD_TYPE, new FldTypeData(CreatePasswordBox, TextDataExtractor, TextDataWriter, TextSelector) }, + { xmlrpc.Constants.NUMERIC_TYPE, new FldTypeData(CreateNumericBox, NumericDataExtractor, TextDataWriter, TextSelector) }, + { xmlrpc.Constants.HIDDEN_TYPE, new FldTypeData(null, TextDataExtractor, TextDataWriter, TextSelector) }, + { xmlrpc.Constants.CHOICE_TYPE, new FldTypeData(CreateChoiceBox, ChoiceDataExtractor, ChoiceDataWriter, ChoiceSelector) }, + { xmlrpc.Constants.MULTI_CHOICE_TYPE, new FldTypeData(CreateMultiChoiceBox, MultiChoiceExtractor, MultichoiceDataWriter, MultiChoiceSelector, MultiChoiceSizeCalculator) }, + { xmlrpc.Constants.EDITABLE_LIST, new FldTypeData(CreateEditList, EditListExtractor, EditListDataWriter, EditListSelector, BtnSizeCalculator) }, + { xmlrpc.Constants.CHECKBOX_TYPE, new FldTypeData(CreateCheckBox, CheckBoxExtractor, CheckBoxDataWriter, CheckBoxSelector, CheckBoxSizeCalculator) }, + }; + + // Put fields on the specified tablelayoutpanel so they can be edited + public static Size PutFields(TableLayoutPanel panel, xmlrpc.GuiField[] fields, xmlrpc.GuiFieldValue[] values) + { + // Controls generator + // Size getters + + // We count the fields, except for hiddend fields, so we know the number of rows + int numRows = fields.Count(fld => (fld.gui.type != xmlrpc.Constants.HIDDEN_TYPE)); + panel.SuspendLayout(); + panel.Controls.Clear(); + panel.RowCount = numRows; + panel.ColumnCount = 2; + + panel.AutoSize = true; + + // Sort the array by fielrd order + Array.Sort(fields, delegate( xmlrpc.GuiField f1, xmlrpc.GuiField f2 ) { + return f1.gui.order.CompareTo(f2.gui.order); + }); + + List ordererValues = new List(); + Dictionary dict = new Dictionary(); + + if (values != null) + foreach (xmlrpc.GuiFieldValue v in values) + dict.Add(v.name, v); + + int row = 0; + foreach (xmlrpc.GuiField fld in fields) + { + if (fld.gui.type != xmlrpc.Constants.HIDDEN_TYPE) // Hiden fields are textbox that don't appears and don't add rows.. + { + ToolTip tt = new ToolTip(); + // Creates the label + Label label = new Label(); + label.AutoSize = true; + label.Margin = new Padding(3, 6, 3, 6); + label.Text = fld.gui.label; + label.TextAlign = ContentAlignment.MiddleLeft; + tt.SetToolTip(label, fld.gui.tooltip); + panel.Controls.Add(label, 0, row); + //panel.SetCellPosition(label, new TableLayoutPanelCellPosition(0, row)); + Control ctrl = ctrlTypeInfo[fld.gui.type].ctrlGenerator(fld, panel); + ctrl.Dock = DockStyle.Fill; + if (dict.ContainsKey(fld.name)) + { + // We have it in order + ordererValues.Add(dict[fld.name]); + } + if (fld.gui.rdonly == true && values != null) + { + label.Enabled = false; + ctrl.Enabled = false; + } + panel.Controls.Add(ctrl, 1, row); + // panel.SetCellPosition(ctrl, new TableLayoutPanelCellPosition(1, row)); + row++; + } + else + { + TextBox hidden = new TextBox(); + if (fld.value != "") + hidden.Text = fld.value; + else + hidden.Text = fld.gui.defvalue; + hidden.Name = fld.name; hidden.Tag = fld; hidden.Visible = false; + panel.Controls.Add(hidden, 0, 0); + } + } + + // Set panel column and rows styles + TableLayoutColumnStyleCollection styles = panel.ColumnStyles; + + foreach (ColumnStyle style in styles) + { + style.SizeType = SizeType.AutoSize; + } + + TableLayoutRowStyleCollection stylesR = panel.RowStyles; + foreach (RowStyle style in stylesR) + { + style.SizeType = SizeType.AutoSize; + } + + panel.ResumeLayout(); + + // Now stores the values of fields if needed (we do it now cause we need all controls to be created BEFORE this + // because the events of change fields will be launched + // Generate a dict with the desired values + foreach (xmlrpc.GuiFieldValue v in ordererValues) + { + Control[] ctrl = panel.Controls.Find(v.name, true); + if (ctrl.Length > 0) + { + xmlrpc.GuiField field = (xmlrpc.GuiField)ctrl[0].Tag; + ctrlTypeInfo[field.gui.type].dataSelector(ctrl[0], v); + } + } + + + return panel.PreferredSize; + } + + + public static xmlrpc.GuiFieldValue[] ReadFields(TableLayoutPanel panel, xmlrpc.GuiField[] flds) + { + + Dictionary ctrlsDict = new Dictionary(); + foreach (Control ctrl in panel.Controls) + { + if (ctrl.Name == "") // Skip labels... + continue; + ctrlsDict.Add(ctrl.Name, ctrl); + } + // Extract data from controls and put it on + xmlrpc.GuiFieldValue[] res = new xmlrpc.GuiFieldValue[flds.Count()]; + int cnt = 0; + foreach (xmlrpc.GuiField fld in flds) + { + res[cnt++] = ctrlTypeInfo[fld.gui.type].dataExtractor(ctrlsDict[fld.name], fld, true); + } + return res; + } + + + // Controls creator helpers + private static Control CreateTextBox(xmlrpc.GuiField fld, Control container) + { + TextBox text = new TextBox(); + text.Name = fld.name; + text.Tag = fld; + Size sz = ctrlTypeInfo[fld.gui.type].sizeCalculator(fld); + text.Width = sz.Width; + text.Height = sz.Height; + text.MaxLength = fld.gui.length; + if (fld.value != "") + text.Text = fld.value; + else + text.Text = fld.gui.defvalue; + return text; + } + + private static Control CreatePasswordBox(xmlrpc.GuiField fld, Control container) + { + TextBox text = new TextBox(); + text.Name = fld.name; + text.Tag = fld; + Size sz = ctrlTypeInfo[fld.gui.type].sizeCalculator(fld); + text.Width = sz.Width; + text.Height = sz.Height; + text.MaxLength = fld.gui.length; + if (fld.value != "") + text.Text = fld.value; + else + text.Text = fld.gui.defvalue; + text.UseSystemPasswordChar = true; + return text; + } + + private static decimal ToDecimal(string value) + { + try + { + return Convert.ToDecimal(value); + } + catch (Exception) + { + return 0; + } + } + + private static Control CreateNumericBox(xmlrpc.GuiField fld, Control container) + { + NumericUpDown num = new NumericUpDown(); + num.Name = fld.name; + num.Tag = fld; + Size sz = ctrlTypeInfo[fld.gui.type].sizeCalculator(fld); + num.Width = sz.Width + 20; + num.Height = sz.Height; + num.Minimum = 0; + double length = fld.gui.length < 8 ? fld.gui.length : 8; + num.Maximum = (decimal)Math.Pow(10, length)-1; + if (fld.value != "") + num.Value = ToDecimal(fld.value); + else + { + num.Value = ToDecimal(fld.gui.defvalue); + } + return num; + } + + private static Control CreateChoiceBox(xmlrpc.GuiField fld, Control container) + { + ComboBox box = new ComboBox(); + box.Name = fld.name; + box.Tag = fld; + box.DropDownStyle = ComboBoxStyle.DropDownList; + Size sz = ctrlTypeInfo[fld.gui.type].sizeCalculator(fld); + box.Width = sz.Width; + box.Height = sz.Height; + foreach (xmlrpc.Choice ch in fld.gui.values) + { + box.Items.Add(ch); + if (ch.id == fld.gui.defvalue) + box.SelectedItem = ch; + } + + if (fld.gui.fills.callbackName != null) + { + // We create a delegate to support the callback for this combo box + box.SelectedIndexChanged += delegate(object sender, EventArgs args) + { + xmlrpc.GuiFieldValue[] data = new xmlrpc.GuiFieldValue[fld.gui.fills.parameters.Length]; + int pos = 0; + foreach (string parameter in fld.gui.fills.parameters) + { + Control[] ctrlParameter = container.Controls.Find(parameter, true); + if (ctrlParameter.Length > 0) + { + xmlrpc.GuiField field = (xmlrpc.GuiField)ctrlParameter[0].Tag; + data[pos++] = ctrlTypeInfo[field.gui.type].dataExtractor(ctrlParameter[0], field, false); + } + } + xmlrpc.GuiFieldValue[] newData = xmlrpc.UdsAdminService.InvokeChooseCallback(fld.gui.fills.callbackName, data); + foreach (xmlrpc.GuiFieldValue val in newData) + { + Control[] ctrlResult = container.Controls.Find(val.name, true); + if (ctrlResult.Length > 0) + { + xmlrpc.GuiField field = (xmlrpc.GuiField)ctrlResult[0].Tag; + ctrlTypeInfo[field.gui.type].dataWriter(ctrlResult[0], val); + } + } + }; + } + // With the event active, we change the selected item (if there is a callback, it should launc now) + return box; + } + + // Multichoice creator + private static Control CreateMultiChoiceBox(xmlrpc.GuiField fld, Control container) + { + ListBox box = new ListBox(); + box.Name = fld.name; + box.Tag = fld; + box.SelectionMode = SelectionMode.MultiExtended; + Size sz = ctrlTypeInfo[fld.gui.type].sizeCalculator(fld); + box.Width = sz.Width; + box.Height = sz.Height; + foreach (xmlrpc.Choice ch in fld.gui.values) + { + box.Items.Add(ch); + if (fld.gui.defvalue == ch.id) + box.SelectedItem = ch; + } + + return box; + } + + private static Control CreateEditList(xmlrpc.GuiField fld, Control container) + { + controls.ListEditor lst = new controls.ListEditor(); + lst.Name = fld.name; + lst.Tag = fld; + lst.Text = fld.gui.label; + Size sz = ctrlTypeInfo[fld.gui.type].sizeCalculator(fld); + lst.Width = sz.Width; + List vals = new List(); + foreach (xmlrpc.Choice ch in fld.gui.values) + vals.Add(ch.id); + lst.Items = vals; + + return lst; + } + + private static Control CreateCheckBox(xmlrpc.GuiField fld, Control container) + { + CheckBox check = new CheckBox(); + check.Name = fld.name; + check.Tag = fld; + Size sz = ctrlTypeInfo[fld.gui.type].sizeCalculator(fld); + check.Width = sz.Width; + check.Height = sz.Height; + string value = (fld.value != "") ? fld.value : fld.gui.defvalue; + + if (value != xmlrpc.Constants.TRUE) + check.Checked = false; + else + check.Checked = true; + return check; + } + + // Controls size calculators + private static Size DefaultSizeCalculator(xmlrpc.GuiField fld) + { + Size sz = ctrlTypeInfo[fld.gui.type].size(); + sz.Width *= fld.gui.length; + if (sz.Width > UdsAdmin.Properties.Settings.Default.MaxControlWidth) + sz.Width = UdsAdmin.Properties.Settings.Default.MaxControlWidth; + + return sz; + } + + // Button size calculators + private static Size BtnSizeCalculator(xmlrpc.GuiField fld) + { + Size sz = ctrlTypeInfo[fld.gui.type].size(); + sz.Height += 8; + sz.Width *= fld.gui.length; + if (sz.Width > UdsAdmin.Properties.Settings.Default.MaxControlWidth) + sz.Width = UdsAdmin.Properties.Settings.Default.MaxControlWidth; + + return sz; + } + + // Listbox size calculator + private static Size MultiChoiceSizeCalculator(xmlrpc.GuiField fld) + { + Size sz = ctrlTypeInfo[fld.gui.type].size(); + sz.Width *= fld.gui.length; + if (sz.Width > UdsAdmin.Properties.Settings.Default.MaxControlWidth) + sz.Width = UdsAdmin.Properties.Settings.Default.MaxControlWidth; + int rows = 2; + if (fld.gui.rows != -1) + rows = fld.gui.rows; + sz.Height *= rows; + return sz; + } + + private static Size CheckBoxSizeCalculator(xmlrpc.GuiField fld) + { + Size sz = ctrlTypeInfo[fld.gui.type].size() + new Size(4, 4); + if (sz.Width > UdsAdmin.Properties.Settings.Default.MaxControlWidth) + sz.Width = UdsAdmin.Properties.Settings.Default.MaxControlWidth; + + return sz; + } + + // Control extractors helpers + private static xmlrpc.GuiFieldValue TextDataExtractor(Control ctrl, xmlrpc.GuiField fld, bool validate) + { + TextBox txt = (TextBox)ctrl; + string val = txt.Text.Trim(); + if (validate && fld.gui.required && val.Length == 0) + throw new ValidationError(fld); + return new xmlrpc.GuiFieldValue(fld.name, val); + } + + private static xmlrpc.GuiFieldValue NumericDataExtractor(Control ctrl, xmlrpc.GuiField fld, bool validate) + { + NumericUpDown num = (NumericUpDown)ctrl; + string val = ((int)(num.Value)).ToString(); + if( validate && fld.gui.required && val == "0" ) + throw new ValidationError(fld); + return new xmlrpc.GuiFieldValue(fld.name, val); + } + + private static xmlrpc.GuiFieldValue ChoiceDataExtractor(Control ctrl, xmlrpc.GuiField fld, bool validate) + { + ComboBox box = (ComboBox)ctrl; + string val = ""; + if( box.SelectedItem != null ) + val = ((xmlrpc.Choice)box.SelectedItem).id; + if( validate && fld.gui.required && val.Length == 0 ) + throw new ValidationError(fld); + return new xmlrpc.GuiFieldValue(fld.name, val); + } + + private static xmlrpc.GuiFieldValue MultiChoiceExtractor(Control ctrl, xmlrpc.GuiField fld, bool validate) + { + ListBox box = (ListBox)ctrl; + xmlrpc.Choice[] selected = new xmlrpc.Choice[box.SelectedItems.Count]; + for (int i = 0; i < box.SelectedItems.Count; i++) + selected[i] = (xmlrpc.Choice)box.SelectedItems[i]; + if (validate && fld.gui.required && selected.Length == 0) + throw new ValidationError(fld); + return new xmlrpc.GuiFieldValue(fld.name, selected); + } + + private static xmlrpc.GuiFieldValue EditListExtractor(Control ctrl, xmlrpc.GuiField fld, bool validate) + { + controls.ListEditor lst = (controls.ListEditor)ctrl; + List vals = lst.Items; + xmlrpc.Choice[] selected = new xmlrpc.Choice[vals.Count]; + for (int i = 0; i < vals.Count; i++) + selected[i] = new xmlrpc.Choice(vals[i], ""); // Returned values goes inside ids (the significant part of choices, text are used to display data to user) + if ( validate && fld.gui.required && selected.Length == 0) + throw new ValidationError(fld); + return new xmlrpc.GuiFieldValue(fld.name, selected); + } + + private static xmlrpc.GuiFieldValue CheckBoxExtractor(Control ctrl, xmlrpc.GuiField fld, bool validate) + { + CheckBox chk = (CheckBox)ctrl; + return new xmlrpc.GuiFieldValue(fld.name, chk.Checked ? xmlrpc.Constants.TRUE : xmlrpc.Constants.FALSE); + } + + // Control writers helpers + private static void TextDataWriter(Control ctrl, xmlrpc.GuiFieldValue value) + { + ctrl.Text = value.value; + } + + // Control writers helpers + private static void NumericDataWriter(Control ctrl, xmlrpc.GuiFieldValue value) + { + NumericUpDown num = (NumericUpDown)ctrl; + num.Value = ToDecimal(value.value); + } + + private static void ChoiceDataWriter(Control ctrl, xmlrpc.GuiFieldValue value) + { + ComboBox box = (ComboBox)ctrl; + box.Items.Clear(); + foreach (xmlrpc.Choice ch in value.values) + { + box.Items.Add(ch); + } + if (box.Items.Count > 0) + box.SelectedIndex = 0; + else + box.SelectedIndex = -1; + } + + private static void MultichoiceDataWriter(Control ctrl, xmlrpc.GuiFieldValue value) + { + ListBox box = (ListBox)ctrl; + box.Items.Clear(); + foreach (xmlrpc.Choice ch in value.values) + { + box.Items.Add(ch); + } + } + + private static void EditListDataWriter(Control ctrl, xmlrpc.GuiFieldValue value) + { + controls.ListEditor lst = (controls.ListEditor)ctrl; + List vals = new List(); + foreach (xmlrpc.Choice ch in value.values) + vals.Add(ch.text); + lst.Items = vals; + } + + private static void CheckBoxDataWriter(Control ctrl, xmlrpc.GuiFieldValue value) + { + CheckBox chk = (CheckBox)ctrl; + if (value.value != xmlrpc.Constants.TRUE) + chk.Checked = false; + else + chk.Checked = true; + } + + // Controls "Selectors" (that is, select the items without overwriting it + private static void TextSelector(Control ctrl, xmlrpc.GuiFieldValue value) + { + ctrl.Text = value.value; + } + + private static void NumericSelector(Control ctrl, xmlrpc.GuiFieldValue value) + { + NumericUpDown num = (NumericUpDown)ctrl; + num.Value = ToDecimal(value.value); + } + + private static void ChoiceSelector(Control ctrl, xmlrpc.GuiFieldValue value) + { + ComboBox box = (ComboBox)ctrl; + foreach( xmlrpc.Choice ch in box.Items ) + { + if (ch.id == value.value) + box.SelectedItem = ch; + } + } + + private static void MultiChoiceSelector(Control ctrl, xmlrpc.GuiFieldValue value) + { + ListBox box = (ListBox)ctrl; + box.BeginUpdate(); + box.ClearSelected(); + foreach (xmlrpc.Choice val in value.values) + box.SelectedItems.Add(val); + box.EndUpdate(); + } + + private static void EditListSelector(Control ctrl, xmlrpc.GuiFieldValue value) + { + // All items are "Selected" in editlist, i mean, we fill the list with this items + controls.ListEditor lst = (controls.ListEditor)ctrl; + List vals = new List(); + foreach (xmlrpc.Choice ch in value.values) + vals.Add(ch.id); + lst.Items = vals; + + } + + private static void CheckBoxSelector(Control ctrl, xmlrpc.GuiFieldValue value) + { + CheckBox chk = (CheckBox)ctrl; + if (value.value != xmlrpc.Constants.TRUE) + chk.Checked = false; + else + chk.Checked = true; + } + + } +} diff --git a/client/administration/UdsAdmin/gui/Helpers.cs b/client/administration/UdsAdmin/gui/Helpers.cs new file mode 100644 index 000000000..8e964db67 --- /dev/null +++ b/client/administration/UdsAdmin/gui/Helpers.cs @@ -0,0 +1,102 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Drawing; +using System.Drawing.Imaging; + + +namespace UdsAdmin.gui +{ + class Helpers + { + /// + /// Method to convert an string in base64 to an image + /// + /// String with base64 data + /// Image produced from the string provided + public static Bitmap ImageFromBase64(string base64) + { + byte[] raw = Convert.FromBase64String(base64); + System.ComponentModel.TypeConverter tc = System.ComponentModel.TypeDescriptor.GetConverter(typeof(Bitmap)); + + return (Bitmap)tc.ConvertFrom(raw); + } + + public static Icon IconFromBase64(string base64) + { + return Icon.FromHandle(gui.Helpers.ImageFromBase64(base64).GetHicon()); + } + + /// + /// method for changing the opacity of an image + /// + /// image to set opacity on + /// percentage of opacity + /// new Image with the opacity + public static Image SetImageOpacity(Image image, float opacity) + { + try + { + //create a Bitmap the size of the image provided + Bitmap bmp = new Bitmap(image.Width, image.Height); + + //create a graphics object from the image + using (Graphics gfx = Graphics.FromImage(bmp)) + { + + //create a color matrix object + ColorMatrix matrix = new ColorMatrix(); + + //set the opacity + matrix.Matrix33 = opacity; + + //create image attributes + ImageAttributes attributes = new ImageAttributes(); + + //set the color(opacity) of the image + attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); + + //now draw the image + gfx.DrawImage(image, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + return bmp; + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + return null; + } + } + } +} diff --git a/client/administration/UdsAdmin/gui/ListViewSorter.cs b/client/administration/UdsAdmin/gui/ListViewSorter.cs new file mode 100644 index 000000000..4f81e51a3 --- /dev/null +++ b/client/administration/UdsAdmin/gui/ListViewSorter.cs @@ -0,0 +1,162 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Collections; + +namespace UdsAdmin.gui +{ + class ListViewSorter : IComparer + { + private const string ASC = " (asc)"; + private const string DES = " (des)"; + private int _colToSort; + private ImageList sortOrderImages; + SortOrder _order; + IComparer _strComparer; + ListView _list; + ICollection _dateColumns; + + public ListViewSorter(ListView lst, ICollection dateColumns = null ) + { + _colToSort = 0; + _order = SortOrder.None; + _strComparer = new CaseInsensitiveComparer(); + _list = lst; + + _dateColumns = dateColumns; + + + sortOrderImages = new ImageList(); + sortOrderImages.ColorDepth = ColorDepth.Depth24Bit; + sortOrderImages.Images.Add("up", Images.uparrow16); + sortOrderImages.Images.Add("down", Images.downarrow16); + sortOrderImages.Images.Add("empty", Images.empty16); + + lst.SmallImageList = sortOrderImages; + foreach (ColumnHeader h in lst.Columns) + { + h.TextAlign = HorizontalAlignment.Left; + h.ImageKey = "empty"; + h.Width = TextRenderer.MeasureText(h.Text, lst.Parent.Font).Width + 40; + } + } + + + public int Compare(object x, object y) + { + int compareResult; + string listviewX, listviewY; + + // Cast the objects to be compared to ListViewItem objects + listviewX = ((ListViewItem)x).SubItems[_colToSort].Text; + listviewY = ((ListViewItem)y).SubItems[_colToSort].Text; + + // Compare the two items + if( _dateColumns != null && _dateColumns.Contains(_colToSort ) ) + compareResult = DateTime.Compare(DateTime.Parse(listviewX), DateTime.Parse(listviewY)); + else + compareResult = _strComparer.Compare(listviewX, listviewY); + + // Calculate correct return value based on object comparison + if (_order == SortOrder.Ascending) + { + // Ascending sort is selected, return normal result of compare operation + return compareResult; + } + else if (_order == SortOrder.Descending) + { + // Descending sort is selected, return negative result of compare operation + return (-compareResult); + } + else + { + // Return '0' to indicate they are equal + return 0; + } + } + + private void updateColumnHeaderText(int column, string order) + { + ColumnHeader h = _list.Columns[column]; + string txt = h.Text; + // Try to remove " (asc)" or " (des)" + /*if (txt.Contains(ASC)) + txt = txt.Substring(0, txt.Length - ASC.Length); + else if (txt.Contains(DES)) + txt = txt.Substring(0, txt.Length - DES.Length); + txt += order; + h.Text = txt;*/ + if (order == ASC) + h.ImageKey = "down"; + else if (order == DES) + h.ImageKey = "up"; + else + h.ImageKey = "empty"; + } + + private void updateColumHeaders() + { + for (int i = 0; i < _list.Columns.Count; i++) + if (i == _colToSort) + updateColumnHeaderText(i, _order == SortOrder.Ascending ? ASC : DES); + else + updateColumnHeaderText(i, ""); + } + + public void ColumnClick(object sender, ColumnClickEventArgs e) + { + // Determine if clicked column is already the column that is being sorted. + if (e.Column == _colToSort) + { + // Reverse the current sort direction for this column. + if (_order == SortOrder.Ascending) + { + _order = SortOrder.Descending; + } + else + { + _order = SortOrder.Ascending; + } + } + else + { + // Set the column number that is to be sorted; default to ascending. + _colToSort = e.Column; + _order = SortOrder.Ascending; + } + updateColumHeaders(); + _list.Sort(); + } + } +} diff --git a/client/administration/UdsAdmin/gui/MenusManager.cs b/client/administration/UdsAdmin/gui/MenusManager.cs new file mode 100644 index 000000000..d609d7bdf --- /dev/null +++ b/client/administration/UdsAdmin/gui/MenusManager.cs @@ -0,0 +1,239 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.gui +{ + public class MenusManager + { + public static void InitProvidersMenu(ToolStripMenuItem menu, EventHandler action, xmlrpc.ServiceProviderType[] providersTypes) + { + menu.DropDownItems.Clear(); + menu.Text = Strings.newItem; + List lst = new List(providersTypes); + lst.Sort(new xmlrpc.ServiceProviderTypeSorterByName()); + foreach (xmlrpc.ServiceProviderType pt in lst) + { + ToolStripItem m = new ToolStripMenuItem(pt.name); + m.Name = pt.type; + m.ToolTipText = pt.description; + m.Image = gui.Helpers.ImageFromBase64(pt.icon); + m.Click += action; + + menu.DropDownItems.Add(m); + } + } + + public static ContextMenuStrip ServicesMenu(EventHandler action, xmlrpc.ServiceType[] servicesTypes, bool withModifyDelete) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem newA = new ToolStripMenuItem(); newA.Name = "" ; + + newA.DropDownItems.Clear(); + newA.Text = Strings.newItem; + + List lst = new List(servicesTypes); + lst.Sort(new xmlrpc.ServiceTypeSorterByName()); + + foreach (xmlrpc.ServiceType st in lst) + { + ToolStripItem m = new ToolStripMenuItem(st.name); + m.Name = ActionTree.NEW_ACTION; + m.Tag = st; + m.ToolTipText = st.description; + m.Image = gui.Helpers.ImageFromBase64(st.icon); + m.Click += action; + + newA.DropDownItems.Add(m); + } + + if (withModifyDelete) + { + ToolStripMenuItem modify = new ToolStripMenuItem(Strings.modifyItem); modify.Name = ActionTree.MODIFY_ACTION; modify.Click += action; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Name = ActionTree.DELETE_ACTION; delete.Click += action; + ToolStripMenuItem check = new ToolStripMenuItem(Strings.checkServiceProvider); check.Name = ActionTree.CHECK_ACTION; check.Click += action; + if( servicesTypes.Length > 0 ) + menu.Items.AddRange(new ToolStripItem[] { newA, modify, delete, check }); + else + menu.Items.AddRange(new ToolStripItem[] { modify, delete, check }); + } + else + menu.Items.Add(newA); + return menu; + } + + public static ContextMenuStrip ServiceMenu(EventHandler action, xmlrpc.Service service, xmlrpc.ServiceType type) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem modify = new ToolStripMenuItem(Strings.modifyItem); modify.Name = ActionTree.MODIFY_ACTION; modify.Click += action; modify.Tag = type; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Name = ActionTree.DELETE_ACTION; delete.Click += action; + menu.Items.AddRange(new ToolStripItem[] { modify, delete }); + return menu; + } + + public static void InitAuthenticatorsMenu(ToolStripMenuItem menu, EventHandler action, xmlrpc.AuthenticatorType[] authsTypes) + { + menu.DropDownItems.Clear(); + menu.Text = Strings.newItem; + List lst = new List(authsTypes); + lst.Sort(new xmlrpc.AuthenticatorTypeSorterByName()); + + foreach (xmlrpc.AuthenticatorType authType in lst) + { + ToolStripItem m = new ToolStripMenuItem(authType.name); + m.Name = authType.type; + m.ToolTipText = authType.description; + m.Image = gui.Helpers.ImageFromBase64(authType.icon); + m.Click += action; + + menu.DropDownItems.Add(m); + } + } + + public static ContextMenuStrip AuthMenu(EventHandler action) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem modify = new ToolStripMenuItem(Strings.modifyItem); modify.Name = ActionTree.MODIFY_ACTION; modify.Click += action; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Name = ActionTree.DELETE_ACTION; delete.Click += action; + ToolStripMenuItem check = new ToolStripMenuItem(Strings.checkAuthenticator); check.Name = ActionTree.CHECK_ACTION; check.Click += action; + menu.Items.AddRange(new ToolStripItem[] { modify, delete, check }); + return menu; + } + + public static void InitOSManagersMenu(ToolStripMenuItem menu, EventHandler action, xmlrpc.OSManagerType[] osmTypes) + { + menu.DropDownItems.Clear(); + menu.Text = Strings.newItem; + List lst = new List(osmTypes); + lst.Sort(new xmlrpc.OSManagerTypeSorterByName()); + + foreach (xmlrpc.OSManagerType osmType in lst) + { + ToolStripItem m = new ToolStripMenuItem(osmType.name); + m.Name = osmType.type; + m.ToolTipText = osmType.description; + m.Image = gui.Helpers.ImageFromBase64(osmType.icon); + m.Click += action; + + menu.DropDownItems.Add(m); + } + } + + public static ContextMenuStrip OSManagerMenu(EventHandler action, xmlrpc.OSManager osm) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem modify = new ToolStripMenuItem(Strings.modifyItem); modify.Name = ActionTree.MODIFY_ACTION; modify.Click += action; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Name = ActionTree.DELETE_ACTION; delete.Click += action; + ToolStripMenuItem check = new ToolStripMenuItem(Strings.checkOSManager); check.Name = ActionTree.CHECK_ACTION; check.Click += action; + menu.Items.AddRange(new ToolStripItem[] { modify, delete, check }); + return menu; + } + + public static void InitTransportsMenu(ToolStripMenuItem menu, EventHandler action, xmlrpc.TransportType[] transTypes) + { + menu.DropDownItems.Clear(); + menu.Text = Strings.newItem; + List lst = new List(transTypes); + lst.Sort(new xmlrpc.TransportTypeSorterByName()); + + foreach (xmlrpc.TransportType tType in lst) + { + ToolStripItem m = new ToolStripMenuItem(tType.name); + m.Name = tType.type; + m.ToolTipText = tType.description; + m.Image = gui.Helpers.ImageFromBase64(tType.icon); + m.Click += action; + + menu.DropDownItems.Add(m); + } + } + + public static ContextMenuStrip TransportMenu(EventHandler action, xmlrpc.Transport trans) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem modify = new ToolStripMenuItem(Strings.modifyItem); modify.Name = ActionTree.MODIFY_ACTION; modify.Click += action; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Name = ActionTree.DELETE_ACTION; delete.Click += action; + menu.Items.AddRange(new ToolStripItem[] { modify, delete }); + return menu; + } + + public static ContextMenuStrip NetworksMenu(EventHandler action) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem newNetwork = new ToolStripMenuItem(Strings.newItem); + newNetwork.Click += action; + menu.Items.Add(newNetwork); + return menu; + } + + + public static void InitDeployedServicesMenu(ToolStripMenuItem menu, EventHandler action) + { + menu.Text = Strings.newItem; + menu.Click += action; + } + + public static ContextMenuStrip DeployedServiceMenu(EventHandler action, xmlrpc.DeployedService dps) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem modify = new ToolStripMenuItem(Strings.modifyItem); modify.Name = ActionTree.MODIFY_ACTION; modify.Click += action; + ToolStripMenuItem delete = new ToolStripMenuItem(Strings.deleteItem); delete.Name = ActionTree.DELETE_ACTION; delete.Click += action; + menu.Items.AddRange(new ToolStripItem[] { modify, delete}); + if (dps.info.needsPublication) + { + ToolStripSeparator sep = new ToolStripSeparator(); + ToolStripMenuItem publish = new ToolStripMenuItem(Strings.publish); publish.Name = ActionTree.PUBLISH_ACTION; publish.Click += action; + menu.Items.AddRange(new ToolStripItem[] {sep, publish }); + } + return menu; + } + + public static ContextMenuStrip TreeUsersGroupsMenu(EventHandler action) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem newU = new ToolStripMenuItem(Strings.newItem); newU.Name = ActionTree.NEW_ACTION; newU.Click += action; + menu.Items.Add(newU); + return menu; + } + + + public static ContextMenuStrip PublicationMenu(EventHandler action) + { + ContextMenuStrip menu = new ContextMenuStrip(); + ToolStripMenuItem publish = new ToolStripMenuItem(Strings.publish); publish.Name = ActionTree.PUBLISH_ACTION; publish.Click += action; + menu.Items.Add(publish); + return menu; + } + } +} diff --git a/client/administration/UdsAdmin/gui/UserNotifier.cs b/client/administration/UdsAdmin/gui/UserNotifier.cs new file mode 100644 index 000000000..b91982fc1 --- /dev/null +++ b/client/administration/UdsAdmin/gui/UserNotifier.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace UdsAdmin.gui +{ + public class UserNotifier + { + public static void notifyRpcException(CookComputing.XmlRpc.XmlRpcFaultException e) + { + new xmlrpc.ExceptionExplainer(e).showError(); + } + + public static void notifyValidationException(gui.DinamycFieldsManager.ValidationError err) + { + notifyError(err.Message); + } + + public static void notifyError(string msg) + { + MessageBox.Show(msg, Strings.error, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + public static void notifyTestResult(xmlrpc.ResultTest res) + { + if (res.ok) + MessageBox.Show(res.message, Strings.resultTestOk, MessageBoxButtons.OK, MessageBoxIcon.Information); + else + MessageBox.Show(res.message, Strings.resultTestError, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } +} diff --git a/client/administration/UdsAdmin/obj/x86/Debug/UdsAdmin.TrustInfo.xml b/client/administration/UdsAdmin/obj/x86/Debug/UdsAdmin.TrustInfo.xml new file mode 100644 index 000000000..7923810b6 --- /dev/null +++ b/client/administration/UdsAdmin/obj/x86/Debug/UdsAdmin.TrustInfo.xml @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/client/administration/UdsAdmin/xmlrpc/Constants.cs b/client/administration/UdsAdmin/xmlrpc/Constants.cs new file mode 100644 index 000000000..eea986c7d --- /dev/null +++ b/client/administration/UdsAdmin/xmlrpc/Constants.cs @@ -0,0 +1,64 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UdsAdmin.xmlrpc +{ + public class Constants + { + public const string TEXT_TYPE = "text"; + public const string NUMERIC_TYPE = "numeric"; + public const string PASSWORD_TYPE = "password"; + public const string HIDDEN_TYPE = "hidden"; + public const string CHOICE_TYPE = "choice"; + public const string MULTI_CHOICE_TYPE = "multichoice"; + public const string EDITABLE_LIST = "editlist"; + public const string CHECKBOX_TYPE = "checkbox"; + + public const string TRUE = "true"; + public const string FALSE = "false"; + + public const string STATE_ACTIVE = "A"; + public const string STATE_INACTIVE = "I"; + public const string STATE_BLOCKED = "B"; + public const string STATE_LAUNCHING = "L"; + public const string STATE_PREPARING = "P"; + public const string STATE_USABLE = "U"; + public const string STATE_REMOVABLE = "R"; + public const string STATE_REMOVING = "M"; + public const string STATE_REMOVED = "S"; + public const string STATE_ERROR = "E"; + public const string STATE_CANCELED = "C"; + public const string STATE_CANCELING = "K"; + } +} diff --git a/client/administration/UdsAdmin/xmlrpc/ExceptionExplainer.cs b/client/administration/UdsAdmin/xmlrpc/ExceptionExplainer.cs new file mode 100644 index 000000000..5c8a08863 --- /dev/null +++ b/client/administration/UdsAdmin/xmlrpc/ExceptionExplainer.cs @@ -0,0 +1,115 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UdsAdmin.xmlrpc +{ + public class ExceptionExplainer + { + public const int AUTH_CLASS = 0x1000; + public const int DATA_CLASS = 0x2000; + public const int ACTION_CLASS = 0x3000; + + public const int FAIL = 0x0100; + + public const int AUTH_FAILED = AUTH_CLASS | FAIL | 0x0001; + public const int DUPLICATE_FAIL = DATA_CLASS | FAIL | 0x0001; + public const int INSERT_FAIL = DATA_CLASS | FAIL | 0x0002; + public const int DELETE_FAIL = DATA_CLASS | FAIL | 0x0003; + public const int FIND_FAIL = DATA_CLASS | FAIL | 0x0004; + public const int VALIDATION_FAIL = DATA_CLASS | FAIL | 0x0005; + public const int PARAMETERS_FAIL = DATA_CLASS | FAIL | 0x0006; + public const int MODIFY_FAIL = DATA_CLASS | FAIL | 0x0007; + + public const int PUBLISH_FAIL = ACTION_CLASS | FAIL | 0x0001; + public const int CANCEL_FAIL = ACTION_CLASS | FAIL | 0x0001; + + + private int _code; + private string _text; + private bool _fatal; + + public ExceptionExplainer(CookComputing.XmlRpc.XmlRpcFaultException e) + { + _code = e.FaultCode; _text = e.FaultString; + _fatal = false; + } + + public string explain() + { + string res = ""; + switch (_code) + { + case DUPLICATE_FAIL: + res = Strings.duplicatedItem + ": " + _text; + break; + case DELETE_FAIL: + res = Strings.deletionFailed + ": " + _text; + break; + case MODIFY_FAIL: + res = Strings.modificationFailed + ": " + _text; + break; + case INSERT_FAIL: + res = _text; + break; + case FIND_FAIL: + res = Strings.findFailed + ":" + _text; + break; + case VALIDATION_FAIL: + res = Strings.validationFailed + ":" + _text; + break; + case AUTH_FAILED: + _fatal = true; + res = Strings.authFailed + ":" + _text; + break; + case PUBLISH_FAIL: + res = Strings.publicationFailed + ": " + _text; + break; + case PARAMETERS_FAIL: + default: + res = _text; + break; + } + if (_fatal) + res += "\n" + Strings.appWillTerminate; + return res; + } + + public void showError() + { + System.Windows.Forms.MessageBox.Show(explain(), Strings.error, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); + if (_fatal) + System.Windows.Forms.Application.Exit(); + } + } +} diff --git a/client/administration/UdsAdmin/xmlrpc/IUDSAdmin.cs b/client/administration/UdsAdmin/xmlrpc/IUDSAdmin.cs new file mode 100644 index 000000000..660f8cf48 --- /dev/null +++ b/client/administration/UdsAdmin/xmlrpc/IUDSAdmin.cs @@ -0,0 +1,338 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using CookComputing.XmlRpc; + +namespace UdsAdmin.xmlrpc +{ + public interface IUDSAdmin : IXmlRpcProxy + { + // Login, return credentials if logged in or empty if can't log in + [XmlRpcMethod("login")] + LoginData Login(string username, string password, string idAuth, string locale); + + [XmlRpcMethod("getAdminAuths")] + SimpleInfo[] GetAdminAuths(string locale); + + [XmlRpcMethod("logout")] + bool Logout(string credentials); + + // Service provider related methods + [XmlRpcMethod("getServiceProvidersTypes")] + ServiceProviderType[] GetServiceProvidersTypes(string credentials); + + [XmlRpcMethod("getServiceProviders")] + ServiceProvider[] GetServiceProviders(string credentials); + + [XmlRpcMethod("getServiceProviderGui")] + GuiField[] GetServiceProviderGui(string credentials, string type); + + [XmlRpcMethod("getServiceProvider")] + GuiFieldValue[] GetServiceProvider(string credentials, string id); + + [XmlRpcMethod("createServiceProvider")] + bool CreateServiceProvider(string credentials, string type, GuiFieldValue[] data); + + [XmlRpcMethod("modifyServiceProvider")] + bool ModifyServiceProvider(string credentials, string id, GuiFieldValue[] data); + + [XmlRpcMethod("removeServiceProvider")] + bool RemoveServiceProvider(string credentials, string id); + + [XmlRpcMethod("getOffersFromServiceProvider")] + ServiceType[] GetOffersFromServiceProvider(string credentials, string type); + + [XmlRpcMethod("testServiceProvider")] + ResultTest TestServiceProvider(string credentials, string type, GuiFieldValue[] data); + + [XmlRpcMethod("checkServiceProvider")] + string CheckServiceProvider(string credentials, string id); + + // Services related methods + [XmlRpcMethod("getServices")] + Service[] GetServices(string credentials, string idParent); + + [XmlRpcMethod("getAllServices")] + Service[] GetAllServices(string credentials); + + [XmlRpcMethod("getServiceGui")] + GuiField[] GetServiceGui(string credentials, string idParent, string type); + + [XmlRpcMethod("getService")] + GuiFieldValue[] GetService(string credentials, string id); + + [XmlRpcMethod("createService")] + bool CreateService(string credentials, string idParent, string type, GuiFieldValue[] data); + + [XmlRpcMethod("modifyService")] + bool ModifyService(string credentials, string id, GuiFieldValue[] data); + + [XmlRpcMethod("removeService")] + bool RemoveService(string credentials, string id); + + // Authenticators + [XmlRpcMethod("getAuthenticatorsTypes")] + AuthenticatorType[] GetAuthenticatorsTypes(string credentials); + + [XmlRpcMethod("getAuthenticatorType")] + AuthenticatorType GetAuthenticatorType(string credentials, string id); + + [XmlRpcMethod("getAuthenticators")] + Authenticator[] GetAuthenticators(string credentials); + + [XmlRpcMethod("getAuthenticatorGui")] + GuiField[] GetAuthenticatorGui(string credentials, string type); + + [XmlRpcMethod("getAuthenticator")] + GuiFieldValue[] GetAuthenticator(string credentials, string id); + + [XmlRpcMethod("getAuthenticatorGroups")] + Group[] GetAuthenticatorGroups(string credentials, string id); + + [XmlRpcMethod("createAuthenticator")] + bool CreateAuthenticator(string credentials, string type, GuiFieldValue[] data); + + [XmlRpcMethod("modifyAuthenticator")] + bool ModifyAuthenticator(string credentials, string id, GuiFieldValue[] data); + + [XmlRpcMethod("removeAuthenticator")] + bool RemoveAuthenticator(string credentials, string id); + + [XmlRpcMethod("testAuthenticator")] + ResultTest TestAuthenticator(string credentials, string type, GuiFieldValue[] data); + + [XmlRpcMethod("checkAuthenticator")] + string CheckAuthenticator(string credentials, string id); + + [XmlRpcMethod("searchAuthenticator")] + SimpleInfo[] SearchAuthenticator(string credentials, string id, bool srchUser, string srchString); + + // Authenticators, groups part + [XmlRpcMethod("getGroups")] + Group[] GetGroups(string credentials, string idParent); + + [XmlRpcMethod("getGroup")] + Group GetGroup(string credentials, string id); + + [XmlRpcMethod("createGroup")] + bool CreateGroup(string credentials, Group group); + + [XmlRpcMethod("changeGroupsState")] + bool ChangeGroupsState(string credentials, string[] ids, bool newState); + + [XmlRpcMethod("removeGroups")] + bool RemoveGroups(string credentials, string[] ids); + + // Authenticators, users part + [XmlRpcMethod("getUsers")] + User[] GetUsers(string credentials, string idParent); + + [XmlRpcMethod("getUser")] + User GetUser(string credentials, string id); + + [XmlRpcMethod("getUserGroups")] + Group[] GetUserGroups(string credentials, string id); + + [XmlRpcMethod("changeUsersState")] + bool ChangeUsersState(string credentials, string[] ids, string newState); + + [XmlRpcMethod("removeUsers")] + bool RemoveUsers(string credentials, string[] ids); + + [XmlRpcMethod("createUser")] + bool CreateUser(string credentials, User user); + + [XmlRpcMethod("modifyUser")] + bool ModifyUser(string credentials, User user); + + [XmlRpcMethod("getPrefsForUser")] + PrefGroup[] GetPrefsForUser(string credentials, string id); + + [XmlRpcMethod("setPrefsForUser")] + bool SetPrefsForUser(string credentials, string id, GuiFieldValue[] data); + + // OS Managers related methods + [XmlRpcMethod("getOSManagersTypes")] + OSManagerType[] GetOSManagersTypes(string credentials); + + [XmlRpcMethod("getOSManagers")] + OSManager[] GetOSManagers(string credentials); + + [XmlRpcMethod("getOSManagerGui")] + GuiField[] GetOSManagerGui(string credentials, string type); + + [XmlRpcMethod("getOSManager")] + GuiFieldValue[] GetOSManager(string credentials, string id); + + [XmlRpcMethod("createOSManager")] + bool CreateOSManager(string credentials, string type, GuiFieldValue[] data); + + [XmlRpcMethod("modifyOSManager")] + bool ModifyOSManager(string credentials, string id, GuiFieldValue[] data); + + [XmlRpcMethod("removeOSManager")] + bool RemoveOSManager(string credentials, string id); + + [XmlRpcMethod("testOsManager")] + ResultTest TestOsManager(string credentials, string type, GuiFieldValue[] data); + + [XmlRpcMethod("checkOSManager")] + string CheckOSManager(string credentials, string id); + + // Transports related methods + [XmlRpcMethod("getTransportsTypes")] + TransportType[] GetTransportsTypes(string credentials); + + [XmlRpcMethod("getTransports")] + Transport[] GetTransports(string credentials); + + [XmlRpcMethod("getTransportGui")] + GuiField[] GetTransportGui(string credentials, string type); + + [XmlRpcMethod("getTransport")] + GuiFieldValue[] GetTransport(string credentials, string id); + + [XmlRpcMethod("createTransport")] + string CreateTransport(string credentials, string type, GuiFieldValue[] data); + + [XmlRpcMethod("modifyTransport")] + bool ModifyTransport(string credentials, string id, GuiFieldValue[] data); + + [XmlRpcMethod("removeTransport")] + bool RemoveTransport(string credentials, string id); + + [XmlRpcMethod("checkTransport")] + string CheckTransport(string credentials, string id); + + // Networks related (transports networks) + [XmlRpcMethod("getNetworks")] + Network[] GetNetworks(string credentials); + + [XmlRpcMethod("getNetworksForTransport")] + string[] getNetworksForTransport(string credentials, string id); + + [XmlRpcMethod("setNetworksForTransport")] + bool setNetworksForTransport(string credentials, string id, string[] networks); + + [XmlRpcMethod("getNetwork")] + Network GetNetwork(string credentials, string id); + + [XmlRpcMethod("createNetwork")] + bool CreateNetwork(string credentials, Network network); + + [XmlRpcMethod("modifyNetwork")] + bool ModifyNetwork(string credentials, Network network); + + [XmlRpcMethod("removeNetworks")] + bool RemoveNetworks(string credentials, string[] ids); + + + // Deployed Services related + [XmlRpcMethod("getDeployedServices")] + DeployedService[] GetDeployedServices(string credentials, bool all); + + [XmlRpcMethod("getDeployedService")] + DeployedService GetDeployedService(string credentials, string id); + + [XmlRpcMethod("removeDeployedService")] + bool RemoveDeployedService(string credentials, string id); + + [XmlRpcMethod("createDeployedService")] + string CreateDeployedService(string credentials, DeployedService deployedService); + + [XmlRpcMethod("modifyDeployedService")] + bool ModifyDeployedService(string credentials, DeployedService deployedService); + + [XmlRpcMethod("getGroupsAssignedToDeployedService")] + Group[] GetGroupsAssignedToDeployedService(string credentials, string deployedServiceId); + + [XmlRpcMethod("assignGroupToDeployedService")] + bool AssignGroupToDeployedService(string credentials, string deployedServiceId, string groupId); + + [XmlRpcMethod("removeGroupsFromDeployedService")] + bool RemoveGroupsFromDeployedService(string credentials, string deployedService, string[] groupIds); + + [XmlRpcMethod("getTransportsAssignedToDeployedService")] + Transport[] GetTransportsAssignedToDeployedService(string credentials, string idDeployedService); + + [XmlRpcMethod("assignTransportToDeployedService")] + bool AssignTransportToDeployedService(string credentials, string deployedServiceId, string transportId); + + [XmlRpcMethod("removeTransportFromDeployedService")] + bool RemoveTransportFromDeployedService(string credentials, string deployedService, string[] transportIds); + + [XmlRpcMethod("getPublications")] + DeployedServicePublication[] GetPublications(string credentials, string idParent); + + [XmlRpcMethod("publishDeployedService")] + bool PublishDeployedService(string credentials, string idParent); + + [XmlRpcMethod("cancelPublication")] + bool CancelPublication(string credentials, string id); + + [XmlRpcMethod("getCachedDeployedServices")] + CachedDeployedService[] GetCachedDeployedServices(string credentials, string idParent); + + [XmlRpcMethod("getAssignedDeployedServices")] + AssignedDeployedService[] GetAssignedDeployedServices(string credentials, string idParent); + + [XmlRpcMethod("getAssignableDeployedServices")] + AssignableDeployedService[] GetAssignableDeployedServices(string credentials, string idParent); + + [XmlRpcMethod("removeUserService")] + bool RemoveUserService(string credentials, string[] ids); + + [XmlRpcMethod("assignDeployedService")] + bool AssignDeployedService(string credentials, string idParent, string idDeployedUserService, string idUser); + + [XmlRpcMethod("getUserDeployedServiceError")] + string GetUserDeployedServiceError(string credentials, string id); + + // Utilities and related stuff + [XmlRpcMethod("flushCache")] + bool FlushCache(string credentials); + + [XmlRpcMethod("getConfiguration")] + Configuration[] GetConfiguration(string credentials); + + [XmlRpcMethod("updateConfiguration")] + bool UpdateConfiguration(string credentials, Configuration[] configuration); + + // Callbacks invoker + [XmlRpcMethod("chooseCallback")] + GuiFieldValue[] InvokeChooseCallback(string credentials, string name, GuiFieldValue[] parameters); + + + } + +} diff --git a/client/administration/UdsAdmin/xmlrpc/UDSAdminService.cs b/client/administration/UdsAdmin/xmlrpc/UDSAdminService.cs new file mode 100644 index 000000000..295746abf --- /dev/null +++ b/client/administration/UdsAdmin/xmlrpc/UDSAdminService.cs @@ -0,0 +1,727 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.Windows.Forms; +using System.Reflection; + +using CookComputing.XmlRpc; + +namespace UdsAdmin.xmlrpc +{ + public class UdsAdminService + { + //public enum RemoteType { ServiceProvider = 0, Service = 1, Authenticator = 2 }; + private static IUDSAdmin s = null; + private static string server = ""; + private static string credentials = ""; + public static bool isAdmin = false; + + private static void insertNameCommentsPriorityNet(string name, string comments, int priority, bool positiveNet, ref GuiFieldValue[] data) + { + int len = data.Length; + Array.Resize(ref data, len + 4); + data[len] = new GuiFieldValue("name", name); + data[len + 1] = new GuiFieldValue("comments", comments); + data[len + 2] = new GuiFieldValue("priority", priority.ToString()); + data[len + 3] = new GuiFieldValue("positiveNet", positiveNet ? xmlrpc.Constants.TRUE : xmlrpc.Constants.FALSE); + } + + private static void insertNameCommentsPriority(string name, string comments, int priority, ref GuiFieldValue[] data) + { + int len = data.Length; + Array.Resize(ref data, len + 3); + data[len] = new GuiFieldValue("name", name); + data[len + 1] = new GuiFieldValue("comments", comments); + data[len + 2] = new GuiFieldValue("priority", priority.ToString()); + } + + private static void insertNameComments(string name, string comments, ref GuiFieldValue[] data) + { + int len = data.Length; + Array.Resize(ref data, len + 2); + data[len] = new GuiFieldValue("name", name); + data[len + 1] = new GuiFieldValue("comments", comments); + } + + public static void Initialize(string url) + { + System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; + s = XmlRpcProxyGen.Create(); + s.Timeout = UdsAdmin.Properties.Settings.Default.TimeOut; + s.Url = url; + s.KeepAlive = false; + s.EnableCompression = true; // Accepts gzip and compress data + } + + // Service provider related methods + public static ServiceProviderType[] GetServiceProvidersTypes() + { + try + { + return s.GetServiceProvidersTypes(credentials); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + return null; + } + + public static ServiceProvider[] GetServiceProviders() + { + return s.GetServiceProviders(credentials); + } + + public static GuiField[] GetServiceProviderGui(string type) + { + try { + return s.GetServiceProviderGui(credentials, type); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Exception invoking getServiceProviderGui!!!"); + return null; + } + + } + + public static GuiFieldValue[] GetServiceProvider(string id) + { + return s.GetServiceProvider(credentials, id); + } + + + public static bool CreateServiceProvider(string name, string comments, string type, GuiFieldValue[] data) + { + insertNameComments(name, comments, ref data); + + return s.CreateServiceProvider(credentials, type, data); + } + + public static bool ModifyServiceProvider(string name, string comments, string id, GuiFieldValue[] data) + { + insertNameComments(name, comments, ref data); + + return s.ModifyServiceProvider(credentials, id, data); + } + + public static bool RemoveServiceProvider(string id) + { + return s.RemoveServiceProvider(credentials, id); + } + + public static ServiceType[] GetOffersFromServiceProvider(string type) + { + return s.GetOffersFromServiceProvider(credentials, type); + } + + public static ResultTest testServiceProvider(string type, GuiFieldValue[] data) + { + return s.TestServiceProvider(credentials, type, data); + } + + public static string CheckServiceProvider(string id) + { + return s.CheckServiceProvider(credentials, id); + } + + // Services related methods + public static Service[] GetServices(string idParent) + { + return s.GetServices(credentials,idParent); + } + + public static Service[] GetAllServices() + { + return s.GetAllServices(credentials); + } + + public static GuiField[] GetServiceGui(string idParent, string type) + { + try + { + return s.GetServiceGui(credentials, idParent, type); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Exception invoking getServiceGui!!!"); + return null; + } + } + + public static GuiFieldValue[] GetService(string id) + { + return s.GetService(credentials, id); + } + + public static bool CreateService(string idParent, string name, string comments, string type, GuiFieldValue[] data) + { + insertNameComments(name, comments, ref data); + + return s.CreateService(credentials, idParent, type, data); + } + + public static bool ModifyService(string name, string comments, string id, GuiFieldValue[] data) + { + insertNameComments(name, comments, ref data); + + return s.ModifyService(credentials, id, data); + } + + public static bool RemoveService(string id) + { + return s.RemoveService(credentials, id); + } + + + // Login related + + public static SimpleInfo[] GetAdminAuths(string host, bool https) + { + server = https ? "https" : "http"; + server += "://" + host; + string url = server + "/xmlrpc"; + try + { + Initialize(url); + return s.GetAdminAuths(UdsAdmin.Properties.Settings.Default.Locale); + } + catch (Exception e) + { + throw new exceptions.CommunicationException("Can't connect to server " + e.Message); + } + } + + public static void Login(string username, string password, string idAuth) + { + LoginData login = s.Login(username, password, idAuth, UdsAdmin.Properties.Settings.Default.Locale); + credentials = login.credentials; + if (credentials == "") + throw new exceptions.AuthenticationException("Invalid username os password"); + Version v = Assembly.GetExecutingAssembly().GetName().Version; + string executingVersion = v.Major.ToString() + "." + + v.Minor.ToString() + "." + v.Build.ToString(); + if( executingVersion != login.versionRequired ) + throw new exceptions.NewVersionRequiredException("New version required", login.url); + isAdmin = login.isAdmin; + return; + + } + + public static void Logout() + { + if (s != null) + try + { + s.Logout(credentials); + credentials = ""; + s = null; + } + catch (Exception) + { + // If credentials are not valid, or we can't reach server, we are exiting so we don't care :-) + } + } + + // Authenticators + public static AuthenticatorType[] GetAuthenticatorsTypes() + { + try + { + return s.GetAuthenticatorsTypes(credentials); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + return null; + } + + public static AuthenticatorType GetAuthenticatorType(string id) + { + return s.GetAuthenticatorType(credentials, id); + } + + public static Authenticator[] GetAuthenticators() + { + return s.GetAuthenticators(credentials); + } + + public static GuiField[] GetAuthenticatorGui(string type) + { + try + { + return s.GetAuthenticatorGui(credentials, type); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Exception invoking getAuthenticatorGui!!!"); + return null; + } + + } + + public static GuiFieldValue[] GetAuthenticator(string id) + { + return s.GetAuthenticator(credentials, id); + } + + public static Group[] GetAuthenticatorGroups(string id) + { + return s.GetAuthenticatorGroups(credentials, id); + } + + public static bool CreateAuthenticator(string name, string comments, string type, int priority, GuiFieldValue[] data) + { + insertNameCommentsPriority(name, comments, priority, ref data); + + return s.CreateAuthenticator(credentials, type, data); + } + + public static bool ModifyAuthenticator(string name, string comments, int priority, string id, GuiFieldValue[] data) + { + insertNameCommentsPriority(name, comments, priority, ref data); + + return s.ModifyAuthenticator(credentials, id, data); + } + + public static bool RemoveAuthenticator(string id) + { + return s.RemoveAuthenticator(credentials, id); + } + + public static ResultTest TestAuthenticator(string type, GuiFieldValue[] data) + { + return s.TestAuthenticator(credentials, type, data); + } + + public static string CheckAuthenticator(string id) + { + return s.CheckAuthenticator(credentials, id); + } + + public static SimpleInfo[] SearchAuthenticator(string id, bool searchUser, string srchString) + { + return s.SearchAuthenticator(credentials, id, searchUser, srchString); + } + + // Authenticators, groups related methods + public static Group[] GetGroups(string idParent) + { + return s.GetGroups(credentials, idParent); + } + + + public static Group GetGroup(string id) + { + return s.GetGroup(credentials, id); + } + + public static bool CreateGroup(Group grp) + { + return s.CreateGroup(credentials, grp); + } + + public static bool ChangeGroupsState(string[] ids, bool newState) + { + return s.ChangeGroupsState(credentials, ids, newState); + } + + public static bool RemoveGroups(string[] ids) + { + return s.RemoveGroups(credentials, ids); + } + + // Authenticators, users related methods + public static User[] GetUsers(string idParent) + { + return s.GetUsers(credentials, idParent); + } + + public static User GetUser(string id) + { + return s.GetUser(credentials, id); + } + + public static Group[] GetUserGroups(string id) + { + try + { + return s.GetUserGroups(credentials, id); + } + catch (System.Net.WebException e) + { + MessageBox.Show(e.Message, "Exception invoking getUserGroups!!!"); + return null; + } + + } + + public static bool ChangeUsersState(string[] ids, string newState) + { + return s.ChangeUsersState(credentials, ids, newState); + } + + public static bool RemoveUsers(string[] ids) + { + return s.RemoveUsers(credentials, ids); + } + + public static bool CreateUser(User usr) + { + return s.CreateUser(credentials, usr); + } + + + public static bool ModifyUser(User user) + { + return s.ModifyUser(credentials, user); + } + + public static PrefGroup[] GetPrefsForUser(string id) + { + return s.GetPrefsForUser(credentials, id); + } + + + public static bool SetPrefsForUser(string id, GuiFieldValue[] data) + { + return s.SetPrefsForUser(credentials, id, data); + } + + // OS Manager Related methods + public static OSManagerType[] GetOSManagersTypes() + { + try + { + return s.GetOSManagersTypes(credentials); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + return null; + } + + public static OSManager[] GetOSManagers() + { + return s.GetOSManagers(credentials); + } + + public static GuiField[] GetOSManagerGui(string type) + { + try + { + return s.GetOSManagerGui(credentials, type); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Exception invoking GetOSManagerGui!!!"); + return null; + } + + } + + public static GuiFieldValue[] GetOSManager(string id) + { + return s.GetOSManager(credentials, id); + } + + + public static bool CreateOSManager(string name, string comments, string type, GuiFieldValue[] data) + { + insertNameComments(name, comments, ref data); + + return s.CreateOSManager(credentials, type, data); + } + + public static bool ModifyOSManager(string name, string comments, string id, GuiFieldValue[] data) + { + insertNameComments(name, comments, ref data); + + return s.ModifyOSManager(credentials, id, data); + } + + public static bool RemoveOSManager(string id) + { + return s.RemoveOSManager(credentials, id); + } + + public static ResultTest TestOsManager(string type, GuiFieldValue[] data) + { + return s.TestOsManager(credentials, type, data); + } + + public static string CheckOSManager(string id) + { + return s.CheckOSManager(credentials, id); + } + + // Transports Related methods + public static TransportType[] GetTransportsTypes() + { + try + { + return s.GetTransportsTypes(credentials); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + return null; + } + + public static Transport[] GetTransports() + { + return s.GetTransports(credentials); + } + + public static GuiField[] GetTransportGui(string type) + { + try + { + return s.GetTransportGui(credentials, type); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Exception invoking GetTransportGui!!!"); + return null; + } + + } + + public static GuiFieldValue[] GetTransport(string id) + { + return s.GetTransport(credentials, id); + } + + + public static string CreateTransport(string name, string comments, int priority, bool positiveNets, string type, GuiFieldValue[] data) + { + insertNameCommentsPriorityNet(name, comments, priority, positiveNets, ref data); + + return s.CreateTransport(credentials, type, data); + } + + public static bool ModifyTransport(string name, string comments, int priority, bool positiveNets, string id, GuiFieldValue[] data) + { + insertNameCommentsPriorityNet(name, comments, priority, positiveNets, ref data); + + return s.ModifyTransport(credentials, id, data); + } + + public static bool RemoveTransport(string id) + { + return s.RemoveTransport(credentials, id); + } + + public static string CheckTransport(string id) + { + return s.CheckTransport(credentials, id); + } + + // Networks (for transports) Related methods + public static Network[] GetNetworks() + { + return s.GetNetworks(credentials); + } + + public static string[] GetNetworksForTransport(string id) + { + return s.getNetworksForTransport(credentials, id); + } + + public static bool SetNetworksForTransport(string id, string[] networks) + { + return s.setNetworksForTransport(credentials, id, networks); + } + + public static Network GetNetwork(string id) + { + return s.GetNetwork(credentials, id); + } + + public static bool CreateNetwork(Network net) + { + return s.CreateNetwork(credentials, net); + } + + public static bool ModifyNetwork(Network net) + { + return s.ModifyNetwork(credentials, net); + } + + public static bool RemoveNetworks(string[] ids) + { + return s.RemoveNetworks(credentials, ids); + } + + + // Deployed service related + public static DeployedService[] GetDeployedServices(bool all = false) + { + return s.GetDeployedServices(credentials, all); + } + + public static DeployedService GetDeployedService(string id) + { + return s.GetDeployedService(credentials, id); + } + + public static bool RemoveDeployedService(string id) + { + return s.RemoveDeployedService(credentials, id); + } + + public static string CreateDeployedService(DeployedService deployedService) + { + return s.CreateDeployedService(credentials, deployedService); + } + + public static bool ModifyDeployedService(DeployedService deployedService) + { + return s.ModifyDeployedService(credentials, deployedService); + } + + public static Group[] GetGroupsAssignedToDeployedService(string deployedServiceId) + { + return s.GetGroupsAssignedToDeployedService(credentials, deployedServiceId); + } + + public static bool AssignGroupToDeployedService(string deployedServiceId, string groupId) + { + return s.AssignGroupToDeployedService(credentials, deployedServiceId, groupId); + } + + public static bool RemoveGroupsFromDeployedService(string deployedServiceId, string[] groupIds) + { + return s.RemoveGroupsFromDeployedService(credentials, deployedServiceId, groupIds); + } + + public static Transport[] GetTransportsAssignedToDeployedService(string idDeployedService) + { + return s.GetTransportsAssignedToDeployedService(credentials, idDeployedService); + } + + public static bool AssignTransportToDeployedService(string deployedServiceId, string groupId) + { + return s.AssignTransportToDeployedService(credentials, deployedServiceId, groupId); + } + + public static bool RemoveTransportFromDeployedService(string deployedServiceId, string[] groupIds) + { + return s.RemoveTransportFromDeployedService(credentials, deployedServiceId, groupIds); + } + + public static DeployedServicePublication[] getPublications(DeployedService deployedService) + { + return s.GetPublications(credentials, deployedService.id); + } + + public static bool PublishDeployedService(DeployedService deployedService) + { + return s.PublishDeployedService(credentials, deployedService.id); + } + + public static bool CancelPublication(string id) + { + return s.CancelPublication(credentials, id); + } + + + public static CachedDeployedService[] GetCachedDeployedServices(DeployedService deployedService) + { + return s.GetCachedDeployedServices(credentials, deployedService.id); + } + + public static AssignedDeployedService[] GetAssignedDeployedServices(DeployedService deployedService) + { + return s.GetAssignedDeployedServices(credentials, deployedService.id); + } + + public static AssignableDeployedService[] GetAssignableDeployedServices(string idParent) + { + return s.GetAssignableDeployedServices(credentials, idParent); + } + + public static bool AssignDeployedService(string idParent, string idDeployedUserService, string idUser) + { + return s.AssignDeployedService(credentials, idParent, idDeployedUserService, idUser); + } + + public static bool RemoveUserService(string[] ids) + { + return s.RemoveUserService(credentials, ids); + } + + public static string GetUserDeployedServiceError(string id) + { + return s.GetUserDeployedServiceError(credentials, id); + } + + + // Utility methods + public static bool FlushCache() + { + return s.FlushCache(credentials); + } + + public static Configuration[] GetConfiguration() + { + return s.GetConfiguration(credentials); + } + + public static bool UpdateConfiguration(Configuration[] configuration) + { + return s.UpdateConfiguration(credentials, configuration); + } + + // Calbacks + + public static GuiFieldValue[] InvokeChooseCallback(string name, GuiFieldValue[] parameters) + { + try + { + return s.InvokeChooseCallback(credentials, name, parameters); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Exception invoking getServiceGui!!!"); + return null; + } + + } + + } +} diff --git a/client/administration/UdsAdmin/xmlrpc/Util.cs b/client/administration/UdsAdmin/xmlrpc/Util.cs new file mode 100644 index 000000000..23f8e4887 --- /dev/null +++ b/client/administration/UdsAdmin/xmlrpc/Util.cs @@ -0,0 +1,76 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UdsAdmin.xmlrpc +{ + internal class Util + { + public static string GetStringFromState(string state, string osState = "") + { + switch (state) + { + case Constants.STATE_USABLE: + if (osState != "" && osState != Constants.STATE_USABLE ) + return Strings.waitingOsReady; + return Strings.stateUsable; + case Constants.STATE_LAUNCHING: + return Strings.stateLaunching; + case Constants.STATE_PREPARING: + return Strings.statePreparing; + case Constants.STATE_CANCELING: + return Strings.stateCanceling; + case Constants.STATE_CANCELED: + return Strings.stateCanceled; + case Constants.STATE_REMOVABLE: + return Strings.stateRemovable; + case Constants.STATE_REMOVING: + return Strings.stateRemoving; + case Constants.STATE_REMOVED: + return Strings.stateRemoved; + case Constants.STATE_ERROR: + return Strings.stateError; + // From here, user, groups, ... states + case Constants.STATE_ACTIVE: + return Strings.stateActive; + case Constants.STATE_BLOCKED: + return Strings.stateBlocked; + case Constants.STATE_INACTIVE: + return Strings.stateInactive; + default: + return Strings.stateUnknown; + } + } + + } +} diff --git a/client/administration/UdsAdmin/xmlrpc/structs.cs b/client/administration/UdsAdmin/xmlrpc/structs.cs new file mode 100644 index 000000000..c05e81513 --- /dev/null +++ b/client/administration/UdsAdmin/xmlrpc/structs.cs @@ -0,0 +1,474 @@ +// +// Copyright (c) 2012 Virtual Cable S.L. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Virtual Cable S.L. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// author: Adolfo Gómez, dkmaster at dkmon dot com + +using System; +using System.Collections.Generic; +using System.Text; +using CookComputing.XmlRpc; + +namespace UdsAdmin.xmlrpc +{ + + public class BaseType + { + public string name; + public string type; + public string description; + public string icon; + + public override string ToString() + { + return name; + } + + } + + public class BaseItemData + { + public string id; + public string name; + public string comments; + public string type; + public string typeName; + + public override string ToString() + { + return name + "," + comments; + } + + public override bool Equals(object obj) + { + bool eq = base.Equals(obj); + if (eq == true) + return eq; + if (!(obj is BaseItemData)) + return false; + return ((BaseItemData)obj).id == id; + } + + public override int GetHashCode() { return 0; } + } + + public class SimpleInfo + { + public string id; + public string name; + + public SimpleInfo() + { + id = name = ""; + } + + + public SimpleInfo(string id, string name) + { + this.id = id; + this.name = name; + } + + public override string ToString() + { + return name; + } + } + + public struct LoginData + { + public string credentials; + public string versionRequired; + public string url; // Download url for new version if needed + [XmlRpcMissingMapping(MappingAction.Ignore)] // Right now, linux url is not used for anything... :-) + public string urlLinux; // Download url for linux version + public bool isAdmin; + } + + public class GuiItem + { + public string defvalue; + public int length; + public bool required; + public string label; + public bool rdonly; + public int order; + public string tooltip; + public string type; + // Choice values (available only for choices) + [XmlRpcMissingMapping(MappingAction.Ignore)] + public Choice[] values; + + [XmlRpcMissingMapping(MappingAction.Ignore)] + public ChoiceCallback fills; + + // For multichoices + [XmlRpcMissingMapping(MappingAction.Ignore)] + public int rows; + } + + public class GuiField + { + public string name; + public string value; // For "modify" operations + public GuiItem gui; + } + + public class PrefGroup + { + public string moduleLabel; + public GuiField[] prefs; + } + + public struct GuiFieldValue + { + public string name; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public string value; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public Choice[] values; + + public GuiFieldValue(string name, string value) + { + this.name = name; + this.value = value; + this.values = null; + } + + public GuiFieldValue(string name, Choice[] values) + { + this.name = name; + this.value = null; + this.values = values; + } + + static public string getData(GuiFieldValue[] fields, string field) + { + foreach( GuiFieldValue g in fields ) + if( g.name == field ) + return g.value; + return ""; + } + } + + public class ServiceProviderType : BaseType + { + } + + public class ServiceProvider : BaseItemData + { + } + + public class ServiceType : BaseType + { + } + + public class ServiceInfo + { + public bool needsPublication; + public int maxDeployed; + public bool usesCache; + public bool usesCacheL2; + public string cacheTooltip; + public string cacheTooltipL2; + public bool needsManager; + public bool mustAssignManually; + public string typeName; // Type of the service + } + + public class Service : BaseItemData + { + public string idParent; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public ServiceInfo info; + } + + public class AuthenticatorType : BaseType + { + public bool isExternalSource; + public bool canSearchUsers; + public bool canSearchGroups; + public bool needsPassword; + public string userNameLabel; + public string groupNameLabel; + public string passwordLabel; + public bool canCreateUsers; + } + + public class Authenticator : BaseItemData + { + public string priority; + } + + public class Group + { + public string idParent; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public string nameParent; + public string id; + public string name; + public string comments; + public bool active; + + public override string ToString() + { + return name + ", " + comments; + } + } + + public class User + { + public string idParent; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public string nameParent; + public string id; + public string name; + public string password; + public string oldPassword; // To check if password has changed, cause it's encripted at database and can't be reversed + public string realName; + public string comments; + public string state; + public DateTime lastAccess; + public bool staffMember; + public bool isAdmin; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public Group[] groups; // For internal authenticators, active groups are those that the user belongs to, inactive those not + + public override string ToString() + { + return name + "(" + realName + ")" + ", " + comments; + } + } + + public class OSManagerType : BaseType + { + } + + public class OSManager : BaseItemData + { + } + + + public class TransportType : BaseType + { + } + + public class Transport : BaseItemData + { + public string priority; + public string[] networks; + } + + public class Network + { + public string id; + public string name; + public string netStart; + public string netEnd; + + public Network() + { + id = name = netStart = netEnd = ""; + } + + public override string ToString() + { + return name; + } + } + + public class DeployedService + { + public string id; + public string name; + public string comments; + public string idService; + public string idOsManager; + public string state; + public int initialServices; + public int cacheL1; + public int cacheL2; + public int maxServices; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public string serviceName; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public string osManagerName; + public SimpleInfo[] transports; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public SimpleInfo[] groups; + [XmlRpcMissingMapping(MappingAction.Ignore)] + public ServiceInfo info; + } + + public class DeployedServicePublication + { + public string idParent; + public string id; + public string state; + public DateTime publishDate; + public string reason; + public string revision; + } + + public class UserDeployedService + { + public string idParent; + public string id; + public string uniqueId; + public string friendlyName; + public string state; + public string osState; + public DateTime stateDate; + public DateTime creationDate; + public string revision; + } + + public class CachedDeployedService : UserDeployedService + { + public string cacheLevel; + } + + public class AssignedDeployedService : UserDeployedService + { + public string user; + public bool inUse; + public DateTime inUseDate; + } + + public class AssignableDeployedService + { + public string id; + public string name; + + public override string ToString() + { + return name; + } + } + + public class ResultTest + { + public bool ok; + public string message; + } + + public class Configuration + { + public string section; + public string key; + public string value; + public bool crypt; + + public Configuration() + { + section = key = value = ""; + crypt = false; + } + + public Configuration(string section, string key, string value, bool crypt) + { + this.section = section; this.key = key; this.value = value; this.crypt = crypt; + } + }; + + public struct ChoiceCallback + { + public string callbackName; + public string[] parameters; + } + + public struct Choice + { + public string id; + public string text; + + public Choice(string id, string text) + { + this.id = id; + this.text = text; + } + + public override string ToString() + { + return text; + } + + public override bool Equals(object obj) + { + bool eq = base.Equals(obj); + if (eq == true) + return eq; + if (!(obj is Choice)) + return false; + return ((Choice)obj).id == id; + } + + public override int GetHashCode() { return 0; } + } + + // Comparators + public class ServiceProviderTypeSorterByName : IComparer + { + public int Compare(ServiceProviderType a, ServiceProviderType b) + { + return a.name.CompareTo(b.name); + } + } + + public class ServiceTypeSorterByName : IComparer + { + public int Compare(ServiceType a, ServiceType b) + { + return a.name.CompareTo(b.name); + } + } + + + public class AuthenticatorTypeSorterByName : IComparer + { + public int Compare(AuthenticatorType a, AuthenticatorType b) + { + return a.name.CompareTo(b.name); + } + } + + public class OSManagerTypeSorterByName : IComparer + { + public int Compare(OSManagerType a, OSManagerType b) + { + return a.name.CompareTo(b.name); + } + } + + public class TransportTypeSorterByName : IComparer + { + public int Compare(TransportType a, TransportType b) + { + return a.name.CompareTo(b.name); + } + } + +} diff --git a/client/administration/installer/UDSAdminInstaller/.project b/client/administration/installer/UDSAdminInstaller/.project new file mode 100644 index 000000000..8f9652cb4 --- /dev/null +++ b/client/administration/installer/UDSAdminInstaller/.project @@ -0,0 +1,11 @@ + + + UDSAdminInstaller + + + + + + + + diff --git a/client/administration/installer/UDSAdminInstaller/UDSAdminSetup.exe b/client/administration/installer/UDSAdminInstaller/UDSAdminSetup.exe new file mode 100644 index 0000000000000000000000000000000000000000..3b8f606129e0a8ebe693fd66e3221fc709d8b462 GIT binary patch literal 520046 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Æ?Ak*R`!{zFr$CV4aMCbJWYggJT+>ULz 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
NhSh*lA-tDfyS5eqj@kBo#nKAm?A+&Me>uYw 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

8pj?#ytHws#<(tj|Y-v5UJj_MP9n&xm4t(m$4 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

xh)6D0;z3r&Q*78kd}M1I>`~Iu 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°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 literal 0 HcmV?d00001 diff --git a/client/administration/installer/UDSAdminInstaller/license.txt b/client/administration/installer/UDSAdminInstaller/license.txt new file mode 100644 index 000000000..985b510fc --- /dev/null +++ b/client/administration/installer/UDSAdminInstaller/license.txt @@ -0,0 +1,27 @@ +Copyright (c) 2012 Virtual Cable S.L. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Virtual Cable S.L. nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/client/administration/installer/UDSAdminInstaller/udsadmin.nsi b/client/administration/installer/UDSAdminInstaller/udsadmin.nsi new file mode 100644 index 000000000..411fed925 --- /dev/null +++ b/client/administration/installer/UDSAdminInstaller/udsadmin.nsi @@ -0,0 +1,163 @@ +# Auto-generated by EclipseNSIS Script Wizard +# 28-nov-2011 9:41:00 + +Name "UDS Administration" + +# General Symbol Definitions +!define REGKEY "SOFTWARE\$(^Name)" +!define VERSION 1.0 +!define COMPANY "Virtual Cable S.L." +!define URL http://www.virtualcable.es + +# MultiUser Symbol Definitions +!define MULTIUSER_EXECUTIONLEVEL Standard +!define MULTIUSER_INSTALLMODE_COMMANDLINE +!define MULTIUSER_INSTALLMODE_INSTDIR "UDS Administration Client" +!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "${REGKEY}" +!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUE "Path" + +# MUI Symbol Definitions +!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" +!define MUI_UNFINISHPAGE_NOAUTOCLOSE +!define MUI_LANGDLL_REGISTRY_ROOT HKLM +!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY} +!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage + +# Included files +!include MultiUser.nsh +!include Sections.nsh +!include MUI2.nsh + +# Reserved Files +!insertmacro MUI_RESERVEFILE_LANGDLL + +# Variables +Var StartMenuGroup + +# Installer pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE license.txt +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +# Installer languages +!insertmacro MUI_LANGUAGE English +!insertmacro MUI_LANGUAGE Spanish +!insertmacro MUI_LANGUAGE French +!insertmacro MUI_LANGUAGE German + +# Installer attributes +OutFile UDSAdminSetup.exe +InstallDir "UDS Administration Client" +CRCCheck on +XPStyle on +ShowInstDetails show +VIProductVersion 1.0.0.0 +VIAddVersionKey /LANG=${LANG_ENGLISH} ProductName "UDS Administration Client" +VIAddVersionKey /LANG=${LANG_ENGLISH} ProductVersion "${VERSION}" +VIAddVersionKey /LANG=${LANG_ENGLISH} CompanyName "${COMPANY}" +VIAddVersionKey /LANG=${LANG_ENGLISH} CompanyWebsite "${URL}" +VIAddVersionKey /LANG=${LANG_ENGLISH} FileVersion "${VERSION}" +VIAddVersionKey /LANG=${LANG_ENGLISH} FileDescription "" +VIAddVersionKey /LANG=${LANG_ENGLISH} LegalCopyright "" +InstallDirRegKey HKLM "${REGKEY}" Path +ShowUninstDetails show + +# Installer sections +Section -Main SEC0000 + SetOutPath $INSTDIR + SetOverwrite on + File ..\..\UdsAdmin\bin\Release\CookComputing.XmlRpcV2.dll + File ..\..\UdsAdmin\bin\Release\UdsAdmin.exe + File ..\..\UdsAdmin\bin\Release\UdsAdmin.exe.config + SetOutPath $SMPROGRAMS\$StartMenuGroup + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\UDS Administration Client.lnk" $INSTDIR\UdsAdmin.exe + SetOutPath $INSTDIR\de + File ..\..\UdsAdmin\bin\Release\de\UdsAdmin.resources.dll + SetOutPath $INSTDIR\es + File ..\..\UdsAdmin\bin\Release\es\UdsAdmin.resources.dll + SetOutPath $INSTDIR\fr + File ..\..\UdsAdmin\bin\Release\fr\UdsAdmin.resources.dll + WriteRegStr HKLM "${REGKEY}\Components" Main 1 +SectionEnd + +Section -post SEC0001 + WriteRegStr HKLM "${REGKEY}" Path $INSTDIR + SetOutPath $INSTDIR + WriteUninstaller $INSTDIR\UDSAdminRemover.exe + SetOutPath $SMPROGRAMS\$StartMenuGroup + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\$(^UninstallLink).lnk" $INSTDIR\UDSAdminRemover.exe + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\UDSAdminRemover.exe + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\UDSAdminRemover.exe + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1 + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1 +SectionEnd + +# Macro for selecting uninstaller sections +!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID + Push $R0 + ReadRegStr $R0 HKLM "${REGKEY}\Components" "${SECTION_NAME}" + StrCmp $R0 1 0 next${UNSECTION_ID} + !insertmacro SelectSection "${UNSECTION_ID}" + GoTo done${UNSECTION_ID} +next${UNSECTION_ID}: + !insertmacro UnselectSection "${UNSECTION_ID}" +done${UNSECTION_ID}: + Pop $R0 +!macroend + +# Uninstaller sections +Section /o -un.Main UNSEC0000 + Delete /REBOOTOK $INSTDIR\fr\UdsAdmin.resources.dll + Delete /REBOOTOK $INSTDIR\es\UdsAdmin.resources.dll + Delete /REBOOTOK $INSTDIR\de\UdsAdmin.resources.dll + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\UDS Administration Client.lnk" + Delete /REBOOTOK $INSTDIR\UdsAdmin.exe.config + Delete /REBOOTOK $INSTDIR\UdsAdmin.exe + Delete /REBOOTOK $INSTDIR\CookComputing.XmlRpcV2.dll + DeleteRegValue HKLM "${REGKEY}\Components" Main +SectionEnd + +Section -un.post UNSEC0001 + DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^UninstallLink).lnk" + Delete /REBOOTOK $INSTDIR\UDSAdminRemover.exe + DeleteRegValue HKLM "${REGKEY}" Path + DeleteRegKey /IfEmpty HKLM "${REGKEY}\Components" + DeleteRegKey /IfEmpty HKLM "${REGKEY}" + RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup + RmDir /REBOOTOK $INSTDIR +SectionEnd + +# Installer functions +Function .onInit + InitPluginsDir + StrCpy $StartMenuGroup "Virtual Cable\UDS Administration Client" + !insertmacro MUI_LANGDLL_DISPLAY + !insertmacro MULTIUSER_INIT +FunctionEnd + +# Uninstaller functions +Function un.onInit + StrCpy $StartMenuGroup "Virtual Cable\UDS Administration Client" + !insertmacro MUI_UNGETLANGUAGE + !insertmacro MULTIUSER_UNINIT + !insertmacro SELECT_UNSECTION Main ${UNSEC0000} +FunctionEnd + +# Installer Language Strings +# TODO Update the Language Strings with the appropriate translations. + +LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall $(^Name)" +LangString ^UninstallLink ${LANG_SPANISH} "Uninstall $(^Name)" +LangString ^UninstallLink ${LANG_FRENCH} "Uninstall $(^Name)" +LangString ^UninstallLink ${LANG_GERMAN} "Uninstall $(^Name)" diff --git a/client/administration/xmlrpc/CookComputing.XmlRpcV2.dll b/client/administration/xmlrpc/CookComputing.XmlRpcV2.dll new file mode 100644 index 0000000000000000000000000000000000000000..2c52939a090a358bd6b4838783d0f608d177b8eb GIT binary patch literal 122880 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%0Al!QoszWFF9 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 + + linuxActor + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/linuxActor/.pydevproject b/linuxActor/.pydevproject new file mode 100644 index 000000000..6ca10d9e1 --- /dev/null +++ b/linuxActor/.pydevproject @@ -0,0 +1,10 @@ + + + + +python2.6 +python 2.6 + +/linuxActor/src + + diff --git a/linuxActor/.settings/org.eclipse.core.resources.prefs b/linuxActor/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..e8ab30bb9 --- /dev/null +++ b/linuxActor/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,8 @@ +#Thu Nov 17 08:31:37 CET 2011 +eclipse.preferences.version=1 +encoding//src/actor.py=utf-8 +encoding//src/uds/actor/__init__.py=utf-8 +encoding//src/uds/actor/daemon.py=utf-8 +encoding//src/uds/actor/renamer/__init__.py=utf-8 +encoding//src/uds/actor/renamer/debian.py=utf-8 +encoding//src/uds/actor/rpc.py=utf-8 diff --git a/linuxActor/.settings/org.eclipse.mylyn.tasks.ui.prefs b/linuxActor/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 000000000..1f33b4559 --- /dev/null +++ b/linuxActor/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Tue Jan 03 02:54:54 CET 2012 +eclipse.preferences.version=1 +project.repository.kind=mantis +project.repository.url=http\://192.168.0.6/mantis diff --git a/linuxActor/src/Makefile b/linuxActor/src/Makefile new file mode 100644 index 000000000..9d316af17 --- /dev/null +++ b/linuxActor/src/Makefile @@ -0,0 +1,26 @@ +LIBDIR = $(DESTDIR)/usr/share/pyshared/udsactor +BINDIR = $(DESTDIR)/usr/bin +CFGDIR = $(DESTDIR)/etc/udsactor +clean: + rm -f *.py[co] */*.py[co] +install: + mkdir -p $(LIBDIR) + mkdir -p $(BINDIR) + mkdir -p $(CFGDIR) + + mkdir -p $(LIBDIR)/uds/actor/renamer + + cp actor.py $(LIBDIR)/ + cp uds/*.py $(LIBDIR)/uds + cp uds/actor/*.py $(LIBDIR)/uds/actor + cp uds/actor/renamer/*.py $(LIBDIR)/uds/actor/renamer + + cp runActor.sh $(BINDIR)/udsactor + + cp udsactor.cfg $(CFGDIR) + + chmod 0755 $(BINDIR)/udsactor +uninstall: + rm -rf $(LIBDIR) + rm -f $(BINDIR)/udsactor + rm -rf $(CFGDIR) \ No newline at end of file diff --git a/linuxActor/src/actor.py b/linuxActor/src/actor.py new file mode 100644 index 000000000..e43508b60 --- /dev/null +++ b/linuxActor/src/actor.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +''' +Created on Nov 16, 2011 + +@author: dkmaster +''' + +import sys, time, logging +from uds.actor.daemon import Daemon +from uds.actor.rpc import Rpc +from uds.actor import net +from uds.actor.renamer import rename + +logger = logging.getLogger('uds') + +class MyDaemon(Daemon): + def run(self): + while True: + # Wait for networks to become ready + info = net.getExternalIpAndMacs() + if len(info) > 0: + break + time.sleep(4) + # Now, get info of what to do + Rpc.initialize() + + # waits for a valid command (maybe broker down or not reachable, so we loop here till we know what to do (that is, broker is reachable)) + todo = None + while todo is None: + Rpc.resetId() + todo = Rpc.getInfo() + if todo is None: + time.sleep(4) + + # We can get 'rename:newname', ''. Anything else is an error + data = todo.split(':') + + if data[0] == 'rename': + logger.info('Renaming to {0}'.format(data[1])) + rename(data[1]) + Rpc.setReady() + elif todo == '': + logger.info('Unmanaged machine') + # Unmanaged machine, exit + return + else: + # Error, log and exit + logger.error('Unknown command received: {0}'.format(todo)) + return + + # Keep notifiyin ip changes + info = net.getExternalIpAndMacs() + # We have "info" with know interfaces + while True: + newInfo = net.getExternalIpAndMacs() + for k in info.keys(): + if info[k]['ip'] != newInfo[k]['ip']: + if Rpc.notifyIpChange() is not None: + info = newInfo + else: + logger.info('Could not notify IP address. Will retry later.') + break + time.sleep(5) + return + + +if __name__ == '__main__': + if len(sys.argv) == 3: + if 'login' == sys.argv[1]: + logger.debug('Notifiyin login') + Rpc.initialize() + Rpc.login(sys.argv[2]) + sys.exit(0) + elif 'logout' == sys.argv[1]: + logger.debug('Notifiyin logout') + Rpc.initialize() + Rpc.logout(sys.argv[2]) + sys.exit(0) + + logger.debug('Executing actor') + daemon = MyDaemon('/var/run/udsactor.pid') + if len(sys.argv) == 2: + if 'start' == sys.argv[1]: + daemon.start() + elif 'stop' == sys.argv[1]: + daemon.stop() + elif 'restart' == sys.argv[1]: + daemon.restart() + else: + print "Unknown command" + sys.exit(2) + sys.exit(0) + else: + print "usage: %s start|stop|restart|login 'username'|logout 'username'" % sys.argv[0] + sys.exit(2) + diff --git a/linuxActor/src/debian/changelog b/linuxActor/src/debian/changelog new file mode 100644 index 000000000..ebcd24b67 --- /dev/null +++ b/linuxActor/src/debian/changelog @@ -0,0 +1,3 @@ +udsactor (1.0) stable; urgency=low + * Initial version + -- Adolfo Gómez García Fri, 18 Nov 2011 05:35:11 +0100 \ No newline at end of file diff --git a/linuxActor/src/debian/compat b/linuxActor/src/debian/compat new file mode 100644 index 000000000..c7930257d --- /dev/null +++ b/linuxActor/src/debian/compat @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/linuxActor/src/debian/config b/linuxActor/src/debian/config new file mode 100644 index 000000000..15abfcbb9 --- /dev/null +++ b/linuxActor/src/debian/config @@ -0,0 +1,46 @@ +#!/bin/bash -e + +. /usr/share/debconf/confmodule + +Q=0 +Q_DONE=3 + +nextState() { + if [[ $1 -eq 30 ]]; then + Q=$((Q-1)) + else + Q=$((Q+1)) + fi +} + +if [[ -f /etc/udsactor/udsactor.cfg ]]; then + TMPFILE=$(mktemp /tmp/udsactor.cfg.XXXXX) + trap "rm -f $TMPFILE" 0 + cat /etc/udsactor/udsactor.cfg | sed -e "s/\\[.*\\]//; s/ *= */=/; s/False/false/; s/True/true/" > $TMPFILE + . $TMPFILE + db_set udsactor/server $server + db_set udsactor/secure $ssl + db_set udsactor/timeout $timeout +fi + +db_capb backup + +while [[ $Q -lt $Q_DONE ]]; do + case $Q in + 0) + db_input high udsactor/server || true + db_go || true + nextState 0 + ;; + 1) + db_input high udsactor/secure || true + db_go || RET=$? + nextState $RET + ;; + 2) + db_input high udsactor/timeout || true + db_go || RET=$? + nextState 0 + ;; + esac +done diff --git a/linuxActor/src/debian/control b/linuxActor/src/debian/control new file mode 100644 index 000000000..1bb358358 --- /dev/null +++ b/linuxActor/src/debian/control @@ -0,0 +1,14 @@ +Source: udsactor +Section: contrib/net +Priority: extra +Maintainer: Adolfo Gómez García +Build-Depends: debhelper (>= 7), po-debconf +Standards-Version: 3.9.2 +Homepage: http://www.virtualcable.es + +Package: udsactor +Architecture: all +Depends: python (>= 2.6), ${misc:Depends} +Description: UDS Actor for Universal Destop Services + This package provides the actor needed for integration of + debian with uds services platform. diff --git a/linuxActor/src/debian/copyright b/linuxActor/src/debian/copyright new file mode 100644 index 000000000..c47d0a70b --- /dev/null +++ b/linuxActor/src/debian/copyright @@ -0,0 +1,26 @@ +Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 +Name: udsactor +Maintainer: Adolfo Gómez García +Source: http://www.virtualcable.es/ + +Copyright: 2010 Virtual Cable S.L. +License: GPL-2+ + +License: GPL-2+ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +. +On Debian systems, the full text of the GNU General Public +License version 2 can be found in the file +`/usr/share/common-licenses/GPL-2'. \ No newline at end of file diff --git a/linuxActor/src/debian/dirs b/linuxActor/src/debian/dirs new file mode 100644 index 000000000..2c662cfdc --- /dev/null +++ b/linuxActor/src/debian/dirs @@ -0,0 +1,2 @@ +/usr/bin/ +/usr/share/pyshared/udsactor diff --git a/linuxActor/src/debian/docs b/linuxActor/src/debian/docs new file mode 100644 index 000000000..b2b2a781e --- /dev/null +++ b/linuxActor/src/debian/docs @@ -0,0 +1 @@ +readme.txt diff --git a/linuxActor/src/debian/files b/linuxActor/src/debian/files new file mode 100644 index 000000000..db5df6fac --- /dev/null +++ b/linuxActor/src/debian/files @@ -0,0 +1 @@ +udsactor_1.0_all.deb contrib/net extra diff --git a/linuxActor/src/debian/init b/linuxActor/src/debian/init new file mode 100755 index 000000000..2d6ed8856 --- /dev/null +++ b/linuxActor/src/debian/init @@ -0,0 +1,21 @@ +#!/bin/sh -e +### BEGIN INIT INFO +# Provides: uds-actor +# Required-Start: $local_fs $remote_fs $network $syslog $named +# Required-Stop: $local_fs $remote_fs $network $syslog $named +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: UDS Actor +### END INIT INFO +# + +case "$1" in + start|stop|restart) + /usr/bin/udsactor $1 + ;; + force-reload) + ./actor restart + ;; + *) echo "Usage: $0 {start|stop|restart|force-reload}" >&2; exit 1 ;; +esac + diff --git a/linuxActor/src/debian/po/POTFILES.in b/linuxActor/src/debian/po/POTFILES.in new file mode 100644 index 000000000..98e8416d9 --- /dev/null +++ b/linuxActor/src/debian/po/POTFILES.in @@ -0,0 +1 @@ +[type: gettext/rfc822deb] templates \ No newline at end of file diff --git a/linuxActor/src/debian/po/templates.pot b/linuxActor/src/debian/po/templates.pot new file mode 100644 index 000000000..248e6f491 --- /dev/null +++ b/linuxActor/src/debian/po/templates.pot @@ -0,0 +1,58 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: agomez@virtualcable.es\n" +"POT-Creation-Date: 2011-11-21 11:40+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../templates:1001 +msgid "UDS Server address:" +msgstr "" + +#. Type: string +#. Description +#: ../templates:1001 +msgid "" +"The actor needs the address of the server in order to communicate with it. " +"Provide here full address (or i) of the UDS server" +msgstr "" + +#. Type: boolean +#. Description +#: ../templates:2001 +msgid "Use secure (https) connection to communicate with UDS server?" +msgstr "" + +#. Type: boolean +#. Description +#: ../templates:2001 +msgid "" +"If selected, the communication will be done using https. If not selected, " +"the communication will be done using http" +msgstr "" + +#. Type: string +#. Description +#: ../templates:3001 +msgid "Timeout in communications with UDS server:" +msgstr "" + +#. Type: string +#. Description +#: ../templates:3001 +msgid "The timeout is expressed in seconds" +msgstr "" diff --git a/linuxActor/src/debian/postinst b/linuxActor/src/debian/postinst new file mode 100644 index 000000000..272f94d8b --- /dev/null +++ b/linuxActor/src/debian/postinst @@ -0,0 +1,38 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + +set -e + +case "$1" in + configure) + # Ensure actor is not running + db_get udsactor/server + server=$RET + db_get udsactor/secure + ssl=$RET + if [ "$ssl" = "true" ]; then ssl=True; else ssl=False; fi + db_get udsactor/timeout + timeout=$RET + TMPFILE=$(mktemp /tmp/udsactor.cfg.XXXXX) + trap "rm -f $TMPFILE" 0 + cat /etc/udsactor/udsactor.cfg | sed -e "s/server=.*/server=$server/; s/ssl=.*/ssl=$ssl/; s/timeout=.*/timeout=$timeout/" > $TMPFILE + cp $TMPFILE /etc/udsactor/udsactor.cfg + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +# Don't know why, but descriptors get "weird" when launched daemon, so we tell here to debconf to stop. +# Solved not starting the service right now, defered to next reboot + + +exit 0 \ No newline at end of file diff --git a/linuxActor/src/debian/rules b/linuxActor/src/debian/rules new file mode 100755 index 000000000..cacfb7071 --- /dev/null +++ b/linuxActor/src/debian/rules @@ -0,0 +1,44 @@ +#!/usr/bin/make -f +# -*- makefile -*- +configure: configure-stamp +configure-stamp: + dh_testdir + touch configure-stamp +build: build-arch build-indep +build-arch: build-stamp +build-indep: build-stamp +build-stamp: configure-stamp + dh_testdir + $(MAKE) + touch $@ +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + dh_clean +install: build + dh_testdir + dh_testroot + dh_prep + dh_installdirs + $(MAKE) DESTDIR=$(CURDIR)/debian/udsactor install +binary-arch: build install + # emptyness +binary-indep: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installdebconf + dh_installinit --no-start + dh_python2=python + dh_compress + dh_link + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb +binary: binary-indep +.PHONY: build clean binary-indep binary install configure diff --git a/linuxActor/src/debian/templates b/linuxActor/src/debian/templates new file mode 100644 index 000000000..28432e3f5 --- /dev/null +++ b/linuxActor/src/debian/templates @@ -0,0 +1,19 @@ +Template: udsactor/server +Type: string +Default: +_Description: UDS Server address: + The actor needs the address of the server in order to communicate with it. + Provide here full address (or i) of the UDS server + +Template: udsactor/secure +Type: boolean +Default: true +_Description: Use secure (https) connection to communicate with UDS server? + If selected, the communication will be done using https. + If not selected, the communication will be done using http + +Template: udsactor/timeout +Type: string +Default: 5 +_Description: Timeout in communications with UDS server: + The timeout is expressed in seconds \ No newline at end of file diff --git a/linuxActor/src/readme.txt b/linuxActor/src/readme.txt new file mode 100644 index 000000000..64c14f772 --- /dev/null +++ b/linuxActor/src/readme.txt @@ -0,0 +1 @@ +This package provides the actor needed for using with UDS infrastructure. diff --git a/linuxActor/src/runActor.sh b/linuxActor/src/runActor.sh new file mode 100644 index 000000000..fbb141953 --- /dev/null +++ b/linuxActor/src/runActor.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd /usr/share/pyshared/udsactor +python actor.py $@ diff --git a/linuxActor/src/uds/__init__.py b/linuxActor/src/uds/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/linuxActor/src/uds/actor/__init__.py b/linuxActor/src/uds/actor/__init__.py new file mode 100644 index 000000000..3bf7f8746 --- /dev/null +++ b/linuxActor/src/uds/actor/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +''' +Created on Nov 16, 2011 + +@author: dkmaster +''' +import logging, logging.handlers +from config import config + +# Initializes logging facility (don't using dictConfig) + +log = logging.getLogger('uds') +log.setLevel(config['debug']) + +formatter = logging.Formatter('%(levelname)s %(asctime)s %(module)s %(message)s') + +fileHandler = logging.handlers.RotatingFileHandler(filename = config['log'], mode = 'a', maxBytes = config['maxsize'], backupCount = config['backups'], encoding = 'utf-8') +fileHandler.setLevel(logging.DEBUG) +fileHandler.setFormatter(formatter) + +#streamHandler = logging.StreamHandler() +#streamHandler.setLevel(logging.DEBUG) +#streamHandler.setFormatter(formatter) + +log.addHandler(fileHandler) +#log.addHandler(streamHandler) diff --git a/linuxActor/src/uds/actor/config.py b/linuxActor/src/uds/actor/config.py new file mode 100644 index 000000000..0176141d4 --- /dev/null +++ b/linuxActor/src/uds/actor/config.py @@ -0,0 +1,50 @@ +''' +Created on Nov 17, 2011 + +@author: dkmaster +''' +import os + +# Config file format: +# [broker] +# server = host:port (required) +# ssl = [True|False] (defaults to False) +# timeout = Timeout in seconds for xmlrpc (defaults to 10) +# [logging] +# log = /path/to/log (required, defaults to /tmp/udsactor.log) +# debug = [ERROR|INFO|DEBUG|WARN| (defaults to ERROR) +# maxsize = Max size of log file, in megas (defaults to 20) +# backups = Number of backups to keep of log file (defaults to 3) + + +import ConfigParser, logging, sys + +CONFIGFILE = '/etc/udsactor/udsactor.cfg' + +cfg = ConfigParser.SafeConfigParser(defaults={ 'server' : '', 'ssl' : False, 'timeout' : '10', + 'log' : '/tmp/udsactor.log', 'debug' : 'ERROR', 'maxsize' : '20', 'backups' : '3' }) +cfg.read(CONFIGFILE) + +levels = { + 'WARN' : logging.WARN, + 'INFO' : logging.INFO, + 'DEBUG': logging.DEBUG, + 'ERROR': logging.ERROR + } +try: + config = { + 'server' : cfg.get('broker', 'server'), + 'ssl' : cfg.getboolean('broker', 'ssl'), + 'timeout' : cfg.getint('broker', 'timeout'), + 'log' : cfg.get('logging', 'log'), + 'debug' : levels.get(cfg.get('logging', 'debug'), logging.ERROR), + 'maxsize' : cfg.getint('logging', 'maxsize')*1024*1024, + 'backups' : cfg.getint('logging', 'backups') + } + # Config file is used only in "root mode", in user mode we overwrite it + if os.getuid() != 0: + config['log'] = os.getenv('HOME', '/tmp') + "/udsactor.log" +except Exception, e: + sys.stderr.write("Error reading configuration file: " + str(e)) + sys.exit(2) + diff --git a/linuxActor/src/uds/actor/daemon.py b/linuxActor/src/uds/actor/daemon.py new file mode 100644 index 000000000..0427fc317 --- /dev/null +++ b/linuxActor/src/uds/actor/daemon.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +''' +@author: : http://www.jejik.com/authors/sander_marechal/ +@see: : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ +''' +import sys, os, time, atexit +from signal import SIGTERM + +class Daemon: + """ + A generic daemon class. + + Usage: subclass the Daemon class and override the run() method + """ + def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + self.pidfile = pidfile + + def daemonize(self): + """ + do the UNIX double-fork magic, see Stevens' "Advanced + Programming in the UNIX Environment" for details (ISBN 0201563177) + http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 + """ + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = file(self.stdin, 'r') + so = file(self.stdout, 'a+') + se = file(self.stderr, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # write pidfile + atexit.register(self.delpid) + pid = str(os.getpid()) + file(self.pidfile,'w+').write("%s\n" % pid) + + def delpid(self): + os.remove(self.pidfile) + + def start(self): + """ + Start the daemon + """ + # Check for a pidfile to see if the daemon already runs + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if pid: + message = "pidfile %s already exist. Daemon already running?\n" + sys.stderr.write(message % self.pidfile) + sys.exit(1) + + # Start the daemon + self.daemonize() + self.run() + + def stop(self): + """ + Stop the daemon + """ + # Get the pid from the pidfile + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if not pid: + message = "pidfile %s does not exist. Daemon not running?\n" + sys.stderr.write(message % self.pidfile) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, SIGTERM) + time.sleep(1) + except OSError, err: + err = str(err) + if err.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print str(err) + sys.exit(1) + + def restart(self): + """ + Restart the daemon + """ + self.stop() + self.start() + + def run(self): + """ + You should override this method when you subclass Daemon. It will be called after the process has been + daemonized by start() or restart(). + """ diff --git a/linuxActor/src/uds/actor/net.py b/linuxActor/src/uds/actor/net.py new file mode 100644 index 000000000..d471d43f3 --- /dev/null +++ b/linuxActor/src/uds/actor/net.py @@ -0,0 +1,62 @@ +''' +@author: Ben Mackey (getHwAddr) and paul cannon (getIpAddr) +@see: http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/ +''' +import fcntl, socket, struct, array, platform + +def getMacAddr(ifname): + if isinstance(ifname, list): + return dict([ (name, getMacAddr(name)) for name in ifname ]) + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15])) + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + except Exception: + return None + +def getIpAddr(ifname): + if isinstance(ifname, list): + return dict([ (name, getIpAddr(name)) for name in ifname ]) + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + return socket.inet_ntoa(fcntl.ioctl( + s.fileno(), + 0x8915, # SIOCGIFADDR + struct.pack('256s', ifname[:15]) + )[20:24]) + except Exception: + return None + +def getInterfaces(): + max_possible = 128 # arbitrary. raise if needed. + space = max_possible * 16 + if platform.architecture()[0] == '32bit': + offset, length = 32, 32 + elif platform.architecture()[0] == '64bit': + offset, length = 16, 40 + else: + raise OSError('Unknown arquitecture {0}'.format( platform.architecture()[0])) + + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + names = array.array('B', '\0' * space) + outbytes = struct.unpack('iL', fcntl.ioctl( + s.fileno(), + 0x8912, # SIOCGIFCONF + struct.pack('iL', space, names.buffer_info()[0]) + ))[0] + namestr = names.tostring() + return [namestr[i:i+offset].split('\0', 1)[0] for i in range(0, outbytes, length)] + +def getIpAndMac(ifname): + ip, mac = getIpAddr(ifname), getMacAddr(ifname) + if isinstance(ifname, list): + return dict( [ (key, { 'ip': ip[key], 'mac': mac[key] }) for key in ip.keys() ] ) + return (ip, mac) + +def getExternalIpAndMacs(): + res = getIpAndMac(getInterfaces()) + for key in res.keys(): + if res[key]['mac'] == '00:00:00:00:00:00': + del res[key] + return res + diff --git a/linuxActor/src/uds/actor/renamer/__init__.py b/linuxActor/src/uds/actor/renamer/__init__.py new file mode 100644 index 000000000..779188799 --- /dev/null +++ b/linuxActor/src/uds/actor/renamer/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +''' +Created on Nov 17, 2011 + +@author: dkmaster +''' +import platform, logging, os, sys, pkgutil + +logger = logging.getLogger(__name__) + +renamers = {} + +def rename(newName): + distribution = platform.dist()[0].lower() + if renamers.has_key(distribution): + return renamers[distribution](newName) + + logger.error('Renamer for platform "{0}" not found'.format(distribution)) + return False + +pkgpath = os.path.dirname(sys.modules[__name__].__file__) +for _, name, _ in pkgutil.iter_modules([pkgpath]): + __import__(name, globals(), locals(), [], -1) diff --git a/linuxActor/src/uds/actor/renamer/debian.py b/linuxActor/src/uds/actor/renamer/debian.py new file mode 100644 index 000000000..0c46ec46b --- /dev/null +++ b/linuxActor/src/uds/actor/renamer/debian.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +''' +Created on Nov 17, 2011 + +@author: dkmaster +''' + +from . import renamers +import logging, os + +logger = logging.getLogger(__name__) + +def rename(newName): + logger.debug('Debian renamer') + f = open('/etc/hostname', 'w') + f.write(newName) + f.close() + os.system('/bin/hostname %s' % newName) + + # add name to "hosts" + f = open('/etc/hosts', 'r') + lines = f.readlines() + f.close() + f = open('/etc/hosts', 'w') + f.write("127.0.1.1\t%s\n" % newName) + for l in lines: + if l[:9] == '127.0.1.1': + continue + f.write(l) + f.close() + + return True +# All names in lower case +renamers['debian'] = rename +renamers['ubuntu'] = rename \ No newline at end of file diff --git a/linuxActor/src/uds/actor/rpc.py b/linuxActor/src/uds/actor/rpc.py new file mode 100644 index 000000000..b6d435dc8 --- /dev/null +++ b/linuxActor/src/uds/actor/rpc.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +''' +Created on Nov 17, 2011 + +@author: dkmaster +''' +import logging, xmlrpclib, socket +import net +from config import config + +logger = logging.getLogger(__name__) + +LOGIN_MSG = 'login' +LOGOUT_MSG = 'logout' +READY_MSG = 'ready' +INFO_MSG = 'info' +IP_MSG = 'ip' + +class Rpc(object): + + _manager = None + + def __init__(self, broker, ssl, timeout=10): + url = ( ssl and 'https' or 'http' ) + '://' + broker + '/xmlrpc' + logger.debug('Remote address: {0}'.format(url)) + self._server = xmlrpclib.ServerProxy(uri = url, verbose=False) + self._id = None + socket.setdefaulttimeout(timeout) + + @staticmethod + def initialize(): + Rpc._manager = Rpc(config['server'], config['ssl'], config['timeout']) + + def test(self): + try: + self._server.test() + logger.debug('Test successful') + return True + except Exception: + logger.error('Test unsuccessful') + return False + + def message(self, msg, data): + try: + if self._id is None: + self._id = ','.join( [ v['mac'] for v in net.getExternalIpAndMacs().values() ] ) + logger.debug('Sending message to broker: {0} -> {1}, {2}'.format(self._id, msg, data)) + return self._server.message(self._id, msg, data) + except Exception: + return None + return '' + + @staticmethod + def login(username): + if Rpc._manager is None: # Not managed + return + return Rpc._manager.message(LOGIN_MSG, username) + + @staticmethod + def logout(username): + if Rpc._manager is None: # Not managed + return + return Rpc._manager.message(LOGOUT_MSG, username) + + @staticmethod + def getInfo(): + if Rpc._manager is None: # Not managed + return + return Rpc._manager.message(INFO_MSG, '') + + @staticmethod + def setReady(): + if Rpc._manager is None: # Not managed + return + interfaces = ','.join( [ v['mac'] + '=' + v['ip'] for v in net.getExternalIpAndMacs().values() ] ) + return Rpc._manager.message(READY_MSG, interfaces) + + @staticmethod + def notifyIpChange(): + if Rpc._manager is None: # Not managed + return + interfaces = ','.join( [ v['mac'] + '=' + v['ip'] for v in net.getExternalIpAndMacs().values() ] ) + return Rpc._manager.message(IP_MSG, interfaces) + + @staticmethod + def resetId(): + logger.debug('Reseting rpc id') + Rpc._manager._id = None + diff --git a/linuxActor/src/udsactor.cfg b/linuxActor/src/udsactor.cfg new file mode 100644 index 000000000..4bb199007 --- /dev/null +++ b/linuxActor/src/udsactor.cfg @@ -0,0 +1,13 @@ +[broker] + +server=192.168.0.1 +ssl=False +timeout=5 + +[logging] + +debug=DEBUG +log=/var/log/udsactor.log +# Size in megas +maxsize=20 +backups=3 \ No newline at end of file diff --git a/linuxActorNX/.project b/linuxActorNX/.project new file mode 100644 index 000000000..ff272a3c9 --- /dev/null +++ b/linuxActorNX/.project @@ -0,0 +1,11 @@ + + + linuxActorNX + + + + + + + + diff --git a/linuxActorNX/.settings/org.eclipse.mylyn.tasks.ui.prefs b/linuxActorNX/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 000000000..2a1042326 --- /dev/null +++ b/linuxActorNX/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Tue Jan 03 02:54:49 CET 2012 +eclipse.preferences.version=1 +project.repository.kind=mantis +project.repository.url=http\://192.168.0.6/mantis diff --git a/linuxActorNX/src/Makefile b/linuxActorNX/src/Makefile new file mode 100644 index 000000000..83d97a453 --- /dev/null +++ b/linuxActorNX/src/Makefile @@ -0,0 +1,13 @@ +BINDIR = $(DESTDIR)/usr/bin + +clean: + echo "Clean" +install: + mkdir -p $(BINDIR) + cp udsnxstart.sh $(BINDIR)/udsnxstart + cp udsnxstop.sh $(BINDIR)/udsnxstop + + chmod 0755 $(BINDIR)/udsnxstart + chmod 0755 $(BINDIR)/udsnxstop +uninstall: + echo "Uninstall" diff --git a/linuxActorNX/src/debian/changelog b/linuxActorNX/src/debian/changelog new file mode 100644 index 000000000..7e6398e83 --- /dev/null +++ b/linuxActorNX/src/debian/changelog @@ -0,0 +1,3 @@ +udsactor-nx (1.0) stable; urgency=low + * Initial version + -- Adolfo Gómez García Fri, 21 Nov 2011 23:35:11 +0100 \ No newline at end of file diff --git a/linuxActorNX/src/debian/compat b/linuxActorNX/src/debian/compat new file mode 100644 index 000000000..c7930257d --- /dev/null +++ b/linuxActorNX/src/debian/compat @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/linuxActorNX/src/debian/control b/linuxActorNX/src/debian/control new file mode 100644 index 000000000..c34be58a8 --- /dev/null +++ b/linuxActorNX/src/debian/control @@ -0,0 +1,14 @@ +Source: udsactor-nx +Section: contrib/net +Priority: extra +Maintainer: Adolfo Gómez García +Build-Depends: debhelper (>= 7) +Standards-Version: 3.9.2 +Homepage: http://www.virtualcable.es + +Package: udsactor-nx +Architecture: all +Depends: nxnode (>= 3.5.0), udsactor (>= 1.0), ${misc:Depends} +Description: UDS Actor component for nx + This package provides connection between uds actor and nx + diff --git a/linuxActorNX/src/debian/copyright b/linuxActorNX/src/debian/copyright new file mode 100644 index 000000000..c47d0a70b --- /dev/null +++ b/linuxActorNX/src/debian/copyright @@ -0,0 +1,26 @@ +Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 +Name: udsactor +Maintainer: Adolfo Gómez García +Source: http://www.virtualcable.es/ + +Copyright: 2010 Virtual Cable S.L. +License: GPL-2+ + +License: GPL-2+ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +. +On Debian systems, the full text of the GNU General Public +License version 2 can be found in the file +`/usr/share/common-licenses/GPL-2'. \ No newline at end of file diff --git a/linuxActorNX/src/debian/dirs b/linuxActorNX/src/debian/dirs new file mode 100644 index 000000000..e69de29bb diff --git a/linuxActorNX/src/debian/docs b/linuxActorNX/src/debian/docs new file mode 100644 index 000000000..b2b2a781e --- /dev/null +++ b/linuxActorNX/src/debian/docs @@ -0,0 +1 @@ +readme.txt diff --git a/linuxActorNX/src/debian/files b/linuxActorNX/src/debian/files new file mode 100644 index 000000000..2b2f737e8 --- /dev/null +++ b/linuxActorNX/src/debian/files @@ -0,0 +1 @@ +udsactor-nx_1.0_all.deb contrib/net extra diff --git a/linuxActorNX/src/debian/postinst b/linuxActorNX/src/debian/postinst new file mode 100644 index 000000000..9d629ce6c --- /dev/null +++ b/linuxActorNX/src/debian/postinst @@ -0,0 +1,37 @@ +#!/bin/sh + +NXNODECFG=/usr/NX/etc/node.cfg + +. /usr/share/debconf/confmodule + +set -e + +case "$1" in + configure) + TMPFILE=$(mktemp /tmp/node.cfg.XXXXX) + trap "rm -f $TMPFILE" 0 + cat $NXNODECFG | sed -e "s/.*udsnxst.*//; s/\(UserScriptAfterSessionStart *=.*\)/#\1/;s/\(UserScriptAfterSessionClose *=.*\)/#\1/" > $TMPFILE + echo >> $TMPFILE + echo "# Added by udsactor-nx (udsnxstart and udsnxstop)" >> $TMPFILE + echo UserScriptAfterSessionStart = \"/usr/bin/udsnxstart\" >> $TMPFILE + echo UserScriptAfterSessionClose = \"/usr/bin/udsnxstop\" >> $TMPFILE + cp $TMPFILE $NXNODECFG + invoke-rc.d nxserver restart + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +# Don't know why, but descriptors get "weird" when launched daemon, so we tell here to debconf to stop. +# Solved not starting the service right now, defered to next reboot + + +exit 0 \ No newline at end of file diff --git a/linuxActorNX/src/debian/postrm b/linuxActorNX/src/debian/postrm new file mode 100644 index 000000000..d8d174246 --- /dev/null +++ b/linuxActorNX/src/debian/postrm @@ -0,0 +1,28 @@ +#!/bin/sh + +NXNODECFG=/usr/NX/etc/node.cfg + +. /usr/share/debconf/confmodule + +set -e + +case "$1" in + purge) + ;; + remove) + TMPFILE=$(mktemp /tmp/node.cfg.XXXXX) + trap "rm -f $TMPFILE" 0 + cat $NXNODECFG | sed -e "s/.*udsnxst.*//" > $TMPFILE + cp $TMPFILE $NXNODECFG + invoke-rc.d nxserver restart + ;; + upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# diff --git a/linuxActorNX/src/debian/rules b/linuxActorNX/src/debian/rules new file mode 100755 index 000000000..80c231304 --- /dev/null +++ b/linuxActorNX/src/debian/rules @@ -0,0 +1,41 @@ +#!/usr/bin/make -f +# -*- makefile -*- +configure: configure-stamp +configure-stamp: + dh_testdir + touch configure-stamp +build: build-arch build-indep +build-arch: build-stamp +build-indep: build-stamp +build-stamp: configure-stamp + dh_testdir + $(MAKE) + touch $@ +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + dh_clean +install: build + dh_testdir + dh_testroot + dh_prep + dh_installdirs + $(MAKE) DESTDIR=$(CURDIR)/debian/udsactor-nx install +binary-arch: build install + # emptyness +binary-indep: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_compress + dh_link + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb +binary: binary-indep +.PHONY: build clean binary-indep binary install configure diff --git a/linuxActorNX/src/readme.txt b/linuxActorNX/src/readme.txt new file mode 100644 index 000000000..6a894a2a7 --- /dev/null +++ b/linuxActorNX/src/readme.txt @@ -0,0 +1 @@ +This package provides the interaction between nx and uds diff --git a/linuxActorNX/src/udsnxstart.sh b/linuxActorNX/src/udsnxstart.sh new file mode 100644 index 000000000..d54d7eeab --- /dev/null +++ b/linuxActorNX/src/udsnxstart.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/bin/udsactor login $2 & \ No newline at end of file diff --git a/linuxActorNX/src/udsnxstop.sh b/linuxActorNX/src/udsnxstop.sh new file mode 100644 index 000000000..73846f8af --- /dev/null +++ b/linuxActorNX/src/udsnxstop.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/bin/udsactor logout $2 & \ No newline at end of file diff --git a/nxtransport/.classpath b/nxtransport/.classpath new file mode 100644 index 000000000..fb5011632 --- /dev/null +++ b/nxtransport/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/nxtransport/.project b/nxtransport/.project new file mode 100644 index 000000000..097b8833b --- /dev/null +++ b/nxtransport/.project @@ -0,0 +1,17 @@ + + + nxtransport + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/nxtransport/.settings/org.eclipse.mylyn.tasks.ui.prefs b/nxtransport/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 000000000..1113c3499 --- /dev/null +++ b/nxtransport/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Tue Jan 03 02:54:43 CET 2012 +eclipse.preferences.version=1 +project.repository.kind=mantis +project.repository.url=http\://192.168.0.6/mantis diff --git a/nxtransport/description.jardesc b/nxtransport/description.jardesc new file mode 100644 index 000000000..77028d3b4 --- /dev/null +++ b/nxtransport/description.jardesc @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/nxtransport/jar/nxtransport.jar b/nxtransport/jar/nxtransport.jar new file mode 100644 index 0000000000000000000000000000000000000000..c859f30cdcaefc2451c74939737fae6e389423d0 GIT binary patch literal 24418 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 literal 0 HcmV?d00001 diff --git a/nxtransport/src/NxTransportApplet.java b/nxtransport/src/NxTransportApplet.java new file mode 100644 index 000000000..4817571c7 --- /dev/null +++ b/nxtransport/src/NxTransportApplet.java @@ -0,0 +1,135 @@ + +import java.applet.*; +import java.awt.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Hashtable; + +import es.virtualcable.nx.LinuxApplet; +import es.virtualcable.nx.MacApplet; +import es.virtualcable.nx.OsApplet; +import es.virtualcable.nx.WindowsApplet; + +public class NxTransportApplet extends Applet { + /** + * + */ + private static final long serialVersionUID = 6553108857320035827L; + /** + * + */ + //private int width; + //private int height; + private OsApplet applet; + + public void init() { + //width = getSize().width; + //height = getSize().height; + setBackground(Color.lightGray); + + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + String os = System.getProperty("os.name"); + if( os.startsWith("Windows") ) + applet = new WindowsApplet(); + else if( os.startsWith("Linux")) + applet = new LinuxApplet(); + else if (os.startsWith("Mac")) + applet = new MacApplet(); + else + throw new Exception("Invalid os!!!"); + + Hashtable params = parseParams(simpleUnscrambler(getParameter("data"))); + String baseUrl = getCodeBase().toString(); + + Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize(); + applet.setParameters(params, baseUrl, scrSize.width, scrSize.height); + applet.init(); + + return null; // nothing to return + } catch (Exception e) { + System.out.println("Exception:" + e.getMessage()); + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void start() { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + applet.start(); + return null; // nothing to return + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void destroy() { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + applet.destroy(); + return null; // nothing to return + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void paint(Graphics g) { + g.setColor(Color.black); + g.drawString("UDS NX connector", 8, 16); + } + + private Hashtable parseParams(String params) + { + Hashtable res = new Hashtable(); + String[] parms = params.split("\t"); + for( int i = 0; i < parms.length; i++) { + String[] val = parms[i].split(":"); + if( val.length == 1 ) + res.put(val[0], ""); + else + res.put(val[0], val[1]); + } + return res; + } + + private String simpleUnscrambler(String in) + { + int len = in.length(); + StringBuilder res = new StringBuilder(len/2); + int val = (int)'M'; + int pos = 0; + for( int i = 0; i < len; i += 2, pos++) { + String b = in.substring(i, i+2); + int c = Integer.parseInt(b, 16)^val; + val = (val ^ pos)&0xFF; + res.append((char)c); + } + return res.toString(); + } + +} diff --git a/nxtransport/src/es/virtualcable/nx/LinuxApplet.java b/nxtransport/src/es/virtualcable/nx/LinuxApplet.java new file mode 100644 index 000000000..d85e504b7 --- /dev/null +++ b/nxtransport/src/es/virtualcable/nx/LinuxApplet.java @@ -0,0 +1,104 @@ +package es.virtualcable.nx; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.UUID; + +public class LinuxApplet implements OsApplet { + + private final String[] paths = { "/usr/NX/bin/", "/usr/local/bin/", "/usr/bin/" }; + private final String nxclient = "nxclient"; + + private Hashtable params; + private String tmpDir = ""; + // private String baseUrl = ""; + private String nxFileName = ""; + private String scrWidth; + private String scrHeight; + + public void start() { + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + nxFileName = tmpDir + UUID.randomUUID().toString() + ".nxs"; + + String width = params.get("width"); + String height = params.get("height"); + boolean fullScreen = false; + + if( width.equals("-1")) + { + width = scrWidth; + height = scrHeight; + fullScreen = true; + } + + NxFile nx = new NxFile(fullScreen, width, height); + nx.host = params.get("ip"); + nx.username = params.get("user"); + nx.password = params.get("pass"); + nx.cachedisk = params.get("cacheDisk"); + nx.cachemem = params.get("cacheMem"); + nx.port = params.get("port"); + nx.desktop = params.get("session"); + nx.linkSpeed = params.get("connection"); + + try { + nx.saveFile( nxFileName ); + } catch (IOException e) { + javax.swing.JOptionPane.showMessageDialog(null, "Can't save nx temporal file: " + e.getMessage()); + e.printStackTrace(); + return; + } + + String execPath = ""; + + for(int i = 0; i < paths.length; i++ ) + { + File f = new File(paths[i] + nxclient); + if( f.exists() ) + { + execPath = paths[i] + nxclient; + break; + } + } + + if( execPath.length() == 0 ) + { + javax.swing.JOptionPane.showMessageDialog(null, "Can't find nxclient client.\nShould be at /usr/NX/bin, /usr/bin or /usr/local/bin\nPlease, install it"); + System.err.println("Can't find nxclient."); + return; + } + + ArrayList exec = new ArrayList(); + exec.add(execPath); + exec.add("--session"); + exec.add(nxFileName); + + try { + ProcessBuilder pb = new ProcessBuilder(exec); + pb.start(); + } catch(Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + } + + public void init() { + } + + public void destroy() { + // TODO Auto-generated method stub + + } + + public void setParameters(Hashtable parameters, String urlBase, + int screenWidth, int screenHeight) { + params = parameters; + // baseUrl = urlBase; + scrWidth = Integer.toString(screenWidth); + scrHeight = Integer.toString(screenHeight); + } + +} diff --git a/nxtransport/src/es/virtualcable/nx/MacApplet.java b/nxtransport/src/es/virtualcable/nx/MacApplet.java new file mode 100644 index 000000000..67891f36e --- /dev/null +++ b/nxtransport/src/es/virtualcable/nx/MacApplet.java @@ -0,0 +1,104 @@ +package es.virtualcable.nx; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.UUID; + +public class MacApplet implements OsApplet { + + private final String[] paths = { "/Applications/NX Client for OSX.app/Contents/MacOS/" }; + private final String nxclient = "nxclient"; + + private Hashtable params; + private String tmpDir = ""; + // private String baseUrl = ""; + private String nxFileName = ""; + private String scrWidth; + private String scrHeight; + + public void start() { + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + nxFileName = tmpDir + UUID.randomUUID().toString() + ".nxs"; + + String width = params.get("width"); + String height = params.get("height"); + boolean fullScreen = false; + + if( width.equals("-1")) + { + width = scrWidth; + height = scrHeight; + fullScreen = true; + } + + NxFile nx = new NxFile(fullScreen, width, height); + nx.host = params.get("ip"); + nx.username = params.get("user"); + nx.password = params.get("pass"); + nx.cachedisk = params.get("cacheDisk"); + nx.cachemem = params.get("cacheMem"); + nx.port = params.get("port"); + nx.desktop = params.get("session"); + nx.linkSpeed = params.get("connection"); + + try { + nx.saveFile( nxFileName ); + } catch (IOException e) { + javax.swing.JOptionPane.showMessageDialog(null, "Can't save nx temporal file: " + e.getMessage()); + e.printStackTrace(); + return; + } + + String execPath = ""; + + for(int i = 0; i < paths.length; i++ ) + { + File f = new File(paths[i] + nxclient); + if( f.exists() ) + { + execPath = paths[i] + nxclient; + break; + } + } + + if( execPath.length() == 0 ) + { + javax.swing.JOptionPane.showMessageDialog(null, "Can't find nxclient client.\nShould be at /Applications/NX Client for OSX.app/Contents/MacOS/\nPlease, install it"); + System.err.println("Can't find nxclient."); + return; + } + + ArrayList exec = new ArrayList(); + exec.add(execPath); + exec.add("--session"); + exec.add(nxFileName); + + try { + ProcessBuilder pb = new ProcessBuilder(exec); + pb.start(); + } catch(Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + } + + public void init() { + } + + public void destroy() { + // TODO Auto-generated method stub + + } + + public void setParameters(Hashtable parameters, String urlBase, + int screenWidth, int screenHeight) { + params = parameters; + // baseUrl = urlBase; + scrWidth = Integer.toString(screenWidth); + scrHeight = Integer.toString(screenHeight); + } + +} diff --git a/nxtransport/src/es/virtualcable/nx/NXPassword.java b/nxtransport/src/es/virtualcable/nx/NXPassword.java new file mode 100644 index 000000000..da853ddd6 --- /dev/null +++ b/nxtransport/src/es/virtualcable/nx/NXPassword.java @@ -0,0 +1,122 @@ +package es.virtualcable.nx; + +import java.util.HashMap; + +public class NXPassword { + // Encoding method extracted from nomachine web site: + // http://www.nomachine.com/ar/view.php?ar_id=AR01C00125 + + private static final int numValidCharList = 85; + private static final String dummyString = "{{{{"; + private static final char[] validCharList = { + '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', + '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', ':', ';', '<', '>', '?', '@', 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', ']', '_', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '{', '|', '}' + }; + + private static StringBuilder encodePassword(String p) + { + StringBuilder sPass = new StringBuilder(":"); + + if (p.length() == 0) + return new StringBuilder(""); + + for (int i = 0; i < p.length(); i++) + { + char c = (char)p.charAt(i); + sPass.append(c+i+1).append(":"); + } + + return sPass; + } + + private static int findCharInList(char c) + { + int i = -1; + + for (int j = 0; j < numValidCharList; j++) + { + if (validCharList[j] == c) + { + i = j; + return i; + } + } + + return i; + } + + private static char getRandomValidCharFromList() + { + // int k = (int)(java.lang.System.currentTimeMillis() % 60); + int k = 0; + return validCharList[k]; + } + + public static String scrambleString(String s) + { + StringBuilder pass = new StringBuilder(); + + if (s == null || s.length() == 0) + return s; + + StringBuilder str = encodePassword(s); + + if (str.length() < 32) + str.append(dummyString); + + pass.append(str.reverse()); + + if (pass.length() < 32) + pass.append(dummyString); + + int k = getRandomValidCharFromList(); + int l = k + pass.length() - 2; + + pass.insert(0, (char)k); + + for (int i1 = 1; i1 < (int)pass.length(); i1++) + { + int j = findCharInList(pass.charAt(i1)); + + if (j == -1) + return s; + + int i = (j + l * (i1 + 1)) % numValidCharList; + + pass.setCharAt(i1,validCharList[i]); + } + + char c = (char)(getRandomValidCharFromList() + 2); + pass.append(c); + + // Convert entities + HashMap replacements = new HashMap(); + replacements.put('&', "&"); + replacements.put('<', "<"); + replacements.put('"', """); + replacements.put('\'', "'"); + // And convert $ to \$ + replacements.put('$', "\\$"); + + StringBuilder result = new StringBuilder(); + for( int i = 0; i < pass.length(); i++ ) + { + c = pass.charAt(i); + if( replacements.containsKey(c) ) + result.append(replacements.get(c)); + else + result.append(c); + } + + return result.toString(); + } + + +} diff --git a/nxtransport/src/es/virtualcable/nx/NxFile.java b/nxtransport/src/es/virtualcable/nx/NxFile.java new file mode 100644 index 000000000..e40cddc26 --- /dev/null +++ b/nxtransport/src/es/virtualcable/nx/NxFile.java @@ -0,0 +1,192 @@ +package es.virtualcable.nx; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +public class NxFile { + private static final String EMPTY_PASSWORD = "EMPTY_PASSWORD"; + + public boolean fullScreen; + public String width; + public String height; + public String cachemem = "4"; + public String cachedisk = "32"; + public String keyboardLayout = ""; + public String linkSpeed = "wan"; + public String host = ""; + public String port = ""; + public String username = ""; + public String password = ""; + public String desktop = "gnome"; + + public NxFile(boolean fullScreen, String width, String height) { + this.fullScreen = fullScreen; + this.width = width; + this.height = height; + } + + public void saveFile(String fname) throws IOException { + String rememberPass = "true"; + String pass = NXPassword.scrambleString(password); + if( pass == "" ) + { + rememberPass = "false"; + pass = EMPTY_PASSWORD; + } + String resolution = width + "x" + height; + if( fullScreen ) + resolution = "fullscreen"; + + String save = NXTemplate.replaceAll("\\{CACHEMEM\\}", cachemem); + save = save.replaceAll("\\{CACHEDISK\\}", cachedisk); + save = save.replaceAll("\\{KEYLAYOUT\\}", keyboardLayout); + save = save.replaceAll("\\{LINKSPEED\\}", linkSpeed); + save = save.replaceAll("\\{REMEMBERPASS\\}", rememberPass); + save = save.replaceAll("\\{RESOLUTION\\}", resolution); + save = save.replaceAll("\\{WIDTH\\}", width); + save = save.replaceAll("\\{HEIGHT\\}", height); + save = save.replaceAll("\\{HOST\\}", host); + save = save.replaceAll("\\{PORT\\}", port); + save = save.replaceAll("\\{DESKTOP\\}", desktop); + save = save.replaceAll("\\{USERNAME\\}", username); + save = save.replaceAll("\\{PASSWORD\\}", pass); + + FileWriter fstream = new FileWriter(fname); + PrintWriter out = new PrintWriter(fstream); + out.write(save); + out.close(); + } + + public static void main(String [] args) + { + NxFile nx = new NxFile(true, "1024", "768"); + try { + nx.saveFile(""); + } catch( Exception e ) + { + System.out.println(e); + } + } + + // NX Template used to generate file + private static final String NXTemplate = + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + +} diff --git a/nxtransport/src/es/virtualcable/nx/OsApplet.java b/nxtransport/src/es/virtualcable/nx/OsApplet.java new file mode 100644 index 000000000..819378392 --- /dev/null +++ b/nxtransport/src/es/virtualcable/nx/OsApplet.java @@ -0,0 +1,15 @@ +package es.virtualcable.nx; + +import java.util.Hashtable; + +public interface OsApplet { + + void setParameters(Hashtable parameters, String urlBase, int screenWidth, int screenHeight); + + void init(); + + void start(); + + void destroy(); + +} diff --git a/nxtransport/src/es/virtualcable/nx/WindowsApplet.java b/nxtransport/src/es/virtualcable/nx/WindowsApplet.java new file mode 100644 index 000000000..2588cea27 --- /dev/null +++ b/nxtransport/src/es/virtualcable/nx/WindowsApplet.java @@ -0,0 +1,86 @@ +package es.virtualcable.nx; + +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; +import java.util.UUID; + +import es.virtualcable.windows.WinRegistry; + +public class WindowsApplet implements OsApplet { + + private Hashtable params; + private String tmpDir = ""; + // private String baseUrl = ""; + private String nxFileName = ""; + private String scrWidth; + private String scrHeight; + + public void setParameters(Hashtable parameters, String urlBase, + int screenWidth, int screenHeight) { + params = parameters; + // baseUrl = urlBase; + scrWidth = Integer.toString(screenWidth); + scrHeight = Integer.toString(screenHeight); + } + + public void start() { + try { + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + System.out.println(tmpDir); + nxFileName = tmpDir + UUID.randomUUID().toString() + ".nxs"; + + String width = params.get("width"); + String height = params.get("height"); + boolean fullScreen = false; + + if( width.equals("-1")) + { + width = scrWidth; + height = scrHeight; + fullScreen = true; + } + + NxFile nx = new NxFile(fullScreen, width, height); + nx.host = params.get("ip"); + nx.username = params.get("user"); + nx.password = params.get("pass"); + nx.cachedisk = params.get("cacheDisk"); + nx.cachemem = params.get("cacheMem"); + nx.port = params.get("port"); + nx.desktop = params.get("session"); + nx.linkSpeed = params.get("connection"); + + try { + nx.saveFile( nxFileName ); + } catch (IOException e) { + javax.swing.JOptionPane.showMessageDialog(null, "Can't save nx temporal file: " + e.getMessage()); + e.printStackTrace(); + return; + } + + String cmd = WinRegistry.readString(WinRegistry.HKEY_CURRENT_USER, "Software\\Classes\\NXClient.session\\shell\\open\\command", ""); + if ( null == cmd ) + javax.swing.JOptionPane.showMessageDialog(null, "Can't find Nomachine client. Please, install it"); + else + { + nxFileName = nxFileName.replace("\\", "\\\\"); + cmd = cmd.replaceAll("%1", nxFileName); + System.out.println(cmd); + // Process p = + Runtime.getRuntime().exec( cmd ); + } + } catch (Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + } + + public void init() { + } + + public void destroy() { + } + +} diff --git a/nxtransport/src/es/virtualcable/windows/WinRegistry.java b/nxtransport/src/es/virtualcable/windows/WinRegistry.java new file mode 100644 index 000000000..cd9ff892a --- /dev/null +++ b/nxtransport/src/es/virtualcable/windows/WinRegistry.java @@ -0,0 +1,392 @@ +package es.virtualcable.windows; + +/* Code borrowed from http://stackoverflow.com/questions/62289/read-write-to-windows-registry-using-java. */ +/* Original author don't found, if someone found him, please note it here */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; + +public class WinRegistry { + public static final int HKEY_CURRENT_USER = 0x80000001; + public static final int HKEY_LOCAL_MACHINE = 0x80000002; + public static final int REG_SUCCESS = 0; + public static final int REG_NOTFOUND = 2; + public static final int REG_ACCESSDENIED = 5; + + private static final int KEY_ALL_ACCESS = 0xf003f; + private static final int KEY_READ = 0x20019; + private static Preferences userRoot = Preferences.userRoot(); + private static Preferences systemRoot = Preferences.systemRoot(); + + private static Class userClass = userRoot.getClass(); + private static Method regOpenKey = null; + private static Method regCloseKey = null; + private static Method regQueryValueEx = null; + private static Method regEnumValue = null; + private static Method regQueryInfoKey = null; + private static Method regEnumKeyEx = null; + private static Method regCreateKeyEx = null; + private static Method regSetValueEx = null; + private static Method regDeleteKey = null; + private static Method regDeleteValue = null; + + static { + try { + regOpenKey = userClass.getDeclaredMethod("WindowsRegOpenKey", + new Class[] { int.class, byte[].class, int.class }); + regOpenKey.setAccessible(true); + regCloseKey = userClass.getDeclaredMethod("WindowsRegCloseKey", + new Class[] { int.class }); + regCloseKey.setAccessible(true); + regQueryValueEx = userClass.getDeclaredMethod("WindowsRegQueryValueEx", + new Class[] { int.class, byte[].class }); + regQueryValueEx.setAccessible(true); + regEnumValue = userClass.getDeclaredMethod("WindowsRegEnumValue", + new Class[] { int.class, int.class, int.class }); + regEnumValue.setAccessible(true); + regQueryInfoKey = userClass.getDeclaredMethod("WindowsRegQueryInfoKey1", + new Class[] { int.class }); + regQueryInfoKey.setAccessible(true); + regEnumKeyEx = userClass.getDeclaredMethod( + "WindowsRegEnumKeyEx", new Class[] { int.class, int.class, + int.class }); + regEnumKeyEx.setAccessible(true); + regCreateKeyEx = userClass.getDeclaredMethod( + "WindowsRegCreateKeyEx", new Class[] { int.class, + byte[].class }); + regCreateKeyEx.setAccessible(true); + regSetValueEx = userClass.getDeclaredMethod( + "WindowsRegSetValueEx", new Class[] { int.class, + byte[].class, byte[].class }); + regSetValueEx.setAccessible(true); + regDeleteValue = userClass.getDeclaredMethod( + "WindowsRegDeleteValue", new Class[] { int.class, + byte[].class }); + regDeleteValue.setAccessible(true); + regDeleteKey = userClass.getDeclaredMethod( + "WindowsRegDeleteKey", new Class[] { int.class, + byte[].class }); + regDeleteKey.setAccessible(true); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private WinRegistry() { } + + /** + * Read a value from key and value name + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @param valueName + * @return the value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static String readString(int hkey, String key, String valueName) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readString(systemRoot, hkey, key, valueName); + } + else if (hkey == HKEY_CURRENT_USER) { + return readString(userRoot, hkey, key, valueName); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Read value(s) and value name(s) form given key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @return the value name(s) plus the value(s) + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static Map readStringValues(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readStringValues(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + return readStringValues(userRoot, hkey, key); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Read the value name(s) from a given key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @return the value name(s) + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static List readStringSubKeys(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readStringSubKeys(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + return readStringSubKeys(userRoot, hkey, key); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Create a key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void createKey(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int [] ret; + if (hkey == HKEY_LOCAL_MACHINE) { + ret = createKey(systemRoot, hkey, key); + regCloseKey.invoke(systemRoot, new Object[] { new Integer(ret[0]) }); + } + else if (hkey == HKEY_CURRENT_USER) { + ret = createKey(userRoot, hkey, key); + regCloseKey.invoke(userRoot, new Object[] { new Integer(ret[0]) }); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + if (ret[1] != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + ret[1] + " key=" + key); + } + } + + /** + * Write a value in a given key/value name + * @param hkey + * @param key + * @param valueName + * @param value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void writeStringValue + (int hkey, String key, String valueName, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + writeStringValue(systemRoot, hkey, key, valueName, value); + } + else if (hkey == HKEY_CURRENT_USER) { + writeStringValue(userRoot, hkey, key, valueName, value); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Delete a given key + * @param hkey + * @param key + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void deleteKey(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc = -1; + if (hkey == HKEY_LOCAL_MACHINE) { + rc = deleteKey(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + rc = deleteKey(userRoot, hkey, key); + } + if (rc != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + rc + " key=" + key); + } + } + + /** + * delete a value from a given key/value name + * @param hkey + * @param key + * @param value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void deleteValue(int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc = -1; + if (hkey == HKEY_LOCAL_MACHINE) { + rc = deleteValue(systemRoot, hkey, key, value); + } + else if (hkey == HKEY_CURRENT_USER) { + rc = deleteValue(userRoot, hkey, key, value); + } + if (rc != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + rc + " key=" + key + " value=" + value); + } + } + + // ===================== + + private static int deleteValue + (Preferences root, int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS) }); + if (handles[1] != REG_SUCCESS) { + return handles[1]; // can be REG_NOTFOUND, REG_ACCESSDENIED + } + int rc =((Integer) regDeleteValue.invoke(root, + new Object[] { + new Integer(handles[0]), toCstr(value) + })).intValue(); + regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) }); + return rc; + } + + private static int deleteKey(Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc =((Integer) regDeleteKey.invoke(root, + new Object[] { new Integer(hkey), toCstr(key) })).intValue(); + return rc; // can REG_NOTFOUND, REG_ACCESSDENIED, REG_SUCCESS + } + + private static String readString(Preferences root, int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_READ) }); + if (handles[1] != REG_SUCCESS) { + return null; + } + byte[] valb = (byte[]) regQueryValueEx.invoke(root, new Object[] { + new Integer(handles[0]), toCstr(value) }); + regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) }); + return (valb != null ? new String(valb).trim() : null); + } + + private static Map readStringValues + (Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + HashMap results = new HashMap(); + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_READ) }); + if (handles[1] != REG_SUCCESS) { + return null; + } + int[] info = (int[]) regQueryInfoKey.invoke(root, + new Object[] { new Integer(handles[0]) }); + + int count = info[2]; // count + int maxlen = info[3]; // value length max + for(int index=0; index readStringSubKeys + (Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + List results = new ArrayList(); + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_READ) + }); + if (handles[1] != REG_SUCCESS) { + return null; + } + int[] info = (int[]) regQueryInfoKey.invoke(root, + new Object[] { new Integer(handles[0]) }); + + int count = info[2]; // count + int maxlen = info[3]; // value length max + for(int index=0; index + + + + + diff --git a/nxtuntransport/.project b/nxtuntransport/.project new file mode 100644 index 000000000..2730eeacf --- /dev/null +++ b/nxtuntransport/.project @@ -0,0 +1,17 @@ + + + nxtuntransport + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/nxtuntransport/.settings/org.eclipse.mylyn.tasks.ui.prefs b/nxtuntransport/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 000000000..1113c3499 --- /dev/null +++ b/nxtuntransport/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Tue Jan 03 02:54:43 CET 2012 +eclipse.preferences.version=1 +project.repository.kind=mantis +project.repository.url=http\://192.168.0.6/mantis diff --git a/nxtuntransport/description.jardesc b/nxtuntransport/description.jardesc new file mode 100644 index 000000000..d9cad7439 --- /dev/null +++ b/nxtuntransport/description.jardesc @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/nxtuntransport/jar/nxtuntransport.jar b/nxtuntransport/jar/nxtuntransport.jar new file mode 100644 index 0000000000000000000000000000000000000000..6ec27d4f28f73af22126668cbd49d4a8c26b3aed GIT binary patch literal 28785 zcmbTdQ<$egw&h)E+pe^28~?Oz+qPY4Rob>~+qP|0@~iHiZ>Hxwea^+idlT33{9?yi z?_OJ83Ir7O2jt(c!fHt1$G8KRF+HW~+$`APc?iGYxaH( zB)A{8hP)y5%-tE35ctkhF#$SS{2(5$Sf3Xtse+KQ0x_!}5(u6>^V0*8(gZ6)PxjQe zU5}Nm!3>Se&6!u@=z3eARuL^kItNuE#46AFvNWBX|GUlgKy{Uru-HRvTqc`X{wwV% zuw21@_7Ov|-mszhC!ds~LJFtw2mCW=m1MZ5sM{aLmn>T+BOvB!0RB=(oG6mj)>Ke< zmo`HWR#~Y@=yP#-`%*d4I+_ah;zFK3>^_H4(gUF{1XLlZh1b*2U9zg-N`DM{bGsp- znq6v}tr^UljNEt1n;%OJrHG2Qr0UDaJ~xZ*7(4 zGPH{fySn4_kOY3MowERG_4D-2#qT~!02RZS-qO5ij4MSKTm{*n3iFRJ&A3)av3w=! ztBU*-UNZtrDgI`H!KZI$7J-qo)Jds7f_k8%clYoV&n2nQN7oZr#aCiskdNuQ2Ns=V zOQ>b5(QRCJXeAO7_H=6naQp8)h+Et)KTqb*txRbSw#?qPx$^(SUxkS8cKQWKI#F4F z9z0L#RS5^+BxY<-b?p0sB^a0OXNoihgqQW_Uh-JBn1E~#-pFM`SW`8yN z0m(~2Kz>MU5TF46_|XIUXqMaoUmdF zFm%4^QEw#0eIE6FQz~kII5ETSr2lD6?AUQ!Aw}ntNw#lE$9gCfv_**E4p>?*@D!$R zZ&&v!_3s;M2B6^QDa5`5Gav=MV$=a)QJ~+90UO#PqM6Y%yh<~&dyQnkUE0b3hMu18 zNk&M@@OQfwy;JwzPfX}ZwGN?ZC}Qn6oV=gp(V_W$EzdE`!(iuH`nP;lVh~^rixRGk z%Tw?1Y7?!D1j3o=d+h7>uragARv(K|3^c z8SxnzDO^!nVagTsXjiPMna2u?i~}pR(J%}2+*aw=*rG>b!wk;)q?+ILL#j?&@>cRz zbDN7AEK>zKovxhwf(FUJgIbj@DH+*JTOmkii&!54`#_-s9jbK+QRDLS3T3#Bz(5U@ z67Qnrx3(fL2bn`4OHgz-ooB35t|HMse#oO&c;BViCb}F4<~o1PKk$2f-qg{=zeJ2l z{U&4S&bQdVV|wvyN(P?iTUx58!Kb$2v-8KLLyK zAAnU<5@a?v>>d~{TRNV5&Pt8XvJgah&f!`1JPQ5_nOR$2sa4eUe#lfjT^RZGY(k3YJHX!;-{ zO*t+d0988xRtQTX0Ib~~Xz*K=s)k>|)P^JVQN&j4$n>y4q`6>aMtiR*r}ogL|`wq(>`Vq4>;uR>3GExm}x854de| z+3w`S&n8H|I5ljXOrCo>~66GOfz1@o*9`2Gu7#}Gt~-9e?zjgPCSK=?`D z)NUkhUhe9;{!9;iBK;~^icD(z)7(g(r|ymuogH}562cBvW`|`$cyY8|*i}}shc>O+#DfFhb>6-^8YQ=s7R0C*MOYWGN1{TibNDgny_g#lCzKUa4M;T7o5eXgWRF zZI|up>T4qJJ1mQ&3u42!%*s(drrLYHgHHJ_b(RY<=cRy5w8F-8j656F2}3#n5tBNbsvD2S7fu56_H*41lU3Z#GlS-Z!uv}Z8~gOtctGQDef}XpU%r{U741` zrLtMz8{9r=LMcFnQlUYA03{KY<|8k`niyChMqdNRXWuiDzyM$wAkm za}s?W!(2&yv|ky>Ae?2NpV)p?+?F>cDKd3tJ`G@d`H_wY`!kRYtSZT`?5%V)f+8r# zD&4-b_*yH^V>$}KfTSh4qv>4H9jHp@jy3UA%}Aej#t5q^Xcgc$az1maKVQfHJ*r1`Ve5BI)RO+ z;GpcPo{V?QR@aBtth`yX&+ÁT-PrJT=g5H#p5Ad34Lm4*i!_C;^HzVz$V;X1mb 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 ZrlJfK^sg%@ua5`FH*btyuf2KmzW_gtS_S|B literal 0 HcmV?d00001 diff --git a/nxtuntransport/src/NxTunTransportApplet.java b/nxtuntransport/src/NxTunTransportApplet.java new file mode 100644 index 000000000..a228ca37f --- /dev/null +++ b/nxtuntransport/src/NxTunTransportApplet.java @@ -0,0 +1,135 @@ + +import java.applet.*; +import java.awt.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Hashtable; + +import es.virtualcable.nx.LinuxApplet; +import es.virtualcable.nx.MacApplet; +import es.virtualcable.nx.OsApplet; +import es.virtualcable.nx.WindowsApplet; + +public class NxTunTransportApplet extends Applet { + /** + * + */ + private static final long serialVersionUID = 6553108857320035827L; + /** + * + */ + //private int width; + //private int height; + private OsApplet applet; + + public void init() { + //width = getSize().width; + //height = getSize().height; + setBackground(Color.lightGray); + + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + String os = System.getProperty("os.name"); + if( os.startsWith("Windows") ) + applet = new WindowsApplet(); + else if( os.startsWith("Linux")) + applet = new LinuxApplet(); + else if (os.startsWith("Mac")) + applet = new MacApplet(); + else + throw new Exception("Invalid os!!!"); + + Hashtable params = parseParams(simpleUnscrambler(getParameter("data"))); + String baseUrl = getCodeBase().toString(); + + Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize(); + applet.setParameters(params, baseUrl, scrSize.width, scrSize.height); + applet.init(); + + return null; // nothing to return + } catch (Exception e) { + System.out.println("Exception:" + e.getMessage()); + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void start() { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + applet.start(); + return null; // nothing to return + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void destroy() { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + applet.destroy(); + return null; // nothing to return + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void paint(Graphics g) { + g.setColor(Color.black); + g.drawString("UDS NX Tunel Connector", 8, 16); + } + + private Hashtable parseParams(String params) + { + Hashtable res = new Hashtable(); + String[] parms = params.split("\t"); + for( int i = 0; i < parms.length; i++) { + String[] val = parms[i].split(":"); + if( val.length == 1 ) + res.put(val[0], ""); + else + res.put(val[0], val[1]); + } + return res; + } + + private String simpleUnscrambler(String in) + { + int len = in.length(); + StringBuilder res = new StringBuilder(len/2); + int val = (int)'M'; + int pos = 0; + for( int i = 0; i < len; i += 2, pos++) { + String b = in.substring(i, i+2); + int c = Integer.parseInt(b, 16)^val; + val = (val ^ pos)&0xFF; + res.append((char)c); + } + return res.toString(); + } + +} diff --git a/nxtuntransport/src/es/virtualcable/nx/FreePortFinder.java b/nxtuntransport/src/es/virtualcable/nx/FreePortFinder.java new file mode 100644 index 000000000..d9ddc5fea --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/FreePortFinder.java @@ -0,0 +1,40 @@ +package es.virtualcable.nx; + +import java.io.IOException; +import java.net.*; +import java.util.Random; + + +public class FreePortFinder { + + private final static int START_PORT = 35000; + private final static int START_PORT_MAX = 44000; + private final static int END_PORT = 45500; + + public static boolean isFree(int port) + { + boolean free = true; + try { + InetAddress addr = InetAddress.getByName("localhost"); + SocketAddress sa = new InetSocketAddress(addr, port); + ServerSocket socket = new ServerSocket(); + socket.bind(sa); + socket.close(); + } catch (IOException e) { + free = false; + } + return free; + } + + public static int findFreePort() + { + Random rnd = new Random(); + int startPort = START_PORT + rnd.nextInt(START_PORT_MAX-START_PORT); + for( int port = startPort; port < END_PORT; port ++) + { + if( isFree(port) ) + return port; + } + return -1; + } +} diff --git a/nxtuntransport/src/es/virtualcable/nx/LinuxApplet.java b/nxtuntransport/src/es/virtualcable/nx/LinuxApplet.java new file mode 100644 index 000000000..894c96277 --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/LinuxApplet.java @@ -0,0 +1,130 @@ +package es.virtualcable.nx; + +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; + + +public class LinuxApplet implements OsApplet { + + private final String[] paths = { "/usr/NX/bin/", "/usr/local/bin/", "/usr/bin/" }; + private final String nxclient = "nxclient"; + + private Hashtable params; + private String tmpDir = ""; + private String baseUrl = ""; + private String nxFileName = ""; + private String jarFileName = ""; + private String scrWidth; + private String scrHeight; + private String tunPort = ""; + + public void start() { + try { + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + nxFileName = tmpDir + UUID.randomUUID().toString() + ".nxs"; + jarFileName = tmpDir + UUID.randomUUID().toString() + ".jar"; + + //System.out.println(nxFileName); + + String width = params.get("width"); + String height = params.get("height"); + boolean fullScreen = false; + + if( width.equals("-1")) + { + width = scrWidth; + height = scrHeight; + fullScreen = true; + } + + if( downloadJar() == false) + return; + tunPort = Integer.toString(FreePortFinder.findFreePort()); + + NxFile nx = new NxFile(fullScreen, width, height); + nx.host = "127.0.0.1"; + nx.port = tunPort; + nx.username = params.get("user"); + nx.password = params.get("pass"); + nx.cachedisk = params.get("cacheDisk"); + nx.cachemem = params.get("cacheMem"); + nx.desktop = params.get("session"); + nx.linkSpeed = params.get("connection"); + + try { + nx.saveFile( nxFileName ); + } catch (IOException e) { + javax.swing.JOptionPane.showMessageDialog(null, "Can't save nx temporal file: " + e.getMessage()); + e.printStackTrace(); + return; + } + + String execPath = ""; + + for(int i = 0; i < paths.length; i++ ) + { + File f = new File(paths[i] + nxclient); + if( f.exists() ) + { + execPath = paths[i] + nxclient; + break; + } + } + + if( execPath.length() == 0 ) + { + javax.swing.JOptionPane.showMessageDialog(null, "Can't find nxclient client.\nShould be at /usr/NX/bin, /usr/bin or /usr/local/bin\nPlease, install it"); + System.err.println("Can't find nxclient."); + return; + } + + executeTunnel(execPath); + + } catch (Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + } + + public void init() { + } + + public void destroy() { + } + + private void executeTunnel(String nxCmd) throws IOException + { + String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + String [] exec = { java, "-jar", jarFileName, tunPort, nxCmd, "--session", nxFileName }; + ProcessBuilder pb = new ProcessBuilder( exec ); + Map env = pb.environment(); + env.put("TPARAMS", params.get("tun")); + /*System.out.println("TPARAMS: " + params.get("tun")); + for (String str : exec) { + System.out.print(str + " "); + } + System.out.println();*/ + pb.start(); + + } + + public void setParameters(Hashtable parameters, String urlBase, + int screenWidth, int screenHeight) { + params = parameters; + baseUrl = urlBase; + scrWidth = Integer.toString(screenWidth); + scrHeight = Integer.toString(screenHeight); + + } + + private boolean downloadJar() + { + return util.download(baseUrl, "2", jarFileName); + } + + +} diff --git a/nxtuntransport/src/es/virtualcable/nx/MacApplet.java b/nxtuntransport/src/es/virtualcable/nx/MacApplet.java new file mode 100644 index 000000000..183e8cb4d --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/MacApplet.java @@ -0,0 +1,130 @@ +package es.virtualcable.nx; + +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; + + +public class MacApplet implements OsApplet { + + private final String[] paths = { "/Applications/NX Client for OSX.app/Contents/MacOS/" }; + private final String nxclient = "nxclient"; + + private Hashtable params; + private String tmpDir = ""; + private String baseUrl = ""; + private String nxFileName = ""; + private String jarFileName = ""; + private String scrWidth; + private String scrHeight; + private String tunPort = ""; + + public void start() { + try { + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + nxFileName = tmpDir + UUID.randomUUID().toString() + ".nxs"; + jarFileName = tmpDir + UUID.randomUUID().toString() + ".jar"; + + //System.out.println(nxFileName); + + String width = params.get("width"); + String height = params.get("height"); + boolean fullScreen = false; + + if( width.equals("-1")) + { + width = scrWidth; + height = scrHeight; + fullScreen = true; + } + + if( downloadJar() == false) + return; + tunPort = Integer.toString(FreePortFinder.findFreePort()); + + NxFile nx = new NxFile(fullScreen, width, height); + nx.host = "127.0.0.1"; + nx.port = tunPort; + nx.username = params.get("user"); + nx.password = params.get("pass"); + nx.cachedisk = params.get("cacheDisk"); + nx.cachemem = params.get("cacheMem"); + nx.desktop = params.get("session"); + nx.linkSpeed = params.get("connection"); + + try { + nx.saveFile( nxFileName ); + } catch (IOException e) { + javax.swing.JOptionPane.showMessageDialog(null, "Can't save nx temporal file: " + e.getMessage()); + e.printStackTrace(); + return; + } + + String execPath = ""; + + for(int i = 0; i < paths.length; i++ ) + { + File f = new File(paths[i] + nxclient); + if( f.exists() ) + { + execPath = paths[i] + nxclient; + break; + } + } + + if( execPath.length() == 0 ) + { + javax.swing.JOptionPane.showMessageDialog(null, "Can't find nxclient client.\nShould be at /Applications/NX Client for OSX.app/Contents/MacOS/\nPlease, install it"); + System.err.println("Can't find nxclient."); + return; + } + + executeTunnel(execPath); + + } catch (Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + } + + public void init() { + } + + public void destroy() { + } + + private void executeTunnel(String nxCmd) throws IOException + { + String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + String [] exec = { java, "-jar", jarFileName, tunPort, nxCmd, "--session", nxFileName }; + ProcessBuilder pb = new ProcessBuilder( exec ); + Map env = pb.environment(); + env.put("TPARAMS", params.get("tun")); + System.out.println("TPARAMS: " + params.get("tun")); + for (String str : exec) { + System.out.print(str + " "); + } + System.out.println(); + pb.start(); + + } + + public void setParameters(Hashtable parameters, String urlBase, + int screenWidth, int screenHeight) { + params = parameters; + baseUrl = urlBase; + scrWidth = Integer.toString(screenWidth); + scrHeight = Integer.toString(screenHeight); + + } + + private boolean downloadJar() + { + return util.download(baseUrl, "2", jarFileName); + } + + +} diff --git a/nxtuntransport/src/es/virtualcable/nx/NXPassword.java b/nxtuntransport/src/es/virtualcable/nx/NXPassword.java new file mode 100644 index 000000000..da853ddd6 --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/NXPassword.java @@ -0,0 +1,122 @@ +package es.virtualcable.nx; + +import java.util.HashMap; + +public class NXPassword { + // Encoding method extracted from nomachine web site: + // http://www.nomachine.com/ar/view.php?ar_id=AR01C00125 + + private static final int numValidCharList = 85; + private static final String dummyString = "{{{{"; + private static final char[] validCharList = { + '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', + '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', ':', ';', '<', '>', '?', '@', 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', ']', '_', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '{', '|', '}' + }; + + private static StringBuilder encodePassword(String p) + { + StringBuilder sPass = new StringBuilder(":"); + + if (p.length() == 0) + return new StringBuilder(""); + + for (int i = 0; i < p.length(); i++) + { + char c = (char)p.charAt(i); + sPass.append(c+i+1).append(":"); + } + + return sPass; + } + + private static int findCharInList(char c) + { + int i = -1; + + for (int j = 0; j < numValidCharList; j++) + { + if (validCharList[j] == c) + { + i = j; + return i; + } + } + + return i; + } + + private static char getRandomValidCharFromList() + { + // int k = (int)(java.lang.System.currentTimeMillis() % 60); + int k = 0; + return validCharList[k]; + } + + public static String scrambleString(String s) + { + StringBuilder pass = new StringBuilder(); + + if (s == null || s.length() == 0) + return s; + + StringBuilder str = encodePassword(s); + + if (str.length() < 32) + str.append(dummyString); + + pass.append(str.reverse()); + + if (pass.length() < 32) + pass.append(dummyString); + + int k = getRandomValidCharFromList(); + int l = k + pass.length() - 2; + + pass.insert(0, (char)k); + + for (int i1 = 1; i1 < (int)pass.length(); i1++) + { + int j = findCharInList(pass.charAt(i1)); + + if (j == -1) + return s; + + int i = (j + l * (i1 + 1)) % numValidCharList; + + pass.setCharAt(i1,validCharList[i]); + } + + char c = (char)(getRandomValidCharFromList() + 2); + pass.append(c); + + // Convert entities + HashMap replacements = new HashMap(); + replacements.put('&', "&"); + replacements.put('<', "<"); + replacements.put('"', """); + replacements.put('\'', "'"); + // And convert $ to \$ + replacements.put('$', "\\$"); + + StringBuilder result = new StringBuilder(); + for( int i = 0; i < pass.length(); i++ ) + { + c = pass.charAt(i); + if( replacements.containsKey(c) ) + result.append(replacements.get(c)); + else + result.append(c); + } + + return result.toString(); + } + + +} diff --git a/nxtuntransport/src/es/virtualcable/nx/NxFile.java b/nxtuntransport/src/es/virtualcable/nx/NxFile.java new file mode 100644 index 000000000..88673b15e --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/NxFile.java @@ -0,0 +1,192 @@ +package es.virtualcable.nx; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +public class NxFile { + private static final String EMPTY_PASSWORD = "EMPTY_PASSWORD"; + + public boolean fullScreen; + public String width; + public String height; + public String cachemem = "4"; + public String cachedisk = "32"; + public String keyboardLayout = ""; + public String linkSpeed = "wan"; + public String host = ""; + public String port = ""; + public String username = ""; + public String password = ""; + public String desktop = "gnome"; + + public NxFile(boolean fullScreen, String width, String height) { + this.fullScreen = fullScreen; + this.width = width; + this.height = height; + } + + public void saveFile(String fname) throws IOException { + String rememberPass = "true"; + String pass = NXPassword.scrambleString(password); + if( pass == "" ) + { + rememberPass = "false"; + pass = EMPTY_PASSWORD; + } + String resolution = width + "x" + height; + if( fullScreen ) + resolution = "fullscreen"; + + String save = NXTemplate.replaceAll("\\{CACHEMEM\\}", cachemem); + save = save.replaceAll("\\{CACHEDISK\\}", cachedisk); + save = save.replaceAll("\\{KEYLAYOUT\\}", keyboardLayout); + save = save.replaceAll("\\{LINKSPEED\\}", linkSpeed); + save = save.replaceAll("\\{REMEMBERPASS\\}", rememberPass); + save = save.replaceAll("\\{RESOLUTION\\}", resolution); + save = save.replaceAll("\\{WIDTH\\}", width); + save = save.replaceAll("\\{HEIGHT\\}", height); + save = save.replaceAll("\\{HOST\\}", host); + save = save.replaceAll("\\{PORT\\}", port); + save = save.replaceAll("\\{DESKTOP\\}", desktop); + save = save.replaceAll("\\{USERNAME\\}", username); + save = save.replaceAll("\\{PASSWORD\\}", pass); + + FileWriter fstream = new FileWriter(fname); + PrintWriter out = new PrintWriter(fstream); + out.write(save); + out.close(); + } + + public static void main(String [] args) + { + NxFile nx = new NxFile(true, "1024", "768"); + try { + nx.saveFile(""); + } catch( Exception e ) + { + e.printStackTrace(); + } + } + + // NX Template used to generate file + private static final String NXTemplate = + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + +} diff --git a/nxtuntransport/src/es/virtualcable/nx/OsApplet.java b/nxtuntransport/src/es/virtualcable/nx/OsApplet.java new file mode 100644 index 000000000..819378392 --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/OsApplet.java @@ -0,0 +1,15 @@ +package es.virtualcable.nx; + +import java.util.Hashtable; + +public interface OsApplet { + + void setParameters(Hashtable parameters, String urlBase, int screenWidth, int screenHeight); + + void init(); + + void start(); + + void destroy(); + +} diff --git a/nxtuntransport/src/es/virtualcable/nx/WindowsApplet.java b/nxtuntransport/src/es/virtualcable/nx/WindowsApplet.java new file mode 100644 index 000000000..1b810da43 --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/WindowsApplet.java @@ -0,0 +1,116 @@ +package es.virtualcable.nx; + +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; + +import es.virtualcable.nx.FreePortFinder; +import es.virtualcable.nx.util; +import es.virtualcable.windows.WinRegistry; + +public class WindowsApplet implements OsApplet { + + private Hashtable params; + private String tmpDir = ""; + private String baseUrl = ""; + private String nxFileName = ""; + private String jarFileName = ""; + private String scrWidth; + private String scrHeight; + private String tunPort = ""; + + + public void setParameters(Hashtable parameters, String urlBase, + int screenWidth, int screenHeight) { + params = parameters; + baseUrl = urlBase; + scrWidth = Integer.toString(screenWidth); + scrHeight = Integer.toString(screenHeight); + } + + public void start() { + try { + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + nxFileName = tmpDir + UUID.randomUUID().toString() + ".nxs"; + jarFileName = tmpDir + UUID.randomUUID().toString() + ".jar"; + + //System.out.println(nxFileName); + + String width = params.get("width"); + String height = params.get("height"); + boolean fullScreen = false; + + if( width.equals("-1")) + { + width = scrWidth; + height = scrHeight; + fullScreen = true; + } + + if( downloadJar() == false) + return; + tunPort = Integer.toString(FreePortFinder.findFreePort()); + + NxFile nx = new NxFile(fullScreen, width, height); + nx.host = "127.0.0.1"; + nx.port = tunPort; + nx.username = params.get("user"); + nx.password = params.get("pass"); + nx.cachedisk = params.get("cacheDisk"); + nx.cachemem = params.get("cacheMem"); + nx.desktop = params.get("session"); + nx.linkSpeed = params.get("connection"); + + try { + nx.saveFile( nxFileName ); + } catch (IOException e) { + javax.swing.JOptionPane.showMessageDialog(null, "Can't save nx temporal file: " + e.getMessage()); + e.printStackTrace(); + return; + } + + String cmd = WinRegistry.readString(WinRegistry.HKEY_CURRENT_USER, "Software\\Classes\\NXClient.session\\shell\\open\\command", ""); + if ( null == cmd ) + javax.swing.JOptionPane.showMessageDialog(null, "Can't find Nomachine client. Please, install it"); + else + { + nxFileName = nxFileName.replace("\\", "\\\\"); + System.out.println(nxFileName); + cmd = cmd.replaceAll("%1", nxFileName); + executeTunnel(cmd); + + } + } catch (Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + } + + public void init() { + } + + public void destroy() { + } + + private void executeTunnel(String nxCmd) throws IOException + { + String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java.exe"; + String cmd = "\"" + java + "\" -jar " + jarFileName + " " + tunPort + " " + nxCmd; + ProcessBuilder pb = new ProcessBuilder( cmd ); + Map env = pb.environment(); + env.put("TPARAMS", params.get("tun")); + System.out.println("TPARAMS: " + params.get("tun")); + System.out.println(cmd); + pb.start(); + + } + + private boolean downloadJar() + { + return util.download(baseUrl, "2", jarFileName); + } + +} diff --git a/nxtuntransport/src/es/virtualcable/nx/util.java b/nxtuntransport/src/es/virtualcable/nx/util.java new file mode 100644 index 000000000..b377782cf --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/nx/util.java @@ -0,0 +1,49 @@ +package es.virtualcable.nx; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class util { + + public static boolean download(String baseUrl, String id, String outputFileName) + { + try { + java.net.URL u = new java.net.URL(baseUrl + id); + java.net.URLConnection uc = u.openConnection(); + String contentType = uc.getContentType(); + int contentLength = uc.getContentLength(); + if (contentType.startsWith("text/") || contentLength == -1) { + throw new IOException("This is not a binary file."); + } + InputStream raw = uc.getInputStream(); + InputStream in = new BufferedInputStream(raw); + byte[] data = new byte[contentLength]; + int bytesRead = 0; + int offset = 0; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + throw new IOException("Only read " + offset + " bytes; Expected " + contentLength + " bytes"); + } + + java.io.FileOutputStream out = new java.io.FileOutputStream(outputFileName); + out.write(data); + out.flush(); + out.close(); + + } catch(Exception e) { + System.out.println("Unable to download component, already present or network error? " + e.getMessage()); + return false; + } + return true; + } + + +} diff --git a/nxtuntransport/src/es/virtualcable/windows/WinRegistry.java b/nxtuntransport/src/es/virtualcable/windows/WinRegistry.java new file mode 100644 index 000000000..cd9ff892a --- /dev/null +++ b/nxtuntransport/src/es/virtualcable/windows/WinRegistry.java @@ -0,0 +1,392 @@ +package es.virtualcable.windows; + +/* Code borrowed from http://stackoverflow.com/questions/62289/read-write-to-windows-registry-using-java. */ +/* Original author don't found, if someone found him, please note it here */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; + +public class WinRegistry { + public static final int HKEY_CURRENT_USER = 0x80000001; + public static final int HKEY_LOCAL_MACHINE = 0x80000002; + public static final int REG_SUCCESS = 0; + public static final int REG_NOTFOUND = 2; + public static final int REG_ACCESSDENIED = 5; + + private static final int KEY_ALL_ACCESS = 0xf003f; + private static final int KEY_READ = 0x20019; + private static Preferences userRoot = Preferences.userRoot(); + private static Preferences systemRoot = Preferences.systemRoot(); + + private static Class userClass = userRoot.getClass(); + private static Method regOpenKey = null; + private static Method regCloseKey = null; + private static Method regQueryValueEx = null; + private static Method regEnumValue = null; + private static Method regQueryInfoKey = null; + private static Method regEnumKeyEx = null; + private static Method regCreateKeyEx = null; + private static Method regSetValueEx = null; + private static Method regDeleteKey = null; + private static Method regDeleteValue = null; + + static { + try { + regOpenKey = userClass.getDeclaredMethod("WindowsRegOpenKey", + new Class[] { int.class, byte[].class, int.class }); + regOpenKey.setAccessible(true); + regCloseKey = userClass.getDeclaredMethod("WindowsRegCloseKey", + new Class[] { int.class }); + regCloseKey.setAccessible(true); + regQueryValueEx = userClass.getDeclaredMethod("WindowsRegQueryValueEx", + new Class[] { int.class, byte[].class }); + regQueryValueEx.setAccessible(true); + regEnumValue = userClass.getDeclaredMethod("WindowsRegEnumValue", + new Class[] { int.class, int.class, int.class }); + regEnumValue.setAccessible(true); + regQueryInfoKey = userClass.getDeclaredMethod("WindowsRegQueryInfoKey1", + new Class[] { int.class }); + regQueryInfoKey.setAccessible(true); + regEnumKeyEx = userClass.getDeclaredMethod( + "WindowsRegEnumKeyEx", new Class[] { int.class, int.class, + int.class }); + regEnumKeyEx.setAccessible(true); + regCreateKeyEx = userClass.getDeclaredMethod( + "WindowsRegCreateKeyEx", new Class[] { int.class, + byte[].class }); + regCreateKeyEx.setAccessible(true); + regSetValueEx = userClass.getDeclaredMethod( + "WindowsRegSetValueEx", new Class[] { int.class, + byte[].class, byte[].class }); + regSetValueEx.setAccessible(true); + regDeleteValue = userClass.getDeclaredMethod( + "WindowsRegDeleteValue", new Class[] { int.class, + byte[].class }); + regDeleteValue.setAccessible(true); + regDeleteKey = userClass.getDeclaredMethod( + "WindowsRegDeleteKey", new Class[] { int.class, + byte[].class }); + regDeleteKey.setAccessible(true); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private WinRegistry() { } + + /** + * Read a value from key and value name + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @param valueName + * @return the value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static String readString(int hkey, String key, String valueName) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readString(systemRoot, hkey, key, valueName); + } + else if (hkey == HKEY_CURRENT_USER) { + return readString(userRoot, hkey, key, valueName); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Read value(s) and value name(s) form given key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @return the value name(s) plus the value(s) + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static Map readStringValues(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readStringValues(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + return readStringValues(userRoot, hkey, key); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Read the value name(s) from a given key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @return the value name(s) + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static List readStringSubKeys(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readStringSubKeys(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + return readStringSubKeys(userRoot, hkey, key); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Create a key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void createKey(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int [] ret; + if (hkey == HKEY_LOCAL_MACHINE) { + ret = createKey(systemRoot, hkey, key); + regCloseKey.invoke(systemRoot, new Object[] { new Integer(ret[0]) }); + } + else if (hkey == HKEY_CURRENT_USER) { + ret = createKey(userRoot, hkey, key); + regCloseKey.invoke(userRoot, new Object[] { new Integer(ret[0]) }); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + if (ret[1] != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + ret[1] + " key=" + key); + } + } + + /** + * Write a value in a given key/value name + * @param hkey + * @param key + * @param valueName + * @param value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void writeStringValue + (int hkey, String key, String valueName, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + writeStringValue(systemRoot, hkey, key, valueName, value); + } + else if (hkey == HKEY_CURRENT_USER) { + writeStringValue(userRoot, hkey, key, valueName, value); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Delete a given key + * @param hkey + * @param key + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void deleteKey(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc = -1; + if (hkey == HKEY_LOCAL_MACHINE) { + rc = deleteKey(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + rc = deleteKey(userRoot, hkey, key); + } + if (rc != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + rc + " key=" + key); + } + } + + /** + * delete a value from a given key/value name + * @param hkey + * @param key + * @param value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void deleteValue(int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc = -1; + if (hkey == HKEY_LOCAL_MACHINE) { + rc = deleteValue(systemRoot, hkey, key, value); + } + else if (hkey == HKEY_CURRENT_USER) { + rc = deleteValue(userRoot, hkey, key, value); + } + if (rc != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + rc + " key=" + key + " value=" + value); + } + } + + // ===================== + + private static int deleteValue + (Preferences root, int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS) }); + if (handles[1] != REG_SUCCESS) { + return handles[1]; // can be REG_NOTFOUND, REG_ACCESSDENIED + } + int rc =((Integer) regDeleteValue.invoke(root, + new Object[] { + new Integer(handles[0]), toCstr(value) + })).intValue(); + regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) }); + return rc; + } + + private static int deleteKey(Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc =((Integer) regDeleteKey.invoke(root, + new Object[] { new Integer(hkey), toCstr(key) })).intValue(); + return rc; // can REG_NOTFOUND, REG_ACCESSDENIED, REG_SUCCESS + } + + private static String readString(Preferences root, int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_READ) }); + if (handles[1] != REG_SUCCESS) { + return null; + } + byte[] valb = (byte[]) regQueryValueEx.invoke(root, new Object[] { + new Integer(handles[0]), toCstr(value) }); + regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) }); + return (valb != null ? new String(valb).trim() : null); + } + + private static Map readStringValues + (Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + HashMap results = new HashMap(); + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_READ) }); + if (handles[1] != REG_SUCCESS) { + return null; + } + int[] info = (int[]) regQueryInfoKey.invoke(root, + new Object[] { new Integer(handles[0]) }); + + int count = info[2]; // count + int maxlen = info[3]; // value length max + for(int index=0; index readStringSubKeys + (Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + List results = new ArrayList(); + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + new Integer(hkey), toCstr(key), new Integer(KEY_READ) + }); + if (handles[1] != REG_SUCCESS) { + return null; + } + int[] info = (int[]) regQueryInfoKey.invoke(root, + new Object[] { new Integer(handles[0]) }); + + int count = info[2]; // count + int maxlen = info[3]; // value length max + for(int index=0; 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 literal 0 HcmV?d00001 diff --git a/rdptransport/Release/rdppass.dll b/rdptransport/Release/rdppass.dll new file mode 100644 index 0000000000000000000000000000000000000000..ce6e4a7009f00eba762d4359ed1ee93b14c6fbaf GIT binary patch literal 40448 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 literal 0 HcmV?d00001 diff --git a/rdptransport/java/.classpath b/rdptransport/java/.classpath new file mode 100644 index 000000000..fb5011632 --- /dev/null +++ b/rdptransport/java/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/rdptransport/java/.project b/rdptransport/java/.project new file mode 100644 index 000000000..fb728e4c4 --- /dev/null +++ b/rdptransport/java/.project @@ -0,0 +1,17 @@ + + + rdp + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/rdptransport/java/.settings/org.eclipse.core.resources.prefs b/rdptransport/java/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..2db27b5bf --- /dev/null +++ b/rdptransport/java/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Wed Dec 28 00:15:05 CET 2011 +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/rdptransport/java/.settings/org.eclipse.mylyn.tasks.ui.prefs b/rdptransport/java/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 000000000..1a79ace0a --- /dev/null +++ b/rdptransport/java/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Tue Jan 03 02:53:59 CET 2012 +eclipse.preferences.version=1 +project.repository.kind=mantis +project.repository.url=http\://192.168.0.6/mantis diff --git a/rdptransport/java/descrdp.jardesc b/rdptransport/java/descrdp.jardesc new file mode 100644 index 000000000..bc09190ff --- /dev/null +++ b/rdptransport/java/descrdp.jardesc @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/rdptransport/java/description.jardesc b/rdptransport/java/description.jardesc new file mode 100644 index 000000000..580985655 --- /dev/null +++ b/rdptransport/java/description.jardesc @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/rdptransport/java/jar/rdp.jar b/rdptransport/java/jar/rdp.jar new file mode 100644 index 0000000000000000000000000000000000000000..57828b99fb75a706d7045bf76b1117afd393d9f7 GIT binary patch literal 24270 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 z z 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 literal 0 HcmV?d00001 diff --git a/rdptransport/java/src/RdpApplet.java b/rdptransport/java/src/RdpApplet.java new file mode 100644 index 000000000..1d221366d --- /dev/null +++ b/rdptransport/java/src/RdpApplet.java @@ -0,0 +1,135 @@ + +import java.applet.*; +import java.awt.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Hashtable; +import es.virtualcable.rdp.LinuxApplet; +import es.virtualcable.rdp.OsApplet; +import es.virtualcable.rdp.WindowsApplet; +import es.virtualcable.rdp.MacApplet; + +public class RdpApplet extends Applet { + /** + * + */ + private static final long serialVersionUID = -7930346560654300533L; + //private int width; + //private int height; + private OsApplet applet; + private String appletStr = "UDS RDP Connector"; + + public void init() { + //width = getSize().width; + //height = getSize().height; + setBackground(Color.lightGray); + + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + String os = System.getProperty("os.name"); + if( os.startsWith("Windows") ) + applet = new WindowsApplet(); + else if( os.startsWith("Linux")) + applet = new LinuxApplet(); + else if (os.startsWith("Mac")) + applet = new MacApplet(); + else + throw new Exception("Invalid os!!!"); + + Hashtable params = parseParams(unscramble(getParameter("data"))); + String baseUrl = getCodeBase().toString(); + if( params.get("tun") != null ) + appletStr = "UDS RDP Tunnel Connector"; + + Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize(); + + applet.setParameters(params, baseUrl, scrSize.width, scrSize.height); + applet.init(); + + return null; // nothing to return + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void start() { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + applet.start(); + return null; // nothing to return + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void destroy() { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + applet.destroy(); + return null; // nothing to return + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void paint(Graphics g) { + g.setColor(Color.black); + g.drawString(appletStr, 8, 16); + } + + private Hashtable parseParams(String params) + { + Hashtable res = new Hashtable(); + String[] parms = params.split("\t"); + for( int i = 0; i < parms.length; i++) { + String[] val = parms[i].split(":"); + if( val.length == 1 ) + res.put(val[0], ""); + else + res.put(val[0], val[1]); + } + return res; + } + + private String unscramble(String in) + { + int len = in.length(); + StringBuilder res = new StringBuilder(len/2); + int val = 0x32; + for( int i = 0; i < len; i += 2) { + String b = in.substring(i, i+2); + int c = Integer.parseInt(b, 16)^val; + val = (val + c) & 0xFF; + res.append((char)c); + } + + return res.reverse().toString(); + } + + +} diff --git a/rdptransport/java/src/es/virtualcable/rdp/FreePortFinder.java b/rdptransport/java/src/es/virtualcable/rdp/FreePortFinder.java new file mode 100644 index 000000000..1e5f8b16a --- /dev/null +++ b/rdptransport/java/src/es/virtualcable/rdp/FreePortFinder.java @@ -0,0 +1,40 @@ +package es.virtualcable.rdp; + +import java.io.IOException; +import java.net.*; +import java.util.Random; + + +public class FreePortFinder { + + private final static int START_PORT = 35000; + private final static int START_PORT_MAX = 44000; + private final static int END_PORT = 45500; + + public static boolean isFree(int port) + { + boolean free = true; + try { + InetAddress addr = InetAddress.getByName("localhost"); + SocketAddress sa = new InetSocketAddress(addr, port); + ServerSocket socket = new ServerSocket(); + socket.bind(sa); + socket.close(); + } catch (IOException e) { + free = false; + } + return free; + } + + public static int findFreePort() + { + Random rnd = new Random(); + int startPort = START_PORT + rnd.nextInt(START_PORT_MAX-START_PORT); + for( int port = startPort; port < END_PORT; port ++) + { + if( isFree(port) ) + return port; + } + return -1; + } +} diff --git a/rdptransport/java/src/es/virtualcable/rdp/LinuxApplet.java b/rdptransport/java/src/es/virtualcable/rdp/LinuxApplet.java new file mode 100644 index 000000000..5d1a2263e --- /dev/null +++ b/rdptransport/java/src/es/virtualcable/rdp/LinuxApplet.java @@ -0,0 +1,187 @@ +package es.virtualcable.rdp; + +import java.io.File; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; + +public class LinuxApplet implements OsApplet { + private final String[] paths = { "/usr/local/bin/", "/usr/bin/" }; + private final String rdesktop = "rdesktop"; + + private Hashtable params; + private String jarFileName = ""; + private String tmpDir = ""; + private String baseUrl = ""; + private String tunPort = ""; + + private void addParameterIfNotEmpty(ArrayList exec, String param, String value) + { + if( value.length() == 0 ) + return; + if( param.indexOf(' ') != -1 ) + exec.add( param + "\"" + value + "\""); + else + exec.add(param + value); + } + + public void start() { + try { + String colors = params.get("c"); + String width = params.get("w"); + String height = params.get("h"); + String server = params.get("s"); // Server + String user = params.get("u"); // User + String password = params.get("p"); // password + String domain = params.get("d"); // domain + + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + jarFileName = tmpDir + UUID.randomUUID().toString() + ".jar"; + + boolean redirectSmartcards = params.get("sc").equals("1"); + boolean redirectDrives = params.get("dr").equals("1"); + boolean redirectSerials = params.get("se").equals("1"); + boolean redirectPrinters = params.get("pr").equals("1"); + boolean redirectAudio = params.get("au").equals("1"); + boolean compression = params.get("cr").equals("1"); + + String home = System.getProperty("user.home"); + + ArrayList exec = new ArrayList(); + + if(params.get("tun") != null) + { + if( downloadJar() == false) + return; + tunPort = Integer.toString(FreePortFinder.findFreePort()); + server = "localhost:" + tunPort; + String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + exec.add(java); exec.add("-jar"); exec.add(jarFileName); exec.add(tunPort); + } + + String execPath = ""; + + for(int i = 0; i < paths.length; i++ ) + { + File f = new File(paths[i] + rdesktop); + if( f.exists() ) + { + execPath = paths[i] + rdesktop; + break; + } + } + + if( execPath.length() == 0 ) + { + javax.swing.JOptionPane.showMessageDialog(null, "Can't find rdesktop client.\nShould be at /usr/bin or /usr/local/bin\nPlease, install it"); + return; + } + + exec.add(execPath); + + addParameterIfNotEmpty(exec, "-u", user); + addParameterIfNotEmpty(exec, "-d", domain); + if( password.length() != 0 ) + exec.add("-p-"); + addParameterIfNotEmpty(exec, "-a", colors); + if( width.equals("-1") ) + exec.add("-f"); + else + exec.add("-g" + width + "x" + height); + + exec.add("-TUDS-RDP"); + exec.add("-P"); + + if( redirectSmartcards ) + { + } + + if( compression ) + { + exec.add("-z"); + } + + if( redirectDrives ) + exec.add("-rdisk:home=\"" + home + "\""); + + if( redirectAudio ) + exec.add("-rsound:local"); + else + exec.add("-rsound:off"); + + if( redirectSerials ) + exec.add("-rcomport:COM1=/dev/ttyS0"); + + if( redirectPrinters ) // Will have to look at local cups to find printer + { + } + + exec.add(server); + +/* Iterator it = exec.iterator(); + while( it.hasNext()) { + System.out.print(it.next() + " "); + } + System.out.println(); +*/ + Process p; + try { + ProcessBuilder pb = new ProcessBuilder(exec); + if( params.get("tun") != null) + { + Map env = pb.environment(); + env.put("TPARAMS", params.get("tun")); + //System.out.println("TPARAMS: " + params.get("tun")); + } + p = pb.start(); + } catch(Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception launching " + execPath + ":\n" + e.getMessage()); + e.printStackTrace(); + return; + } + + if( password.length() != 0 ) + { + try { + p.getOutputStream().write(password.getBytes()); + p.getOutputStream().write( new byte[]{'\n'} ); + p.getOutputStream().flush(); + } + catch( Exception e ) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception communicating with " + execPath + ":\n" + e.getMessage()); + return; + + } + } + } + catch(Exception e) + { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + + + } + + public void init() { + + } + + public void destroy() { + + } + + public void setParameters(Hashtable parameters, + String urlBase, int screenWidth, int screenHeight) { + params = parameters; + baseUrl = urlBase; + } + + private boolean downloadJar() + { + return util.download(baseUrl, "3", jarFileName); + } + +} diff --git a/rdptransport/java/src/es/virtualcable/rdp/MacApplet.java b/rdptransport/java/src/es/virtualcable/rdp/MacApplet.java new file mode 100644 index 000000000..ee20ab2cd --- /dev/null +++ b/rdptransport/java/src/es/virtualcable/rdp/MacApplet.java @@ -0,0 +1,206 @@ +package es.virtualcable.rdp; + +import java.io.File; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; + +public class MacApplet implements OsApplet { + private final String[] paths = { "/usr/bin/" }; + private final String open = "open"; + private final String cordPath = "/Applications/CoRD.app/Contents/MacOS/CoRD"; + + private Hashtable params; + private String jarFileName = ""; + private String tmpDir = ""; + private String baseUrl = ""; + private String tunPort = ""; + + private String convSpaces(String value) + { + final String weird = "$#$_$#$"; + if( value == null || value.isEmpty()) + return ""; + String res = value.replaceAll(" ", "%20"); + res = res.replaceAll("&", weird); + return res.replaceAll(weird, "&"); + } + + private void Cord(ArrayList exec, String user, String password, String server, + String domain, String colors, String width, String height, boolean redirectAudio, + boolean redirectSmartcards, boolean redirectDrives, + boolean redirectSerials, boolean redirectPrinters) + { + String url = "rdp://"; + + if(!user.isEmpty()) { + url += user; + if(!password.isEmpty()) + url += ":" + password; + url += "@"; + } + + url += server + "/"; + if(!domain.isEmpty()) + url += domain; + url += "?screenDepth=" + colors + "&"; + if(width.equals("-1")) + url += "fullscreen=true"; + else + url += "screenWidth="+width+"&screenHeight="+height; + + url += "&forwardAudio="; + + if( redirectAudio ) + url += "0"; + else + url += "1"; + + if( redirectSmartcards ) // Not supported by Cord + { + } + + if( redirectDrives ) + url += "&forwardDisks=true"; + + if( redirectSerials ) + { + } + + if( redirectPrinters ) + url += "&forwardPrinters=true"; + + exec.add(url); + + } + + public void start() { + try { + String colors = params.get("c"); + String width = params.get("w"); + String height = params.get("h"); + String server = params.get("s"); // Server + String user = convSpaces(params.get("u")); // User + String password = convSpaces(params.get("p")); // password + String domain = convSpaces(params.get("d")); // domain + + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + jarFileName = tmpDir + UUID.randomUUID().toString() + ".jar"; + + boolean redirectSmartcards = params.get("sc").equals("1"); + boolean redirectDrives = params.get("dr").equals("1"); + boolean redirectSerials = params.get("se").equals("1"); + boolean redirectPrinters = params.get("pr").equals("1"); + boolean redirectAudio = params.get("au").equals("1"); + //boolean compression = params.get("cr").equals("1"); + + //String home = System.getProperty("user.home"); + + ArrayList exec = new ArrayList(); + + if(params.get("tun") != null) + { + if( downloadJar() == false) + return; + tunPort = Integer.toString(FreePortFinder.findFreePort()); + server = "127.0.0.1:" + tunPort; + String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + exec.add(java); exec.add("-jar"); exec.add(jarFileName); exec.add(tunPort); + } + + String execPath = ""; + + for(int i = 0; i < paths.length; i++ ) + { + File f = new File(paths[i] + open); + if( f.exists() ) + { + execPath = paths[i] + open; + break; + } + } + + if(( new File(cordPath)).exists() == false ) + { + javax.swing.JOptionPane.showMessageDialog(null, "Can't find CoRD client. Please, install it before proceeding."); + return; + } + + if( execPath.length() == 0 ) + { + javax.swing.JOptionPane.showMessageDialog(null, "Can't find rdesktop client.\nShould be at /usr/bin or /usr/local/bin\nPlease, install it"); + return; + } + + exec.add(execPath); + + Cord(exec, user, password, server, domain, colors, width, height, redirectAudio, redirectSmartcards, + redirectDrives, redirectSerials, redirectPrinters); + +/* Iterator it = exec.iterator(); + while( it.hasNext()) { + System.out.print(it.next() + " "); + } + System.out.println(); +*/ + Process p; + try { + ProcessBuilder pb = new ProcessBuilder(exec); + if( params.get("tun") != null) + { + Map env = pb.environment(); + env.put("TPARAMS", params.get("tun")); + //System.out.println("TPARAMS: " + params.get("tun")); + } + p = pb.start(); + } catch(Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception launching " + execPath + ":\n" + e.getMessage()); + e.printStackTrace(); + return; + } + + if( password.length() != 0 ) + { + try { + p.getOutputStream().write(password.getBytes()); + p.getOutputStream().write( new byte[]{'\n'} ); + p.getOutputStream().flush(); + } + catch( Exception e ) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception communicating with " + execPath + ":\n" + e.getMessage()); + return; + + } + } + } + catch(Exception e) + { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + + + } + + public void init() { + + } + + public void destroy() { + + } + + public void setParameters(Hashtable parameters, + String urlBase, int screenWidth, int screenHeight) { + params = parameters; + baseUrl = urlBase; + } + + private boolean downloadJar() + { + return util.download(baseUrl, "3", jarFileName); + } + +} diff --git a/rdptransport/java/src/es/virtualcable/rdp/OsApplet.java b/rdptransport/java/src/es/virtualcable/rdp/OsApplet.java new file mode 100644 index 000000000..ef3f41814 --- /dev/null +++ b/rdptransport/java/src/es/virtualcable/rdp/OsApplet.java @@ -0,0 +1,15 @@ +package es.virtualcable.rdp; + +import java.util.Hashtable; + +public interface OsApplet { + + void setParameters(Hashtable parameters, String urlBase, int screenWidth, int screenHeight); + + void init(); + + void start(); + + void destroy(); + +} diff --git a/rdptransport/java/src/es/virtualcable/rdp/WinRdpFile.java b/rdptransport/java/src/es/virtualcable/rdp/WinRdpFile.java new file mode 100644 index 000000000..97a550f7a --- /dev/null +++ b/rdptransport/java/src/es/virtualcable/rdp/WinRdpFile.java @@ -0,0 +1,94 @@ +package es.virtualcable.rdp; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import net.sourceforge.jdpapi.DataProtector; + +public class WinRdpFile { + public String width; + public String height; + public boolean fullScreen; + public String bpp; + public String address = ""; + public String username = ""; + public String domain = ""; + public String password = ""; + public boolean redirectSerials = false; + public boolean redirectPrinters = false; + public boolean redirectDrives = false; + public boolean redirectSmartcards = false; + public boolean redirectAudio = false; + public boolean compression = false; + + public WinRdpFile(boolean fullScreen, String width, String height, String bpp) { + this.width = width; + this.height = height; + this.bpp = bpp; + this.fullScreen = fullScreen; + } + + public void saveFile(String fname) throws IOException { + DataProtector d = new DataProtector(true); + String pass = encode(d.protect(this.password)); + String screenMode = fullScreen ? "2" : "1"; + String audioMode = redirectAudio ? "0" : "2"; + String serials = redirectSerials ? "1" : "0"; + String drives = redirectDrives ? "1" : "0"; + String scards = redirectSmartcards ? "1" : "0"; + String printers = redirectPrinters ? "1" : "0"; + String compression = this.compression ? "1" : "0"; + + FileWriter fstream = new FileWriter(fname); + PrintWriter out = new PrintWriter(fstream); + out.println("screen mode id:i:" + screenMode); + out.println("desktopwidth:i:"+this.width); + out.println("desktopheight:i:"+this.height); + out.println("session bpp:i:"+this.bpp); + out.println("auto connect:i:1"); + out.println("full address:s:"+this.address); + out.println("compression:i:"+compression); + out.println("keyboardhook:i:2"); + out.println("audiomode:i:"+audioMode); + out.println("redirectdrives:i:" + drives); + out.println("redirectprinters:i:" + printers); + out.println("redirectcomports:i:" + serials); + out.println("redirectsmartcards:i:" + scards); + out.println("redirectclipboard:i:1"); + out.println("displayconnectionbar:i:0"); + if( this.username.length() != 0) { + out.println("username:s:"+this.username); + out.println("domain:s:"+this.domain); + out.println("password 51:b:"+pass); + } + out.println("alternate shell:s:"); + out.println("shell working directory:s:"); + out.println("disable wallpaper:i:1"); + out.println("disable full window drag:i:1"); + out.println("disable menu anims:i:1"); + out.println("disable themes:i:1"); + out.println("bitmapcachepersistenable:i:1"); + out.println("authentication level:i:0"); + out.println("enablecredsspsupport:i:1"); + out.println("prompt for credentials:i:0"); + out.println("negotiate security layer:i:1"); + out.close(); + } + + protected static final byte[] Hexhars = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + public static String encode(byte[] b) { + + StringBuilder s = new StringBuilder(2 * b.length); + for (int i = 0; i < b.length; i++) { + int v = b[i] & 0xff; + s.append((char) Hexhars[v >> 4]); + s.append((char) Hexhars[v & 0xf]); + } + + return s.toString(); + } + +} diff --git a/rdptransport/java/src/es/virtualcable/rdp/WindowsApplet.java b/rdptransport/java/src/es/virtualcable/rdp/WindowsApplet.java new file mode 100644 index 000000000..07a367f53 --- /dev/null +++ b/rdptransport/java/src/es/virtualcable/rdp/WindowsApplet.java @@ -0,0 +1,133 @@ +package es.virtualcable.rdp; + +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; + +import es.virtualcable.rdp.util; + +public class WindowsApplet implements OsApplet { + + private static final String MSTSC_CMD = "c:\\windows\\system32\\mstsc.exe"; + + private Hashtable params; + private String scrWidth; + private String scrHeight; + private String tmpDir = ""; + private String rdpFileName = ""; + private String dllFileName = ""; + private String jarFileName = ""; + private String baseUrl = ""; + private String tunPort = ""; + + public void setParameters(Hashtable parameters, String urlBase, + int screenWidth, int screenHeight) { + params = parameters; + baseUrl = urlBase; + scrWidth = Integer.toString(screenWidth); + scrHeight = Integer.toString(screenHeight); + } + + public void start() { + try { + tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + rdpFileName = tmpDir + UUID.randomUUID().toString() + ".tmp"; + dllFileName = tmpDir + UUID.randomUUID().toString() + ".tmp"; + jarFileName = tmpDir + UUID.randomUUID().toString() + ".jar"; + if( downloadDll() == false ) + return; + + if(params.get("tun") != null) + { + if( downloadJar() == false) + return; + tunPort = Integer.toString(FreePortFinder.findFreePort()); + } + + System.load(dllFileName); + + String width = params.get("w"); + String height = params.get("h"); + boolean fullScreen = false; + + if( width.equals("-1")) + { + width = scrWidth; + height = scrHeight; + fullScreen = true; + } + + WinRdpFile rdp = new WinRdpFile(fullScreen, width, height, params.get("c")); + if( params.get("tun") != null ) + { + rdp.address = "127.0.0.1:" + tunPort; + } + else + rdp.address = params.get("s"); // Server + rdp.username = params.get("u"); // User + rdp.password = params.get("p"); // password + rdp.domain = params.get("d"); // domain + rdp.redirectSmartcards = params.get("sc").equals("1"); + rdp.redirectDrives = params.get("dr").equals("1"); + rdp.redirectSerials = params.get("se").equals("1"); + rdp.redirectPrinters = params.get("pr").equals("1"); + rdp.redirectAudio = params.get("au").equals("1"); + rdp.compression = params.get("cr").equals("1"); + + try { + rdp.saveFile( rdpFileName ); + if( params.get("tun") != null ) + executeTunnel(); + else + executeDirect(); + + } catch (IOException e) { + e.printStackTrace(); + return; + } + + // Process p = + } catch (Exception e) { + javax.swing.JOptionPane.showMessageDialog(null,"Exception at applet:\n" + e.getMessage()); + e.printStackTrace(); + return; + } + } + + public void init() { + } + + public void destroy() { + } + + private void executeTunnel() throws IOException + { + String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java.exe"; + String [] cmd = { java, "-jar", jarFileName, tunPort, MSTSC_CMD, rdpFileName }; + ProcessBuilder pb = new ProcessBuilder( cmd ); + Map env = pb.environment(); + env.put("TPARAMS", params.get("tun")); + //System.out.println("TPARAMS: " + params.get("tun")); + //System.out.println("java: " + java + " -jar " + jarFileName + " " + MSTSC_CMD + " " + rdpFileName); + pb.start(); + + } + + private void executeDirect() throws IOException + { + Runtime.getRuntime().exec( MSTSC_CMD + " \"" + rdpFileName + "\"" ); + } + + private boolean downloadDll() + { + return util.download(baseUrl, "2", dllFileName); + } + + private boolean downloadJar() + { + return util.download(baseUrl, "3", jarFileName); + } + +} diff --git a/rdptransport/java/src/es/virtualcable/rdp/util.java b/rdptransport/java/src/es/virtualcable/rdp/util.java new file mode 100644 index 000000000..44fcf65df --- /dev/null +++ b/rdptransport/java/src/es/virtualcable/rdp/util.java @@ -0,0 +1,49 @@ +package es.virtualcable.rdp; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class util { + + public static boolean download(String baseUrl, String id, String outputFileName) + { + try { + java.net.URL u = new java.net.URL(baseUrl + id); + java.net.URLConnection uc = u.openConnection(); + String contentType = uc.getContentType(); + int contentLength = uc.getContentLength(); + if (contentType.startsWith("text/") || contentLength == -1) { + throw new IOException("This is not a binary file."); + } + InputStream raw = uc.getInputStream(); + InputStream in = new BufferedInputStream(raw); + byte[] data = new byte[contentLength]; + int bytesRead = 0; + int offset = 0; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + throw new IOException("Only read " + offset + " bytes; Expected " + contentLength + " bytes"); + } + + java.io.FileOutputStream out = new java.io.FileOutputStream(outputFileName); + out.write(data); + out.flush(); + out.close(); + + } catch(Exception e) { + System.out.println("Unable to download file, already present or network error? " + e.getMessage()); + return false; + } + return true; + } + + +} diff --git a/rdptransport/java/src/manifest b/rdptransport/java/src/manifest new file mode 100644 index 000000000..e3467bb08 --- /dev/null +++ b/rdptransport/java/src/manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Sealed: true + diff --git a/rdptransport/java/src/net/sourceforge/jdpapi/DPAPI.java b/rdptransport/java/src/net/sourceforge/jdpapi/DPAPI.java new file mode 100644 index 000000000..95f651edf --- /dev/null +++ b/rdptransport/java/src/net/sourceforge/jdpapi/DPAPI.java @@ -0,0 +1,27 @@ +package net.sourceforge.jdpapi; + + +class DPAPI { + + /** + * See the CryptProtectData + * documentation for more details + * + * @param input Plaintext to encrypt + * @param entropy Additional entropy to include when encrypting (optional) + * @param localMachine If true, allow all users on the machine to decrypt the return ciphertext + * @return ciphertext + */ + static native byte [] CryptProtectData(String input, byte [] entropy, boolean localMachine); + + /** + * See the CryptUnprotectData + * documentation for more details + * + * @param input Ciphertext + * @param entropy Entropy that was included when {@code input} was encrypted + * @return + */ + static native String CryptUnprotectData(byte [] input, byte [] entropy); + +} \ No newline at end of file diff --git a/rdptransport/java/src/net/sourceforge/jdpapi/DPAPIException.java b/rdptransport/java/src/net/sourceforge/jdpapi/DPAPIException.java new file mode 100644 index 000000000..6bc5019a0 --- /dev/null +++ b/rdptransport/java/src/net/sourceforge/jdpapi/DPAPIException.java @@ -0,0 +1,28 @@ +package net.sourceforge.jdpapi; + +/** + * Exception that can be thrown from the native DPAPI + * + * @author Kevin Conaway + */ +public class DPAPIException extends RuntimeException { + + private static final long serialVersionUID = 1l; + + public DPAPIException() { + super(); + } + + public DPAPIException(String message, Throwable cause) { + super(message, cause); + } + + public DPAPIException(String message) { + super(message); + } + + public DPAPIException(Throwable cause) { + super(cause); + } + +} \ No newline at end of file diff --git a/rdptransport/java/src/net/sourceforge/jdpapi/DataProtector.java b/rdptransport/java/src/net/sourceforge/jdpapi/DataProtector.java new file mode 100644 index 000000000..7aed18083 --- /dev/null +++ b/rdptransport/java/src/net/sourceforge/jdpapi/DataProtector.java @@ -0,0 +1,75 @@ +package net.sourceforge.jdpapi; + +/** + *

An interface to the Microsoft Data Protection API (DPAPI).

+ * + *

See MSDN for more information

+ * + * @author Kevin Conaway + */ +public class DataProtector { + + private final byte [] entropy; + private final boolean localMachine; + + /** + * @param entropy Additional entropy to include when encrypting (optional) + * @param localMachine If true, allow all users on the machine to decrypt a given ciphertext + * + */ + public DataProtector(byte[] entropy, boolean localMachine) { + this.entropy = entropy; + this.localMachine = localMachine; + } + + /** + * @param entropy + * @see #DataProtector(byte[], boolean) + */ + public DataProtector(byte[] entropy) { + this(entropy, false); + } + + /** + * @param localMachine + * @see #DataProtector(byte[], boolean) + */ + public DataProtector(boolean localMachine) { + this(null, localMachine); + } + + /** + * Initializes the protector with no additional entropy + * and {@code localMachine} set to false + * @see #DataProtector(byte[], boolean) + */ + public DataProtector() { + this(false); + } + + /** + *

Protect {@code input} using the Microsoft DPAPI CryptProtectData function.

+ * + *

See the CryptProtectData + * documentation for more details

+ * + * @param input Plaintext to encrypt + * @return ciphertext + */ + public byte[] protect(String input) { + return DPAPI.CryptProtectData(input, entropy, localMachine); + } + + /** + *

Unprotect {@code input} using the Microsoft DPAPI CryptUnprotectData function.

+ * + *

See the CryptUnprotectData + * documentation for more details

+ * + * @param input Ciphertext + * @return Plaintext + */ + public String unprotect(byte [] input) { + return DPAPI.CryptUnprotectData(input, entropy); + } +} \ No newline at end of file diff --git a/rdptransport/rdppass.sln b/rdptransport/rdppass.sln new file mode 100644 index 000000000..a2057bce4 --- /dev/null +++ b/rdptransport/rdppass.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rdppass", "rdppass\rdppass.vcxproj", "{93D07375-5BA0-4EE5-BE8F-68B0DF187BFF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testPass", "testPass\testPass.vcxproj", "{7616C72B-2571-4DF9-8BF7-8B32359CE489}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93D07375-5BA0-4EE5-BE8F-68B0DF187BFF}.Debug|Win32.ActiveCfg = Debug|Win32 + {93D07375-5BA0-4EE5-BE8F-68B0DF187BFF}.Debug|Win32.Build.0 = Debug|Win32 + {93D07375-5BA0-4EE5-BE8F-68B0DF187BFF}.Release|Win32.ActiveCfg = Release|Win32 + {93D07375-5BA0-4EE5-BE8F-68B0DF187BFF}.Release|Win32.Build.0 = Release|Win32 + {7616C72B-2571-4DF9-8BF7-8B32359CE489}.Debug|Win32.ActiveCfg = Debug|Win32 + {7616C72B-2571-4DF9-8BF7-8B32359CE489}.Debug|Win32.Build.0 = Debug|Win32 + {7616C72B-2571-4DF9-8BF7-8B32359CE489}.Release|Win32.ActiveCfg = Release|Win32 + {7616C72B-2571-4DF9-8BF7-8B32359CE489}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rdptransport/rdppass.suo b/rdptransport/rdppass.suo new file mode 100644 index 0000000000000000000000000000000000000000..f282c90e8e1acb108611d8d2ba61b14bcbb4056d GIT binary patch literal 15872 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 RZ)Tg}-#8Yxt{WSH{{dPGzX$*T literal 0 HcmV?d00001 diff --git a/rdptransport/rdppass/ReadMe.txt b/rdptransport/rdppass/ReadMe.txt new file mode 100644 index 000000000..1068cbaaa --- /dev/null +++ b/rdptransport/rdppass/ReadMe.txt @@ -0,0 +1,54 @@ +======================================================================== + BIBLIOTECA DE VÍNCULOS DINÁMICOS: rdppass + Información general del proyecto +======================================================================== + +AppWizard ha creado este archivo DLL rdppass. + +Este archivo incluye un resumen acerca del contenido de los archivos que +constituyen su aplicación rdppass. + + +rdppass.vcxproj + Éste es el archivo de proyecto principal para los proyectos de VC++ + generados mediante un Asistente para aplicaciones. + Contiene información acerca de la versión de Visual C++ con la que + se generó el archivo, así como información acerca de las plataformas, + configuraciones y características del proyecto seleccionadas en el + asistente para aplicaciones. + +rdppass.vcxproj.filters + Éste es el archivo de filtros para los proyectos de VC++ generados + mediante un asistente para aplicaciones. + Contiene información acerca de la asociación entre los archivos de + un proyecto y los filtros. Esta asociación se usa en el IDE para mostrar + la agrupación de archivos con extensiones similares bajo un nodo + específico (por ejemplo, los archivos ".cpp" se asocian con el filtro + "Archivos de código fuente"). + +rdppass.cpp + Éste es el archivo de código fuente DLL principal. + + Cuando se crea este archivo DLL, no exporta símbolos. Por lo tanto, no + creará un archivo .lib cuando se genere. Si desea que este proyecto sea + una dependencia de otro proyecto, es preciso agregar código para exportar + algunos símbolos del archivo DLL para crear una biblioteca de exportación, + o establecer la propiedad Omitir biblioteca de entrada en Sí en la página + de propiedades General en la carpeta Vinculador del cuadro de diálogo + Páginas de propiedades del proyecto. + +///////////////////////////////////////////////////////////////////////////// +Otros archivos estándar: + +StdAfx.h, StdAfx.cpp + Estos archivos se utilizan para crear un archivo de encabezado precompilado + (PCH) denominado rdppass.pch y un archivo de tipos + precompilado denominado StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Otras notas: + +El asistente para aplicaciones utiliza comentarios "TODO:" para indicar las +partes del código fuente que tendrá que agregar o personalizar. + +///////////////////////////////////////////////////////////////////////////// diff --git a/rdptransport/rdppass/dllmain.cpp b/rdptransport/rdppass/dllmain.cpp new file mode 100644 index 000000000..8e7951c07 --- /dev/null +++ b/rdptransport/rdppass/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Define el punto de entrada de la aplicacin DLL. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/rdptransport/rdppass/dpapi.cpp b/rdptransport/rdppass/dpapi.cpp new file mode 100644 index 000000000..6aecb6b83 --- /dev/null +++ b/rdptransport/rdppass/dpapi.cpp @@ -0,0 +1,133 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include "net_sourceforge_jdpapi_DPAPI.h" + +void throwByName(JNIEnv *env, const char *name, const char *msg) { + jclass cls = env->FindClass(name); + /* if cls is NULL, an exception has already been thrown */ + if (cls != NULL) { + env->ThrowNew(cls, msg); + } + /* free the local ref */ + env->DeleteLocalRef(cls); + } + +void throwLastError(JNIEnv *env) { + DWORD errorCode = GetLastError(); + LPVOID buf; + + int messageFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + + FormatMessage(messageFlags, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL); + + throwByName(env, "net/sourceforge/jdpapi/DPAPIException", (char *) buf); + LocalFree(buf); + } + +DWORD getFlags(jboolean useLocalMachine) { + DWORD flags = CRYPTPROTECT_UI_FORBIDDEN; + if (useLocalMachine) { + flags |= CRYPTPROTECT_LOCAL_MACHINE; + } + return flags; +} + +DATA_BLOB getBlobFromBytes(JNIEnv *env, jbyteArray bytes) { + DATA_BLOB result; + + if (bytes != NULL) { + result.pbData = (BYTE *) env->GetByteArrayElements(bytes, JNI_FALSE); + result.cbData = (DWORD) env->GetArrayLength(bytes); + } else { + result.pbData = NULL; + result.cbData = 0; + } + return result; +} + +void freeBytesFromBlob(JNIEnv *env, jbyteArray originalBytes, DATA_BLOB blob) { + if (originalBytes != NULL) { + env->ReleaseByteArrayElements(originalBytes, (jbyte*) blob.pbData, JNI_ABORT); + } +} + +jstring getJStringFromBlob(JNIEnv *env, DATA_BLOB blob) { + char * str = new char[blob.cbData + 1]; + memcpy(str, blob.pbData, blob.cbData); + str[blob.cbData] = NULL; + + jstring result = env->NewStringUTF(str); + + delete str; + LocalFree(blob.pbData); + + return result; +} + +jbyteArray getJByteArrayFromBlob(JNIEnv *env, DATA_BLOB blob) { + jbyteArray result = env->NewByteArray(blob.cbData); + env->SetByteArrayRegion(result, 0, blob.cbData, (jbyte *)blob.pbData); + + LocalFree(blob.pbData); + return result; +} + +DATA_BLOB getBlobFromJString(JNIEnv *env, jstring str) { + DATA_BLOB result; + + const jchar *nativeString = env->GetStringChars(str, 0); + jsize len = env->GetStringLength(str); + result.pbData = (BYTE *) nativeString; + result.cbData = (DWORD)len*sizeof(wchar_t); + + return result; +} + +void freeJStringFromBlob(JNIEnv *env, jstring original, DATA_BLOB blob) { + env->ReleaseStringChars(original, (const jchar *) blob.pbData); +} + +JNIEXPORT jbyteArray JNICALL Java_net_sourceforge_jdpapi_DPAPI_CryptProtectData + (JNIEnv *env, jclass clazz, jstring key, jbyteArray entropyBytes, jboolean useLocalMachine) { + + DATA_BLOB output; + DATA_BLOB input = getBlobFromJString(env, key); + DATA_BLOB entropy = getBlobFromBytes(env, entropyBytes); + +// BOOL completed = CryptProtectData(&input, L"psw", &entropy, NULL, NULL, getFlags(useLocalMachine), &output); + BOOL completed = CryptProtectData(&input, L"psw", NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &output); + + freeBytesFromBlob(env, entropyBytes, entropy); + freeJStringFromBlob(env, key, input); + + if (!completed) { + throwLastError(env); + return NULL; + } + + return getJByteArrayFromBlob(env, output); +} + + +JNIEXPORT jstring JNICALL Java_net_sourceforge_jdpapi_DPAPI_CryptUnprotectData + (JNIEnv *env, jclass clazz, jbyteArray data, jbyteArray entropyBytes) { + + DATA_BLOB output; + DATA_BLOB input = getBlobFromBytes(env, data); + DATA_BLOB entropy = getBlobFromBytes(env, entropyBytes); + + BOOL completed = CryptUnprotectData(&input, (LPWSTR *) NULL, &entropy, NULL, NULL, getFlags(JNI_FALSE), &output); + + freeBytesFromBlob(env, entropyBytes, entropy); + freeBytesFromBlob(env, data, input); + + if (!completed) { + throwLastError(env); + return NULL; + } + + return getJStringFromBlob(env, output); +} \ No newline at end of file diff --git a/rdptransport/rdppass/net_sourceforge_jdpapi_DPAPI.h b/rdptransport/rdppass/net_sourceforge_jdpapi_DPAPI.h new file mode 100644 index 000000000..c36ca4dca --- /dev/null +++ b/rdptransport/rdppass/net_sourceforge_jdpapi_DPAPI.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class net_sourceforge_jdpapi_DPAPI */ + +#ifndef _Included_net_sourceforge_jdpapi_DPAPI +#define _Included_net_sourceforge_jdpapi_DPAPI +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: net_sourceforge_jdpapi_DPAPI + * Method: CryptProtectData + * Signature: (Ljava/lang/String;[BZ)[B + */ +JNIEXPORT jbyteArray JNICALL Java_net_sourceforge_jdpapi_DPAPI_CryptProtectData + (JNIEnv *, jclass, jstring, jbyteArray, jboolean); + +/* + * Class: net_sourceforge_jdpapi_DPAPI + * Method: CryptUnprotectData + * Signature: ([B[B)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_net_sourceforge_jdpapi_DPAPI_CryptUnprotectData + (JNIEnv *, jclass, jbyteArray, jbyteArray); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/rdptransport/rdppass/rdppass.cpp b/rdptransport/rdppass/rdppass.cpp new file mode 100644 index 000000000..633780da3 --- /dev/null +++ b/rdptransport/rdppass/rdppass.cpp @@ -0,0 +1,6 @@ +// rdppass.cpp: define las funciones exportadas de la aplicacin DLL. +// + +#include "stdafx.h" + + diff --git a/rdptransport/rdppass/rdppass.vcxproj b/rdptransport/rdppass/rdppass.vcxproj new file mode 100644 index 000000000..41714d2a3 --- /dev/null +++ b/rdptransport/rdppass/rdppass.vcxproj @@ -0,0 +1,107 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {93D07375-5BA0-4EE5-BE8F-68B0DF187BFF} + Win32Proj + rdppass + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + Static + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;RDPPASS_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files %28x86%29\Java\jdk1.5.0_22\include;C:\Program Files %28x86%29\Java\jdk1.5.0_22\include\win32;%(AdditionalIncludeDirectories) + + + Windows + true + crypt32.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;RDPPASS_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files %28x86%29\Java\jdk1.5.0_22\include;C:\Program Files %28x86%29\Java\jdk1.5.0_22\include\win32;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + crypt32.lib;%(AdditionalDependencies) + LinkVerbose + + + + + + + + + + + + + false + + + false + + + + + + + Create + Create + + + + + + \ No newline at end of file diff --git a/rdptransport/rdppass/rdppass.vcxproj.filters b/rdptransport/rdppass/rdppass.vcxproj.filters new file mode 100644 index 000000000..915022aba --- /dev/null +++ b/rdptransport/rdppass/rdppass.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Archivos de encabezado + + + Archivos de encabezado + + + Archivos de encabezado + + + + + Archivos de código fuente + + + Archivos de código fuente + + + Archivos de código fuente + + + Archivos de código fuente + + + \ No newline at end of file diff --git a/rdptransport/rdppass/rdppass.vcxproj.user b/rdptransport/rdppass/rdppass.vcxproj.user new file mode 100644 index 000000000..695b5c78b --- /dev/null +++ b/rdptransport/rdppass/rdppass.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/rdptransport/rdppass/stdafx.cpp b/rdptransport/rdppass/stdafx.cpp new file mode 100644 index 000000000..4b41d38e8 --- /dev/null +++ b/rdptransport/rdppass/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp: archivo de cdigo fuente que contiene slo las inclusiones estndar +// rdppass.pch ser el encabezado precompilado +// stdafx.obj contiene la informacin de tipos precompilada + +#include "stdafx.h" + +// TODO: mencionar los encabezados adicionales que se necesitan en STDAFX.H +// pero no en este archivo diff --git a/rdptransport/rdppass/stdafx.h b/rdptransport/rdppass/stdafx.h new file mode 100644 index 000000000..355b4a8c3 --- /dev/null +++ b/rdptransport/rdppass/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h: archivo de inclusin de los archivos de inclusin estndar del sistema +// o archivos de inclusin especficos de un proyecto utilizados frecuentemente, +// pero rara vez modificados +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Excluir material rara vez utilizado de encabezados de Windows +// Archivos de encabezado de Windows: +#include + + + +// TODO: mencionar aqu los encabezados adicionales que el programa necesita diff --git a/rdptransport/rdppass/targetver.h b/rdptransport/rdppass/targetver.h new file mode 100644 index 000000000..da80cd8f3 --- /dev/null +++ b/rdptransport/rdppass/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// La inclusin de SDKDDKVer.h define la plataforma Windows ms alta disponible. + +// Si desea compilar la aplicacin para una plataforma Windows anterior, incluya WinSDKVer.h y +// establezca la macro _WIN32_WINNT en la plataforma que desea admitir antes de incluir SDKDDKVer.h. + +#include diff --git a/rdptransport/testPass/ReadMe.txt b/rdptransport/testPass/ReadMe.txt new file mode 100644 index 000000000..d53c3e4ba --- /dev/null +++ b/rdptransport/testPass/ReadMe.txt @@ -0,0 +1,46 @@ +======================================================================== + APLICACIÓN DE CONSOLA: testPass + Información general del proyecto +======================================================================== + +AppWizard ha creado esta aplicación testPass. + +Este archivo incluye un resumen acerca del contenido de los archivos que +constituyen su aplicación testPass. + + +testPass.vcxproj + Éste es el archivo de proyecto principal para los proyectos de VC++ + generados mediante un Asistente para aplicaciones. + Contiene información acerca de la versión de Visual C++ con la que + se generó el archivo, así como información acerca de las plataformas, + configuraciones y características del proyecto seleccionadas en el + asistente para aplicaciones. + +testPass.vcxproj.filters + Éste es el archivo de filtros para los proyectos de VC++ generados + mediante un asistente para aplicaciones. + Contiene información acerca de la asociación entre los archivos de + un proyecto y los filtros. Esta asociación se usa en el IDE para mostrar + la agrupación de archivos con extensiones similares bajo un nodo + específico (por ejemplo, los archivos ".cpp" se asocian con el filtro + "Archivos de código fuente"). + +testPass.cpp + Ésta es la aplicación principal del archivo de código fuente. + +///////////////////////////////////////////////////////////////////////////// +Otros archivos estándar: + +StdAfx.h, StdAfx.cpp + Estos archivos se utilizan para crear un archivo de encabezado precompilado + (PCH) denominado testPass.pch y un archivo de tipos + precompilado denominado StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Otras notas: + +El asistente para aplicaciones utiliza comentarios "TODO:" para indicar las +partes del código fuente que tendrá que agregar o personalizar. + +///////////////////////////////////////////////////////////////////////////// diff --git a/rdptransport/testPass/stdafx.cpp b/rdptransport/testPass/stdafx.cpp new file mode 100644 index 000000000..e2bbbec2b --- /dev/null +++ b/rdptransport/testPass/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp: archivo de cdigo fuente que contiene slo las inclusiones estndar +// testPass.pch ser el encabezado precompilado +// stdafx.obj contiene la informacin de tipos precompilada + +#include "stdafx.h" + +// TODO: mencionar los encabezados adicionales que se necesitan en STDAFX.H +// pero no en este archivo diff --git a/rdptransport/testPass/stdafx.h b/rdptransport/testPass/stdafx.h new file mode 100644 index 000000000..3e732adcf --- /dev/null +++ b/rdptransport/testPass/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h: archivo de inclusin de los archivos de inclusin estndar del sistema +// o archivos de inclusin especficos de un proyecto utilizados frecuentemente, +// pero rara vez modificados +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: mencionar aqu los encabezados adicionales que el programa necesita diff --git a/rdptransport/testPass/targetver.h b/rdptransport/testPass/targetver.h new file mode 100644 index 000000000..da80cd8f3 --- /dev/null +++ b/rdptransport/testPass/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// La inclusin de SDKDDKVer.h define la plataforma Windows ms alta disponible. + +// Si desea compilar la aplicacin para una plataforma Windows anterior, incluya WinSDKVer.h y +// establezca la macro _WIN32_WINNT en la plataforma que desea admitir antes de incluir SDKDDKVer.h. + +#include diff --git a/rdptransport/testPass/testPass.cpp b/rdptransport/testPass/testPass.cpp new file mode 100644 index 000000000..581787017 --- /dev/null +++ b/rdptransport/testPass/testPass.cpp @@ -0,0 +1,115 @@ +// testPass.cpp: define el punto de entrada de la aplicacin de consola. +// + +#include "stdafx.h" +#include +#include +#include + +void MyHandleError(char *s); + +char arr[] = { '0', '1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; + +void byteToHex(BYTE data) +{ + int n1 = data & 0x0f; + int n2 = (data>>4) & 0x0f; + printf("%c%c", arr[n2], arr[n1]); + +} + +void bytesToHex(BYTE* data, size_t len) +{ + for( int i = 0; i < len; i++ ) + byteToHex(data[i]); + printf("\n"); +} + +int _tmain(int argc, _TCHAR* argv[]) +{ + wchar_t* pass = L"temporal"; + printf("Size of wchar_t: %d\n", sizeof(wchar_t)); + DATA_BLOB DataIn; + DATA_BLOB DataOut; + DATA_BLOB DataVerify; + BYTE *pbDataInput =(BYTE *)pass; + DWORD cbDataInput = 16;//strlen((char *)pbDataInput)+1; + DataIn.pbData = pbDataInput; + DataIn.cbData = cbDataInput; + //------------------------------------------------------------------- + // Begin processing. + + printf("The data to be encrypted is: %s\n",pass); + + //------------------------------------------------------------------- + // Begin protect phase. + + if(CryptProtectData( + &DataIn, + L"psw", // A description string. + NULL, // Optional entropy + // not used. + NULL, // Reserved. + NULL, // Pass a PromptStruct. + CRYPTPROTECT_UI_FORBIDDEN, + &DataOut)) + { + printf("The encryption phase worked. \n"); + printf("Data len: %d\n", DataOut.cbData); + printf("01000000D08C9DDF0115D1118C7A00C04FC297EB01000000FE75A256A68D3C4C881421C753628A9800000000080000007000730077000000106600000001000020000000D2A595F857AC73031C75AA190BCEF52327E7FE2B51A35D6C6AC03703DE115714000000000E8000000002000020000000F1C8AC290D19EFE11EEAA10B6A9F933105ADC8C04526A139000E7F535F2FCF6E20000000104E256EF4ABD26A8A7F506938E2A23127D7F9EB1CFFEEB6356445043F1E02C140000000ACC013C6D269C09DEB9B951E952348C12A5259C7A475AF66ED3861D9F8D1D1057449761332A4B624905CD043FEEB45918E0AE26245801E5ED229C43DF7872876\n"); + bytesToHex(DataOut.pbData, DataOut.cbData); + } + else + { + MyHandleError("Encryption error!"); + } + //------------------------------------------------------------------- + // Begin unprotect phase. + + if (CryptUnprotectData( + &DataOut, + NULL, + NULL, // Optional entropy + NULL, // Reserved + NULL, // Optional PromptStruct + CRYPTPROTECT_UI_FORBIDDEN, + &DataVerify)) + { + printf("The decrypted data is: %s\n", DataVerify.pbData); + } + else + { + MyHandleError("Decryption error!"); + } + //------------------------------------------------------------------- + // At this point, memcmp could be used to compare DataIn.pbData and + // DataVerify.pbDate for equality. If the two functions worked + // correctly, the two byte strings are identical. + + //------------------------------------------------------------------- + // Clean up. + + LocalFree(DataOut.pbData); + LocalFree(DataVerify.pbData); + char c; + scanf("%c", &c); +} // End of main + +//------------------------------------------------------------------- +// This example uses the function MyHandleError, a simple error +// handling function, to print an error message to the +// standard error (stderr) file and exit the program. +// For most applications, replace this function with one +// that does more extensive error reporting. + +void MyHandleError(char *s) +{ + fprintf(stderr,"An error occurred in running the program. \n"); + fprintf(stderr,"%s\n",s); + fprintf(stderr, "Error number %x.\n", GetLastError()); + fprintf(stderr, "Program terminating. \n"); + exit(1); +} // End of MyHandleError + + + diff --git a/rdptransport/testPass/testPass.vcxproj b/rdptransport/testPass/testPass.vcxproj new file mode 100644 index 000000000..7c65e7530 --- /dev/null +++ b/rdptransport/testPass/testPass.vcxproj @@ -0,0 +1,93 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {7616C72B-2571-4DF9-8BF7-8B32359CE489} + Win32Proj + testPass + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + crypt32.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + crypt32.lib;%(AdditionalDependencies) + + + + + + + + + + + + Create + Create + + + + + + + \ No newline at end of file diff --git a/rdptransport/testPass/testPass.vcxproj.filters b/rdptransport/testPass/testPass.vcxproj.filters new file mode 100644 index 000000000..6ca300c9c --- /dev/null +++ b/rdptransport/testPass/testPass.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Archivos de encabezado + + + Archivos de encabezado + + + + + Archivos de código fuente + + + Archivos de código fuente + + + \ No newline at end of file diff --git a/rdptransport/testPass/testPass.vcxproj.user b/rdptransport/testPass/testPass.vcxproj.user new file mode 100644 index 000000000..695b5c78b --- /dev/null +++ b/rdptransport/testPass/testPass.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/server/.project b/server/.project new file mode 100644 index 000000000..c72ed172e --- /dev/null +++ b/server/.project @@ -0,0 +1,18 @@ + + + uds + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + org.python.pydev.django.djangoNature + + diff --git a/server/.pydevproject b/server/.pydevproject new file mode 100644 index 000000000..f0c43afa2 --- /dev/null +++ b/server/.pydevproject @@ -0,0 +1,16 @@ + + + + +python2.7 +python 2.7 + +DJANGO_MANAGE_LOCATION +src/manage.py +DJANGO_SETTINGS_MODULE +server.settings + + +/uds/src + + diff --git a/server/.settings/org.eclipse.core.resources.prefs b/server/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..aee1c1056 --- /dev/null +++ b/server/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,168 @@ +eclipse.preferences.version=1 +encoding//documentation/_downloads/samples/services/SampleProvider.py=utf-8 +encoding//documentation/_downloads/samples/services/SamplePublication.py=utf-8 +encoding//documentation/_downloads/samples/services/SampleService.py=utf-8 +encoding//documentation/_downloads/samples/services/SampleUserDeploymentOne.py=utf-8 +encoding//documentation/_downloads/samples/services/SampleUserDeploymentTwo.py=utf-8 +encoding//documentation/_downloads/samples/services/__init__.py=utf-8 +encoding//documentation/conf.py=utf-8 +encoding//src/server/settings.py=utf-8 +encoding//src/server/urls.py=utf-8 +encoding//src/uds/__init__.py=utf-8 +encoding//src/uds/auths/ActiveDirectory/Authenticator.py=utf-8 +encoding//src/uds/auths/ActiveDirectory/__init__.py=utf-8 +encoding//src/uds/auths/Dummy/Authenticator.py=utf-8 +encoding//src/uds/auths/Dummy/__init__.py=utf-8 +encoding//src/uds/auths/EDirectory/Authenticator.py=utf-8 +encoding//src/uds/auths/EDirectory/__init__.py=utf-8 +encoding//src/uds/auths/IP/Authenticator.py=utf-8 +encoding//src/uds/auths/IP/__init__.py=utf-8 +encoding//src/uds/auths/InternalDB/Authenticator.py=utf-8 +encoding//src/uds/auths/InternalDB/__init__.py=utf-8 +encoding//src/uds/auths/RegexLdap/Authenticator.py=utf-8 +encoding//src/uds/auths/RegexLdap/__init__.py=utf-8 +encoding//src/uds/auths/SAML/SAML.py=utf-8 +encoding//src/uds/auths/SAML/__init__.py=utf-8 +encoding//src/uds/auths/Sample/SampleAuth.py=utf-8 +encoding//src/uds/auths/Sample/__init__.py=utf-8 +encoding//src/uds/auths/SimpleLDAP/Authenticator.py=utf-8 +encoding//src/uds/auths/SimpleLDAP/__init__.py=utf-8 +encoding//src/uds/auths/__init__.py=utf-8 +encoding//src/uds/core/BaseModule.py=utf-8 +encoding//src/uds/core/Environment.py=utf-8 +encoding//src/uds/core/Serializable.py=utf-8 +encoding//src/uds/core/__init__.py=utf-8 +encoding//src/uds/core/auths/AuthsFactory.py=utf-8 +encoding//src/uds/core/auths/BaseAuthenticator.py=utf-8 +encoding//src/uds/core/auths/Exceptions.py=utf-8 +encoding//src/uds/core/auths/Group.py=utf-8 +encoding//src/uds/core/auths/GroupsManager.py=utf-8 +encoding//src/uds/core/auths/User.py=utf-8 +encoding//src/uds/core/auths/__init__.py=utf-8 +encoding//src/uds/core/auths/auth.py=utf-8 +encoding//src/uds/core/jobs/DelayedTask.py=utf-8 +encoding//src/uds/core/jobs/DelayedTaskRunner.py=utf-8 +encoding//src/uds/core/jobs/Job.py=utf-8 +encoding//src/uds/core/jobs/JobsFactory.py=utf-8 +encoding//src/uds/core/jobs/Scheduler.py=utf-8 +encoding//src/uds/core/managers/CryptoManager.py=utf-8 +encoding//src/uds/core/managers/DownloadsManager.py=utf-8 +encoding//src/uds/core/managers/PublicationManager.py=utf-8 +encoding//src/uds/core/managers/TaskManager.py=utf-8 +encoding//src/uds/core/managers/UserPrefsManager.py=utf-8 +encoding//src/uds/core/managers/UserServiceManager.py=utf-8 +encoding//src/uds/core/osmanagers/BaseOsManager.py=utf-8 +encoding//src/uds/core/osmanagers/OSManagersFactory.py=utf-8 +encoding//src/uds/core/services/BaseDeployed.py=utf-8 +encoding//src/uds/core/services/BasePublication.py=utf-8 +encoding//src/uds/core/services/BaseService.py=utf-8 +encoding//src/uds/core/services/BaseServiceProvider.py=utf-8 +encoding//src/uds/core/services/Exceptions.py=utf-8 +encoding//src/uds/core/services/ServiceProviderFactory.py=utf-8 +encoding//src/uds/core/services/__init__.py=utf-8 +encoding//src/uds/core/transports/BaseTransport.py=utf-8 +encoding//src/uds/core/transports/TransportsFactory.py=utf-8 +encoding//src/uds/core/ui/UserInterface.py=utf-8 +encoding//src/uds/core/util/AutoAttributes.py=utf-8 +encoding//src/uds/core/util/Cache.py=utf-8 +encoding//src/uds/core/util/Config.py=utf-8 +encoding//src/uds/core/util/Log.py=utf-8 +encoding//src/uds/core/util/OsDetector.py=utf-8 +encoding//src/uds/core/util/State.py=utf-8 +encoding//src/uds/core/util/StateQueue.py=utf-8 +encoding//src/uds/core/util/Storage.py=utf-8 +encoding//src/uds/core/util/UniqueIDGenerator.py=utf-8 +encoding//src/uds/core/util/UniqueMacGenerator.py=utf-8 +encoding//src/uds/core/util/UniqueNameGenerator.py=utf-8 +encoding//src/uds/core/util/connection.py=utf-8 +encoding//src/uds/core/util/modfinder.py=utf-8 +encoding//src/uds/core/workers/AssignedAndUnused.py=utf-8 +encoding//src/uds/core/workers/CacheCleaner.py=utf-8 +encoding//src/uds/core/workers/DeployedServiceCleaner.py=utf-8 +encoding//src/uds/core/workers/PublicationCleaner.py=utf-8 +encoding//src/uds/core/workers/ServiceCacheUpdater.py=utf-8 +encoding//src/uds/core/workers/UserServiceCleaner.py=utf-8 +encoding//src/uds/dispatchers/pam/urls.py=utf-8 +encoding//src/uds/dispatchers/pam/views.py=utf-8 +encoding//src/uds/dispatchers/test/urls.py=utf-8 +encoding//src/uds/dispatchers/test/views.py=utf-8 +encoding//src/uds/management/commands/config.py=utf-8 +encoding//src/uds/management/commands/taskManager.py=utf-8 +encoding//src/uds/migrations/0001_initial.py=utf-8 +encoding//src/uds/migrations/0002_auto__del_unique_userpreference_name_module.py=utf-8 +encoding//src/uds/migrations/0004_auto__add_field_deployedservice_state_date.py=utf-8 +encoding//src/uds/migrations/0005_auto__add_field_config_crypt.py=utf-8 +encoding//src/uds/models.py=utf-8 +encoding//src/uds/osmanagers/LinuxOsManager/LinuxOsManager.py=utf-8 +encoding//src/uds/osmanagers/LinuxOsManager/__init__.py=utf-8 +encoding//src/uds/osmanagers/NoneOsManager/Manager.py=utf-8 +encoding//src/uds/osmanagers/NoneOsManager/__init__.py=utf-8 +encoding//src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py=utf-8 +encoding//src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py=utf-8 +encoding//src/uds/osmanagers/WindowsOsManager/__init__.py=utf-8 +encoding//src/uds/osmanagers/__init__.py=utf-8 +encoding//src/uds/services/PhysicalMachines/IPMachineDeployed.py=utf-8 +encoding//src/uds/services/PhysicalMachines/IPMachinesService.py=utf-8 +encoding//src/uds/services/PhysicalMachines/ServiceProvider.py=utf-8 +encoding//src/uds/services/PhysicalMachines/__init__.py=utf-8 +encoding//src/uds/services/Sample/SampleProvider.py=utf-8 +encoding//src/uds/services/Sample/SamplePublication.py=utf-8 +encoding//src/uds/services/Sample/SampleService.py=utf-8 +encoding//src/uds/services/Sample/SampleUserDeploymentOne.py=utf-8 +encoding//src/uds/services/Sample/SampleUserDeploymentTwo.py=utf-8 +encoding//src/uds/services/Sample/__init__.py=utf-8 +encoding//src/uds/services/Vmware/Helpers.py=utf-8 +encoding//src/uds/services/Vmware/PublicationVC.py=utf-8 +encoding//src/uds/services/Vmware/ServiceProviderVC.py=utf-8 +encoding//src/uds/services/Vmware/VCLinkedCloneDeployed.py=utf-8 +encoding//src/uds/services/Vmware/VCLinkedCloneService.py=utf-8 +encoding//src/uds/services/Vmware/__init__.py=utf-8 +encoding//src/uds/services/Vmware/client/Client.py=utf-8 +encoding//src/uds/services/Vmware/client/Exceptions.py=utf-8 +encoding//src/uds/services/Vmware/client/Server.py=utf-8 +encoding//src/uds/services/Vmware/client/Task.py=utf-8 +encoding//src/uds/services/Vmware/client/ws/VimService_types.py=utf-8 +encoding//src/uds/services/__init__.py=utf-8 +encoding//src/uds/tests.py=utf-8 +encoding//src/uds/transports/NX/NXTransport.py=utf-8 +encoding//src/uds/transports/NX/__init__.py=utf-8 +encoding//src/uds/transports/NX/web.py=utf-8 +encoding//src/uds/transports/RDP/RDPTransport.py=utf-8 +encoding//src/uds/transports/RDP/TSRDPTransport.py=utf-8 +encoding//src/uds/transports/RDP/__init__.py=utf-8 +encoding//src/uds/transports/RDP/web.py=utf-8 +encoding//src/uds/transports/RGS/RGSTransport.py=utf-8 +encoding//src/uds/transports/RGS/TRGSTransport.py=utf-8 +encoding//src/uds/transports/RGS/__init__.py=utf-8 +encoding//src/uds/transports/RGS/web.py=utf-8 +encoding//src/uds/transports/TSNX/TSNXTransport.py=utf-8 +encoding//src/uds/transports/TSNX/__init__.py=utf-8 +encoding//src/uds/transports/TSNX/web.py=utf-8 +encoding//src/uds/transports/__init__.py=utf-8 +encoding//src/uds/urls.py=utf-8 +encoding//src/uds/views.py=utf-8 +encoding//src/uds/web/errors.py=utf-8 +encoding//src/uds/web/forms/LoginForm.py=utf-8 +encoding//src/uds/web/transformers.py=utf-8 +encoding//src/uds/web/views.py=utf-8 +encoding//src/uds/xmlrpc/actor/Actor.py=utf-8 +encoding//src/uds/xmlrpc/auths/AdminAuth.py=utf-8 +encoding//src/uds/xmlrpc/auths/Authenticators.py=utf-8 +encoding//src/uds/xmlrpc/auths/Groups.py=utf-8 +encoding//src/uds/xmlrpc/auths/UserPreferences.py=utf-8 +encoding//src/uds/xmlrpc/auths/Users.py=utf-8 +encoding//src/uds/xmlrpc/osmanagers/OSManagers.py=utf-8 +encoding//src/uds/xmlrpc/services/DeployedServices.py=utf-8 +encoding//src/uds/xmlrpc/services/Publications.py=utf-8 +encoding//src/uds/xmlrpc/services/ServiceProviders.py=utf-8 +encoding//src/uds/xmlrpc/services/Services.py=utf-8 +encoding//src/uds/xmlrpc/services/UserDeployedServices.py=utf-8 +encoding//src/uds/xmlrpc/tools/Cache.py=utf-8 +encoding//src/uds/xmlrpc/tools/Config.py=utf-8 +encoding//src/uds/xmlrpc/transports/Networks.py=utf-8 +encoding//src/uds/xmlrpc/transports/Transports.py=utf-8 +encoding//src/uds/xmlrpc/util/Callbacks.py=utf-8 +encoding//src/uds/xmlrpc/util/Exceptions.py=utf-8 +encoding//src/uds/xmlrpc/util/Helpers.py=utf-8 +encoding//src/uds/xmlrpc/util/TestTransport.py=utf-8 +encoding//src/uds/xmlrpc/views.py=utf-8 diff --git a/server/.settings/org.eclipse.ltk.core.refactoring.prefs b/server/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 000000000..6599c0710 --- /dev/null +++ b/server/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,3 @@ +#Wed Jun 15 12:44:29 CEST 2011 +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/server/.settings/org.eclipse.mylyn.tasks.ui.prefs b/server/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 000000000..0f638bd2c --- /dev/null +++ b/server/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Tue Jan 03 02:43:56 CET 2012 +eclipse.preferences.version=1 +project.repository.kind=mantis +project.repository.url=http\://192.168.0.6/mantis diff --git a/server/.settings/org.eclipse.wst.css.core.prefs b/server/.settings/org.eclipse.wst.css.core.prefs new file mode 100644 index 000000000..aaad38b58 --- /dev/null +++ b/server/.settings/org.eclipse.wst.css.core.prefs @@ -0,0 +1,3 @@ +#Thu Jan 19 07:33:49 CET 2012 +css-profile//src/uds/templates/uds/base.html=org.eclipse.wst.css.core.cssprofile.css3 +eclipse.preferences.version=1 diff --git a/server/.settings/org.eclipse.wst.html.core.prefs b/server/.settings/org.eclipse.wst.html.core.prefs new file mode 100644 index 000000000..86d79db53 --- /dev/null +++ b/server/.settings/org.eclipse.wst.html.core.prefs @@ -0,0 +1,3 @@ +#Thu Jan 19 07:33:49 CET 2012 +document-type//src/uds/templates/uds/base.html=-//W3C//DTD XHTML 1.0 Transitional//EN +eclipse.preferences.version=1 diff --git a/server/documentation/Makefile b/server/documentation/Makefile new file mode 100644 index 000000000..8cbfba742 --- /dev/null +++ b/server/documentation/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = a4 +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/UDS.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/UDS.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/UDS" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/UDS" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/server/documentation/_downloads/samples/auths/SampleAuth.py b/server/documentation/_downloads/samples/auths/SampleAuth.py new file mode 100644 index 000000000..7b3699b8f --- /dev/null +++ b/server/documentation/_downloads/samples/auths/SampleAuth.py @@ -0,0 +1,307 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.utils.translation import ugettext_noop as translatable +from uds.core.ui.UserInterface import gui +from uds.core import auths + +import logging + +logger = logging.getLogger(__name__) + +class SampleAuth(auths.Authenticator): + ''' + This class represents a sample authenticator. + + As this, it will provide: + * The authenticator functionality + * 3 Groups, "Mortals", "Gods" and "Daemons", just random group names selected.. :-), + plus groups that we enter at Authenticator form, from admin interface. + * Search of groups (inside the 3 groups used in this sample plus entered) + * Search for people (will return the search string + 000...999 as usernames) + * The Required form description for administration interface, so admins can create + new authenticators of this kind. + + In this sample, we will provide a simple standard auth, with owner drawn + login form that will simply show users that has been created and allow web user + to select one of them. + + For this class to get visible at administration client as a authenticator type, + we MUST register it at package __init__ + + :note: At class level, the translations must be simply marked as so + using ugettext_noop. This is done in this way because we will translate + the string when it is sent to the administration client. + ''' + + #: Name of type, used at administration interface to identify this + #: authenticator (i.e. LDAP, SAML, ...) + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeName = translatable('Sample Authenticator') + + #: Name of type used by Managers to identify this type of service + #: We could have used here the Class name, but we decided that the + #: module implementator will be the one that will provide a name that + #: will relation the class (type) and that name. + typeType = 'SampleAuthenticator' + + #: Description shown at administration level for this authenticator. + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeDescription = translatable('Sample dummy authenticator') + + + #: Icon file, used to represent this authenticator at administration interface + #: This file should be at same folder as this class is, except if you provide + #: your own :py:meth:uds.core.BaseModule.BaseModule.icon method. + iconFile = 'auth.png' + + #: Mark this authenticator as that the users comes from outside the UDS + #: database, that are most authenticator (except Internal DB) + #: True is the default value, so we do not need it in fact + # isExternalSource = True + + #: If we need to enter the password for this user when creating a new + #: user at administration interface. Used basically by internal authenticator. + #: False is the default value, so this is not needed in fact + #: needsPassword = False + + #: Label for username field, shown at administration interface user form. + userNameLabel = translatable('Fake User') + + # Label for group field, shown at administration interface user form. + groupNameLabel = translatable('Fake Group') + + #: Definition of this type of authenticator form + #: We will define a simple form where we will use a simple + #: list editor to allow entering a few group names + + groups = gui.EditableList(label=translatable('Groups'), values = ['Gods', 'Daemons', 'Mortals']) + + def initialize(self, values): + ''' + Simply check if we have + at least one group in the list + ''' + + # To avoid problems, we only check data if values are passed + # If values are not passed in, form data will only be available after + # unserialization, and at this point all will be default values + # so self.groups.value will be [] + if values is not None and len(self.groups.value) < 2: + raise auths.Authenticator.ValidationException(translatable('We need more that two items!')) + + def searchUsers(self, pattern): + ''' + Here we will receive a pattern for searching users. + + This method is invoked from interface, so an administrator can search users. + + If we do not provide this method, the authenticator will not provide search + facility for users. In our case, we will simply return a list of users + (array of dictionaries with ids and names) with the pattern plus 1..10 + ''' + return [ { 'id' : '{0}-{1}'.format(pattern, a), 'name' : '{0} number {1}'.format(pattern, a) } for a in range(1, 10)] + + def searchGroups(self, pattern): + ''' + Here we we will receive a patter for searching groups. + + In this sample, we will try to locate elements that where entered at + sample authenticator form (when created), and return the ones that + contains the pattern indicated. + ''' + pattern = pattern.lower() + res = [] + for g in self.groups.value: + if g.lower().find(pattern) != -1: + res.append({'id' : g, 'name' : ''}) + return res + + def authenticate(self, username, credentials, groupsManager): + ''' + This method is invoked by UDS whenever it needs an user to be authenticated. + It is used from web interface, but also from administration interface to + check credentials and access of user. + + The tricky part of this method is the groupsManager, but it's easy to + understand what is used it for. + + Imagine some authenticator, for example, an LDAP. It has its users, it has + its groups, and it has it relations (which user belongs to which group). + + Now think about UDS. UDS know nothing about this, it only knows what + the administator has entered at admin interface (groups mainly, but he can + create users also). + + UDS knows about this groups, but we need to relation those with the ones + know by the authenticator. + + To do this, we have created a simple mechanism, where the authenticator + receives a groupsManager, that knows all groups known by UDS, and has + the method so the authenticator can say, for the username being validated, + to which uds groups it belongs to. + + This is done using the :py:meth:uds.core.auths.GroupsManager.GroupsManager.validate + method of the provided groups manager. + + At return, UDS will do two things: + * If there is no group inside the groupsManager mareked as valid, it will + denied access. + * If there is some groups marked as valid, it will refresh the known + UDS relations (this means that the database will be refresehd so the user + has valid groups). + + This also means that the group membership is only checked at user login (well, + in fact its also checked when an administrator tries to modify an user) + + So, authenticate must not also validate the user credentials, but also + indicate the group membership of this user inside UDS. + + :note: groupsManager is an in/out parameter + ''' + if username != credentials: # All users with same username and password are allowed + return False + + # Now the tricky part. We will make this user belong to groups that contains at leat + # two letters equals to the groups names known by UDS + # For this, we will ask the groups manager for the groups names, and will check that and, + # if the user match this criteria, will mark that group as valid + for g in groupsManager.getGroupsNames(): + if len(set(g.lower()).intersection(username.lower())) >= 2: + groupsManager.validate(g) + + return True + + def getGroups(self, username, groupsManager): + ''' + As with authenticator part related to groupsManager, this + method will fill the groups to which the specified username belongs to. + + We have to fill up groupsManager from two different places, so it's not + a bad idea to make a method that get the "real" authenticator groups and + them simply call to :py:meth:uds.core.auths.GroupsManager.GroupsManager.validate + + In our case, we simply repeat the process that we also do at authenticate + ''' + for g in groupsManager.getGroupsNames(): + if len(set(g.lower()).intersection(username.lower())) >= 2: + groupsManager.validate(g) + + def getHtml(self, request): + ''' + If we override this method from the base one, we are telling UDS + that we want to draw our own authenticator. + + This way, we can do whataver we want here (for example redirect to a site + for a single sign on) generation our ouwn html (and javascript ofc). + + ''' + # Here there is a sample, commented out + # In this sample, we will make a list of valid users, and when clicked, + # it will fill up original form with username and same password, and submit it. + #res = '' + #for u in self.dbAuthenticator().users.all(): + # res += '{0}
'.format(u.name) + # + #res += '' + #return res + + # I know, this is a bit ugly, but this is just a sample :-) + + res = '

Login name:

' + res +='

Login

' + return res + + + def authCallback(self, parameters): + ''' + We provide this as a sample of callback for an user. + We will accept all petitions that has "user" parameter + + This method will get invoked by url redirections, probably by an SSO. + + The idea behind this is that we can provide: + * Simple user/password authentications + * Own authentications (not UDS, authenticator "owned"), but with no redirections + * Own authentications via redirections (as most SSO will do) + + Here, we will receive the parameters for this + ''' + user = parameters.get('user', None) + + return user + + def createUser(self, usrData): + ''' + This method provides a "check oportunity" to authenticators for users created + manually at administration interface. + + If we do not provide this method, the administration interface will not allow + to create new users "by hand", i mean, the "new" options from menus will dissapear. + + usrData is a dictionary that contains the input parameters from user, + with at least name, realName, comments, state & password. + + We can modify this parameters, we can modify ALL, but name is not recommended to + modify it unles you know what you are doing. + + Here, we will set the state to "Inactive" and realName to the same as username, but twice :-) + ''' + from uds.core.util.State import State + usrData['realName'] = usrData['name'] + ' ' + usrData['name'] + usrData['state'] = State.INACTIVE + + def modifyUser(self, usrData): + ''' + This method provides a "check opportunity" to authenticator for users modified + at administration interface. + + If we do not provide this method, nothing will happen (default one does nothing, but + it's valid). + + usrData is a dictionary that contains the input parameters from user, + with at least name, realName, comments, state & password. + + We can modify this parameters, we can modify ALL, but name is not recommended to + modify it unless you know what you are doing. + + Here, we will simply update the realName of the user, and (we have to take care + this this kind of things) modify the userName to a new one, the original plus '-1' + ''' + usrData['realName'] = usrData['name'] + ' ' + usrData['name'] + usrData['name'] = usrData['name'] + '-1' diff --git a/server/documentation/_downloads/samples/auths/__init__.py b/server/documentation/_downloads/samples/auths/__init__.py new file mode 100644 index 000000000..548fde418 --- /dev/null +++ b/server/documentation/_downloads/samples/auths/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' +Dummy test authenticator. Not used in production (it doesn't registers itself) + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from uds.core import auths +from SampleAuth import SampleAuth + +# Commented, this auth exists for testing purposes only +auths.factory().insert(SampleAuth) diff --git a/server/documentation/_downloads/samples/auths/auth.png b/server/documentation/_downloads/samples/auths/auth.png new file mode 100644 index 0000000000000000000000000000000000000000..c8560b1d52b07059aa5066b274114304e7807b91 GIT binary patch literal 1225 zcmY+EeNa?Y7{=doKbB=-S=e0^S3WitcYsBZufPml3J@r9F)?bi3CA>xX;O=r9K$HT zs;SImBypN%imWuX5o|Qc6&;;S16KqoS{G>%WS9NEyLa#1(*-N+kN2E;|2V&So@d_o z>@id=l}c0+0FYLcm#h%(=&2Hmgfq#F>j0>aD@ux2Hnvg5>?pk+N{vk3yCvVXytsd> z?CIB3yL!e~o$cM0+p=x+oBgG=m(J%Wl)qChHz>z;I))Z}?m1RJU(5+Cj45NaWVwkLDduz`3W);4A*J&K4>D!qK+hfh4Wo%g2m>LHtBF)a0rs5^5*V;Q6hah5 zLuo|hgQ%d;goI*v4pUT^T<+`EV$0D9m(^_x0YS&=k|8-?ciTPMf-!4LC&Q>1U@^W%8yu^UW-~Ds_R?W95Z_YJ6 z`_}RsDOt(6mr)R|BVLH*->ito0mx-Szr=2@{j0N6#Rfz8NwuOT4-E%-M1cLe#bhzJ zg~gZ2YdAmVJ4`>SCe&lKfiw&U<2~dIii)RcEF}mC^LmF_g20Zkdwg5{79b0ER7I;4 z;IevkB5C2i#-@ECB+O2Pg2ar7A13AjjEQ(5XhGFl`5j9jOP6{%>=^bmxhxO$}vQt*9a9m1|8$1}p#IB0tD!Uw!1K;rOOo7v`LL_2uI^?yfpc_JAY- zh)I?WDN|F`LB@wUnG)NURzj3+A&%iWJ_Hy%?uCR)vKonri`b3qI6)n&9-J6A>wJR! znV_)XXjXRi=D5_fPC+~s@OWWXf(p$eoe0UKltdCksUEsVB+iv1m)8p+F-xUrRGrxg zshimh@qKsG^NBjk%GN7Ys#Z^LFDNrIut+WkV%&lU?lO~a;M#*OQGiu0`)H#^6RwC1 zk459mDD+u|&?B>p;OfnR4Ku3HS6b~^iO*M*l%F4pNl&N3laffskQtcXE5YBaoM?yP zPf*Nf*^IbMS{s`TW~Z6&vkr27H!XzE8tR@#O>OP=1!6e&rFi9)5$nj>r2b33?34qk zhrOBA*DfUcPj5L;d|1@p`_wbPpO6i{6un{Nf^BQEq-T~p8f#mc^ECX7B8vC>E4@zV zBB|Z3psiLX&oKSMQVem74;X(Cyi5?t2;ZRzpY4#1v1@D|N7|&@DRVkKHh%zL{6~>7 zX1h8PjPF= 5: + return State.FINISHED + + # random fail + if random.randint(0, 9) == 9: + self.storage().saveData('error', 'Random error at checkState :-)') + return State.ERROR + + self.storage().saveData('count', str(count)) + return State.RUNNING + + def finish(self): + ''' + Invoked when the core notices that the deployment of a service has finished. + (No matter wether it is for cache or for an user) + + This gives the oportunity to make something at that moment. + :note: You can also make these operations at checkState, this is really + not needed, but can be provided (default implementation of base class does + nothing) + ''' + # Note that this is not really needed, is just a sample of storage use + self.storage().remove('count') + + def assignToUser(self, user): + ''' + This method is invoked whenever a cache item gets assigned to an user. + This gives the User Deployment an oportunity to do whatever actions + are required so the service puts at a correct state for using by a service. + + In our sample, the service is always ready, so this does nothing. + + This is not a task method. All level 1 cache items can be diretly + assigned to an user with no more work needed, but, if something is needed, + here you can do whatever you need + ''' + pass + + def userLoggedIn(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged into a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responability of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actor. + ''' + # We store the value at storage, but never get used, just an example + self.storage().saveData('user', user) + + def userLoggedOut(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged out if a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responability of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actor. + ''' + # We do nothing more that remove the user + self.storage().remove('user') + + def reasonOfError(self): + ''' + Returns the reason of the error. + + Remember that the class is responsible of returning this whenever asked + for it, and it will be asked everytime it's needed to be shown to the + user (when the administation asks for it). + ''' + return self.storage().readData('error') or 'No error' + + def destroy(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + Invoked for destroying a deployed service + Do whatever needed here, as deleting associated data if needed (i.e. a copy of the machine, snapshots, etc...) + @return: State.FINISHED if no more checks/steps for deployment are needed, State.RUNNING if more steps are needed (steps checked using checkState) + ''' + return State.FINISHED + + def cancel(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + This can be invoked directly by an administration or by the clean up + of the deployed service (indirectly). + When administrator requests it, the cancel is "delayed" and not + invoked directly. + ''' + return State.FINISHED + \ No newline at end of file diff --git a/server/documentation/_downloads/samples/services/SampleUserDeploymentTwo.py b/server/documentation/_downloads/samples/services/SampleUserDeploymentTwo.py new file mode 100644 index 000000000..687971e7f --- /dev/null +++ b/server/documentation/_downloads/samples/services/SampleUserDeploymentTwo.py @@ -0,0 +1,469 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.services import UserDeployment +from uds.core.util.State import State +import logging + +logger = logging.getLogger(__name__) + +class SampleUserDeploymentTwo(UserDeployment): + ''' + This class generates the user consumable elements of the service tree. + + This is almost the same as SampleUserDeploymentOne, but differs that this one + uses the publication to get data from it, in a very basic way. + + After creating at administration interface an Deployed Service, UDS will + create consumable services for users using UserDeployment class as + provider of this elements. + + At class instantiation, this will receive an environment with"generator", + that are classes that provides a way to generate unique items. + + The generators provided right now are 'mac' and 'name'. To get more info + about this, look at py:class:`uds.core.util.UniqueMacGenerator.UniqueNameGenerator` + and py:class:`uds.core.util.UniqueNameGenerator.UniqueNameGenerator` + + As sample also of environment storage usage, wi will use here the provider + storage to keep all our needed info, leaving marshal and unmarshal (needed + by Serializable classes, like this) empty (that is, returns '' first and does + nothing the second one) + + Also Remember, if you don't include this class as the deployedType of the + SampleServiceTwo, or whenever you try to access a service of SampleServiceTwo, + you will get an exception that says that you haven't included the deployedType. + ''' + + #: Recheck every five seconds by default (for task methods) + suggestedTime = 2 + + def initialize(self): + ''' + Initialize default attributes values here. We can do whatever we like, + but for this sample this is just right... + ''' + self._name = '' + self._ip = '' + self._mac = '' + self._error = '' + self._count = 0 + + # Serializable needed methods + def marshal(self): + ''' + Marshal own data, in this sample we will marshal internal needed + attributes. + + In this case, the data will be store with the database record. To + minimize database storage usage, we will "zip" data before returning it. + Anyway, we should keep this data as low as possible, we also have an + storage for loading larger data. + + :note: It's a good idea when providing marshalers, to store a 'version' + beside the values, so we can, at a later stage, treat with old + data for current modules. + ''' + data = '\t'.join(['v1', self._name, self._ip, self._mac, self._error, + str(self._count)]) + return data.encode('zip') + + def unmarshal(self, str_): + ''' + We unmarshal the content. + ''' + data = str_.decode('zip').split('\t') + # Data Version check + # If we include some new data at some point in a future, we can + # add "default" values at v1 check, and load new values at 'v2' check. + if data[0] == 'v1': + self._name, self._ip, self._mac, self._error, count = data[1:] + self._count = int(count) + + def getName(self): + ''' + We override this to return a name to display. Default implementation + (in base class), returns getUniqueIde() value + This name will help user to identify elements, and is only used + at administration interface. + + We will use here the environment name provided generator to generate + a name for this element. + + The namaGenerator need two params, the base name and a length for a + numeric incremental part for generating unique names. This are unique for + all UDS names generations, that is, UDS will not generate this name again + until this name is freed, or object is removed, what makes its environment + to also get removed, that makes all unique ids (names and macs right now) + to also get released. + + Every time get method of a generator gets called, the generator creates + a new unique name, so we keep the first generated name cached and don't + generate more names. (Generator are simple utility classes) + ''' + if self._name == '': + self._name = self.nameGenerator().get( self.publication().getBaseName(), + 3 ) + # self._name will be stored when object is marshaled + return self._name + + def setIp(self, ip): + ''' + In our case, there is no OS manager associated with this, so this method + will never get called, but we put here as sample. + + Whenever an os manager actor notifies the broker the state of the service + (mainly machines), the implementation of that os manager can (an probably will) + need to notify the IP of the deployed service. Remember that UDS treats with + IP services, so will probable needed in every service that you will create. + :note: This IP is the IP of the "consumed service", so the transport can + access it. + ''' + self._ip = ip + + def getUniqueId(self): + ''' + Return and unique identifier for this service. + In our case, we will generate a mac name, that can be also as sample + of 'mac' generator use, and probably will get used something like this + at some services. + + The get method of a mac generator takes one param, that is the mac range + to use to get an unused mac. + + The mac generated is not used by anyone, it will not depend on + the range, the generator will take care that this mac is unique + and in the range provided, or it will return None. The ranges + are wide enough to ensure that we always will get a mac address + in this case, but if this is not your case, take into account that + None is a possible return value, and in that case, you should return an + invalid id right now. Every time a task method is invoked, the core + will try to update the value of the unique id using this method, so + that id can change with time. (In fact, it's not unique at database level, + it's unique in the sense that you must return an unique id that can, for + example, be used by os managers to identify this element). + + :note: Normally, getting out of macs in the mac pool is a bad thing... :-) + ''' + if self._mac == '': + self._mac = self.macGenerator().get( '00:00:00:00:00:00-00:FF:FF:FF:FF:FF' ) + return self._mac + + def getIp(self): + ''' + We need to implement this method, so we can return the IP for transports + use. If no IP is known for this service, this must return None + + If our sample do not returns an IP, IP transport will never work with + this service. Remember in real cases to return a valid IP address if + the service is accesible and you alredy know that (for example, because + the IP has been assigend via setIp by an os manager) or because + you get it for some other method. + + Storage returns None if key is not stored. + + :note: Keeping the IP address is responsibility of the User Deployment. + Every time the core needs to provide the service to the user, or + show the IP to the administrator, this method will get called + + ''' + if self._ip == '': + return '192.168.0.34' # Sample IP for testing purposes only + return self._ip + + def setReady(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + The method is invoked whenever a machine is provided to an user, right + before presenting it (via transport rendering) to the user. + + This method exist for this kind of situations (i will explain it with a + sample) + + Imagine a Service tree (Provider, Service, ...) for virtual machines. + This machines will get created by the UserDeployment implementation, but, + at some time, the machine can be put at in an state (suspend, shut down) + that will make the transport impossible to connect with it. + + This method, in this case, will check the state of the machine, and if + it is "ready", that is, powered on and accessible, it will return + "State.FINISHED". If the machine is not accessible (has been erased, for + example), it will return "State.ERROR" and store a reason of error so UDS + can ask for it and present this information to the Administrator. + + If the machine powered off, or suspended, or any other state that is not + directly usable but can be put in an usable state, it will return + "State.RUNNING", and core will use checkState to see when the operation + has finished. + + I hope this sample is enough to explain the use of this method.. + ''' + + # In our case, the service is always ready + return State.FINISHED + + def deployForUser(self, user): + ''' + Deploys an service instance for an user. + + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + The user parameter is not realy neded, but provided. It indicates the + Database User Object (see py:mod:`uds.modules`) to which this deployed + user service will be assigned to. + + This method will get called whenever a new deployed service for an user + is needed. This will give this class the oportunity to create + a service that is assigned to an user. + + The way of using this method is as follows: + + If the service gets created in "one step", that is, before the return + of this method, the consumable service for the user gets created, it + will return "State.FINISH". + If the service needs more steps (as in this case), we will return + "State.RUNNING", and if it has an error, it wil return "State.ERROR" and + store an error string so administration interface can show it. + + We do not use user for anything, as in most cases will be. + ''' + import random + + self._count = 0 + + # random fail + if random.randint(0, 9) == 9: + # Note that we can mark this string as translatable, and return + # it translated at reasonOfError method + self._error = 'Random error at deployForUser :-)' + return State.ERROR + + return State.RUNNING + + def deployForCache(self, cacheLevel): + ''' + Deploys a user deployment as cache. + + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + In our sample, this will do exactly the same as deploy for user, + except that it will never will give an error. + + See deployForUser for a description of what this method should do. + + :note: deployForCache is invoked whenever a new cache element is needed + for an specific user deployment. It will also indicate for what + cache level (L1, L2) is the deployment + ''' + self._count = 0 + return State.RUNNING + + def moveToCache(self, newLevel): + ''' + This method is invoked whenever the core needs to move from the current + cache level to a new cache level an user deployment. + + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + We only provide newLevel, because there is only two cache levels, so if + newLevel is L1, the actual is L2, and if it is L2, the actual is L1. + + Actually there is no possibility to move assigned services again back to + cache. If some service needs that kind of functionallity, this must be + provided at service level (for example, when doing publishing creating + a number of services that will be used, released and reused by users). + + Also, user deployments that are at cache level 2 will never get directly + assigned to user. First, it will pass to L1 and then it will get assigned. + + A good sample of a real implementation of this is moving a virtual machine + from a "suspended" state to "running" state to assign it to an user. + + In this sample, there is L2 cache also, but moving from L1 to L2 and + from L2 to L1 is doing really nothing, so this method will do nothing. + + In a real scenario, we will, for example, suspend or resume virtual machine + and, return State.RUNNING and at checkState check if this task is completed. + ''' + pass + + def checkState(self): + ''' + Our deployForUser method will initiate the consumable service deployment, + but will not finish it. + + So in our sample, we will only check if a number reaches 5, and if so + return that we have finished, else we will return that we are working + on it. + + One deployForUser returns State.RUNNING, this task will get called until + checkState returns State.FINISHED. + + Also, we will make the user deployment fail one of every 10 calls to this + method. + + Note: Destroying, canceling and deploying for cache also makes use of + this method, so you must keep the info of that you are checking if you + need it. + + In our case, destroy is 1-step action so this will no get called while + destroying, and cancel will simply invoke destroy. Cache deployment is + exactly as user deployment, except that the core will not assign it to + anyone, and cache moving operations is + ''' + import random + + self._count += 1 + # Count is always a valid value, because this method will never get + # called before deployForUser, deployForCache, destroy or cancel. + # In our sample, we only use checkState in case of deployForUser, + # so at first call count will be 0. + if self._count >= 5: + return State.FINISHED + + # random fail + if random.randint(0, 9) == 9: + self._error = 'Random error at checkState :-)' + return State.ERROR + + return State.RUNNING + + def finish(self): + ''' + Invoked when the core notices that the deployment of a service has finished. + (No matter whether it is for cache or for an user) + + This gives the opportunity to make something at that moment. + + :note: You can also make these operations at checkState, this is really + not needed, but can be provided (default implementation of base class does + nothing) + ''' + # We set count to 0, not needed but for sample purposes + self._count = 0 + + def assignToUser(self, user): + ''' + This method is invoked whenever a cache item gets assigned to an user. + This is not a task method right now, simply a notification. This means + that L1 cache items must be directly usable (except for the readyness part) + by users in a single step operation. + + Note that there will be an setReady call before letting the user consume + this user deployment, so this is more informational (so, if you keep at + what cache level is this instance, you can update it) than anything else. + + This is not a task method. All level 1 cache items can be dircetly + assigned to an user with no more work needed, but, if something is needed, + here you can do whatever you need. + + user is a Database user object. + ''' + logger.debug('Assigned to user {0}'.format(user)) + + def userLoggedIn(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged into a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responsibility of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actors. + ''' + # We store the value at storage, but never get used, just an example + self.storage().saveData('user', user) + + def userLoggedOut(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged out if a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responability of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actor. + ''' + # We do nothing more that remove the user + self.storage().remove('user') + + def reasonOfError(self): + ''' + Returns the reason of the error. + + Remember that the class is responsible of returning this whenever asked + for it, and it will be asked everytime it's needed to be shown to the + user (when the administation asks for it). + + :note: Remember that you can use ugettext to translate this error to + user language whenever it is possible. (This one will get invoked + directly from admin interface and, as so, will have translation + environment correctly set up. + ''' + return self._error + + def destroy(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + Invoked for destroying a deployed service + Do whatever needed here, as deleting associated data if needed (i.e. a copy of the machine, snapshots, etc...) + @return: State.FINISHED if no more checks/steps for deployment are needed, State.RUNNING if more steps are needed (steps checked using checkState) + ''' + return State.FINISHED + + def cancel(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + This can be invoked directly by an administration or by the clean up + of the deployed service (indirectly). + When administrator requests it, the cancel is "delayed" and not + invoked directly. + ''' + return State.FINISHED diff --git a/server/documentation/_downloads/samples/services/__init__.py b/server/documentation/_downloads/samples/services/__init__.py new file mode 100644 index 000000000..38a9126af --- /dev/null +++ b/server/documentation/_downloads/samples/services/__init__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +Sample Service module. + +This package simply shows how a new service can be implemented. + + +The first thing to do in every package that is a module is register the +class that is responsible of providing the module with the system. + +For this, we must simply import the class at __init__, UDS will take care +of the rest +''' + +from SampleProvider import Provider + diff --git a/server/documentation/_downloads/samples/services/provider.png b/server/documentation/_downloads/samples/services/provider.png new file mode 100644 index 0000000000000000000000000000000000000000..d2a954d44526a447e6f4e693324450868f7443b4 GIT binary patch literal 491 zcmV@I z6tZ>cs#3?$MGAIGp@M&a*2RKp$X#+d-yK9MULgrQczO4}pZ6m4M zO;r>{>SMK1(R3jAkU$c z(ue|C=n(p<8K9a`77{=;?4T$LkSNe1Vn%?5fE)o*{&oS%K|tex5Zy(6z;}}H5(OFOaM9~Q zx7&rU^;%d7NZx5-7`_aL!@KIe_FWhZ2B&COuIuhI#@gB+jYenIeO0r?aeN&F!KL=o zG(F6?Xp54<9?3z011HBvpSaL>eLkDbZue{YI{B7A; zx^zG+3`~(Sh76=N$^gPH@B}t~{eE8{Qq_s}Bc0B^clW(J|M0A}@Cg0^OH^2+eWX=z z8HV;9tA?$Y#~XCs8)eiD`C;wl#NCMX9_3J#DwZG;yA(cv%$jyaC7s6 zE3I*NH|N>gxtL5Qa(_%f^G&E%3b9(P{uT&=6^lLX z&UHLKeamS)yuRv7&0Gw~Ajab{`u+a9fTeNw4=^TXvZa_O&nYAfl2g!g-+^ei+c;q* z`(?de-|OLAfZmE8=2uN%Mx)WT+wE3y0khd`tJ?F=^odr-WyG{^|NT4v2BY2&XM6rZ QqyPW_07*qoM6N<$f=DO_-T(jq literal 0 HcmV?d00001 diff --git a/server/documentation/_images/LogoUDS.png b/server/documentation/_images/LogoUDS.png new file mode 100644 index 0000000000000000000000000000000000000000..f3196430b40fba4013ea2b266c472edacf56a1b0 GIT binary patch literal 9441 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 literal 0 HcmV?d00001 diff --git a/server/documentation/api/index.rst b/server/documentation/api/index.rst new file mode 100644 index 000000000..1353be239 --- /dev/null +++ b/server/documentation/api/index.rst @@ -0,0 +1,9 @@ +============== +UDS's core API +============== + +.. toctree:: + + models + modules + /development/samples/samples diff --git a/server/documentation/api/models.rst b/server/documentation/api/models.rst new file mode 100644 index 000000000..e2639d1a6 --- /dev/null +++ b/server/documentation/api/models.rst @@ -0,0 +1,24 @@ +=================== +UDS Database Models +=================== + +This section describes de models used in UDS. + +The models described here are implemented using Django models, so you can get more +info about Django models functionalty at `Django project website `_ + +The function of the models inside UDS is to provide the persistence needed by +the core and by other utility classes that are provided, such as a Cache, Storage +or unique IDs. + +Right now the models are used all over UDS, but with time we will limit the use +of this models to be done through managers or utility clases designed for that +purpose. + +.. toctree:: + + models/services + models/authentication + models/transport + models/other + diff --git a/server/documentation/api/models/authentication.rst b/server/documentation/api/models/authentication.rst new file mode 100644 index 000000000..75d64235e --- /dev/null +++ b/server/documentation/api/models/authentication.rst @@ -0,0 +1,25 @@ +============================= +Authentication Related models +============================= +.. toctree:: + :maxdepth: 2 + +.. module:: uds.models + +.. autoclass:: Authenticator + :members: + :show-inheritance: + +.. autoclass:: User + :members: + :show-inheritance: + +.. autoclass:: Group + :members: + :show-inheritance: + +.. autoclass:: UserPreference + :members: + :show-inheritance: + + diff --git a/server/documentation/api/models/other.rst b/server/documentation/api/models/other.rst new file mode 100644 index 000000000..6c6f9e2e1 --- /dev/null +++ b/server/documentation/api/models/other.rst @@ -0,0 +1,41 @@ +============ +Other models +============ + +Environment related +------------------- + +.. module:: uds.models + +.. toctree:: + :maxdepth: 2 + +.. autoclass:: Cache + :members: + :show-inheritance: + +.. autoclass:: Storage + :members: + :show-inheritance: + +.. autoclass:: UniqueId + :members: + :show-inheritance: + +Module related +-------------- + +.. autoclass:: Config + :members: + :show-inheritance: + +Scheduling and background workers related +----------------------------------------- + +.. autoclass:: Scheduler + :members: + :show-inheritance: + +.. autoclass:: DelayedTask + :members: + :show-inheritance: diff --git a/server/documentation/api/models/services.rst b/server/documentation/api/models/services.rst new file mode 100644 index 000000000..c11794cfb --- /dev/null +++ b/server/documentation/api/models/services.rst @@ -0,0 +1,33 @@ +====================== +Service Related models +====================== + +This models takes cares of persistence of the Services and its associated elements. + + DESCRIBE HIEARARCHY HERE + + +.. toctree:: + :maxdepth: 2 + +.. module:: uds.models + +.. autoclass:: Provider + :members: + :show-inheritance: + +.. autoclass:: Service + :members: + :show-inheritance: + +.. autoclass:: DeployedService + :members: + :show-inheritance: + +.. autoclass:: DeployedServicePublication + :members: + :show-inheritance: + +.. autoclass:: UserService + :members: + :show-inheritance: diff --git a/server/documentation/api/models/transport.rst b/server/documentation/api/models/transport.rst new file mode 100644 index 000000000..3fb2545c6 --- /dev/null +++ b/server/documentation/api/models/transport.rst @@ -0,0 +1,18 @@ +======================== +Transport Related models +======================== +.. toctree:: + :maxdepth: 2 + +.. module:: uds.models + +.. autoclass:: Transport + :members: + :show-inheritance: + +.. autoclass:: Network + :members: + :show-inheritance: + + + diff --git a/server/documentation/api/modules.rst b/server/documentation/api/modules.rst new file mode 100644 index 000000000..5aaebc0d0 --- /dev/null +++ b/server/documentation/api/modules.rst @@ -0,0 +1,18 @@ +=========== +UDS Modules +=========== + + +Modules are the basic component of plugin architecture of UDS. + +As so, they are spreadly covered here, and with +:doc:`samples ` must give enough information for +allowing anyone to develop their own modules. + +.. toctree:: + + modules/BaseModule + modules/FormFields + modules/ServiceModules + modules/AuthenticatorModule + diff --git a/server/documentation/api/modules/AuthenticatorModule.rst b/server/documentation/api/modules/AuthenticatorModule.rst new file mode 100644 index 000000000..b210793a0 --- /dev/null +++ b/server/documentation/api/modules/AuthenticatorModule.rst @@ -0,0 +1,27 @@ +===================== +Authenticator Modules +===================== + +Authenticator modules are responsible of providing the user authentication +part inside UDS. + +They are composed of a package where it is provided and, at least, the following +elements: + + * One icon for administration interface representation. Icon is png file of + 16x16. + * One class, derived from uds.core.auths.Authenticator, providing the needed + logic for that authenticator. + * Registration of the class inside uds at package's __init__. + +All packages included inside uds.auths will automatically be imported, but +the authenticators needs to register as valid authenticators, and the best place +to do that is at the authenticator's package __init__. + +The best way to understand what you need to create your own authenticator, +is to look at :doc:`modules samples ` + + +.. toctree:: + + auths/Authenticator \ No newline at end of file diff --git a/server/documentation/api/modules/BaseModule.rst b/server/documentation/api/modules/BaseModule.rst new file mode 100644 index 000000000..d2d30187e --- /dev/null +++ b/server/documentation/api/modules/BaseModule.rst @@ -0,0 +1,57 @@ +=========== +Base Module +=========== + +The Base module is the base class used for all modules of UDS. + +In order to deveplop an UDS Module, there is a number of basic methods that you must provide. + +There are the clases that are base of BaseModule, that are: + * BaseModule_ + * Environmentable_ + * Serializable_ + * UserInterface_ + +.. toctree:: + +BaseModule +---------- + +.. module:: uds.core.BaseModule + +.. autoclass:: BaseModule + :members: + +Environmentable +--------------- + +.. module:: uds.core.Environment + +.. autoclass:: Environmentable + :members: + + +Serializable +------------ + +.. module:: uds.core.Serializable + +.. autoclass:: Serializable + :members: + + +UserInterface +------------- + + UserInterface is the class responsible for managing the Field Descriptions of modules. + + This fields descriptions are intended for allowing an easy exposition of configuration form via the + administration interface. + + You can obtain more information about user interface fields at :doc:`User interface fields types `. + +.. module:: uds.core.ui.UserInterface + +.. autoclass:: UserInterface + :members: + diff --git a/server/documentation/api/modules/FormFields.rst b/server/documentation/api/modules/FormFields.rst new file mode 100644 index 000000000..3b15da2a1 --- /dev/null +++ b/server/documentation/api/modules/FormFields.rst @@ -0,0 +1,33 @@ +Form Fields +=========== + +Form Fields are utility clases provided for allowing easy communication of modules +and administration interface. + +It helps to define the administration level forms that will be used to manage +different modules (service providers, services, authenticators, transports, ...) + +All modules that needs to be presented to admin users, use UserInterface as one +of their base class. + +Think that not all interfaces needed by different modules need a direct representation +at administration interface level, (for example, UserDeployment do not need to be +managed by administrators, nor publications, both corresponding to service modules). + +.. module:: uds.core.ui.UserInterface + +.. toctree:: + + +The types of fields provided are: + * :py:class:`gui.TextField` + * :py:class:`gui.NumericField` + * :py:class:`gui.PasswordField` + * :py:class:`gui.HiddenField` + * :py:class:`gui.CheckBoxField` + * :py:class:`gui.ChoiceField` + * :py:class:`gui.MultiChoiceField` + * :py:class:`gui.EditableList` + +.. autoclass:: gui + :members: InputField, TextField, NumericField, PasswordField, HiddenField, CheckBoxField, ChoiceField, MultiChoiceField, EditableList diff --git a/server/documentation/api/modules/ServiceModules.rst b/server/documentation/api/modules/ServiceModules.rst new file mode 100644 index 000000000..a3c9c1e44 --- /dev/null +++ b/server/documentation/api/modules/ServiceModules.rst @@ -0,0 +1,53 @@ +=============== +Service Modules +=============== + +Service modules are responsible for giving the user consumable ip services for +users. + +They are composed of a package where it is provided, at least, the following +elements: + + * One icon for administration interface representation. Icon is png file of + 16x16. + * A Full tree of classes, derived from interfaces (descrived below) + * Registration of the class inside UDS at package's __init__. + +All packages included inside uds.services will automatically be imported, but +the service providers (root of service trees) needs to register as valid +providers, and the best place to do that is at the authenticator's package __init__. + +the Full tree of classes needed by the service modules are: + + * **Provider**: This is the root tree of any service. It represents an agrupation + of services under the same root. As sample, a service provider can be an + Open nebula server, an VC, or whataver is a common root for a number of services. + * **Service**: This is the representation of what a service will give to an user. + As such, this is not what the user will consume, but this is more the definition + of what the user will consume. Before assigning a service to an user, the admin + will need to declare a "Deployed Service", that is a definition, using this service + an a number of other modules, of what the user will consume. Inside this service + we need to provide the information needed for deploying an user consumable item, + such as if it needs to be "prepared", if it supports cache, if it must be assigned + to an user "manually", and all the custom data that the user deployments and publications + will need. + * **Publication**. Some services, before being assigned to users, needs some kind of + preparation. This process of preparation is called here "publication". The service + itself will declare if it needs a publication and, if needed, who is responsible of + that. Services with needed publication will use this kind of class to provide + such preparation. + * **User Deployment**. This is what will provide the final user consumable service. + The user deployment is the last responsible for, using the provided service + and provided publication (if needed), to create the elements that the user will + consume. + +The best way to understand what you need to create your own services, +is to look at :doc:`modules samples ` + +.. toctree:: + + services/Provider + services/Service + services/Publication + services/UserDeployment + services/Exceptions \ No newline at end of file diff --git a/server/documentation/api/modules/auths/Authenticator.rst b/server/documentation/api/modules/auths/Authenticator.rst new file mode 100644 index 000000000..f30d9fb75 --- /dev/null +++ b/server/documentation/api/modules/auths/Authenticator.rst @@ -0,0 +1,15 @@ +======================= +Authenticator Interface +======================= + +The authenticator class is in fact an interface. UDS authenticators must derive +from this, and must provide the logic so UDS can manage the users and groups that +an authenticator provides. + + +.. toctree:: + +.. module:: uds.core.auths + +.. autoclass:: Authenticator + :members: diff --git a/server/documentation/api/modules/services/Exceptions.rst b/server/documentation/api/modules/services/Exceptions.rst new file mode 100644 index 000000000..f137b05be --- /dev/null +++ b/server/documentation/api/modules/services/Exceptions.rst @@ -0,0 +1,9 @@ +================== +Service Exceptions +================== + +.. toctree:: + +.. automodule:: uds.core.services.Exceptions + :members: + diff --git a/server/documentation/api/modules/services/Provider.rst b/server/documentation/api/modules/services/Provider.rst new file mode 100644 index 000000000..ad7fde37c --- /dev/null +++ b/server/documentation/api/modules/services/Provider.rst @@ -0,0 +1,27 @@ +================== +Provider interface +================== + +The provider class is the root class of the module. It keeps the common information +needed by all services provided by this "provider". + +Think about a provider as the class that will declare all stuff neded by core and +child services to provide and administrator user a way to create services to be +consumed by users. + +One good example is a Virtualization server. Here we keep information about that +server (ip address, protocol, ....) and services provided by that "provider" will +make use of that information to make the administrator not provide it once an again +for every service we put on that virtualization server. + +.. toctree:: + +.. module:: uds.core.services + +For a detailed example of a service provider, you can see the provided +:doc:`provider sample ` + +.. autoclass:: ServiceProvider + :members: + + diff --git a/server/documentation/api/modules/services/Publication.rst b/server/documentation/api/modules/services/Publication.rst new file mode 100644 index 000000000..290e91503 --- /dev/null +++ b/server/documentation/api/modules/services/Publication.rst @@ -0,0 +1,30 @@ +===================== +Publication interface +===================== + +The publication class is in fact an interface. It represents, in those case that +a service needs the preparation, the logic for that preparation. + +So the publication class is responsible of doing whatever is needed to get the +deployed service (that is the compound of a service, an os manager, transports +and authenticators) ready for deploying user consumables. + +Note that not all services needs to implement this class, only in those case +where that service declares that a publication is needed. + + +As functional sample of a publication, imagine that we want to assing KVM COW +machines to users. The publication class can make a clone of the base machine +(that the service itself has taken note of which one is), and then the COWs will +be created from this cloned machine. + +.. toctree:: + +.. module:: uds.core.services + +For a detailed example of a service provider, you can see the provided +:doc:`publication sample ` + +.. autoclass:: Publication + :members: + diff --git a/server/documentation/api/modules/services/Service.rst b/server/documentation/api/modules/services/Service.rst new file mode 100644 index 000000000..6e26c1281 --- /dev/null +++ b/server/documentation/api/modules/services/Service.rst @@ -0,0 +1,25 @@ +================= +Service interface +================= + +The service class is in fact an interface. It represents the base for all user +deployments (that is, consumable user services) that will be provided. + +As such, the service is responsible for keeping the information that, at deployments, +will be neded by provided user consumable services. + +A good sample of a service can be a KVM machine that will be copied COW and that COWs +will be assigned to users. In that case, we will collect which machine will be copied, +where it is to be copied, an a few more params that the user deployments will need. + +.. toctree:: + +.. module:: uds.core.services + +For a detailed example of a service provider, you can see the provided +:doc:`service sample ` + +.. autoclass:: Service + :members: + + diff --git a/server/documentation/api/modules/services/UserDeployment.rst b/server/documentation/api/modules/services/UserDeployment.rst new file mode 100644 index 000000000..6f97c2de9 --- /dev/null +++ b/server/documentation/api/modules/services/UserDeployment.rst @@ -0,0 +1,23 @@ +======================== +UserDeployment interface +======================== + +The user deployment class is in fact an interface. It represents the final consumable +that will be assigned to an user, and, as such, it must provide some mechanisms to +allow core to manage those consumables. + +A good sample of an user deployment can be a KVM Virtual Machine, cloned COW from +another, and assigned to an user. + +.. toctree:: + +.. module:: uds.core.services + +For detailed examples of a couple of user deployments, you can see the provided +:doc:`service sample ` and +:doc:`service sample ` + +.. autoclass:: UserDeployment + :members: + + diff --git a/server/documentation/conf.py b/server/documentation/conf.py new file mode 100644 index 000000000..239d5cf81 --- /dev/null +++ b/server/documentation/conf.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +# +# UDS documentation build configuration file, created by +# sphinx-quickstart on Mon Jun 18 01:41:48 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('../src/')) + +from server import settings +from django.core.management import setup_environ +setup_environ(settings) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'UDS' +copyright = u'2012, Virtual Cable S.L.U.' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# default, sphinxdoc, traditional, nature, scrolls, agogo, haiku, pyramid +html_theme = 'sphinxdoc' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { + # 'stickysidebar' : False, + # 'collapsiblesidebar' : True, + # 'externalrefs' : True, + +} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'UDSdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +'papersize': 'a4paper', + +# The font size ('10pt', '11pt' or '12pt'). +'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +'preamble': '\setcounter{tocdepth}{6}', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'UDS.tex', u'UDS Documentation', + u'Virtual Cable S.L.U.', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +latex_logo = '_images/LogoUDS.png' + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +latex_show_pagerefs = True + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'uds', u'UDS Documentation', + [u'Virtual Cable S.L.U.'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'UDS', u'UDS Documentation', + u'Virtual Cable S.L.U.', 'UDS', 'Universal Desktop Services.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/server/documentation/development/architecture.rst b/server/documentation/development/architecture.rst new file mode 100644 index 000000000..0bf2b1fd4 --- /dev/null +++ b/server/documentation/development/architecture.rst @@ -0,0 +1,36 @@ +================== +UDS's architecture +================== + +This section covers the current UDS Arquiceture & diagrams. + +UDS is built on the Django web framework, which itself is +built on Python, thus MyTARDIS follows the architectural model +of Django. + +Component Architecture +---------------------- + +This diagram shows the major components of UDS. + +* Core components + * `Apache Http `_ + * `WSGI `_ + * `Django `_ + * `Python `_. + +* RDBMS + UDS is currently being developed/testing on Mysql 5 Database. + May other databases will work also, but no one else has been tested. + +Functional Architecture +----------------------- + +UDS is build using Django as base support for Web acess and Database access. + +Over this, UDS uses the following diagram: + +DIAGRAM + +Core + Basic core funcionality. diff --git a/server/documentation/development/contributing.rst b/server/documentation/development/contributing.rst new file mode 100644 index 000000000..ec04f5a4c --- /dev/null +++ b/server/documentation/development/contributing.rst @@ -0,0 +1,3 @@ +=================== +Contributing to UDS +=================== diff --git a/server/documentation/development/repository.rst b/server/documentation/development/repository.rst new file mode 100644 index 000000000..36b5e39bd --- /dev/null +++ b/server/documentation/development/repository.rst @@ -0,0 +1,3 @@ +============== +UDS Repository +============== \ No newline at end of file diff --git a/server/documentation/development/samples/auths/Authenticator.rst b/server/documentation/development/samples/auths/Authenticator.rst new file mode 100644 index 000000000..f4394981f --- /dev/null +++ b/server/documentation/development/samples/auths/Authenticator.rst @@ -0,0 +1,15 @@ +==================== +Sample Authenticator +==================== + +The authenticator is the responsible of providing the needed mechanisms to UDS for +user authentication. + +As thatm this must provide a number of methods, that will allow UDS to manage +things the way it needs to. (Access users, groups, check credentials, etc...) + +Here you can :download:`Download sample ` + + +.. literalinclude:: /_downloads/samples/auths/SampleAuth.py + :linenos: diff --git a/server/documentation/development/samples/samples.rst b/server/documentation/development/samples/samples.rst new file mode 100644 index 000000000..604170bf2 --- /dev/null +++ b/server/documentation/development/samples/samples.rst @@ -0,0 +1,100 @@ +=================== +UDS Modules Samples +=================== + +In this section we cover basic samples of the different kind of mudules supported +by UDS. + +UDS is designed in a modular way, meaning this that it has a core that allows +a number of modules to get plugged inside the whole system. + +This modules are: + + * Services, including all stuff around them. + * Transports + * OS Managers + * Authenticators + +This secion will try to give sample of every module, what it must do and how this +must be done. + +Service Sample +-------------- + +A service is composed of several classes. This classes depends on how the service works. + +This are: + + * *Provider*, that is simply the "root" where services + descent, so we can configure just one part of the service parameters and rest + of them at service level. + + One sample of provider is a virtualization server, such as oVirt, Open Nebula, or + others like it. We can keep info about server at provider level, and info about + what we need in an specific service at service level. + + * *Service*, that is a service definition, that must be deployed at a later stage + to offer something to the users. + + Following our previous sample, if provider was an oVirt server, a service can + be a Virtual Machine cloned COW. + + * *Publication*, This class is optional. If service declares that needs a + publication for deployment of user instance, this class implements exactly + that, the publication for that service. Publications are in fact a way of + allowing services to prepare something in a stage prior to creating the + user consumable services. + + Following our previous sample, if provider was an oVirt Server and the service + was a Virtual Machine cloned for Cow, the poblication can be a full clone of + the service machine for making COWS from this one. + + * *DeployedService*, This class is the user consumed service itself. After a + service is created, it must be deployed, and deploy will mean that there will + be "instances" of that service (User Deployments) that will be consumed by + users. + + Following our previous sample, if the publication was a full copy machine, + an deployed service can be a machine in COW format using as base that + machine. + + +From theese, the only not really needed is Publication. Publication will only be +needed whenever a service needs a "preparation" before creating the user consumable +deployed services. For a service to be usable, we will need the full tree, meaning +this that we will provide all controllers (Provider, service or services, publication +or publications, deployed service or deployed services.). + +All class belonging to a service must be grouped under the same package, and we +well need to register this package for the system to recognize it as service. + +For this, we must register the Provider, that has references to rest of items. + +Provider declares which services it provides. Services declares which publication +and deployed service it needs. Provider can declare multiples services it offers, +but services has at most one publication and exatly one deployed service. + +So, by registering the Provider, we register the whole tree provided by de package. + +Here you can find samples of every class needed for creating a new package of +services. + +.. toctree:: + + services/whatisneeded + services/Provider + services/Service + services/Publication + services/DeployedServiceOne + services/DeployedServiceTwo + + +Authenticator Sample +-------------------- + +An authenticator is composed of a single class, derived from :py:class:`uds.core.auths.Authenticator`. + +Here you can find a sample of an authenticator. + +.. toctree:: + auths/Authenticator \ No newline at end of file diff --git a/server/documentation/development/samples/services/DeployedServiceOne.rst b/server/documentation/development/samples/services/DeployedServiceOne.rst new file mode 100644 index 000000000..abf2b43dd --- /dev/null +++ b/server/documentation/development/samples/services/DeployedServiceOne.rst @@ -0,0 +1,20 @@ +========================== +Sample User Deployment One +========================== + +User deployments are the class that are responsible for creating the ultimate consumable +user service, that is, for managing that whenever the core requests a new service for +an user, this classes will take responsibility to provide it. + +Here we cover SampleUserDeploymentOne that is for SampleServiceOne, do not needs to be +published and do not uses cache. + +You can easily follow the code to see what it does, and what you have to do if you +want to provide a new one. + +:download:`Download sample ` + + +.. literalinclude:: /_downloads/samples/services/SampleUserDeploymentOne.py + :linenos: + diff --git a/server/documentation/development/samples/services/DeployedServiceTwo.rst b/server/documentation/development/samples/services/DeployedServiceTwo.rst new file mode 100644 index 000000000..f3664a740 --- /dev/null +++ b/server/documentation/development/samples/services/DeployedServiceTwo.rst @@ -0,0 +1,20 @@ +========================== +Sample User Deployment Two +========================== + +User deployments are the class that are responsible for creating the ultimate consumable +user service, that is, for managing that whenever the core requests a new service for +an user, this classes will take responsibility to provide it. + +Here we cover SampleUserDeploymentTwo that is for SampleServiceTwo, needs to be +published and has L1 and L2 cache items. + +You can easily follow the code to see what it does, and what you have to do if you +want to provide a new one. + +:download:`Download sample ` + + +.. literalinclude:: /_downloads/samples/services/SampleUserDeploymentTwo.py + :linenos: + diff --git a/server/documentation/development/samples/services/Provider.rst b/server/documentation/development/samples/services/Provider.rst new file mode 100644 index 000000000..b792a6424 --- /dev/null +++ b/server/documentation/development/samples/services/Provider.rst @@ -0,0 +1,19 @@ +======================= +Sample Service Provider +======================= + +The service provider is the top of the tree of services needed clases. +It main function is to provide a base for services, where this services contains +a common parent that is, for example, a server, a range of IPs, etc... + +This sample covers a simple service provider, explains also a bit about FormFields +and shows what tasks must be done by a service provider. + +You can easily follow the code to see what it does, and what you have to do if you +want to provide a new one. + +:download:`Download sample ` + + +.. literalinclude:: /_downloads/samples/services/SampleProvider.py + :linenos: diff --git a/server/documentation/development/samples/services/Publication.rst b/server/documentation/development/samples/services/Publication.rst new file mode 100644 index 000000000..7d82cc0c4 --- /dev/null +++ b/server/documentation/development/samples/services/Publication.rst @@ -0,0 +1,23 @@ +================== +Sample publication +================== + +A publication is a class responsible for making a service defined available to be +consumed by users. + +Not all services needs publications as you have already seen if you are following +the samples. Publications are only needed for services that needs some kind of +preparation, as, for example, with Virtual Machines, clone the base virtual machine +so we can create COW copies from this clone. This kind of behavior needs a preparation +step, that is efectively to clone the virtual base, and that will be the task of a +publication for that kind of services. + +You can easily follow the code to see what it does, and what you have to do if you +want to provide a new one. + +:download:`Download sample ` + + +.. literalinclude:: /_downloads/samples/services/SamplePublication.py + :linenos: + diff --git a/server/documentation/development/samples/services/Service.rst b/server/documentation/development/samples/services/Service.rst new file mode 100644 index 000000000..49482e701 --- /dev/null +++ b/server/documentation/development/samples/services/Service.rst @@ -0,0 +1,15 @@ +============== +Sample service +============== + +Here we cover two services. ServiceOne, that do not needs publication and +ServiceTwo, that needs publication. + +This sample should be enought to guide you through the creation of a new service. + +:download:`Download sample ` + + +.. literalinclude:: /_downloads/samples/services/SampleService.py + :linenos: + diff --git a/server/documentation/development/samples/services/whatisneeded.rst b/server/documentation/development/samples/services/whatisneeded.rst new file mode 100644 index 000000000..20eb9ffbe --- /dev/null +++ b/server/documentation/development/samples/services/whatisneeded.rst @@ -0,0 +1,32 @@ +Needs for a service package +--------------------------- + +For a new package of services, you will need: + + + * One package (python package), of course :-). + * One icon for the provider, in png format an 16x16 size. Colours is left + to your election. This icon will be informed at Provider class. + * One icon for every service that the provider will expose. Same as provider + icons. These icons will be informed at Service class. Every single class + must provide its own icon. + * Registering the provider. For the samples show here, this will be at + __init__ of the package. + + The contents of the sample package __init__ file is: + + .. literalinclude:: /_downloads/samples/services/__init__.py + :linenos: + + :download:`Download sample ` + + * Put the package under the apropiate uds package. In the case of + services, this is under "uds.core". + + Core will look for all packages under "uds.services" and import them at + initialization of the server, so every package under this will get their + __init__ called, where we register the provider. + + * Follow the samples provided here as base + + \ No newline at end of file diff --git a/server/documentation/index.rst b/server/documentation/index.rst new file mode 100644 index 000000000..b7e3f500e --- /dev/null +++ b/server/documentation/index.rst @@ -0,0 +1,71 @@ +.. _index: + +=================== +UDS's documentation +=================== + +This documentation is provided so we can understand (hopefully) UDS, its internals, +and everything about it. + +Right now the documentation is not too ritch, but we are working on it so it will +get the needed level for this kind of project. + + +First Steps +=========== + +* **From scratch:** + :doc:`Overview ` | + :doc:`Installation ` + +.. toctree:: + :hidden: + + intro/overview + intro/install + +The internals of uds +==================== + +.. toctree:: + + development/architecture + development/development + api/index + +UDS Open source project +======================= + +* **Community:** + :doc:`How to get involved ` | + :doc:`The UDS source code repository ` + +.. toctree:: + :hidden: + + development/contributing + development/repository + + +Acknowledgements +================ + +We want to thaks all the people that has contributed to de project, an also +other Open Source project used to improve this one. + +List of other software used to build UDS: + + * `Django `_ + * `XML-RPC.NET Copyright (c) 2006 Charles Cook `_ + * `Darkglass reworked graphics `_ + * `Crystal project `_ + * `South `_ + * `Jsch `_ + * `JQuery `_ + * `Plugin detect library `_ + * `JQuery UI `_ + +I hope to do nor forget anythinh here, if i do, please, report it so we can credit +to every project that UDS makes use of. + + \ No newline at end of file diff --git a/server/documentation/intro/install.rst b/server/documentation/intro/install.rst new file mode 100644 index 000000000..39b17dcb8 --- /dev/null +++ b/server/documentation/intro/install.rst @@ -0,0 +1,46 @@ +============== +Installing UDS +============== + +In order to run UDS, you will need: + + * Django Server 1.4 + * South module for Django + * Mysql libraries for python + * Mysql Database + * Ldap Libraries for python + * Criptographic package for python + +Default transports are compiled in binary form, and keeped inside UDS repository, +so you won't need Java to put UDS to work. + +Once you have all of this, you will have to follow these steps: + + * Obtain UDS from repository, you can see how to do this from +:doc:`repository access documentation ` + * Configure a database for use with UDS. To do this, simple create a database + inside your Mysql server, and a user with all permissions in this database. + * Configure UDS settings. + Inside "server" folder, you will find "settings.py". This file contains the + configuration of UDS (if it runs in debug mode, ..). The most important part + here is the DATABASES section, where you will set up the database that UDS + will use. Simply change "host", "port", "udsername", "password" and "name" + to match your database settings. + Here, we have to take care that, if we left UDS in debug mode, Django will keep + track of all petitions to UDS, so memory will grow constantly. Do not get scared + if you see that UDS starts to waste too much memory. Simply restart it or, if it's + intended to be running for a while, set DEBUG variable to "False". + Important sections are: + + * Create initial database tables. + Inside UDS folder, where you downloaded it, you will see a "manage.py". + This python application is the responsible for managing UDS, from database creation, + migrations, backend start & stop, web server (testing web server btw), ... + To create initial databases, we will do: + + python manage.py sync + python manage.py migrate + + Now we have all databases and everything that UDS needs for starting up ready... :-) + + diff --git a/server/documentation/intro/overview.rst b/server/documentation/intro/overview.rst new file mode 100644 index 000000000..2e8705d7d --- /dev/null +++ b/server/documentation/intro/overview.rst @@ -0,0 +1,32 @@ +=============== +UDS at a glance +=============== + +UDS has been developed to make a single open source server that allows the access +to the growing ip services catalog. + +For this, we have try to make a framework that allows the use of any ip service, +focusing initially at VDI because it's the mayor need for the people we have +contacted initially . + +Also, first version of UDS has been developed "fast" (very fast indeed), so now +we need to make a revision an adapt de code of the framework so it's more +'pythonic'. (Think that i start learning python one day like this, and less than +a week later i started this proyect). So think that, althouth UDS is fully +functional, has been tested and is stable enought for any production environment, +there is a lot of work to do. + +As so, UDS not only provides default modules for a lot of things (virtualization +provider, authentication providers, protocols, ...), but also provides the core +itself to allow anyone who wants or needs something, incorporate it to the +catalog of UDS in an easy and fast way. + +* In order to use UDS, you must simply :doc:`Follow the installation guide `. + +* In order to design and implement your own modules, you must: + + * :doc:`Understand the architecture ` + * :doc:`See some module samples ` + +* In order to contribute, you must install UDS, understand it, an read the + :doc:`contributing guide ` \ No newline at end of file diff --git a/server/documentation/make.bat b/server/documentation/make.bat new file mode 100644 index 000000000..3ebfb5f05 --- /dev/null +++ b/server/documentation/make.bat @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\UDS.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\UDS.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/server/src/manage.py b/server/src/manage.py new file mode 100755 index 000000000..fa9aa7afc --- /dev/null +++ b/server/src/manage.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +import os, sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "server.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/server/src/server/__init__.py b/server/src/server/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/server/settings.py.sample b/server/src/server/settings.py.sample new file mode 100644 index 000000000..71a9a21f8 --- /dev/null +++ b/server/src/server/settings.py.sample @@ -0,0 +1,302 @@ +# -*- coding: utf-8 -*- +''' +Settings file for uds server (Django) +''' + +import os +import django +import django.conf.global_settings as DEFAULT_SETTINGS + +# calculated paths for django and the site +# used as starting points for various other paths +DJANGO_ROOT = os.path.dirname(os.path.realpath(django.__file__)) +SITE_ROOT = '/'.join(os.path.dirname(os.path.realpath(__file__)).split('/')[:-1]) + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@example.com'), +) + +MANAGERS = ADMINS + +# This mark can be used by a setup script to easy locate DB Section of the settings file +#DB_SECTION_START +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'OPTIONS': { + #'init_command': 'SET storage_engine=INNODB, SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', + 'init_command' : 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', + }, + 'NAME': 'dbuds', # Or path to database file if using sqlite3. + 'USER': 'dbuds', # Not used with sqlite3. + 'PASSWORD': 'dbuds', # Not used with sqlite3. + 'HOST': '172.27.0.1', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '3306', # Set to empty string for default. Not used with sqlite3. + } +} +#DB_SECTION_END + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. + +#TIME_SECTION_START +TIME_ZONE = 'Europe/Madrid' +#TIME_SECTION_END + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en' + +ugettext = lambda s: s + +LANGUAGES = ( + ('es', ugettext('Spanish')), + ('en', ugettext('English')), + ('fr', ugettext('French')), + ('de', ugettext('German')), +) + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/media/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = os.path.join(SITE_ROOT, 'static') + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# URL prefix for admin static files -- CSS, JavaScript and images. +# Make sure to use a trailing slash. +# Examples: "http://foo.com/static/admin/", "/static/admin/". +#ADMIN_MEDIA_PREFIX = '/static/admin/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +#KEYS_SECTION_START +# Make this unique, and don't share it with anybody. +SECRET_KEY = 's5ky!7b5f#s35!e38xv%e-+iey6yi-#630x)tm1hf6_j8rie2*' +# This is a very long string, an RSA KEY (this can be changed, but if u loose it, all encription will be lost) +RSA_KEY = '-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQC0qe1GlriQbHFYdKYRPBFDSS8Ne/TEKI2mtPKJf36XZTy6rIyH\nvUpT1gMScVjHjOISLNJQqktyv0G+ZGzLDmfkCUBev6JBlFwNeX3Dv/97Q0BsEzJX\noYHiDANUkuB30ukmGvG0sg1v4ccl+xs2Su6pFSc5bGINBcQ5tO0ZI6Q1nQIDAQAB\nAoGBAKA7Octqb+T/mQOX6ZXNjY38wXOXJb44LXHWeGnEnvUNf/Aci0L0epCidfUM\nfG33oKX4BMwwTVxHDrsa/HaXn0FZtbQeBVywZqMqWpkfL/Ho8XJ8Rsq8OfElrwek\nOCPXgxMzQYxoNHw8V97k5qhfupQ+h878BseN367xSyQ8plahAkEAuPgAi6aobwZ5\nFZhx/+6rmQ8sM8FOuzzm6bclrvfuRAUFa9+kMM2K48NAneAtLPphofqI8wDPCYgQ\nTl7O96GXVQJBAPoKtWIMuBHJXKCdUNOISmeEvEzJMPKduvyqnUYv17tM0JTV0uzO\nuDpJoNIwVPq5c3LJaORKeCZnt3dBrdH1FSkCQQC3DK+1hIvhvB0uUvxWlIL7aTmM\nSny47Y9zsc04N6JzbCiuVdeueGs/9eXHl6f9gBgI7eCD48QAocfJVygphqA1AkEA\nrvzZjcIK+9+pJHqUO0XxlFrPkQloaRK77uHUaW9IEjui6dZu4+2T/q7SjubmQgWR\nZy7Pap03UuFZA2wCoqJbaQJAUG0FVrnyUORUnMQvdDjAWps2sXoPvA8sbQY1W8dh\nR2k4TCFl2wD7LutvsdgdkiH0gWdh5tc1c4dRmSX1eQ27nA==\n-----END RSA PRIVATE KEY-----' +#KEYS_SECTION_END + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +# Own context processors plus django's onw +TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + ( + 'uds.core.util.Config.context_processor', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +) + +SESSION_EXPIRE_AT_BROWSER_CLOSE = True + +SESSION_COOKIE_HTTPONLY = False + +ROOT_URLCONF = 'server.urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + os.path.join(SITE_ROOT, 'templates') +) + +INSTALLED_APPS = ( + #'django.contrib.contenttypes', # Not used + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'south', + 'uds', +) + +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGDIR = SITE_ROOT + '/' + 'log' +LOGFILE = 'uds.log' +SERVICESFILE = 'services.log' +AUTHFILE = 'auth.log' +USEFILE = 'use.log' +LOGLEVEL = DEBUG and 'DEBUG' or 'INFO' +ROTATINGSIZE = 32*1024*1024 # 32 Megabytes before rotating files + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + }, + 'simple': { + 'format': '%(levelname)s %(asctime)s %(module)s %(message)s' + }, + 'database': { + 'format': '%(levelname)s %(asctime)s Database %(message)s' + }, + 'auth': { + 'format': '%(asctime)s %(message)s' + }, + 'use': { + 'format': '%(asctime)s %(message)s' + } + }, + 'handlers': { + 'null': { + 'level':'DEBUG', + 'class':'django.utils.log.NullHandler', + }, + + 'file':{ + 'level':'DEBUG', + 'class':'logging.handlers.RotatingFileHandler', + 'formatter': 'simple', + 'filename': LOGDIR + '/' + LOGFILE, + 'mode': 'a', + 'maxBytes': ROTATINGSIZE, + 'backupCount': 3, + 'encoding': 'utf-8' + }, + + 'servicesFile':{ + 'level':'DEBUG', + 'class':'logging.handlers.RotatingFileHandler', + 'formatter': 'simple', + 'filename': LOGDIR + '/' + SERVICESFILE, + 'mode': 'a', + 'maxBytes': ROTATINGSIZE, + 'backupCount': 3, + 'encoding': 'utf-8' + }, + + 'authFile':{ + 'level':'DEBUG', + 'class':'logging.handlers.RotatingFileHandler', + 'formatter': 'auth', + 'filename': LOGDIR + '/' + AUTHFILE, + 'mode': 'a', + 'maxBytes': ROTATINGSIZE, + 'backupCount': 3, + 'encoding': 'utf-8' + }, + + 'useFile':{ + 'level':'DEBUG', + 'class':'logging.handlers.RotatingFileHandler', + 'formatter': 'use', + 'filename': LOGDIR + '/' + USEFILE, + 'mode': 'a', + 'maxBytes': ROTATINGSIZE, + 'backupCount': 3, + 'encoding': 'utf-8' + }, + + 'console':{ + 'level':'DEBUG', + 'class':'logging.StreamHandler', + 'formatter': 'simple' + }, + 'database':{ + 'level':'DEBUG', + 'class':'logging.StreamHandler', + 'formatter': 'database' + }, + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler', + } + }, + 'loggers': { + 'django': { + 'handlers':['null'], + 'propagate': True, + 'level':'INFO', + }, + 'django.request': { + 'handlers': ['file'], + 'level': 'ERROR', + 'propagate': False, + }, + 'django.db.backends': { + 'handlers': ['database'], + 'level': 'ERROR', + 'propagate': False, + }, + + 'uds': { + 'handlers': ['file'], + 'level': LOGLEVEL, + }, + + 'uds.services': { + 'handlers': ['servicesFile'], + 'level': LOGLEVEL, + 'propagate': False, + }, + # Custom Auth log + 'authLog': { + 'handlers' : ['authFile'], + 'level': 'INFO', + 'propagate': False, + }, + # Custom Services use log + 'useLog': { + 'handlers' : ['useFile'], + 'level': 'INFO', + 'propagate': False, + } + + } +} diff --git a/server/src/server/urls.py b/server/src/server/urls.py new file mode 100644 index 000000000..822d5f7e5 --- /dev/null +++ b/server/src/server/urls.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +''' +Url patterns for UDS project (Django) +''' + +from django.conf.urls.defaults import patterns, include + +# Uncomment the next two lines to enable the admin: +# from django.contrib import admin +# admin.autodiscover() + +urlpatterns = patterns('', + (r'^', include('uds.urls')) +) diff --git a/server/src/server/wsgi.py b/server/src/server/wsgi.py new file mode 100644 index 000000000..8906f5eee --- /dev/null +++ b/server/src/server/wsgi.py @@ -0,0 +1,34 @@ +""" +WSGI config for server project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "server.settings") + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +# Django 1.4 +#from django.core.wsgi import get_wsgi_application +#application = get_wsgi_application() + +# Django 1.3 +import django.core.handlers.wsgi +application = django.core.handlers.wsgi.WSGIHandler() + + +# Apply WSGI middleware here. +# from helloworld.wsgi import HelloWorldApplication +# application = HelloWorldApplication(application) diff --git a/server/src/uds/__init__.py b/server/src/uds/__init__.py new file mode 100644 index 000000000..15fe2208f --- /dev/null +++ b/server/src/uds/__init__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + + + +from django.dispatch import dispatcher +from django.db.models import signals + +# Make sure that all services are "available" at service startup +import services # to make sure that the packages are initialized at this point +import auths# To make sure that the packages are initialized at this point +from osmanagers import * # To make sure that packages are initialized at this point +from transports import * # To make sure that packages are initialized at this point + + + +def modify_MySQL_storage(sender, **kwargs): + from django.db import connection + cursor = connection.cursor() + + innoDbTables = ( uds.models.UserService, uds.models.DeployedService, uds.models.DeployedServicePublication, + uds.models.Scheduler, uds.models.DelayedTask, ) + dicTables = { k._meta.db_table: True for k in innoDbTables } + + for model in kwargs['created_models']: + db_table=model._meta.db_table + if dicTables.has_key(db_table): + stmt = 'ALTER TABLE %s ENGINE=%s' % (db_table,'InnoDB') + cursor.execute(stmt) + +signals.post_syncdb.connect(modify_MySQL_storage, sender=uds.models) diff --git a/server/src/uds/auths/IP/Authenticator.py b/server/src/uds/auths/IP/Authenticator.py new file mode 100644 index 000000000..34b9d60bb --- /dev/null +++ b/server/src/uds/auths/IP/Authenticator.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.utils.translation import ugettext_noop as _ +from uds.core.auths import Authenticator +from uds.core.auths.GroupsManager import GroupsManager +from uds.models import Network +from uds.core.util.Config import Config +import logging, random, string + +logger = logging.getLogger(__name__) + +class IPAuth(Authenticator): + typeName = _('IP Authenticator') + typeType = 'IPAuth' + typeDescription = _('IP Authenticator') + iconFile = 'auth.png' + + needsPassword = False + userNameLabel = _('IP') + groupNameLabel = _('IP Range') + isExternalSource = True + + def __init__(self, dbAuth, environment, values = None): + super(IPAuth, self).__init__(dbAuth, environment, values) + # Ignore values + + def valuesDict(self): + res = {} + return res + + def __str__(self): + return "Internal IP Authenticator" + + def marshal(self): + return "v1" + + def unmarshal(self, str_): + data = str_.split('\t') + if data[0] == 'v1': + pass + + def getGroups(self, ip, groupsManager): + # these groups are a bit special. They are in fact ip-ranges, and we must check that the ip is in betwen + # The ranges are stored in group names + ip = Network.ipToLong(ip) + g = [] + for g in groupsManager.getGroupsNames(): + rangeStart, rangeEnd = g.split('-') + rangeStart = Network.ipToLong(rangeStart) + rangeEnd = Network.ipToLong(rangeEnd) + if ip >= rangeStart and ip <= rangeEnd: + groupsManager.validate(g) + + def authenticate(self, username, credentials, groupsManager): + if self.cache().get(username) == credentials: + self.cache().remove(username) + self.getGroups(username, groupsManager) + return True + return False + + + @staticmethod + def test(env, data): + return "Internal structures seems ok" + + def check(self): + return _("All seems fine in the authenticator.") + + def getHtml(self, request): + # doAutoLogin = Config.section('IPAUTH').value('autoLogin', '0').getBool() + gm = GroupsManager(self.dbAuthenticator()) + self.getGroups(request.ip, gm) + if len(gm.getValidGroups()) > 0 and self.dbAuthenticator().isValidUser(request.ip, True): + passw = ''.join(random.choice(string.letters + string.digits) for __ in xrange(12)) + self.cache().put(request.ip, passw) + return '' + else: + return '
This ip is not allowed to autologin (' + request.ip +')
' + # We will authenticate ip here, from request.ip + # If valid, it will simply submit form with ip submited and a cached generated random password diff --git a/server/src/uds/auths/IP/__init__.py b/server/src/uds/auths/IP/__init__.py new file mode 100644 index 000000000..5c267fcdc --- /dev/null +++ b/server/src/uds/auths/IP/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from uds.core.util.Config import Config +from Authenticator import IPAuth + +# Access configuration value as soon as we can, so it is available at db +Config.section('IPAUTH').value('autoLogin', '0').get() # If 1, try to autologin + diff --git a/server/src/uds/auths/IP/auth.png b/server/src/uds/auths/IP/auth.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec2bc52bf7ab8ecb9972b864824cffc876d3c38 GIT binary patch literal 1193 zcmYL|c`%%39LJycefP3i?4rBts02k45?5NOV2`e%5qGA}4C-n^Ffxs6S~X2DG^8Cx zV$u>eZAc^=>~KF zfTpL1$e+kewP+OLKD>cP0zgXk6bS;;Du4ipamaj0Fq}(d zE{*|?QmN8~V3@u1QhIe;A@7192-#8YsJL7%=xA>zr~t?JP$V{6pX1DPdPnoYwemjb2VSY0&v+53ekf}M91U0}^;I4_Q+ihWis zIXTK625-+30itIgVUqfT3p#%IK!D>(Q&yFKP58AaIuj=+Q))w6wy#WvSDxhA+@jMp zqp#_kccM3&a?`*3GrNllat^EOB1r@~d|Sedji+O0pkt8T4QYZ)aqF3~kjCOP9}iJ? zdPLjW~i2W#RbKwi@X=Eux|fq@a|>yePA$43AJBY?x<0F^4?pg#gFzAckK(Cifc z@3)WfcRT#&xFy5>4H~t&Z8Bbbckt=Ueaergs{OhB9VDB7Im6 z^LvI!5#KXvWQ`oO&u;|28mY2da4>Wm!$n(jh4NcbFnvNF_UkHyNW2i2Esgx z8m|-A-luw@(3{Te+n8P)?p75oz&h-Efw~%Q`8O0_UHq)%cH&ri@9%bT!lm^2XJawK zc~(jU80oCEtRbH#Qx67oPK(cHWDhhuxXr=V6L`pb48u?}aBL#1lZNH)G4@w?WqEnaGQ%4}yOsck;J1ibSP*(mXoZkEa6HPdAn@$4$H#NA zyS=DlsMMw>>{bQik!Zy(>+_uwL2*E~q_eAHR+g(>C|g~QM0d6uk0J7&l=%8L z5EfJRciUB&#@(t4#$84oI*k|N`Zaph+^Gj&L=_K~MxgR}PJ2N>GbWBnljDtOsNMB( zMyWoRi`nss*&KXFc(8mpexiPMh)$-$kOn{y8Uj;TG#F8l;IYT(a#BrPAEUK-ebIwxx9GkwBIiHlCImGA literal 0 HcmV?d00001 diff --git a/server/src/uds/auths/InternalDB/Authenticator.py b/server/src/uds/auths/InternalDB/Authenticator.py new file mode 100644 index 000000000..c05ee16d6 --- /dev/null +++ b/server/src/uds/auths/InternalDB/Authenticator.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.utils.translation import ugettext_noop as _ +from uds.core.auths import Authenticator +from uds.core.managers.CryptoManager import CryptoManager +from uds.models import Authenticator as dbAuthenticator +from uds.core.util.State import State +import hashlib +import logging + +logger = logging.getLogger(__name__) + +class InternalDBAuth(Authenticator): + typeName = _('Internal Database') + typeType = 'InternalDBAuth' + typeDescription = _('Internal dabasase authenticator. Doesn\'t uses external sources') + iconFile = 'auth.png' + + # If we need to enter the password for this user + needsPassword = True + + + def __init__(self, dbAuth, environment, values = None): + super(InternalDBAuth, self).__init__(dbAuth, environment, values) + # Ignore values + + def valuesDict(self): + res = {} + return res + + def __str__(self): + return "Internal DB Authenticator Authenticator" + + def marshal(self): + return "v1" + + def unmarshal(self, str_): + data = str_.split('\t') + if data[0] == 'v1': + pass + + def authenticate(self, username, credentials, groupsManager): + logger.debug('Username: {0}, Password: {1}'.format(username, credentials)) + auth = self.dbAuthenticator() + try: + usr = auth.users.filter(name=username, state=State.ACTIVE) + if len(usr) == 0: + return False + usr = usr[0] + # Internal Db Auth has its own groups, and if it active it is valid + if usr.password == hashlib.sha1(credentials).hexdigest(): + return True + return False + except dbAuthenticator.DoesNotExist: + return False + + def createUser(self, usrData): + pass + + @staticmethod + def test(env, data): + return [True, _("Internal structures seems ok")] + + def check(self): + return _("All seems fine in the authenticator.") + + + + \ No newline at end of file diff --git a/server/src/uds/auths/InternalDB/__init__.py b/server/src/uds/auths/InternalDB/__init__.py new file mode 100644 index 000000000..94105fc9f --- /dev/null +++ b/server/src/uds/auths/InternalDB/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from Authenticator import InternalDBAuth + diff --git a/server/src/uds/auths/InternalDB/auth.png b/server/src/uds/auths/InternalDB/auth.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec2bc52bf7ab8ecb9972b864824cffc876d3c38 GIT binary patch literal 1193 zcmYL|c`%%39LJycefP3i?4rBts02k45?5NOV2`e%5qGA}4C-n^Ffxs6S~X2DG^8Cx zV$u>eZAc^=>~KF zfTpL1$e+kewP+OLKD>cP0zgXk6bS;;Du4ipamaj0Fq}(d zE{*|?QmN8~V3@u1QhIe;A@7192-#8YsJL7%=xA>zr~t?JP$V{6pX1DPdPnoYwemjb2VSY0&v+53ekf}M91U0}^;I4_Q+ihWis zIXTK625-+30itIgVUqfT3p#%IK!D>(Q&yFKP58AaIuj=+Q))w6wy#WvSDxhA+@jMp zqp#_kccM3&a?`*3GrNllat^EOB1r@~d|Sedji+O0pkt8T4QYZ)aqF3~kjCOP9}iJ? zdPLjW~i2W#RbKwi@X=Eux|fq@a|>yePA$43AJBY?x<0F^4?pg#gFzAckK(Cifc z@3)WfcRT#&xFy5>4H~t&Z8Bbbckt=Ueaergs{OhB9VDB7Im6 z^LvI!5#KXvWQ`oO&u;|28mY2da4>Wm!$n(jh4NcbFnvNF_UkHyNW2i2Esgx z8m|-A-luw@(3{Te+n8P)?p75oz&h-Efw~%Q`8O0_UHq)%cH&ri@9%bT!lm^2XJawK zc~(jU80oCEtRbH#Qx67oPK(cHWDhhuxXr=V6L`pb48u?}aBL#1lZNH)G4@w?WqEnaGQ%4}yOsck;J1ibSP*(mXoZkEa6HPdAn@$4$H#NA zyS=DlsMMw>>{bQik!Zy(>+_uwL2*E~q_eAHR+g(>C|g~QM0d6uk0J7&l=%8L z5EfJRciUB&#@(t4#$84oI*k|N`Zaph+^Gj&L=_K~MxgR}PJ2N>GbWBnljDtOsNMB( zMyWoRi`nss*&KXFc(8mpexiPMh)$-$kOn{y8Uj;TG#F8l;IYT(a#BrPAEUK-ebIwxx9GkwBIiHlCImGA literal 0 HcmV?d00001 diff --git a/server/src/uds/auths/RegexLdap/Authenticator.py b/server/src/uds/auths/RegexLdap/Authenticator.py new file mode 100644 index 000000000..2fd0d8864 --- /dev/null +++ b/server/src/uds/auths/RegexLdap/Authenticator.py @@ -0,0 +1,381 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.utils.translation import ugettext_noop as _ +from uds.core.ui.UserInterface import gui +from uds.core.auths import Authenticator +from uds.models import Authenticator as dbAuthenticator +from uds.core.util.State import State +import ldap, re + +import logging +from uds.core.auths.Exceptions import AuthenticatorException + +logger = logging.getLogger(__name__) + +LDAP_RESULT_LIMIT = 50 + +class RegexLdap(Authenticator): + + host = gui.TextField(length=64, label = _('Host'), order = 1, tooltip = _('VMWare VC Server IP or Hostname'), required = True) + port = gui.NumericField(length=5, label = _('Port'), defvalue = '389', order = 2, tooltip = _('Ldap port (389 for non ssl, 636 for ssl normally'), required = True) + ssl = gui.CheckBoxField(label = _('Use SSL'), order = 3, tooltip = _('If checked, will use a ssl connection to ldap (if port is 389, will use in fact port 636)')) + username = gui.TextField(length=64, label = _('Ldap User'), order = 4, tooltip = _('Username with read privileges on the base selected'), required = True) + password = gui.PasswordField(lenth=32, label = _('Password'), order = 5, tooltip = _('Password of the ldap user'), required = True) + timeout = gui.NumericField(length=3, label = _('Timeout'), defvalue = '10', order = 6, tooltip = _('Timeout in seconds of connection to LDAP'), required = True) + ldapBase = gui.TextField(length=64, label = _('Base'), order = 7, tooltip = _('Common search base (used for "users" and "groups"'), required = True) + userClass = gui.TextField(length=64, label = _('User class'), defvalue = 'posixAccount', order = 8, tooltip = _('Class for LDAP users (normally posixAccount)'), required = True) + userIdAttr = gui.TextField(length=64, label = _('User Id Attr'), defvalue = 'uid', order = 9, tooltip = _('Attribute that contains the user id'), required = True) + userNameAttr = gui.TextField(length=64, label = _('User Name Attr'), defvalue = 'uid', order = 10, tooltip = _('Attributes that contains the user name (list of comma separated values)'), required = True) + groupNameAttr = gui.TextField(length=64, label = _('Group Name Attr'), defvalue = 'cn', order = 11, tooltip = _('Attribute that contains the group name'), required = True) + regex = gui.TextField(length=64, label = _('Regular Exp. for groups'), defvalue = '^(.*)', order = 12, tooltip = _('Regular Expression to extract the group name'), required = True) + + typeName = _('Regex LDAP Authenticator') + typeType = 'RegexLdapAuthenticator' + typeDescription = _('Regular Expressions LDAP authenticator') + iconFile = 'auth.png' + + # If it has and external source where to get "new" users (groups must be declared inside UDS) + isExternalSource = True + # If we need to enter the password for this user + needsPassword = False + # Label for username field + userNameLabel = _('Username') + # Label for group field + groupNameLabel = _("Group") + # Label for password field + passwordLabel = _("Password") + + def __init__(self, dbAuth, environment, values = None): + super(RegexLdap, self).__init__(dbAuth, environment, values) + if values != None: + self._host = values['host'] + self._port = values['port'] + self._ssl = gui.strToBool(values['ssl']) + self._username = values['username'] + self._password = values['password'] + self._timeout = values['timeout'] + self._ldapBase = values['ldapBase'] + self._userClass = values['userClass'] + self._userIdAttr = values['userIdAttr'] + self._groupNameAttr = values['groupNameAttr'] + self._regex = values['regex'] + self._userNameAttr = values['userNameAttr'] + else: + self._host = None + self._port = None + self._ssl = None + self._username = None + self._password = None + self._timeout = None + self._ldapBase = None + self._userClass = None + self._userIdAttr = None + self._groupNameAttr = None + self._regex = None + self._userNameAttr = None + self._connection = None + + def valuesDict(self): + return { 'host' : self._host, 'port' : self._port, 'ssl' : gui.boolToStr(self._ssl), + 'username' : self._username, 'password' : self._password, 'timeout' : self._timeout, + 'ldapBase' : self._ldapBase, 'userClass' : self._userClass, + 'userIdAttr' : self._userIdAttr, 'groupNameAttr' : self._groupNameAttr, 'regex' : self._regex, + 'userNameAttr' : self._userNameAttr + } + + def __str__(self): + return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, userIdAttr = {6}, groupNameAttr = {7}, reg.ex. = {8}, userName attr = {9}".format( + self._username, self._password, self._host, self._port, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._regex, + self._userNameAttr) + + def marshal(self): + return str.join('\t', ['v1', + self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password, self._timeout, + self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._regex, self._userNameAttr ]) + + def unmarshal(self, val): + data = val.split('\t') + if data[0] == 'v1': + logger.debug("Data: {0}".format(data[1:])) + self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._regex, self._userNameAttr = data[1:] + self._ssl = gui.strToBool(self._ssl) + + def __connection(self, username = None, password = None): + if self._connection is None or username is not None: # We want this method also to check credentials + l = None + cache = False + try: + #ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9) + ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) + schema = self._ssl and 'ldaps' or 'ldap' + port = self._port != '389' and ':' + self._port or '' + uri = "%s://%s%s" % (schema, self._host, port) + logger.debug('Ldap uri: {0}'.format(uri)) + l = ldap.initialize(uri=uri) + l.network_timeout = l.timeout = int(self._timeout) + l.protocol_version = ldap.VERSION3 + + if username is None: + cache = True + username = self._username + password = self._password + + l.simple_bind_s(who = username, cred = password) + except ldap.LDAPError, e: + str = _('Ldap connection error: ') + if type(e.message) == dict: + str += e.message.has_key('info') and e.message['info'] + ',' or '' + str += e.message.has_key('desc') and e.message['desc'] or '' + else : + str += str(e) + raise Exception(str) + if cache is True: + self._connection = l + else: + return l # Do not cache nor overwrite "global" connection + return self._connection + + def __getUser(self, username): + try: + con = self.__connection() + filter = '(&(objectClass=%s)(%s=%s))' % (self._userClass, self._userIdAttr, username) + attrlist = self._userNameAttr.split(',') + [self._userIdAttr, self._groupNameAttr] + logger.debug('Getuser filter: {0}, attr list: {1}'.format(filter, attrlist)) + res = con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, + filterstr = filter, attrlist = attrlist, sizelimit = LDAP_RESULT_LIMIT)[0] + usr = dict(( k, '' ) for k in attrlist) + usr.update(res[1]) + usr.update( {'dn' : res[0], '_id' : username }) + logger.debug('Usr: {0}'.format(usr)) + return usr + except Exception, e: + logger.exception('Exception:') + return None + + def __getGroups(self, usr): + grps = usr[self._groupNameAttr] + if type(grps) is not list: + grps = [grps] + logger.debug("Groups: {0}".format(grps)) + logger.debug("Re: {0}".format(self._regex)) + rg = re.compile(self._regex) + res = [] + for g in grps: + ma = rg.match(g) + if ma is not None: + for m in ma.groups(): + res.append(m) + logger.debug('Res: {0}'.format(res)) + return res + + def __getUserRealName(self, usr): + return ' '.join([ (type(usr.get(id, '')) is list and ' '.join(( str(k) for k in usr.get(id, ''))) or str(usr.get(id, ''))) for id in self._userNameAttr.split(',') ]).strip() + + def authenticate(self, username, credentials, groupsManager): + ''' + Must authenticate the user. + We can have to different situations here: + 1.- The authenticator is external source, what means that users may be unknown to system before callig this + 2.- The authenticator isn't external source, what means that users have been manually added to system and are known before this call + We receive the username, the credentials used (normally password, but can be a public key or something related to pk) and a group manager. + The group manager is responsible for letting know the authenticator which groups we currently has active. + @see: uds.core.auths.GroupsManager + ''' + try: + # Locate the user at LDAP + usr = self.__getUser(username) + + if usr is None: + return False + + # Let's see first if it credentials are fine + self.__connection(usr['dn'], credentials) # Will raise an exception if it can't connect + + groupsManager.validate(self.__getGroups(usr)) + + return True + + except Exception: + return False + + def createUser(self, usrData): + ''' + We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..) + External sources already has the user cause they are managed externally, so, it can at most test if the users exists on external source + before accepting it. + Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to + @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password + @return: Raises an exception (AuthException) it things didn't went fine + ''' + res = self.__getUser(usrData['name']) + if res is None: + raise AuthenticatorException(_('Username not found')) + # Fills back realName field + usrData['realName'] = self.__getUserRealName(res) + + + def getRealName(self, username): + ''' + Tries to get the real name of an user + ''' + res = self.__getUser(username) + if res is None: + return username + return self.__getUserRealName(res) + + def modifyUser(self, usrData): + ''' + We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..) + Modify user has no reason on external sources, so it will never be used (probably) + Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to + @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password + @return: Raises an exception it things doesn't go fine + ''' + return self.createUser(usrData) + + def createGroup(self, groupData): + ''' + We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..) + External sources already has its own groups and, at most, it can check if it exists on external source before accepting it + Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to + @params groupData: a dict that has, at least, name, comments and active + @return: Raises an exception it things doesn't go fine + ''' + pass + + + def getGroups(self, username, groupsManager): + ''' + Looks for the real groups to which the specified user belongs + Updates groups manager with valid groups + Remember to override it in derived authentication if needed (external auths will need this, for internal authenticators this is never used) + ''' + user = self.__getUser(username) + if user is None: + raise AuthenticatorException(_('Username not found')) + groups = self.__getGroups(user) + res = [] + for g in groups: + gg = groupsManager.validate(g) + + def searchUsers(self, pattern): + try: + con = self.__connection() + res = [] + for r in con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(&(objectClass=%s)(%s=%s*))' % (self._userClass, self._userIdAttr, pattern), sizelimit=LDAP_RESULT_LIMIT): + usrId = r[1].get(self._userIdAttr, '') + usrId = type(usrId) == list and usrId[0] or usrId + res.append( { 'id' : usrId, + 'name' : self.__getUserRealName(r[1]) } ) + return res + except Exception, e: + logger.exception("Exception: ") + raise AuthenticatorException(_('Too many results, be more specific')) + + @staticmethod + def test(env, data): + try: + auth = RegexLdap(None, env, data) + return auth.testConnection() + except Exception, e: + logger.error("Exception found testing Simple LDAP auth {0}: {1}".format(e.__class__, e)) + return [False, "Error testing connection"] + + def testConnection(self): + try: + con = self.__connection() + except Exception, e: + return [False, str(e)] + + try: + con.search_s(base = self._ldapBase, scope = ldap.SCOPE_BASE) + except Exception: + return [False, _('Ldap search base is incorrect')] + + try: + if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(objectClass=%s)' % self._userClass, sizelimit=1)) == 1: + raise Exception() + return [False, _('Ldap user class seems to be incorrect (no user found by that class)')] + except Exception, e: + # If found 1 or more, all right + pass + + try: + if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(objectClass=%s)' % self._groupClass, sizelimit=1)) == 1: + raise Exception() + return [False, _('Ldap group class seems to be incorrect (no group found by that class)')] + except Exception, e: + # If found 1 or more, all right + pass + + try: + if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(%s=*)' % self._userIdAttr, sizelimit=1)) == 1: + raise Exception() + return [False, _('Ldap user id attribute seems to be incorrect (no user found by that attribute)')] + except Exception, e: + # If found 1 or more, all right + pass + + try: + if self._groupNameAttr == 'dn': + raise Exception() # Can't search entries by dn, so this is not possible and dn is always retrieved + if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(%s=*)' % self._groupNameAttr, sizelimit=1)) == 1: + raise Exception() + return [False, _('Ldap group id attribute seems to be incorrect (no group found by that attribute)')] + except Exception, e: + # If found 1 or more, all right + pass + + # Now test objectclass and attribute of users + try: + if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(&(objectClass=%s)(%s=*))' % (self._userClass, self._userIdAttr), sizelimit=1)) == 1: + raise Exception() + return [False, _('Ldap user class or user id attr is probably wrong (can\'t find any user with both conditions)')] + except Exception as e: + # If found 1 or more, all right + pass + + # Now try to test regular expression to see if it matches anything ( + try: + # Check the existence of at least a () grouping + # Check validity of regular expression (try to compile it) + # this only right now + pass + except Exception as e: + pass + + + return [True, _("Connection params seem correct, test was succesfully executed")] + \ No newline at end of file diff --git a/server/src/uds/auths/RegexLdap/__init__.py b/server/src/uds/auths/RegexLdap/__init__.py new file mode 100644 index 000000000..3554b7412 --- /dev/null +++ b/server/src/uds/auths/RegexLdap/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from Authenticator import RegexLdap + diff --git a/server/src/uds/auths/RegexLdap/auth.png b/server/src/uds/auths/RegexLdap/auth.png new file mode 100644 index 0000000000000000000000000000000000000000..47566edc64273e0445680e1cbf73c34193460ac4 GIT binary patch literal 3322 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/auths/Sample/SampleAuth.py b/server/src/uds/auths/Sample/SampleAuth.py new file mode 100644 index 000000000..7b3699b8f --- /dev/null +++ b/server/src/uds/auths/Sample/SampleAuth.py @@ -0,0 +1,307 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.utils.translation import ugettext_noop as translatable +from uds.core.ui.UserInterface import gui +from uds.core import auths + +import logging + +logger = logging.getLogger(__name__) + +class SampleAuth(auths.Authenticator): + ''' + This class represents a sample authenticator. + + As this, it will provide: + * The authenticator functionality + * 3 Groups, "Mortals", "Gods" and "Daemons", just random group names selected.. :-), + plus groups that we enter at Authenticator form, from admin interface. + * Search of groups (inside the 3 groups used in this sample plus entered) + * Search for people (will return the search string + 000...999 as usernames) + * The Required form description for administration interface, so admins can create + new authenticators of this kind. + + In this sample, we will provide a simple standard auth, with owner drawn + login form that will simply show users that has been created and allow web user + to select one of them. + + For this class to get visible at administration client as a authenticator type, + we MUST register it at package __init__ + + :note: At class level, the translations must be simply marked as so + using ugettext_noop. This is done in this way because we will translate + the string when it is sent to the administration client. + ''' + + #: Name of type, used at administration interface to identify this + #: authenticator (i.e. LDAP, SAML, ...) + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeName = translatable('Sample Authenticator') + + #: Name of type used by Managers to identify this type of service + #: We could have used here the Class name, but we decided that the + #: module implementator will be the one that will provide a name that + #: will relation the class (type) and that name. + typeType = 'SampleAuthenticator' + + #: Description shown at administration level for this authenticator. + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeDescription = translatable('Sample dummy authenticator') + + + #: Icon file, used to represent this authenticator at administration interface + #: This file should be at same folder as this class is, except if you provide + #: your own :py:meth:uds.core.BaseModule.BaseModule.icon method. + iconFile = 'auth.png' + + #: Mark this authenticator as that the users comes from outside the UDS + #: database, that are most authenticator (except Internal DB) + #: True is the default value, so we do not need it in fact + # isExternalSource = True + + #: If we need to enter the password for this user when creating a new + #: user at administration interface. Used basically by internal authenticator. + #: False is the default value, so this is not needed in fact + #: needsPassword = False + + #: Label for username field, shown at administration interface user form. + userNameLabel = translatable('Fake User') + + # Label for group field, shown at administration interface user form. + groupNameLabel = translatable('Fake Group') + + #: Definition of this type of authenticator form + #: We will define a simple form where we will use a simple + #: list editor to allow entering a few group names + + groups = gui.EditableList(label=translatable('Groups'), values = ['Gods', 'Daemons', 'Mortals']) + + def initialize(self, values): + ''' + Simply check if we have + at least one group in the list + ''' + + # To avoid problems, we only check data if values are passed + # If values are not passed in, form data will only be available after + # unserialization, and at this point all will be default values + # so self.groups.value will be [] + if values is not None and len(self.groups.value) < 2: + raise auths.Authenticator.ValidationException(translatable('We need more that two items!')) + + def searchUsers(self, pattern): + ''' + Here we will receive a pattern for searching users. + + This method is invoked from interface, so an administrator can search users. + + If we do not provide this method, the authenticator will not provide search + facility for users. In our case, we will simply return a list of users + (array of dictionaries with ids and names) with the pattern plus 1..10 + ''' + return [ { 'id' : '{0}-{1}'.format(pattern, a), 'name' : '{0} number {1}'.format(pattern, a) } for a in range(1, 10)] + + def searchGroups(self, pattern): + ''' + Here we we will receive a patter for searching groups. + + In this sample, we will try to locate elements that where entered at + sample authenticator form (when created), and return the ones that + contains the pattern indicated. + ''' + pattern = pattern.lower() + res = [] + for g in self.groups.value: + if g.lower().find(pattern) != -1: + res.append({'id' : g, 'name' : ''}) + return res + + def authenticate(self, username, credentials, groupsManager): + ''' + This method is invoked by UDS whenever it needs an user to be authenticated. + It is used from web interface, but also from administration interface to + check credentials and access of user. + + The tricky part of this method is the groupsManager, but it's easy to + understand what is used it for. + + Imagine some authenticator, for example, an LDAP. It has its users, it has + its groups, and it has it relations (which user belongs to which group). + + Now think about UDS. UDS know nothing about this, it only knows what + the administator has entered at admin interface (groups mainly, but he can + create users also). + + UDS knows about this groups, but we need to relation those with the ones + know by the authenticator. + + To do this, we have created a simple mechanism, where the authenticator + receives a groupsManager, that knows all groups known by UDS, and has + the method so the authenticator can say, for the username being validated, + to which uds groups it belongs to. + + This is done using the :py:meth:uds.core.auths.GroupsManager.GroupsManager.validate + method of the provided groups manager. + + At return, UDS will do two things: + * If there is no group inside the groupsManager mareked as valid, it will + denied access. + * If there is some groups marked as valid, it will refresh the known + UDS relations (this means that the database will be refresehd so the user + has valid groups). + + This also means that the group membership is only checked at user login (well, + in fact its also checked when an administrator tries to modify an user) + + So, authenticate must not also validate the user credentials, but also + indicate the group membership of this user inside UDS. + + :note: groupsManager is an in/out parameter + ''' + if username != credentials: # All users with same username and password are allowed + return False + + # Now the tricky part. We will make this user belong to groups that contains at leat + # two letters equals to the groups names known by UDS + # For this, we will ask the groups manager for the groups names, and will check that and, + # if the user match this criteria, will mark that group as valid + for g in groupsManager.getGroupsNames(): + if len(set(g.lower()).intersection(username.lower())) >= 2: + groupsManager.validate(g) + + return True + + def getGroups(self, username, groupsManager): + ''' + As with authenticator part related to groupsManager, this + method will fill the groups to which the specified username belongs to. + + We have to fill up groupsManager from two different places, so it's not + a bad idea to make a method that get the "real" authenticator groups and + them simply call to :py:meth:uds.core.auths.GroupsManager.GroupsManager.validate + + In our case, we simply repeat the process that we also do at authenticate + ''' + for g in groupsManager.getGroupsNames(): + if len(set(g.lower()).intersection(username.lower())) >= 2: + groupsManager.validate(g) + + def getHtml(self, request): + ''' + If we override this method from the base one, we are telling UDS + that we want to draw our own authenticator. + + This way, we can do whataver we want here (for example redirect to a site + for a single sign on) generation our ouwn html (and javascript ofc). + + ''' + # Here there is a sample, commented out + # In this sample, we will make a list of valid users, and when clicked, + # it will fill up original form with username and same password, and submit it. + #res = '' + #for u in self.dbAuthenticator().users.all(): + # res += '{0}
'.format(u.name) + # + #res += '' + #return res + + # I know, this is a bit ugly, but this is just a sample :-) + + res = '

Login name:

' + res +='

Login

' + return res + + + def authCallback(self, parameters): + ''' + We provide this as a sample of callback for an user. + We will accept all petitions that has "user" parameter + + This method will get invoked by url redirections, probably by an SSO. + + The idea behind this is that we can provide: + * Simple user/password authentications + * Own authentications (not UDS, authenticator "owned"), but with no redirections + * Own authentications via redirections (as most SSO will do) + + Here, we will receive the parameters for this + ''' + user = parameters.get('user', None) + + return user + + def createUser(self, usrData): + ''' + This method provides a "check oportunity" to authenticators for users created + manually at administration interface. + + If we do not provide this method, the administration interface will not allow + to create new users "by hand", i mean, the "new" options from menus will dissapear. + + usrData is a dictionary that contains the input parameters from user, + with at least name, realName, comments, state & password. + + We can modify this parameters, we can modify ALL, but name is not recommended to + modify it unles you know what you are doing. + + Here, we will set the state to "Inactive" and realName to the same as username, but twice :-) + ''' + from uds.core.util.State import State + usrData['realName'] = usrData['name'] + ' ' + usrData['name'] + usrData['state'] = State.INACTIVE + + def modifyUser(self, usrData): + ''' + This method provides a "check opportunity" to authenticator for users modified + at administration interface. + + If we do not provide this method, nothing will happen (default one does nothing, but + it's valid). + + usrData is a dictionary that contains the input parameters from user, + with at least name, realName, comments, state & password. + + We can modify this parameters, we can modify ALL, but name is not recommended to + modify it unless you know what you are doing. + + Here, we will simply update the realName of the user, and (we have to take care + this this kind of things) modify the userName to a new one, the original plus '-1' + ''' + usrData['realName'] = usrData['name'] + ' ' + usrData['name'] + usrData['name'] = usrData['name'] + '-1' diff --git a/server/src/uds/auths/Sample/__init__.py b/server/src/uds/auths/Sample/__init__.py new file mode 100644 index 000000000..7ed261f46 --- /dev/null +++ b/server/src/uds/auths/Sample/__init__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' +Sample authenticator. We import here the module, and uds.auths module will +take care of registering it as provider + +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from SampleAuth import SampleAuth + diff --git a/server/src/uds/auths/Sample/auth.png b/server/src/uds/auths/Sample/auth.png new file mode 100644 index 0000000000000000000000000000000000000000..c8560b1d52b07059aa5066b274114304e7807b91 GIT binary patch literal 1225 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/auths/__init__.py b/server/src/uds/auths/__init__.py new file mode 100644 index 000000000..be007c225 --- /dev/null +++ b/server/src/uds/auths/__init__.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +Authentication modules for uds are contained inside this module. +To create a new authentication module, you will need to follow this steps: + 1.- Create the authentication module, probably based on an existing one + 2.- Insert the module as child of this module + 3.- Import the class of your authentication module at __init__. For example:: + from Authenticator import SimpleAthenticator + 4.- Done. At Server restart, the module will be recognized, loaded and treated + +The registration of modules is done locating subclases of :py:class:`uds.core.auths.Authentication` + +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +def __init__(): + ''' + This imports all packages that are descendant of this package, and, after that, + it register all subclases of authenticator as + ''' + import os.path, pkgutil + import sys + from uds.core import auths + + # Dinamycally import children of this package. The __init__.py files must register, if needed, inside AuthsFactory + pkgpath = os.path.dirname(sys.modules[__name__].__file__) + for _, name, _ in pkgutil.iter_modules([pkgpath]): + __import__(name, globals(), locals(), [], -1) + + a = auths.Authenticator + for cls in a.__subclasses__(): + auths.factory().insert(cls) + +__init__() diff --git a/server/src/uds/core/BaseModule.py b/server/src/uds/core/BaseModule.py new file mode 100644 index 000000000..5bd54c6f4 --- /dev/null +++ b/server/src/uds/core/BaseModule.py @@ -0,0 +1,266 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext as _ +from uds.core.ui.UserInterface import UserInterface +from uds.core.Environment import Environmentable +from uds.core.Serializable import Serializable +import base64, os.path, sys, logging + +logger = logging.getLogger(__name__) + +class BaseModule(UserInterface, Environmentable, Serializable): + ''' + Base class for all modules used by UDS. + This base module provides all the needed methods that modules must implement + + All modules must, at least, implement the following: + + * Attributes: + * :py:attr:`.typeName`: + Name for this type of module (human readable) to assign to the module (string) + This name will be used to let the administrator identify this module. + * :py:attr:`.typeType`: + Name for this type of module (machine only) to assing to the module (string) + This name will be used internally to identify when a serialized module corresponds with this class. + * :py:attr:`.typeDescription`: + Description for this type of module. + This descriptio will be used to let the administrator identify what this module provides + * :py:attr:`.iconFile`: This is an icon file, in png format, used at administration client to identify this module. + This parameter may be optionall if you override the "icon" method. + * Own Methods: + * :py:meth:`.__init__` + The default constructor. The environment value is always provided (see Environment), but the + default values provided can be None. + Remember to allow the instantiation of the module with default params, because when deserialization is done, + the process is first instatiate with an environment but no parameters and then call "unmarshal" from Serializable. + * :py:meth:`.test` + * :py:meth:`.check` + * :py:meth:`.destroy`: Optional + * :py:meth:`.icon`: Optional, if you provide an icon file, this method loads it from module folder, + but you can override this so the icon is obtained from other source. + * :py:meth:`.marshal` + By default, this method serializes the values provided by user in form fields. You can override it, + but now it's not needed because you can access config vars using Form Fields. + + Anyway, if you override this method, you must also override next one + * :py:meth:`.unmarshal` + By default, this method de-serializes the values provided by user in form fields. You can override it, + but now it's not needed because you can access config vars using Form Fields. + + Anyway, if you override this method, you must also override previous one + + * UserInterface Methods: + * :py:meth:`uds.core.ui.UserInterface.UserInterface.valuesDict` + This method, by default, provides the values contained in the form fields. If you don't override the marshal and + unmarshal, this method should be fine as is for you also. + + + Environmentable is a base class that provides utility method to access a separate Environment for every single + module. + ''' + #: Which coded to use to encode module by default. + #: This overrides the Environmentable and Serializable Attribute, but in all cases we are using 'base64' + CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded + + #: Basic name used to provide the administrator an "huma readable" form for the module + typeName = 'Base Module' + #: Internal type name, used by system to locate this module + typeType = 'BaseModule' + #: Description of this module, used at admin level + typeDescription = 'Base Module' + #: Icon file, relative to module folders + iconFile = 'base.png' # This is expected to be png, use this format always + + class ValidationException(Exception): + ''' + Exception used to indicate that the params assigned are invalid + ''' + + @classmethod + def name(cls): + ''' + Returns "translated" typeName, using ugettext for transforming + cls.typeName + + Args: + cls: This is a class method, so cls is the class + + Returns: + Translated type name (using ugettext) + ''' + return _(cls.typeName) + + @classmethod + def type(cls): + ''' + Returns typeType + + Args: + cls: This is a class method, so cls is the class + + Returns: + the typeType of this class (or derived class) + ''' + return cls.typeType + + @classmethod + def description(cls): + ''' + This method returns the "translated" description, that is, using + ugettext for transforming cls.typeDescription. + + Args: + cls: This is a class method, so cls is the class + + Returns: + Translated description (using ugettext) + + ''' + return _(cls.typeDescription) + + + @classmethod + def icon(cls, inBase64 = True): + ''' + Reads the file specified by iconFile at module folder, and returns it content. + This is used to obtain an icon so administration can represent it. + + Args: + cls: Class + + inBase64: If true, the image will be returned as base 64 encoded + + Returns: + Base 64 encoded or raw image, obtained from the specified file at + 'iconFile' class attribute + ''' + logger.debug('Loading icon for class {0} ({1})'.format(cls, cls.iconFile)) + file_ = open( os.path.dirname(sys.modules[cls.__module__].__file__) + '/' + cls.iconFile, 'rb') + data = file_.read() + file_.close() + if inBase64 == True: + return base64.encodestring(data) + else: + return data + + @staticmethod + def test(env, data): + ''' + Test if the connection data is ok. + + Returns an array, first value indicates "Ok" if true, "Bad" or "Error" + if false. Second is a string describing operation + + Args: + env: environment passed for testing (temporal environment passed) + + data: data passed for testing (data obtained from the form + definition) + + Returns: + Array of two elements, first is True of False, depending on test + (True is all right, false is error), + second is an String with error, preferably internacionalizated.. + ''' + return [True, _("No connection checking method is implemented.")] + + def __init__(self, environment, values = None): + ''' + Do not forget to invoke this in your derived class using + "super(self.__class__, self).__init__(environment, values)". + + We want to use the env, cache and storage methods outside class. + If not called, you must implement your own methods. + + cache and storage are "convenient" methods to access _env.cache() and + _env.storage() + + The values param is passed directly to UserInterface base. + + The environment param is passed directly to environment. + + Values are passed to __initialize__ method. It this is not None, + the values contains a dictionary of values received from administration gui, + that contains the form data requested from user. + + If you override marshal, unmarshal and inherited UserInterface method + valuesDict, you must also take account of values (dict) provided at the + __init__ method of your class. + ''' + UserInterface.__init__(self, values) + Environmentable.__init__(self, environment) + Serializable.__init__(self) + + def __str__(self): + return "Base Module" + + def marshal(self): + ''' + By default and if not overriden by descendants, this method, overridden + from Serializable, and returns the serialization of + form field stored values. + ''' + return self.serializeForm() + + def unmarshal(self, str_): + ''' + By default and if not overriden by descendants, this method recovers + data serialized using serializeForm + ''' + self.unserializeForm(str_) + + def check(self): + ''' + Method that will provide the "check" capability for the module. + + The return value that this method must provide is simply an string, + preferable internacionalizated. + + Returns: + Internacionalized (using ugettext) string of result of the check. + ''' + return _("No check method provided.") + + def destroy(self): + ''' + Invoked before deleting an module from database. + + Do whatever needed here, as deleting associated data if needed + (no example come to my head right now... :-) ) + + Returns: + Nothing + ''' + pass + diff --git a/server/src/uds/core/Environment.py b/server/src/uds/core/Environment.py new file mode 100644 index 000000000..b0d2f189a --- /dev/null +++ b/server/src/uds/core/Environment.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +TEMP_ENV = 'temporary' +GLOBAL_ENV = 'global' + +class Environment(object): + ''' + Class to manipulate the associated environment with "environmentable" classes (mainly modules). + It purpose is to provide an "object owned" environment, so every db record can contain associated values + not stored with main module data. + The environment is composed of a "cache" and a "storage". First are volatile data, while second are persistent data. + ''' + + def __init__(self, uniqueKey, idGenerators = {}): + ''' + Initialized the Environment for the specified id + @param uniqueId: Key for this environment + @param idGenerators: Hash of generators of ids for this environment. This "generators of ids" feature + is used basically at User Services to auto-create ids for macs or names, using + {'mac' : UniqueMacGenerator, 'name' : UniqueNameGenerator } as argument. + ''' + from uds.core.util.Cache import Cache + from uds.core.util.Storage import Storage + self._key = uniqueKey + self._cache = Cache(uniqueKey) + self._storage = Storage(uniqueKey) + self._idGenerators = idGenerators + + def cache(self): + ''' + Method to acces the cache of the environment. + @return: a referente to a Cache instance + ''' + return self._cache + + def storage(self): + ''' + Method to acces the cache of the environment. + @return: a referente to an Storage Instance + ''' + return self._storage + + def idGenerators(self, generatorId): + ''' + The idea of generator of id is to obtain at some moment Ids with a proper generator. + If the environment do not contains generators of id, this method will return None. + The id generator feature is used by User Services to obtain different auto-id generators, as macs or names + @param generatorId: Id of the generator to obtain + @return: Generator for that id, or None if no generator for that id is found + ''' + if self._idGenerators.has_key(generatorId): + return self._idGenerators[generatorId] + return None + + def key(self): + ''' + @return: the key used for this environment + ''' + return self._key + + def clearRelatedData(self): + ''' + Removes all related information from database for this environment. + ''' + from uds.core.util.Cache import Cache + from uds.core.util.Storage import Storage + Cache.delete(self._key) + Storage.delete(self._key) + for __, v in self._idGenerators.iteritems(): + v.release() + + @staticmethod + def getEnvForTableElement(tblName, id_, idGeneratorsTypes = {}): + ''' + From a table name, and a id, tries to load the associated environment or creates a new + one if no environment exists at database. The table name and the id are used to obtain the key + for the environment, so each element at database can have its own environment. + @param tblName: Table name + @param id_: Id of the element (normally primary key of the record for which we want an environment) + @param idGeneratorsTypes: Associated Generators. Defaults to none + @return: Obtained associated environment (may be empty if none exists at database, but it will be valid) + ''' + name = 't-' + tblName + '-' + str(id_) + idGenerators = {} + for k,v in idGeneratorsTypes.iteritems(): + idGenerators[k] = v(name) + return Environment(name, idGenerators) + + @staticmethod + def getEnvForType(type_): + ''' + Obtains an environment associated with a type instead of a record + @param type_: Type + @return Associated Environment + ''' + return Environment('type-'+str(type_)) + + @staticmethod + def getTempEnv(): + ''' + Provides a temporary environment needed in some calls (test provider, for example) + It will not make environment persistent + ''' + return Environment(TEMP_ENV) # TODO: In fact, we should provide a "null" cache and a "null" storage, but for now this is right + + @staticmethod + def getGlobalEnv(): + ''' + Provides global environment + ''' + return Environment(GLOBAL_ENV) # This environment is a global environment for general utility. + +class Environmentable(object): + ''' + This is a base class provided for all objects that have an environment associated. These are mainly modules + ''' + + def __init__(self, environment): + ''' + Initialized the element + + Args: + environment: Environment to associate with + ''' + self._env = environment + + def setEnv(self, environment): + ''' + Assigns a new environment + + Args: + environment: Environment to assign + ''' + self._env = environment + + def env(self): + ''' + Utility method to access the envionment contained by this object + + Returns: + Environmnet for the object + ''' + return self._env + + def cache(self): + ''' + Utility method to access the cache of the environment containe by this object + + Returns: + Cache for the object + ''' + return self._env.cache() + + def storage(self): + ''' + Utility method to access the storage of the environment containe by this object + + Returns: + Storage for the object + ''' + return self._env.storage() + + def idGenerators(self, generatorId): + ''' + Utility method to access the id generator of the environment containe by this object + + Args: + generatorId: Id of the generator to obtain + + Returns: + Generator for the object and the id specified + ''' + return self._env.idGenerators(generatorId) + \ No newline at end of file diff --git a/server/src/uds/core/Serializable.py b/server/src/uds/core/Serializable.py new file mode 100644 index 000000000..d8c26624d --- /dev/null +++ b/server/src/uds/core/Serializable.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +class Serializable(object): + ''' + This class represents the interface that all serializable objects must provide. + + Every single serializable class must implement marshall & unmarshall methods. Also, the class must allow + to be initialized without parameters, so we can: + - Initialize the object with default values + - Read values from seralized data + ''' + # Codify codec constant + CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded + + def __init__(self): + pass + + def marshal(self): + ''' + This is the method that must be overriden in order to serialize an object. + + The system will use in fact 'seralize' and 'deserialize' methods, but theese are + only suitable methods to "codify" serialized values + + :note: This method must be overridden + ''' + raise Exception('Base marshaler called!!!') + + def unmarshal(self, str_): + ''' + This is the method that must be overriden in order to unserialize an object. + + The system will use in fact 'seralize' and 'deserialize' methods, but theese are + only convenients methods to "codify" serialized values. + + Take into account that _str can be '' (empty string), but hopefully it will never be none. + In that case, initialize the object with default values + + Args: + str _ : String readed from persistent storage to deseralilize + + :note: This method must be overridden + ''' + raise Exception('Base unmarshaler called!!!') + + def serialize(self): + ''' + Serializes and "obfuscates' the data. + + The codec used to encode the string is obtained from the instance CODEC, so derived classes can + overwrite this attribute to set another codec + ''' + return self.marshal().encode(self.CODEC) + + def unserialize(self, str_): + ''' + des-obfuscates the data and then de-serializes it via unmarshal method + + The codec used to decode the string is obtained from the instance CODEC, so derived classes can + overwrite this attribute to set another codec + ''' + return self.unmarshal(str_.decode(self.CODEC)) + \ No newline at end of file diff --git a/server/src/uds/core/__init__.py b/server/src/uds/core/__init__.py new file mode 100644 index 000000000..ffd183bff --- /dev/null +++ b/server/src/uds/core/__init__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +Core of UDS. +This package contains all core-related code for UDS +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +# Core needs tasks manager to register scheduled jobs, so we ensure of that here +from managers.TaskManager import TaskManager +import services +import auths + +TaskManager.registerScheduledTask() \ No newline at end of file diff --git a/server/src/uds/core/auths/AuthsFactory.py b/server/src/uds/core/auths/AuthsFactory.py new file mode 100644 index 000000000..b28f776e8 --- /dev/null +++ b/server/src/uds/core/auths/AuthsFactory.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +class AuthsFactory(object): + ''' + This class holds the register of all known authentication modules + inside UDS. + + It provides a way to register and recover Authentication providers. + ''' + _factory = None + + def __init__(self): + self._auths = {} + + @staticmethod + def factory(): + ''' + Returns the factory that keeps the register of authentication providers. + ''' + if AuthsFactory._factory == None: + AuthsFactory._factory = AuthsFactory() + return AuthsFactory._factory + + def providers(self): + ''' + Returns the list of authentication providers already registered. + ''' + return self._auths + + def insert(self, type_): + ''' + Registers a new authentication provider + ''' + self._auths[type_.type()] = type_ + + def lookup(self, typeName): + ''' + Tries to locate an authentication provider and by its name, and, if + not found, returns None + ''' + return self._auths.get(typeName, None) diff --git a/server/src/uds/core/auths/BaseAuthenticator.py b/server/src/uds/core/auths/BaseAuthenticator.py new file mode 100644 index 000000000..c5609a1e2 --- /dev/null +++ b/server/src/uds/core/auths/BaseAuthenticator.py @@ -0,0 +1,495 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +Base module for all authenticators + +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from uds.core.BaseModule import BaseModule +from django.utils.translation import ugettext_noop as translatable +from GroupsManager import GroupsManager +from Exceptions import InvalidUserException +import logging + +logger = logging.getLogger(__name__) + +class Authenticator(BaseModule): + ''' + This class represents the base interface to implement authenticators. + + An authenticator is responsible for managing user and groups of a kind + inside UDS. As so, it must provide a number of method and mechanics to + allow UDS to manage users and groups using that kind of authenticator. + + Some samples of authenticators are LDAP, Internal Database, SAML, CAS, ... + + As always, if you override __init__, do not forget to invoke base __init__ as this:: + + super(self.__class__, self).__init__(self, dbAuth, environment, values) + + This is a MUST, so internal structured gets filled correctly, so don't forget it!. + + The preferred method of doing initialization is to provide the :py:meth:`.initialize`, + and do not override __init__ method. This (initialize) will be invoked after + all internal initialization. + + There are basically two kind of authenticators, that are "Externals" and + "Internals". + + Internal authenticators are those where and administrator has created manually + the user at admin interface. The users are not created from an external source, + so if an user do not exist at UDS database, it will not be valid. + In other words, if you have an authenticator where you must create users, + you can modify them, you must assign passwords manually, and group membership + also must be assigned manually, the authenticator is not an externalSource. + + As you can notice, almost avery authenticator except internal db will be + external source, so, by default, attribute that indicates that is an external + source is set to True. + + + In fact, internal source authenticator is intended to allow UDS to identify + if the users come from internal DB (just the case of local authenticator), + or the users come from other sources. Also, this allos UDS to know when to + "update" group membership information for an user whenever it logs in. + + External authenticator are in fact all authenticators except local database, + so we have defined isExternalSource as True by default, that will be most + cases. + + :note: All attributes that are "translatable" here means that they will be + translated when provided to administration interface, so remember + to mark them in your own authenticators as "translatable" using + ugettext_noop. We have aliased it here to "translatable" so it's + easier to understand. + ''' + + #: Name of type, used at administration interface to identify this + #: authenticator (i.e. LDAP, SAML, ...) + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeName = translatable('Base Authenticator') + + #: Name of type used by Managers to identify this type of service + #: We could have used here the Class name, but we decided that the + #: module implementator will be the one that will provide a name that + #: will relation the class (type) and that name. + typeType = 'BaseAuthenticator' + + #: Description shown at administration level for this authenticator. + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeDescription = translatable('Base Authenticator') + + + #: Icon file, used to represent this authenticator at administration interface + #: This file should be at same folder as this class is, except if you provide + #: your own :py:meth:uds.core.BaseModule.BaseModule.icon method. + iconFile = 'auth.png' + + #: Mark this authenticator as that the users comes from outside the UDS + #: database, that are most authenticator (except Internal DB) + #: So, isInternalSource means that "user is kept at database only" + isExternalSource = True + + #: If we need to enter the password for this user when creating a new + #: user at administration interface. Used basically by internal authenticator. + needsPassword = False + + #: Label for username field, shown at administration interface user form. + userNameLabel = translatable('User name') + + #: Label for group field, shown at administration interface user form. + groupNameLabel = translatable('Group name') + + #: Label for password field, , shown at administration interface user form. + #: Not needed for external authenticators (where credentials are stored with + #: an already existing user. + passwordLabel = translatable('Password') + + from User import User + from Group import Group + + #: The type of user provided, normally standard user will be enough. + #: This is here so if we need it in some case, we can write our own + #: user class + userType = User + + #: The type of group provided, normally standard group will be enough + #: This is here so if we need it in some case, we can write our own + #: group class + groupType = Group + + def __init__(self, dbAuth, environment, values): + ''' + Instantiathes the authenticator. + @param dbAuth: Database object for the authenticator + @param environment: Environment for the authenticator + @param values: Values passed to element + ''' + self._dbAuth = dbAuth + super(Authenticator, self).__init__(environment, values) + self.initialize(values) + + def initialize(self, values): + ''' + This method will be invoked from __init__ constructor. + This is provided so you don't have to provide your own __init__ method, + and invoke base methods. + This will get invoked when all initialization stuff is done + + Args: + Values: If values is not none, this object is being initialized + from administration interface, and not unmarshal will be done. + If it's None, this is initialized internally, and unmarshal will + be called after this. + + Default implementation does nothing + ''' + pass + + def dbAuthenticator(self): + ''' + Helper method to access the Authenticator database object + ''' + return self._dbAuth + + def recreateGroups(self, user): + ''' + Helper method, not needed to be overriden. + It simply checks if the source is external and if so, recreates + the user groups for storing them at database. + + user param is a database user object + ''' + if self.isExternalSource == True: + groupsManager = GroupsManager(self._dbAuth) + self.getGroups(user.name, groupsManager) + user.groups = [ g.dbGroup() for g in groupsManager.getValidGroups()] + + def callbackUrl(self): + ''' + Helper method to return callback url for self (authenticator). + + This method will allow us to know where to do redirection in case + we need to use callback + ''' + from auth import authCallbackUrl + return authCallbackUrl(self.dbAuthenticator()) + + def searchUsers(self, pattern): + ''' + If you provide this method, the user will be allowed to search users, + that is, the search button at administration interface, at user form, + will be enabled. + + Returns an array of users that match the supplied pattern + If none found, returns empty array. + + Must return is an array of dictionaries that must contains 'id' and 'name' + example: [ {'id': 'user1', 'name': 'Nombre 1'} ] + + Args: + pattern: Pattern to search for (simple pattern, string) + + Returns + a list of found users for the pattern specified + ''' + return [] + + def searchGroups(self, pattern): + ''' + Returns an array of groups that match the supplied pattern + If none found, returns empty array. Items returned are BaseGroups (or derived) + If you override this method, the admin interface will allow the use of + "search" at group form. If not overriden, the search will not be allowed. + + Must return array of dictionaries that must contains 'id' and 'name' + example: [ {'id': 'user1', 'name': 'Nombre 1'} ] + + Default implementation returns empty array, but is never used because if + not overriden, search of groups will not be allowed. + ''' + return [] + + def authenticate(self, username, credentials, groupsManager): + ''' + This method must be overriden, and is responsible for authenticating + users. + + We can have to different situations here: + + * The authenticator is external source, what means that users may + be unknown to system before callig this + * The authenticator isn't external source, what means that users have + been manually added to system and are known before this call. + This will only happen at Internal DB Authenticator. + + We receive the username, the credentials used (normally password, but can + be a public key or something related to pk) and a group manager. + + The group manager is responsible for letting know the authenticator which + groups we currently has active. + + Args: + username: User name to authenticate + credentilas: Credentials for this user, (password, pki, or whatever needs to be used) + groupManager: Group manager to modify with groups to which this users belongs to. + + Returns: + True if authentication success, False if don't. + + See uds.core.auths.GroupsManager + + :note: This method must check not only that the user has valid credentials, but also + check the valid groups from groupsManager. + If this method returns false, of method getValidGroups of the groupsManager + passed into this method has no elements, the user will be considered invalid. + So remember to check validity of groups this user belongs to (inside the authenticator, + not inside UDS) using groupsManager.validate(group to which this users belongs to). + + This is done in this way, because UDS has only a subset of groups for this user, and + we let the authenticator decide inside wich groups of UDS this users is included. + ''' + return False + + def getForAuth(self, username): + ''' + Process the username for this authenticator and returns it. + This transformation is used for transports only, not for transforming + anything at login time. Transports that will need the username, will invoke + this method. + For example, ad authenticator can add '@domain' so transport use the complete + 'user@domain' instead of 'user'. + + Right now, all authenticators keep this value "as is", i mean, it simply + returns the unprocessed username + ''' + return username + + def getGroups(self, username, groupsManager): + ''' + Looks for the real groups to which the specified user belongs + Returns a list of groups. + Remember to override it in derived authentication if needed (external auths will need this, for internal authenticators this is never used) + ''' + return [] + + def getHtml(self, request): + ''' + If you override this method, and returns something different of None, + UDS will consider your authenticator as "Owner draw", that is, that it + will not use the standard form for user authentication. + + Args: + Request is the DJango request received for generating this html, + with included user ip at request.ip. + + We have here a few things that we should know for creating our own + html for authenticator: + + * We use jQuery, so your javascript can use it + * The id of the username input field is **id_user** + * The id of the password input field is **id_password** + * The id of the login form is **loginform** + * The id of the "back to login" link is **backToLogin** + + This is what happens when an authenticator that has getHtml method is + selected in the front end (from the combo shown): + + * The div with id **login** is hidden. + * The div with id **nonStandard** is shown + * Using Ajax, the html provided by this method is requested for + the authenticator + * The returned html is rendered inside **nonStandardLogin** div. + * The **nonStandard** div is shown. + + **nonStandard** div has two inner divs, **nonStandardLogin** and + **divBackToLogin**. If there is no standard auths, divBackToLogin is + erased. + + With this, and :py:meth:.authCallback method, we can add SSO engines + to UDS with no much problems. + ''' + return None + + def authCallback(self, parameters): + ''' + There is a view inside UDS, an url, that will redirect the petition + to this callback. + + If someone gets authenticated via this callback, the method will return + an "username" must be return. This username will be used to: + + * Add user to UDS + * Get user groups. + + So, if this callback is called, also get the membership to groups of the user, and keep them. + This method will have to keep track of those until UDS request that groups + using getGroups. (This is easy, using storage() provided with the environment (env()) + + If this returns None, or empty, the authentication will be considered "invalid" + and an error will be shown. + + :note: Keeping user information about group membership inside storage is highly recommended. + There will be calls to getGroups one an again, and also to getRealName, not just + at login, but at future (from admin interface, at user editing for example) + ''' + return None + + def getRealName(self, username): + ''' + Tries to get the real name of an user + + Default implementation returns just the same user name that is passed in. + ''' + return username + + def createUser(self, usrData): + ''' + This method is used when creating an user to allow the authenticator: + + * Check that the name inside usrData is fine + * Fill other (not name, if you don't know what are you doing) usrData dictionary values. + + This will be invoked from admin interface, when admin wants to create a new user + + modified usrData will be used to store values at database. + + Args: + usrData: Contains data received from user directly, that is a dictionary + with at least: name, realName, comments, state & password. + This is an in/out parameter, so you can modify, for example, + **realName** + + Returns: + Raises an exception if things didn't went fine, + return value is ignored, but modified usrData is used if this does not + raises an exception. + + Take care with whatever you modify here, you can even modify provided + name (login name!) to a new one! + + :note: If you have an SSO where you can't create an user from admin interface, + raise an exception here indicating that the creation can't be done. + Default implementation simply raises "AuthenticatorException" and + says that user can't be created manually + + ''' + raise InvalidUserException(translatable('Users can\'t be created inside this authenticator')) + + + def modifyUser(self, usrData): + ''' + This method is used when modifying an user to allow the authenticator: + + * Check that the name inside usrData is fine + * Fill other (not name, if you don't know what are you doing) usrData dictionary values. + + Args: + usrData: Contains data received from user directly, that is a dictionary + with at least: name, realName, comments, state & password. + This is an in/out parameter, so you can modify, for example, + **realName** + + + Returns: + Raises an exception if things didn't went fine, + return value is ignored, but modified usrData is used if this does not + raises an exception. + + Take care with whatever you modify here, you can even modify provided + name (login name!) to a new one! + + :note: By default, this will do nothing, as we can only modify "accesory" internal + data of users. + ''' + pass + + + def createGroup(self, groupData): + ''' + This method is used when creating a new group to allow the authenticator: + + * Check that the name inside groupData is fine + * Fill other (not name, if you don't know what are you doing) usrData dictionary values. + + This will be invoked from admin interface, when admin wants to create a new group. + + modified groupData will be used to store values at database. + + Args: + groupData: Contains data received from user directly, that is a dictionary + with at least: name, comments and active. + This is an in/out parameter, so you can modify, for example, + **comments** + + Returns: + Raises an exception if things didn't went fine, + return value is ignored, but modified groupData is used if this does not + raises an exception. + + Take care with whatever you modify here, you can even modify provided + name (group name) to a new one! + ''' + pass + + def removeUser(self, username): + ''' + Remove user is used whenever from the administration interface, or from other + internal workers, an user needs to be removed. + + This is a notification method, whenever an user gets removed from UDS, this + will get called. + + You can do here whatever you want, but you are not requested to do anything + at your authenticators. + + If this method raises an exception, the user will not be removed from UDS + ''' + pass + + # We don't have a "modify" group option. Once u have created it, the only way of changing it if removing it an recreating it with another name + + def removeGroup(self, groupname): + ''' + Remove user is used whenever from the administration interface, or from other + internal workers, an group needs to be removed. + + This is a notification method, whenever an group gets removed from UDS, this + will get called. + + You can do here whatever you want, but you are not requested to do anything + at your authenticators. + + If this method raises an exception, the group will not be removed from UDS + ''' + pass diff --git a/server/src/uds/core/auths/Exceptions.py b/server/src/uds/core/auths/Exceptions.py new file mode 100644 index 000000000..c68d17564 --- /dev/null +++ b/server/src/uds/core/auths/Exceptions.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +class AuthenticatorException(Exception): + ''' + Generic authentication exception + ''' + pass + +class InvalidUserException(Exception): + ''' + Invalid user specified. The user cant access the requested service + ''' + pass + +class InvalidAuthenticatorException(Exception): + ''' + Invalida authenticator has been specified + ''' + pass \ No newline at end of file diff --git a/server/src/uds/core/auths/Group.py b/server/src/uds/core/auths/Group.py new file mode 100644 index 000000000..e1178abb7 --- /dev/null +++ b/server/src/uds/core/auths/Group.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification,are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +import logging + +logger = logging.getLogger(__name__) + +class Group(object): + ''' + A group is simply a database group associated with its authenticator instance + + It's only constructor expect a database group as parameter. + ''' + + def __init__(self, dbGroup): + ''' + Initializes internal data + ''' + self._manager = dbGroup.getManager() + self._dbGroup = dbGroup + + def manager(self): + ''' + Returns the database authenticator associated with this group + ''' + return self._manager + + def dbGroup(self): + ''' + Returns the database group associated with this + ''' + return self._dbGroup diff --git a/server/src/uds/core/auths/GroupsManager.py b/server/src/uds/core/auths/GroupsManager.py new file mode 100644 index 000000000..84e4a574c --- /dev/null +++ b/server/src/uds/core/auths/GroupsManager.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.util.State import State +from Group import Group +import logging + +logger = logging.getLogger(__name__) + +class GroupsManager(object): + ''' + Manages registered groups for an specific authenticator. + + Most authenticators (except internal database one, that is an special case) + has their database of users and passwords outside UDS. Think, for example, + about LDAP. It has its own database of users and groups, and has its own + correspondence of which user belongs to which group. + + UDS Only knows a subset of this groups, those that the administrator has + registered inside UDS. + + To manage the equivalence between groups from the authenticator and UDS groups, + we provide a list of "known groups" by uds. The authenticator then makes the + correspondence, marking the groups (UDS groups) that the user belongs to as + valid. + + Managed groups names are compared using case insensitive comparison. + ''' + + def __init__(self, dbAuthenticator): + ''' + Initializes the groups manager. + The dbAuthenticator is the database record of the authenticator + to which this groupsManager will be associated + ''' + self._groups = {} # We just get active groups, inactive aren't visible to this class + for g in dbAuthenticator.groups.filter(state = State.ACTIVE): + self._groups[g.name.lower()] = { 'group': Group(g), 'valid': False } + + def contains(self, groupName): + ''' + Returns true if this groups manager contains the specified group name (string) + ''' + return self._groups.has_key(groupName.lower()) + + def getGroupsNames(self): + ''' + Return all groups names managed by this groups manager. The names are returned + as where inserted inside Database (most probably using administration interface) + ''' + for g in self._groups.itervalues(): + yield g['group'].dbGroup().name + + def getValidGroups(self): + ''' + returns the list of valid groups (:py:class:uds.core.auths.Group.Group) + ''' + res = [] + for g in self._groups.itervalues(): + if g['valid'] is True: + res.append(g['group']) + return res + + def hasValidGroups(self): + ''' + Checks if this groups manager has at least one group that has been + validated (using :py:meth:.validate) + ''' + for g in self._groups.itervalues(): + if g['valid'] is True: + return True + return False + + def getGroup(self, groupName): + ''' + If this groups manager contains that group manager, it returns the + :py:class:uds.core.auths.Group.Group representing that group name. + ''' + if self._groups.has_key(groupName.lower()): + return self._groups[groupName.lower()]['group'] + else: + return None + + def validate(self, groupName): + ''' + Validates that the group groupName passed in is valid for this group manager. + + It check that the group specified is known by this group manager. + + Args: + groupName: string, list or tuple of values (strings) to check + + Returns nothing, it changes the groups this groups contains attributes, + so they reflect the known groups that are considered valid. + ''' + if type(groupName) is tuple or type(groupName) is list: + for n in groupName: + self.validate(n) + else: + if self._groups.has_key(groupName.lower()): + self._groups[groupName.lower()]['valid'] = True + + def isValid(self, groupName): + ''' + Checks if this group name is marked as valid inside this groups manager. + Returns True if group name is marked as valid, False if it isn't. + ''' + if self._groups.has_key(groupName.lower()): + return self._groups[groupName.lower()]['valid'] + return False + + def __str__(self): + return "Groupsmanager: {0}".format(self._groups) + + diff --git a/server/src/uds/core/auths/User.py b/server/src/uds/core/auths/User.py new file mode 100644 index 000000000..880c6ed5a --- /dev/null +++ b/server/src/uds/core/auths/User.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +import logging + +logger = logging.getLogger(__name__) + +class User(object): + ''' + An user represents a database user, associated with its authenticator (instance) + and its groups. + ''' + + def __init__(self, dbUser): + self._manager = dbUser.getManager() + self._grpsManager = None + self._dbUser = dbUser + self._groups = None + + + def _groupsManager(self): + ''' + If the groups manager for this user already exists, it returns this. + If it does not exists, it creates one default from authenticator and + returns it. + ''' + from GroupsManager import GroupsManager + + if self._grpsManager == None: + self._grpsManager = GroupsManager(self._manager.dbAuthenticator()) + return self._grpsManager + + def groups(self): + ''' + Returns the valid groups for this user. + To do this, it will validate groups throuht authenticator instance using + :py:meth:`uds.core.auths.Authenticator.getGroups` method. + + :note: Once obtained valid groups, it caches them until object removal. + ''' + from uds.models import User as DbUser + from Group import Group + + if self._groups == None: + if self._manager.isExternalSource == True: + self._manager.getGroups(self._dbUser.name, self._groupsManager()) + self._groups = self._groupsManager().getValidGroups() + # This is just for updating "cached" data of this user, we only get real groups at login and at modify user operation + usr = DbUser.objects.get(pk=self._dbUser.id) + usr.groups = [ g.dbGroup() for g in self._groups ] + else: + # From db + usr = DbUser.objects.get(pk=self._dbUser.id) + self._groups = [] + for g in usr.groups.all(): + self._groups.append(Group(g)) + return self._groups + + + def manager(self): + ''' + Returns the authenticator instance + ''' + return self._manager + + def dbUser(self): + ''' + Returns the database user + ''' + return self._dbUser + diff --git a/server/src/uds/core/auths/__init__.py b/server/src/uds/core/auths/__init__.py new file mode 100644 index 000000000..77e3c53ba --- /dev/null +++ b/server/src/uds/core/auths/__init__.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +UDS authentication related interfaces and classes + +From 1.0 onwards, the refactoring of UDS has started. + +We can access the base service interfaces the old method, or recommended +and easier use the new one, that is "from uds.core.services import ..." + +The new valid names for classes are: + + +I think this is an easier to use and understand way of accessing this classes + +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from BaseAuthenticator import Authenticator +from User import User +from Group import Group +from GroupsManager import GroupsManager +import Exceptions + +def factory(): + ''' + Returns factory for register/access to authenticators + ''' + from AuthsFactory import AuthsFactory + return AuthsFactory.factory() + + diff --git a/server/src/uds/core/auths/auth.png b/server/src/uds/core/auths/auth.png new file mode 100644 index 0000000000000000000000000000000000000000..c8560b1d52b07059aa5066b274114304e7807b91 GIT binary patch literal 1225 zcmY+EeNa?Y7{=doKbB=-S=e0^S3WitcYsBZufPml3J@r9F)?bi3CA>xX;O=r9K$HT zs;SImBypN%imWuX5o|Qc6&;;S16KqoS{G>%WS9NEyLa#1(*-N+kN2E;|2V&So@d_o z>@id=l}c0+0FYLcm#h%(=&2Hmgfq#F>j0>aD@ux2Hnvg5>?pk+N{vk3yCvVXytsd> z?CIB3yL!e~o$cM0+p=x+oBgG=m(J%Wl)qChHz>z;I))Z}?m1RJU(5+Cj45NaWVwkLDduz`3W);4A*J&K4>D!qK+hfh4Wo%g2m>LHtBF)a0rs5^5*V;Q6hah5 zLuo|hgQ%d;goI*v4pUT^T<+`EV$0D9m(^_x0YS&=k|8-?ciTPMf-!4LC&Q>1U@^W%8yu^UW-~Ds_R?W95Z_YJ6 z`_}RsDOt(6mr)R|BVLH*->ito0mx-Szr=2@{j0N6#Rfz8NwuOT4-E%-M1cLe#bhzJ zg~gZ2YdAmVJ4`>SCe&lKfiw&U<2~dIii)RcEF}mC^LmF_g20Zkdwg5{79b0ER7I;4 z;IevkB5C2i#-@ECB+O2Pg2ar7A13AjjEQ(5XhGFl`5j9jOP6{%>=^bmxhxO$}vQt*9a9m1|8$1}p#IB0tD!Uw!1K;rOOo7v`LL_2uI^?yfpc_JAY- zh)I?WDN|F`LB@wUnG)NURzj3+A&%iWJ_Hy%?uCR)vKonri`b3qI6)n&9-J6A>wJR! znV_)XXjXRi=D5_fPC+~s@OWWXf(p$eoe0UKltdCksUEsVB+iv1m)8p+F-xUrRGrxg zshimh@qKsG^NBjk%GN7Ys#Z^LFDNrIut+WkV%&lU?lO~a;M#*OQGiu0`)H#^6RwC1 zk459mDD+u|&?B>p;OfnR4Ku3HS6b~^iO*M*l%F4pNl&N3laffskQtcXE5YBaoM?yP zPf*Nf*^IbMS{s`TW~Z6&vkr27H!XzE8tR@#O>OP=1!6e&rFi9)5$nj>r2b33?34qk zhrOBA*DfUcPj5L;d|1@p`_wbPpO6i{6un{Nf^BQEq-T~p8f#mc^ECX7B8vC>E4@zV zBB|Z3psiLX&oKSMQVem74;X(Cyi5?t2;ZRzpY4#1v1@D|N7|&@DRVkKHh%zL{6~>7 zX1h8PjPF 0: + retries -= 1 + try: + self.__insert(instance, delay, tag) + break + except Exception, e: + logger.info('Exception inserting a delayed task {0}: {1}'.format(str(e.__class__), e)) + # If retries == 0, this is a big error + if retries == 0: + logger.error("Could not insert delayed task!!!! {0} {1} {2}".format(instance, delay, tag)) + return False + return True + + @transaction.commit_on_success + def remove(self, tag): + try: + dbDelayedTask.objects.select_for_update().filter(tag=tag).delete() + except Exception as e: + logger.exception('Exception removing a delayed task {0}: {1}'.format(str(e.__class__), e)) + + @transaction.commit_on_success + def checkExists(self, tag): + number = 0 + try: + number = dbDelayedTask.objects.filter(tag=tag).count() + except Exception as e: + logger.error('Exception looking for a delayed task tag {0}'.format(tag)) + return number > 0 + + def run(self): + logger.debug("At loop") + while self._keepRunning: + try: + time.sleep(self.granularity) + self.executeOneDelayedTask() + except Exception, e: + logger.error('Unexpected exception at run loop {0}: {1}'.format(e.__class__, e)) diff --git a/server/src/uds/core/jobs/Job.py b/server/src/uds/core/jobs/Job.py new file mode 100644 index 000000000..2f38a5645 --- /dev/null +++ b/server/src/uds/core/jobs/Job.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.models import Scheduler +from uds.core.Environment import Environmentable +import logging + +logger = logging.getLogger(__name__) + +class Job(Environmentable): + # Default frecuency, once a day. Remenber that precision will be based on "granurality" of Scheduler + # If a job is used for delayed execution, this attribute is in fact ignored + frecuency = Scheduler.DAY + + def __init__(self, environment): + ''' + Remember to invoke parent init in derived clases using super(myClass,self).__init__(environmnet) if u want to use env(), cache() and storage() methods + ''' + Environmentable.__init__(self, environment) + + def execute(self): + try: + self.run() + except Exception, e: + logger.exception('Job {0} raised an exception:'.format(self.__class__)) + + def run(self): + ''' + You must provide your own "run" method to do whatever you need + ''' + logging.debug("Base run of job called for class") + pass \ No newline at end of file diff --git a/server/src/uds/core/jobs/JobsFactory.py b/server/src/uds/core/jobs/JobsFactory.py new file mode 100644 index 000000000..feca15c1a --- /dev/null +++ b/server/src/uds/core/jobs/JobsFactory.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from datetime import timedelta +import logging + +logger = logging.getLogger(__name__) + +class JobsFactory(object): + _factory = None + + def __init__(self): + self._jobs = {} + + @staticmethod + def factory(): + if JobsFactory._factory == None: + JobsFactory._factory = JobsFactory() + return JobsFactory._factory + + def jobs(self): + return self._jobs + + def insert(self, name, type): + try: + self._jobs[name] = type + except Exception, e: + logger.debug('Exception at insert in JobsFactory: {0}, {1}'.format(e.__class__, e)) + + def ensureJobsInDatabase(self): + from uds.models import Scheduler, getSqlDatetime, State + try: + logger.debug('Ensuring that jobs are registered inside database') + for name, type in self._jobs.iteritems(): + try: + # We use database server datetime + now = getSqlDatetime() + next = now + job = Scheduler.objects.create(name = name, frecuency = type.frecuency, last_execution = now, next_execution = next, state = State.FOR_EXECUTE) + except Exception: # already exists + job = Scheduler.objects.get(name=name) + job.frecuency = type.frecuency + job.save() + except Exception, e: + logger.debug('Exception at insert in JobsFactory: {0}, {1}'.format(e.__class__, e)) + + + def lookup(self, typeName): + try: + return self._jobs[typeName] + except KeyError: + return None diff --git a/server/src/uds/core/jobs/Scheduler.py b/server/src/uds/core/jobs/Scheduler.py new file mode 100644 index 000000000..50ecf5ad9 --- /dev/null +++ b/server/src/uds/core/jobs/Scheduler.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.db.models import Q +from django.db import transaction, DatabaseError +from uds.models import Scheduler as dbScheduler, getSqlDatetime, State +from uds.core.jobs.JobsFactory import JobsFactory +from datetime import datetime, timedelta +from socket import gethostname +import threading, time +import logging + +logger = logging.getLogger(__name__) + +class JobThread(threading.Thread): + def __init__(self, jobInstance, dbJob): + super(JobThread,self).__init__() + self._jobInstance = jobInstance + self._dbJobId = dbJob.id + + @transaction.commit_on_success + def run(self): + try: + self._jobInstance.execute() + except Exception: + logger.debug("Exception executing job {0}".format(self._dbJobId)) + try: + job = dbScheduler.objects.select_for_update().get(id=self._dbJobId) + job.state = State.FOR_EXECUTE + job.owner_server = '' + job.next_execution = getSqlDatetime() + timedelta(seconds = job.frecuency) + # Update state and last execution time at database + job.save() + except Exception as e: + # Erased from database, nothing hapens + logger.exception(e) + +class Scheduler(object): + granularity = 2 # We check for cron jobs every THIS seconds + + # to keep singleton Scheduler + _scheduler = None + + def __init__(self): + self._hostname = gethostname() + self._keepRunning = True + + @staticmethod + def scheduler(): + if Scheduler._scheduler == None: + Scheduler._scheduler = Scheduler() + return Scheduler._scheduler + + def notifyTermination(self): + self._keepRunning = False + + @transaction.commit_manually + def executeOneJob(self): + ''' + Looks for a job and executes it + ''' + jobInstance = None + try: + now = getSqlDatetime() # Datetimes are based on database server times + filter = Q(state = State.FOR_EXECUTE) & (Q(owner_server = self._hostname) | Q(owner_server = '')) & (Q(last_execution__gt = now) | Q(next_execution__lt = now)) + # If next execution is before now or last execution is in the future (clock changed on this server, we take that task as executable) + # This params are all set inside filter (look at __init__) + job = dbScheduler.objects.select_for_update().filter(filter).order_by('next_execution')[0] + jobInstance = job.getInstance() + + if jobInstance == None: + logger.error('Job instance can\'t be resolved for {0}, removing it'.format(job)) + job.delete() + transaction.commit() + return + job.state = State.RUNNING + job.owner_server = self._hostname + job.last_execution = now + job.save() + transaction.commit() + JobThread(jobInstance, job).start() # Do not instatiate thread, just run it + except IndexError: + transaction.rollback() + # Do nothing, there is no jobs for execution + return + except DatabaseError: + # Whis will happen whenever a connection error or a deadlock error happens + # This in fact means that we have to retry operation, and retry will happen on main loop + # Look at this http://dev.mysql.com/doc/refman/5.0/en/innodb-deadlocks.html + # I have got some deadlock errors, but looking at that url, i found that it is not so abnormal + logger.debug('Deadlock, no problem at all :-) (sounds hards, but really, no problem)') + transaction.rollback() # So django do not complains about this + + @transaction.commit_on_success + def releaseOwnShedules(self): + ''' + Releases all scheduleds being executed by this scheduler + ''' + dbScheduler.objects.select_for_update().filter(owner_server = self._hostname).update(owner_server = '', state = State.FOR_EXECUTE) + + + def run(self): + # We ensure that the jobs are also in database so we can + JobsFactory.factory().ensureJobsInDatabase() + self.releaseOwnShedules() + logger.debug("At loop") + while self._keepRunning: + try: + time.sleep(self.granularity) + self.executeOneJob() + except Exception, e: + logger.exception('Unexpected exception at run loop {0}: {1}'.format(e.__class__, e)) + diff --git a/server/src/uds/core/jobs/__init__.py b/server/src/uds/core/jobs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/core/managers/CryptoManager.py b/server/src/uds/core/managers/CryptoManager.py new file mode 100644 index 000000000..d666b2c2a --- /dev/null +++ b/server/src/uds/core/managers/CryptoManager.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from server.settings import RSA_KEY +from Crypto.PublicKey import RSA +from Crypto.Random import atfork +import hashlib, array + +# To generate an rsa key, first we need the crypt module +# next, we do: +# from Crypto.PublicKey import RSA +# import os +# RSA.generate(1024, os.urandom).exportKey() + +class CryptoManager(object): + CODEC = 'base64' + + instance = None + + def __init__(self): + self._rsa = RSA.importKey(RSA_KEY) + + @staticmethod + def manager(): + if CryptoManager.instance is None: + CryptoManager.instance = CryptoManager() + return CryptoManager.instance + + def encrypt(self, string): + atfork() + return self._rsa.encrypt(string, '')[0].encode(CryptoManager.CODEC) + + def decrypt(self, string): + atfork() + return self._rsa.decrypt(string.decode(CryptoManager.CODEC)) + + def xor(self, s1, s2): + mult = (len(s1)/len(s2)) + 1 + s1 = array.array('B', s1) + s2 = array.array('B', s2 * mult) + return array.array('B', (s1[i] ^ s2[i] for i in range(len(s1)))).tostring() + + def hash(self, string): + if string is '' or string is None: + return '' + return hashlib.sha1(string).hexdigest() diff --git a/server/src/uds/core/managers/DownloadsManager.py b/server/src/uds/core/managers/DownloadsManager.py new file mode 100644 index 000000000..57a11730e --- /dev/null +++ b/server/src/uds/core/managers/DownloadsManager.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +import os, tempfile, zipfile, uuid +from django.http import HttpResponse, Http404 +from django.core.servers.basehttp import FileWrapper +import logging + +logger = logging.getLogger(__name__) + +class DownloadsManager(object): + ''' + Manager so connectors can register their own downloadables + For registering, use at __init__.py of the conecto something like this: + from uds.core.managers.DownloadsManager import DownloadsManager + import os.path, sys + DownloadsManager.manager().registerDownloadable('test.exe', + _('comments for test'), + os.path.dirname(sys.modules[__package__].__file__) + '/files/test.exe', + 'application/x-msdos-program') + ''' + _manager = None + + def __init__(self): + self._downloadables = {} + self._namespace = uuid.UUID('627a37a5-e8db-431a-b783-73f7d20b4934') + + @staticmethod + def manager(): + if DownloadsManager._manager == None: + DownloadsManager._manager = DownloadsManager() + return DownloadsManager._manager + + + def registerDownloadable(self, name, comment, path, mime = 'application/octet-stream'): + ''' + Registers a downloadable file. + @param name: name shown + @param path: path to file + @params zip: If download as zip + ''' + id = str(uuid.uuid5(self._namespace, name)) + self._downloadables[id] = { 'name': name, 'comment' : comment, 'path' : path, 'mime' : mime } + + def getDownloadables(self): + return self._downloadables + + + def send(self, request, id): + if self._downloadables.has_key(id) is False: + return Http404() + return self.__send_file(request, self._downloadables[id]['name'], self._downloadables[id]['path'], self._downloadables[id]['mime']); + + def __send_file(self, request, name, filename, mime): + """ + Send a file through Django without loading the whole file into + memory at once. The FileWrapper will turn the file object into an + iterator for chunks of 8KB. + """ + wrapper = FileWrapper(file(filename)) + response = HttpResponse(wrapper, content_type=mime) + response['Content-Length'] = os.path.getsize(filename) + response['Content-Disposition'] = 'attachment; filename=' + name + return response + diff --git a/server/src/uds/core/managers/PublicationManager.py b/server/src/uds/core/managers/PublicationManager.py new file mode 100644 index 000000000..3e895e466 --- /dev/null +++ b/server/src/uds/core/managers/PublicationManager.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext as _ +from django.db import transaction +from uds.core.jobs.DelayedTask import DelayedTask +from uds.core.jobs.DelayedTaskRunner import DelayedTaskRunner +from uds.core.services.Exceptions import PublishException +from uds.models import DeployedServicePublication, getSqlDatetime, State +import logging + +logger = logging.getLogger(__name__) + +PUBTAG = 'pm-' + +class PublicationLauncher(DelayedTask): + def __init__(self, publish): + super(PublicationLauncher,self).__init__() + self._publishId = publish.id + + @transaction.commit_on_success + def run(self): + logger.debug('Publishing') + try: + dsp = DeployedServicePublication.objects.select_for_update().get(pk=self._publishId) + if dsp.state != State.LAUNCHING: # If not preparing (may has been canceled by user) just return + return + dsp.state = State.PREPARING + pi = dsp.getInstance() + state = pi.publish() + deployedService = dsp.deployed_service + deployedService.current_pub_revision += 1 + deployedService.save() + PublicationFinishChecker.checkAndUpdateState(dsp, pi, state) + except Exception as e: + logger.exception("Exception launching publication") + dsp.state = State.ERROR + dsp.save() + + +# Delayed Task that checks if a publication is done +class PublicationFinishChecker(DelayedTask): + def __init__(self, publish): + super(PublicationFinishChecker,self).__init__() + self._publishId = publish.id + self._state = publish.state + + @staticmethod + def checkAndUpdateState(dsp, pi, state): + ''' + Checks the value returned from invocation to publish or checkPublishingState, updating the dsp database object + Return True if it has to continue checking, False if finished + ''' + prevState = dsp.state + checkLater = False + if State.isFinished(state): + # Now we mark, if it exists, the previous usable publication as "Removable" + if State.isPreparing(prevState): + dsp.deployed_service.publications.filter(state=State.USABLE).update(state=State.REMOVABLE) + dsp.setState(State.USABLE) + dsp.deployed_service.markOldDeployedServicesAsRemovables(dsp) + elif State.isRemoving(prevState): + dsp.setState(State.REMOVED) + else: # State is canceling + dsp.setState(State.CANCELED) + # Mark all previous publications deployed services as removables + # and make this usable + pi.finish() + dsp.updateData(pi) + elif State.isErrored(state): + dsp.updateData(pi) + dsp.state = State.ERROR + else: + checkLater = True # The task is running + dsp.updateData(pi) + + dsp.save() + if checkLater: + PublicationFinishChecker.checkLater(dsp, pi) + + @staticmethod + def checkLater(dsp, pi): + ''' + Inserts a task in the delayedTaskRunner so we can check the state of this publication + @param dps: Database object for DeployedServicePublication + @param pi: Instance of Publication manager for the object + ''' + DelayedTaskRunner.runner().insert(PublicationFinishChecker(dsp), pi.suggestedTime, PUBTAG + str(dsp.id)) + + @transaction.commit_on_success + def run(self): + logger.debug('Checking publication finished {0}'.format(self._publishId)) + try : + dsp = DeployedServicePublication.objects.select_for_update().get(pk=self._publishId) + if dsp.state != self._state: + logger.debug('Task overrided by another task (state of item changed)') + else: + pi = dsp.getInstance() + logger.debug("publication instance class: {0}".format(pi.__class__)) + state = pi.checkState() + PublicationFinishChecker.checkAndUpdateState(dsp, pi, state) + except Exception, e: + logger.debug('Deployed service not found (erased from database) {0} : {1}'.format(e.__class__, e)) + + +class PublicationManager(object): + _manager = None + + def __init__(self): + pass + + @staticmethod + def manager(): + if PublicationManager._manager == None: + PublicationManager._manager = PublicationManager() + return PublicationManager._manager + + + @transaction.commit_on_success + def publish(self, deployedService): + if deployedService.publications.select_for_update().filter(state__in=State.PUBLISH_STATES).count() > 0: + raise PublishException(_('Already publishing. Wait for previous publication to finish and try again')) + try: + now = getSqlDatetime() + dsp = deployedService.publications.create(state = State.LAUNCHING, state_date = now, publish_date = now, revision = deployedService.current_pub_revision) + DelayedTaskRunner.runner().insert(PublicationLauncher(dsp), 4, PUBTAG + str(dsp.id)) + except Exception as e: + raise PublishException(str(e)) + + @transaction.commit_on_success + def cancel(self,dsp): + dsp = DeployedServicePublication.objects.select_for_update().get(id=dsp.id) + if dsp.state not in State.PUBLISH_STATES: + raise PublishException(_('Can\'t cancel non running publication')) + + if dsp.state == State.LAUNCHING: + dsp.state = State.CANCELED + dsp.save() + return dsp + + try: + pi = dsp.getInstance() + state = pi.cancel() + dsp.setState(State.CANCELING) + PublicationFinishChecker.checkAndUpdateState(dsp, pi, state) + return dsp + except Exception, e: + raise PublishException(str(e)) + + def unpublish(self, dsp): + if State.isUsable(dsp.state) == False and State.isRemovable(dsp.state) == False: + raise PublishException(_('Can\'t unpublish non usable publication')) + # TODO: Call assignation manager to remove removable items + if dsp.userServices.exclude(state__in=State.INFO_STATES).count() > 0: + raise PublishException(_('Can\'t unpublish publications with services in process')) + try: + pi = dsp.getInstance() + state = pi.destroy() + dsp.setState(State.REMOVING) + PublicationFinishChecker.checkAndUpdateState(dsp, pi, state) + except Exception, e: + raise PublishException(str(e)) + diff --git a/server/src/uds/core/managers/TaskManager.py b/server/src/uds/core/managers/TaskManager.py new file mode 100644 index 000000000..37cb0efb8 --- /dev/null +++ b/server/src/uds/core/managers/TaskManager.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.db import transaction +from uds.core.jobs.Scheduler import Scheduler +from uds.core.jobs.DelayedTaskRunner import DelayedTaskRunner +from uds.core.jobs.JobsFactory import JobsFactory +from uds.core.util.Config import GlobalConfig +import threading, time, signal +import logging, gc + +logger = logging.getLogger(__name__) + +class SchedulerThread(threading.Thread): + def run(self): + Scheduler.scheduler().run() + + def notifyTermination(self): + Scheduler.scheduler().notifyTermination() + +class DelayedTaskThread(threading.Thread): + def run(self): + DelayedTaskRunner.runner().run() + + def notifyTermination(self): + DelayedTaskRunner.runner().notifyTermination() + + +class TaskManager(object): + keepRunning = True + + @staticmethod + def sigTerm(sigNum, frame): + ''' + This method will ensure that we finish correctly current running task before exiting. + If we need to stop cause something went wrong (that should not happen), we must send sigterm, wait a while (10-20 secs) and after that send sigkill + kill task + sleep 10 + kill -9 task + Take a look at killTaskManager.sh :-) + ''' + logger.info("Caught term signal, finishing task manager") + TaskManager.keepRunning = False + + + @staticmethod + def registerScheduledTask(): + from uds.core.workers.ServiceCacheUpdater import ServiceCacheUpdater + from uds.core.workers.UserServiceCleaner import UserServiceInfoItemsCleaner, UserServiceRemover + from uds.core.workers.PublicationCleaner import PublicationInfoItemsCleaner, PublicationCleaner + from uds.core.workers.CacheCleaner import CacheCleaner + from uds.core.workers.DeployedServiceCleaner import DeployedServiceInfoItemsCleaner, DeployedServiceRemover + + logger.info("Registering sheduled tasks") + JobsFactory.factory().insert('Service Cache Updater', ServiceCacheUpdater) + JobsFactory.factory().insert('User Service Info Cleaner', UserServiceInfoItemsCleaner) + JobsFactory.factory().insert('User Service Cleaner', UserServiceRemover) + JobsFactory.factory().insert('Publications Info Cleaner', PublicationInfoItemsCleaner) + JobsFactory.factory().insert('Publication Cleaner', PublicationCleaner) + JobsFactory.factory().insert('Utility Cache Cleaner', CacheCleaner) + JobsFactory.factory().insert('User Service Info Cleaner', UserServiceInfoItemsCleaner) + JobsFactory.factory().insert('User Service Cleaner', UserServiceRemover) + JobsFactory.factory().insert('Deployed Service Info Cleaner', DeployedServiceInfoItemsCleaner) + JobsFactory.factory().insert('Deployed Service Cleaner', DeployedServiceRemover) + + + + @staticmethod + def run(): + TaskManager.keepRunning = True + # Runs Scheduler in a separate thread and DelayedTasks here + + noSchedulers = GlobalConfig.SCHEDULER_THREADS.getInt() + noDelayedTasks = GlobalConfig.DELAYED_TASKS_THREADS.getInt() + + threads = [] + for n in range(noSchedulers): + thread = SchedulerThread() + thread.start() + threads.append(thread) + time.sleep(0.5) + + for n in range(noDelayedTasks): + thread = DelayedTaskThread() + thread.start() + threads.append(thread) + time.sleep(1) + + signal.signal(signal.SIGTERM, TaskManager.sigTerm) + + + # Debugging stuff + #import guppy + #from guppy.heapy import Remote + #Remote.on() + + #gc.set_debug(gc.DEBUG_LEAK) + while( TaskManager.keepRunning ): + time.sleep(1) + + for thread in threads: + thread.notifyTermination() + + # The join of threads will happen before termination, so its fine to just return here + diff --git a/server/src/uds/core/managers/UserPrefsManager.py b/server/src/uds/core/managers/UserPrefsManager.py new file mode 100644 index 000000000..ee77b473a --- /dev/null +++ b/server/src/uds/core/managers/UserPrefsManager.py @@ -0,0 +1,268 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django import forms +from django.utils.translation import ugettext as _, ugettext_lazy +from uds.models import UserPreference +from uds.core.ui.UserInterface import gui +import logging + +logger = logging.getLogger(__name__) + +class UserPrefsManager(object): + _manager = None + + def __init__(self): + self._prefs = {} + + @staticmethod + def manager(): + if UserPrefsManager._manager == None: + UserPrefsManager._manager = UserPrefsManager() + return UserPrefsManager._manager + + def __nameFor(self, module, name): + return module + "_" + name + + def registerPrefs(self, modName, friendlyModName, prefs): + ''' + Register an array of preferences for a module + ''' + self._prefs[modName] = { 'friendlyName' : friendlyModName, 'prefs' : prefs } + + def getPreferencesForUser(self, modName, user): + ''' + Gets the preferences for an specified module for the user + ''' + prefs = {} + for up in user.preferences.filter(module=modName): + prefs[up.name] = up.value + for p in self._prefs[modName]['prefs']: + if prefs.has_key(p.getName()) is False: + prefs[p.getName()] = p.getDefValue() + return prefs + + def getHtmlForUserPreferences(self, user): + # First fill data for all preferences + data = {} + for up in user.preferences.all(): + data[self.__nameFor(up.module, up.name)] = up.value + res = '' + for mod, v in self._prefs.iteritems(): + form = forms.Form() + for p in v['prefs']: + name = self.__nameFor(mod, p.getName()) + val = data[name] if data.has_key(name) else p.getDefValue() + form.fields[ name ] = p.formField(val) + res += '
' + v['friendlyName'] + '' + form.as_p() + '
' + return res + + def getGuiForUserPreferences(self, user=None): + data = {} + if user is not None: + for up in user.preferences.all(): + data[self.__nameFor(up.module, up.name)] = up.value + res = [] + for mod, v in self._prefs.iteritems(): + grp = [] + for p in v['prefs']: + name = self.__nameFor(mod, p.getName()) + val = data[name] if data.has_key(name) else p.getDefValue() + grp.append( { 'name' : name, 'gui' : p.guiField(val).guiDescription(), 'value' : val } ) + res.append( {'moduleLabel': v['friendlyName'], 'prefs': grp} ) + return res + + + def processRequestForUserPreferences(self, user, data): + ''' + Returns a list of errors in case of error, else return None + ''' + # First, read fields form every single "section" + logger.debug('Processing {0}'.format(self._prefs)) + prefs = [] + for mod, v in self._prefs.iteritems(): + logger.debug(mod) + form = forms.Form(data) + for p in v['prefs']: + name = self.__nameFor(mod, p.getName()) + form.fields[name] = p.formField(None) + if form.is_valid() is False: + logger.debug("errors") + return form.errors + for p in v['prefs']: + name = self.__nameFor(mod, p.getName()) + logger.debug(name) + prefs.append({ 'module': mod, 'name': p.getName(), 'value': form.cleaned_data[name] } ) + user.preferences.all().delete() + for p in prefs: + user.preferences.create(module=p['module'], name=p['name'], value=p['value']) + return None + + def processGuiForUserPreferences(self, user, data): + ''' + ''' + logger.debug('Processing data {0}'.format(data)) + prefs = [] + for mod, v in self._prefs.iteritems(): + logger.debug(mod) + for p in v['prefs']: + name = self.__nameFor(mod, p.getName()) + if data.has_key(name): + prefs.append( { 'module': mod, 'name': p.getName(), 'value': data[name] } ) + user.preferences.all().delete() + for p in prefs: + user.preferences.create(module=p['module'], name=p['name'], value=p['value']) + + + +class UserPreference(object): + TYPE = 'abstract' + def __init__(self, **kwargs): + self._name = kwargs['name'] + self._label = kwargs['label'] + self._defValue = kwargs['defvalue'] if kwargs.has_key('defvalue') else None + + def getName(self): + return self._name + + def getDefValue(self): + return self._defValue + + def formField(self, value): + ''' + Returns a form field to add to the preferences form + ''' + raise NameError('Can\'t create an abstract preference!!!') + + def guiField(self): + ''' + ''' + raise NameError('Can\'t create an abstract preference!!!') + + +class UserTextPreference(UserPreference): + TYPE = 'text' + def __init__(self, **kwargs): + super(self.__class__,self).__init__(**kwargs) + self._length = kwargs['length'] if kwargs.has_key('length') else None + + def formField(self, value): + return forms.CharField(label = _(self._label), initial = value) + + +class UserNumericPreference(UserPreference): + TYPE = 'numeric' + def __init__(self, **kwargs): + super(self.__class__,self).__init__(**kwargs) + self._min = kwargs['minvalue'] if kwargs.has_key('minvalue') else None + self._max = kwargs['maxvalue'] if kwargs.has_key('maxvalue') else None + + def formField(self, value): + return forms.IntegerField(label = _(self._label), initial = value, min_value = self._min, max_value = self._max) + +class UserChoicePreference(UserPreference): + TYPE = 'choice' + def __init__(self, **kwargs): + super(self.__class__,self).__init__(**kwargs) + ''' + Values are a tuple of + ''' + self._values = kwargs['values'] + + def formField(self, value): + return forms.ChoiceField(label = _(self._label), initial = value, choices = self._values) + + def guiField(self, value): + vals = [] + for v in self._values: + vals.append( { 'id': v[0], 'text': _(v[1]) } ) + return gui.ChoiceField(label = _(self._label), rdonly = False, values = vals, defvalue=value, tooltip = _(self._label)) + +class UserCheckboxPreference(UserPreference): + TYPE = 'checkbox' + def __init__(self, **kwargs): + super(self.__class__,self).__init__(**kwargs) + + +class CommonPrefs(object): + SZ_PREF = 'screenSize' + SZ_640x480 = '1' + SZ_800x600 = '2' + SZ_1024x768 = '3' + SZ_FULLSCREEN = 'F' + + DEPTH_PREF = 'screenDepth' + DEPTH_8 = '1' + DEPTH_16 = '2' + DEPTH_24 = '3' + DEPTH_32 = '4' + + @staticmethod + def getWidthHeight(prefsDict): + ''' + Get width based on screenSizePref value + ''' + return { CommonPrefs.SZ_640x480 : (640, 480), + CommonPrefs.SZ_800x600 : (800, 600), + CommonPrefs.SZ_1024x768 : (1024, 768), + CommonPrefs.SZ_FULLSCREEN : (-1, -1) + }[prefsDict[CommonPrefs.SZ_PREF]] + + @staticmethod + def getDepth(prefsDict): + ''' + Get depth based on depthPref value + ''' + return { CommonPrefs.DEPTH_8 : 8, + CommonPrefs.DEPTH_16 : 16, + CommonPrefs.DEPTH_24 : 24, + CommonPrefs.DEPTH_32 : 32 }[ prefsDict[CommonPrefs.DEPTH_PREF] ] + + + + + screenSizePref = UserChoicePreference(name = SZ_PREF, label = ugettext_lazy('Screen Size'), defvalue = SZ_FULLSCREEN, values = ( + (SZ_640x480, '640x480'), + (SZ_800x600, '800x600'), + (SZ_1024x768, '1024x768'), + (SZ_FULLSCREEN, ugettext_lazy('Full Screen')) + ) + ) + depthPref = UserChoicePreference(name = DEPTH_PREF, label = ugettext_lazy('Screen colors'), defvalue = DEPTH_24, values = ( + (DEPTH_8, ugettext_lazy('8 bits')), + (DEPTH_16, ugettext_lazy('16 bits')), + (DEPTH_24, ugettext_lazy('24 bits')), + (DEPTH_32, ugettext_lazy('32 bits')), + ) + ) + \ No newline at end of file diff --git a/server/src/uds/core/managers/UserServiceManager.py b/server/src/uds/core/managers/UserServiceManager.py new file mode 100644 index 000000000..aa2ddb430 --- /dev/null +++ b/server/src/uds/core/managers/UserServiceManager.py @@ -0,0 +1,451 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext as _ +from django.db.models import Q +from django.db import transaction +from uds.core.jobs.DelayedTask import DelayedTask +from uds.core.jobs.DelayedTaskRunner import DelayedTaskRunner +from uds.core.services.Exceptions import OperationException +from uds.core.util.State import State +from uds.core.util.Config import GlobalConfig +from uds.core.services.Exceptions import MaxServicesReachedException +from uds.models import UserService, getSqlDatetime +from uds.core import services +from uds.core.services import Service +import logging + +logger = logging.getLogger(__name__) + +USERSERVICE_TAG = 'cm-' + +class UserServiceOpChecker(DelayedTask): + def __init__(self, cache): + super(UserServiceOpChecker,self).__init__() + self._svrId = cache.id + self._state = cache.state + + @staticmethod + def makeUnique(userService, userServiceInstance, state): + ''' + This method makes sure that there will be only one delayedtask related to the userService indicated + ''' + DelayedTaskRunner.runner().remove(USERSERVICE_TAG + str(userService.id)) + UserServiceOpChecker.checkAndUpdateState(userService, userServiceInstance, state) + + @staticmethod + def checkAndUpdateState(userService, userServiceInstance, state): + ''' + Checks the value returned from invocation to publish or checkPublishingState, updating the dsp database object + Return True if it has to continue checking, False if finished + ''' + prevState = userService.state + userService.unique_id = userServiceInstance.getUniqueId() # Updates uniqueId + userService.friendly_name = userServiceInstance.getName() # And name, both methods can modify serviceInstance, so we save it later + if State.isFinished(state): + checkLater = False + userServiceInstance.finish() + if State.isPreparing(prevState): + if userServiceInstance.service().publicationType is None or userService.publication == userService.deployed_service.activePublication(): + userService.setState(State.USABLE) + # and make this usable if os manager says that it is usable, else it pass to configuring state + if userServiceInstance.osmanager() is not None and userService.os_state == State.PREPARING: # If state is already "Usable", do not recheck it + stateOs = userServiceInstance.osmanager().checkState(userService) + # If state is finish, we need to notify the userService again that os has finished + if State.isFinished(stateOs): + state = userServiceInstance.notifyReadyFromOsManager('') + userService.updateData(userServiceInstance) + else: + stateOs = State.FINISHED + + if State.isRuning(stateOs): + userService.setOsState(State.PREPARING) + else: + userService.setOsState(State.USABLE) + else: + # We ignore OsManager info and if userService don't belong to "current" publication, mark it as removable + userService.setState(State.REMOVABLE) + elif State.isRemoving(prevState): + if userServiceInstance.osmanager() is not None: + userServiceInstance.osmanager().release(userService) + userService.setState(State.REMOVED) + else: + # Canceled, + userService.setState(State.CANCELED) + userService.updateData(userServiceInstance) + elif State.isErrored(state): + checkLater = False + userService.updateData(userServiceInstance) + userService.setState(State.ERROR) + else: + checkLater = True # The task is running + userService.updateData(userServiceInstance) + userService.save() + if checkLater: + UserServiceOpChecker.checkLater(userService, userServiceInstance) + + @staticmethod + def checkLater(userService, ci): + ''' + Inserts a task in the delayedTaskRunner so we can check the state of this publication + @param dps: Database object for DeployedServicePublication + @param pi: Instance of Publication manager for the object + ''' + # Do not add task if already exists one that updates this service + if DelayedTaskRunner.runner().checkExists(USERSERVICE_TAG + str(userService.id)): + return + DelayedTaskRunner.runner().insert(UserServiceOpChecker(userService), ci.suggestedTime, USERSERVICE_TAG + str(userService.id)) + + + @transaction.commit_manually + def run(self): + logger.debug('Checking user service finished {0}'.format(self._svrId)) + try: + uService = UserService.objects.select_for_update().get(pk=self._svrId) + if uService.state != self._state: + logger.debug('Task overrided by another task (state of item changed)') + # This item is no longer valid, returning will not check it again (no checkLater called) + transaction.rollback() + return + + ci = uService.getInstance() + logger.debug("uService instance class: {0}".format(ci.__class__)) + state = ci.checkState() + UserServiceOpChecker.checkAndUpdateState(uService, ci, state) + except UserService.DoesNotExist, e: + logger.error('User service not found (erased from database?) {0} : {1}'.format(e.__class__, e)) + except Exception, e: + # Exception caught, mark service as errored + logger.exception("Error {0}, {1} :".format(e.__class__, e)) + try: + uService.setState(State.ERROR) + uService.save() + except Exception: + logger.error('Can\'t update state of uService object') + transaction.commit() + + +class UserServiceManager(object): + _manager = None + + def __init__(self): + pass + + @staticmethod + def manager(): + if UserServiceManager._manager == None: + UserServiceManager._manager = UserServiceManager() + return UserServiceManager._manager + + @staticmethod + def getCacheStateFilter(level): + return Q(cache_level=level) & UserServiceManager.getStateFilter() + + @staticmethod + def getStateFilter(): + return Q(state__in=[State.PREPARING, State.USABLE]) + + + @transaction.commit_on_success + def __checkMaxdeployedReached(self, deployedService): + ''' + Checks if maxDeployed for the service has been reached, and, if so, + raises an exception that no more services of this kind can be reached + ''' + serviceInstance = deployedService.service.getInstance() + # Early return, so no database count is needed + if serviceInstance.maxDeployed == Service.UNLIMITED: + return + + numberOfServices = deployedService.userServices.select_for_update().filter( + state__in=[State.PREPARING, State.USABLE]).count() + if serviceInstance.maxDeployed <= numberOfServices: + raise MaxServicesReachedException( + 'Max number of allowed deployments for service reached' + ) + + + def __createCacheAtDb(self, deployedService, cacheLevel): + ''' + Private method to instatiate a cache element at database with default states + ''' + # Checks if maxDeployed has been reached and if so, raises an exception + self.__checkMaxDeployedReached(deployedService) + now = getSqlDatetime() + return deployedService.userServices.create(cache_level = cacheLevel, state = State.PREPARING, os_state = State.PREPARING, + state_date=now, creation_date=now, data = '', deployed_service = deployedService.deployed_service, + user = None, in_use = False ) + + def __createAssignedAtDb(self, deployedService, user): + ''' + Private method to instatiate an assigned element at database with default state + ''' + self.__checkMaxDeployedReached(deployedService) + now = getSqlDatetime() + return deployedService.userServices.create(cache_level=0, state=State.PREPARING, os_state=State.PREPARING, + state_date=now, creation_date=now, data='', deployed_service=deployedService.deployed_service, user=user, in_use=False) + + def __createAssignedAtDbForNoPublication(self, deployedService, user): + self.__checkMaxDeployedReached(deployedService) + now = getSqlDatetime() + return deployedService.userServices.create(cache_level=0, state=State.PREPARING, os_state=State.PREPARING, + state_date=now, creation_date=now, data='', publication=None, user=user, in_use=False) + + + @transaction.commit_on_success + def createCacheFor(self, deployedService, cacheLevel): + ''' + Creates a new cache for the deployed service publication at level indicated + ''' + logger.debug('Creating a new cache element at level {0} for publication {1}'.format(cacheLevel, deployedService)) + cache = self.__createCacheAtDb(deployedService, cacheLevel) + ci = cache.getInstance() + state = ci.deployForCache(cacheLevel) + + UserServiceOpChecker.checkAndUpdateState(cache, ci, state) + return cache + + @transaction.commit_on_success + def createAssignedFor(self, ds, user): + ''' + Creates a new assigned deployed service for the publication and user indicated + ''' + if ds.service.getType().publicationType is not None: + dsp = ds.activePublication() + logger.debug('Creating a new assigned element for user {0} por publication {1}'.format(user, dsp)) + assigned = self.__createAssignedAtDb(dsp, user) + else: + logger.debug('Creating a new assigned element for user {0}'.format(user)) + assigned = self.__createAssignedAtDbForNoPublication(ds, user) + + ai = assigned.getInstance() + state = ai.deployForUser(user) + + UserServiceOpChecker.makeUnique(assigned, ai, state) + + return assigned + + @transaction.commit_on_success + def createAssignable(self, ds, deployed, user): + ''' + Creates an assignable service + ''' + now = getSqlDatetime() + assignable = ds.userServices.create(cache_level=0, state=State.PREPARING, os_state=State.PREPARING, + state_date=now, creation_date=now, data='', user=user, in_use=False) + state = deployed.deployForUser(user) + try: + UserServiceOpChecker.makeUnique(assignable, deployed, state) + except Exception, e: + logger.exception("Exception {0}".format(e)) + logger.debug("Assignable: {0}".format(assignable)) + return assignable + + + + @transaction.commit_on_success + def moveToLevel(self, cache, cacheLevel): + ''' + Moves a cache element from one level to another + @return: cache element + ''' + cache = UserService.objects.select_for_update().get(id=cache.id) + logger.debug('Moving cache {0} to level {1}'.format(cache, cacheLevel)) + ci = cache.getInstance() + state = ci.moveToCache(cacheLevel) + cache.cache_level = cacheLevel + if State.isRuning(state) and cache.isUsable(): + cache.setState(State.PREPARING) + + UserServiceOpChecker.makeUnique(cache, ci, state) + transaction.commit() + + @transaction.commit_on_success + def cancel(self, uService): + ''' + Cancels a user service creation + @return: the Uservice canceling + ''' + uService = UserService.objects.select_for_update().get(id=uService.id) + logger.debug('Canceling uService {0} creation'.format(uService)) + if uService.isPreparing() == False: + raise OperationException(_('Can\'t cancel non running operation')) + ui = uService.getInstance() + # We simply notify service that it should cancel operation + state = ui.cancel() + uService.updateData(ui) + uService.setState(State.CANCELING) + UserServiceOpChecker.makeUnique(uService, ui, state) + return uService + + + @transaction.commit_on_success + def remove(self, uService): + ''' + Removes a uService element + @return: the uService removed (marked for removal) + ''' + uService = UserService.objects.select_for_update().get(id=uService.id) + logger.debug('Removing uService {0}'.format(uService)) + if uService.isUsable() == False and State.isRemovable(uService.state) == False: + raise OperationException(_('Can\'t remove a non active element')) + + ci = uService.getInstance() + state = ci.destroy() + uService.setState(State.REMOVING) + UserServiceOpChecker.makeUnique(uService, ci, state) + + def removeOrCancel(self, uService): + if uService.isUsable() or State.isRemovable(uService.state): + return self.remove(uService) + elif uService.isPreparing(): + return self.cancel(uService) + else: + raise OperationException(_('Can\'t remove nor cancel {0} cause its states doesn\'t allows it')) + + @transaction.commit_on_success + def removeInfoItems(self, dsp): + dsp.cachedDeployedService.select_for_update().filter(state__in=State.INFO_STATES).delete() + + + @transaction.commit_on_success + def getAssignationForUser(self, ds, user): + # First, we try to locate an already assigned service + existing = ds.assignedUserServices().filter(user=user,state__in=State.VALID_STATES) + lenExisting = existing.count() + if lenExisting > 0: # Already has 1 assigned + logger.debug('Found assigned service from {0} to user {1}'.format(ds, user.name)) + if existing[0].state == State.ERROR: + if lenExisting > 1: + return existing[1] + else: + return existing[0] + + # Now try to locate 1 from cache already "ready" (must be usable and at level 1) + cache = ds.cachedUserServices().select_for_update().filter(cache_level = services.UserDeployment.L1_CACHE, state = State.USABLE)[:1] + if len(cache) > 0: + cache = cache[0] # Database object + cache.assignToUser(user) + cache.save() # Store assigned ASAP, we do not know how long assignToUser method of instance will take + logger.debug('Found a cached-ready service from {0} for user {1}, item {2}'.format(ds, user, cache)) + ci = cache.getInstance() # User Deployment instance + ci.assignToUser(user) + cache.updateData(ci) + cache.save() + return cache + # Now find if there is a preparing one + cache = ds.cachedUserServices().select_for_update().filter(cache_level = services.UserDeployment.L1_CACHE, state = State.PREPARING)[:1] + if len(cache) > 0: + cache = cache[0] + cache.assignToUser(user) + cache.save() + logger.debug('Found a cached-preparing service from {0} for user {1}, item {2}'.format(ds, user, cache)) + cache.getInstance().assignToUser(user) + return cache + # Can't assign directly from L2 cache... so we check if we can create e new service in the limits requested + ty = ds.service.getType() + if ty.usesCache is True: + inCacheL1 = ds.cachedUserServices().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L1_CACHE)).count() + inAssigned = ds.assignedUserServices().filter(UserServiceManager.getStateFilter()).count() + totalL1Assigned = inCacheL1 + inAssigned + if totalL1Assigned >= ds.max_srvs: + raise MaxServicesReachedException() + # Can create new service, create it + return self.createAssignedFor(ds, user) + + def getServicesInStateForProvider(self, provider_id, state): + ''' + Returns the number of services of a service provider in the state indicated + ''' + return UserService.objects.filter(deployed_service__service__provider__id=provider_id, state=state).count() + + def canRemoveServiceFromDeployedService(self, ds): + ''' + checks if we can do a "remove" from a deployed service + ''' + removing = self.getServicesInStateForProvider(ds.service.provider_id, State.REMOVING) + if removing >= GlobalConfig.MAX_REMOVING_SERVICES.getInt() and GlobalConfig.IGNORE_LIMITS.getBool() == False: + return False + return True + + def canInitiateServiceFromDeployedService(self, ds): + ''' + Checks if we can start a new service + ''' + preparing = self.getServicesInStateForProvider(ds.service.provider_id, State.PREPARING) + if preparing >= GlobalConfig.MAX_PREPARING_SERVICES.getInt() and GlobalConfig.IGNORE_LIMITS.getBool() == False: + return False + return True + + @transaction.commit_on_success + def isReady(self, uService): + UserService.objects.update() + uService = UserService.objects.select_for_update().get(id=uService.id) + logger.debug('Checking ready of {0}'.format(uService)) + if uService.state != State.USABLE or uService.os_state != State.USABLE: + logger.debug('State is not usable for {0}'.format(uService)) + return False + logger.debug('Service {0} is usable, checking it via setReady'.format(uService)) + ui = uService.getInstance() + state = ui.setReady() + logger.debug('State: {0}'.format(state)) + uService.updateData(ui) + if state == State.FINISHED: + uService.save() + return True + uService.setState(State.PREPARING) + UserServiceOpChecker.makeUnique(uService, ui, state) + return False + + @transaction.commit_on_success + def checkForRemoval(self, uService): + uService = UserService.objects.select_for_update().get(id=uService.id) + if uService.publication == None: + return + if uService.publication.id != uService.deployed_service.activePublication().id: + logger.debug('Old revision of user service, marking as removable: {0}'.format(uService)) + uService.setState(State.REMOVABLE) + + + def notifyReadyFromOsManager(self, uService, data): + ui = uService.getInstance() + logger.debug('Notifying user service ready state') + state = ui.notifyReadyFromOsManager(data) + logger.debug('State: {0}'.format(state)) + uService.updateData(ui) + if state == State.FINISHED: + uService.save() + elif uService.state in (State.USABLE, State.PREPARING): # We don't want to get active deleting or deleted machines... + uService.setState(State.PREPARING) + UserServiceOpChecker.makeUnique(uService, ui, state) + diff --git a/server/src/uds/core/managers/__init__.py b/server/src/uds/core/managers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/core/osmanagers/BaseOsManager.py b/server/src/uds/core/osmanagers/BaseOsManager.py new file mode 100644 index 000000000..f6dc56166 --- /dev/null +++ b/server/src/uds/core/osmanagers/BaseOsManager.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.util.State import State +from uds.core.BaseModule import BaseModule +from uds.core.managers.UserServiceManager import UserServiceManager + +STORAGE_KEY = 'osmk' + +class BaseOSManager(BaseModule): + ''' + An OS Manager is responsible for communication the service the different actions to take (i.e. adding a windows machine to a domain) + The Service (i.e. virtual machine) communicates with the OSManager via a published web method, that must include the unique ID. + In order to make easier to agents identify themselfs, the Unique ID can be a list with various Ids (i.e. the macs of the virtual machine). + Server will iterate thought them and look for an identifier associated with the service. This list is a comma separated values (i.e. AA:BB:CC:DD:EE:FF,00:11:22:...) + Remember also that we inherit the test and check methods from BaseModule + ''' + # Service informational related data + typeName = _('Base OS Manager') + typeType = 'BaseOSManager' + typeDescription = _('Base Manager') + iconFile = 'osmanager.png' + + # If true, this os manager will be invoked with every user service assigned, but not used from time to time + # Time is defined as a global config + processUnusedMachines = False + + def __init__(self,environment, values): + super(BaseOSManager, self).__init__(environment, values) + self.initialize(values) + + def initialize(self, values): + ''' + This method will be invoked from __init__ constructor. + This is provided so you don't have to provide your own __init__ method, + and invoke base methods. + This will get invoked when all initialization stuff is done + + Args: + Values: If values is not none, this object is being initialized + from administration interface, and not unmarshal will be done. + If it's None, this is initialized internally, and unmarshal will + be called after this. + + Default implementation does nothing + ''' + pass + + def release(self, service): + ''' + Called by a service that is in Usable state before destroying it so osmanager can release data associated with it + Only invoked for services that reach the state "removed" + @return nothing + ''' + pass + + # These methods must be overriden + def process(self,service, message, data): + ''' + This method must be overriden so your so manager can manage requests and responses from agent. + @param service: Service that sends the request (virtual machine or whatever) + @param message: message to process (os manager dependent) + @param data: Data for this message + ''' + pass + + def checkState(self,service): + ''' + This method must be overriden so your os manager can respond to requests from system to the current state of the service + This method will be invoked when: + * After service creation has finished, with the service wanting to see if it has to wait for os manager process finalization + * After call to process method, to check if the state has changed + * Before assigning a service to an user (maybe this is not needed)? + Notice that the service could be in any state. In fact, what we want with this is return FINISHED if nothing is expected from os o RUNING else + The state will be updated by actors inside oss, so no more direct checking is needed + @return: RUNNING, FINISHED + We do not expect any exception from this method + ''' + return State.FINISHED + + + def processUnused(self, userService): + ''' + This will be invoked for every assigned and unused user service that has been in this state at least 1/2 of Globalconfig.CHECK_UNUSED_TIME + This function can update userService values. Normal operation will be remove machines if this state is not valid + ''' + pass + + def destroy(self): + ''' + Invoked when OS Manager is deleted + ''' + pass + + def __str__(self): + return "Base OS Manager" + \ No newline at end of file diff --git a/server/src/uds/core/osmanagers/OSManagersFactory.py b/server/src/uds/core/osmanagers/OSManagersFactory.py new file mode 100644 index 000000000..2a9067d2c --- /dev/null +++ b/server/src/uds/core/osmanagers/OSManagersFactory.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +import logging + +logger = logging.getLogger(__name__) + +class OSManagersFactory(object): + _factory = None + + def __init__(self): + self._jobs = {} + + @staticmethod + def factory(): + if OSManagersFactory._factory == None: + OSManagersFactory._factory = OSManagersFactory() + return OSManagersFactory._factory + + def providers(self): + return self._jobs + + def insert(self, type): + self._jobs[type.type()] = type + + def lookup(self, typeName): + try: + return self._jobs[typeName] + except KeyError: + return None diff --git a/server/src/uds/core/osmanagers/__init__.py b/server/src/uds/core/osmanagers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/core/osmanagers/osmanager.png b/server/src/uds/core/osmanagers/osmanager.png new file mode 100644 index 0000000000000000000000000000000000000000..a11c034dbd145af27d977cc48ad778081b2bfc1f GIT binary patch literal 1253 zcmZ9LYfM{Z7{{M;-qQg)&t}cyD!0pYg z&CQHr8?x<(f)#7tta=htCRX(SblT>bI3Lw#XIL-mj2_U6lSf16X zDVfWc81HN`RZI7eQBqLh4~y^U%wo`#rcrQ99-3O(U|=BXxOWNVZ0tR&HN@6*HizKaBk5H8Ko+<{DQzQ&duEWC!0lMToILDVEzaSqfPalJr z#{pA=RzPdNLrmd;=b;F)(Md6!@k!L~V(t9k4JV$m2e4kB0}u^?)o~B*-kE@O-7q9D z1ODYC7}Wb=!utzE85@|g=y{1F*3N*n6RQWq?8_leQ%tFlm#O=`vDYI z1C%PNAQE+Bt@dS9aq1%MuYK+Ef25r#q}Q;wp3Os7G}=rQk0-b-ItgwXK?sG#5g|~@ zpMq3X9{A#ZffkmC#OC}rXlrv$M4|t?!hOG4h)ZAodah6>^OTv2sD_d--pgIZ9{F9E zCHld_T?8xH3lG7H*{~a~UFZxvuzMew+UZJIaC(XOW(W;-_kEX{^ekys$B4kfeL`SK zLG&cGsnlv3>oRjDXcvp%=))RY0G}e_1>H@7j7Ncjs?-~B#CtKdY3|Ht~ zbuE%?f+jgOK_-(7QmK?wAQ;9BL*E#=*4rtCdmK<&2K+*q9&!tHYYyA`b)ZrfJP~pl zqXP4DHE-=%u~~{d!LV{|bDoJtg6^PybA%ws>jIY?9vT{YdmGjISzCMe;D#n17=7of z*S~IUv>k80H+`=C{P~=gjs#O2wY!bjIr_oI`WNj39X!i-yQZvYv8xNaPqnq>NxvV) Z2uhl6(|z)%-xoij{?&$>{)5e*{0sb1D-8eu literal 0 HcmV?d00001 diff --git a/server/src/uds/core/services/BaseDeployed.py b/server/src/uds/core/services/BaseDeployed.py new file mode 100644 index 000000000..7df1850ee --- /dev/null +++ b/server/src/uds/core/services/BaseDeployed.py @@ -0,0 +1,572 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from uds.core.Environment import Environmentable +from uds.core.Serializable import Serializable +from uds.core.util.State import State + +class UserDeployment(Environmentable, Serializable): + ''' + Interface for deployed services. + + This class provides the needed logic for implementing an "consumable user service", + that are the elements that the user will interact with. + + A good way to understand this class is to look at the sample services provided + with the documentation. + + As with all modules interfaces, if you override __init__ method, + do not forget to invoke this class __init__ method as this:: + + super(self.__class__, self).__init__(environment, **kwargs) + + This is a MUST (if you override __init___), so internal structured gets filled correctly, so don't forget it!. + + The preferred way of initializing, is to provide :py:meth:`.initialize`, that + will be invoked just after all initialization stuff is done at __init__. + + Normally objects of classes deriving from this one, will be serialized, called, + deserialized. This means that all that you want to ensure that is keeped inside + the class must be serialized and unserialized, because there is no warantee that + the object will get two methods invoked without haven't been remoded from memory + and loaded again, this means, IMPLEMENT marshal and unmarshal with all attributes + that you want to keep. + + + Things to know about this class: + + * Once a deployment is done, it will never be called again for same instance + object + * The method getUniqueId will be invoked after call to deploys and check. + You can change it on the fly, but remember that uniqueId is the "keyword" + used inside services to communicate with os managers (os manager will + receive an instance of UserDeployment, and this will be located via that + uniqueId) + + Uniques ids can be repeated at database level, to let it come at a later + deployment stage, but if two services has same uniqueid at a time, + os manager will simply not work. + * suggestedTime is always accessed through instance objects, and used after + deployForCache, deployForUser and moveToCache it these methods returns + RUNNING + * Checks (if a deployment has finished, or the cache movement is finished) + are always done using checkState(). It is secuential, i mean, will only + be called when a deployment,a cache movement or a cancel operation is + running + * If the service that supports this deployeds do not use L2 cache, the + moveCache method will never be invoked + * The L1 cache should be a fast access cache (i.e. running service but + not assigned to an user), while L2 cache should be non consuming or + almost-non-consuming service. This means that if we cannont make an + slower an less resources consumable form for a service, this should + not have an L2 cache (slower is not a must, + but probably it will be slower to recover from L2 cache than from L1, + but faster than creating a new service) + Ofc, if a service has an "Instant" creation, it don't needs cache... + * We do not expect any exception from these methods, but if there is an + error, the method can return "ERROR". To show the reason of error, the + method reasonOfError can be called multiple times, including + serializations in middle, so remember to include reason of error in serializations + ''' + L1_CACHE = 1 #: Constant for Cache of level 1 + L2_CACHE = 2 #: Constant for Cache of level 2 + + #: Suggested time for deployment finishing, in seconds + #: This allows the manager to, if deployment is no done in 1 step, re-check + #: the deployment once this time has passed, i.e. KVM COW deployment takes + #: low time, so we suggest to check at short intervals, but full copys takes + #: a bit more so we can use longer interval checks + #: This attribute is accessed always through an instance object, + #: so u can modify it at your own implementation. + suggestedTime = 10 + + def __init__(self, environment, **kwargs): + ''' + Do not forget to invoke this in your derived class using "super(self.__class__, self).__init__(environment, **kwargs)" + We want to use the env, cache and storage methods outside class. If not called, you must implement your own methods + cache and storage are "convenient" methods to access _env.cache() and _env.storage() + + Invoking this from derived classes is a MUST, again, do not forget it or your + module will probable never work. + + Args: + + environment: Environment assigned to this publication + kwargs: List of arguments that will receive: + service: Parent service (derived from Service) of this deployment (this is an instance, not database object) + publication: Parent publication (derived from Publication) of this deployment (optional)(this is an instance, not database object) + osmanager: Parent osmanager (derived from BaseOsManager) of this deployment (optional)(this is an instance, not database object) + dbservice: Database object for this service + ''' + Environmentable.__init__(self, environment) + Serializable.__init__(self) + self._service = kwargs['service'] # Raises an exception if service is not included. Parent + if kwargs.has_key('publication'): + self._publication = kwargs['publication'] + else: + self._publication = None + if kwargs.has_key('osmanager'): + self._osmanager = kwargs['osmanager'] + else: + self._osmanager = None + if kwargs.has_key('dbservice'): # Reference to database service, will be there most time :-) + self._dbService = kwargs['dbservice'] + else: + self._dbService = None + + self.initialize() + + def initialize(self): + ''' + This method will be invoked from __init__ constructor. + This is provided so you don't have to provide your own __init__ method, + and invoke base class __init__. + This will get invoked when all initialization stuff is done, so + you can here access publication, service, osManager, ... + ''' + pass + + + def getName(self): + ''' + Override this to return a name to display under some circustances + + Returns: + + name, default implementation returns unique id + ''' + return self.getUniqueId() + + def service(self): + ''' + Utility method to access parent service. This doesn't need to be override. + + Normaly user deployments will need parent service to provide the + consumable to the user. + + Returns: + + Parent service of this User Deployment + ''' + return self._service + + def publication(self): + ''' + Utility method to access publication. This doesn't need to be overriden. + + Returns: + + publication for this user deployment, or None if this deployment has + no publication at all. + ''' + return self._publication + + def osmanager(self): + ''' + Utility method to access os manager. This doesn't need to be overriden. + + Returns: + + os manager for this user deployment, or None if this deployment has + no os manager. + ''' + return self._osmanager + + def dbservice(self): + ''' + Utility method to access database object for the object this represents. + + Returns: + + Database object that got unserialized to obtain this object. + ''' + return self._dbService + + def macGenerator(self): + ''' + Utility method to access provided macs generator (inside environment) + + Returns the environment unique mac addresses generator + ''' + return self.idGenerators('mac') + + def nameGenerator(self): + ''' + Utility method to access provided names generator (inside environment) + + Returns the environment unique name generator + ''' + return self.idGenerators('name') + + def getUniqueId(self): + ''' + Obtains an unique id for this deployed service, you MUST override this + + Returns: + + An unique identifier for this object, that is an string and must be + unique. + ''' + raise Exception('Base getUniqueId for User Deployment called!!!') + + def notifyReadyFromOsManager(self, data): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + This method provides a mechanism to let os managers notify readyness + to deployed services. + + Args: + + Data: Data sent by os manager. + Data is os manager dependant, so check if this data is known by you + (normally, it will be None, but is os manager dependad as i say) + + This is a task-initiating method, so if there is something to do, + just return State.RUNNING. If not, return State.FINISHED. In case of + error, return State.ERROR and be ready to provide error message when + + if State.RUNNING is returned, the :py:meth:.checkState method will be + used to check when this process has finished. + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + return State.FINISHED + + def getIp(self): + ''' + All services are "IP" services, so this method is a MUST + + Returns: + + The needed ip to let the user connect to the his deployed service. + This ip will be managed by transports, without IP there is no connection + ''' + raise Exception('Base getIp for User Deployment got called!!!') + + def setIp(self, ip): + ''' + This is an utility method, invoked by some os manager to notify what they thinks is the ip for this service. + If you assign the service IP by your own methods, do not override this + ''' + pass + + def setReady(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + The method is invoked whenever a machine is provided to an user, right + before presenting it (via transport rendering) to the user. + + This method exist for this kind of situations (i will explain it with a + sample) + + Imagine a Service tree (Provider, Service, ...) for virtual machines. + This machines will get created by the UserDeployment implementation, but, + at some time, the machine can be put at in an state (suspend, shut down) + that will make the transport impossible to connect with it. + + This method, in this case, will check the state of the machine, and if + it is "ready", that is, powered on and accessible, it will return + "State.FINISHED". If the machine is not accessible (has been erased, for + example), it will return "State.ERROR" and store a reason of error so UDS + can ask for it and present this information to the Administrator. + + If the machine powered off, or suspended, or any other state that is not + directly usable but can be put in an usable state, it will return + "State.RUNNING", and core will use checkState to see when the operation + has finished. + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + return State.FINISHED + + def deployForCache(self, cacheLevel): + ''' + Deploys a user deployment as cache. + + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + The objective of this method is providing a cache copy of an user consumable, + and will be invoked whenever the core need to create a new copy for cache + of the service this UserDeployment manages. + + Things to take care with this method are: + + * cacheLevel can be L1 or L2 (class constants) + * If a deploy for cache is asked for a L1 cache, the generated + element is expected to be all-done for user consume. L1 cache items + will get directly assigned to users whenever needed, and are expected + to be fast. (You always have setReady method to do anything else needed + to assign the cache element to an user, but generally L1 cached items + must be ready to use. + * An L2 cache is expected to be an cached element that is "almost ready". + The main idea behind L2 is to keep some elements almost usable by users + but in an state that they do not consume (or consumes much less) resources. + If your L2 cache consumes the same that L1 cache, L2 cache is in fact not + needed. + * This works as :py:meth:.deployForUser, meaning that you can take a look + also to that method for more info + + :note: If your service uses caching, this method MUST be provided. If it + do not uses cache, this method will never get called, so you can + skip it implementation + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('Base deploy for cache invoked! for class {0}'.format(self.__class__.__name__)) + + def deployForUser(self, user): + ''' + Deploys an service instance for an user. + + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + The user parameter is not realy neded, but provided. It indicates the + Database User Object (see py:mod:`uds.modules`) to which this deployed + user service will be assigned to. + + This method will get called whenever a new deployed service for an user + is needed. This will give this class the oportunity to create + a service that is assigned to an user. + + The way of using this method is as follows: + + If the service gets created in "one step", that is, before the return + of this method, the consumable service for the user gets created, it + will return "State.FINISH". + If the service needs more steps (as in this case), we will return + "State.RUNNING", and if it has an error, it wil return "State.ERROR" and + store an error string so administration interface can show it. + + We do not use user for anything, as in most cases will be. + + :note: override ALWAYS this method, or an exception will be raised + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('Base deploy for user invoked! for class {0}'.format(self.__class__.__name__)) + + + def checkState(self): + ''' + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + + If some of the initiating action tasks returns State.RUNNING. this method + will get called until it returns State.FINISH or State.ERROR. + + In other words, whenever a multi step operation is initiated, this method + will get the responsability to check that the operation has finished or + failed. If the operation continues, but haven't finished yet, it must + return State.RUNNING. If has finished must return State.FINISH and if it + has some kind of error, State.ERROR and also store somewhere the info + that will be requested using :py:meth:.reasonOfError + + :note: override ALWAYS this method, or an exception will be raised + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('Base check state invoked! for class {0}'.format(self.__class__.__name__)) + + def finish(self): + ''' + Invoked when the core notices that the deployment of a service has finished. + (No matter whether it is for cache or for an user) + + This gives the opportunity to make something at that moment. + + Default implementation does nothing at all. + + :note: You can also make these operations at checkState, this is really + not needed, but can be provided (default implementation of base class does + nothing) + ''' + pass + + def assignToUser(self, user): + ''' + This method is invoked whenever a cache item gets assigned to an user. + This is not a task method right now, simply a notification. This means + that L1 cache items must be directly usable (except for the readyness part) + by users in a single step operation. + + Note that there will be an setReady call before letting the user consume + this user deployment, so this is more informational (so, if you keep at + what cache level is this instance, you can update it) than anything else. + + This is not a task method. All level 1 cache items can be dircetly + assigned to an user with no more work needed, but, if something is needed, + here you can do whatever you need. + + user is a Database user object. + ''' + pass + + def moveToCache(self, newLevel): + ''' + This method is invoked whenever the core needs to move from the current + cache level to a new cache level an user deployment. + + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + We only provide newLevel, because there is only two cache levels, so if + newLevel is L1, the actual is L2, and if it is L2, the actual is L1. + + Actually there is no possibility to move assigned services again back to + cache. If some service needs that kind of functionallity, this must be + provided at service level (for example, when doing publishing creating + a number of services that will be used, released and reused by users). + + Also, user deployments that are at cache level 2 will never get directly + assigned to user. First, it will pass to L1 and then it will get assigned. + + A good sample of a real implementation of this is moving a virtual machine + from a "suspended" state to "running" state to assign it to an user. + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + return State.FINISHED + + def userLoggedIn(self, username): + ''' + This method must be available so os managers can invoke it whenever + an user get logged into a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responsibility of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actors. + ''' + pass + + def userLoggedOut(self, username): + ''' + This method must be available so os managers can invoke it whenever + an user get logged out if a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responability of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actor. + ''' + pass + + def reasonOfError(self): + ''' + Returns the reason of the error. + + Remember that the class is responsible of returning this whenever asked + for it, and it will be asked everytime it's needed to be shown to the + user (when the administation asks for it). + + :note: Remember that you can use ugettext to translate this error to + user language whenever it is possible. (This one will get invoked + directly from admin interface and, as so, will have translation + environment correctly set up. + ''' + return 'unknown' + + def destroy(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + This method gives the oportunity to remove associated data (virtual machine, + ...) for the user consumable this instance represents. + + If return value is State.RUNNING, :py:meth:.checkState will be used to + check if the destroy operation has finished. + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('destroy method for class {0} not provided!'.format(self.__class__.__name__)) + + def cancel(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + Cancel represents a canceling of the current running operation, and + can be invoked directly by an administration or by the clean up + of the deployed service (indirectly). + + When administrator requests it, the cancel is "delayed" and not + invoked directly. + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('cancel method for class {0} not provided!'.format(self.__class__.__name__)) + + def __str__(self): + ''' + Mainly used for debugging purposses + ''' + return "Base Deployed Service" diff --git a/server/src/uds/core/services/BasePublication.py b/server/src/uds/core/services/BasePublication.py new file mode 100644 index 000000000..da5db54f0 --- /dev/null +++ b/server/src/uds/core/services/BasePublication.py @@ -0,0 +1,269 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from uds.core.Environment import Environmentable +from uds.core.Serializable import Serializable + +class Publication(Environmentable, Serializable): + ''' + This class is in fact an interface, and defines the logic of a publication + for a Service. + + A publication is the preparation of the needs of a service before it can + be provided to users. One good sample of this is, in case of virtual machines, + to copy a machine to provide COWS of this copy to users. + + As always, do not forget to invoke base class __init__ if you override it as this:: + + super(self.__class__, self).__init__(environment, **kwargs) + + This is a MUST, so internal structured gets filled correctly, so don't forget it!. + + The preferred method is not to override init, but provide the :py:meth:`.initialize`, + that will be invoked just after all internal initialization is completed. + + Normally objects of classes deriving from this one, will be serialized, called, + deserialized. This means that all that you want to ensure that is keeped inside + the class must be serialized and unserialized, because there is no warantee that + the object will get two methods invoked without haven't been remoded from memory + and loaded again, this means, IMPLEMENT marshal and unmarshal with all attributes + that you want to keep. + ''' + + # Constants for publications + + # Description of the publication + + #:Suggested time for publication finishing, in seconds + #: This allows the manager to, if publication is no done in 1 step, + #: re-check the publication once this time has passed, i.e. KVM COW publication + #: takes low time, so we suggest to check at short intervals, + #: but full clone takes a lot, so we suggest that checks are done more steady. + #: This attribute is always accessed using an instance object, so you can + #: change suggestedTime in your implementation. + suggestedTime = 10 + + def __init__(self, environment, **kwargs): + ''' + Do not forget to invoke this in your derived class using "super(self.__class__, self).__init__(environment, values)" + We want to use the env, cache and storage methods outside class. If not called, you must implement your own methods + cache and storage are "convenient" methods to access _env.cache() and _env.storage() + @param environment: Environment assigned to this publication + ''' + Environmentable.__init__(self, environment) + Serializable.__init__(self) + self._osManager = kwargs.get('osManager', None) + self._service = kwargs['service'] # Raises an exception if service is not included + self._revision = kwargs.get('revision', -1) + self._dsName = kwargs.get('dsName', 'Unknown') + + self.initialize() + + def initialize(self): + ''' + This method will be invoked from __init__ constructor. + This is provided so you don't have to provide your own __init__ method, + and invoke base class __init__. + This will get invoked when all initialization stuff is done, so + you can here access service, osManager, ... + ''' + pass + + def service(self): + ''' + Utility method to access parent service of this publication + + Returns + + Parent service instance object (not database object) + ''' + return self._service + + def osManager(self): + ''' + Utility method to access os manager for this publication. + + Returns + + Parent service instance object (not database object) + The returned value can be None if no Os manager is needed by + the service owner of this publication. + ''' + return self._osManager + + def revision(self): + ''' + Utility method to access the revision of this publication + This is a numeric value, and is set by core + ''' + return self._revision + + def dsName(self): + ''' + Utility method to access the declared deployed service name. + + This name is set by core, using the administrator provided data + at administration interface. + ''' + return self._dsName + + def publish(self): + ''' + This method is invoked whenever the administrator requests a new publication. + + The method is not invoked directly (i mean, that the administration request + do no makes a call to this method), but a DelayedTask is saved witch will + initiate all publication stuff (and, of course, call this method). + + You MUST implement it, so the publication do really something. + All publications can be synchronous or asynchronous. + + The main difference between both is that first do whatever needed, (the + action must be fast enough to do not block core), returning State.FINISHED. + + The second (asynchronous) are publications that could block the core, so + it have to be done in more than one step. + + An example publication could be a copy of a virtual machine, where: + * First we invoke the copy operation to virtualization provider + * Second, we kept needed values inside instance so we can serialize + them whenever requested + * Returns an State.RUNNING, indicating the core that the publication + has started but has to finish sometime later. (We do no check + again the state and keep waiting here, because we will block the + core untill this operation is finished). + + :note: This method MUST be provided, an exception is raised if not. + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('publish method for class {0} not provided! '.format(self.__class__.__name__)) + + def checkState(self): + ''' + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + This method will be invoked whenever a publication is started, but it + do not finish in 1 step. + + The idea behind this is simple, we can initiate an operation of publishing, + that will be done at :py:meth:.publish method. + + If this method returns that the operation has been initiated, but not finished + (State.RUNNING), the core will keep calling this method until checkState + returns State.FINISHED (or State.error). + + You MUST always provide this method if you expect the publication no to be + done in 1 step (meaning this that if publish can return State.RUNNING, this + will get called) + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('checkState method for class {0} not provided!!!'.format(self.__class__.__name__)) + + def finish(self): + ''' + Invoked when Publication manager noticed that the publication has finished. + This give us the opportunity of cleaning up things (as stored vars, etc..) + Returned value, if any, is ignored + + Default implementation does nothing. You can leave default method if you + are going to do nothing. + ''' + pass + + def reasonOfError(self): + ''' + If a publication produces an error, here we must return the reason why + it happened. This will be called just after publish or checkPublishingState + if they return State.ERROR + + The returned value, an string, will be used always by administration interface, + meaning this that the translation environment will be ready, and that you + can use ugettext to return a version that can be translated to administration + interface language. + ''' + return 'unknown' + + def destroy(self): + ''' + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + Invoked for destroying a deployed service + Do whatever needed here, as deleting associated data if needed + (i.e. a copy of the machine, snapshots, etc...) + + This method MUST be provided, even if you do nothing here (in that case, + simply return State.FINISHED). Default implementation will raise an + exception if it gets called + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('destroy method for class {0} not provided!'.format(self.__class__.__name__)) + + def cancel(self): + ''' + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + This method is invoked whenever the core needs a cancelation of current + operation. This will happen if we are, for example, preparing the + service for users, but the administration request to stop doing this. + + This method MUST be provided, even if you do nothing here (in that case, + simply return State.FINISHED). Default implementation will raise an + exception if it gets called + + :note: All task methods, like this one, are expected to handle + all exceptions, and never raise an exception from these methods + to the core. Take that into account and handle exceptions inside + this method. + ''' + raise Exception('cancel method for class {0} not provided!'.format(self.__class__.__name__)) + + def __str__(self): + ''' + String method, mainly used for debugging purposes + ''' + return "Base Publication" + diff --git a/server/src/uds/core/services/BaseService.py b/server/src/uds/core/services/BaseService.py new file mode 100644 index 000000000..29a1536fa --- /dev/null +++ b/server/src/uds/core/services/BaseService.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.utils.translation import ugettext_noop as translatable +from uds.core.BaseModule import BaseModule + +class Service(BaseModule): + ''' + This class is in fact an interface, and represents a service, that is the + definition of an offering for consumers (users). + + Class derived from this one declares the behavior of the service, as well + as custom parameter that will be needed to provide final consumable elements + to users. + + The behavior attributes must be declared always, although they have default + values, this can change in a future and declaring all needed is a good way + to avoid future problems. Of course, if you declare that do no do something + (i.e. do not uses cache), you will not have to declare related attributes + (i.e. cacheTooltip, usesCache_L2 and cacheTooltip_L2) + + As you derive from this class, if you provide __init__ in your own class, + remember to call ALWAYS at base class __init__ as this: + + super(self.__class__, self).__init__(dbAuth, environment, values) + + This is a MUST (if you override __init__), so internal structured gets + filled correctly, so don't forget it!. + + The preferred method of provide initialization is to provide the :py:meth:`.initialize`, + and do not override __init__ method. This (initialize) will be invoked after + all internal initialization, so there will be available parent, environment and storage. + + Normally objects of classes deriving from this one, will be serialized, called, + deserialized. This means that all that you want to ensure that is kept inside + the class must be serialized and unserialized, because there is no warrantee that + the object will get two methods invoked without haven't been removed from memory + and loaded again. One thing to have into account on this are Form Fields, that + default implementation marshals and unmashals them, so if your case is that you + only need data that is keeped at form fields, marshal and unmarshal and in fact + not needed. + + ''' + + #: Constant for indicating that max elements this service can deploy is unlimited. + UNLIMITED = -1 + + #: Name of type, used at administration interface to identify this + #: service (i.e. Xen server, oVirt Server, ...) + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeName = translatable('Base Service') + + #: Name of type used by Managers to identify this type of service + #: We could have used here the Class name, but we decided that the + #: module implementator will be the one that will provide a name that + #: will relation the class (type) and that name. + typeType = 'BaseService' + + #: Description shown at administration level for this service. + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeDescription = translatable('Base Service') + + #: Icon file, used to represent this service at administration interface + #: This file should be at same folder as this class is, except if you provide + #: your own :py:meth:uds.core.BaseModule.BaseModule.icon method. + iconFile = 'service.png' + + # Functional related data + + #: Normally set to UNLIMITED. This attribute indicates if the service has some "limitation" + #: for providing deployed services to users. This attribute can be set here or + #: modified at instance level, core will access always to it using an instance object. + maxDeployed = UNLIMITED #: If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy + + #: If this class uses cache or not. If uses cache is true, means that the + #: service can "prepare" some user deployments to allow quicker user access + #: to services if he already do not have one. + #: If you set this to True, please, provide a translatable :py:attr:.cacheToolTip + usesCache = False + + #: Tooltip to be used if services uses cache at administration interface, indicated by :py:attr:.usesCache + cacheTooltip = translatable('None') #: Tooltip shown to user when this item is pointed at admin interface + + #: If user deployments can be cached (see :py:attr:.usesCache), may he also can provide a secondary cache, + #: that is no more that user deployments that are "almost ready" to be used, but preperably consumes less + #: resources than L1 cache. This can give a boost to cache L1 recovering in case of peaks + #: in demand. If you set this to True, please, provide also a translatable :py:attr:.cacheTooltip_L2 + usesCache_L2 = False #: If we need to generate a "Level 2" cache for this service (i.e., L1 could be running machines and L2 suspended machines) + + #: Tooltip to be used if services uses L2 cache at administration interface, indicated by :py:attr:.usesCache_L2 + cacheTooltip_L2 = translatable('None') #: Tooltip shown to user when this item is pointed at admin interface + + #: If the service needs a o.s. manager (see os managers section) + needsManager = False + + #: If the service can be autoassigned or needs to be assigned by administrator + #: Not all services are for assigning it. Thing, i.e., a Service that manages + #: a number of Server. The desired behavior will be to let administrator + #: the service to a user in the administration interface, an not the system + #: to assign the service automatically. If this is true, the core will not + #: assign the service automatically, so if the user do not have a consumable + #: assigned, the user will never get one (of this kind, of course) + mustAssignManually = False + + #: Types of publications (preparated data for deploys) + #: If you provide this, UDS will assume that the service needs a preparation. + #: If not provided (it is None), UDS will assume that service do not needs + #: preparation. Take care, if you mark a service as it uses cache, you MUST + #: provide a publication type + #: This refers to class that provides the logic for publication, you can see + #: :py:class:uds.core.services.Publication + publicationType = None + + #: Types of deploys (services in cache and/or assigned to users) + #: This is ALWAYS a MUST. You mast indicate the class responsible + #: for managing the user deployments (user consumable services generated + #: from this one). If this attribute is not set, the service will never work + #: (core will not know how to handle the user deployments) + deployedType = None + + def __init__(self, environment, parent, values = None): + ''' + Do not forget to invoke this in your derived class using "super(self.__class__, self).__init__(environment, parent, values)". + We want to use the env, parent methods outside class. If not called, you must implement your own methods + cache and storage are "convenient" methods to access _env.cache() and _env.storage() + ''' + super(Service, self).__init__(environment, values) + self._provider = parent + self.initialize(values) + + def initialize(self, values): + ''' + This method will be invoked from __init__ constructor. + This is provided so you don't have to provide your own __init__ method, + and invoke base methods. + This will get invoked when all initialization stuff is done + + Args: + Values: If values is not none, this object is being initialized + from administration interface, and not unmarshal will be done. + If it's None, this is initialized internally, and unmarshal will + be called after this. + + Default implementation does nothing + ''' + pass + + def parent(self): + ''' + Utility method to access parent provider for this service + + Returns + + Parent provider instance object (not database object) + ''' + return self._provider + + def requestServicesForAssignation(self, **kwargs): + ''' + override this if mustAssignManualy is True + @params kwargs: Named arguments + @return an array with the services that we can assign (they must be of type deployedType) + We will access the returned array in "name" basis. This means that the service will be assigned by "name", so be care that every single service + returned are not repeated... :-) + ''' + raise Exception('The class {0} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'.format(self.__class__.__name__)) + + def __str__(self): + ''' + String method, mainly used for debugging purposes + ''' + return "Base Service Provider" + diff --git a/server/src/uds/core/services/BaseServiceProvider.py b/server/src/uds/core/services/BaseServiceProvider.py new file mode 100644 index 000000000..d4fd93c87 --- /dev/null +++ b/server/src/uds/core/services/BaseServiceProvider.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from uds.core.BaseModule import BaseModule +import logging + +logger = logging.getLogger(__name__) + + +class ServiceProvider(BaseModule): + ''' + Base Service Provider Class. + + All classes that will represent a service provider will need to be derived + from this class. + + The preferred way of using this class is by its alias name, provided + at uds.core.services module, ServiceProvider. + + This is a very basic class, intended to be the root class of services. + This means that services are childs of this class, declared at "offers" attribute. + + As you derive from this class, if you provide __init__ in your own class, + remember to call ALWAYS at base class __init__ as this: + + super(...., self).__init__(environment, values) + + The preferred method of provide initialization is to provide the :py:meth:`.initialize`, + and do not overrie __init__ method. This (initialize) will be invoked after + all internal initialization. + + This is a MUST, so internal structured gets filled correctly, so don't forget it!. + + Normally objects of classes deriving from this one, will be serialized, called, + deserialized. This means that all that you want to ensure that is keeped inside + the class must be serialized and unserialized, because there is no warantee that + the object will get two methods invoked without haven't been removed from memory + and loaded again. One thing to have into account on this are Form Fields, that + default implementation marshals and unmashals them, so if your case is that you + only need data that is keeped at form fields, marshal and unmarshal and in fact + not needed. + ''' + + #: Services that we offers. Here is a list of service types (python types) that + #: this class will provide. This types are the python clases, derived from + #: Service, that are childs of this provider + offers = [] + + #: Name of type, used at administration interface to identify this + #: provider (i.e. Xen server, oVirt Server, ...) + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeName = 'Base Provider' + + #: Name of type used by Managers to identify this tipe of service + #: We could have used here the Class name, but we decided that the + #: module implementator will be the one that will provide a name that + #: will relation the class (type) and that name. + typeType = 'BaseServiceProvider' + + #: Description shown at administration level for this provider. + #: This string will be translated when provided to admin interface + #: using ugettext, so you can mark it as "translatable" at derived classes (using ugettext_noop) + #: if you want so it can be translated. + typeDescription = 'Base Service Provider' + + #: Icon file, used to represent this provider at administration interface + #: This file should be at same folder as this class is, except if you provide + #: your own py:meth:`uds.core.BaseModule.BaseModule.icon` method. + iconFile = 'provider.png' + + @classmethod + def getServicesTypes(cls): + ''' + Returns what type of services this provider offers + ''' + return cls.offers + + @classmethod + def getServiceByType(cls, typeName): + ''' + Tries to locate a child service which type corresponds with the + one provided. + Returns None if can't find one. + + :note: The type that this method looks for is not the class, but + the typeType that Service has. + ''' + res = None + for _type in cls.offers: + if _type.type() == typeName: + res = _type + break + return res + + + def __init__(self, environment, values = None): + ''' + Do not forget to invoke this in your derived class using "super(self.__class__, self).__init__(environment, values)" + if you override this method. Better is to provide an "__initialize__" method, that will be invoked + by __init__ + Values parameter is provided (are not None) when creating or modifying the service provider, so params check should ocur here and, if not + valid, raise an "ValidationException" message + ''' + super(ServiceProvider, self).__init__(environment, values) + self.initialize(values) + + + def initialize(self, values): + ''' + This method will be invoked from __init__ constructor. + This is provided so you don't have to provide your own __init__ method, + and invoke base methods. + This will get invoked when all initialization stuff is done + + Args: + Values: If values is not none, this object is being initialized + from administration interface, and not unmarshal will be done. + If it's None, this is initialized internally, and unmarshal will + be called after this. + + Default implementation does nothing + ''' + pass + + def __str__(self): + ''' + Basic implementation, mostly used for debuging and testing, never used + at user or admin interfaces. + ''' + return "Base Service Provider" + diff --git a/server/src/uds/core/services/Exceptions.py b/server/src/uds/core/services/Exceptions.py new file mode 100644 index 000000000..f7c03975c --- /dev/null +++ b/server/src/uds/core/services/Exceptions.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +class UnsupportedException(Exception): + ''' + Reflects that we request an operation that is not supported, i.e. Cancel a publication with snapshots + ''' + pass + +class OperationException(Exception): + ''' + Reflects that the operation requested can't be acomplished, i.e. remove an snapshot without snapshot reference, cancel non running operation, etc... + ''' + pass + +class PublishException(Exception): + ''' + Reflects thate the publication can't be done for causes we don't know in advance + ''' + pass + +class DeploymentException(Exception): + ''' + Reflects that a deployment of a service (at cache, or assigned to user) can't be done for causes we don't know in advance + ''' + pass + +class CancelException(Exception): + ''' + Reflects that a "cancel" operation can't be done for some reason + ''' + +class InvalidServiceException(Exception): + ''' + Invalid service specified. The service is not ready + ''' + pass + +class MaxServicesReachedException(Exception): + ''' + Number of maximum services has been reached, and no more services + can be created for users. + ''' + pass \ No newline at end of file diff --git a/server/src/uds/core/services/ServiceProviderFactory.py b/server/src/uds/core/services/ServiceProviderFactory.py new file mode 100644 index 000000000..5936ca2cb --- /dev/null +++ b/server/src/uds/core/services/ServiceProviderFactory.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +import logging + +logger = logging.getLogger(__name__) + +class ServiceProviderFactory(object): + ''' + This class holds the register of all known service provider modules + inside UDS. + + It provides a way to register and recover providers providers. + ''' + _factory = None + + def __init__(self): + ''' + Initializes internal dictionary for service providers registration + ''' + self._providers = {} + + @staticmethod + def factory(): + ''' + Returns the factory that keeps the register of service providers. + ''' + if ServiceProviderFactory._factory == None: + ServiceProviderFactory._factory = ServiceProviderFactory() + return ServiceProviderFactory._factory + + def providers(self): + ''' + Returns the list of service providers already registered. + ''' + return self._providers + + def insert(self, type_): + ''' + Inserts type_ as a service provider + ''' + # Before inserting type, we will make a couple of sanity checks + # We could also check if it provides at least a service, but + # for debugging purposes, it's better to not check that + # We will check that if service provided by "provider" needs + # cache, but service do not provides publicationType, + # that service will not be registered and it will be informed + offers = [] + for s in type_.offers: + if s.usesCache_L2 is True: + s.usesCache = True + if s.usesCache is True and s.publicationType is None: + logger.error('Provider {0} offers {1}, but {1} needs cache and do not have publicationType defined'.format( + type_, s)) + continue + offers.append(s) + + # Only offers valid services + type_.offers = offers + logger.debug('Adding provider {0} as {1}'.format(type_.type(), type_)) + self._providers[type_.type()] = type_ + + def lookup(self, typeName): + ''' + Tries to locate a server provider and by its name, and, if + not found, returns None + ''' + return self._providers.get(typeName, None) + + def servicesThatDoNotNeedPublication(self): + ''' + Returns a list of all service providers registered that do not need + to be published + ''' + res = [] + for p in self._providers.values(): + for s in p.offers: + if s.publicationType is None and s.mustAssignManually is False: + res.append(s) + return res diff --git a/server/src/uds/core/services/__init__.py b/server/src/uds/core/services/__init__.py new file mode 100644 index 000000000..509185af5 --- /dev/null +++ b/server/src/uds/core/services/__init__.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +UDS Service modules interfaces and classes. + +From 1.0 onwards, the refactoring of UDS has started. + +We can access the base service interfaces the old method, or recommended +and easier use the new one, that is "from uds.core.services import ..." + +The new valid names for classes are: + * **factory** for old ServiceProviderFactory.ServiceProviderFactory.factory() + +I think this is an easier to use and understand way of accessing this classes + +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from BaseServiceProvider import ServiceProvider +from BaseService import Service +from BasePublication import Publication +from BaseDeployed import UserDeployment +import Exceptions + +def factory(): + ''' + Returns factory for register/access to service providers + ''' + from ServiceProviderFactory import ServiceProviderFactory + return ServiceProviderFactory.factory() diff --git a/server/src/uds/core/services/provider.png b/server/src/uds/core/services/provider.png new file mode 100644 index 0000000000000000000000000000000000000000..d2a954d44526a447e6f4e693324450868f7443b4 GIT binary patch literal 491 zcmV@I z6tZ>cs#3?$MGAIGp@M&a*2RKp$X#+d-yK9MULgrQczO4}pZ6m4M zO;r>{>SMK1(R3jAkU$c z(ue|C=n(p<8K9a`77{=;?4T$LkSNe1Vn%?5fE)o*{&oS%K|tex5Zy(6z;}}H5(OFOaM9~Q zx7&rU^;%d7NZx5-7`_aL!@KIe_FWhZ2B&COuIuhI#@gB+jYenIeO0r?aeN&F!KL=o zG(F6?Xp54<9?3z011HBvpSaL>eLkDbZue{YI{B7A; zx^zG+3`~(Sh76=N$^gPH@B}t~{eE8{Qq_s}Bc0B^clW(J|M0A}@Cg0^OH^2+eWX=z z8HV;9tA?$Y#~XCs8)eiD`C;wl#NCMX9_3J#DwZG;yA(cv%$jyaC7s6 zE3I*NH|N>gxtL5Qa(_%f^G&E%3b9(P{uT&=6^lLX z&UHLKeamS)yuRv7&0Gw~Ajab{`u+a9fTeNw4=^TXvZa_O&nYAfl2g!g-+^ei+c;q* z`(?de-|OLAfZmE8=2uN%Mx)WT+wE3y0khd`tJ?F=^odr-WyG{^|NT4v2BY2&XM6rZ QqyPW_07*qoM6N<$f=DO_-T(jq literal 0 HcmV?d00001 diff --git a/server/src/uds/core/transports/BaseTransport.py b/server/src/uds/core/transports/BaseTransport.py new file mode 100644 index 000000000..8a31d7ed5 --- /dev/null +++ b/server/src/uds/core/transports/BaseTransport.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext as _ +from uds.core.util import OsDetector +from uds.core.BaseModule import BaseModule + +class BaseTransport(BaseModule): + ''' + An OS Manager is responsible for communication the service the different actions to take (i.e. adding a windows machine to a domain) + The Service (i.e. virtual machine) communicates with the OSManager via a published web method, that must include the unique ID. + In order to make easier to agents identify themselfs, the Unique ID can be a list with various Ids (i.e. the macs of the virtual machine). + Server will iterate thought them and look for an identifier associated with the service. This list is a comma separated values (i.e. AA:BB:CC:DD:EE:FF,00:11:22:...) + Remember also that we inherit the test and check methods from BaseModule + ''' + # Transport informational related data, inherited from BaseModule + typeName = 'Base Transport Manager' + typeType = 'BaseTransport' + typeDescription = 'Base Transport' + iconFile = 'transport.png' + needsJava = False # If this transport needs java for rendering + # Supported names for OS (used right now, but lots of more names for sure) + # Windows + # Macintosh + # Linux + supportedOss = [OsDetector.Linux, OsDetector.Windows, OsDetector.Macintosh] # Supported operating systems + + # If this transport is visible via Web, via Thick Client or both + webTransport = False + tcTransport = False + + def __init__(self,environment, values): + super(BaseTransport, self).__init__(environment, values) + self.initialize(values) + + def initialize(self, values): + ''' + This method will be invoked from __init__ constructor. + This is provided so you don't have to provide your own __init__ method, + and invoke base methods. + This will get invoked when all initialization stuff is done + + Args: + Values: If values is not none, this object is being initialized + from administration interface, and not unmarshal will be done. + If it's None, this is initialized internally, and unmarshal will + be called after this. + + Default implementation does nothing + ''' + pass + + def destroy(self): + ''' + Invoked when Transport is deleted + ''' + pass + + def isAvailableFor(self, ip): + ''' + Checks if the transport is available for the requested destination ip + Override this in yours transports + ''' + return False + + @classmethod + def supportsOs(cls, osName): + ''' + Helper method to check if transport supports requested operating system. + Class method + ''' + return cls.supportedOss.count(osName) > 0 + + def renderForHtml(self, id, ip, os, user, password): + ''' + Requests the html rendering of connector for the destination ip, (dbUser) and password + @param id: id of the transport + @param ip: ip of the destination + @param user: user (dbUser) logged in + @param pass: password used in authentication + ''' + return _('Transport empty') + + def getHtmlComponent(self, id, os, componentId): + ''' + This is a method to let the transport add own components (images, applets, or whatever) to the rendered html + The reference to object will be the access to the uds.web.views.transcomp, with parameters transportId = ourTransportId and + componentId = one id recognized by this method + We expect an return array, with first parameter as mime/type and second the content to return + ''' + return ['text/plain', ''] + + def __str__(self): + return "Base OS Manager" + \ No newline at end of file diff --git a/server/src/uds/core/transports/TransportsFactory.py b/server/src/uds/core/transports/TransportsFactory.py new file mode 100644 index 000000000..27532c8b8 --- /dev/null +++ b/server/src/uds/core/transports/TransportsFactory.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +import logging + +logger = logging.getLogger(__name__) + +class TransportsFactory(object): + _factory = None + + def __init__(self): + self._jobs = {} + + @staticmethod + def factory(): + if TransportsFactory._factory == None: + TransportsFactory._factory = TransportsFactory() + return TransportsFactory._factory + + def providers(self): + return self._jobs + + def insert(self, type): + logger.debug('Adding transport {0} as {1}'.format(type.type(), type)) + self._jobs[type.type()] = type + + def lookup(self, typeName): + try: + return self._jobs[typeName] + except KeyError: + return None diff --git a/server/src/uds/core/transports/__init__.py b/server/src/uds/core/transports/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/core/transports/transport.png b/server/src/uds/core/transports/transport.png new file mode 100644 index 0000000000000000000000000000000000000000..e572c72f989c1ca97e7994e6395da4c636d2b084 GIT binary patch literal 1258 zcmYk6Yitx%6vyw}J9B4ec6O$_v)iZb){-L6SP4an9|%%;h2R5FjKQEEj4>u^{GcEB zV2lZBlmz1k3>XuQi4kJbL_mYQR-mMS)Pg*0(NemlyYnhLkDd2*s}c6Yy*KCPo?rgw z+;h%HPi-$%2d3Qfp1&$MH_gCKo-7bT) zAOPPEYOhE`L;{;juqcjVHj3DVx-yw-$jH|N;)@U<>{tEQZACra+}OHr)`9$sqHo>2 zv3JHXCrnWowC>uxp{YA5EEXbKI^&o&!y|V?=v(bEt?p)#4-oLGQ4ALgEd4D?a5d6B zR22Br6~#&eFIoy|`m7*jzp2>NY8sG>8A%wz3?BzB+QBB}GNL>ZN6}*##thFfCs_{F zqf%04NDSjbvLU@)pO}0>Nb#6Q_R*>rZjF=a2bcuUc>451aj17EI0jTKYja~Jd4T}H zLxik4RTQEL1Q@aa9H5ip;V|$&gmZfO{KXr(u_JG(7N-m>jw0HfY-g;iKZycAO=GUe zaXd=$sd`nBUws-;@q;8HBqi`#XjC${<&HJHXMyU30g!Pm&Dzs|G%tH6hgC%{-EkE~ zd(Bo$qoJIuPrIRNZdt1)XRx^h&Xt8s;tCh>#Zd=)srI;r@s*QzAlFdkfXa)*-Y!kmK8w;28 zbg0hRL%9@~Z0ek&Hi*)HDEd^N_+F1dpS05-^fQFC_A*;P-n)F)ub(^{%uKr z9|zw$f#yHF=hNZ0cda?UXUE2!k0`GmI{4!Dv${*&uL72dRtS{Stm)1&ws0UcaR+vk zUJ_JdC5RZ9p-l=zWff)fO=IK5VysN{P!d|8mhbEfeYZX`qdYaoRBSIuaz)p5%UpP2 zxJlwyFwNZzv^^D?=S38bcO+Qir}YzGT0~#!+RV1Xv7>7tgkRKH|Cf0f!>{FR_H!E(~0xB^xT%pOv-^k{?bq5ihGBK-#Mq6*Ou2Yc$_}j?AGZFc5#(h#v*_y3<#Ytl8#Gtyg|T(hvF+Zh zESKw0jrrCttz^&t`S1Y$=at`wGTCfil-r*h|7&cjVyHzSCAK9nid@fYgA_0vOTX5U zZ|{%7@L)}keIEsVv)Xs*+^H^gYGR~@f!*^LKOY6vY0TnFa$~d6HE)3|q>>S4+20t9 zhqZEXgiEFA#+H^K48!<7imvFDZPzbf1hq+0y9Iz{*g;(@D~7HCglL{m;xU02_*%bT sVOh4BVzBF&Gk=JZ)TEV4vo3@nknfzvb6?)vG;6PaVpIPQkMDT@AB#dT<^TWy literal 0 HcmV?d00001 diff --git a/server/src/uds/core/ui/UserInterface.py b/server/src/uds/core/ui/UserInterface.py new file mode 100644 index 000000000..bb019d7a3 --- /dev/null +++ b/server/src/uds/core/ui/UserInterface.py @@ -0,0 +1,779 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext as _ +import cPickle +import logging + +logger = logging.getLogger(__name__) + +class gui(object): + ''' + This class contains the representations of fields needed by UDS modules and + administation interface. + + This contains fields types, that modules uses to make a form and interact + with users. + + The use of this provided fields are as follows: + + The Module is descendant of "BaseModule", which also is inherited from this + class. + + At class level, we declare the fields needed to interact with the user, as + this example: + + .. code-block:: python + + class AuthModule(Authenticator): + # ... + # Other initializations + # ... + users = gui.EditableList(label = 'Users', tooltip = 'Select users', + order = 1, values = ['user1', 'user2', 'user3', 'user4']) + passw = gui.Password(label='Pass', length=32, tooltip='Password', + order = 2, required = True, defValue = '12345') + # ... + # more fields + # ... + + At class instantiation, this data is extracted and processed, so the admin + can access this form to let users + create new instances of this module. + ''' + + #: True string value + TRUE = 'true' + #: False string value + FALSE = 'false' + + #: Static Callbacks simple registry + callbacks = {} + + # Helpers + @staticmethod + def convertToChoices(vals): + ''' + Helper to convert from array of strings to the same dict used in choice, + multichoice, .. + The id is set to values in the array (strings), while text is left empty. + ''' + res = [] + for v in vals: + res.append( { 'id' : v, 'text' : '' } ) + return res + + @staticmethod + def choiceItem(id_, text): + ''' + Helper method to create a single choice item. + + Args: + id: Id of the choice to create + + text: Text to assign to the choice to create + + Returns: + An dictionary, that is the representation of a single choice item, + with 2 keys, 'id' and 'text' + + :note: Text can be anything, the method converts it first to text before + assigning to dictionary + ''' + return { 'id' : str(id_), 'text' : str(text) } + + @staticmethod + def strToBool(str_): + ''' + Converts the string "true" (case insensitive) to True (boolean). + Anything else is converted to false + + Args: + str: Str to convert to boolean + + Returns: + True if the string is "true" (case insensitive), False else. + ''' + if str_.lower() == gui.TRUE: + return True + return False + + @staticmethod + def boolToStr(bol): + ''' + Converts a boolean to the string representation. True is converted to + "true", False to "false". + + Args: + bol: Boolean value (True or false) to convert + + Returns: + "true" if bol evals to True, "false" if don't. + ''' + if bol: + return gui.TRUE + return gui.FALSE + + # Classes + + class InputField(object): + ''' + Class representing an simple input field. + This class is not directly usable, must be used by any inherited class + (fields all of them) + All fields are inherited from this one + + The data managed for an input field, and their default values are: + * length: Max length of the field. Defaults to DEFAULT_LENGTH + * required: If this field is a MUST. defaults to false + * label: Label used with this field. Defaults to '' + * defvalue: Default value for the field. Defaults to '' (this is + always an string) + * rdonly: If the field is read only on modification. On creation, + all fields are "writable". Defaults to False + * order: order inside the form, defaults to 0 (if two or more fields + has same order, the output order may be anything) + * tooltip: Tooltip used in the form, defaults to '' + * type: type of the input field, defaults to "text box" (TextField) + + In every single field, you must at least indicate: + * if required or not + * order + * label + * tooltip + * defvalue + * rdonly if can't be modified once it's created + + Any other paremeter needed is indicated in the corresponding field class. + + Also a value field is available, so you can get/set the form field value. + This property expects always an string, no matter what kind of field it is. + + Take into account also that "value" has precedence over "defValue", + so if you use both, the used one will be "value". This is valid for + all form fields. + ''' + TEXT_TYPE = 'text' + TEXTBOX_TYPE = 'textbox' + NUMERIC_TYPE = 'numeric' + PASSWORD_TYPE = 'password' + HIDDEN_TYPE = 'hidden' + CHOICE_TYPE = 'choice' + MULTI_CHOICE_TYPE = 'multichoice' + EDITABLE_LIST = 'editlist' + CHECKBOX_TYPE = 'checkbox' + + DEFAULT_LENTGH = 32 #: If length of some fields are not especified, this value is used as default + + + + def __init__(self, **options): + self._data = { + 'length' : options.get('length', gui.InputField.DEFAULT_LENTGH), + 'required' : options['required'] if options.has_key('required') else False, + 'label': options['label'] if options.has_key('label') else '', + 'defvalue' : str(options['defvalue']) if options.has_key('defvalue') else '', + 'rdonly' : options['rdonly'] if options.has_key('rdonly') else False, # This property only affects in "modify" operations + 'order' : options['order'] if options.has_key('order') else 0, + 'tooltip' : options['tooltip'] if options.has_key('tooltip') else '', + 'type' : gui.InputField.TEXT_TYPE, + 'value' : options['value'] if options.has_key('value') else '', + } + + def _type(self, type_): + ''' + Sets the type of this field. + + Args: + type: Type to set (from constants of this class) + ''' + self._data['type'] = type_ + + def isType(self, type_): + ''' + Returns true if this field is of specified type + ''' + return self._data['type'] == type_ + + @property + def value(self): + ''' + Obtains the stored value + ''' + return self._data['value'] + + @value.setter + def value(self, value): + ''' + Stores new value (not the default one) + ''' + self._setValue(value) + + def _setValue(self, value): + ''' + So we can override value setting at descendants + ''' + self._data['value'] = value + + + def guiDescription(self): + ''' + Returns the dictionary with the description of this item. + We copy it, cause we need to translate the label and tooltip fields + and don't want to + alter original values. + ''' + data = self._data.copy() + data['label'] = _(data['label']) + data['tooltip'] = _(data['tooltip']) + return data + + def setDefValue(self, defValue): + ''' + Sets the default value of the field· + + Args: + defValue: Default value (string) + ''' + self._data['defvalue'] = defValue + + class TextField(InputField): + ''' + This represents a text field. + + The values of parameters are inherited from :py:class:`InputField` + + Additionally to standard parameters, the length parameter is a + recommended one for this kind of field. + + You can specify that this is a multiline text box with **multiline** + parameter. If it exists, and is greater than 1, indicates how much + lines will be used to display field. (Max number is 8) + + Example usage: + + .. code-block:: python + + # Declares an text form field, with label "Host", tooltip + # "Host name for this module", that is required, + # with max length of 64 chars and order = 1, and is editable + # after creation. + host = gui.TextField(length=64, label = _('Host'), order = 1, + tooltip = _('Host name for this module'), required = True) + + # Declares an text form field, with label "Other", + # tooltip "Other info", that is not required, that is not + # required and that is not editable after creation. + other = gui.TextField(length=64, label = _('Other'), order = 1, + tooltip = _('Other info'), rdonly = True) + + ''' + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._type(gui.InputField.TEXT_TYPE) + multiline = int(options.get('multiline', 0)) + if multiline > 8: + multiline = 8 + self._data['multiline'] = multiline + + + class NumericField(InputField): + ''' + This represents a numeric field. It apears with an spin up/down button. + + The values of parameres are inherited from :py:class:`InputField` + + Additionally to standard parameters, the length parameter indicates the + max number of digits (0-9 values). + + Example usage: + + .. code-block:: python + + # Declares an numeric form field, with max value of 99999, label + # "Port", that is required, + # with tooltip "Port (usually 443)" and order 1 + num = gui.NumericField(length=5, label = _('Port'), + defvalue = '443', order = 1, tooltip = _('Port (usually 443)'), + required = True) + ''' + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._type(gui.InputField.NUMERIC_TYPE) + + def num(self): + ''' + Return value as integer + ''' + return int(self.value) + + class PasswordField(InputField): + ''' + This represents a password field. It appears with "*" at input, so the contents is not displayed + + The values of parameres are inherited from :py:class:`InputField` + + Additionally to standard parameters, the length parameter is a recommended one for this kind of field. + + Example usage: + + .. code-block:: python + + # Declares an text form field, with label "Password", + # tooltip "Password of the user", that is required, + # with max length of 32 chars and order = 2, and is + # editable after creation. + passw = gui.PasswordField(lenth=32, label = _('Password'), + order = 4, tooltip = _('Password of the user'), + required = True) + + ''' + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._type(gui.InputField.PASSWORD_TYPE) + + class HiddenField(InputField): + ''' + This represents a hidden field. It is not displayed to the user. It use + is for keeping info at form needed + by module, but not editable by user (i.e., one service can keep info + about the parent provider in hiddens) + + The values of parameres are inherited from :py:class:`InputField` + + These are almost the same as TextFields, but they do not get displayed + for user interaction. + + Example usage: + + .. code-block:: python + + # Declares an empty hidden field + hidden = gui.HiddenField() + + + After that, at initGui method of module, we can store a value inside + using setDefValue as shown here: + + .. code-block:: python + + def initGui(self): + # always set defValue using self, cause we only want to store + # value for current instance + self.hidden.setDefValue(self.parent().serialize()) + + ''' + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._type(gui.InputField.HIDDEN_TYPE) + + class CheckBoxField(InputField): + ''' + This represents a check box field, with values "true" and "false" + + The values of parameters are inherited from :py:class:`InputField` + + The valid values for this defvalue are: "true" and "false" (as strings) + + Example usage: + + .. code-block:: python + + # Declares an check box field, with label "Use SSL", order 3, + # tooltip "If checked, will use a ssl connection", default value + # unchecked (not included, so it's empty, so it's not true :-)) + ssl = gui.CheckBoxField(label = _('Use SSL'), order = 3, + tooltip = _('If checked, will use a ssl connection')) + + ''' + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._type(gui.InputField.CHECKBOX_TYPE) + + def isTrue(self): + ''' + Checks that the value is true + ''' + return self.value == 'true' + + class ChoiceField(InputField): + ''' + This represents a simple combo box with single selection. + + The values of parameters are inherited from :py:class:`InputField` + + ChoiceField needs a function to provide values inside it. + + * We specify the values via "values" option this way: + + Example: + + .. code-block:: python + + choices = gui.ChoiceField(label="choices", values = [ {'id':'1', + 'text':'Text 1'}, {'id':'xxx', 'text':'Text 2'}]) + + You can specify a multi valuated field via id-values, or a + single-valued field via id-value + + * We can override choice values at UserInterface derived class + constructor or initGui using setValues + + There is an extra option available for this kind of field: + + fills: This options is a dictionary that contains this fields: + * 'callbackName' : Callback name for invocation via the specific + method xml-rpc. This name is a name we assign to this callback, + and is used to locate the method when callback is invoked from + admin interface. + * 'function' : Function to execute. + + This funtion receives one parameter, that is a dictionary with + all parameters (that, in time, are fields names) that we have + requested. + + The expected return value for this callback is an array of + dictionaries with fields and values to set, as + example show below shows. + * 'parameters' : Array of field names to pass back to server so + it can obtain the results. + + Of course, this fields must be part of the module. + + Example: + + .. code-block:: python + + choice1 = gui.ChoiceField(label="Choice 1", values = ...., + fills = { 'target': 'choice2', 'callback': fncValues, + 'parameters': ['choice1', 'name']} + ) + choice2 = ghui.ChoiceField(label="Choice 2") + + Here is a more detailed explanation, using the VC service module as + sample. + + .. code-block:: python + + class VCHelpers(object): + # ... + # other stuff + # ... + @staticmethod + def getMachines(parameters): + # ...initialization and other stuff... + if parameters['resourcePool'] != '': + # ... do stuff ... + data = [ { 'name' : 'machine', 'values' : 'xxxxxx' } ] + return data + + class ModuleVC(services.Service) + # ... + # stuff + # ... + resourcePool = gui.ChoiceField( + label=_("Resource Pool"), rdonly = False, order = 5, + fills = { + 'callbackName' : 'vcFillMachinesFromResource', + 'function' : VCHelpers.getMachines, + 'parameters' : ['vc', 'ev', 'resourcePool'] + }, + tooltip = _('Resource Pool containing base machine'), + required = True + ) + + machine = gui.ChoiceField(label = _("Base Machine"), order = 6, + tooltip = _('Base machine for this service'), required = True ) + + vc = gui.HiddenField() + ev = gui.HiddenField() # .... + + ''' + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._data['values'] = options['values'] if options.has_key('values') else [] + if options.has_key('fills'): + # Save fnc to register as callback + fills = options['fills'] + fnc = fills['function'] + fills.pop('function') + self._data['fills'] = fills + gui.callbacks[fills['callbackName']] = fnc + self._type(gui.InputField.CHOICE_TYPE) + + def setValues(self, values): + ''' + Set the values for this choice field + ''' + self._data['values'] = values + + class MultiChoiceField(InputField): + ''' + Multichoices are list of items that are multi-selectable. + + There is a new parameter here, not covered by InputField: + * 'rows' to tell gui how many rows to display (the length of the + displayable list) + + "defvalue" is expresed as a comma separated list of ids + + This class do not have callback support, as ChoiceField does. + + The values is an array of dictionaries, in the form [ { 'id' : 'a', + 'text': b }, ... ] + + Example usage: + + .. code-block:: python + + # Declares a multiple choices field, with label "Datastores", that + is editable, with 5 rows for displaying + # data at most in user interface, 8th in order, that is required + and has tooltip "Datastores where to put incrementals", + # this field is required and has 2 selectable items: "datastore0" + with id "0" and "datastore1" with id "1" + datastores = gui.MultiChoiceField(label = _("Datastores"), + rdonly = False, rows = 5, order = 8, + tooltip = _('Datastores where to put incrementals'), + required = True, + values = [ {'id': '0', 'text': 'datastore0' }, + {'id': '1', 'text': 'datastore1' } ] + ) + ''' + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._data['values'] = options['values'] if options.has_key('values') else [] + self._data['rows'] = options['rows'] if options.has_key('rows') else -1 + self._type(gui.InputField.MULTI_CHOICE_TYPE) + + def setValues(self, values): + ''' + Set the values for this multi choice field + ''' + self._data['values'] = values + + class EditableList(InputField): + ''' + Editables list are lists of editable elements (i.e., a list of IPs, macs, + names, etcc) treated as simple strings with no id + + The struct used to pass values is an array of strings, i.e. ['1', '2', + 'test', 'bebito', ...] + + This list don't have "selected" items, so its defvalue field is simply + ignored. + + We only nee to pass in "label" and, maybe, "values" to set default + content for the list. + + Keep in mind that this is an user editable list, so the user can insert + values and/or import values from files, so + by default it will probably have no content at all. + + Example usage: + + .. code-block:: python + + # + ipList = gui.EditableList(label=_('List of IPS')) + + ''' + + #: Constant for separating values at "value" method + SEPARATOR = '\001' + + def __init__(self, **options): + super(self.__class__, self).__init__(**options) + self._data['values'] = gui.convertToChoices(options['values']) if options.has_key('values') else [] + self._type(gui.InputField.EDITABLE_LIST) + + def _setValue(self, values): + ''' + So we can override value setting at descendants + ''' + super(self.__class__, self)._setValue(values) + self._data['values'] = gui.convertToChoices(values) + + + +class UserInterfaceType(type): + ''' + Metaclass definition for moving the user interface descriptions to a usable + better place + ''' + def __new__(cls, classname, bases, classDict): + newClassDict = { } + _gui = {} + # We will keep a reference to gui elements also at _gui so we can access them easily + for attrName, attr in classDict.items(): + if isinstance(attr, gui.InputField): + _gui[attrName] = attr + newClassDict[attrName] = attr + newClassDict['_gui'] = _gui + return type.__new__(cls, classname, bases, newClassDict) + +class UserInterface(object): + ''' + This class provides the management for gui descriptions (user forms) + + Once a class is derived from this one, that class can contain Field + Descriptions, + that will be managed correctly. + + By default, the values passed to this class constructor are used to fill + the gui form fields values. + ''' + __metaclass__ = UserInterfaceType + + def __init__(self, values = None): + #: If there is an array of elements to initialize, simply try to store values on form fields + if values is not None: + for k, v in self._gui.iteritems(): + if values.has_key(k): + v.value = values[k] + + + def initGui(self): + ''' + This method gives the oportunity to initialize gui fields before they + are send to administartion client. + We need this because at initialization time we probably don't have the + data for gui. + + :note: This method is used as a "trick" to allow to modify default form + data for services. Services are child of Service Providers, and + will probably need data from Provider to fill initial form data. + The rest of modules will not use this, and this only will be used + when the user requests a new service or wants to modify existing + one. + :note: There is a drawback of this, and it is that there is that this + method will modify service default data. It will run fast (probably), + but may happen that two services of same type are requested at same + time, and returned data will be probable a nonsense. We will take care + of this posibility in a near version... + ''' + pass + + def valuesDict(self): + ''' + Returns own data needed for user interaction as a dict of key-names -> + values. The values returned must be strings. + + Example: + we have 2 text field, first named "host" and second named "port", + we can do something like this: + + .. code-block:: python + + return { 'host' : self.host, 'port' : self.port } + + (Just the reverse of :py:meth:`.__init__`, __init__ receives this + dict, valuesDict must return the dict) + + Names must coincide with fields declared. + + Returns: + Dictionary, associated with declared fields. + Default implementation returns the values stored at the gui form + fields declared. + + :note: By default, the provided method returns the correct values + extracted from form fields + + ''' + dic = {} + for k, v in self._gui.iteritems(): + if v.isType(gui.InputField.EDITABLE_LIST): + dic[k] = gui.convertToChoices(v.value) + else: + dic[k] = v.value + return dic + + + def serializeForm(self): + ''' + All values stored at form fields are serialized and returned as a single + string + Separating char is + + The returned string is zipped and then converted to base 64 + + Note: Hidens are not serialized, they are ignored + + ''' + arr = [] + for k, v in self._gui.iteritems(): + if v.isType(gui.InputField.HIDDEN_TYPE): + pass + if v.isType(gui.InputField.EDITABLE_LIST): + val = '\001' + cPickle.dumps(v.value) + else: + val = v.value + arr.append(k + '\003' + val) + return '\002'.join(arr).encode('zip') + + def unserializeForm(self, values): + ''' + This method unserializes the values previously obtained using + :py:meth:`serializeForm`, and stores + the valid values form form fileds inside its corresponding field + ''' + if values == '': # Has nothing + return + for txt in values.decode('zip').split('\002'): + k, v = txt.split('\003') + if self._gui.has_key(k): + if v[0] == '\001': + val = cPickle.loads(v[1:]) + else: + val = v + self._gui[k].value = val + + @classmethod + def guiDescription(cls, obj = None): + ''' + This simple method generates the gui description needed by the + administration client, so it can + represent it at user interface and manage it. + + Args: + object: If not none, object that will get its "initGui" invoked + This will only happen (not to be None) in Services. + ''' + if obj is not None: + obj.initGui() # We give the "oportunity" to fill necesary gui data before providing it to client + + res = [] + for key, val in cls._gui.iteritems(): + res.append( { 'name' : key, 'gui' : val.guiDescription(), 'value' : '' }, ) + return res diff --git a/server/src/uds/core/ui/__init__.py b/server/src/uds/core/ui/__init__.py new file mode 100644 index 000000000..fd7539037 --- /dev/null +++ b/server/src/uds/core/ui/__init__.py @@ -0,0 +1,8 @@ +''' +User interface part of UDS modules. + +This module contains the definition of UserInterface, needed to describe the interaction +between an UDS module and the administration interface +''' + +from UserInterface import gui diff --git a/server/src/uds/core/util/AutoAttributes.py b/server/src/uds/core/util/AutoAttributes.py new file mode 100644 index 000000000..357ca38e3 --- /dev/null +++ b/server/src/uds/core/util/AutoAttributes.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.Serializable import Serializable +import cPickle +import timeit + +class Attribute(object): + + def __init__(self, theType, value = None): + self._type = theType + self.setValue(value) + + def getType(self): + return self._type + + def getValue(self): + return self._value + + def getStrValue(self): + return str(self._value) + + def setValue(self, value): + if value is None: + self._value = self._type() + else: + self._value = self._type(value) + +class AutoAttributes(Serializable): + ''' + Easy creation of attributes to marshal & unmarshal at modules + usage as base class (First class so yours inherits this "marshal" and "unmarshal" + initialize at init with super(myclass,self).__init__(attr1=type, attr2=type, ...) + or with declare(attr1=type,attr2=type,..) + Access attrs as "self._attr1, self._attr2" + ''' + + #: This codec is not intended to override Serializable codec + #: Serializable codec is for encoding marshaled data, + #: while this codec is for encoding pickled data from autoattributes + ACODEC = 'zip' + + def __init__(self, **kwargs): + self.declare(**kwargs) + + def __getattribute__(self, name): + if name.startswith('_') and self.dict.has_key(name[1:]): + return self.dict[name[1:]].getValue() + return object.__getattribute__(self, name) + + def __setattr__(self, name, value): + if name.startswith('_') and self.dict.has_key(name[1:]): + self.dict[name[1:]].setValue(value) + else: + object.__setattr__(self, name, value) + + def declare(self, **kwargs): + d = {} + for key,typ in kwargs.iteritems(): + d[key] = Attribute(typ) + self.dict = d + + def marshal(self): + return '\2'.join( [ '%s\1%s' % (k, cPickle.dumps(v)) for k, v in self.dict.iteritems() ] ).encode(AutoAttributes.ACODEC) + + def unmarshal(self, data): + if data == '': # Can be empty + return + # We keep original data (maybe incomplete) + for pair in data.decode(AutoAttributes.ACODEC).split('\2'): + k, v = pair.split('\1') + self.dict[k] = cPickle.loads(v) + + def __str__(self): + str = '' diff --git a/server/src/uds/core/util/Cache.py b/server/src/uds/core/util/Cache.py new file mode 100644 index 000000000..39872155b --- /dev/null +++ b/server/src/uds/core/util/Cache.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.db import transaction +from uds.models import Cache as dbCache, getSqlDatetime +from datetime import datetime, timedelta +import hashlib +import logging +import cPickle + +logger = logging.getLogger(__name__) + +class Cache(object): + DEFAULT_VALIDITY = 60 + CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded + + def __init__(self, owner): + self._owner = owner + + + def __getKey(self, key): + h = hashlib.md5() + h.update(self._owner + key) + return h.hexdigest() + + def get(self,skey): + now = getSqlDatetime() + #logger.debug('Requesting key "%s" for cache "%s"' % (skey, self._owner,)) + try: + key = self.__getKey(skey) + c = dbCache.objects.get(pk=key) + expired = now > c.created + timedelta(seconds = c.validity) + if expired: + return None + val = cPickle.loads(c.value.decode(Cache.CODEC)) + return val + except dbCache.DoesNotExist: + logger.debug('key not found') + return None + + def remove(self,skey): + #logger.debug('Removing key "%s" for uService "%s"' % (skey, self._owner)) + try: + key = self.__getKey(skey) + dbCache.objects.get(pk=key).delete() + except dbCache.DoesNotExist: + logger.debug('key not found') + + @transaction.autocommit + def put(self, skey, value, validity = None): + #logger.debug('Saving key "%s" for cache "%s"' % (skey, self._owner,)) + if validity == None: + validity = Cache.DEFAULT_VALIDITY + key = self.__getKey(skey) + value = cPickle.dumps(value).encode(Cache.CODEC) + now = getSqlDatetime() + try: + dbCache.objects.create( owner = self._owner, key = key, value = value, created = now, validity = validity ) + except Exception: + # Already exists, modify it + c = dbCache.objects.get(pk=key) + c.owner = self._owner + c.key = key + c.value = value + c.created = datetime.now() + c.validity = validity + c.save() + + @transaction.autocommit + def refresh(self, skey): + #logger.debug('Refreshing key "%s" for cache "%s"' % (skey, self._owner,)) + try: + key = self.__getKey(skey) + c = dbCache.objects.get(pk=key) + c.created = getSqlDatetime() + c.save() + except dbCache.DoesNotExist: + logger.debug('Can\'t refresh cache key %s because it don\'t exists' % skey) + return + + @staticmethod + def purge(): + dbCache.objects.all().delete() + + @staticmethod + def cleanUp(): + dbCache.cleanUp() + + @staticmethod + def delete(owner = None): + #logger.info("Deleting cache items") + if owner == None: + objects = dbCache.objects.all() + else: + objects = dbCache.objects.filter(owner=owner) + objects.delete() + diff --git a/server/src/uds/core/util/Config.py b/server/src/uds/core/util/Config.py new file mode 100644 index 000000000..d89720c90 --- /dev/null +++ b/server/src/uds/core/util/Config.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.conf import settings +from uds.models import Config as dbConfig +from uds.core.managers.CryptoManager import CryptoManager +import logging + +logger = logging.getLogger(__name__) + +GLOBAL_SECTION = 'UDS' + + +class Config: + ''' + Keeps persistend configuration data + ''' + + class _Value: + def __init__(self, section, key, default = '', crypt = False): + self._section = section + self._key = key + self._crypt = crypt + if crypt is False: + self._default = default + else: + self._default = CryptoManager.manager().encrypt(default) + self._data = None + + def get(self, force = False): + try: + if force or self._data is None: + #logger.debug('Accessing db config {0}.{1}'.format(self._section.name(), self._key)) + readed = dbConfig.objects.filter(section=self._section.name(), key=self._key)[0] + self._data = readed.value + self._crypt = [self._crypt, True][readed.crypt] # True has "higher" precedende than False + except Exception: + # Not found + if self._default != '': + if self._crypt: + self.set( CryptoManager.manager().decrypt(self._default) ) + else: + self.set(self._default) + self._data = self._default + if self._crypt is True: + return CryptoManager.manager().decrypt(self._data) + else: + return self._data + + def getInt(self): + try: + return int(self.get()) + except Exception: + return self._default + + def getBool(self): + if self.get() == '0': + return False + return True + + def key(self): + return self._key + + def section(self): + return self._section.name() + + def isCrypted(self): + return self._crypt + + def set(self, value): + if self._crypt is True: + value = CryptoManager.manager().encrypt(value) + ''' + Editable here means that this configuration value can be edited by admin directly (generally, that this is a "clean text" value) + ''' + logger.debug('Saving config {0}.{1} as {2}'.format(self._section.name(), self._key, value)) + try: + if dbConfig.objects.filter(section=self._section.name(), key=self._key).update(value=value, crypt=self._crypt) == 0: + raise Exception() # Do not exists, create a new one + except Exception: + try: + dbConfig.objects.create(section=self._section.name(), key=self._key, value=value, crypt=self._crypt) + except Exception: + # Probably a migration issue, just ignore it + logger.info("Could not save configuration key {0}.{1}".format(self._section.name(), self._key)) + + class _Section: + def __init__(self, sectionName): + self._sectionName = sectionName + + def value(self, key, default = ''): + return Config._Value(self, key, default) + + def valueCrypt(self, key, default = ''): + return Config._Value(self, key, default, True) + + def name(self): + return self._sectionName + + + + @staticmethod + def section(sectionName): + return Config._Section(sectionName) + + @staticmethod + def enumerate(): + for cfg in dbConfig.objects.all(): + if cfg.crypt is True: + val = Config.section(cfg.section).valueCrypt(cfg.key, CryptoManager.manager().decrypt(cfg.value)) + else: + val = Config.section(cfg.section).value(cfg.key, cfg.value) + yield val + + @staticmethod + def update(section, key, value): + # If cfg value does not exists, simply ignore request + try: + cfg = dbConfig.objects.filter(section=section, key=key)[0] + if cfg.crypt is True: + value = CryptoManager.manager().encrypt(value) + cfg.value = value + cfg.save() + logger.debug('Updated value for {0}.{1} to {2}'.format(section, key, value)) + except Exception: + pass + +class GlobalConfig: + ''' + Simple helper to keep track of global configuration + ''' + SESSION_EXPIRE_TIME = Config.section(GLOBAL_SECTION).value('sessionExpireTime', '24') # Max session duration (in use) after a new publishment has been made + # Delay between cache checks. reducing this number will increase cache generation speed but also will load service providers + CACHE_CHECK_DELAY = Config.section(GLOBAL_SECTION).value('cacheCheckDelay', '20') + # Delayed task number of threads PER SERVER, with higher number of threads, deplayed task will complete sooner, but it will give more load to overall system + DELAYED_TASKS_THREADS = Config.section(GLOBAL_SECTION).value('delayedTasksThreads', '2') + # Number of scheduler threads running PER SERVER, with higher number of threads, deplayed task will complete sooner, but it will give more load to overall system + SCHEDULER_THREADS = Config.section(GLOBAL_SECTION).value('schedulerThreads', '2') + # Waiting time before removing "errored" and "removed" publications, cache, and user assigned machines. Time is in seconds + CLEANUP_CHECK = Config.section(GLOBAL_SECTION).value('cleanupCheck', '3600') + # Time to maintaing "info state" items before removing it, in seconds + KEEP_INFO_TIME = Config.section(GLOBAL_SECTION).value('keepInfoTime', '14400') # Defaults to 2 days 172800?? better 4 hours xd + # Max number of services to be "preparing" at same time + MAX_PREPARING_SERVICES = Config.section(GLOBAL_SECTION).value('maxPreparingServices', '15') # Defaults to 15 services at once (per service provider) + # Max number of service to be at "removal" state at same time + MAX_REMOVING_SERVICES = Config.section(GLOBAL_SECTION).value('maxRemovinggServices', '15') # Defaults to 15 services at once (per service provider) + # If we ignore limits (max....) + IGNORE_LIMITS = Config.section(GLOBAL_SECTION).value('ignoreLimits', '0') + # Number of services to initiate removal per run of CacheCleaner + USER_SERVICE_CLEAN_NUMBER = Config.section(GLOBAL_SECTION).value('userServiceCleanNumber', '3') # Defaults to 3 per wun + # Removal Check time for cache, publications and deployed services + REMOVAL_CHECK = Config.section(GLOBAL_SECTION).value('removalCheck', '30') # Defaults to 30 seconds + # Login URL + LOGIN_URL = Config.section(GLOBAL_SECTION).value('loginUrl', '/login') # Defaults to /login + # Session duration + USER_SESSION_LENGTH = Config.section(GLOBAL_SECTION).value('userSessionLength', '14400') # Defaults to 4 hours + # Superuser (do not need to be at database!!!) + SUPER_USER_LOGIN = Config.section(GLOBAL_SECTION).value('superUser', 'root') # Defaults to 4 hours + # Superuser password (do not need to be at database!!!) + SUPER_USER_PASS = Config.section(GLOBAL_SECTION).valueCrypt('rootPass', 'udsmam0') + # Idle time before closing session on admin + ADMIN_IDLE_TIME = Config.section(GLOBAL_SECTION).value('adminIdleTime', '14400') # Defaults to 4 hous + # Time betwen checks of unused services by os managers + # Unused services will be invoked for every machine assigned but not in use AND that has been assigned at least 1/2 of this time + CHECK_UNUSED_TIME = Config.section(GLOBAL_SECTION).value('checkUnusedTime', '600') # Defaults to 10 minutes + # Default CSS Used + CSS = Config.section(GLOBAL_SECTION).value('css', settings.STATIC_URL + 'css/uds.css') + # Max logins before blocking an account + MAX_LOGIN_TRIES = Config.section(GLOBAL_SECTION).value('maxLoginTries', '3') + # Block time in second for an user that makes too many mistakes, 5 minutes default + LOGIN_BLOCK = Config.section(GLOBAL_SECTION).value('loginBlockTime', '300') + # Do autorun of service if just one service. + # 0 = No autorun, 1 = Autorun at login + # In a future, maybe necessary another value "2" that means that autorun always + AUTORUN_SERVICE = Config.section(GLOBAL_SECTION).value('autorunService', '0') + # Redirect HTTP to HTTPS + REDIRECT_TO_HTTPS = Config.section(GLOBAL_SECTION).value('redirectToHttps', '0') + # Max time needed to get a service "fully functional" before it's considered "failed" and removed + # The time is in seconds + MAX_INITIALIZING_TIME = Config.section(GLOBAL_SECTION).value('maxInitTime', '3600') + + + initDone = False + + @staticmethod + def initialize(): + try: + # Tries to initialize database data for global config so it is stored asap and get cached for use + GlobalConfig.SESSION_EXPIRE_TIME.get() + GlobalConfig.CACHE_CHECK_DELAY.get() + GlobalConfig.DELAYED_TASKS_THREADS.get() + GlobalConfig.SCHEDULER_THREADS.get() + GlobalConfig.CLEANUP_CHECK.get() + GlobalConfig.KEEP_INFO_TIME.get() + GlobalConfig.MAX_PREPARING_SERVICES.get() + GlobalConfig.MAX_REMOVING_SERVICES.get() + GlobalConfig.USER_SERVICE_CLEAN_NUMBER.get() + GlobalConfig.REMOVAL_CHECK.get() + GlobalConfig.LOGIN_URL.get() + GlobalConfig.USER_SESSION_LENGTH.get() + GlobalConfig.SUPER_USER_LOGIN.get() + GlobalConfig.SUPER_USER_PASS.get() + GlobalConfig.ADMIN_IDLE_TIME.get() + GlobalConfig.CHECK_UNUSED_TIME.get() + GlobalConfig.CSS.get() + GlobalConfig.MAX_LOGIN_TRIES.get() + GlobalConfig.LOGIN_BLOCK.get() + GlobalConfig.AUTORUN_SERVICE.get() + GlobalConfig.REDIRECT_TO_HTTPS.get() + GlobalConfig.MAX_INITIALIZING_TIME.get() + except Exception, e: + logger.debug('Config table do not exists!!!, maybe we are installing? :-)') + +# Context processor +def context_processor(request): + return { 'css_path' : GlobalConfig.CSS.get() } + +# Initialization of global configurations +GlobalConfig.initialize() \ No newline at end of file diff --git a/server/src/uds/core/util/Decorators.py b/server/src/uds/core/util/Decorators.py new file mode 100644 index 000000000..dc1dc6c7d --- /dev/null +++ b/server/src/uds/core/util/Decorators.py @@ -0,0 +1,28 @@ +''' +Created on Feb 6, 2012 + +@author: dkmaster +''' + +from time import sleep +from functools import wraps + +# Have to test these decorators before using them +def retryOnException(retries=3, delay = 0): + ''' + Decorator to retry + ''' + def decorator(func): + @wraps(func) + def _wrapped_func(*args, **kwargs): + while retries > 0: + retries -= 1 + try: + return func(*args, **kwargs) + except Exception: + if retries == 0: + raise + if delay > 0: + sleep(delay) + return _wrapped_func + return decorator diff --git a/server/src/uds/core/util/Log.py b/server/src/uds/core/util/Log.py new file mode 100644 index 000000000..8454fe3a2 --- /dev/null +++ b/server/src/uds/core/util/Log.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from uds.models import UserService, State +import logging + +logger = logging.getLogger(__name__) +useLogger = logging.getLogger('useLog') + + +def useLog(type_, serviceUniqueId, serviceIp, username): + useLogger.info('|'.join([type_, serviceUniqueId, serviceIp, username])) + \ No newline at end of file diff --git a/server/src/uds/core/util/OsDetector.py b/server/src/uds/core/util/OsDetector.py new file mode 100644 index 000000000..3105a8862 --- /dev/null +++ b/server/src/uds/core/util/OsDetector.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +import re +import logging + +logger = logging.getLogger(__name__) + +Linux = 'Linux' +Windows = 'Windows' +Macintosh = 'Macintosh' + + +knownOss = { 'Linux' : Linux, 'Windows' : Windows, 'Macintosh' : Macintosh } + +def getOsFromUA(ua): + ''' + Basic OS Client detector (very basic indeed :-)) + ''' + res = {'OS' : 'Unknown', 'Version' : 'unused' } + for k, v in knownOss.iteritems(): + try: + ua.index(v) + res['OS'] = k + break + except Exception: + pass + logger.debug(res) + return res + \ No newline at end of file diff --git a/server/src/uds/core/util/State.py b/server/src/uds/core/util/State.py new file mode 100644 index 000000000..a52018794 --- /dev/null +++ b/server/src/uds/core/util/State.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + + +from django.utils.translation import ugettext_noop as _, ugettext + +# States for different objects. Not all objects supports all States +class State(object): + ''' + This class represents possible states for objects at database. + Take in consideration that objects do not have to support all states, they are here for commodity + ''' + ACTIVE = 'A' + INACTIVE = 'I' + BLOCKED = 'B' + LAUNCHING = 'L' + PREPARING = 'P' + USABLE = 'U' + REMOVABLE = 'R' + REMOVING = 'M' + REMOVED = 'S' + CANCELED = 'C' + CANCELING = 'K' + ERROR = 'E' + RUNNING = 'W' + FINISHED = 'F' + FOR_EXECUTE = 'X' + + string = { ACTIVE: _('Active'), INACTIVE: _('Inactive'), BLOCKED: _('Blocked'), LAUNCHING: _('Waiting publication'), + PREPARING: _('In preparation'), USABLE: _('Valid'), + REMOVABLE: _('Waiting for removal'), REMOVING: _('Removing'), REMOVED: _('Removed'), CANCELED: _('Canceled'), + CANCELING: _('Canceling'), ERROR: _('Error'), RUNNING: _('Running'), FINISHED: _('Finished'), FOR_EXECUTE: _('Waiting execution') } + + # States that are merely for "information" to the user. They don't contain any usable instance + INFO_STATES = [REMOVED, CANCELED, ERROR] + + # States that indicates that the service is "Valid" for a user + VALID_STATES = [USABLE,PREPARING] + + # Publication States + PUBLISH_STATES = [LAUNCHING, PREPARING] + + @staticmethod + def isActive(state): + return state == State.ACTIVE + + @staticmethod + def isInactive(state): + return state == State.INACTIVE + + @staticmethod + def isBlocked(state): + return state == State.BLOCKED + + @staticmethod + def isPreparing(state): + return state == State.PREPARING + + @staticmethod + def isUsable(state): + return state == State.USABLE + + @staticmethod + def isRemovable(state): + return state == State.REMOVABLE + + @staticmethod + def isRemoving(state): + return state == State.REMOVING + + @staticmethod + def isRemoved(state): + return state == State.REMOVED + + @staticmethod + def isCanceling(state): + return state == State.CANCELING + + @staticmethod + def isCanceled(state): + return state == State.CANCELED + + @staticmethod + def isErrored(state): + return state == State.ERROR + + @staticmethod + def isFinished(state): + return state == State.FINISHED + + @staticmethod + def isRuning(state): + return state == State.RUNNING + + @staticmethod + def isForExecute(state): + return state == State.FOR_EXECUTE + + @staticmethod + def toString(state): + try: + return State.string[state] + except Exception: + return '' + diff --git a/server/src/uds/core/util/StateQueue.py b/server/src/uds/core/util/StateQueue.py new file mode 100644 index 000000000..72a7ad76f --- /dev/null +++ b/server/src/uds/core/util/StateQueue.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +class StateQueue(object): + + def __init__(self): + self.reset() + + def __str__(self): + res = '' % (self._current , ','.join( state for state in self._queue )) + return res + + def clearQueue(self): + self._queue = [] + + def reset(self): + self._queue = [] + self._current = None + + def getCurrent(self): + return self._current + + def setCurrent(self, newState): + self._current = newState + return self._current + + def contains(self, state): + #if self._queue.co + for s in self._queue: + if s == state: + return True + return False + + def push_back(self, state): + self._queue.append(state) + + def pop_front(self): + if len(self._queue) > 0: + return self._queue.pop(0) + return None + + def remove(self, state): + try: + self._queue.remove(state) + except Exception: + pass # If state not in queue, nothing happens diff --git a/server/src/uds/core/util/Storage.py b/server/src/uds/core/util/Storage.py new file mode 100644 index 000000000..d6ba9298e --- /dev/null +++ b/server/src/uds/core/util/Storage.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.models import Storage as dbStorage +import hashlib +import logging + +logger = logging.getLogger(__name__) + +class Storage(object): + CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded + + def __init__(self, owner): + self._owner = owner + + def __getKey(self, key): + h = hashlib.md5() + h.update(self._owner) + h.update(str(key)) + return h.hexdigest() + + + def saveData(self, skey, data, attr1 = None): + logger.debug('Saving key {0}, data {1} and attr1 {2}'.format(skey, data, attr1)) + key = self.__getKey(skey) + data = data.encode(Storage.CODEC) + attr1 = '' if attr1 == None else attr1 + try: + dbStorage.objects.create(owner = self._owner, key = key, data = data, attr1 = attr1 ) + except Exception: + dbStorage.objects.filter(key=key).update(owner = self._owner, data = data, attr1 = attr1) + logger.debug('Key saved') + + def updateData(self, skey, data, attr1 = None): + self.saveData(skey, data, attr1) + + def readData(self, skey): + try: + key = self.__getKey(skey) + logger.debug('Accesing to {0} {1}'.format(skey, key)) + c = dbStorage.objects.get(pk=key) + return c.data.decode(Storage.CODEC) + except dbStorage.DoesNotExist: + logger.debug('key not found') + return None + + def remove(self, skey): + try: + key = self.__getKey(skey) + dbStorage.objects.filter(key=key).delete() + except Exception: + pass + + def lock(self): + ''' + Use with care. If locked, it must be unlocked before returning + ''' + dbStorage.objects.lock() + + def unlock(self): + ''' + Must be used to unlock table + ''' + dbStorage.objects.unlock() + + @staticmethod + def delete(owner = None): + logger.info("Deleting storage items") + if owner == None: + objects = dbStorage.objects.all() + else: + objects = dbStorage.objects.filter(owner=owner) + objects.delete() + + def locateByAttr1(self, attr1): + res = [] + for v in dbStorage.objects.filter( attr1 = attr1 ): + res.append( v.data.decode(Storage.CODEC) ) + return res diff --git a/server/src/uds/core/util/UniqueIDGenerator.py b/server/src/uds/core/util/UniqueIDGenerator.py new file mode 100644 index 000000000..139669d5e --- /dev/null +++ b/server/src/uds/core/util/UniqueIDGenerator.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.models import UniqueId as dbUniqueId +import logging + +logger = logging.getLogger(__name__) + +class UniqueIDGenerator(object): + + def __init__(self, typeName, owner, baseName = 'uds'): + self._owner = owner + typeName + self._baseName = baseName + + def setBaseName(self, newBaseName): + self._baseName = newBaseName + + def __filter(self, rangeStart): + return dbUniqueId.objects.filter( basename = self._baseName, seq__gte=rangeStart ) + + def get(self, rangeStart=0, rangeEnd=1000000000): + ''' + Tries to generate a new unique id in the range provided. This unique id + is global to "unique ids' database + ''' + # First look for a name in the range defined + try: + dbUniqueId.objects.lock() + flt = self.__filter(rangeStart) + try: + item = flt.filter(assigned=False)[0] + dbUniqueId.objects.filter(id=item.id).update( owner = self._owner, assigned = True ) + seq = item.seq + except Exception, e: # No free element found + try: + last = flt.filter(assigned = True)[0] # DB Returns correct order so the 0 item is the last + seq = last.seq + 1 + except Exception: # If there is no assigned at database + seq = rangeStart + logger.debug('Found seq {0}'.format(seq)) + if seq > rangeEnd: + return None # No ids free in range + dbUniqueId.objects.create( owner = self._owner, basename = self._baseName, seq = seq, assigned = True) + return seq + except Exception: + logger.exception('Generating unique id sequence') + return None + finally: + dbUniqueId.objects.unlock() + + def free(self, seq): + try: + logger.debug('Freeing seq {0} from {1} ({2})'.format(seq, self._owner, self._baseName)) + dbUniqueId.objects.lock() + flt = self.__filter(0).filter(owner = self._owner, seq=seq).update(owner='', assigned=False) + if flt > 0: + self.__purge() + finally: + dbUniqueId.objects.unlock() + + + def __purge(self): + try: + last = self.__filter(0).filter(assigned=True)[0] + seq = last.seq+1 + except: + seq = 0 + self.__filter(seq).delete() # Clean ups all unassigned after last assigned in this range + + + def release(self): + try: + dbUniqueId.objects.lock() + dbUniqueId.objects.filter(owner=self._owner).update(assigned=False, owner='') + self.__purge() + finally: + dbUniqueId.objects.unlock() + \ No newline at end of file diff --git a/server/src/uds/core/util/UniqueMacGenerator.py b/server/src/uds/core/util/UniqueMacGenerator.py new file mode 100644 index 000000000..4f97bfb7b --- /dev/null +++ b/server/src/uds/core/util/UniqueMacGenerator.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from UniqueIDGenerator import UniqueIDGenerator +import logging, re + +logger = logging.getLogger(__name__) + +class UniqueMacGenerator(UniqueIDGenerator): + + def __init__(self, owner): + super(UniqueMacGenerator, self).__init__('mac', owner, '\tmac') + + def __toInt(self, mac): + return int(mac.replace(':', ''), 16) + + def __toMac(self, seq): + return re.sub(r"(..)", r"\1:", "%0*X" % (12, seq))[:-1] + + def get(self, macRange): + firstMac, lastMac = macRange.split('-') + firstMac = self.__toInt(firstMac) + lastMac = self.__toInt(lastMac) + return self.__toMac(super(UniqueMacGenerator, self).get(firstMac, lastMac)) + + def free(self, mac): + super(UniqueMacGenerator, self).free( self.__toInt(mac) ) + + # Release is inherited, no mod needed \ No newline at end of file diff --git a/server/src/uds/core/util/UniqueNameGenerator.py b/server/src/uds/core/util/UniqueNameGenerator.py new file mode 100644 index 000000000..ec9ab20c2 --- /dev/null +++ b/server/src/uds/core/util/UniqueNameGenerator.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from UniqueIDGenerator import UniqueIDGenerator +import logging + +logger = logging.getLogger(__name__) + +class UniqueNameGenerator(UniqueIDGenerator): + + def __init__(self, owner): + super(UniqueNameGenerator, self).__init__('name', owner, ) + + def __toName(self, seq, length): + return "%s%0*d" % (self._baseName, length, seq) + + def get(self, baseName, length=5): + self.setBaseName(baseName) + minVal = 0 + maxVal = 10**length - 1 + return self.__toName(super(UniqueNameGenerator, self).get(minVal, maxVal), length) + + + def free(self, baseName, name): + self.setBaseName(baseName) + super(UniqueNameGenerator, self).free(int(name[len(self._baseName):])) \ No newline at end of file diff --git a/server/src/uds/core/util/__init__.py b/server/src/uds/core/util/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/core/util/connection.py b/server/src/uds/core/util/connection.py new file mode 100644 index 000000000..436049c4e --- /dev/null +++ b/server/src/uds/core/util/connection.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +import logging +import socket + +logger = logging.getLogger(__name__) + +def testServer(host, port, timeOut = 4): + try: + logger.debug('Checking connection to {0}:{1} with {2} seconds timeout'.format(host, port, timeOut)) + sock = socket.create_connection((host, port), timeOut) + sock.close() + except Exception, e: + logger.debug('Exception checking {0}:{1} with {2} timeout: {3}'.format(host, port, timeOut, e)) + return False + return True + + \ No newline at end of file diff --git a/server/src/uds/core/util/db/LockingManager.py b/server/src/uds/core/util/db/LockingManager.py new file mode 100644 index 000000000..ea3bb3448 --- /dev/null +++ b/server/src/uds/core/util/db/LockingManager.py @@ -0,0 +1,73 @@ +''' +From django locking manager snippet at http://djangosnippets.org/snippets/833/ +Author: + miohtama +''' + +from django.db import models, connection +import logging + +logger = logging.getLogger(__name__) + +# Table locking in mysql at least is based on thread requesting it, so we should not have problems with a bit of care +class LockingManager(models.Manager): + """ Add lock/unlock functionality to manager. + + Example:: + + class Job(models.Model): + + manager = LockingManager() + + counter = models.IntegerField(null=True, default=0) + + @staticmethod + def do_atomic_update(job_id) + ''' Updates job integer, keeping it below 5 ''' + try: + # Ensure only one HTTP request can do this update at once. + Job.objects.lock() + + job = Job.object.get(id=job_id) + # If we don't lock the tables two simultanous + # requests might both increase the counter + # going over 5 + if job.counter < 5: + job.counter += 1 + job.save() + + finally: + Job.objects.unlock() + + + """ + + def lock(self): + """ Lock table. + + Locks the object model table so that atomic update is possible. + Simulatenous database access request pend until the lock is unlock()'ed. + + Note: If you need to lock multiple tables, you need to do lock them + all in one SQL clause and this function is not enough. To avoid + dead lock, all tables must be locked in the same order. + + See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html + """ + con = connection + cursor = con.cursor() + table = self.model._meta.db_table + #logger.debug("Locking table %s" % table) + cursor.execute("LOCK TABLES %s WRITE" % table) + row = cursor.fetchone() + return row + + def unlock(self): + """ Unlock the table. """ + #logger.debug("Unlocked tables") + con = connection + cursor = con.cursor() + #table = self.model._meta.db_table + cursor.execute("UNLOCK TABLES") + row = cursor.fetchone() + return row \ No newline at end of file diff --git a/server/src/uds/core/util/db/__init__.py b/server/src/uds/core/util/db/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/core/util/modfinder.py b/server/src/uds/core/util/modfinder.py new file mode 100644 index 000000000..c6b02b1d6 --- /dev/null +++ b/server/src/uds/core/util/modfinder.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +import os.path, pkgutil +import sys, imp +import logging +import uds.dispatchers + +logger = logging.getLogger(__name__) + + +def loadModulesUrls(): + patterns = [] + try: + modName = 'uds.dispatchers' + pkgpath = os.path.dirname(sys.modules[modName].__file__) + for _, name, _ in pkgutil.iter_modules([pkgpath]): + fullModName = '%s.%s.urls' % (modName, name) + mod = __import__(fullModName, globals(), locals(), ['urlpatterns'], -1) + patterns += mod.urlpatterns + except Exception, e: + logger.debug(e) + pass + return patterns \ No newline at end of file diff --git a/server/src/uds/core/workers/AssignedAndUnused.py b/server/src/uds/core/workers/AssignedAndUnused.py new file mode 100644 index 000000000..5be83d6c9 --- /dev/null +++ b/server/src/uds/core/workers/AssignedAndUnused.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.util.Config import GlobalConfig +from uds.models import DeployedService, getSqlDatetime +from uds.core.util.State import State +from uds.core.jobs.Job import Job +from datetime import timedelta +import logging + +logger = logging.getLogger(__name__) + +class AssignedAndUnused(Job): + frecuency = GlobalConfig.CHECK_UNUSED_TIME.getInt() + + def __init__(self, environment): + super(AssignedAndUnused,self).__init__(environment) + + def run(self): + for ds in DeployedService.objects.all(): + osm = ds.osmanager.getInstance() + if osm.processUnusedMachines is True: + logger.debug('Processing unused machines for {0}'.format(osm)) + since_state = getSqlDatetime() - timedelta( seconds = GlobalConfig.CHECK_UNUSED_TIME.getInt() / 2 ) + for us in ds.assignedUserServices().select_for_update().filter(in_use=False,since_state__lt=since_state): + logger.debug('Found unused assigned service {0}'.format(us)) + osm.processUnused(us) \ No newline at end of file diff --git a/server/src/uds/core/workers/CacheCleaner.py b/server/src/uds/core/workers/CacheCleaner.py new file mode 100644 index 000000000..fc6fb90f4 --- /dev/null +++ b/server/src/uds/core/workers/CacheCleaner.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.util.Cache import Cache +from uds.core.jobs.Job import Job +import logging + +logger = logging.getLogger(__name__) + + +class CacheCleaner(Job): + + frecuency = 3600*24 # Once a day + + def __init__(self, environment): + super(CacheCleaner,self).__init__(environment) + + def run(self): + logger.debug('Starting cache cleanup') + Cache.cleanUp() + logger.debug('Done cache cleanup') + \ No newline at end of file diff --git a/server/src/uds/core/workers/DeployedServiceCleaner.py b/server/src/uds/core/workers/DeployedServiceCleaner.py new file mode 100644 index 000000000..2e26ac483 --- /dev/null +++ b/server/src/uds/core/workers/DeployedServiceCleaner.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.db import transaction +from uds.core.util.Config import GlobalConfig +from uds.models import DeployedService, getSqlDatetime +from uds.core.util.State import State +from uds.core.jobs.Job import Job +from datetime import timedelta +import logging + +logger = logging.getLogger(__name__) + +class DeployedServiceInfoItemsCleaner(Job): + frecuency = GlobalConfig.CLEANUP_CHECK.getInt() # Request run cache "info" cleaner every configured seconds. If config value is changed, it will be used at next reload + + def __init__(self, environment): + super(DeployedServiceInfoItemsCleaner,self).__init__(environment) + + def run(self): + removeFrom = getSqlDatetime() - timedelta(seconds = GlobalConfig.KEEP_INFO_TIME.getInt()) + DeployedService.objects.filter(state__in=State.INFO_STATES, state_date__lt=removeFrom).delete() + + +class DeployedServiceRemover(Job): + frecuency = GlobalConfig.REMOVAL_CHECK.getInt() # Request run publication "removal" every configued seconds. If config value is changed, it will be used at next reload + + def __init__(self, environment): + super(DeployedServiceRemover,self).__init__(environment) + + @transaction.commit_on_success + def startRemovalOf(self, ds): + # Get publications in course...., can be at most 1!!! + + publishing = ds.publications.filter(state=State.PREPARING) + for p in publishing: + p.cancel() + # Now all publishments are canceling, let's try to cancel cache and assigned + uServices = ds.userServices.filter(state=State.PREPARING) + for u in uServices: + u.cancel() + # Nice start of removal, maybe we need to do some limitation later, but there should not be too much services nor publications cancelable at once + ds.state = State.REMOVING + ds.name = ds.name + ' (removed)' + ds.save() + + + @transaction.commit_on_success + def continueRemovalOf(self, ds): + # First, we remove all publications and user services in "info_state" + ds.userServices.select_for_update().filter(state__in=State.INFO_STATES).delete() + # Mark usable user services as removable + ds.userServices.select_for_update().filter(state=State.USABLE).update(state=State.REMOVABLE) + + # When no service is at database, we start with publications + if ds.userServices.all().count() == 0: + try: + logger.debug('All services removed, checking active publication') + if ds.activePublication() is not None: + logger.debug('Active publication found, unpublishing it') + ds.unpublish() + else: + logger.debug('No active publication found, removing info states and checking if removal is done') + ds.publications.filter(state__in=State.INFO_STATES).delete() + if ds.publications.count() is 0: + ds.removed() # Mark it as removed, clean later from database + except Exception as e: + logger.exception('Cought unexpected exception at continueRemovalOf: ') + + def run(self): + # First check if there is someone in "removable" estate + rems = DeployedService.objects.filter(state=State.REMOVABLE)[:10] + if len(rems) > 0: + logger.debug('Found a deployed service marked for removal. Starting removal of {0}'.format(rems)) + for rem in rems: + self.startRemovalOf(rem) + rems = DeployedService.objects.filter(state=State.REMOVING)[:10] + if len(rems) > 0: + logger.debug('Found a deployed service in removing state, continuing removal of {0}'.format(rems)) + for rem in rems: + self.continueRemovalOf(rem) + + diff --git a/server/src/uds/core/workers/PublicationCleaner.py b/server/src/uds/core/workers/PublicationCleaner.py new file mode 100644 index 000000000..a3e123b97 --- /dev/null +++ b/server/src/uds/core/workers/PublicationCleaner.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.managers.PublicationManager import PublicationManager +from uds.core.util.Config import GlobalConfig +from uds.models import DeployedServicePublication, DeployedService, getSqlDatetime +from uds.core.services.Exceptions import PublishException +from uds.core.util.State import State +from uds.core.jobs.Job import Job +from datetime import timedelta +import logging + +logger = logging.getLogger(__name__) + + +class PublicationInfoItemsCleaner(Job): + frecuency = GlobalConfig.CLEANUP_CHECK.getInt() # Request run cache "info" cleaner every configured seconds. If config value is changed, it will be used at next reload + + def __init__(self, environment): + super(PublicationInfoItemsCleaner,self).__init__(environment) + + def run(self): + removeFrom = getSqlDatetime() - timedelta(seconds = GlobalConfig.KEEP_INFO_TIME.getInt()) + DeployedServicePublication.objects.filter(state__in=State.INFO_STATES, state_date__lt=removeFrom).delete() + +class PublicationCleaner(Job): + frecuency = GlobalConfig.REMOVAL_CHECK.getInt() # Request run publication "removal" every configued seconds. If config value is changed, it will be used at next reload + + def __init__(self, environment): + super(PublicationCleaner,self).__init__(environment) + + def run(self): + removables = DeployedServicePublication.objects.filter(state=State.REMOVABLE)[0:3] + for removable in removables: + try: + PublicationManager.manager().unpublish(removable) + except PublishException: # Can say that it cant be removed right now + logger.debug('Delaying removal') + pass + + + \ No newline at end of file diff --git a/server/src/uds/core/workers/ServiceCacheUpdater.py b/server/src/uds/core/workers/ServiceCacheUpdater.py new file mode 100644 index 000000000..b35394c87 --- /dev/null +++ b/server/src/uds/core/workers/ServiceCacheUpdater.py @@ -0,0 +1,245 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.db import transaction +from django.db.models import Q +from uds.core.util.Config import GlobalConfig +from uds.core.util.State import State +from uds.core.managers.UserServiceManager import UserServiceManager +from uds.core.services.Exceptions import MaxServicesReachedException +from uds.models import DeployedService +from uds.core import services +from uds.core.jobs.Job import Job +import logging + +logger = logging.getLogger(__name__) + + +class ServiceCacheUpdater(Job): + ''' + Cache updater is responsible of keeping up to date the cache for different deployed services configurations requested + We only process items that are "cacheables", to speed up process we will use the fact that initialServices = preparedServices = maxServices = 0 + if cache is not needed. + This is included as a scheduled task that will run every X seconds, and scheduler will keep it so it will be only executed by one backend at a time + ''' + frecuency = GlobalConfig.CACHE_CHECK_DELAY.getInt() # Request run cache manager every configured seconds. If config value is changed, it will be used at next reload + + def __init__(self, environment): + super(ServiceCacheUpdater,self).__init__(environment) + + @staticmethod + def calcProportion(max, actual): + return actual * 10000 / max + + def bestDeployedServiceNeedingCacheUpdate(self): + # State filter for cached and inAssigned objects + # First we get all deployed services that could need cache generation + DeployedService.objects.update() + # We start filtering out the deployed services that do not need caching at all. + whichNeedsCaching = DeployedService.objects.filter(Q(initial_srvs__gt=0) | Q(cache_l1_srvs__gt=0)).filter(max_srvs__gt=0,state=State.ACTIVE) + + # We will get the one that proportionally needs more cache + selected = None + cachedL1, cachedL2, assigned = 0,0,0 + toCacheL1 = False # Mark for prefering update L1 cache before L2 cache + prop = ServiceCacheUpdater.calcProportion(1,1) + for ds in whichNeedsCaching: + ds.userServices.update() # Cleans cached queries + # If this deployedService don't have a publication active and needs it, ignore it + if ds.activePublication() == None and ds.service.getInstance().publicationType is not None: + logger.debug('Needs publication but do not have one, cache test ignored') + continue + # If it has any running publication, do not generate cache anymore + if ds.publications.filter(state=State.PREPARING).count() > 0: + logger.debug('Stopped cache generation for deployed service with publication running: {0}'.format(ds)) + continue + + # Get data related to actual state of cache + inCacheL1 = ds.cachedUserServices().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L1_CACHE)).count() + inCacheL2 = ds.cachedUserServices().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L2_CACHE)).count() + inAssigned = ds.assignedUserServices().filter(UserServiceManager.getStateFilter()).count() + # if we bypasses max cache, we will reduce it in first place. This is so because this will free resources on service provider + logger.debug("Examining {0} with {1} in cache L1 and {2} in cache L2, {3} inAssigned".format( + ds, inCacheL1, inCacheL2, inAssigned)) + totalL1Assigned = inCacheL1 + inAssigned + # We have more than we want + if totalL1Assigned > ds.max_srvs: + logger.debug('We have more services than max configured') + cachedL1, cachedL2, assigned = inCacheL1, inCacheL2, inAssigned + selected = ds + break + # We have more in L1 cache than needed + if totalL1Assigned > ds.initial_srvs and inCacheL1 > ds.cache_l1_srvs: + logger.debug('We have more services in cache L1 than configured') + cachedL1, cachedL2, assigned = inCacheL1, inCacheL2, inAssigned + selected = ds + break + + # If we have more in L2 cache than needed, decrease L2 cache, but int this case, we continue checking cause L2 cache removal + # has less priority than l1 creations or removals, but higher. In this case, we will simply take last l2 oversized found and reduce it + if inCacheL2 > ds.cache_l2_srvs: + if toCacheL1 == False: + logger.debug('We have more services in L2 cache than configured, decreasing it') + cachedL1, cachedL2, assigned = inCacheL1, inCacheL2, inAssigned + selected = ds + prop = ServiceCacheUpdater.calcProportion(1,0) + + # If this service don't allows more starting user services, continue + if UserServiceManager.manager().canInitiateServiceFromDeployedService(ds) is False: + logger.debug('This provider has the max allowed starting services running: {0}'.format(ds)) + continue + + # If wee need to grow l2 cache, annotate it + # Whe check this before checking the total, because the l2 cache is independent of max services or l1 cache. + # It reflects a value that must be keeped in cache for futre fast use. + if inCacheL2 < ds.cache_l2_srvs: + p = ServiceCacheUpdater.calcProportion(ds.cache_l2_srvs, inCacheL2) + if p < prop and toCacheL1 == False: + logger.debug("Found best for cache until now comparing cache L2: {0}, {1} < {2}".format(ds, p, prop)) + cachedL1, cachedL2, assigned = inCacheL1, inCacheL2, inAssigned + selected = ds + prop = p + + # We skip it if already at max + if totalL1Assigned == ds.max_srvs: + continue; + + if totalL1Assigned < ds.initial_srvs: + p = ServiceCacheUpdater.calcProportion(ds.initial_srvs, totalL1Assigned) + if p < prop or toCacheL1 == False: + logger.debug("Found best for cache until now comparing initial: {0}, {1} < {2}".format(ds, p, prop)) + toCacheL1 = True + cachedL1, cachedL2, assigned = inCacheL1, inCacheL2, inAssigned + selected = ds + prop = p + if inCacheL1 < ds.cache_l1_srvs: + p = ServiceCacheUpdater.calcProportion(ds.cache_l1_srvs, inCacheL1) + if p < prop or toCacheL1 == False: + logger.debug("Found best for cache until now comparing prepared: {0}, {1} < {2}".format(ds, p, prop)) + toCacheL1 = True + selected = ds + cachedL1, cachedL2, assigned = inCacheL1, inCacheL2, inAssigned + prop = p + + # We also return calculated values so we can reuse then + return selected, cachedL1, cachedL2, assigned + + @transaction.autocommit + def growL1Cache(self, ds, cacheL1, cacheL2, assigned): + ''' + This method tries to enlarge L1 cache. + + If for some reason the number of deployed services (Counting all, ACTIVE + and PREPARING, assigned, L1 and L2) is over max allowed service deployments, + this method will not grow the L1 cache + ''' + logger.debug("Growing L1 cache creating a new service for {0}".format(ds)) + # First, we try to assign from L2 cache + if cacheL2 > 0: + cache = ds.cachedUserServices().select_for_update().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L2_CACHE)).order_by('creation_date')[0] + cache.moveToLevel(services.UserDeployment.L1_CACHE) + else: + try: + UserServiceManager.manager().createCacheFor(ds.activePublication(), services.UserDeployment.L1_CACHE) + except MaxServicesReachedException as e: + logger.error(str(e)) + # TODO: When alerts are ready, notify this + + @transaction.autocommit + def growL2Cache(self, ds, cacheL1, cacheL2, assigned): + ''' + Tries to grow L2 cache of service. + + If for some reason the number of deployed services (Counting all, ACTIVE + and PREPARING, assigned, L1 and L2) is over max allowed service deployments, + this method will not grow the L1 cache + ''' + logger.debug("Growing L2 cache creating a new service for {0}".format(ds)) + try: + UserServiceManager.manager().createCacheFor(ds.activePublication(), services.UserDeployment.L2_CACHE) + except MaxServicesReachedException as e: + logger.error(str(e)) + # TODO: When alerts are ready, notify this + + def reduceL1Cache(self, ds, cacheL1, cacheL2, assigned): + logger.debug("Reducing L1 cache erasing a service in cache for {0}".format(ds)) + # We will try to destroy the newest cacheL1 element that is USABLE if the deployer can't cancel a new service creation + cacheItems = ds.cachedUserServices().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L1_CACHE)).order_by('-creation_date') + if len(cacheItems) == 0: + logger.debug('There is more services than configured, but could not reduce cache cause its already empty') + return + + if cacheL2 < ds.cache_l2_srvs: + cacheItems[0].moveToLevel(services.UserDeployment.L2_CACHE) + else: + # TODO: Look first for non finished cache items and cancel them + cache = cacheItems[0] + cache.removeOrCancel() + + def reduceL2Cache(self, ds, cacheL1, cacheL2, assigned): + logger.debug("Reducing L2 cache erasing a service in cache for {0}".format(ds)) + if cacheL2 > 0: + cacheItems = ds.cachedUserServices().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L2_CACHE)).order_by('creation_date') + # TODO: Look first for non finished cache items and cancel them + cache = cacheItems[0] + cache.removeOrCancel() + + def run(self): + logger.debug('Starting cache checking') + # We need to get + ds, cacheL1, cacheL2, assigned = self.bestDeployedServiceNeedingCacheUpdate() + # We have cache to update?? + if ds == None: + logger.debug('Cache up to date') + return + logger.debug("Updating cache for {0}".format(ds)) + totalL1Assigned = cacheL1 + assigned + + # We try first to reduce cache before tring to increase it. + # This means that if there is excesive number of user deployments + # for L1 or L2 cache, this will be reduced untill they have good numbers. + # This is so because service can have limited the number of services and, + # if we try to increase cache before having reduced whatever needed + # first, the service will get lock until someone removes something. + if totalL1Assigned > ds.max_srvs: + self.reduceL1Cache(ds, cacheL1, cacheL2, assigned) + elif totalL1Assigned > ds.initial_srvs and cacheL1 > ds.cache_l1_srvs: + self.reduceL1Cache(ds, cacheL1, cacheL2, assigned) + elif cacheL2 > ds.cache_l2_srvs: # We have excesives L2 items + self.reduceL2Cache(ds, cacheL1, cacheL2, assigned) + elif totalL1Assigned < ds.max_srvs and (totalL1Assigned < ds.initial_srvs or cacheL1 < ds.cache_l1_srvs): # We need more services + self.growL1Cache(ds, cacheL1, cacheL2, assigned) + elif cacheL2 < ds.cache_l2_srvs: # We need more L2 items + self.growL2Cache(ds, cacheL1, cacheL2, assigned) + else: + logger.info("We have more services than max requested for {0}, but can't erase any of then cause all of them are already assigned".format(ds)) diff --git a/server/src/uds/core/workers/UserServiceCleaner.py b/server/src/uds/core/workers/UserServiceCleaner.py new file mode 100644 index 000000000..161ab1dc2 --- /dev/null +++ b/server/src/uds/core/workers/UserServiceCleaner.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.db import transaction +from uds.core.managers.UserServiceManager import UserServiceManager +from uds.core.util.Config import GlobalConfig +from uds.models import UserService, getSqlDatetime, State +from uds.core.jobs.Job import Job +from datetime import timedelta +import logging + +logger = logging.getLogger(__name__) + +# Notas: +# Clean cache info items. DONE +# Initiate removal of "removable" cached items, with a limit (at most X per run). DONE +# Look for non current cache items and mark them as removables. + + + +class UserServiceInfoItemsCleaner(Job): + frecuency = GlobalConfig.CLEANUP_CHECK.getInt() # Request run cache "info" cleaner every configured seconds. If config value is changed, it will be used at next reload + + def __init__(self, environment): + super(UserServiceInfoItemsCleaner,self).__init__(environment) + + @transaction.commit_on_success + def run(self): + removeFrom = getSqlDatetime() - timedelta(seconds = GlobalConfig.KEEP_INFO_TIME.getInt()) + UserService.objects.select_for_update().filter(state__in=State.INFO_STATES, state_date__lt=removeFrom).delete() + + +class UserServiceRemover(Job): + frecuency = GlobalConfig.REMOVAL_CHECK.getInt() # Request run cache "info" cleaner every configued seconds. If config value is changed, it will be used at next reload + removeAtOnce = GlobalConfig.USER_SERVICE_CLEAN_NUMBER.getInt() # Same, it will work at reload + + def __init__(self, environment): + super(UserServiceRemover,self).__init__(environment) + + @transaction.commit_on_success + def run(self): + removables = UserService.objects.filter(state=State.REMOVABLE)[0:UserServiceRemover.removeAtOnce] + for us in removables: + UserServiceManager.manager().remove(us) diff --git a/server/src/uds/core/workers/__init__.py b/server/src/uds/core/workers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/dispatchers/__init__.py b/server/src/uds/dispatchers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/dispatchers/pam/__init__.py b/server/src/uds/dispatchers/pam/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/dispatchers/pam/urls.py b/server/src/uds/dispatchers/pam/urls.py new file mode 100644 index 000000000..3933d1a8a --- /dev/null +++ b/server/src/uds/dispatchers/pam/urls.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.conf.urls.defaults import patterns, include + +urlpatterns = patterns('uds.dispatchers.pam.views', + (r'^pam$', 'pam'), + ) + \ No newline at end of file diff --git a/server/src/uds/dispatchers/pam/views.py b/server/src/uds/dispatchers/pam/views.py new file mode 100644 index 000000000..8992b65e8 --- /dev/null +++ b/server/src/uds/dispatchers/pam/views.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.http import HttpResponseNotAllowed, HttpResponse +from uds.core.util.Cache import Cache +import logging + +logger = logging.getLogger(__name__) + +# We will use the cache to "hold" the tickets valid for users + +# Create your views here. +def pam(request): + response = '' + cache = Cache('pam') + if request.method == 'POST': + return HttpResponseNotAllowed(['GET']) + if request.GET.has_key('id') & request.GET.has_key('pass'): + # This is an "auth" request + logger.debug("Auth request for user [{0}] and pass [{1}]".format(request.GET['id'], request.GET['pass'])) + password = cache.get(request.GET['id']) + response = '0' + if password == request.GET['pass']: + response = '1' + cache.remove(request.GET['id']) # Ticket valid for just 1 login + + elif request.GET.has_key('uid'): + # This is an "get name for id" call + logger.debug("NSS Request for id [{0}]".format(request.GET['uid'])) + response = '10000 udstmp' + elif request.GET.has_key('name'): + logger.debug("NSS Request for username [{0}]".format(request.GET['name'])) + response = '10000 udstmp' + + return HttpResponse(response, content_type='text/plain') diff --git a/server/src/uds/locale/de/LC_MESSAGES/django.mo b/server/src/uds/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..de2fa8276d244218e5b360da5333fd8f8e5c7338 GIT binary patch literal 27889 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 bN9V?xI*{Nm+>0%jt}eY?EH&GtgarQq8*&>o literal 0 HcmV?d00001 diff --git a/server/src/uds/locale/de/LC_MESSAGES/django.po b/server/src/uds/locale/de/LC_MESSAGES/django.po new file mode 100644 index 000000000..95e7c3841 --- /dev/null +++ b/server/src/uds/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,1543 @@ +# Translations for german +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Adolfo Gómez , 2012. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-07-12 21:26+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: auths/ActiveDirectory/Authenticator.py:29 +#: auths/EDirectory/Authenticator.py:60 auths/RegexLdap/Authenticator.py:51 +#: auths/SimpleLDAP/Authenticator.py:49 +#: services/Vmware/ServiceProviderVC.py:28 +msgid "Host" +msgstr "Host" + +#: auths/ActiveDirectory/Authenticator.py:29 +#: auths/EDirectory/Authenticator.py:60 auths/RegexLdap/Authenticator.py:51 +#: auths/SimpleLDAP/Authenticator.py:49 +#: services/Vmware/ServiceProviderVC.py:28 +msgid "VMWare VC Server IP or Hostname" +msgstr "VMWare VC Server IP oder Hostname" + +#: auths/ActiveDirectory/Authenticator.py:30 +#: auths/EDirectory/Authenticator.py:62 auths/RegexLdap/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:51 +msgid "Use SSL" +msgstr "Verwendung SSL" + +#: auths/ActiveDirectory/Authenticator.py:30 +msgid "If checked, will use a ssl connection to Active Directory" +msgstr "" +"Wenn diese Option aktiviert ist, verwendet eine Ssl-Verbindung zum Ldap " +"(Wenn Port 389 ist, wird in Tatsache Port 636)" + +#: auths/ActiveDirectory/Authenticator.py:31 +#: auths/RegexLdap/Authenticator.py:54 auths/SimpleLDAP/Authenticator.py:52 +msgid "Ldap User" +msgstr "LDAP-Benutzer" + +#: auths/ActiveDirectory/Authenticator.py:31 +msgid "" +"Username with read privileges on the base selected (use USER@DOMAIN.DOM form " +"for this)" +msgstr "Benutzernamen mit lesen Berechtigungen auf der Basis ausgewählt" + +#: auths/ActiveDirectory/Authenticator.py:32 +#: auths/ActiveDirectory/Authenticator.py:50 +#: auths/EDirectory/Authenticator.py:64 auths/RegexLdap/Authenticator.py:55 +#: auths/RegexLdap/Authenticator.py:78 auths/SimpleLDAP/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:77 +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:31 +#: services/Vmware/ServiceProviderVC.py:31 transports/NX/NXTransport.py:62 +#: transports/RDP/RDPTransport.py:38 transports/RDP/TSRDPTransport.py:42 +#: transports/RGS/RGSTransport.py:43 transports/RGS/TRGSTransport.py:48 +#: transports/TSNX/TSNXTransport.py:67 web/forms/LoginForm.py:60 +msgid "Password" +msgstr "Passwort" + +#: auths/ActiveDirectory/Authenticator.py:32 +#: auths/EDirectory/Authenticator.py:64 auths/RegexLdap/Authenticator.py:55 +#: auths/SimpleLDAP/Authenticator.py:53 +msgid "Password of the ldap user" +msgstr "Kennwort für den Ldap-Benutzer" + +#: auths/ActiveDirectory/Authenticator.py:33 +#: auths/EDirectory/Authenticator.py:65 auths/RegexLdap/Authenticator.py:56 +#: auths/SimpleLDAP/Authenticator.py:54 +#: services/Vmware/ServiceProviderVC.py:32 +msgid "Timeout" +msgstr "Timeout" + +#: auths/ActiveDirectory/Authenticator.py:33 +#: auths/EDirectory/Authenticator.py:65 auths/RegexLdap/Authenticator.py:56 +#: auths/SimpleLDAP/Authenticator.py:54 +msgid "Timeout in seconds of connection to LDAP" +msgstr "Timeout in Sekunden über LDAP-Verbindung" + +#: auths/ActiveDirectory/Authenticator.py:35 +msgid "Active Directory Authenticator" +msgstr "Active Directory-Authenticator" + +#: auths/ActiveDirectory/Authenticator.py:37 +#, fuzzy +msgid "Authenticate against Active Directory" +msgstr "Authentifikator mit Active Directory" + +#: auths/ActiveDirectory/Authenticator.py:46 +#: auths/EDirectory/Authenticator.py:77 auths/RegexLdap/Authenticator.py:74 +#: auths/SimpleLDAP/Authenticator.py:73 +#: services/Vmware/ServiceProviderVC.py:30 transports/NX/NXTransport.py:61 +#: transports/RDP/RDPTransport.py:37 transports/RDP/TSRDPTransport.py:41 +#: transports/RGS/RGSTransport.py:42 transports/RGS/TRGSTransport.py:47 +#: transports/TSNX/TSNXTransport.py:66 web/forms/LoginForm.py:59 +msgid "Username" +msgstr "Benutzername" + +#: auths/ActiveDirectory/Authenticator.py:48 +#: auths/EDirectory/Authenticator.py:79 auths/RegexLdap/Authenticator.py:76 +#: auths/SimpleLDAP/Authenticator.py:75 +msgid "Group" +msgstr "Gruppe" + +#: auths/ActiveDirectory/Authenticator.py:61 +#: auths/ActiveDirectory/Authenticator.py:395 +msgid "Must specify the username in the form USERNAME@DOMAIN.DOM" +msgstr "Müssen den Benutzernamen in der Form Benutzername@Domäne angeben.DOM" + +#: auths/ActiveDirectory/Authenticator.py:127 +#: auths/EDirectory/Authenticator.py:123 auths/RegexLdap/Authenticator.py:157 +#: auths/SimpleLDAP/Authenticator.py:158 +msgid "Ldap connection error: " +msgstr "LDAP-Verbindungsfehler: " + +#: auths/ActiveDirectory/Authenticator.py:299 +#: auths/ActiveDirectory/Authenticator.py:345 +#: auths/EDirectory/Authenticator.py:243 auths/EDirectory/Authenticator.py:286 +#: auths/RegexLdap/Authenticator.py:244 auths/RegexLdap/Authenticator.py:287 +#: auths/SimpleLDAP/Authenticator.py:268 auths/SimpleLDAP/Authenticator.py:312 +msgid "Username not found" +msgstr "Benutzername wurde nicht gefunden" + +#: auths/ActiveDirectory/Authenticator.py:332 +#: auths/SimpleLDAP/Authenticator.py:301 +msgid "Group not found" +msgstr "Gruppe nicht gefunden" + +#: auths/ActiveDirectory/Authenticator.py:364 +#: auths/ActiveDirectory/Authenticator.py:381 +#: auths/EDirectory/Authenticator.py:303 auths/RegexLdap/Authenticator.py:305 +#: auths/SimpleLDAP/Authenticator.py:327 auths/SimpleLDAP/Authenticator.py:341 +msgid "Too many results, be more specific" +msgstr "Zu viele Ergebnisse, sein mehr spezifisch" + +#: auths/ActiveDirectory/Authenticator.py:404 +msgid "Domain seems to be incorrect, please check it" +msgstr "Domäne scheint falsch sein, bitte überprüfen es" + +#: auths/ActiveDirectory/Authenticator.py:409 +msgid "Ldap does not seem an Active Directory (do not have user objects)" +msgstr "LDAP scheint nicht Active Directory (keine Benutzerobjekte)" + +#: auths/ActiveDirectory/Authenticator.py:417 +msgid "Ldap does not seem an Active Directory (no not have group objects)" +msgstr "LDAP scheint nicht Active Directory (Nein, nicht haben Gruppenobjekte)" + +#: auths/ActiveDirectory/Authenticator.py:425 +msgid "" +"Ldap does not seem an Active Directory (do not have any user nor groups)" +msgstr "" +"LDAP scheint nicht Active Directory (nicht haben keine Benutzer oder Gruppen)" + +#: auths/ActiveDirectory/Authenticator.py:430 +#: auths/EDirectory/Authenticator.py:358 auths/RegexLdap/Authenticator.py:380 +#: auths/SimpleLDAP/Authenticator.py:422 +msgid "Connection params seem correct, test was succesfully executed" +msgstr "Verbindung Params scheinen korrekt, Test wurde erfolgreich ausgeführt" + +#: auths/EDirectory/Authenticator.py:61 auths/RegexLdap/Authenticator.py:52 +#: auths/SimpleLDAP/Authenticator.py:50 +#: services/Vmware/ServiceProviderVC.py:29 +msgid "Port" +msgstr "Port" + +#: auths/EDirectory/Authenticator.py:61 auths/RegexLdap/Authenticator.py:52 +#: auths/SimpleLDAP/Authenticator.py:50 +msgid "Ldap port (389 for non ssl, 636 for ssl normally" +msgstr "LDAP-Port (389 für nicht Ssl, 636 für Ssl normalerweise" + +#: auths/EDirectory/Authenticator.py:62 auths/RegexLdap/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:51 +msgid "" +"If checked, will use a ssl connection to ldap (if port is 389, will use in " +"fact port 636)" +msgstr "" +"Wenn diese Option aktiviert ist, verwendet eine Ssl-Verbindung zum Ldap " +"(Wenn Port 389 ist, wird in Tatsache Port 636)" + +#: auths/EDirectory/Authenticator.py:63 +#, fuzzy +msgid "Admin user" +msgstr "Admin" + +#: auths/EDirectory/Authenticator.py:63 +#, fuzzy +msgid "Username with read privileges on the eDirectory" +msgstr "Benutzernamen mit lesen Berechtigungen auf der Basis ausgewählt" + +#: auths/EDirectory/Authenticator.py:67 +#, fuzzy +msgid "eDirectory Authenticator" +msgstr "Active Directory-Authenticator" + +#: auths/EDirectory/Authenticator.py:69 +#, fuzzy +msgid "Authenticate against eDirectory" +msgstr "Authentifikator mit Active Directory" + +#: auths/EDirectory/Authenticator.py:323 auths/RegexLdap/Authenticator.py:325 +#: auths/SimpleLDAP/Authenticator.py:362 +msgid "Ldap search base is incorrect" +msgstr "LDAP-Suche base ist falsch" + +#: auths/EDirectory/Authenticator.py:328 auths/RegexLdap/Authenticator.py:330 +#: auths/SimpleLDAP/Authenticator.py:367 +msgid "Ldap user class seems to be incorrect (no user found by that class)" +msgstr "" +"LDAP-Benutzerklasse scheint nicht korrekt (kein Benutzer gefunden durch " +"diese Klasse)" + +#: auths/EDirectory/Authenticator.py:336 auths/RegexLdap/Authenticator.py:346 +#: auths/SimpleLDAP/Authenticator.py:383 +msgid "" +"Ldap user id attribute seems to be incorrect (no user found by that " +"attribute)" +msgstr "" +"LDAP-Benutzer-Id-Attribut scheint falsch (kein Benutzer gefunden damit -" +"Attribut)" + +#: auths/EDirectory/Authenticator.py:344 +msgid "Expected group attribute " +msgstr "Erwartete Group-Attribut " + +#: auths/EDirectory/Authenticator.py:353 +#, fuzzy +msgid "" +"Ldap user class or user id attr is probably wrong (Ldap is an eDirectory?)" +msgstr "" +"LDAP Benutzer Klasse oder Benutzer-Id Attr ist wahrscheinlich falsch (keiner " +"Benutzer mit finden beide Bedingungen)" + +#: auths/IP/Authenticator.py:45 auths/IP/Authenticator.py:47 +msgid "IP Authenticator" +msgstr "IP-Authenticator" + +#: auths/IP/Authenticator.py:51 +msgid "IP" +msgstr "IP " + +#: auths/IP/Authenticator.py:52 +msgid "IP Range" +msgstr "IP-Bereich" + +#: auths/IP/Authenticator.py:99 auths/InternalDB/Authenticator.py:94 +msgid "All seems fine in the authenticator." +msgstr "Alles scheint in Ordnung in der Authentifikator." + +#: auths/InternalDB/Authenticator.py:46 +msgid "Internal Database" +msgstr "Interne Datenbank" + +#: auths/InternalDB/Authenticator.py:48 +msgid "Internal dabasase authenticator. Doesn't uses external sources" +msgstr "Interne Dabasase Authentifikator. Keine verwendet externe Quellen" + +#: auths/InternalDB/Authenticator.py:91 +msgid "Internal structures seems ok" +msgstr "Interne Strukturen scheint in Ordnung" + +#: auths/RegexLdap/Authenticator.py:54 auths/SimpleLDAP/Authenticator.py:52 +msgid "Username with read privileges on the base selected" +msgstr "Benutzernamen mit lesen Berechtigungen auf der Basis ausgewählt" + +#: auths/RegexLdap/Authenticator.py:57 auths/SimpleLDAP/Authenticator.py:55 +msgid "Base" +msgstr "Base" + +#: auths/RegexLdap/Authenticator.py:57 auths/SimpleLDAP/Authenticator.py:55 +msgid "Common search base (used for \"users\" and \"groups\"" +msgstr "Gemeinsame Suchbeginn (verwendet für \"Users\" und \"Gruppen\"" + +#: auths/RegexLdap/Authenticator.py:58 auths/SimpleLDAP/Authenticator.py:56 +msgid "User class" +msgstr "User-Klasse" + +#: auths/RegexLdap/Authenticator.py:58 auths/SimpleLDAP/Authenticator.py:56 +msgid "Class for LDAP users (normally posixAccount)" +msgstr "Klasse für LDAP-Benutzer (normalerweise \"posixAccount\")" + +#: auths/RegexLdap/Authenticator.py:59 auths/SimpleLDAP/Authenticator.py:57 +msgid "User Id Attr" +msgstr "Benutzer-Id Attr" + +#: auths/RegexLdap/Authenticator.py:59 auths/SimpleLDAP/Authenticator.py:57 +msgid "Attribute that contains the user id" +msgstr "Attribut, das die Benutzer-Id enthält" + +#: auths/RegexLdap/Authenticator.py:60 auths/SimpleLDAP/Authenticator.py:58 +msgid "User Name Attr" +msgstr "Benutzer Name Attr" + +#: auths/RegexLdap/Authenticator.py:60 auths/SimpleLDAP/Authenticator.py:58 +msgid "Attributes that contains the user name (list of comma separated values)" +msgstr "" +"Attribute, die dem Benutzernamen (Liste der durch Kommas getrennte Werte)" + +#: auths/RegexLdap/Authenticator.py:61 +msgid "Group Name Attr" +msgstr "Gruppe Name Attr" + +#: auths/RegexLdap/Authenticator.py:61 +msgid "Attribute that contains the group name" +msgstr "Attribut, das den Namen den Gruppe enthält" + +#: auths/RegexLdap/Authenticator.py:62 +msgid "Regular Exp. for groups" +msgstr "Regelmäßige EXP für Gruppen." + +#: auths/RegexLdap/Authenticator.py:62 +msgid "Regular Expression to extract the group name" +msgstr "Reguläre Ausdruck, der den Namen den Gruppe zu extrahieren" + +#: auths/RegexLdap/Authenticator.py:64 +msgid "Regex LDAP Authenticator" +msgstr "Regex LDAP-Authenticator" + +#: auths/RegexLdap/Authenticator.py:66 +msgid "Regular Expressions LDAP authenticator" +msgstr "Reguläre Ausdrücke LDAP-Authentifizierungsserver" + +#: auths/RegexLdap/Authenticator.py:338 auths/SimpleLDAP/Authenticator.py:375 +msgid "Ldap group class seems to be incorrect (no group found by that class)" +msgstr "" +"LDAP Gruppe Klasse scheint nicht korrekt (keine Gruppe gefunden durch diese " +"Klasse)" + +#: auths/RegexLdap/Authenticator.py:356 auths/SimpleLDAP/Authenticator.py:391 +msgid "" +"Ldap group id attribute seems to be incorrect (no group found by that " +"attribute)" +msgstr "" +"LDAP Gruppe Id-Attribut scheint nicht korrekt (keine Gruppe gefunden, die -" +"Attribut)" + +#: auths/RegexLdap/Authenticator.py:365 auths/SimpleLDAP/Authenticator.py:400 +msgid "" +"Ldap user class or user id attr is probably wrong (can't find any user with " +"both conditions)" +msgstr "" +"LDAP Benutzer Klasse oder Benutzer-Id Attr ist wahrscheinlich falsch (keiner " +"Benutzer mit finden beide Bedingungen)" + +#: auths/SimpleLDAP/Authenticator.py:59 +msgid "Group class" +msgstr "Group-Klasse" + +#: auths/SimpleLDAP/Authenticator.py:59 +msgid "Class for LDAP groups (normally poxisGroup)" +msgstr "Klasse für LDAP-Gruppen (normalerweise PoxisGroup)" + +#: auths/SimpleLDAP/Authenticator.py:60 +msgid "Group Id Attr" +msgstr "Gruppe Id Attr" + +#: auths/SimpleLDAP/Authenticator.py:60 +msgid "Attribute that contains the group id" +msgstr "Attribut, das die Gruppen-Id enthält" + +#: auths/SimpleLDAP/Authenticator.py:61 +msgid "Group membership attr" +msgstr "Gruppe Mitgliedschaft attr" + +#: auths/SimpleLDAP/Authenticator.py:61 +msgid "Attribute of the group that contains the users belonging to it" +msgstr "Attribut der Gruppe mit die Benutzern gehören es" + +#: auths/SimpleLDAP/Authenticator.py:63 +msgid "SimpleLDAP Authenticator" +msgstr "SimpleLDAP Authenticator" + +#: auths/SimpleLDAP/Authenticator.py:65 +msgid "Simple LDAP authenticator" +msgstr "Einfache LDAP-Authentifizierungsserver" + +#: auths/SimpleLDAP/Authenticator.py:409 +msgid "" +"Ldap group class or group id attr is probably wrong (can't find any group " +"with both conditions)" +msgstr "" +"LDAP Gruppe Klasse oder Gruppe Id Attr ist wahrscheinlich falsch (kann nicht " +"finden jede Gruppe beide Bedingungen)" + +#: auths/SimpleLDAP/Authenticator.py:416 +msgid "Can't locate any group with the membership attribute specified" +msgstr "" +"Jede Gruppe kann nicht mit das angegebene Mitgliedschaft-Attribut gefunden " +"werden" + +#: core/BaseModule.py:196 +msgid "No connection checking method is implemented." +msgstr "Keine Verbindung überprüfen Methode ist implementiert." + +#: core/BaseModule.py:248 +msgid "No check method provided." +msgstr "Keine Prüfmethode zur Verfügung gestellt." + +#: core/managers/PublicationManager.py:156 +msgid "" +"Already publishing. Wait for previous publication to finish and try again" +msgstr "" +"Bereits veröffentlicht. Warten Sie auf vorherige Veröffentlichung zu beenden " +"und versuchen Sie es erneut" + +#: core/managers/PublicationManager.py:168 +msgid "Can't cancel non running publication" +msgstr "Nicht ausgeführte Veröffentlichung kann nicht abgebrochen werden." + +#: core/managers/PublicationManager.py:186 +msgid "Can't unpublish non usable publication" +msgstr "Kann nicht nicht nutzbare Veröffentlichung aufheben" + +#: core/managers/PublicationManager.py:189 +msgid "Can't unpublish publications with services in process" +msgstr "" +"Publikationen mit Dienstleistungen im Prozess kann nicht Veröffentlichung " +"rückgängig machen" + +#: core/managers/UserPrefsManager.py:254 +msgid "Screen Size" +msgstr "Bildschirmgröße" + +#: core/managers/UserPrefsManager.py:258 +msgid "Full Screen" +msgstr "Vollbild" + +#: core/managers/UserPrefsManager.py:261 +msgid "Screen colors" +msgstr "Bildschirmfarben" + +#: core/managers/UserPrefsManager.py:262 +msgid "8 bits" +msgstr "8 Bit" + +#: core/managers/UserPrefsManager.py:263 +msgid "16 bits" +msgstr "16 bits" + +#: core/managers/UserPrefsManager.py:264 +msgid "24 bits" +msgstr "24 Bit" + +#: core/managers/UserPrefsManager.py:265 +msgid "32 bits" +msgstr "32 bits" + +#: core/managers/UserServiceManager.py:302 +msgid "Can't cancel non running operation" +msgstr "Kann nicht nicht ausgeführten Vorgang abbrechen" + +#: core/managers/UserServiceManager.py:321 +msgid "Can't remove a non active element" +msgstr "Ein nicht aktive Element kann nicht entfernt werden." + +#: core/managers/UserServiceManager.py:334 +msgid "Can't remove nor cancel {0} cause its states doesn't allows it" +msgstr "" +"Kann weder entfernen noch Abbrechen {0} Ursache ihrer Staaten nicht erlaubt " +"es" + +#: core/osmanagers/BaseOsManager.py:50 +msgid "Base OS Manager" +msgstr "Base OS Manager" + +#: core/osmanagers/BaseOsManager.py:52 +msgid "Base Manager" +msgstr "Base Manager" + +#: core/transports/BaseTransport.py:94 +msgid "Transport empty" +msgstr "Transport leer" + +#: core/util/State.py:59 +msgid "Active" +msgstr "Aktive" + +#: core/util/State.py:59 +msgid "Inactive" +msgstr "Inaktiv" + +#: core/util/State.py:59 +msgid "Blocked" +msgstr "Blockiert" + +#: core/util/State.py:59 +msgid "Waiting publication" +msgstr "Wartet auf Veröffentlichung" + +#: core/util/State.py:60 +msgid "In preparation" +msgstr "In Vorbereitung" + +#: core/util/State.py:60 +msgid "Valid" +msgstr "Gültig" + +#: core/util/State.py:61 +msgid "Waiting for removal" +msgstr "Warten auf Entfernung" + +#: core/util/State.py:61 +msgid "Removing" +msgstr "Entfernen" + +#: core/util/State.py:61 +msgid "Removed" +msgstr "Entfernt" + +#: core/util/State.py:61 +msgid "Canceled" +msgstr "Abgebrochen" + +#: core/util/State.py:62 +msgid "Canceling" +msgstr "Abbrechen" + +#: core/util/State.py:62 templates/uds/error.html:6 +#: templates/uds/error.html.py:11 +msgid "Error" +msgstr "Fehler" + +#: core/util/State.py:62 +msgid "Running" +msgstr "Ausführen" + +#: core/util/State.py:62 +msgid "Finished" +msgstr "Fertig" + +#: core/util/State.py:62 +msgid "Waiting execution" +msgstr "Personen, die auf Ausführung" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:44 +msgid "Linux OS Manager" +msgstr "Linux OS Manager" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:46 +msgid "" +"Os Manager to control linux virtual machines (basically renames machine and " +"notify state)" +msgstr "" +"OS-Manager, um die Kontrolle von Linux virtuellen Maschinen (im Grunde " +"benennt Maschine und Benachrichtigen Sie Zustand)" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:49 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:40 +msgid "On Logout" +msgstr "Beim Abmelden" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:49 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:40 +msgid "What to do when user logout from service" +msgstr "Was tun, wenn Benutzer abmelden vom Dienst" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:50 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:41 +msgid "Keep service assigned" +msgstr "Halten Sie Dienst zugewiesen" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:51 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:42 +msgid "Remove service" +msgstr "Basisdienst" + +#: osmanagers/LinuxOsManager/__init__.py:43 +msgid "UDS Actor for linux machines (Requires python 2.6 or greater)" +msgstr "" +"UDS Schauspieler für Linux Maschinen (Python 2.6 oder höher erforderlich)" + +#: osmanagers/NoneOsManager/Manager.py:43 +msgid "None OS Manager" +msgstr "Keine OS Manager" + +#: osmanagers/NoneOsManager/Manager.py:45 +msgid "Os Manager with no actions" +msgstr "OS-Manager keine Aktionen" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:23 +msgid "Windows Domain OS Manager" +msgstr "Windows OS Gebietsmanager" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:25 +msgid "" +"Os Manager to control windows machines with domain. (Basically renames " +"machine)" +msgstr "" +"OS-Manager, um Windows-Maschinen mit Domäne steuern. (Im Grunde benennt " +"Maschine)" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:29 +#: transports/RDP/RDPTransport.py:39 transports/RDP/TSRDPTransport.py:43 +#: transports/RGS/RGSTransport.py:44 transports/RGS/TRGSTransport.py:49 +msgid "Domain" +msgstr "Domäne" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:29 +msgid "Domain to join machines to (better use dns form of domain)" +msgstr "Domäne beitreten Maschinen (bessere DNS-Form der Domäne verwenden)" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:30 +msgid "Account" +msgstr "Konto" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:30 +msgid "Account with rights to add machines to domain" +msgstr "Konto mit der Berechtigung zum Computer zur Domäne hinzufügen" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:31 +msgid "Password of the account" +msgstr "Kennwort für das Konto" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:32 +msgid "OU" +msgstr "OU" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:32 +msgid "" +"Organizational unit where to add machines in domain (check it before using " +"it)" +msgstr "" +"Organisationseinheit, an Maschinen in Domäne hinzugefügt (überprüfen sie vor " +"der Verwendung es)" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:40 +msgid "Must provide a domain!!!" +msgstr "Müssen eine Domäne zur Verfügung stellen!" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:42 +msgid "Must provide an account to add machines to domain!!!" +msgstr "Muss ein Konto Domäne Maschinen hinzu bieten!!!" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:44 +msgid "Must provide a password for the account!!!" +msgstr "Muss ein Kennwort für das Konto angeben!" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:35 +msgid "Windows Basic OS Manager" +msgstr "Grundlegende Windows-OS-Manager" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:37 +msgid "" +"Os Manager to control windows machines without domain. (Basically renames " +"machine)" +msgstr "" +"OS-Manager, um Windows-Rechner ohne Domäne steuern. (Im Grunde benennt " +"Maschine)" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:51 +msgid "Length must be numeric!!" +msgstr "Länge muss numerisch sein!" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:53 +msgid "Length must be betwen 1 and six" +msgstr "Länge muss zwischen 1 und 6" + +#: osmanagers/WindowsOsManager/__init__.py:23 +msgid "" +"UDS Actor for windows machines (Important!! Requires .net framework 3.5 " +"sp1)" +msgstr "" +"UDS Schauspieler für Windows-Rechner (wichtig! .Net Framework 3.5 " +"erfordert SP1)" + +#: services/PhysicalMachines/IPMachineDeployed.py:56 +msgid "IP " +msgstr "IP " + +#: services/PhysicalMachines/IPMachinesService.py:46 +msgid "List of IPS" +msgstr "Liste der IPS" + +#: services/PhysicalMachines/IPMachinesService.py:49 +msgid "Physical machines accesed by ip" +msgstr "Physische Computer Zugang von ip" + +#: services/PhysicalMachines/IPMachinesService.py:51 +msgid "This service provides access to POWERED-ON Machines by ip" +msgstr "" +"Dieser Service ermöglicht den Zugriff auf verknüpfte Clones Maschinen auf " +"einen Virtual Center" + +#: services/Sample/SampleProvider.py:142 +msgid "Methuselah is not alive!!! :-)" +msgstr "Methusalem ist nicht lebendig!!! :-)" + +#: services/Sample/SampleProvider.py:182 +msgid "Nothing tested, but all went fine.." +msgstr "Nichts getestet, aber alles ging gut..." + +#: services/Sample/SamplePublication.py:199 +msgid "Random integer was 9!!! :-)" +msgstr "Zufällige ganze war 9!!! :-)" + +#: services/Vmware/Helpers.py:72 +msgid "Local" +msgstr "Lokale" + +#: services/Vmware/Helpers.py:74 +msgid "Remote" +msgstr "Entfernte" + +#: services/Vmware/PublicationVC.py:36 +msgid "Publication" +msgstr "Veröffentlichung" + +#: services/Vmware/PublicationVC.py:37 +msgid "UDS Publication for {0} created at {1}" +msgstr "UDS-Veröffentlichung erstellt am {1} {0}" + +#: services/Vmware/ServiceProviderVC.py:29 +msgid "VMWare VC Server Port (usually 443)" +msgstr "VMWare VC Server Port (in der Regel 443)" + +#: services/Vmware/ServiceProviderVC.py:30 +msgid "User with valid privileges on VC" +msgstr "Benutzer mit gültigen Berechtigungen für VC" + +#: services/Vmware/ServiceProviderVC.py:31 +msgid "Password of the user of the VC" +msgstr "Kennwort des Benutzers des VC" + +#: services/Vmware/ServiceProviderVC.py:32 +msgid "Timeout in seconds of connection to VC" +msgstr "Timeout in Sekunden der Verbindung zum VC" + +#: services/Vmware/ServiceProviderVC.py:33 +msgid "Macs range" +msgstr "Mac-Bereich" + +#: services/Vmware/ServiceProviderVC.py:34 +msgid "Range of valids macs for created machines" +msgstr "Bereich von gilt Macs für erstellte Maschinen" + +#: services/Vmware/ServiceProviderVC.py:39 +msgid "VMWare Virtual Center Provider" +msgstr "VMWare Virtual Center-Provider" + +#: services/Vmware/ServiceProviderVC.py:41 +msgid "Provides connection to Virtual Center Services" +msgstr "Stellt Verbindung zu Virtual Center Services" + +#: services/Vmware/ServiceProviderVC.py:111 +msgid "Error testing connection" +msgstr "Fehler testen Verbindung" + +#: services/Vmware/ServiceProviderVC.py:114 +msgid "VmwareVC Provider: " +msgstr "VmwareVC Provider: " + +#: services/Vmware/ServiceProviderVC.py:120 +msgid "Connection params ok" +msgstr "Verbindung Params ok" + +#: services/Vmware/ServiceProviderVC.py:121 +msgid "Connection failed. Check connection params" +msgstr "Verbindung ist fehlgeschlagen. Kontrollkästchen Verbindung params" + +#: services/Vmware/VCLinkedCloneService.py:28 +msgid "Datacenter" +msgstr "Datacenter" + +#: services/Vmware/VCLinkedCloneService.py:34 +msgid "Datacenter containing base machine" +msgstr "Datacenter mit Basismaschine" + +#: services/Vmware/VCLinkedCloneService.py:36 +msgid "Network" +msgstr "Netzwerk" + +#: services/Vmware/VCLinkedCloneService.py:37 +msgid "" +"If more than 1 interface is found in machine, use one on this network as main" +msgstr "" +"Wenn mehr als 1 Schnittstelle in Maschine gefunden wird, verwenden Sie eine " +"in diesem Netzwerk als wichtigsten" + +#: services/Vmware/VCLinkedCloneService.py:38 +msgid "Pub. Resource Pool" +msgstr "Kneipe. Ressourcenpool" + +#: services/Vmware/VCLinkedCloneService.py:38 +msgid "Resource Pool where deploy clones" +msgstr "Ressourcen-Pool in der Klone bereitgestellt" + +#: services/Vmware/VCLinkedCloneService.py:39 +msgid "Clones Folder" +msgstr "Klone Ordner" + +#: services/Vmware/VCLinkedCloneService.py:39 +msgid "Folder where deploy clones" +msgstr "Ordner wo bereitstellen Klone" + +#: services/Vmware/VCLinkedCloneService.py:40 +msgid "Resource Pool" +msgstr "Ressourcenpool" + +#: services/Vmware/VCLinkedCloneService.py:46 +msgid "Resource Pool containing base machine" +msgstr "Ressource Pool mit Basismaschine" + +#: services/Vmware/VCLinkedCloneService.py:48 +msgid "Base Machine" +msgstr "Grundmaschine" + +#: services/Vmware/VCLinkedCloneService.py:48 +msgid "Base machine for this service" +msgstr "Grundmaschine für diesen Dienst" + +#: services/Vmware/VCLinkedCloneService.py:49 +msgid "Memory (Mb)" +msgstr "Speicher (Mb)" + +#: services/Vmware/VCLinkedCloneService.py:50 +msgid "Memory for machines deployed from this service" +msgstr "Speicher für Maschinen, die von diesem Dienst bereitgestellt" + +#: services/Vmware/VCLinkedCloneService.py:51 +msgid "Datastores" +msgstr "Datastores" + +#: services/Vmware/VCLinkedCloneService.py:52 +msgid "Datastores where to put incrementals" +msgstr "Datastores wo inkrementelle Backups" + +#: services/Vmware/VCLinkedCloneService.py:53 +msgid "Machine Names" +msgstr "Computernamen" + +#: services/Vmware/VCLinkedCloneService.py:53 +msgid "Base name for clones from this machine" +msgstr "Basisname für Clones von diesem Computer" + +#: services/Vmware/VCLinkedCloneService.py:54 +msgid "Name Length" +msgstr "Länge des Dateinamens" + +#: services/Vmware/VCLinkedCloneService.py:55 +msgid "Length of numeric part for the names of this machines (betwen 3 and 6" +msgstr "" +"Länge der numerische Teil für die Namen dieser Maschinen (zwischen 3 und 6" + +#: services/Vmware/VCLinkedCloneService.py:62 +msgid "VMWare Linked clone base" +msgstr "VMWare Linked Clone-base" + +#: services/Vmware/VCLinkedCloneService.py:64 +msgid "" +"This service provides access to Linked Clones machines on a Virtual Center" +msgstr "" +"Dieser Service ermöglicht den Zugriff auf verknüpfte Clones Maschinen auf " +"einen Virtual Center" + +#: services/Vmware/VCLinkedCloneService.py:70 +msgid "Number of desired machines to keep running waiting for a user" +msgstr "" +"Anzahl der gewünschten Maschinen warten für einen Benutzer ausgeführt wird" + +#: services/Vmware/VCLinkedCloneService.py:72 +msgid "Number of desired machines to keep suspended waiting for use" +msgstr "" +"Anzahl der gewünschten Maschinen zu ausgesetzten Personen, die für die " +"Verwendung auf" + +#: services/Vmware/VCLinkedCloneService.py:93 +msgid "The length of basename plus length must not be greater than 15" +msgstr "Die Länge der Basename plus Länge darf nicht größer als 15 sein." + +#: services/Vmware/VCLinkedCloneService.py:95 +msgid "The machine name can't be only numbers" +msgstr "Der Computername kann nicht nur Zahlen sein." + +#: templates/404.html:4 templates/404.html.py:7 +msgid "Page not found" +msgstr "Seite nicht gefunden" + +#: templates/404.html:9 +msgid "Sorry, but the requested page could not be found." +msgstr "" +"Tut mir leid, aber die angeforderte Seite konnte nicht gefunden werden." + +#: templates/uds/base.html:7 +msgid "UDS" +msgstr "UDS" + +#: templates/uds/downloads.html:8 templates/uds/snippets/admin_user.html:7 +msgid "Downloads" +msgstr "Downloads" + +#: templates/uds/downloads.html:11 +msgid "" +"This page contains a list of downloadables provided by different modules" +msgstr "" +"Diese Seite enthält eine Liste der Downloads von verschiedenen Modulen " +"bereitgestellt" + +#: templates/uds/index.html:51 +msgid "Services" +msgstr "Dienstleistungen" + +#: templates/uds/index.html:70 +msgid "Java not found" +msgstr "Java nicht gefunden" + +#: templates/uds/index.html:71 +msgid "" +"Java is not available on your browser, and the selected transport needs it." +msgstr "" +"Java ist nicht verfügbar in Ihrem Browser, und der ausgewählte Transport " +"muss es." + +#: templates/uds/index.html:72 +msgid "Please, install latest version from" +msgstr "Bitte installieren Sie neueste Version von" + +#: templates/uds/index.html:72 +msgid "Java website" +msgstr "Java-website" + +#: templates/uds/index.html:72 +msgid "and restart browser" +msgstr "und Browser neu starten" + +#: templates/uds/index.html:78 +msgid "Ip" +msgstr "IP" + +#: templates/uds/index.html:79 +msgid "Networks" +msgstr "Netzwerke" + +#: templates/uds/index.html:80 +msgid "Transports" +msgstr "Transporte" + +#: templates/uds/internal_page.html:28 +msgid "User" +msgstr "Benutzer" + +#: templates/uds/internal_page.html:34 templates/uds/prefs.html:12 +msgid "Preferences" +msgstr "Einstellungen" + +#: templates/uds/internal_page.html:40 +msgid "Log out" +msgstr "Melden Sie sich ab" + +#: templates/uds/login.html:6 +msgid "Login to UDS" +msgstr "Anmeldung für UDS" + +#: templates/uds/login.html:78 +msgid "Login" +msgstr "Anmeldung" + +#: templates/uds/login.html:83 +msgid "Login data" +msgstr "Login-Daten" + +#: templates/uds/login.html:86 +msgid "Enter" +msgstr "Geben Sie" + +#: templates/uds/login.html:93 +msgid "Back to login" +msgstr "Zurück zur Anmeldung" + +#: templates/uds/prefs.html:6 +msgid "UDS User Preferences" +msgstr "UDS-Benutzereinstellungen" + +#: templates/uds/prefs.html:16 +msgid "Save Preferences" +msgstr "Speichern Sie Einstellungen" + +#: templates/uds/service_not_ready.html:6 +msgid "Service not ready at this moment. Please, try again in a while." +msgstr "" +"Service im Moment nicht bereit. Bitte, versuchen Sie es noch einmal in eine " +"Weile." + +#: templates/uds/snippets/admin_user.html:4 +msgid "Admin" +msgstr "Admin" + +#: templates/uds/snippets/back_to_list.html:3 +msgid "Back to services list" +msgstr "Zurück zur Liste" + +#: templates/uds/snippets/lang.html:9 +msgid "Language" +msgstr "Sprache" + +#: transports/NX/NXTransport.py:54 +msgid "NX Transport (direct)" +msgstr "NX Transport (direkt)" + +#: transports/NX/NXTransport.py:56 +msgid "NX Transport for direct connection" +msgstr "NX-Transport für den direkten Anschluss" + +#: transports/NX/NXTransport.py:60 transports/RDP/RDPTransport.py:36 +#: transports/RDP/TSRDPTransport.py:40 transports/RGS/RGSTransport.py:41 +#: transports/RGS/TRGSTransport.py:46 transports/TSNX/TSNXTransport.py:65 +msgid "Empty creds" +msgstr "Leere creds" + +#: transports/NX/NXTransport.py:60 transports/RDP/RDPTransport.py:36 +#: transports/RDP/TSRDPTransport.py:40 transports/RGS/RGSTransport.py:41 +#: transports/RGS/TRGSTransport.py:46 transports/TSNX/TSNXTransport.py:65 +msgid "If checked, the credentials used to connect will be emtpy" +msgstr "" +"Wenn diese Option aktiviert, werden die Anmeldeinformationen zum Herstellen " +"einer leer sein." + +#: transports/NX/NXTransport.py:61 transports/RDP/RDPTransport.py:37 +#: transports/RDP/TSRDPTransport.py:41 transports/RGS/RGSTransport.py:42 +#: transports/RGS/TRGSTransport.py:47 transports/TSNX/TSNXTransport.py:66 +msgid "If not empty, this username will be always used as credential" +msgstr "" +"Wenn nicht leer ist, dieser Benutzername wird immer als verwendet " +"Anmeldeinformationen" + +#: transports/NX/NXTransport.py:62 transports/RDP/RDPTransport.py:38 +#: transports/RDP/TSRDPTransport.py:42 transports/RGS/RGSTransport.py:43 +#: transports/RGS/TRGSTransport.py:48 transports/TSNX/TSNXTransport.py:67 +msgid "If not empty, this password will be always used as credential" +msgstr "" +"Wenn nicht leer ist, dieses Kennwort immer verwendet werden als " +"Anmeldeinformationen" + +#: transports/NX/NXTransport.py:63 transports/TSNX/TSNXTransport.py:68 +msgid "Listen port" +msgstr "-Listenanschluss" + +#: transports/NX/NXTransport.py:63 transports/TSNX/TSNXTransport.py:68 +msgid "Listening port of NX (ssh) at client machine" +msgstr "Hören Port des NX (ssh) bei Client-Rechner" + +#: transports/NX/NXTransport.py:64 transports/TSNX/TSNXTransport.py:69 +msgid "Connection" +msgstr "Verbindung" + +#: transports/NX/NXTransport.py:64 transports/TSNX/TSNXTransport.py:69 +msgid "Connection speed for this transport (quality)" +msgstr "Verbindungsgeschwindigkeit für diesen Transport (Qualität)" + +#: transports/NX/NXTransport.py:71 transports/TSNX/TSNXTransport.py:76 +msgid "Session" +msgstr "Sitzung" + +#: transports/NX/NXTransport.py:71 transports/TSNX/TSNXTransport.py:76 +msgid "Desktop session" +msgstr "Desktop-Sitzung" + +#: transports/NX/NXTransport.py:76 transports/TSNX/TSNXTransport.py:81 +msgid "Disk Cache" +msgstr "Festplatten-Cache" + +#: transports/NX/NXTransport.py:76 transports/TSNX/TSNXTransport.py:81 +msgid "Cache size en Mb stored at disk" +msgstr "Cache-Größe de Mb auf der Festplatte gespeichert" + +#: transports/NX/NXTransport.py:84 transports/TSNX/TSNXTransport.py:89 +msgid "Memory Cache" +msgstr "Memory-Caches" + +#: transports/NX/NXTransport.py:84 transports/TSNX/TSNXTransport.py:89 +msgid "Cache size en Mb keept at memory" +msgstr "Cache Größe de Mb Keept auf Speicher" + +#: transports/NX/__init__.py:42 transports/TSNX/__init__.py:42 +msgid "NX Protocol" +msgstr "NX-Protokoll" + +#: transports/NX/__init__.py:48 +msgid "UDS Actor connector for NX (requires nomachine packages)" +msgstr "UDS Schauspieler Connector für NX (erfordert Nomachine Pakete)" + +#: transports/NX/web.py:74 +msgid "" +"In order to use this transport, you need to install first Nomachine Nx " +"Client version 3.5.x" +msgstr "" +"Um diesen Transport verwenden, müssen Sie installieren ersten Nomachine Nx " +"Client Version 3.5.x" + +#: transports/NX/web.py:75 +msgid "you can obtain it for your platform from" +msgstr "Sie können es für Ihre Plattform von abrufen" + +#: transports/NX/web.py:75 +msgid "nochamine web site" +msgstr "Nochamine-Website" + +#: transports/RDP/RDPTransport.py:30 +msgid "RDP Transport (direct)" +msgstr "RDP Transport (direkt)" + +#: transports/RDP/RDPTransport.py:32 +msgid "RDP Transport for direct connection" +msgstr "RDP-Transport für den direkten Anschluss" + +#: transports/RDP/RDPTransport.py:39 transports/RDP/TSRDPTransport.py:43 +#: transports/RGS/RGSTransport.py:44 transports/RGS/TRGSTransport.py:49 +msgid "" +"If not empty, this domain will be always used as credential (used as DOMAIN" +"\\user)" +msgstr "" +"Wenn nicht leer ist, dieser Domäne immer verwendet werden als " +"Anmeldeinformationen (als DOMAIN verwendet\\Benutzer)" + +#: transports/RDP/RDPTransport.py:40 transports/RDP/TSRDPTransport.py:44 +msgid "Allow Smartcards" +msgstr "Ermöglichen Smartcards" + +#: transports/RDP/RDPTransport.py:40 transports/RDP/TSRDPTransport.py:44 +msgid "If checked, this transport will allow the use of smartcards" +msgstr "" +"Wenn aktiviert, wird dieser Transport ermöglichen die Verwendung von " +"smartcards" + +#: transports/RDP/RDPTransport.py:41 transports/RDP/TSRDPTransport.py:45 +msgid "Allow Printers" +msgstr "Druckerveröffentlichung zulassen" + +#: transports/RDP/RDPTransport.py:41 transports/RDP/TSRDPTransport.py:45 +msgid "If checked, this transport will allow the use of user printers" +msgstr "" +"Wenn diese Option aktiviert, wird dieser Transport die Verwendung von " +"Benutzer Drucker ermöglichen." + +#: transports/RDP/RDPTransport.py:42 transports/RDP/TSRDPTransport.py:46 +msgid "Allow Drives" +msgstr "Laufwerke ermöglichen" + +#: transports/RDP/RDPTransport.py:42 transports/RDP/TSRDPTransport.py:46 +msgid "If checked, this transport will allow the use of user drives" +msgstr "" +"Wenn aktiviert, wird dieser Transport die Verwendung von Benutzer-Laufwerke " +"erlauben." + +#: transports/RDP/RDPTransport.py:43 transports/RDP/TSRDPTransport.py:47 +msgid "Allow Serials" +msgstr "Ermöglichen Serien" + +#: transports/RDP/RDPTransport.py:43 transports/RDP/TSRDPTransport.py:47 +msgid "If checked, this transport will allow the use of user serial ports" +msgstr "" +"Wenn aktiviert, wird dieser Transport die Verwendung des Benutzers erlauben " +"serielle ports" + +#: transports/RDP/TSRDPTransport.py:31 +msgid "RDP Transport (tunneled)" +msgstr "RDP-Verkehr (Tunneling)" + +#: transports/RDP/TSRDPTransport.py:33 +msgid "RDP Transport for tunneled connection" +msgstr "RDP-Verkehr für getunnelte Verbindung" + +#: transports/RDP/TSRDPTransport.py:37 transports/RGS/TRGSTransport.py:43 +#: transports/TSNX/TSNXTransport.py:62 +msgid "Tunnel server" +msgstr "Tunnel-server" + +#: transports/RDP/TSRDPTransport.py:37 transports/RGS/TRGSTransport.py:43 +#: transports/TSNX/TSNXTransport.py:62 +msgid "" +"IP or Hostname of tunnel server send to client device (\"public\" ip) and " +"port. (use HOST:PORT format)" +msgstr "" +"IP-Adresse oder Hostname des Tunnel-Server senden an Client-Gerät " +"(\"öffentliche\" IP-Adresse) und Port. (verwenden Sie HOST: PORT-Format)" + +#: transports/RDP/TSRDPTransport.py:38 transports/RGS/TRGSTransport.py:44 +#: transports/TSNX/TSNXTransport.py:63 +msgid "Tunnel host check" +msgstr "Tunnel Host-Prüfung" + +#: transports/RDP/TSRDPTransport.py:38 transports/RGS/TRGSTransport.py:44 +#: transports/TSNX/TSNXTransport.py:63 +msgid "" +"If not empty, this server will be used to check if service is running before " +"assigning it to user. (use HOST:PORT format)" +msgstr "" +"Wenn nicht leer ist, wird dieser Server zu überprüfen, ob der Dienst " +"ausgeführt wird, bevor Sie verwendet werden Benutzer zuweisen. (verwenden " +"Sie HOST: PORT-Format)" + +#: transports/RDP/__init__.py:20 +msgid "Remote Desktop Protocol" +msgstr "Remote Desktop-Protokoll" + +#: transports/RDP/web.py:83 +msgid "In order to use this service, you should first install CoRD." +msgstr "Um diesen Dienst zu nutzen, sollten Sie zunächst Kabel installieren." + +#: transports/RDP/web.py:84 transports/RGS/web.py:82 +msgid "You can obtain it from" +msgstr "Erhalten Sie es aus" + +#: transports/RDP/web.py:84 +msgid "CoRD Website" +msgstr "CoRD-Website" + +#: transports/RGS/RGSTransport.py:34 +#, fuzzy +msgid "RGS Transport (direct)" +msgstr "RDP Transport (direkt)" + +#: transports/RGS/RGSTransport.py:36 +#, fuzzy +msgid "RGS Transport for direct connection" +msgstr "RDP-Transport für den direkten Anschluss" + +#: transports/RGS/RGSTransport.py:45 transports/RGS/TRGSTransport.py:50 +msgid "Image quality" +msgstr "Bildqualität" + +#: transports/RGS/RGSTransport.py:46 transports/RGS/TRGSTransport.py:51 +msgid "Quality of image codec (0-100)" +msgstr "Qualität der Image-Codec (0-100)" + +#: transports/RGS/RGSTransport.py:47 transports/RGS/TRGSTransport.py:52 +msgid "Adjustable Quality" +msgstr "Einstellbare Qualität" + +#: transports/RGS/RGSTransport.py:48 transports/RGS/TRGSTransport.py:53 +msgid "If checked, the image quality will be adjustable with bandwidth" +msgstr "" +"Wenn diese Option aktiviert, wird die Bildqualität mit Bandbreite " +"einstellbar sein" + +#: transports/RGS/RGSTransport.py:49 transports/RGS/TRGSTransport.py:54 +msgid "Min. Adjustable Quality" +msgstr "Min. einstellbare Qualität" + +#: transports/RGS/RGSTransport.py:50 transports/RGS/TRGSTransport.py:55 +msgid "" +"The lowest image quality applied to images to maintain the minimum update " +"rate." +msgstr "" +"Die niedrigsten Bildqualität auf Bilder beibehalten das minimale Update " +"angewendet Preise." + +#: transports/RGS/RGSTransport.py:51 transports/RGS/TRGSTransport.py:56 +msgid "Adjustable Frame Rate" +msgstr "Einstellbare Framerate" + +#: transports/RGS/RGSTransport.py:52 transports/RGS/TRGSTransport.py:57 +msgid "Update rate threshold to begin adjusting image quality" +msgstr "Update-Rate Schwellenwert beginnen, Anpassen der Bildqualität" + +#: transports/RGS/RGSTransport.py:53 transports/RGS/TRGSTransport.py:58 +msgid "Match Local Resolution" +msgstr "Spiel Ortsauflösung" + +#: transports/RGS/RGSTransport.py:54 transports/RGS/TRGSTransport.py:59 +msgid "" +"Change the Sender's resolution to match the Receiver's resolution when " +"connecting" +msgstr "" +"Ändern des Absenders Auflösung Auflösung des Empfängers übereinstimmen wenn " +"verbinden" + +#: transports/RGS/RGSTransport.py:55 transports/RGS/TRGSTransport.py:60 +msgid "Redirect USB" +msgstr "Umleitung USB" + +#: transports/RGS/RGSTransport.py:56 transports/RGS/TRGSTransport.py:61 +msgid "If checked, the USB will be redirected." +msgstr "Wenn diese Option aktiviert, wird die USB umgeleitet werden." + +#: transports/RGS/RGSTransport.py:57 transports/RGS/TRGSTransport.py:62 +msgid "Redirect Audio" +msgstr "Audio umleiten" + +#: transports/RGS/RGSTransport.py:58 transports/RGS/TRGSTransport.py:63 +#, fuzzy +msgid "If checked, the Audio will be redirected." +msgstr "" +"Wenn diese Option aktiviert, werden die Anmeldeinformationen zum Herstellen " +"einer leer sein." + +#: transports/RGS/RGSTransport.py:59 transports/RGS/TRGSTransport.py:64 +msgid "Redirect Mic" +msgstr "Umleitung Mic" + +#: transports/RGS/RGSTransport.py:60 transports/RGS/TRGSTransport.py:65 +#, fuzzy +msgid "If checked, the Mic will be redirected." +msgstr "" +"Wenn diese Option aktiviert, werden die Anmeldeinformationen zum Herstellen " +"einer leer sein." + +#: transports/RGS/TRGSTransport.py:36 +#, fuzzy +msgid "RGS Transport (tunneled)" +msgstr "RDP-Verkehr (Tunneling)" + +#: transports/RGS/TRGSTransport.py:38 +#, fuzzy +msgid "RGS Transport for tunneled connection" +msgstr "RDP-Verkehr für getunnelte Verbindung" + +#: transports/RGS/web.py:81 +#, fuzzy +msgid "In order to use this service, you should first install RGS Receiver." +msgstr "Um diesen Dienst zu nutzen, sollten Sie zunächst Kabel installieren." + +#: transports/RGS/web.py:82 +#, fuzzy +msgid "HP Website" +msgstr "CoRD-Website" + +#: transports/TSNX/TSNXTransport.py:55 +msgid "NX Transport (tunneled)" +msgstr "NX-Transport (Tunneling)" + +#: transports/TSNX/TSNXTransport.py:57 +msgid "NX Transport for tunneled connection" +msgstr "NX-Transport für getunnelte Verbindung" + +#: web/errors.py:53 +msgid "Unknown error" +msgstr "Unbekannter Fehler" + +#: web/errors.py:54 +msgid "Transport not found" +msgstr "Verkehr nicht gefunden" + +#: web/errors.py:55 +msgid "Service not found" +msgstr "-Dienst nicht gefunden" + +#: web/errors.py:56 xmlrpc/auths/AdminAuth.py:147 +msgid "Access denied" +msgstr "Zugriff verweigert" + +#: web/errors.py:57 +msgid "" +"Invalid service. The service is not available at this moment. Please, try " +"later" +msgstr "" +"Ungültiger Service. Der Dienst ist nicht verfügbar im Moment. Bitte, " +"versuchen Sie später" + +#: web/errors.py:58 +msgid "Maximum services limit reached. Please, contact administrator" +msgstr "Maximale Service Limit erreicht. Bitte kontaktieren Sie administrator" + +#: web/errors.py:59 +msgid "You need to enable cookies to let this application work" +msgstr "Sie müssen Cookies, diese Anwendung arbeiten lassen aktivieren" + +#: web/errors.py:60 +msgid "User service not found" +msgstr "Benutzer-Dienst nicht gefunden" + +#: web/forms/LoginForm.py:61 +msgid "Authenticator" +msgstr "Authentifikator" + +#: xmlrpc/auths/AdminAuth.py:91 +msgid "Credentials no longer valid" +msgstr "Anmeldeinformationen nicht mehr gültig" + +#: xmlrpc/auths/AdminAuth.py:125 +msgid "Administration" +msgstr "Verwaltung" + +#: xmlrpc/auths/AdminAuth.py:140 +msgid "Invalid credentials" +msgstr "Ungültiger Anmeldeinformationen" + +#: xmlrpc/auths/AdminAuth.py:145 +msgid "Invalid authenticator" +msgstr "Ungültige Echtheitsbestätigung" + +#: xmlrpc/auths/Authenticators.py:104 +msgid "Authenticator does not exists" +msgstr "Authentifikator ist nicht vorhanden" + +#: xmlrpc/auths/Authenticators.py:158 xmlrpc/osmanagers/OSManagers.py:115 +#: xmlrpc/services/ServiceProviders.py:115 xmlrpc/services/Services.py:159 +#: xmlrpc/transports/Networks.py:86 xmlrpc/transports/Networks.py:97 +#, python-format +msgid "Name %s already exists" +msgstr "Name %s existiert bereits" + +#: xmlrpc/auths/Authenticators.py:229 +msgid "Authenticator do not supports search" +msgstr "Authentifikator ist nicht vorhanden" + +#: xmlrpc/auths/Authenticators.py:235 +msgid "Specified authenticator do not exists anymore. Please, reload gui" +msgstr "" +"Angegebenen Authentifikator tun ist nicht mehr vorhanden. Bitte laden gui" + +#: xmlrpc/auths/Authenticators.py:239 +msgid "BUG: Reached a point that should never have been reached!!!" +msgstr "BUG: Erreicht einen Punkt, der nie zustande gekommen sind, sollten!!!" + +#: xmlrpc/osmanagers/OSManagers.py:129 +msgid "This os mnager is being used by deployed services" +msgstr "Diese os Mnager ist von bereitgestellten Dienste verwendet wird" + +#: xmlrpc/osmanagers/OSManagers.py:145 +msgid "There is deployed services using this os manager" +msgstr "Es gibt bereitgestellten Dienste mit dieser os-manager" + +#: xmlrpc/osmanagers/OSManagers.py:147 +msgid "Can't find os manager" +msgstr "Nicht os Manager gefunden" + +#: xmlrpc/services/DeployedServices.py:52 +msgid "Unknown" +msgstr "Unbekannt" + +#: xmlrpc/services/DeployedServices.py:112 +#: xmlrpc/services/DeployedServices.py:176 +#: xmlrpc/services/DeployedServices.py:195 +#: xmlrpc/services/DeployedServices.py:211 +#: xmlrpc/services/DeployedServices.py:225 +#: xmlrpc/services/DeployedServices.py:252 +#: xmlrpc/services/DeployedServices.py:266 +msgid "Deployed Service does not exists" +msgstr "Bereitgestellten Diensts ist nicht vorhanden" + +#: xmlrpc/services/DeployedServices.py:209 +msgid "Group does not exists" +msgstr "Gruppe existiert nicht" + +#: xmlrpc/services/DeployedServices.py:237 +msgid "Can't find deployed service" +msgstr "Bereitgestellte Dienst nicht gefunden werden." + +#: xmlrpc/services/DeployedServices.py:250 +msgid "Transport does not exists" +msgstr "Verkehr ist nicht vorhanden" + +#: xmlrpc/services/DeployedServices.py:281 +msgid "Deployed service does not exists" +msgstr "Bereitgestellten Diensts ist nicht vorhanden" + +#: xmlrpc/services/ServiceProviders.py:142 +msgid "Can't delete service provider with services associated" +msgstr "Service-Provider kann nicht mit verbundenen Services gelöscht werden" + +#: xmlrpc/services/ServiceProviders.py:145 +msgid "Can't locate the service provider" +msgstr "Der Diensteanbieter kann nicht gefunden werden." + +#: xmlrpc/services/ServiceProviders.py:145 xmlrpc/services/Services.py:189 +#: xmlrpc/transports/Networks.py:70 xmlrpc/transports/Networks.py:78 +#: xmlrpc/transports/Networks.py:95 +msgid "Please, refresh interface" +msgstr "Bitte aktualisieren Sie Schnittstelle" + +#: xmlrpc/services/Services.py:186 +msgid "Can't delete services with deployed services associated" +msgstr "" +"Dienstleistungen mit bereitgestellten Leistungen können nicht gelöscht " +"werden." + +#: xmlrpc/services/Services.py:189 +msgid "Can't locate the service" +msgstr "Der Dienst kann nicht gesucht werden." + +#: xmlrpc/services/UserDeployedServices.py:94 +#: xmlrpc/services/UserDeployedServices.py:110 +msgid "The deployed service is not active" +msgstr "Der bereitgestellte Dienst ist nicht aktiv" + +#: xmlrpc/services/UserDeployedServices.py:97 +#: xmlrpc/services/UserDeployedServices.py:113 +msgid "This service don't allows assignations" +msgstr "Dieser Dienst nicht ermöglicht Forderungsabtretungen" + +#: xmlrpc/services/UserDeployedServices.py:102 +#: xmlrpc/services/UserDeployedServices.py:120 +msgid "Deployed service not found!!! (refresh interface)" +msgstr "Bereitgestellte Dienst nicht gefunden!!! (Aktualisieren Schnittstelle)" + +#: xmlrpc/services/UserDeployedServices.py:122 +msgid "User not found!!! (refresh interface)" +msgstr "Benutzer nicht gefunden!!! (Aktualisieren Schnittstelle)" + +#: xmlrpc/services/UserDeployedServices.py:141 +msgid "No error" +msgstr "Unbekannter Fehler" + +#: xmlrpc/services/UserDeployedServices.py:147 +msgid "User deployed service not found!!!" +msgstr "Benutzer-Dienst nicht gefunden" + +#: xmlrpc/transports/Networks.py:70 +msgid "Can't locate the transport" +msgstr "Den Transport kann nicht gefunden werden." + +#: xmlrpc/transports/Networks.py:78 xmlrpc/transports/Networks.py:95 +msgid "Can't locate the network" +msgstr "Das Netzwerk kann nicht gefunden werden." + +#~ msgid "Base Service" +#~ msgstr "Basisdienst" + +#~ msgid "None" +#~ msgstr "Keine" diff --git a/server/src/uds/locale/es/LC_MESSAGES/django.po b/server/src/uds/locale/es/LC_MESSAGES/django.po new file mode 100644 index 000000000..1e17ea7d3 --- /dev/null +++ b/server/src/uds/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,1531 @@ +# Translations for spanish +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Adolfo Gómez , 2012. +# +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-07-12 21:26+0200\n" +"PO-Revision-Date: 2011-12-21 01:56+0100\n" +"Last-Translator: \n" +"Language-Team: Spanish \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"X-Generator: Lokalize 1.2\n" + +#: auths/ActiveDirectory/Authenticator.py:29 +#: auths/EDirectory/Authenticator.py:60 auths/RegexLdap/Authenticator.py:51 +#: auths/SimpleLDAP/Authenticator.py:49 +#: services/Vmware/ServiceProviderVC.py:28 +msgid "Host" +msgstr "Servidor " + +#: auths/ActiveDirectory/Authenticator.py:29 +#: auths/EDirectory/Authenticator.py:60 auths/RegexLdap/Authenticator.py:51 +#: auths/SimpleLDAP/Authenticator.py:49 +#: services/Vmware/ServiceProviderVC.py:28 +msgid "VMWare VC Server IP or Hostname" +msgstr "IP o nombre DNS del servidor VMWare VC" + +#: auths/ActiveDirectory/Authenticator.py:30 +#: auths/EDirectory/Authenticator.py:62 auths/RegexLdap/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:51 +msgid "Use SSL" +msgstr "Usar SSL" + +#: auths/ActiveDirectory/Authenticator.py:30 +msgid "If checked, will use a ssl connection to Active Directory" +msgstr "Si está activada, utilizará una conexión ssl con Active Directory" + +#: auths/ActiveDirectory/Authenticator.py:31 +#: auths/RegexLdap/Authenticator.py:54 auths/SimpleLDAP/Authenticator.py:52 +msgid "Ldap User" +msgstr "Usuario LDAP" + +#: auths/ActiveDirectory/Authenticator.py:31 +msgid "" +"Username with read privileges on the base selected (use USER@DOMAIN.DOM form " +"for this)" +msgstr "" +"Usuario con derechos de lectura en la base seleccionada (utilice la forma " +"USUARIO@DOMINIO.DOM para este elemento)" + +#: auths/ActiveDirectory/Authenticator.py:32 +#: auths/ActiveDirectory/Authenticator.py:50 +#: auths/EDirectory/Authenticator.py:64 auths/RegexLdap/Authenticator.py:55 +#: auths/RegexLdap/Authenticator.py:78 auths/SimpleLDAP/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:77 +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:31 +#: services/Vmware/ServiceProviderVC.py:31 transports/NX/NXTransport.py:62 +#: transports/RDP/RDPTransport.py:38 transports/RDP/TSRDPTransport.py:42 +#: transports/RGS/RGSTransport.py:43 transports/RGS/TRGSTransport.py:48 +#: transports/TSNX/TSNXTransport.py:67 web/forms/LoginForm.py:60 +msgid "Password" +msgstr "Contraseña" + +#: auths/ActiveDirectory/Authenticator.py:32 +#: auths/EDirectory/Authenticator.py:64 auths/RegexLdap/Authenticator.py:55 +#: auths/SimpleLDAP/Authenticator.py:53 +msgid "Password of the ldap user" +msgstr "Contraseña del usuario del ldap" + +#: auths/ActiveDirectory/Authenticator.py:33 +#: auths/EDirectory/Authenticator.py:65 auths/RegexLdap/Authenticator.py:56 +#: auths/SimpleLDAP/Authenticator.py:54 +#: services/Vmware/ServiceProviderVC.py:32 +msgid "Timeout" +msgstr "Espera " + +#: auths/ActiveDirectory/Authenticator.py:33 +#: auths/EDirectory/Authenticator.py:65 auths/RegexLdap/Authenticator.py:56 +#: auths/SimpleLDAP/Authenticator.py:54 +msgid "Timeout in seconds of connection to LDAP" +msgstr "Tiempo de espera en segundos" + +#: auths/ActiveDirectory/Authenticator.py:35 +msgid "Active Directory Authenticator" +msgstr "Autenticador Active Directory" + +#: auths/ActiveDirectory/Authenticator.py:37 +#, fuzzy +msgid "Authenticate against Active Directory" +msgstr "Autenticador Active Directory" + +#: auths/ActiveDirectory/Authenticator.py:46 +#: auths/EDirectory/Authenticator.py:77 auths/RegexLdap/Authenticator.py:74 +#: auths/SimpleLDAP/Authenticator.py:73 +#: services/Vmware/ServiceProviderVC.py:30 transports/NX/NXTransport.py:61 +#: transports/RDP/RDPTransport.py:37 transports/RDP/TSRDPTransport.py:41 +#: transports/RGS/RGSTransport.py:42 transports/RGS/TRGSTransport.py:47 +#: transports/TSNX/TSNXTransport.py:66 web/forms/LoginForm.py:59 +msgid "Username" +msgstr "Usuario" + +#: auths/ActiveDirectory/Authenticator.py:48 +#: auths/EDirectory/Authenticator.py:79 auths/RegexLdap/Authenticator.py:76 +#: auths/SimpleLDAP/Authenticator.py:75 +msgid "Group" +msgstr "Grupo" + +#: auths/ActiveDirectory/Authenticator.py:61 +#: auths/ActiveDirectory/Authenticator.py:395 +msgid "Must specify the username in the form USERNAME@DOMAIN.DOM" +msgstr "" +"Debe especificar el nombre de usuario en la forma NOMBREUSUARIO@DOMINIO.DOM" + +#: auths/ActiveDirectory/Authenticator.py:127 +#: auths/EDirectory/Authenticator.py:123 auths/RegexLdap/Authenticator.py:157 +#: auths/SimpleLDAP/Authenticator.py:158 +msgid "Ldap connection error: " +msgstr "Error de conexión al ldap: " + +#: auths/ActiveDirectory/Authenticator.py:299 +#: auths/ActiveDirectory/Authenticator.py:345 +#: auths/EDirectory/Authenticator.py:243 auths/EDirectory/Authenticator.py:286 +#: auths/RegexLdap/Authenticator.py:244 auths/RegexLdap/Authenticator.py:287 +#: auths/SimpleLDAP/Authenticator.py:268 auths/SimpleLDAP/Authenticator.py:312 +msgid "Username not found" +msgstr "Nombre de usuario no hallado" + +#: auths/ActiveDirectory/Authenticator.py:332 +#: auths/SimpleLDAP/Authenticator.py:301 +msgid "Group not found" +msgstr "Grupo no hallado" + +#: auths/ActiveDirectory/Authenticator.py:364 +#: auths/ActiveDirectory/Authenticator.py:381 +#: auths/EDirectory/Authenticator.py:303 auths/RegexLdap/Authenticator.py:305 +#: auths/SimpleLDAP/Authenticator.py:327 auths/SimpleLDAP/Authenticator.py:341 +msgid "Too many results, be more specific" +msgstr "Demasiados resultados, sea más específico" + +#: auths/ActiveDirectory/Authenticator.py:404 +msgid "Domain seems to be incorrect, please check it" +msgstr "El dominio parece ser incorrecto, por favor, compruebelo" + +#: auths/ActiveDirectory/Authenticator.py:409 +msgid "Ldap does not seem an Active Directory (do not have user objects)" +msgstr "" +"El ldap indicado no parece un Active Directory (no tiene objetos de usuario)" + +#: auths/ActiveDirectory/Authenticator.py:417 +msgid "Ldap does not seem an Active Directory (no not have group objects)" +msgstr "" +"El ldap indicado no parece un Active Directory (no no tienen objetos de " +"grupo)" + +#: auths/ActiveDirectory/Authenticator.py:425 +msgid "" +"Ldap does not seem an Active Directory (do not have any user nor groups)" +msgstr "" +"El ldap indicado no parece un Active Directory (no tiene ningún usuario ni " +"grupo)" + +#: auths/ActiveDirectory/Authenticator.py:430 +#: auths/EDirectory/Authenticator.py:358 auths/RegexLdap/Authenticator.py:380 +#: auths/SimpleLDAP/Authenticator.py:422 +msgid "Connection params seem correct, test was succesfully executed" +msgstr "" +"Los parámetros de conexión parecen correctos, la prueba fue ejecutado con " +"exito" + +#: auths/EDirectory/Authenticator.py:61 auths/RegexLdap/Authenticator.py:52 +#: auths/SimpleLDAP/Authenticator.py:50 +#: services/Vmware/ServiceProviderVC.py:29 +msgid "Port" +msgstr "Puerto" + +#: auths/EDirectory/Authenticator.py:61 auths/RegexLdap/Authenticator.py:52 +#: auths/SimpleLDAP/Authenticator.py:50 +msgid "Ldap port (389 for non ssl, 636 for ssl normally" +msgstr "Puerto LDAP (389 para no SSL, 636 para ssl normalmente)" + +#: auths/EDirectory/Authenticator.py:62 auths/RegexLdap/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:51 +msgid "" +"If checked, will use a ssl connection to ldap (if port is 389, will use in " +"fact port 636)" +msgstr "" +"Si está activada, utilizará una conexión ssl con ldap (si el puerto es 389, " +"utilizará de hecho el Puerto 636)" + +#: auths/EDirectory/Authenticator.py:63 +#, fuzzy +msgid "Admin user" +msgstr "Admin" + +#: auths/EDirectory/Authenticator.py:63 +#, fuzzy +msgid "Username with read privileges on the eDirectory" +msgstr "Usuario con privilegios de lectura en la base elegida" + +#: auths/EDirectory/Authenticator.py:67 +#, fuzzy +msgid "eDirectory Authenticator" +msgstr "Autenticador Active Directory" + +#: auths/EDirectory/Authenticator.py:69 +#, fuzzy +msgid "Authenticate against eDirectory" +msgstr "Autenticador Active Directory" + +#: auths/EDirectory/Authenticator.py:323 auths/RegexLdap/Authenticator.py:325 +#: auths/SimpleLDAP/Authenticator.py:362 +msgid "Ldap search base is incorrect" +msgstr "La base de búsqueda ldap es incorrecta" + +#: auths/EDirectory/Authenticator.py:328 auths/RegexLdap/Authenticator.py:330 +#: auths/SimpleLDAP/Authenticator.py:367 +msgid "Ldap user class seems to be incorrect (no user found by that class)" +msgstr "" +"La clase de usuario de LDAP parece ser incorrecta (ningún usuario encontrado " +"por esa clase)" + +#: auths/EDirectory/Authenticator.py:336 auths/RegexLdap/Authenticator.py:346 +#: auths/SimpleLDAP/Authenticator.py:383 +msgid "" +"Ldap user id attribute seems to be incorrect (no user found by that " +"attribute)" +msgstr "" +"El atributo de id de usuario de ldap parece ser incorrecto (ningún usuario " +"encontrado por atributo)" + +#: auths/EDirectory/Authenticator.py:344 +msgid "Expected group attribute " +msgstr "Atributo de grupo esperado " + +#: auths/EDirectory/Authenticator.py:353 +#, fuzzy +msgid "" +"Ldap user class or user id attr is probably wrong (Ldap is an eDirectory?)" +msgstr "" +"El atributo del id de usuario o la clase de usuario LDAP son probablemente " +"incorrectos(no se puede encontrar ningún usuario con ambas condiciones)" + +#: auths/IP/Authenticator.py:45 auths/IP/Authenticator.py:47 +msgid "IP Authenticator" +msgstr "Autenticador por IP" + +#: auths/IP/Authenticator.py:51 +msgid "IP" +msgstr "IP " + +#: auths/IP/Authenticator.py:52 +msgid "IP Range" +msgstr "Intervalo IP" + +#: auths/IP/Authenticator.py:99 auths/InternalDB/Authenticator.py:94 +msgid "All seems fine in the authenticator." +msgstr "Parace que todo funciona correctamente en el autenticador. " + +#: auths/InternalDB/Authenticator.py:46 +msgid "Internal Database" +msgstr "Base de datos interna" + +#: auths/InternalDB/Authenticator.py:48 +msgid "Internal dabasase authenticator. Doesn't uses external sources" +msgstr "Dabasase interna autenticador. No utiliza fuentes externas" + +#: auths/InternalDB/Authenticator.py:91 +msgid "Internal structures seems ok" +msgstr "Las estructuras internas parecen correctas" + +#: auths/RegexLdap/Authenticator.py:54 auths/SimpleLDAP/Authenticator.py:52 +msgid "Username with read privileges on the base selected" +msgstr "Usuario con privilegios de lectura en la base elegida" + +#: auths/RegexLdap/Authenticator.py:57 auths/SimpleLDAP/Authenticator.py:55 +msgid "Base" +msgstr "Base" + +#: auths/RegexLdap/Authenticator.py:57 auths/SimpleLDAP/Authenticator.py:55 +msgid "Common search base (used for \"users\" and \"groups\"" +msgstr "Base de búsqueda común (utilizado para \"los usuarios\" y \"grupos\"" + +#: auths/RegexLdap/Authenticator.py:58 auths/SimpleLDAP/Authenticator.py:56 +msgid "User class" +msgstr "Clase de usuario" + +#: auths/RegexLdap/Authenticator.py:58 auths/SimpleLDAP/Authenticator.py:56 +msgid "Class for LDAP users (normally posixAccount)" +msgstr "Clase para los usuarios de LDAP (normalmente posixAccount)" + +#: auths/RegexLdap/Authenticator.py:59 auths/SimpleLDAP/Authenticator.py:57 +msgid "User Id Attr" +msgstr "Attr. de Id de usuario" + +#: auths/RegexLdap/Authenticator.py:59 auths/SimpleLDAP/Authenticator.py:57 +msgid "Attribute that contains the user id" +msgstr "Atributo que contiene el identificador de usuario" + +#: auths/RegexLdap/Authenticator.py:60 auths/SimpleLDAP/Authenticator.py:58 +msgid "User Name Attr" +msgstr "Attr. de nombre usu." + +#: auths/RegexLdap/Authenticator.py:60 auths/SimpleLDAP/Authenticator.py:58 +msgid "Attributes that contains the user name (list of comma separated values)" +msgstr "" +"Atributos que contienen el nombre de usuario (lista de valores separados por " +"comas)" + +#: auths/RegexLdap/Authenticator.py:61 +msgid "Group Name Attr" +msgstr "Atr. de nombre de Grupo" + +#: auths/RegexLdap/Authenticator.py:61 +msgid "Attribute that contains the group name" +msgstr "Atributo que contiene el nombre del grupo" + +#: auths/RegexLdap/Authenticator.py:62 +msgid "Regular Exp. for groups" +msgstr "EXP. regular para grupos" + +#: auths/RegexLdap/Authenticator.py:62 +msgid "Regular Expression to extract the group name" +msgstr "Expresión regular para extraer el nombre del grupo" + +#: auths/RegexLdap/Authenticator.py:64 +msgid "Regex LDAP Authenticator" +msgstr "Autenticador Regex LDAP" + +#: auths/RegexLdap/Authenticator.py:66 +msgid "Regular Expressions LDAP authenticator" +msgstr "Autenticador LDAP de expresiones regulares" + +#: auths/RegexLdap/Authenticator.py:338 auths/SimpleLDAP/Authenticator.py:375 +msgid "Ldap group class seems to be incorrect (no group found by that class)" +msgstr "" +"La clase de grupo LDAP parece ser incorrecta (ningún grupo encontrado por " +"esa clase)" + +#: auths/RegexLdap/Authenticator.py:356 auths/SimpleLDAP/Authenticator.py:391 +msgid "" +"Ldap group id attribute seems to be incorrect (no group found by that " +"attribute)" +msgstr "" +"Atributo de id de grupo ldap parece ser incorrecto (ningún grupo encontrado " +"por atributo)" + +#: auths/RegexLdap/Authenticator.py:365 auths/SimpleLDAP/Authenticator.py:400 +msgid "" +"Ldap user class or user id attr is probably wrong (can't find any user with " +"both conditions)" +msgstr "" +"El atributo del id de usuario o la clase de usuario LDAP son probablemente " +"incorrectos(no se puede encontrar ningún usuario con ambas condiciones)" + +#: auths/SimpleLDAP/Authenticator.py:59 +msgid "Group class" +msgstr "Clase de grupo" + +#: auths/SimpleLDAP/Authenticator.py:59 +msgid "Class for LDAP groups (normally poxisGroup)" +msgstr "Clase de grupos LDAP (normalmente poxisGroup)" + +#: auths/SimpleLDAP/Authenticator.py:60 +msgid "Group Id Attr" +msgstr "Atr. id de Grupo" + +#: auths/SimpleLDAP/Authenticator.py:60 +msgid "Attribute that contains the group id" +msgstr "Atributo que contiene el id de grupo" + +#: auths/SimpleLDAP/Authenticator.py:61 +msgid "Group membership attr" +msgstr "Atr. de pertenencia de grupo" + +#: auths/SimpleLDAP/Authenticator.py:61 +msgid "Attribute of the group that contains the users belonging to it" +msgstr "Atributo del grupo que contiene los usuarios pertenecientes al mismo" + +#: auths/SimpleLDAP/Authenticator.py:63 +msgid "SimpleLDAP Authenticator" +msgstr "Autenticador LDAP Simple" + +#: auths/SimpleLDAP/Authenticator.py:65 +msgid "Simple LDAP authenticator" +msgstr "Autenticador LDAP Simple" + +#: auths/SimpleLDAP/Authenticator.py:409 +msgid "" +"Ldap group class or group id attr is probably wrong (can't find any group " +"with both conditions)" +msgstr "" +"El atributo del identificador de clase o grupo de grupo LDAP son " +"probablemente incorrectos (no se encuentra ningún grupo con ambas " +"condiciones)" + +#: auths/SimpleLDAP/Authenticator.py:416 +msgid "Can't locate any group with the membership attribute specified" +msgstr "" +"No se puede localizar algún grupo con el atributo de pertenencia especificado" + +#: core/BaseModule.py:196 +msgid "No connection checking method is implemented." +msgstr "No se ha implementado ningun metodo de comprobación de conexión" + +#: core/BaseModule.py:248 +msgid "No check method provided." +msgstr "No se ha implementado ningún método de verificación." + +#: core/managers/PublicationManager.py:156 +msgid "" +"Already publishing. Wait for previous publication to finish and try again" +msgstr "" +"Ya hay una publicación en curso. Espere a la finalización de esta o " +"cancelela e intentelo de nuevo." + +#: core/managers/PublicationManager.py:168 +msgid "Can't cancel non running publication" +msgstr "No se puede cancelar una publicación que no está activa" + +#: core/managers/PublicationManager.py:186 +msgid "Can't unpublish non usable publication" +msgstr "No se puede despublicar una publicación no activa" + +#: core/managers/PublicationManager.py:189 +msgid "Can't unpublish publications with services in process" +msgstr "No se puede despublicar con servicios en proceso" + +#: core/managers/UserPrefsManager.py:254 +msgid "Screen Size" +msgstr "Tamaño de pantalla" + +#: core/managers/UserPrefsManager.py:258 +msgid "Full Screen" +msgstr "Pantalla completa" + +#: core/managers/UserPrefsManager.py:261 +msgid "Screen colors" +msgstr "Colores de pantalla" + +#: core/managers/UserPrefsManager.py:262 +msgid "8 bits" +msgstr "8 bits" + +#: core/managers/UserPrefsManager.py:263 +msgid "16 bits" +msgstr "16 bits" + +#: core/managers/UserPrefsManager.py:264 +msgid "24 bits" +msgstr "24 bits" + +#: core/managers/UserPrefsManager.py:265 +msgid "32 bits" +msgstr "32 bits" + +#: core/managers/UserServiceManager.py:302 +msgid "Can't cancel non running operation" +msgstr "No se puede cancelar una operación que no está en curso" + +#: core/managers/UserServiceManager.py:321 +msgid "Can't remove a non active element" +msgstr "No se puede eliminar un elemento que no está activo" + +#: core/managers/UserServiceManager.py:334 +msgid "Can't remove nor cancel {0} cause its states doesn't allows it" +msgstr "No se puede eliminar o cancelar {0} porque sus estados no lo permiten" + +#: core/osmanagers/BaseOsManager.py:50 +msgid "Base OS Manager" +msgstr "Gestor de OS Base" + +#: core/osmanagers/BaseOsManager.py:52 +msgid "Base Manager" +msgstr "Gestor Base" + +#: core/transports/BaseTransport.py:94 +msgid "Transport empty" +msgstr "Transporte Vacio" + +#: core/util/State.py:59 +msgid "Active" +msgstr "Activo" + +#: core/util/State.py:59 +msgid "Inactive" +msgstr "Inactivo" + +#: core/util/State.py:59 +msgid "Blocked" +msgstr "Bloqueado" + +#: core/util/State.py:59 +msgid "Waiting publication" +msgstr "En espera de publicación" + +#: core/util/State.py:60 +msgid "In preparation" +msgstr "En preparación" + +#: core/util/State.py:60 +msgid "Valid" +msgstr "Valido" + +#: core/util/State.py:61 +msgid "Waiting for removal" +msgstr "Esperando eliminación" + +#: core/util/State.py:61 +msgid "Removing" +msgstr "Eliminandose" + +#: core/util/State.py:61 +msgid "Removed" +msgstr "Eliminado " + +#: core/util/State.py:61 +msgid "Canceled" +msgstr "Cancelado" + +#: core/util/State.py:62 +msgid "Canceling" +msgstr "Cancelando" + +#: core/util/State.py:62 templates/uds/error.html:6 +#: templates/uds/error.html.py:11 +msgid "Error" +msgstr "Error" + +#: core/util/State.py:62 +msgid "Running" +msgstr "En ejecución" + +#: core/util/State.py:62 +msgid "Finished" +msgstr "Finalizado" + +#: core/util/State.py:62 +msgid "Waiting execution" +msgstr "En espera de ejecución" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:44 +msgid "Linux OS Manager" +msgstr "Gestor de S.O. Linux" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:46 +msgid "" +"Os Manager to control linux virtual machines (basically renames machine and " +"notify state)" +msgstr "" +"Gestor de s.o. para controlar maquinas virtuales con linux (basicamente " +"renombra las máquinas y notifica los estados)" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:49 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:40 +msgid "On Logout" +msgstr "Al salir" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:49 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:40 +msgid "What to do when user logout from service" +msgstr "Que acción realizar cuando el usuario abandone el servicio" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:50 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:41 +msgid "Keep service assigned" +msgstr "Mantener el servicio asignado" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:51 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:42 +msgid "Remove service" +msgstr "Eliminar servicio" + +#: osmanagers/LinuxOsManager/__init__.py:43 +msgid "UDS Actor for linux machines (Requires python 2.6 or greater)" +msgstr "Actor para las máquinas Linux (Precisa python 2.6 o mayor)" + +#: osmanagers/NoneOsManager/Manager.py:43 +msgid "None OS Manager" +msgstr "Ningún gestor de OS" + +#: osmanagers/NoneOsManager/Manager.py:45 +msgid "Os Manager with no actions" +msgstr "Gestor de OS que no realiza ninguna acción" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:23 +msgid "Windows Domain OS Manager" +msgstr "Gestor para Windows con dominio" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:25 +msgid "" +"Os Manager to control windows machines with domain. (Basically renames " +"machine)" +msgstr "Gestor de s.o. para controlar maquinas windows con dominio." + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:29 +#: transports/RDP/RDPTransport.py:39 transports/RDP/TSRDPTransport.py:43 +#: transports/RGS/RGSTransport.py:44 transports/RGS/TRGSTransport.py:49 +msgid "Domain" +msgstr "Dominio" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:29 +msgid "Domain to join machines to (better use dns form of domain)" +msgstr "" +"Dominio al que unir las máquinas (es preferible utilizar la forma DNS del " +"dominio)" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:30 +msgid "Account" +msgstr "Cuenta" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:30 +msgid "Account with rights to add machines to domain" +msgstr "Cuenta con derecho para añadir máquinas al dominio" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:31 +msgid "Password of the account" +msgstr "Contraseña de la cuenta" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:32 +msgid "OU" +msgstr "OU" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:32 +msgid "" +"Organizational unit where to add machines in domain (check it before using " +"it)" +msgstr "" +"Unidad organizativa donde crear las máquinas del dominio (compruebelo antes " +"de utilizarlo)" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:40 +msgid "Must provide a domain!!!" +msgstr "Debe indicar un dominio!!!" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:42 +msgid "Must provide an account to add machines to domain!!!" +msgstr "Debe indicar una cuenta para añadir máquinas al dominio!!!" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:44 +msgid "Must provide a password for the account!!!" +msgstr "Debe indicar una contraseña para la cuenta!!!" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:35 +msgid "Windows Basic OS Manager" +msgstr "Gestor de SO Windows Básico" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:37 +msgid "" +"Os Manager to control windows machines without domain. (Basically renames " +"machine)" +msgstr "" +"Gestor de SO para controlar máquinas windows sin dominio. (Basicamente, " +"renombra las máquinas)" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:51 +msgid "Length must be numeric!!" +msgstr "La longitud debe ser numerica!!!" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:53 +msgid "Length must be betwen 1 and six" +msgstr "La longitud debe estar entre 1 y seis" + +#: osmanagers/WindowsOsManager/__init__.py:23 +msgid "" +"UDS Actor for windows machines (Important!! Requires .net framework 3.5 " +"sp1)" +msgstr "" +"Actor para las máquinas Windows (Importante!!! Requiere tener .net " +"framework 3.5 sp1)" + +#: services/PhysicalMachines/IPMachineDeployed.py:56 +msgid "IP " +msgstr "IP " + +#: services/PhysicalMachines/IPMachinesService.py:46 +msgid "List of IPS" +msgstr "Lista de IPS" + +#: services/PhysicalMachines/IPMachinesService.py:49 +msgid "Physical machines accesed by ip" +msgstr "Máquinas físicas de acceso por ip" + +#: services/PhysicalMachines/IPMachinesService.py:51 +msgid "This service provides access to POWERED-ON Machines by ip" +msgstr "Este servicio provee acceso a máquinas ENCENDIDAS por IP" + +#: services/Sample/SampleProvider.py:142 +msgid "Methuselah is not alive!!! :-)" +msgstr "Matusalén no está vivo!!! :-)" + +#: services/Sample/SampleProvider.py:182 +msgid "Nothing tested, but all went fine.." +msgstr "Nada probado, pero todo salió bien..." + +#: services/Sample/SamplePublication.py:199 +msgid "Random integer was 9!!! :-)" +msgstr "Entero aleatorio fue 9!!!!!! :-)" + +#: services/Vmware/Helpers.py:72 +msgid "Local" +msgstr "Local" + +#: services/Vmware/Helpers.py:74 +msgid "Remote" +msgstr "Remoto" + +#: services/Vmware/PublicationVC.py:36 +msgid "Publication" +msgstr "Publicación" + +#: services/Vmware/PublicationVC.py:37 +msgid "UDS Publication for {0} created at {1}" +msgstr "Publicación de UDS para {0} creado en {1}" + +#: services/Vmware/ServiceProviderVC.py:29 +msgid "VMWare VC Server Port (usually 443)" +msgstr "Puerto del servidor VMWare VC (normalmente 443)" + +#: services/Vmware/ServiceProviderVC.py:30 +msgid "User with valid privileges on VC" +msgstr "Usuario con privilegios en VC" + +#: services/Vmware/ServiceProviderVC.py:31 +msgid "Password of the user of the VC" +msgstr "Contraseña del usuario de VC" + +#: services/Vmware/ServiceProviderVC.py:32 +msgid "Timeout in seconds of connection to VC" +msgstr "Timeout en segundos" + +#: services/Vmware/ServiceProviderVC.py:33 +msgid "Macs range" +msgstr "Rango de Macs" + +#: services/Vmware/ServiceProviderVC.py:34 +msgid "Range of valids macs for created machines" +msgstr "Rango válido de macs para las máquinas creadas" + +#: services/Vmware/ServiceProviderVC.py:39 +msgid "VMWare Virtual Center Provider" +msgstr "Proveedor para VMWare Virtual Center" + +#: services/Vmware/ServiceProviderVC.py:41 +msgid "Provides connection to Virtual Center Services" +msgstr "Provee conexión a servicios basados en VMWare Virtual Center" + +#: services/Vmware/ServiceProviderVC.py:111 +msgid "Error testing connection" +msgstr "Error comprobando la conexión" + +#: services/Vmware/ServiceProviderVC.py:114 +msgid "VmwareVC Provider: " +msgstr "Proveedor VmwareVC:" + +#: services/Vmware/ServiceProviderVC.py:120 +msgid "Connection params ok" +msgstr "Parametros de conexión correctos" + +#: services/Vmware/ServiceProviderVC.py:121 +msgid "Connection failed. Check connection params" +msgstr "La conexión ha fallado. Compruebe los parámetros." + +#: services/Vmware/VCLinkedCloneService.py:28 +msgid "Datacenter" +msgstr "Datacenter " + +#: services/Vmware/VCLinkedCloneService.py:34 +msgid "Datacenter containing base machine" +msgstr "Datacenter que contiene la máquina de base" + +#: services/Vmware/VCLinkedCloneService.py:36 +msgid "Network" +msgstr "Red" + +#: services/Vmware/VCLinkedCloneService.py:37 +msgid "" +"If more than 1 interface is found in machine, use one on this network as main" +msgstr "" +"Si hay mas de un interfaz en la máquina virtual, use el que esté en esta red " +"como principal." + +#: services/Vmware/VCLinkedCloneService.py:38 +msgid "Pub. Resource Pool" +msgstr "Pool de despliegue" + +#: services/Vmware/VCLinkedCloneService.py:38 +msgid "Resource Pool where deploy clones" +msgstr "Pool de recursos donde desplegar los clones" + +#: services/Vmware/VCLinkedCloneService.py:39 +msgid "Clones Folder" +msgstr "Carpeta de clones" + +#: services/Vmware/VCLinkedCloneService.py:39 +msgid "Folder where deploy clones" +msgstr "Carpeta donde desplegar los clones" + +#: services/Vmware/VCLinkedCloneService.py:40 +msgid "Resource Pool" +msgstr "Pool de recursos" + +#: services/Vmware/VCLinkedCloneService.py:46 +msgid "Resource Pool containing base machine" +msgstr "Pool de recursos que contiene la máquina de base" + +#: services/Vmware/VCLinkedCloneService.py:48 +msgid "Base Machine" +msgstr "Máquina de base " + +#: services/Vmware/VCLinkedCloneService.py:48 +msgid "Base machine for this service" +msgstr "Máquina de base para este servicio" + +#: services/Vmware/VCLinkedCloneService.py:49 +msgid "Memory (Mb)" +msgstr "Memoria (Mb)" + +#: services/Vmware/VCLinkedCloneService.py:50 +msgid "Memory for machines deployed from this service" +msgstr "Memoria para maquinas desplegadas desde este servicio" + +#: services/Vmware/VCLinkedCloneService.py:51 +msgid "Datastores" +msgstr "Almacenamientos" + +#: services/Vmware/VCLinkedCloneService.py:52 +msgid "Datastores where to put incrementals" +msgstr "Almacenamientos donde colocar los incrementales" + +#: services/Vmware/VCLinkedCloneService.py:53 +msgid "Machine Names" +msgstr "Nombres de máquinas" + +#: services/Vmware/VCLinkedCloneService.py:53 +msgid "Base name for clones from this machine" +msgstr "Nombre base para los clones de la máquina base" + +#: services/Vmware/VCLinkedCloneService.py:54 +msgid "Name Length" +msgstr "Longitud del nombre" + +#: services/Vmware/VCLinkedCloneService.py:55 +msgid "Length of numeric part for the names of this machines (betwen 3 and 6" +msgstr "" +"Longitud de la parte numérica de los nombres de esta maquinaria (entre 3 y 6" + +#: services/Vmware/VCLinkedCloneService.py:62 +msgid "VMWare Linked clone base" +msgstr "Servicio basado en VMWare Linked Clones" + +#: services/Vmware/VCLinkedCloneService.py:64 +msgid "" +"This service provides access to Linked Clones machines on a Virtual Center" +msgstr "Este servicio provee acceso a linked clones sobre Virtual Center" + +#: services/Vmware/VCLinkedCloneService.py:70 +msgid "Number of desired machines to keep running waiting for a user" +msgstr "Número de máquinas a manatener en ejecución esperando a un usuario" + +#: services/Vmware/VCLinkedCloneService.py:72 +msgid "Number of desired machines to keep suspended waiting for use" +msgstr "Número de maquinas a mantener suspendidas esperando asignación" + +#: services/Vmware/VCLinkedCloneService.py:93 +msgid "The length of basename plus length must not be greater than 15" +msgstr "La longitud de basename más longitud no debe ser superior a 15" + +#: services/Vmware/VCLinkedCloneService.py:95 +msgid "The machine name can't be only numbers" +msgstr "El nombre del equipo no puede ser sólo números" + +#: templates/404.html:4 templates/404.html.py:7 +msgid "Page not found" +msgstr "Página no encontrada" + +#: templates/404.html:9 +msgid "Sorry, but the requested page could not be found." +msgstr "Lo sentimos, pero no se encontró la página solicitada." + +#: templates/uds/base.html:7 +msgid "UDS" +msgstr "UDS" + +#: templates/uds/downloads.html:8 templates/uds/snippets/admin_user.html:7 +msgid "Downloads" +msgstr "Descargas" + +#: templates/uds/downloads.html:11 +msgid "" +"This page contains a list of downloadables provided by different modules" +msgstr "" +"Esta página contiene una lista de descargas proporcionadas por diferentes " +"módulos" + +#: templates/uds/index.html:51 +msgid "Services" +msgstr "Servicios" + +#: templates/uds/index.html:70 +msgid "Java not found" +msgstr "Java no encontrado" + +#: templates/uds/index.html:71 +msgid "" +"Java is not available on your browser, and the selected transport needs it." +msgstr "" +"Java no está disponible en el navegador, y el transporte seleccionado " +"precisa de el." + +#: templates/uds/index.html:72 +msgid "Please, install latest version from" +msgstr "Instale la versión mas reciente desde el" + +#: templates/uds/index.html:72 +msgid "Java website" +msgstr "Sitio Web de Java" + +#: templates/uds/index.html:72 +msgid "and restart browser" +msgstr "y reinicie el navegador" + +#: templates/uds/index.html:78 +msgid "Ip" +msgstr "IP" + +#: templates/uds/index.html:79 +msgid "Networks" +msgstr "Redes" + +#: templates/uds/index.html:80 +msgid "Transports" +msgstr "Transportes" + +#: templates/uds/internal_page.html:28 +msgid "User" +msgstr "Usuario" + +#: templates/uds/internal_page.html:34 templates/uds/prefs.html:12 +msgid "Preferences" +msgstr "Preferencias" + +#: templates/uds/internal_page.html:40 +msgid "Log out" +msgstr "Desconectar" + +#: templates/uds/login.html:6 +msgid "Login to UDS" +msgstr "Acceder a UDS" + +#: templates/uds/login.html:78 +msgid "Login" +msgstr "Acceder" + +#: templates/uds/login.html:83 +msgid "Login data" +msgstr "Datos de acceso" + +#: templates/uds/login.html:86 +msgid "Enter" +msgstr "Entrar" + +#: templates/uds/login.html:93 +msgid "Back to login" +msgstr "Volver a iniciar sesión" + +#: templates/uds/prefs.html:6 +msgid "UDS User Preferences" +msgstr "UDS Preferencias de usuario " + +#: templates/uds/prefs.html:16 +msgid "Save Preferences" +msgstr "Guardar Preferencias" + +#: templates/uds/service_not_ready.html:6 +msgid "Service not ready at this moment. Please, try again in a while." +msgstr "" +"El servicio no está disponible en estos momentos. Por favor, intentelo de " +"nuevo pasado unos instantes." + +#: templates/uds/snippets/admin_user.html:4 +msgid "Admin" +msgstr "Admin" + +#: templates/uds/snippets/back_to_list.html:3 +msgid "Back to services list" +msgstr "Volver a la lista de servicios" + +#: templates/uds/snippets/lang.html:9 +msgid "Language" +msgstr "Idioma" + +#: transports/NX/NXTransport.py:54 +msgid "NX Transport (direct)" +msgstr "Transporte NX (directo)" + +#: transports/NX/NXTransport.py:56 +msgid "NX Transport for direct connection" +msgstr "Transporte NX para conexión directa" + +#: transports/NX/NXTransport.py:60 transports/RDP/RDPTransport.py:36 +#: transports/RDP/TSRDPTransport.py:40 transports/RGS/RGSTransport.py:41 +#: transports/RGS/TRGSTransport.py:46 transports/TSNX/TSNXTransport.py:65 +msgid "Empty creds" +msgstr "Sin credenciales" + +#: transports/NX/NXTransport.py:60 transports/RDP/RDPTransport.py:36 +#: transports/RDP/TSRDPTransport.py:40 transports/RGS/RGSTransport.py:41 +#: transports/RGS/TRGSTransport.py:46 transports/TSNX/TSNXTransport.py:65 +msgid "If checked, the credentials used to connect will be emtpy" +msgstr "" +"Si está activada, las credenciales utilizadas para conectar estarán vacías" + +#: transports/NX/NXTransport.py:61 transports/RDP/RDPTransport.py:37 +#: transports/RDP/TSRDPTransport.py:41 transports/RGS/RGSTransport.py:42 +#: transports/RGS/TRGSTransport.py:47 transports/TSNX/TSNXTransport.py:66 +msgid "If not empty, this username will be always used as credential" +msgstr "" +"Si no está vacio, este nombre de usuario será utilizado como credencial fija" + +#: transports/NX/NXTransport.py:62 transports/RDP/RDPTransport.py:38 +#: transports/RDP/TSRDPTransport.py:42 transports/RGS/RGSTransport.py:43 +#: transports/RGS/TRGSTransport.py:48 transports/TSNX/TSNXTransport.py:67 +msgid "If not empty, this password will be always used as credential" +msgstr "Si no está vacio, este password será utiizado como credencial fija" + +#: transports/NX/NXTransport.py:63 transports/TSNX/TSNXTransport.py:68 +msgid "Listen port" +msgstr "Puerto de escucha" + +#: transports/NX/NXTransport.py:63 transports/TSNX/TSNXTransport.py:68 +msgid "Listening port of NX (ssh) at client machine" +msgstr "Puerto de escucha de NX (ssh) en el equipo cliente" + +#: transports/NX/NXTransport.py:64 transports/TSNX/TSNXTransport.py:69 +msgid "Connection" +msgstr "Conexión" + +#: transports/NX/NXTransport.py:64 transports/TSNX/TSNXTransport.py:69 +msgid "Connection speed for this transport (quality)" +msgstr "Velocidad de conexión de este transporte (calidad)" + +#: transports/NX/NXTransport.py:71 transports/TSNX/TSNXTransport.py:76 +msgid "Session" +msgstr "Sesiones" + +#: transports/NX/NXTransport.py:71 transports/TSNX/TSNXTransport.py:76 +msgid "Desktop session" +msgstr "Sesión de escritorio" + +#: transports/NX/NXTransport.py:76 transports/TSNX/TSNXTransport.py:81 +msgid "Disk Cache" +msgstr "Caché de disco" + +#: transports/NX/NXTransport.py:76 transports/TSNX/TSNXTransport.py:81 +msgid "Cache size en Mb stored at disk" +msgstr "Tamaño de ca Caché en MB almacenada en disco" + +#: transports/NX/NXTransport.py:84 transports/TSNX/TSNXTransport.py:89 +msgid "Memory Cache" +msgstr "Memoria Caché" + +#: transports/NX/NXTransport.py:84 transports/TSNX/TSNXTransport.py:89 +msgid "Cache size en Mb keept at memory" +msgstr "Tamaño del Caché en Mb a mantener en memoria" + +#: transports/NX/__init__.py:42 transports/TSNX/__init__.py:42 +msgid "NX Protocol" +msgstr "Protocolo NX" + +#: transports/NX/__init__.py:48 +msgid "UDS Actor connector for NX (requires nomachine packages)" +msgstr "Conector de UDS Actor para NX (requiere nomachine paquetes)" + +#: transports/NX/web.py:74 +msgid "" +"In order to use this transport, you need to install first Nomachine Nx " +"Client version 3.5.x" +msgstr "" +"Para poder utilizar este transporte, necesita instalar primera Nomachine Nx " +"Cliente versión 3.5" + +#: transports/NX/web.py:75 +msgid "you can obtain it for your platform from" +msgstr "Usted puede obtener para su plataforma de" + +#: transports/NX/web.py:75 +msgid "nochamine web site" +msgstr "Sitio Web de Nomachine" + +#: transports/RDP/RDPTransport.py:30 +msgid "RDP Transport (direct)" +msgstr "Transporte RDP (directo)" + +#: transports/RDP/RDPTransport.py:32 +msgid "RDP Transport for direct connection" +msgstr "Transporte RDP para conexión directa" + +#: transports/RDP/RDPTransport.py:39 transports/RDP/TSRDPTransport.py:43 +#: transports/RGS/RGSTransport.py:44 transports/RGS/TRGSTransport.py:49 +msgid "" +"If not empty, this domain will be always used as credential (used as DOMAIN" +"\\user)" +msgstr "" +"Si no está vacio, este domínio será usado como parte de las credenciales del " +"usuario (usado como DOMAIN\\user)" + +#: transports/RDP/RDPTransport.py:40 transports/RDP/TSRDPTransport.py:44 +msgid "Allow Smartcards" +msgstr "Permitir tarjetas inteligentes" + +#: transports/RDP/RDPTransport.py:40 transports/RDP/TSRDPTransport.py:44 +msgid "If checked, this transport will allow the use of smartcards" +msgstr "" +"Si está marcado, este transporte permitirá el uso de tarjetas inteligentes" + +#: transports/RDP/RDPTransport.py:41 transports/RDP/TSRDPTransport.py:45 +msgid "Allow Printers" +msgstr "Permitir impresoras" + +#: transports/RDP/RDPTransport.py:41 transports/RDP/TSRDPTransport.py:45 +msgid "If checked, this transport will allow the use of user printers" +msgstr "" +"Si está marcado, este transporte permitirá el uso de impresoras remotas" + +#: transports/RDP/RDPTransport.py:42 transports/RDP/TSRDPTransport.py:46 +msgid "Allow Drives" +msgstr "Permitir unidades" + +#: transports/RDP/RDPTransport.py:42 transports/RDP/TSRDPTransport.py:46 +msgid "If checked, this transport will allow the use of user drives" +msgstr "" +"Si está marcado, este transporte permitirá la redireccion de las unidades " +"locales a la máquina remota" + +#: transports/RDP/RDPTransport.py:43 transports/RDP/TSRDPTransport.py:47 +msgid "Allow Serials" +msgstr "Permitir series" + +#: transports/RDP/RDPTransport.py:43 transports/RDP/TSRDPTransport.py:47 +msgid "If checked, this transport will allow the use of user serial ports" +msgstr "" +"Si está marcado, este transporte permitirá la redirección de puertos serie" + +#: transports/RDP/TSRDPTransport.py:31 +msgid "RDP Transport (tunneled)" +msgstr "Transporte RDP (vía túnel)" + +#: transports/RDP/TSRDPTransport.py:33 +msgid "RDP Transport for tunneled connection" +msgstr "Transporte RDP para conexión vía túnel" + +#: transports/RDP/TSRDPTransport.py:37 transports/RGS/TRGSTransport.py:43 +#: transports/TSNX/TSNXTransport.py:62 +msgid "Tunnel server" +msgstr "Servidor de túnel" + +#: transports/RDP/TSRDPTransport.py:37 transports/RGS/TRGSTransport.py:43 +#: transports/TSNX/TSNXTransport.py:62 +msgid "" +"IP or Hostname of tunnel server send to client device (\"public\" ip) and " +"port. (use HOST:PORT format)" +msgstr "" +"IP o nombre de host del servidor de túnel enviar a dispositivo de cliente " +"(ip \"pública\") y puerto. (utilice el formato HOST: puerto)" + +#: transports/RDP/TSRDPTransport.py:38 transports/RGS/TRGSTransport.py:44 +#: transports/TSNX/TSNXTransport.py:63 +msgid "Tunnel host check" +msgstr "Verificación de host de túnel" + +#: transports/RDP/TSRDPTransport.py:38 transports/RGS/TRGSTransport.py:44 +#: transports/TSNX/TSNXTransport.py:63 +msgid "" +"If not empty, this server will be used to check if service is running before " +"assigning it to user. (use HOST:PORT format)" +msgstr "" +"Si no vacía, este servidor se utilizará para comprobar si el servicio se " +"ejecuta antes de asignarle al usuario. (utilice el formato HOST: puerto)" + +#: transports/RDP/__init__.py:20 +msgid "Remote Desktop Protocol" +msgstr "Protocolo de Escritorio remoto (RDP)" + +#: transports/RDP/web.py:83 +msgid "In order to use this service, you should first install CoRD." +msgstr "Para poder utilizar este servicio, primero debe instalar CoRD." + +#: transports/RDP/web.py:84 transports/RGS/web.py:82 +msgid "You can obtain it from" +msgstr "Puede obtenerlo de" + +#: transports/RDP/web.py:84 +msgid "CoRD Website" +msgstr "Sitio Web de CoRD" + +#: transports/RGS/RGSTransport.py:34 +#, fuzzy +msgid "RGS Transport (direct)" +msgstr "Transporte RDP (directo)" + +#: transports/RGS/RGSTransport.py:36 +#, fuzzy +msgid "RGS Transport for direct connection" +msgstr "Transporte RDP para conexión directa" + +#: transports/RGS/RGSTransport.py:45 transports/RGS/TRGSTransport.py:50 +msgid "Image quality" +msgstr "Calidad de imagen" + +#: transports/RGS/RGSTransport.py:46 transports/RGS/TRGSTransport.py:51 +msgid "Quality of image codec (0-100)" +msgstr "Calidad del códec de imagen (0-100)" + +#: transports/RGS/RGSTransport.py:47 transports/RGS/TRGSTransport.py:52 +msgid "Adjustable Quality" +msgstr "Calidad ajustable" + +#: transports/RGS/RGSTransport.py:48 transports/RGS/TRGSTransport.py:53 +msgid "If checked, the image quality will be adjustable with bandwidth" +msgstr "" +"Si está activada, la calidad de imagen será ajustable con ancho de banda" + +#: transports/RGS/RGSTransport.py:49 transports/RGS/TRGSTransport.py:54 +msgid "Min. Adjustable Quality" +msgstr "Mín. calidad ajustable" + +#: transports/RGS/RGSTransport.py:50 transports/RGS/TRGSTransport.py:55 +msgid "" +"The lowest image quality applied to images to maintain the minimum update " +"rate." +msgstr "" +"La menor calidad de imagen aplicada a las imágenes para mantener la " +"actualización mínima tasa." + +#: transports/RGS/RGSTransport.py:51 transports/RGS/TRGSTransport.py:56 +msgid "Adjustable Frame Rate" +msgstr "Ajustable velocidad de fotogramas" + +#: transports/RGS/RGSTransport.py:52 transports/RGS/TRGSTransport.py:57 +msgid "Update rate threshold to begin adjusting image quality" +msgstr "" +"Umbral de velocidad de actualización para comenzar el ajuste de calidad de " +"imagen" + +#: transports/RGS/RGSTransport.py:53 transports/RGS/TRGSTransport.py:58 +msgid "Match Local Resolution" +msgstr "Resolución Local de partido" + +#: transports/RGS/RGSTransport.py:54 transports/RGS/TRGSTransport.py:59 +msgid "" +"Change the Sender's resolution to match the Receiver's resolution when " +"connecting" +msgstr "" +"Cambiar la resolución del remitente para que coincida con la resolución del " +"receptor cuando conexión" + +#: transports/RGS/RGSTransport.py:55 transports/RGS/TRGSTransport.py:60 +msgid "Redirect USB" +msgstr "Redirección USB" + +#: transports/RGS/RGSTransport.py:56 transports/RGS/TRGSTransport.py:61 +msgid "If checked, the USB will be redirected." +msgstr "Si está activada, se redirigirá el USB." + +#: transports/RGS/RGSTransport.py:57 transports/RGS/TRGSTransport.py:62 +msgid "Redirect Audio" +msgstr "Redirección de Audio" + +#: transports/RGS/RGSTransport.py:58 transports/RGS/TRGSTransport.py:63 +#, fuzzy +msgid "If checked, the Audio will be redirected." +msgstr "" +"Si está activada, las credenciales utilizadas para conectar estarán vacías" + +#: transports/RGS/RGSTransport.py:59 transports/RGS/TRGSTransport.py:64 +msgid "Redirect Mic" +msgstr "Redirigir Mic" + +#: transports/RGS/RGSTransport.py:60 transports/RGS/TRGSTransport.py:65 +#, fuzzy +msgid "If checked, the Mic will be redirected." +msgstr "" +"Si está activada, las credenciales utilizadas para conectar estarán vacías" + +#: transports/RGS/TRGSTransport.py:36 +#, fuzzy +msgid "RGS Transport (tunneled)" +msgstr "Transporte RDP (vía túnel)" + +#: transports/RGS/TRGSTransport.py:38 +#, fuzzy +msgid "RGS Transport for tunneled connection" +msgstr "Transporte RDP para conexión vía túnel" + +#: transports/RGS/web.py:81 +#, fuzzy +msgid "In order to use this service, you should first install RGS Receiver." +msgstr "Para poder utilizar este servicio, primero debe instalar CoRD." + +#: transports/RGS/web.py:82 +#, fuzzy +msgid "HP Website" +msgstr "Sitio Web de CoRD" + +#: transports/TSNX/TSNXTransport.py:55 +msgid "NX Transport (tunneled)" +msgstr "Transporte NX (vía túnel)" + +#: transports/TSNX/TSNXTransport.py:57 +msgid "NX Transport for tunneled connection" +msgstr "Transporte NX para conexión vía túnel" + +#: web/errors.py:53 +msgid "Unknown error" +msgstr "Error desconocido" + +#: web/errors.py:54 +msgid "Transport not found" +msgstr "Transporte no hallado" + +#: web/errors.py:55 +msgid "Service not found" +msgstr "Servicio no hallado" + +#: web/errors.py:56 xmlrpc/auths/AdminAuth.py:147 +msgid "Access denied" +msgstr "Acceso denegado" + +#: web/errors.py:57 +msgid "" +"Invalid service. The service is not available at this moment. Please, try " +"later" +msgstr "" +"Servicio invalido. El servicio no está disponible en estos momentos. Por " +"favor, intentelo de nuevo pasado unos instantes." + +#: web/errors.py:58 +msgid "Maximum services limit reached. Please, contact administrator" +msgstr "" +"Número máximo de servicios alcanzado. Por favor, contacte con su " +"administrador." + +#: web/errors.py:59 +msgid "You need to enable cookies to let this application work" +msgstr "Necesita habilitar los cookies para permitir funcionar esta aplicación" + +#: web/errors.py:60 +msgid "User service not found" +msgstr "Servicio de usuario no hallado" + +#: web/forms/LoginForm.py:61 +msgid "Authenticator" +msgstr "Autenticador" + +#: xmlrpc/auths/AdminAuth.py:91 +msgid "Credentials no longer valid" +msgstr "Las credenciales ya no son válidas" + +#: xmlrpc/auths/AdminAuth.py:125 +msgid "Administration" +msgstr "Administración" + +#: xmlrpc/auths/AdminAuth.py:140 +msgid "Invalid credentials" +msgstr "Credenciales Invalidas" + +#: xmlrpc/auths/AdminAuth.py:145 +msgid "Invalid authenticator" +msgstr "Autenticador Invalido" + +#: xmlrpc/auths/Authenticators.py:104 +msgid "Authenticator does not exists" +msgstr "El autenticador no existe" + +#: xmlrpc/auths/Authenticators.py:158 xmlrpc/osmanagers/OSManagers.py:115 +#: xmlrpc/services/ServiceProviders.py:115 xmlrpc/services/Services.py:159 +#: xmlrpc/transports/Networks.py:86 xmlrpc/transports/Networks.py:97 +#, python-format +msgid "Name %s already exists" +msgstr "El nombre %s ya existe" + +#: xmlrpc/auths/Authenticators.py:229 +msgid "Authenticator do not supports search" +msgstr "El autenticador no soporta búsquedas" + +#: xmlrpc/auths/Authenticators.py:235 +msgid "Specified authenticator do not exists anymore. Please, reload gui" +msgstr "" +"Hacer autenticador especificado no existe ya. Por favor, vuelva a cargar la " +"interfaz gráfica de usuario" + +#: xmlrpc/auths/Authenticators.py:239 +msgid "BUG: Reached a point that should never have been reached!!!" +msgstr "BUG: Llegado a un punto que no debe nunca han alcanzado!!!" + +#: xmlrpc/osmanagers/OSManagers.py:129 +msgid "This os mnager is being used by deployed services" +msgstr "Este OS Manager está siendo utilizado por servicios desplegados" + +#: xmlrpc/osmanagers/OSManagers.py:145 +msgid "There is deployed services using this os manager" +msgstr "Existen servicios desplegados que utilizan este gestor de SO" + +#: xmlrpc/osmanagers/OSManagers.py:147 +msgid "Can't find os manager" +msgstr "No se puede hallar el gestor de SO" + +#: xmlrpc/services/DeployedServices.py:52 +msgid "Unknown" +msgstr "Desconocido" + +#: xmlrpc/services/DeployedServices.py:112 +#: xmlrpc/services/DeployedServices.py:176 +#: xmlrpc/services/DeployedServices.py:195 +#: xmlrpc/services/DeployedServices.py:211 +#: xmlrpc/services/DeployedServices.py:225 +#: xmlrpc/services/DeployedServices.py:252 +#: xmlrpc/services/DeployedServices.py:266 +msgid "Deployed Service does not exists" +msgstr "El servicio desplegado no existe" + +#: xmlrpc/services/DeployedServices.py:209 +msgid "Group does not exists" +msgstr "El grupo no existe" + +#: xmlrpc/services/DeployedServices.py:237 +msgid "Can't find deployed service" +msgstr "No puedo hallar el servicio desplegado" + +#: xmlrpc/services/DeployedServices.py:250 +msgid "Transport does not exists" +msgstr "El transporte no existe" + +#: xmlrpc/services/DeployedServices.py:281 +msgid "Deployed service does not exists" +msgstr "El servicio desplegado no existe" + +#: xmlrpc/services/ServiceProviders.py:142 +msgid "Can't delete service provider with services associated" +msgstr "No se puede borrar un proveedor de servicios con servicios asociados" + +#: xmlrpc/services/ServiceProviders.py:145 +msgid "Can't locate the service provider" +msgstr "No puedo hallar el proveedor de servicios" + +#: xmlrpc/services/ServiceProviders.py:145 xmlrpc/services/Services.py:189 +#: xmlrpc/transports/Networks.py:70 xmlrpc/transports/Networks.py:78 +#: xmlrpc/transports/Networks.py:95 +msgid "Please, refresh interface" +msgstr "Por favor, refresque la interfaz." + +#: xmlrpc/services/Services.py:186 +msgid "Can't delete services with deployed services associated" +msgstr "No se puede borrar un servicio con servicios desplegados asociados" + +#: xmlrpc/services/Services.py:189 +msgid "Can't locate the service" +msgstr "No puedo hallar el servicio" + +#: xmlrpc/services/UserDeployedServices.py:94 +#: xmlrpc/services/UserDeployedServices.py:110 +msgid "The deployed service is not active" +msgstr "El servicio desplegado no está activo" + +#: xmlrpc/services/UserDeployedServices.py:97 +#: xmlrpc/services/UserDeployedServices.py:113 +msgid "This service don't allows assignations" +msgstr "El servicio no admite asignaciones" + +#: xmlrpc/services/UserDeployedServices.py:102 +#: xmlrpc/services/UserDeployedServices.py:120 +msgid "Deployed service not found!!! (refresh interface)" +msgstr "Servicio desplegado no hallado!!! (refresque la interfaz)" + +#: xmlrpc/services/UserDeployedServices.py:122 +msgid "User not found!!! (refresh interface)" +msgstr "Usuario no hallado (refresque la interfaz)" + +#: xmlrpc/services/UserDeployedServices.py:141 +msgid "No error" +msgstr "Sin errores" + +#: xmlrpc/services/UserDeployedServices.py:147 +msgid "User deployed service not found!!!" +msgstr "Servicio de usuario no hallado!!!" + +#: xmlrpc/transports/Networks.py:70 +msgid "Can't locate the transport" +msgstr "No puedo hallar el transporte" + +#: xmlrpc/transports/Networks.py:78 xmlrpc/transports/Networks.py:95 +msgid "Can't locate the network" +msgstr "No puedo hallar la red" + +#~ msgid "Base Service" +#~ msgstr "Servicio Base" + +#~ msgid "None" +#~ msgstr "Ninguno" diff --git a/server/src/uds/locale/fr/LC_MESSAGES/django.mo b/server/src/uds/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..f091be80b50cba76f0eb611e7f437842fd063b32 GIT binary patch literal 28203 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, 2012. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-07-12 21:26+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" + +#: auths/ActiveDirectory/Authenticator.py:29 +#: auths/EDirectory/Authenticator.py:60 auths/RegexLdap/Authenticator.py:51 +#: auths/SimpleLDAP/Authenticator.py:49 +#: services/Vmware/ServiceProviderVC.py:28 +msgid "Host" +msgstr "Serveur" + +#: auths/ActiveDirectory/Authenticator.py:29 +#: auths/EDirectory/Authenticator.py:60 auths/RegexLdap/Authenticator.py:51 +#: auths/SimpleLDAP/Authenticator.py:49 +#: services/Vmware/ServiceProviderVC.py:28 +msgid "VMWare VC Server IP or Hostname" +msgstr "Le nom DNS ou l'adresse IP VC VMWare Server" + +#: auths/ActiveDirectory/Authenticator.py:30 +#: auths/EDirectory/Authenticator.py:62 auths/RegexLdap/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:51 +msgid "Use SSL" +msgstr "Utiliser SSL" + +#: auths/ActiveDirectory/Authenticator.py:30 +msgid "If checked, will use a ssl connection to Active Directory" +msgstr "Si elle est cochée, va utiliser une connexion SSL à Active Directory" + +#: auths/ActiveDirectory/Authenticator.py:31 +#: auths/RegexLdap/Authenticator.py:54 auths/SimpleLDAP/Authenticator.py:52 +msgid "Ldap User" +msgstr "Utilisateur LDAP" + +#: auths/ActiveDirectory/Authenticator.py:31 +msgid "" +"Username with read privileges on the base selected (use USER@DOMAIN.DOM form " +"for this)" +msgstr "" +"Nom d'utilisateur avec des privilèges lire sur la base choisie (utiliser le " +"format USER@DOMAIN.DOMpour cela)" + +#: auths/ActiveDirectory/Authenticator.py:32 +#: auths/ActiveDirectory/Authenticator.py:50 +#: auths/EDirectory/Authenticator.py:64 auths/RegexLdap/Authenticator.py:55 +#: auths/RegexLdap/Authenticator.py:78 auths/SimpleLDAP/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:77 +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:31 +#: services/Vmware/ServiceProviderVC.py:31 transports/NX/NXTransport.py:62 +#: transports/RDP/RDPTransport.py:38 transports/RDP/TSRDPTransport.py:42 +#: transports/RGS/RGSTransport.py:43 transports/RGS/TRGSTransport.py:48 +#: transports/TSNX/TSNXTransport.py:67 web/forms/LoginForm.py:60 +msgid "Password" +msgstr "Mot de passe" + +#: auths/ActiveDirectory/Authenticator.py:32 +#: auths/EDirectory/Authenticator.py:64 auths/RegexLdap/Authenticator.py:55 +#: auths/SimpleLDAP/Authenticator.py:53 +msgid "Password of the ldap user" +msgstr "Mot de passe de l'utilisateur ldap" + +#: auths/ActiveDirectory/Authenticator.py:33 +#: auths/EDirectory/Authenticator.py:65 auths/RegexLdap/Authenticator.py:56 +#: auths/SimpleLDAP/Authenticator.py:54 +#: services/Vmware/ServiceProviderVC.py:32 +msgid "Timeout" +msgstr "Temporisation" + +#: auths/ActiveDirectory/Authenticator.py:33 +#: auths/EDirectory/Authenticator.py:65 auths/RegexLdap/Authenticator.py:56 +#: auths/SimpleLDAP/Authenticator.py:54 +msgid "Timeout in seconds of connection to LDAP" +msgstr "Délai en secondes de la connexion à LDAP" + +#: auths/ActiveDirectory/Authenticator.py:35 +msgid "Active Directory Authenticator" +msgstr "Active Directory authentificateur" + +#: auths/ActiveDirectory/Authenticator.py:37 +#, fuzzy +msgid "Authenticate against Active Directory" +msgstr "Authentificateur sur Active Directory" + +#: auths/ActiveDirectory/Authenticator.py:46 +#: auths/EDirectory/Authenticator.py:77 auths/RegexLdap/Authenticator.py:74 +#: auths/SimpleLDAP/Authenticator.py:73 +#: services/Vmware/ServiceProviderVC.py:30 transports/NX/NXTransport.py:61 +#: transports/RDP/RDPTransport.py:37 transports/RDP/TSRDPTransport.py:41 +#: transports/RGS/RGSTransport.py:42 transports/RGS/TRGSTransport.py:47 +#: transports/TSNX/TSNXTransport.py:66 web/forms/LoginForm.py:59 +msgid "Username" +msgstr "Nom d'utilisateur" + +#: auths/ActiveDirectory/Authenticator.py:48 +#: auths/EDirectory/Authenticator.py:79 auths/RegexLdap/Authenticator.py:76 +#: auths/SimpleLDAP/Authenticator.py:75 +msgid "Group" +msgstr "Groupe" + +#: auths/ActiveDirectory/Authenticator.py:61 +#: auths/ActiveDirectory/Authenticator.py:395 +msgid "Must specify the username in the form USERNAME@DOMAIN.DOM" +msgstr "" +"Doit spécifier le nom d'utilisateur sous la forme NOMDEUTILISEUR@DOMAINE.DOM" + +#: auths/ActiveDirectory/Authenticator.py:127 +#: auths/EDirectory/Authenticator.py:123 auths/RegexLdap/Authenticator.py:157 +#: auths/SimpleLDAP/Authenticator.py:158 +msgid "Ldap connection error: " +msgstr "Erreur de connexion LDAP : " + +#: auths/ActiveDirectory/Authenticator.py:299 +#: auths/ActiveDirectory/Authenticator.py:345 +#: auths/EDirectory/Authenticator.py:243 auths/EDirectory/Authenticator.py:286 +#: auths/RegexLdap/Authenticator.py:244 auths/RegexLdap/Authenticator.py:287 +#: auths/SimpleLDAP/Authenticator.py:268 auths/SimpleLDAP/Authenticator.py:312 +msgid "Username not found" +msgstr "Nom d'utilisateur introuvable" + +#: auths/ActiveDirectory/Authenticator.py:332 +#: auths/SimpleLDAP/Authenticator.py:301 +msgid "Group not found" +msgstr "Groupe introuvable" + +#: auths/ActiveDirectory/Authenticator.py:364 +#: auths/ActiveDirectory/Authenticator.py:381 +#: auths/EDirectory/Authenticator.py:303 auths/RegexLdap/Authenticator.py:305 +#: auths/SimpleLDAP/Authenticator.py:327 auths/SimpleLDAP/Authenticator.py:341 +msgid "Too many results, be more specific" +msgstr "Trop de résultats, être plus spécifique" + +#: auths/ActiveDirectory/Authenticator.py:404 +msgid "Domain seems to be incorrect, please check it" +msgstr "Domaine semble incorrect, veuillez vérifier" + +#: auths/ActiveDirectory/Authenticator.py:409 +msgid "Ldap does not seem an Active Directory (do not have user objects)" +msgstr "" +"LDAP ne semble pas un serveur Active Directory (n'ont pas les objets " +"utilisateur)" + +#: auths/ActiveDirectory/Authenticator.py:417 +msgid "Ldap does not seem an Active Directory (no not have group objects)" +msgstr "" +"LDAP ne semble pas un serveur Active Directory (ne pas ont les objets de " +"groupe)" + +#: auths/ActiveDirectory/Authenticator.py:425 +msgid "" +"Ldap does not seem an Active Directory (do not have any user nor groups)" +msgstr "" +"LDAP ne semble pas un serveur Active Directory (n'ont pas tout utilisateur " +"ni les groupes)" + +#: auths/ActiveDirectory/Authenticator.py:430 +#: auths/EDirectory/Authenticator.py:358 auths/RegexLdap/Authenticator.py:380 +#: auths/SimpleLDAP/Authenticator.py:422 +msgid "Connection params seem correct, test was succesfully executed" +msgstr "" +"Connexion params semblent correctes, le test a été correctement exécutée" + +#: auths/EDirectory/Authenticator.py:61 auths/RegexLdap/Authenticator.py:52 +#: auths/SimpleLDAP/Authenticator.py:50 +#: services/Vmware/ServiceProviderVC.py:29 +msgid "Port" +msgstr "Port" + +#: auths/EDirectory/Authenticator.py:61 auths/RegexLdap/Authenticator.py:52 +#: auths/SimpleLDAP/Authenticator.py:50 +msgid "Ldap port (389 for non ssl, 636 for ssl normally" +msgstr "Port LDAP (389 non SSL, 636 pour ssl normalement" + +#: auths/EDirectory/Authenticator.py:62 auths/RegexLdap/Authenticator.py:53 +#: auths/SimpleLDAP/Authenticator.py:51 +msgid "" +"If checked, will use a ssl connection to ldap (if port is 389, will use in " +"fact port 636)" +msgstr "" +"Si cochée, utilise une connexion ssl à ldap (si le port est 389, utilisera " +"dans port de fait 636)" + +#: auths/EDirectory/Authenticator.py:63 +#, fuzzy +msgid "Admin user" +msgstr "Admin" + +#: auths/EDirectory/Authenticator.py:63 +#, fuzzy +msgid "Username with read privileges on the eDirectory" +msgstr "" +"Nom d'utilisateur avec des privilèges de lecture sur la base sélectionnée" + +#: auths/EDirectory/Authenticator.py:67 +#, fuzzy +msgid "eDirectory Authenticator" +msgstr "Active Directory authentificateur" + +#: auths/EDirectory/Authenticator.py:69 +#, fuzzy +msgid "Authenticate against eDirectory" +msgstr "Authentificateur sur Active Directory" + +#: auths/EDirectory/Authenticator.py:323 auths/RegexLdap/Authenticator.py:325 +#: auths/SimpleLDAP/Authenticator.py:362 +msgid "Ldap search base is incorrect" +msgstr "Base de recherche LDAP est incorrect" + +#: auths/EDirectory/Authenticator.py:328 auths/RegexLdap/Authenticator.py:330 +#: auths/SimpleLDAP/Authenticator.py:367 +msgid "Ldap user class seems to be incorrect (no user found by that class)" +msgstr "" +"Classe d'utilisateur LDAP semble incorrect (aucun utilisateur ne trouvé par " +"cette classe)" + +#: auths/EDirectory/Authenticator.py:336 auths/RegexLdap/Authenticator.py:346 +#: auths/SimpleLDAP/Authenticator.py:383 +msgid "" +"Ldap user id attribute seems to be incorrect (no user found by that " +"attribute)" +msgstr "" +"Attribut d'id utilisateur LDAP semble incorrect (aucun utilisateur ne " +"trouvée par qui attribut)" + +#: auths/EDirectory/Authenticator.py:344 +msgid "Expected group attribute " +msgstr "Attribut du groupe prévu " + +#: auths/EDirectory/Authenticator.py:353 +#, fuzzy +msgid "" +"Ldap user class or user id attr is probably wrong (Ldap is an eDirectory?)" +msgstr "" +"LDAP user utilisateur ou la classe id attr est probablement erroné (ne peut " +"pas trouver n'importe quel utilisateur avec les deux conditions)" + +#: auths/IP/Authenticator.py:45 auths/IP/Authenticator.py:47 +msgid "IP Authenticator" +msgstr "Authentificateur IP" + +#: auths/IP/Authenticator.py:51 +msgid "IP" +msgstr "IP " + +#: auths/IP/Authenticator.py:52 +msgid "IP Range" +msgstr "Plage d'adresses IP" + +#: auths/IP/Authenticator.py:99 auths/InternalDB/Authenticator.py:94 +msgid "All seems fine in the authenticator." +msgstr "Tout semble fine dans l'authentificateur." + +#: auths/InternalDB/Authenticator.py:46 +msgid "Internal Database" +msgstr "Base de données interne" + +#: auths/InternalDB/Authenticator.py:48 +msgid "Internal dabasase authenticator. Doesn't uses external sources" +msgstr "Dabasase interne authentificateur. N'utilise des sources externes" + +#: auths/InternalDB/Authenticator.py:91 +msgid "Internal structures seems ok" +msgstr "Les structures internes semble ok" + +#: auths/RegexLdap/Authenticator.py:54 auths/SimpleLDAP/Authenticator.py:52 +msgid "Username with read privileges on the base selected" +msgstr "" +"Nom d'utilisateur avec des privilèges de lecture sur la base sélectionnée" + +#: auths/RegexLdap/Authenticator.py:57 auths/SimpleLDAP/Authenticator.py:55 +msgid "Base" +msgstr "Base" + +#: auths/RegexLdap/Authenticator.py:57 auths/SimpleLDAP/Authenticator.py:55 +msgid "Common search base (used for \"users\" and \"groups\"" +msgstr "" +"Recherche commune base (utilisé pour les « utilisateurs » et « groupes »" + +#: auths/RegexLdap/Authenticator.py:58 auths/SimpleLDAP/Authenticator.py:56 +msgid "User class" +msgstr "Classe utilisateur" + +#: auths/RegexLdap/Authenticator.py:58 auths/SimpleLDAP/Authenticator.py:56 +msgid "Class for LDAP users (normally posixAccount)" +msgstr "Classe pour les utilisateurs de LDAP (normalement posixAccount)" + +#: auths/RegexLdap/Authenticator.py:59 auths/SimpleLDAP/Authenticator.py:57 +msgid "User Id Attr" +msgstr "Utilisateur Id Attr" + +#: auths/RegexLdap/Authenticator.py:59 auths/SimpleLDAP/Authenticator.py:57 +msgid "Attribute that contains the user id" +msgstr "Attribut qui contient l'id utilisateur" + +#: auths/RegexLdap/Authenticator.py:60 auths/SimpleLDAP/Authenticator.py:58 +msgid "User Name Attr" +msgstr "Utilisateur nom Attr" + +#: auths/RegexLdap/Authenticator.py:60 auths/SimpleLDAP/Authenticator.py:58 +msgid "Attributes that contains the user name (list of comma separated values)" +msgstr "" +"Attributs qui contient le nom d'utilisateur (liste de valeurs séparées par " +"des virgules)" + +#: auths/RegexLdap/Authenticator.py:61 +msgid "Group Name Attr" +msgstr "Groupe nom Attr" + +#: auths/RegexLdap/Authenticator.py:61 +msgid "Attribute that contains the group name" +msgstr "Attribut qui contient le nom du groupe" + +#: auths/RegexLdap/Authenticator.py:62 +msgid "Regular Exp. for groups" +msgstr "Exp régulière pour les groupes" + +#: auths/RegexLdap/Authenticator.py:62 +msgid "Regular Expression to extract the group name" +msgstr "Expression régulière pour extraire le nom du groupe" + +#: auths/RegexLdap/Authenticator.py:64 +msgid "Regex LDAP Authenticator" +msgstr "Authentificateur LDAP Regex" + +#: auths/RegexLdap/Authenticator.py:66 +msgid "Regular Expressions LDAP authenticator" +msgstr "Authentificateur de LDAP d'Expressions régulière" + +#: auths/RegexLdap/Authenticator.py:338 auths/SimpleLDAP/Authenticator.py:375 +msgid "Ldap group class seems to be incorrect (no group found by that class)" +msgstr "" +"Classe de groupe LDAP semble incorrect (aucun groupe ne trouvée par cette " +"classe)" + +#: auths/RegexLdap/Authenticator.py:356 auths/SimpleLDAP/Authenticator.py:391 +msgid "" +"Ldap group id attribute seems to be incorrect (no group found by that " +"attribute)" +msgstr "" +"Attribut d'id groupe LDAP semble incorrect (aucun groupe ne trouvée par qui " +"attribut)" + +#: auths/RegexLdap/Authenticator.py:365 auths/SimpleLDAP/Authenticator.py:400 +msgid "" +"Ldap user class or user id attr is probably wrong (can't find any user with " +"both conditions)" +msgstr "" +"LDAP user utilisateur ou la classe id attr est probablement erroné (ne peut " +"pas trouver n'importe quel utilisateur avec les deux conditions)" + +#: auths/SimpleLDAP/Authenticator.py:59 +msgid "Group class" +msgstr "Groupe classe" + +#: auths/SimpleLDAP/Authenticator.py:59 +msgid "Class for LDAP groups (normally poxisGroup)" +msgstr "Classe pour les groupes LDAP (normalement poxisGroup)" + +#: auths/SimpleLDAP/Authenticator.py:60 +msgid "Group Id Attr" +msgstr "Groupe Id Attr" + +#: auths/SimpleLDAP/Authenticator.py:60 +msgid "Attribute that contains the group id" +msgstr "Attribut qui contient l'id de groupe" + +#: auths/SimpleLDAP/Authenticator.py:61 +msgid "Group membership attr" +msgstr "Groupe adhésion attr" + +#: auths/SimpleLDAP/Authenticator.py:61 +msgid "Attribute of the group that contains the users belonging to it" +msgstr "Attribut du groupe qui contient les utilisateurs appartenant à elle" + +#: auths/SimpleLDAP/Authenticator.py:63 +msgid "SimpleLDAP Authenticator" +msgstr "Authentificateur SimpleLDAP" + +#: auths/SimpleLDAP/Authenticator.py:65 +msgid "Simple LDAP authenticator" +msgstr "Simple authentificateur LDAP" + +#: auths/SimpleLDAP/Authenticator.py:409 +msgid "" +"Ldap group class or group id attr is probably wrong (can't find any group " +"with both conditions)" +msgstr "" +"LDAP groupe classe ou groupe id attr est probablement erroné (ne peut pas " +"trouver n'importe quel groupe avec les deux conditions)" + +#: auths/SimpleLDAP/Authenticator.py:416 +msgid "Can't locate any group with the membership attribute specified" +msgstr "" +"Ne peut pas localiser n'importe quel groupe avec l'attribut d'appartenance " +"spécifié" + +#: core/BaseModule.py:196 +msgid "No connection checking method is implemented." +msgstr "Aucun lien vérifier la méthode n'est implémentée." + +#: core/BaseModule.py:248 +msgid "No check method provided." +msgstr "Aucune méthode de vérification fournie." + +#: core/managers/PublicationManager.py:156 +msgid "" +"Already publishing. Wait for previous publication to finish and try again" +msgstr "" +"Déjà la publication. Attendez une publication antérieure de terminer et " +"réessayez" + +#: core/managers/PublicationManager.py:168 +msgid "Can't cancel non running publication" +msgstr "Ne peut annuler la publication non courante" + +#: core/managers/PublicationManager.py:186 +msgid "Can't unpublish non usable publication" +msgstr "Ne peut annuler la publication publication non utilisable" + +#: core/managers/PublicationManager.py:189 +msgid "Can't unpublish publications with services in process" +msgstr "" +"Ne peut annuler la publication des publications avec services de processus" + +#: core/managers/UserPrefsManager.py:254 +msgid "Screen Size" +msgstr "Taille de l'écran" + +#: core/managers/UserPrefsManager.py:258 +msgid "Full Screen" +msgstr "Plein écran" + +#: core/managers/UserPrefsManager.py:261 +msgid "Screen colors" +msgstr "Couleurs de l'écran" + +#: core/managers/UserPrefsManager.py:262 +msgid "8 bits" +msgstr "8 bits" + +#: core/managers/UserPrefsManager.py:263 +msgid "16 bits" +msgstr "16 bits" + +#: core/managers/UserPrefsManager.py:264 +msgid "24 bits" +msgstr "24 bits" + +#: core/managers/UserPrefsManager.py:265 +msgid "32 bits" +msgstr "32 bits" + +#: core/managers/UserServiceManager.py:302 +msgid "Can't cancel non running operation" +msgstr "Ne peut annuler une opération non courante" + +#: core/managers/UserServiceManager.py:321 +msgid "Can't remove a non active element" +msgstr "Impossible de supprimer un élément non actif" + +#: core/managers/UserServiceManager.py:334 +msgid "Can't remove nor cancel {0} cause its states doesn't allows it" +msgstr "Ne peut pas supprimer ni annuler {0} cause ses États ne lui permet pas" + +#: core/osmanagers/BaseOsManager.py:50 +msgid "Base OS Manager" +msgstr "Gestionnaire d'OS de base" + +#: core/osmanagers/BaseOsManager.py:52 +msgid "Base Manager" +msgstr "Gestionnaire de base de" + +#: core/transports/BaseTransport.py:94 +msgid "Transport empty" +msgstr "Transport vide" + +#: core/util/State.py:59 +msgid "Active" +msgstr "Active" + +#: core/util/State.py:59 +msgid "Inactive" +msgstr "Inactif" + +#: core/util/State.py:59 +msgid "Blocked" +msgstr "Bloqué" + +#: core/util/State.py:59 +msgid "Waiting publication" +msgstr "Attente de publication" + +#: core/util/State.py:60 +msgid "In preparation" +msgstr "En préparation" + +#: core/util/State.py:60 +msgid "Valid" +msgstr "Valide" + +#: core/util/State.py:61 +msgid "Waiting for removal" +msgstr "Attente d'enlèvement" + +#: core/util/State.py:61 +msgid "Removing" +msgstr "Suppression" + +#: core/util/State.py:61 +msgid "Removed" +msgstr "Supprimé" + +#: core/util/State.py:61 +msgid "Canceled" +msgstr "Annulée" + +#: core/util/State.py:62 +msgid "Canceling" +msgstr "Annulation" + +#: core/util/State.py:62 templates/uds/error.html:6 +#: templates/uds/error.html.py:11 +msgid "Error" +msgstr "Erreur" + +#: core/util/State.py:62 +msgid "Running" +msgstr "En cours d'exécution" + +#: core/util/State.py:62 +msgid "Finished" +msgstr "Fini" + +#: core/util/State.py:62 +msgid "Waiting execution" +msgstr "Exécution en attente" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:44 +msgid "Linux OS Manager" +msgstr "Gestionnaire de système d'exploitation Linux" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:46 +msgid "" +"Os Manager to control linux virtual machines (basically renames machine and " +"notify state)" +msgstr "" +"Gestionnaire de l'os pour contrôler les machines virtuelles de linux " +"(essentiellement renomme machine et notifier l'État)" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:49 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:40 +msgid "On Logout" +msgstr "Sur Logout" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:49 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:40 +msgid "What to do when user logout from service" +msgstr "Que faire quand déconnexion de l'utilisateur du service" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:50 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:41 +msgid "Keep service assigned" +msgstr "Garder les services affectés" + +#: osmanagers/LinuxOsManager/LinuxOsManager.py:51 +#: osmanagers/WindowsOsManager/WindowsOsManager.py:42 +msgid "Remove service" +msgstr "Retirer du service" + +#: osmanagers/LinuxOsManager/__init__.py:43 +msgid "UDS Actor for linux machines (Requires python 2.6 or greater)" +msgstr "" +"Acteur de l'UDS pour linux machines (nécessite le python 2.6 ou plus)" + +#: osmanagers/NoneOsManager/Manager.py:43 +msgid "None OS Manager" +msgstr "Aucun gestionnaire de l'OS" + +#: osmanagers/NoneOsManager/Manager.py:45 +msgid "Os Manager with no actions" +msgstr "Gestionnaire d'os avec aucune action" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:23 +msgid "Windows Domain OS Manager" +msgstr "Gestionnaire de système d'exploitation pour le domaine Windows" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:25 +msgid "" +"Os Manager to control windows machines with domain. (Basically renames " +"machine)" +msgstr "" +"Os Manager pour contrôler les machines windows avec le domaine. " +"(Essentiellement renomme machine)" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:29 +#: transports/RDP/RDPTransport.py:39 transports/RDP/TSRDPTransport.py:43 +#: transports/RGS/RGSTransport.py:44 transports/RGS/TRGSTransport.py:49 +msgid "Domain" +msgstr "Domaine" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:29 +msgid "Domain to join machines to (better use dns form of domain)" +msgstr "" +"Domaine de rejoindre les machines (mieux utiliser le formulaire de dns du " +"domaine)" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:30 +msgid "Account" +msgstr "Compte" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:30 +msgid "Account with rights to add machines to domain" +msgstr "Compte avec droits d'ajouter des machines à domaine" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:31 +msgid "Password of the account" +msgstr "Mot de passe du compte" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:32 +msgid "OU" +msgstr "OU" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:32 +msgid "" +"Organizational unit where to add machines in domain (check it before using " +"it)" +msgstr "" +"Unité organisationnelle où ajouter des machines dans le domaine (vérifier il " +"avant d'utiliser GTG" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:40 +msgid "Must provide a domain!!!" +msgstr "Doit fournir un domaine!!!" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:42 +msgid "Must provide an account to add machines to domain!!!" +msgstr "Doit fournir un compte pour ajouter des machines à domaine!!!" + +#: osmanagers/WindowsOsManager/WinDomainOsManager.py:44 +msgid "Must provide a password for the account!!!" +msgstr "Doit fournir un mot de passe du compte!!!" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:35 +msgid "Windows Basic OS Manager" +msgstr "Gestionnaire de base de Windows OS" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:37 +msgid "" +"Os Manager to control windows machines without domain. (Basically renames " +"machine)" +msgstr "" +"Os Manager pour contrôler les machines windows sans domaine. " +"(Essentiellement renomme machine)" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:51 +msgid "Length must be numeric!!" +msgstr "La longueur doit être numérique!!" + +#: osmanagers/WindowsOsManager/WindowsOsManager.py:53 +msgid "Length must be betwen 1 and six" +msgstr "La longueur doit être images 1 et six" + +#: osmanagers/WindowsOsManager/__init__.py:23 +msgid "" +"UDS Actor for windows machines (Important!! Requires .net framework 3.5 " +"sp1)" +msgstr "" +"Acteur de l'UDS pour machines windows (Important!! Nécessite le .net " +"framework 3.5 SP1)" + +#: services/PhysicalMachines/IPMachineDeployed.py:56 +msgid "IP " +msgstr "IP " + +#: services/PhysicalMachines/IPMachinesService.py:46 +msgid "List of IPS" +msgstr "Liste des IPS" + +#: services/PhysicalMachines/IPMachinesService.py:49 +msgid "Physical machines accesed by ip" +msgstr "Accesed de machines physiques par ip" + +#: services/PhysicalMachines/IPMachinesService.py:51 +msgid "This service provides access to POWERED-ON Machines by ip" +msgstr "Ce service permet d'accéder aux machines SOUS-TENSION par ip" + +#: services/Sample/SampleProvider.py:142 +msgid "Methuselah is not alive!!! :-)" +msgstr "Mathusalem n'est pas vivant!!! :-)" + +#: services/Sample/SampleProvider.py:182 +msgid "Nothing tested, but all went fine.." +msgstr "Rien à l'épreuve, mais tout s'est bien passé..." + +#: services/Sample/SamplePublication.py:199 +msgid "Random integer was 9!!! :-)" +msgstr "Nombre entier aléatoire était 9!!! :-)" + +#: services/Vmware/Helpers.py:72 +msgid "Local" +msgstr "Local" + +#: services/Vmware/Helpers.py:74 +msgid "Remote" +msgstr "Distant" + +#: services/Vmware/PublicationVC.py:36 +msgid "Publication" +msgstr "Publication" + +#: services/Vmware/PublicationVC.py:37 +msgid "UDS Publication for {0} created at {1}" +msgstr "Publication de l'UDS pour {0} créé à {1}" + +#: services/Vmware/ServiceProviderVC.py:29 +msgid "VMWare VC Server Port (usually 443)" +msgstr "Port du serveur VMWare VC (habituellement 443)" + +#: services/Vmware/ServiceProviderVC.py:30 +msgid "User with valid privileges on VC" +msgstr "Utilisateur avec des privilèges valides sur VC" + +#: services/Vmware/ServiceProviderVC.py:31 +msgid "Password of the user of the VC" +msgstr "Mot de passe de l'utilisateur de la Croix de Victoria" + +#: services/Vmware/ServiceProviderVC.py:32 +msgid "Timeout in seconds of connection to VC" +msgstr "Délai en secondes de connexion à VC" + +#: services/Vmware/ServiceProviderVC.py:33 +msgid "Macs range" +msgstr "Gamme Mac" + +#: services/Vmware/ServiceProviderVC.py:34 +msgid "Range of valids macs for created machines" +msgstr "Gamme de Mac valides pour les machines créés" + +#: services/Vmware/ServiceProviderVC.py:39 +msgid "VMWare Virtual Center Provider" +msgstr "VMWare Virtual Center fournisseur" + +#: services/Vmware/ServiceProviderVC.py:41 +msgid "Provides connection to Virtual Center Services" +msgstr "Fournit la connexion aux Services du Centre virtuel" + +#: services/Vmware/ServiceProviderVC.py:111 +msgid "Error testing connection" +msgstr "Connexion essai erreur" + +#: services/Vmware/ServiceProviderVC.py:114 +msgid "VmwareVC Provider: " +msgstr "VmwareVC fournisseur : " + +#: services/Vmware/ServiceProviderVC.py:120 +msgid "Connection params ok" +msgstr "Connexion params ok" + +#: services/Vmware/ServiceProviderVC.py:121 +msgid "Connection failed. Check connection params" +msgstr "Échec de la connexion. Vérifiez la connexion params" + +#: services/Vmware/VCLinkedCloneService.py:28 +msgid "Datacenter" +msgstr "Datacenter" + +#: services/Vmware/VCLinkedCloneService.py:34 +msgid "Datacenter containing base machine" +msgstr "Machine de base contenant Datacenter" + +#: services/Vmware/VCLinkedCloneService.py:36 +msgid "Network" +msgstr "Réseau" + +#: services/Vmware/VCLinkedCloneService.py:37 +msgid "" +"If more than 1 interface is found in machine, use one on this network as main" +msgstr "" +"Si plus de 1 interface est trouvé dans la machine, utilisez l'une sur ce " +"réseau comme principal" + +#: services/Vmware/VCLinkedCloneService.py:38 +msgid "Pub. Resource Pool" +msgstr "Pub. Ressource Pool" + +#: services/Vmware/VCLinkedCloneService.py:38 +msgid "Resource Pool where deploy clones" +msgstr "Ressource Pool déployer où les clones" + +#: services/Vmware/VCLinkedCloneService.py:39 +msgid "Clones Folder" +msgstr "Dossier de clones" + +#: services/Vmware/VCLinkedCloneService.py:39 +msgid "Folder where deploy clones" +msgstr "Dossier où déployer clones" + +#: services/Vmware/VCLinkedCloneService.py:40 +msgid "Resource Pool" +msgstr "Ressource Pool" + +#: services/Vmware/VCLinkedCloneService.py:46 +msgid "Resource Pool containing base machine" +msgstr "Machine base contenant de ressource Pool" + +#: services/Vmware/VCLinkedCloneService.py:48 +msgid "Base Machine" +msgstr "Machine de base" + +#: services/Vmware/VCLinkedCloneService.py:48 +msgid "Base machine for this service" +msgstr "Machine de base pour ce service." + +#: services/Vmware/VCLinkedCloneService.py:49 +msgid "Memory (Mb)" +msgstr "Mémoire (Mb)" + +#: services/Vmware/VCLinkedCloneService.py:50 +msgid "Memory for machines deployed from this service" +msgstr "Mémoire pour les machines déployés à partir de ce service" + +#: services/Vmware/VCLinkedCloneService.py:51 +msgid "Datastores" +msgstr "Magasins de données" + +#: services/Vmware/VCLinkedCloneService.py:52 +msgid "Datastores where to put incrementals" +msgstr "Magasins de données où mettre des sauvegardes incrémentielles" + +#: services/Vmware/VCLinkedCloneService.py:53 +msgid "Machine Names" +msgstr "Noms de machine" + +#: services/Vmware/VCLinkedCloneService.py:53 +msgid "Base name for clones from this machine" +msgstr "Nom de base des clones de cette machine." + +#: services/Vmware/VCLinkedCloneService.py:54 +msgid "Name Length" +msgstr "Longueur du nom" + +#: services/Vmware/VCLinkedCloneService.py:55 +msgid "Length of numeric part for the names of this machines (betwen 3 and 6" +msgstr "" +"Longueur de la partie numérique pour les noms de ces machines (images 3 et 6" + +#: services/Vmware/VCLinkedCloneService.py:62 +msgid "VMWare Linked clone base" +msgstr "Base de clone lié VMWare" + +#: services/Vmware/VCLinkedCloneService.py:64 +msgid "" +"This service provides access to Linked Clones machines on a Virtual Center" +msgstr "" +"Ce service donne accès aux machines de Clones liés sur un centre virtuel" + +#: services/Vmware/VCLinkedCloneService.py:70 +msgid "Number of desired machines to keep running waiting for a user" +msgstr "Nombre de machines désirés de courir d'attente pour un utilisateur." + +#: services/Vmware/VCLinkedCloneService.py:72 +msgid "Number of desired machines to keep suspended waiting for use" +msgstr "" +"Nombre de machines désirés pour garder suspendu en attente pour utilisation" + +#: services/Vmware/VCLinkedCloneService.py:93 +msgid "The length of basename plus length must not be greater than 15" +msgstr "" +"La longueur du nom de base plus la longueur ne doit pas être supérieure à 15" + +#: services/Vmware/VCLinkedCloneService.py:95 +msgid "The machine name can't be only numbers" +msgstr "Nom de l'ordinateur ne peut pas être uniquement des nombres" + +#: templates/404.html:4 templates/404.html.py:7 +msgid "Page not found" +msgstr "Page non trouvée" + +#: templates/404.html:9 +msgid "Sorry, but the requested page could not be found." +msgstr "Désolé, mais la page demandée n'a pas pu être trouvée." + +#: templates/uds/base.html:7 +msgid "UDS" +msgstr "UDS" + +#: templates/uds/downloads.html:8 templates/uds/snippets/admin_user.html:7 +msgid "Downloads" +msgstr "Téléchargements" + +#: templates/uds/downloads.html:11 +msgid "" +"This page contains a list of downloadables provided by different modules" +msgstr "" +"Cette page contient une liste de téléchargeables fournis par différents " +"modules" + +#: templates/uds/index.html:51 +msgid "Services" +msgstr "Services" + +#: templates/uds/index.html:70 +msgid "Java not found" +msgstr "Java non trouvé" + +#: templates/uds/index.html:71 +msgid "" +"Java is not available on your browser, and the selected transport needs it." +msgstr "" +"Java n'est pas disponible sur votre navigateur, et le transport sélectionné " +"en a besoin." + +#: templates/uds/index.html:72 +msgid "Please, install latest version from" +msgstr "Veuillez installer une version plus récente de" + +#: templates/uds/index.html:72 +msgid "Java website" +msgstr "Site Web Java" + +#: templates/uds/index.html:72 +msgid "and restart browser" +msgstr "Redémarrez le navigateur" + +#: templates/uds/index.html:78 +msgid "Ip" +msgstr "IP" + +#: templates/uds/index.html:79 +msgid "Networks" +msgstr "Réseaux" + +#: templates/uds/index.html:80 +msgid "Transports" +msgstr "Transports" + +#: templates/uds/internal_page.html:28 +msgid "User" +msgstr "Utilisateur" + +#: templates/uds/internal_page.html:34 templates/uds/prefs.html:12 +msgid "Preferences" +msgstr "Préférences" + +#: templates/uds/internal_page.html:40 +msgid "Log out" +msgstr "Déconnexion" + +#: templates/uds/login.html:6 +msgid "Login to UDS" +msgstr "Connexion à UDS" + +#: templates/uds/login.html:78 +msgid "Login" +msgstr "Login" + +#: templates/uds/login.html:83 +msgid "Login data" +msgstr "Données de connexion" + +#: templates/uds/login.html:86 +msgid "Enter" +msgstr "Entrez" + +#: templates/uds/login.html:93 +msgid "Back to login" +msgstr "Retour à la connexion" + +#: templates/uds/prefs.html:6 +msgid "UDS User Preferences" +msgstr "Préférences de l'utilisateur UDS" + +#: templates/uds/prefs.html:16 +msgid "Save Preferences" +msgstr "Enregistrer les préférences" + +#: templates/uds/service_not_ready.html:6 +msgid "Service not ready at this moment. Please, try again in a while." +msgstr "" +"Le service n'est pas prêt à ce moment. S'il vous plaît, essayez à nouveau de " +"temps en temps." + +#: templates/uds/snippets/admin_user.html:4 +msgid "Admin" +msgstr "Admin" + +#: templates/uds/snippets/back_to_list.html:3 +msgid "Back to services list" +msgstr "Retour à la liste de services" + +#: templates/uds/snippets/lang.html:9 +msgid "Language" +msgstr "Langue" + +#: transports/NX/NXTransport.py:54 +msgid "NX Transport (direct)" +msgstr "NX Transport (direct)" + +#: transports/NX/NXTransport.py:56 +msgid "NX Transport for direct connection" +msgstr "NX Transport pour une connexion directe" + +#: transports/NX/NXTransport.py:60 transports/RDP/RDPTransport.py:36 +#: transports/RDP/TSRDPTransport.py:40 transports/RGS/RGSTransport.py:41 +#: transports/RGS/TRGSTransport.py:46 transports/TSNX/TSNXTransport.py:65 +msgid "Empty creds" +msgstr "Références vide" + +#: transports/NX/NXTransport.py:60 transports/RDP/RDPTransport.py:36 +#: transports/RDP/TSRDPTransport.py:40 transports/RGS/RGSTransport.py:41 +#: transports/RGS/TRGSTransport.py:46 transports/TSNX/TSNXTransport.py:65 +msgid "If checked, the credentials used to connect will be emtpy" +msgstr "" +"Si coché, les informations d'identification utilisées pour se connecter sera " +"vide" + +#: transports/NX/NXTransport.py:61 transports/RDP/RDPTransport.py:37 +#: transports/RDP/TSRDPTransport.py:41 transports/RGS/RGSTransport.py:42 +#: transports/RGS/TRGSTransport.py:47 transports/TSNX/TSNXTransport.py:66 +msgid "If not empty, this username will be always used as credential" +msgstr "" +"Si ce n'est vide, ce nom d'utilisateur sera toujours utilisé comme des " +"titres de compétences" + +#: transports/NX/NXTransport.py:62 transports/RDP/RDPTransport.py:38 +#: transports/RDP/TSRDPTransport.py:42 transports/RGS/RGSTransport.py:43 +#: transports/RGS/TRGSTransport.py:48 transports/TSNX/TSNXTransport.py:67 +msgid "If not empty, this password will be always used as credential" +msgstr "" +"Si ce n'est vide, ce mot de passe sera toujours utilisé comme des titres de " +"compétences" + +#: transports/NX/NXTransport.py:63 transports/TSNX/TSNXTransport.py:68 +msgid "Listen port" +msgstr "Écouter le port" + +#: transports/NX/NXTransport.py:63 transports/TSNX/TSNXTransport.py:68 +msgid "Listening port of NX (ssh) at client machine" +msgstr "Écoute le port de NX (ssh) à la machine client" + +#: transports/NX/NXTransport.py:64 transports/TSNX/TSNXTransport.py:69 +msgid "Connection" +msgstr "Connexion" + +#: transports/NX/NXTransport.py:64 transports/TSNX/TSNXTransport.py:69 +msgid "Connection speed for this transport (quality)" +msgstr "Vitesse de connexion pour ce transport (qualité)" + +#: transports/NX/NXTransport.py:71 transports/TSNX/TSNXTransport.py:76 +msgid "Session" +msgstr "Session" + +#: transports/NX/NXTransport.py:71 transports/TSNX/TSNXTransport.py:76 +msgid "Desktop session" +msgstr "Session de bureau" + +#: transports/NX/NXTransport.py:76 transports/TSNX/TSNXTransport.py:81 +msgid "Disk Cache" +msgstr "Cache disque" + +#: transports/NX/NXTransport.py:76 transports/TSNX/TSNXTransport.py:81 +msgid "Cache size en Mb stored at disk" +msgstr "Cache en taille que Mo stocké à disque" + +#: transports/NX/NXTransport.py:84 transports/TSNX/TSNXTransport.py:89 +msgid "Memory Cache" +msgstr "Mémoire Cache" + +#: transports/NX/NXTransport.py:84 transports/TSNX/TSNXTransport.py:89 +msgid "Cache size en Mb keept at memory" +msgstr "Taille en Mb montagne à la mémoire de cache." + +#: transports/NX/__init__.py:42 transports/TSNX/__init__.py:42 +msgid "NX Protocol" +msgstr "Protocole de NX" + +#: transports/NX/__init__.py:48 +msgid "UDS Actor connector for NX (requires nomachine packages)" +msgstr "Connecteur UDS acteur pour NX (nécessite nomachine packages)" + +#: transports/NX/web.py:74 +msgid "" +"In order to use this transport, you need to install first Nomachine Nx " +"Client version 3.5.x" +msgstr "" +"Afin d'utiliser ce transport, vous devez installer le premier Nomachine Nx " +"Client version 3.5.x" + +#: transports/NX/web.py:75 +msgid "you can obtain it for your platform from" +msgstr "vous pouvez l'obtenir pour votre plate-forme de" + +#: transports/NX/web.py:75 +msgid "nochamine web site" +msgstr "site web de nochamine" + +#: transports/RDP/RDPTransport.py:30 +msgid "RDP Transport (direct)" +msgstr "Transport de RDP (direct)" + +#: transports/RDP/RDPTransport.py:32 +msgid "RDP Transport for direct connection" +msgstr "Transport de RDP pour une connexion directe" + +#: transports/RDP/RDPTransport.py:39 transports/RDP/TSRDPTransport.py:43 +#: transports/RGS/RGSTransport.py:44 transports/RGS/TRGSTransport.py:49 +msgid "" +"If not empty, this domain will be always used as credential (used as DOMAIN" +"\\user)" +msgstr "" +"Si ce n'est vide, ce domaine sera toujours utilisé comme des titres de " +"compétences (utilisé comme domaine\\User)" + +#: transports/RDP/RDPTransport.py:40 transports/RDP/TSRDPTransport.py:44 +msgid "Allow Smartcards" +msgstr "Permettre aux cartes à puce" + +#: transports/RDP/RDPTransport.py:40 transports/RDP/TSRDPTransport.py:44 +msgid "If checked, this transport will allow the use of smartcards" +msgstr "Si cochée, ce transport permettra l'utilisation de cartes à puce" + +#: transports/RDP/RDPTransport.py:41 transports/RDP/TSRDPTransport.py:45 +msgid "Allow Printers" +msgstr "Permettre aux imprimantes" + +#: transports/RDP/RDPTransport.py:41 transports/RDP/TSRDPTransport.py:45 +msgid "If checked, this transport will allow the use of user printers" +msgstr "" +"Si cochée, ce transport permettra l'utilisation des imprimantes utilisateur" + +#: transports/RDP/RDPTransport.py:42 transports/RDP/TSRDPTransport.py:46 +msgid "Allow Drives" +msgstr "Permettre aux lecteurs" + +#: transports/RDP/RDPTransport.py:42 transports/RDP/TSRDPTransport.py:46 +msgid "If checked, this transport will allow the use of user drives" +msgstr "" +"Si cochée, ce transport permettra l'utilisation des lecteurs de l'utilisateur" + +#: transports/RDP/RDPTransport.py:43 transports/RDP/TSRDPTransport.py:47 +msgid "Allow Serials" +msgstr "Permettre aux publications en série" + +#: transports/RDP/RDPTransport.py:43 transports/RDP/TSRDPTransport.py:47 +msgid "If checked, this transport will allow the use of user serial ports" +msgstr "" +"Si cochée, ce transport permettra l'utilisation de l'utilisateur ports série" + +#: transports/RDP/TSRDPTransport.py:31 +msgid "RDP Transport (tunneled)" +msgstr "Transport de RDP (tunnel)" + +#: transports/RDP/TSRDPTransport.py:33 +msgid "RDP Transport for tunneled connection" +msgstr "Transport de RDP de connexion tunnelée" + +#: transports/RDP/TSRDPTransport.py:37 transports/RGS/TRGSTransport.py:43 +#: transports/TSNX/TSNXTransport.py:62 +msgid "Tunnel server" +msgstr "Serveur de tunnel" + +#: transports/RDP/TSRDPTransport.py:37 transports/RGS/TRGSTransport.py:43 +#: transports/TSNX/TSNXTransport.py:62 +msgid "" +"IP or Hostname of tunnel server send to client device (\"public\" ip) and " +"port. (use HOST:PORT format)" +msgstr "" +"IP ou nom d'hôte du serveur de tunnel envoyer à la machine cliente (« public " +"» ip) et port. (utilisez le format de l'hôte : PORT)" + +#: transports/RDP/TSRDPTransport.py:38 transports/RGS/TRGSTransport.py:44 +#: transports/TSNX/TSNXTransport.py:63 +msgid "Tunnel host check" +msgstr "Tunnel hôte cocher" + +#: transports/RDP/TSRDPTransport.py:38 transports/RGS/TRGSTransport.py:44 +#: transports/TSNX/TSNXTransport.py:63 +msgid "" +"If not empty, this server will be used to check if service is running before " +"assigning it to user. (use HOST:PORT format)" +msgstr "" +"Si ce n'est vide, ce serveur sera utilisé pour vérifier si le service " +"s'exécute avant assignant à l'utilisateur. (utilisez le format de l'hôte : " +"PORT)" + +#: transports/RDP/__init__.py:20 +msgid "Remote Desktop Protocol" +msgstr "Protocole Bureau distant" + +#: transports/RDP/web.py:83 +msgid "In order to use this service, you should first install CoRD." +msgstr "Afin d'utiliser ce service, vous devez d'abord installer cordon." + +#: transports/RDP/web.py:84 transports/RGS/web.py:82 +msgid "You can obtain it from" +msgstr "Vous pouvez l'obtenir de" + +#: transports/RDP/web.py:84 +msgid "CoRD Website" +msgstr "Site Web du cordon" + +#: transports/RGS/RGSTransport.py:34 +#, fuzzy +msgid "RGS Transport (direct)" +msgstr "Transport de RDP (direct)" + +#: transports/RGS/RGSTransport.py:36 +#, fuzzy +msgid "RGS Transport for direct connection" +msgstr "Transport de RDP pour une connexion directe" + +#: transports/RGS/RGSTransport.py:45 transports/RGS/TRGSTransport.py:50 +msgid "Image quality" +msgstr "Qualité de l'image" + +#: transports/RGS/RGSTransport.py:46 transports/RGS/TRGSTransport.py:51 +msgid "Quality of image codec (0-100)" +msgstr "Qualité du codec d'image (0-100)" + +#: transports/RGS/RGSTransport.py:47 transports/RGS/TRGSTransport.py:52 +msgid "Adjustable Quality" +msgstr "Qualité réglable" + +#: transports/RGS/RGSTransport.py:48 transports/RGS/TRGSTransport.py:53 +msgid "If checked, the image quality will be adjustable with bandwidth" +msgstr "Si cochée, la qualité de l'image sera réglable avec bande passante" + +#: transports/RGS/RGSTransport.py:49 transports/RGS/TRGSTransport.py:54 +msgid "Min. Adjustable Quality" +msgstr "Min. qualité réglable" + +#: transports/RGS/RGSTransport.py:50 transports/RGS/TRGSTransport.py:55 +msgid "" +"The lowest image quality applied to images to maintain the minimum update " +"rate." +msgstr "" +"La qualité de l'image plus bas appliquée aux images afin de maintenir la " +"mise à jour minimum taux." + +#: transports/RGS/RGSTransport.py:51 transports/RGS/TRGSTransport.py:56 +msgid "Adjustable Frame Rate" +msgstr "Cadence réglable" + +#: transports/RGS/RGSTransport.py:52 transports/RGS/TRGSTransport.py:57 +msgid "Update rate threshold to begin adjusting image quality" +msgstr "" +"Seuil de vitesse de mise à jour pour commencer à ajuster la qualité de " +"l'image" + +#: transports/RGS/RGSTransport.py:53 transports/RGS/TRGSTransport.py:58 +msgid "Match Local Resolution" +msgstr "Résolution locale de match" + +#: transports/RGS/RGSTransport.py:54 transports/RGS/TRGSTransport.py:59 +msgid "" +"Change the Sender's resolution to match the Receiver's resolution when " +"connecting" +msgstr "" +"Modifier la résolution de l'expéditeur pour faire correspondre la résolution " +"du séquestre lors de la connexion" + +#: transports/RGS/RGSTransport.py:55 transports/RGS/TRGSTransport.py:60 +msgid "Redirect USB" +msgstr "Redirection USB" + +#: transports/RGS/RGSTransport.py:56 transports/RGS/TRGSTransport.py:61 +msgid "If checked, the USB will be redirected." +msgstr "Si cochée, la clé USB est redirigée." + +#: transports/RGS/RGSTransport.py:57 transports/RGS/TRGSTransport.py:62 +msgid "Redirect Audio" +msgstr "Redirection Audio" + +#: transports/RGS/RGSTransport.py:58 transports/RGS/TRGSTransport.py:63 +#, fuzzy +msgid "If checked, the Audio will be redirected." +msgstr "" +"Si coché, les informations d'identification utilisées pour se connecter sera " +"vide" + +#: transports/RGS/RGSTransport.py:59 transports/RGS/TRGSTransport.py:64 +msgid "Redirect Mic" +msgstr "Redirection Mic" + +#: transports/RGS/RGSTransport.py:60 transports/RGS/TRGSTransport.py:65 +#, fuzzy +msgid "If checked, the Mic will be redirected." +msgstr "" +"Si coché, les informations d'identification utilisées pour se connecter sera " +"vide" + +#: transports/RGS/TRGSTransport.py:36 +#, fuzzy +msgid "RGS Transport (tunneled)" +msgstr "Transport de RDP (tunnel)" + +#: transports/RGS/TRGSTransport.py:38 +#, fuzzy +msgid "RGS Transport for tunneled connection" +msgstr "Transport de RDP de connexion tunnelée" + +#: transports/RGS/web.py:81 +#, fuzzy +msgid "In order to use this service, you should first install RGS Receiver." +msgstr "Afin d'utiliser ce service, vous devez d'abord installer cordon." + +#: transports/RGS/web.py:82 +#, fuzzy +msgid "HP Website" +msgstr "Site Web du cordon" + +#: transports/TSNX/TSNXTransport.py:55 +msgid "NX Transport (tunneled)" +msgstr "Transport NX (tunnel)" + +#: transports/TSNX/TSNXTransport.py:57 +msgid "NX Transport for tunneled connection" +msgstr "Transport NX pour connexion tunnelée" + +#: web/errors.py:53 +msgid "Unknown error" +msgstr "Erreur inconnue" + +#: web/errors.py:54 +msgid "Transport not found" +msgstr "Transport introuvable" + +#: web/errors.py:55 +msgid "Service not found" +msgstr "Service introuvable" + +#: web/errors.py:56 xmlrpc/auths/AdminAuth.py:147 +msgid "Access denied" +msgstr "Accès refusé" + +#: web/errors.py:57 +msgid "" +"Invalid service. The service is not available at this moment. Please, try " +"later" +msgstr "" +"Service non valide. Le service n'est pas disponible en ce moment. SVP, " +"essayez plus tard" + +#: web/errors.py:58 +msgid "Maximum services limit reached. Please, contact administrator" +msgstr "" +"Limite de services maximale atteinte. Veuillez contacter administrateur" + +#: web/errors.py:59 +msgid "You need to enable cookies to let this application work" +msgstr "Vous devez activer les cookies de laisser cette application" + +#: web/errors.py:60 +msgid "User service not found" +msgstr "Service utilisateur introuvable" + +#: web/forms/LoginForm.py:61 +msgid "Authenticator" +msgstr "Authentificateur" + +#: xmlrpc/auths/AdminAuth.py:91 +msgid "Credentials no longer valid" +msgstr "Informations d'identification n'est plus valides" + +#: xmlrpc/auths/AdminAuth.py:125 +msgid "Administration" +msgstr "Administration" + +#: xmlrpc/auths/AdminAuth.py:140 +msgid "Invalid credentials" +msgstr "Informations d'identification non valides" + +#: xmlrpc/auths/AdminAuth.py:145 +msgid "Invalid authenticator" +msgstr "Authentificateur non valide" + +#: xmlrpc/auths/Authenticators.py:104 +msgid "Authenticator does not exists" +msgstr "Authentificateur n'existe pas" + +#: xmlrpc/auths/Authenticators.py:158 xmlrpc/osmanagers/OSManagers.py:115 +#: xmlrpc/services/ServiceProviders.py:115 xmlrpc/services/Services.py:159 +#: xmlrpc/transports/Networks.py:86 xmlrpc/transports/Networks.py:97 +#, python-format +msgid "Name %s already exists" +msgstr "Nom %s existe déjà" + +#: xmlrpc/auths/Authenticators.py:229 +msgid "Authenticator do not supports search" +msgstr "Authenticateur ne soutient la recherche" + +#: xmlrpc/auths/Authenticators.py:235 +msgid "Specified authenticator do not exists anymore. Please, reload gui" +msgstr "Authentificateur spécifiée n'existe pas plus. Veuillez recharger gui" + +#: xmlrpc/auths/Authenticators.py:239 +msgid "BUG: Reached a point that should never have been reached!!!" +msgstr "BOGUE : Atteint un point qui devrait jamais été atteint!!!" + +#: xmlrpc/osmanagers/OSManagers.py:129 +msgid "This os mnager is being used by deployed services" +msgstr "" +"Ce gestionnaire de système d'exploitation est utilisé par les services de " +"déploiements" + +#: xmlrpc/osmanagers/OSManagers.py:145 +msgid "There is deployed services using this os manager" +msgstr "" +"Il y a des services déployées à l'aide de ce gestionnaire de système " +"d'exploitation" + +#: xmlrpc/osmanagers/OSManagers.py:147 +msgid "Can't find os manager" +msgstr "Impossible de trouver os gestionnaire" + +#: xmlrpc/services/DeployedServices.py:52 +msgid "Unknown" +msgstr "Inconnu" + +#: xmlrpc/services/DeployedServices.py:112 +#: xmlrpc/services/DeployedServices.py:176 +#: xmlrpc/services/DeployedServices.py:195 +#: xmlrpc/services/DeployedServices.py:211 +#: xmlrpc/services/DeployedServices.py:225 +#: xmlrpc/services/DeployedServices.py:252 +#: xmlrpc/services/DeployedServices.py:266 +msgid "Deployed Service does not exists" +msgstr "Service déployée n'existe pas" + +#: xmlrpc/services/DeployedServices.py:209 +msgid "Group does not exists" +msgstr "Groupe n'existe pas" + +#: xmlrpc/services/DeployedServices.py:237 +msgid "Can't find deployed service" +msgstr "Impossible de trouver le service déployé" + +#: xmlrpc/services/DeployedServices.py:250 +msgid "Transport does not exists" +msgstr "Transport n'existe pas" + +#: xmlrpc/services/DeployedServices.py:281 +msgid "Deployed service does not exists" +msgstr "Service déployée n'existe pas" + +#: xmlrpc/services/ServiceProviders.py:142 +msgid "Can't delete service provider with services associated" +msgstr "" +"Impossible de supprimer le fournisseur de service avec services associés" + +#: xmlrpc/services/ServiceProviders.py:145 +msgid "Can't locate the service provider" +msgstr "Impossible de trouver le fournisseur de services" + +#: xmlrpc/services/ServiceProviders.py:145 xmlrpc/services/Services.py:189 +#: xmlrpc/transports/Networks.py:70 xmlrpc/transports/Networks.py:78 +#: xmlrpc/transports/Networks.py:95 +msgid "Please, refresh interface" +msgstr "Veuillez actualiser interface" + +#: xmlrpc/services/Services.py:186 +msgid "Can't delete services with deployed services associated" +msgstr "Impossible de supprimer les services avec des services déployés liés" + +#: xmlrpc/services/Services.py:189 +msgid "Can't locate the service" +msgstr "Impossible de localiser le service" + +#: xmlrpc/services/UserDeployedServices.py:94 +#: xmlrpc/services/UserDeployedServices.py:110 +msgid "The deployed service is not active" +msgstr "Le service de déploiement n'est pas actif" + +#: xmlrpc/services/UserDeployedServices.py:97 +#: xmlrpc/services/UserDeployedServices.py:113 +msgid "This service don't allows assignations" +msgstr "Ce service ne permet l'assignation" + +#: xmlrpc/services/UserDeployedServices.py:102 +#: xmlrpc/services/UserDeployedServices.py:120 +msgid "Deployed service not found!!! (refresh interface)" +msgstr "Déployées service introuvable!!! (actualisation interface)" + +#: xmlrpc/services/UserDeployedServices.py:122 +msgid "User not found!!! (refresh interface)" +msgstr "Utilisateur introuvable!!! (actualisation interface)" + +#: xmlrpc/services/UserDeployedServices.py:141 +msgid "No error" +msgstr "Pas d'erreur" + +#: xmlrpc/services/UserDeployedServices.py:147 +msgid "User deployed service not found!!!" +msgstr "Service de l'utilisateur ne se trouve pas déployée!!!" + +#: xmlrpc/transports/Networks.py:70 +msgid "Can't locate the transport" +msgstr "Impossible de localiser le transport" + +#: xmlrpc/transports/Networks.py:78 xmlrpc/transports/Networks.py:95 +msgid "Can't locate the network" +msgstr "Impossible de localiser le réseau" + +#~ msgid "Base Service" +#~ msgstr "Service de base" + +#~ msgid "None" +#~ msgstr "Aucun" diff --git a/server/src/uds/management/__init__.py b/server/src/uds/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/management/commands/__init__.py b/server/src/uds/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/management/commands/config.py b/server/src/uds/management/commands/config.py new file mode 100644 index 000000000..69104596e --- /dev/null +++ b/server/src/uds/management/commands/config.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.core.management.base import BaseCommand +from optparse import make_option +from uds.core.util.Config import Config, GLOBAL_SECTION +import logging + +logger = logging.getLogger(__name__) + +class Command(BaseCommand): + args = "" + help = "Updates configuration values. If mod is omitted, UDS will be used. Omit whitespaces betwen name, =, and value (they must be a single param)" + + def handle(self, *args, **options): + logger.debug("Handling settings") + try: + for config in args: + print config + first, value = config.split('=') + first = first.split('.') + if len(first) == 2: + mod, name = first + else: + mod, name = GLOBAL_SECTION, first[0] + Config.update(mod, name, value) + except Exception: + logger.exception("Error") + diff --git a/server/src/uds/management/commands/taskManager.py b/server/src/uds/management/commands/taskManager.py new file mode 100644 index 000000000..a04f38898 --- /dev/null +++ b/server/src/uds/management/commands/taskManager.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option +from server.settings import SITE_ROOT, LOGDIR +from django.utils.daemonize import become_daemon +from uds.core.managers.TaskManager import TaskManager +import logging, sys, os, signal, time + +logger = logging.getLogger(__name__) + +PID_FILE = 'taskmanager.pid' + +def getPidFile(): + return SITE_ROOT + '/' + PID_FILE + +class Command(BaseCommand): + args = "None" + help = "Executes the task manager as a daemon. No parameter show current status of task manager" + + option_list = BaseCommand.option_list + ( + make_option('--start', + action='store_true', + dest='start', + default=False, + help='Starts a new daemon'), + make_option('--stop', + action='store_true', + dest='stop', + default=False, + help='Stop any running daemon'), + ) + + def handle(self, *args, **options): + logger.info("Running task manager command") + + start = options['start'] and True or False + stop = options['stop'] and True or False + + pid = None + try: + pid = int(file(getPidFile(), 'r').readline()) + except Exception: + pid = None + + if stop is True and pid is not None: + try: + logger.info('Stopping task manager. pid: {0}'.format(pid)) + os.kill( pid, signal.SIGTERM ) + time.sleep(1) # Wait a bit before running new one + os.unlink(getPidFile()) + except Exception: + logger.error("Could not stop task manager (maybe it's not runing?)") + os.unlink(getPidFile()) + + if start is True: + logger.info('Starting task manager.') + become_daemon(SITE_ROOT, LOGDIR + '/taskManagerStdout.log', LOGDIR + '/taskManagerStderr.log') + pid = str(os.getpid()) + file(getPidFile() ,'w+').write("%s\n" % pid) + + manager = TaskManager() + manager.run() + + if start is False and stop is False: + if pid is not None: + sys.stdout.write("Task manager found running (pid file exists: {0})\n".format(pid)) + else: + sys.stdout.write("Task manager not foud (pid file do not exits)\n") + \ No newline at end of file diff --git a/server/src/uds/migrations/0001_initial.py b/server/src/uds/migrations/0001_initial.py new file mode 100644 index 000000000..8437c4eb7 --- /dev/null +++ b/server/src/uds/migrations/0001_initial.py @@ -0,0 +1,537 @@ +# encoding: utf-8 +#@PydevCodeAnalysisIgnore +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'Provider' + db.create_table('uds_provider', ( + ('comments', self.gf('django.db.models.fields.CharField')(max_length=256)), + ('data', self.gf('django.db.models.fields.TextField')(default='')), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('data_type', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)), + )) + db.send_create_signal('uds', ['Provider']) + + # Adding model 'Service' + db.create_table('uds_service', ( + ('name', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('data_type', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('comments', self.gf('django.db.models.fields.CharField')(max_length=256)), + ('provider', self.gf('django.db.models.fields.related.ForeignKey')(related_name='services', to=orm['uds.Provider'])), + ('data', self.gf('django.db.models.fields.TextField')(default='')), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('uds', ['Service']) + + # Adding unique constraint on 'Service', fields ['provider', 'name'] + db.create_unique('uds_service', ['provider_id', 'name']) + + # Adding model 'OSManager' + db.create_table('uds_osmanager', ( + ('comments', self.gf('django.db.models.fields.CharField')(max_length=256)), + ('data', self.gf('django.db.models.fields.TextField')(default='')), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('data_type', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)), + )) + db.send_create_signal('uds', ['OSManager']) + + # Adding model 'Transport' + db.create_table('uds_transport', ( + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)), + ('data_type', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('comments', self.gf('django.db.models.fields.CharField')(max_length=256)), + ('priority', self.gf('django.db.models.fields.IntegerField')(default=0, db_index=True)), + ('nets_positive', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('data', self.gf('django.db.models.fields.TextField')(default='')), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('uds', ['Transport']) + + # Adding model 'Authenticator' + db.create_table('uds_authenticator', ( + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)), + ('data_type', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('comments', self.gf('django.db.models.fields.TextField')(default='')), + ('priority', self.gf('django.db.models.fields.IntegerField')(default=0, db_index=True)), + ('data', self.gf('django.db.models.fields.TextField')(default='')), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('uds', ['Authenticator']) + + # Adding model 'User' + db.create_table('uds_user', ( + ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('staff_member', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('manager', self.gf('django.db.models.fields.related.ForeignKey')(related_name='users', to=orm['uds.Authenticator'])), + ('comments', self.gf('django.db.models.fields.CharField')(max_length=256)), + ('real_name', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('state', self.gf('django.db.models.fields.CharField')(max_length=1, db_index=True)), + ('is_admin', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('last_access', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(1972, 7, 1, 0, 0))), + ('password', self.gf('django.db.models.fields.CharField')(default='', max_length=128)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('uds', ['User']) + + # Adding unique constraint on 'User', fields ['manager', 'name'] + db.create_unique('uds_user', ['manager_id', 'name']) + + # Adding model 'Group' + db.create_table('uds_group', ( + ('manager', self.gf('django.db.models.fields.related.ForeignKey')(related_name='groups', to=orm['uds.Authenticator'])), + ('state', self.gf('django.db.models.fields.CharField')(default='A', max_length=1, db_index=True)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('comments', self.gf('django.db.models.fields.CharField')(default='', max_length=256)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + )) + db.send_create_signal('uds', ['Group']) + + # Adding unique constraint on 'Group', fields ['manager', 'name'] + db.create_unique('uds_group', ['manager_id', 'name']) + + # Adding M2M table for field users on 'Group' + db.create_table('uds_group_users', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('group', models.ForeignKey(orm['uds.group'], null=False)), + ('user', models.ForeignKey(orm['uds.user'], null=False)) + )) + db.create_unique('uds_group_users', ['group_id', 'user_id']) + + # Adding model 'UserPreference' + db.create_table('uds_userpreference', ( + ('value', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='preferences', to=orm['uds.User'])), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('module', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)), + )) + db.send_create_signal('uds', ['UserPreference']) + + # Adding unique constraint on 'UserPreference', fields ['module', 'name'] + db.create_unique('uds_userpreference', ['module', 'name']) + + # Adding model 'DeployedService' + db.create_table('uds__deployed_service', ( + ('initial_srvs', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('osmanager', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='deployedServices', null=True, to=orm['uds.OSManager'])), + ('name', self.gf('django.db.models.fields.CharField')(default='', max_length=128)), + ('service', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='deployedServices', null=True, to=orm['uds.Service'])), + ('current_pub_revision', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)), + ('max_srvs', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('comments', self.gf('django.db.models.fields.CharField')(default='', max_length=256)), + ('authenticator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='deployedServices', null=True, to=orm['uds.Authenticator'])), + ('state', self.gf('django.db.models.fields.CharField')(default='A', max_length=1, db_index=True)), + ('cache_l2_srvs', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('cache_l1_srvs', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('uds', ['DeployedService']) + + # Adding M2M table for field transports on 'DeployedService' + db.create_table('uds__ds_trans', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('deployedservice', models.ForeignKey(orm['uds.deployedservice'], null=False)), + ('transport', models.ForeignKey(orm['uds.transport'], null=False)) + )) + db.create_unique('uds__ds_trans', ['deployedservice_id', 'transport_id']) + + # Adding M2M table for field assignedGroups on 'DeployedService' + db.create_table('uds__ds_grps', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('deployedservice', models.ForeignKey(orm['uds.deployedservice'], null=False)), + ('group', models.ForeignKey(orm['uds.group'], null=False)) + )) + db.create_unique('uds__ds_grps', ['deployedservice_id', 'group_id']) + + # Adding model 'DeployedServicePublication' + db.create_table('uds__deployed_service_pub', ( + ('deployed_service', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publications', to=orm['uds.DeployedService'])), + ('state', self.gf('django.db.models.fields.CharField')(default='P', max_length=1, db_index=True)), + ('state_date', self.gf('django.db.models.fields.DateTimeField')()), + ('publish_date', self.gf('django.db.models.fields.DateTimeField')(db_index=True)), + ('data', self.gf('django.db.models.fields.TextField')(default='')), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('revision', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)), + )) + db.send_create_signal('uds', ['DeployedServicePublication']) + + # Adding model 'UserService' + db.create_table('uds__user_service', ( + ('cache_level', self.gf('django.db.models.fields.PositiveSmallIntegerField')(default=0, db_index=True)), + ('publication', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='userServices', null=True, to=orm['uds.DeployedServicePublication'])), + ('in_use', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('os_state', self.gf('django.db.models.fields.CharField')(default='P', max_length=1)), + ('friendly_name', self.gf('django.db.models.fields.CharField')(default='', max_length=128)), + ('deployed_service', self.gf('django.db.models.fields.related.ForeignKey')(related_name='userServices', to=orm['uds.DeployedService'])), + ('creation_date', self.gf('django.db.models.fields.DateTimeField')(db_index=True)), + ('state', self.gf('django.db.models.fields.CharField')(default='P', max_length=1, db_index=True)), + ('state_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='userServices', null=True, blank=True, to=orm['uds.User'])), + ('in_use_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(1972, 7, 1, 0, 0))), + ('data', self.gf('django.db.models.fields.TextField')(default='')), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('unique_id', self.gf('django.db.models.fields.CharField')(default='', max_length=128, db_index=True)), + )) + db.send_create_signal('uds', ['UserService']) + + # Adding model 'Cache' + db.create_table('uds_utility_cache', ( + ('owner', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('validity', self.gf('django.db.models.fields.IntegerField')(default=60)), + ('value', self.gf('django.db.models.fields.TextField')(default='')), + ('key', self.gf('django.db.models.fields.CharField')(max_length=64, primary_key=True)), + ('created', self.gf('django.db.models.fields.DateTimeField')()), + )) + db.send_create_signal('uds', ['Cache']) + + # Adding model 'Config' + db.create_table('uds_configuration', ( + ('value', self.gf('django.db.models.fields.TextField')(default='')), + ('section', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=64)), + )) + db.send_create_signal('uds', ['Config']) + + # Adding unique constraint on 'Config', fields ['section', 'key'] + db.create_unique('uds_configuration', ['section', 'key']) + + # Adding model 'Storage' + db.create_table('uds_storage', ( + ('owner', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('data', self.gf('django.db.models.fields.CharField')(default='', max_length=1024)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=64, primary_key=True)), + ('attr1', self.gf('django.db.models.fields.CharField')(default=None, max_length=64, null=True, db_index=True, blank=True)), + )) + db.send_create_signal('uds', ['Storage']) + + # Adding model 'UniqueId' + db.create_table('uds_uniqueid', ( + ('owner', self.gf('django.db.models.fields.CharField')(default='', max_length=128, db_index=True)), + ('assigned', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True, blank=True)), + ('basename', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('seq', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)), + )) + db.send_create_signal('uds', ['UniqueId']) + + # Adding unique constraint on 'UniqueId', fields ['basename', 'seq'] + db.create_unique('uds_uniqueid', ['basename', 'seq']) + + # Adding model 'Scheduler' + db.create_table('uds_scheduler', ( + ('last_execution', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('frecuency', self.gf('django.db.models.fields.PositiveIntegerField')(default=86400)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('state', self.gf('django.db.models.fields.CharField')(default='X', max_length=1, db_index=True)), + ('next_execution', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(1972, 7, 1, 0, 0), db_index=True)), + ('owner_server', self.gf('django.db.models.fields.CharField')(default='', max_length=64, db_index=True)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=64)), + )) + db.send_create_signal('uds', ['Scheduler']) + + # Adding model 'DelayedTask' + db.create_table('uds_delayedtask', ( + ('insert_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('execution_delay', self.gf('django.db.models.fields.PositiveIntegerField')()), + ('execution_time', self.gf('django.db.models.fields.DateTimeField')(db_index=True)), + ('instance', self.gf('django.db.models.fields.TextField')()), + ('tag', self.gf('django.db.models.fields.CharField')(max_length=64, db_index=True)), + ('type', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('uds', ['DelayedTask']) + + # Adding model 'Network' + db.create_table('uds_network', ( + ('net_start', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)), + ('net_end', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=64)), + )) + db.send_create_signal('uds', ['Network']) + + # Adding M2M table for field transports on 'Network' + db.create_table('uds_net_trans', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('network', models.ForeignKey(orm['uds.network'], null=False)), + ('transport', models.ForeignKey(orm['uds.transport'], null=False)) + )) + db.create_unique('uds_net_trans', ['network_id', 'transport_id']) + + + def backwards(self, orm): + + # Deleting model 'Provider' + db.delete_table('uds_provider') + + # Deleting model 'Service' + db.delete_table('uds_service') + + # Removing unique constraint on 'Service', fields ['provider', 'name'] + db.delete_unique('uds_service', ['provider_id', 'name']) + + # Deleting model 'OSManager' + db.delete_table('uds_osmanager') + + # Deleting model 'Transport' + db.delete_table('uds_transport') + + # Deleting model 'Authenticator' + db.delete_table('uds_authenticator') + + # Deleting model 'User' + db.delete_table('uds_user') + + # Removing unique constraint on 'User', fields ['manager', 'name'] + db.delete_unique('uds_user', ['manager_id', 'name']) + + # Deleting model 'Group' + db.delete_table('uds_group') + + # Removing unique constraint on 'Group', fields ['manager', 'name'] + db.delete_unique('uds_group', ['manager_id', 'name']) + + # Removing M2M table for field users on 'Group' + db.delete_table('uds_group_users') + + # Deleting model 'UserPreference' + db.delete_table('uds_userpreference') + + # Removing unique constraint on 'UserPreference', fields ['module', 'name'] + db.delete_unique('uds_userpreference', ['module', 'name']) + + # Deleting model 'DeployedService' + db.delete_table('uds__deployed_service') + + # Removing M2M table for field transports on 'DeployedService' + db.delete_table('uds__ds_trans') + + # Removing M2M table for field assignedGroups on 'DeployedService' + db.delete_table('uds__ds_grps') + + # Deleting model 'DeployedServicePublication' + db.delete_table('uds__deployed_service_pub') + + # Deleting model 'UserService' + db.delete_table('uds__user_service') + + # Deleting model 'Cache' + db.delete_table('uds_utility_cache') + + # Deleting model 'Config' + db.delete_table('uds_configuration') + + # Removing unique constraint on 'Config', fields ['section', 'key'] + db.delete_unique('uds_configuration', ['section', 'key']) + + # Deleting model 'Storage' + db.delete_table('uds_storage') + + # Deleting model 'UniqueId' + db.delete_table('uds_uniqueid') + + # Removing unique constraint on 'UniqueId', fields ['basename', 'seq'] + db.delete_unique('uds_uniqueid', ['basename', 'seq']) + + # Deleting model 'Scheduler' + db.delete_table('uds_scheduler') + + # Deleting model 'DelayedTask' + db.delete_table('uds_delayedtask') + + # Deleting model 'Network' + db.delete_table('uds_network') + + # Removing M2M table for field transports on 'Network' + db.delete_table('uds_net_trans') + + + models = { + 'uds.authenticator': { + 'Meta': {'object_name': 'Authenticator'}, + 'comments': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.cache': { + 'Meta': {'object_name': 'Cache', 'db_table': "'uds_utility_cache'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'validity': ('django.db.models.fields.IntegerField', [], {'default': '60'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.config': { + 'Meta': {'unique_together': "(('section', 'key'),)", 'object_name': 'Config', 'db_table': "'uds_configuration'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.delayedtask': { + 'Meta': {'object_name': 'DelayedTask'}, + 'execution_delay': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'execution_time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'insert_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'instance': ('django.db.models.fields.TextField', [], {}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'uds.deployedservice': { + 'Meta': {'object_name': 'DeployedService', 'db_table': "'uds__deployed_service'"}, + 'assignedGroups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_grps'", 'to': "orm['uds.Group']"}), + 'authenticator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Authenticator']"}), + 'cache_l1_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'cache_l2_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'current_pub_revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initial_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'max_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'osmanager': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.OSManager']"}), + 'service': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Service']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.deployedservicepublication': { + 'Meta': {'object_name': 'DeployedServicePublication', 'db_table': "'uds__deployed_service_pub'"}, + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publications'", 'to': "orm['uds.DeployedService']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'publish_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {}) + }, + 'uds.group': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'Group'}, + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['uds.User']"}) + }, + 'uds.network': { + 'Meta': {'object_name': 'Network'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'net_end': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'net_start': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'networks'", 'symmetrical': 'False', 'db_table': "'uds_net_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.osmanager': { + 'Meta': {'object_name': 'OSManager'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.provider': { + 'Meta': {'object_name': 'Provider'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.scheduler': { + 'Meta': {'object_name': 'Scheduler'}, + 'frecuency': ('django.db.models.fields.PositiveIntegerField', [], {'default': '86400'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_execution': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'next_execution': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)', 'db_index': 'True'}), + 'owner_server': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'X'", 'max_length': '1', 'db_index': 'True'}) + }, + 'uds.service': { + 'Meta': {'unique_together': "(('provider', 'name'),)", 'object_name': 'Service'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'services'", 'to': "orm['uds.Provider']"}) + }, + 'uds.storage': { + 'Meta': {'object_name': 'Storage'}, + 'attr1': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.transport': { + 'Meta': {'object_name': 'Transport'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'nets_positive': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.uniqueid': { + 'Meta': {'unique_together': "(('basename', 'seq'),)", 'object_name': 'UniqueId'}, + 'assigned': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}), + 'basename': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'seq': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}) + }, + 'uds.user': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'User'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_access': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'users'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'staff_member': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '1', 'db_index': 'True'}) + }, + 'uds.userpreference': { + 'Meta': {'unique_together': "(('module', 'name'),)", 'object_name': 'UserPreference'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'preferences'", 'to': "orm['uds.User']"}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.userservice': { + 'Meta': {'object_name': 'UserService', 'db_table': "'uds__user_service'"}, + 'cache_level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'userServices'", 'to': "orm['uds.DeployedService']"}), + 'friendly_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_use': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'in_use_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'os_state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1'}), + 'publication': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'userServices'", 'null': 'True', 'to': "orm['uds.DeployedServicePublication']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'unique_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'userServices'", 'null': 'True', 'blank': 'True', 'to': "orm['uds.User']"}) + } + } + + complete_apps = ['uds'] diff --git a/server/src/uds/migrations/0002_auto__del_unique_userpreference_name_module.py b/server/src/uds/migrations/0002_auto__del_unique_userpreference_name_module.py new file mode 100644 index 000000000..6484d28be --- /dev/null +++ b/server/src/uds/migrations/0002_auto__del_unique_userpreference_name_module.py @@ -0,0 +1,200 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Removing unique constraint on 'UserPreference', fields ['name', 'module'] + db.delete_unique('uds_userpreference', ['name', 'module']) + + + def backwards(self, orm): + + # Adding unique constraint on 'UserPreference', fields ['name', 'module'] + db.create_unique('uds_userpreference', ['name', 'module']) + + + models = { + 'uds.authenticator': { + 'Meta': {'object_name': 'Authenticator'}, + 'comments': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.cache': { + 'Meta': {'object_name': 'Cache', 'db_table': "'uds_utility_cache'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'validity': ('django.db.models.fields.IntegerField', [], {'default': '60'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.config': { + 'Meta': {'unique_together': "(('section', 'key'),)", 'object_name': 'Config', 'db_table': "'uds_configuration'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.delayedtask': { + 'Meta': {'object_name': 'DelayedTask'}, + 'execution_delay': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'execution_time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'insert_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'instance': ('django.db.models.fields.TextField', [], {}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'uds.deployedservice': { + 'Meta': {'object_name': 'DeployedService', 'db_table': "'uds__deployed_service'"}, + 'assignedGroups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_grps'", 'to': "orm['uds.Group']"}), + 'authenticator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Authenticator']"}), + 'cache_l1_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'cache_l2_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'current_pub_revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initial_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'max_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'osmanager': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.OSManager']"}), + 'service': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Service']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.deployedservicepublication': { + 'Meta': {'object_name': 'DeployedServicePublication', 'db_table': "'uds__deployed_service_pub'"}, + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publications'", 'to': "orm['uds.DeployedService']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'publish_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {}) + }, + 'uds.group': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'Group'}, + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['uds.User']"}) + }, + 'uds.network': { + 'Meta': {'object_name': 'Network'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'net_end': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'net_start': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'networks'", 'symmetrical': 'False', 'db_table': "'uds_net_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.osmanager': { + 'Meta': {'object_name': 'OSManager'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.provider': { + 'Meta': {'object_name': 'Provider'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.scheduler': { + 'Meta': {'object_name': 'Scheduler'}, + 'frecuency': ('django.db.models.fields.PositiveIntegerField', [], {'default': '86400'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_execution': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'next_execution': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)', 'db_index': 'True'}), + 'owner_server': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'X'", 'max_length': '1', 'db_index': 'True'}) + }, + 'uds.service': { + 'Meta': {'unique_together': "(('provider', 'name'),)", 'object_name': 'Service'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'services'", 'to': "orm['uds.Provider']"}) + }, + 'uds.storage': { + 'Meta': {'object_name': 'Storage'}, + 'attr1': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.transport': { + 'Meta': {'object_name': 'Transport'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'nets_positive': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.uniqueid': { + 'Meta': {'unique_together': "(('basename', 'seq'),)", 'object_name': 'UniqueId'}, + 'assigned': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}), + 'basename': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'seq': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}) + }, + 'uds.user': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'User'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_access': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'users'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'staff_member': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '1', 'db_index': 'True'}) + }, + 'uds.userpreference': { + 'Meta': {'object_name': 'UserPreference'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'preferences'", 'to': "orm['uds.User']"}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.userservice': { + 'Meta': {'object_name': 'UserService', 'db_table': "'uds__user_service'"}, + 'cache_level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'userServices'", 'to': "orm['uds.DeployedService']"}), + 'friendly_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_use': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'in_use_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'os_state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1'}), + 'publication': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'userServices'", 'null': 'True', 'to': "orm['uds.DeployedServicePublication']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'unique_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'userServices'", 'null': 'True', 'blank': 'True', 'to': "orm['uds.User']"}) + } + } + + complete_apps = ['uds'] diff --git a/server/src/uds/migrations/0003_auto__del_field_deployedservice_authenticator.py b/server/src/uds/migrations/0003_auto__del_field_deployedservice_authenticator.py new file mode 100644 index 000000000..cabe40d02 --- /dev/null +++ b/server/src/uds/migrations/0003_auto__del_field_deployedservice_authenticator.py @@ -0,0 +1,199 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'DeployedService.authenticator' + db.delete_column('uds__deployed_service', 'authenticator_id') + + + def backwards(self, orm): + + # Adding field 'DeployedService.authenticator' + db.add_column('uds__deployed_service', 'authenticator', self.gf('django.db.models.fields.related.ForeignKey')(related_name='deployedServices', null=True, to=orm['uds.Authenticator'], blank=True), keep_default=False) + + + models = { + 'uds.authenticator': { + 'Meta': {'object_name': 'Authenticator'}, + 'comments': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.cache': { + 'Meta': {'object_name': 'Cache', 'db_table': "'uds_utility_cache'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'validity': ('django.db.models.fields.IntegerField', [], {'default': '60'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.config': { + 'Meta': {'unique_together': "(('section', 'key'),)", 'object_name': 'Config', 'db_table': "'uds_configuration'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.delayedtask': { + 'Meta': {'object_name': 'DelayedTask'}, + 'execution_delay': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'execution_time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'insert_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'instance': ('django.db.models.fields.TextField', [], {}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'uds.deployedservice': { + 'Meta': {'object_name': 'DeployedService', 'db_table': "'uds__deployed_service'"}, + 'assignedGroups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_grps'", 'to': "orm['uds.Group']"}), + 'cache_l1_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'cache_l2_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'current_pub_revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initial_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'max_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'osmanager': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.OSManager']"}), + 'service': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Service']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.deployedservicepublication': { + 'Meta': {'object_name': 'DeployedServicePublication', 'db_table': "'uds__deployed_service_pub'"}, + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publications'", 'to': "orm['uds.DeployedService']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'publish_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {}) + }, + 'uds.group': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'Group'}, + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['uds.User']"}) + }, + 'uds.network': { + 'Meta': {'object_name': 'Network'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'net_end': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'net_start': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'networks'", 'symmetrical': 'False', 'db_table': "'uds_net_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.osmanager': { + 'Meta': {'object_name': 'OSManager'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.provider': { + 'Meta': {'object_name': 'Provider'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.scheduler': { + 'Meta': {'object_name': 'Scheduler'}, + 'frecuency': ('django.db.models.fields.PositiveIntegerField', [], {'default': '86400'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_execution': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'next_execution': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)', 'db_index': 'True'}), + 'owner_server': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'X'", 'max_length': '1', 'db_index': 'True'}) + }, + 'uds.service': { + 'Meta': {'unique_together': "(('provider', 'name'),)", 'object_name': 'Service'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'services'", 'to': "orm['uds.Provider']"}) + }, + 'uds.storage': { + 'Meta': {'object_name': 'Storage'}, + 'attr1': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.transport': { + 'Meta': {'object_name': 'Transport'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'nets_positive': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.uniqueid': { + 'Meta': {'unique_together': "(('basename', 'seq'),)", 'object_name': 'UniqueId'}, + 'assigned': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}), + 'basename': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'seq': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}) + }, + 'uds.user': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'User'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_access': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'users'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'staff_member': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '1', 'db_index': 'True'}) + }, + 'uds.userpreference': { + 'Meta': {'object_name': 'UserPreference'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'preferences'", 'to': "orm['uds.User']"}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.userservice': { + 'Meta': {'object_name': 'UserService', 'db_table': "'uds__user_service'"}, + 'cache_level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'userServices'", 'to': "orm['uds.DeployedService']"}), + 'friendly_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_use': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'in_use_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'os_state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1'}), + 'publication': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'userServices'", 'null': 'True', 'to': "orm['uds.DeployedServicePublication']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'unique_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'userServices'", 'null': 'True', 'blank': 'True', 'to': "orm['uds.User']"}) + } + } + + complete_apps = ['uds'] diff --git a/server/src/uds/migrations/0004_auto__add_field_deployedservice_state_date.py b/server/src/uds/migrations/0004_auto__add_field_deployedservice_state_date.py new file mode 100644 index 000000000..385403c1f --- /dev/null +++ b/server/src/uds/migrations/0004_auto__add_field_deployedservice_state_date.py @@ -0,0 +1,200 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'DeployedService.state_date' + db.add_column('uds__deployed_service', 'state_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(1972, 7, 1, 0, 0)), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'DeployedService.state_date' + db.delete_column('uds__deployed_service', 'state_date') + + + models = { + 'uds.authenticator': { + 'Meta': {'object_name': 'Authenticator'}, + 'comments': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.cache': { + 'Meta': {'object_name': 'Cache', 'db_table': "'uds_utility_cache'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'validity': ('django.db.models.fields.IntegerField', [], {'default': '60'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.config': { + 'Meta': {'unique_together': "(('section', 'key'),)", 'object_name': 'Config', 'db_table': "'uds_configuration'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.delayedtask': { + 'Meta': {'object_name': 'DelayedTask'}, + 'execution_delay': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'execution_time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'insert_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'instance': ('django.db.models.fields.TextField', [], {}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'uds.deployedservice': { + 'Meta': {'object_name': 'DeployedService', 'db_table': "'uds__deployed_service'"}, + 'assignedGroups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_grps'", 'to': "orm['uds.Group']"}), + 'cache_l1_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'cache_l2_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'current_pub_revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initial_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'max_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'osmanager': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.OSManager']"}), + 'service': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Service']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.deployedservicepublication': { + 'Meta': {'object_name': 'DeployedServicePublication', 'db_table': "'uds__deployed_service_pub'"}, + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publications'", 'to': "orm['uds.DeployedService']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'publish_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {}) + }, + 'uds.group': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'Group'}, + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['uds.User']"}) + }, + 'uds.network': { + 'Meta': {'object_name': 'Network'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'net_end': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'net_start': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'networks'", 'symmetrical': 'False', 'db_table': "'uds_net_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.osmanager': { + 'Meta': {'object_name': 'OSManager'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.provider': { + 'Meta': {'object_name': 'Provider'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.scheduler': { + 'Meta': {'object_name': 'Scheduler'}, + 'frecuency': ('django.db.models.fields.PositiveIntegerField', [], {'default': '86400'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_execution': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'next_execution': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)', 'db_index': 'True'}), + 'owner_server': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'X'", 'max_length': '1', 'db_index': 'True'}) + }, + 'uds.service': { + 'Meta': {'unique_together': "(('provider', 'name'),)", 'object_name': 'Service'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'services'", 'to': "orm['uds.Provider']"}) + }, + 'uds.storage': { + 'Meta': {'object_name': 'Storage'}, + 'attr1': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.transport': { + 'Meta': {'object_name': 'Transport'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'nets_positive': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.uniqueid': { + 'Meta': {'unique_together': "(('basename', 'seq'),)", 'object_name': 'UniqueId'}, + 'assigned': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}), + 'basename': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'seq': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}) + }, + 'uds.user': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'User'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_access': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'users'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'staff_member': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '1', 'db_index': 'True'}) + }, + 'uds.userpreference': { + 'Meta': {'object_name': 'UserPreference'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'preferences'", 'to': "orm['uds.User']"}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.userservice': { + 'Meta': {'object_name': 'UserService', 'db_table': "'uds__user_service'"}, + 'cache_level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'userServices'", 'to': "orm['uds.DeployedService']"}), + 'friendly_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_use': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'in_use_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'os_state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1'}), + 'publication': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'userServices'", 'null': 'True', 'to': "orm['uds.DeployedServicePublication']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'unique_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'userServices'", 'null': 'True', 'blank': 'True', 'to': "orm['uds.User']"}) + } + } + + complete_apps = ['uds'] diff --git a/server/src/uds/migrations/0005_auto__add_field_config_crypt.py b/server/src/uds/migrations/0005_auto__add_field_config_crypt.py new file mode 100644 index 000000000..48312f695 --- /dev/null +++ b/server/src/uds/migrations/0005_auto__add_field_config_crypt.py @@ -0,0 +1,201 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Config.crypt' + db.add_column('uds_configuration', 'crypt', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Config.crypt' + db.delete_column('uds_configuration', 'crypt') + + + models = { + 'uds.authenticator': { + 'Meta': {'object_name': 'Authenticator'}, + 'comments': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.cache': { + 'Meta': {'object_name': 'Cache', 'db_table': "'uds_utility_cache'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'validity': ('django.db.models.fields.IntegerField', [], {'default': '60'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.config': { + 'Meta': {'unique_together': "(('section', 'key'),)", 'object_name': 'Config', 'db_table': "'uds_configuration'"}, + 'crypt': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'value': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + 'uds.delayedtask': { + 'Meta': {'object_name': 'DelayedTask'}, + 'execution_delay': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'execution_time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'insert_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'instance': ('django.db.models.fields.TextField', [], {}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'uds.deployedservice': { + 'Meta': {'object_name': 'DeployedService', 'db_table': "'uds__deployed_service'"}, + 'assignedGroups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_grps'", 'to': "orm['uds.Group']"}), + 'cache_l1_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'cache_l2_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'current_pub_revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initial_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'max_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'osmanager': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.OSManager']"}), + 'service': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Service']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.deployedservicepublication': { + 'Meta': {'object_name': 'DeployedServicePublication', 'db_table': "'uds__deployed_service_pub'"}, + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publications'", 'to': "orm['uds.DeployedService']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'publish_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {}) + }, + 'uds.group': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'Group'}, + 'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['uds.User']"}) + }, + 'uds.network': { + 'Meta': {'object_name': 'Network'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'net_end': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'net_start': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'networks'", 'symmetrical': 'False', 'db_table': "'uds_net_trans'", 'to': "orm['uds.Transport']"}) + }, + 'uds.osmanager': { + 'Meta': {'object_name': 'OSManager'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.provider': { + 'Meta': {'object_name': 'Provider'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'uds.scheduler': { + 'Meta': {'object_name': 'Scheduler'}, + 'frecuency': ('django.db.models.fields.PositiveIntegerField', [], {'default': '86400'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_execution': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'next_execution': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)', 'db_index': 'True'}), + 'owner_server': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'db_index': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'X'", 'max_length': '1', 'db_index': 'True'}) + }, + 'uds.service': { + 'Meta': {'unique_together': "(('provider', 'name'),)", 'object_name': 'Service'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'services'", 'to': "orm['uds.Provider']"}) + }, + 'uds.storage': { + 'Meta': {'object_name': 'Storage'}, + 'attr1': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.transport': { + 'Meta': {'object_name': 'Transport'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'nets_positive': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'uds.uniqueid': { + 'Meta': {'unique_together': "(('basename', 'seq'),)", 'object_name': 'UniqueId'}, + 'assigned': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}), + 'basename': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'seq': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}) + }, + 'uds.user': { + 'Meta': {'unique_together': "(('manager', 'name'),)", 'object_name': 'User'}, + 'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_access': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'users'", 'to': "orm['uds.Authenticator']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'staff_member': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '1', 'db_index': 'True'}) + }, + 'uds.userpreference': { + 'Meta': {'object_name': 'UserPreference'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'preferences'", 'to': "orm['uds.User']"}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + 'uds.userservice': { + 'Meta': {'object_name': 'UserService', 'db_table': "'uds__user_service'"}, + 'cache_level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'userServices'", 'to': "orm['uds.DeployedService']"}), + 'friendly_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_use': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'in_use_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}), + 'os_state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1'}), + 'publication': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'userServices'", 'null': 'True', 'to': "orm['uds.DeployedServicePublication']"}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}), + 'state_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'unique_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'userServices'", 'null': 'True', 'blank': 'True', 'to': "orm['uds.User']"}) + } + } + + complete_apps = ['uds'] diff --git a/server/src/uds/migrations/__init__.py b/server/src/uds/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/uds/models.py b/server/src/uds/models.py new file mode 100644 index 000000000..a3e006464 --- /dev/null +++ b/server/src/uds/models.py @@ -0,0 +1,1652 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.db import models +from django.db.models import signals +from uds.core.services.ServiceProviderFactory import ServiceProviderFactory +from uds.core.osmanagers.OSManagersFactory import OSManagersFactory +from uds.core.transports.TransportsFactory import TransportsFactory +from uds.core.jobs.JobsFactory import JobsFactory +from uds.core.Environment import Environment +from uds.core.util.db.LockingManager import LockingManager +from uds.core.util.State import State +from uds.core import auths +from uds.core.services.Exceptions import InvalidServiceException +from datetime import datetime, timedelta + +import logging + +logger = logging.getLogger(__name__) + +NEVER = datetime(1972, 7, 1) + + +def getSqlDatetime(): + ''' + Returns the current date/time of the database server. + + We use this time as method of keeping all operations betwen different servers in sync. + + We support get database datetime for: + * mysql + * sqlite + ''' + from django.db import connection + con = connection + cursor = con.cursor() + if con.vendor == 'mysql': + cursor.execute('SELECT NOW()') + return cursor.fetchone()[0] + return datetime.now() # If not know how to get database datetime, returns local datetime (this is fine for sqlite, which is local) + + + +# Services +class Provider(models.Model): + ''' + A Provider represents the Service provider itself, (i.e. a KVM Server or a Terminal Server) + ''' + name = models.CharField(max_length=128, unique = True) + data_type = models.CharField(max_length=128) + data = models.TextField(default='') + comments = models.CharField(max_length = 256) + + class Meta: + ''' + Meta class to declare default order + ''' + ordering = ('name',) + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def getInstance(self, values = None): + ''' + Instantiates the object this record contains. + + Every single record of Provider model, represents an object. + + Args: + values (list): Values to pass to constructor. If no values are especified, + the object is instantiated empty and them de-serialized from stored data. + + Returns: + The instance Instance of the class this provider represents + + Raises: + ''' + spType = self.getType() + env = self.getEnvironment() + sp = spType(env, values) + + # Only unserializes if this is not initialized via user interface and + # data contains something + if values == None and self.data != None and self.data != '': + sp.unserialize(self.data) + return sp + + def getType(self): + ''' + Get the type of the object this record represents. + + The type is Python type, it obtains this type from ServiceProviderFactory and associated record field. + + Returns: + The python type for this record object + ''' + return ServiceProviderFactory.factory().lookup(self.data_type) + + def __unicode__(self): + return "{0} of type {1} (id:{2})".format(self.name, self.data_type, self.id) + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Provider class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + # Only tries to get instance if data is not empty + if toDelete.data != '': + s = toDelete.getInstance() + s.destroy() + s.env().clearRelatedData() + + logger.debug('Before delete service provider '.format(toDelete)) + +#: Connects a pre deletion signal to Provider +signals.pre_delete.connect(Provider.beforeDelete, sender = Provider) + +class Service(models.Model): + ''' + A Service represents an specidied type of service offered to final users, with it configuration (i.e. a KVM Base Machine for cloning + or a Terminal Server configuration). + ''' + provider = models.ForeignKey(Provider, related_name='services') + name = models.CharField(max_length=128, unique = False) + data_type = models.CharField(max_length=128) + data = models.TextField(default='') + comments = models.CharField(max_length = 256) + + class Meta: + ''' + Meta class to declare default order and unique multiple field index + ''' + ordering = ('name',) + unique_together = (("provider", "name"),) + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def getInstance(self, values = None): + ''' + Instantiates the object this record contains. + + Every single record of Provider model, represents an object. + + Args: + values (list): Values to pass to constructor. If no values are especified, + the object is instantiated empty and them de-serialized from stored data. + + Returns: + The instance Instance of the class this provider represents + + Raises: + ''' + prov = self.provider.getInstance() + sType = prov.getServiceByType(self.data_type) + env = self.getEnvironment() + s = sType(env, prov, values) + # Only unserializes if this is not initialized via user interface and + # data contains something + if values == None and self.data != None and self.data != '': + s.unserialize(self.data) + return s + + def getType(self): + ''' + Get the type of the object this record represents. + + The type is Python type, it obtains this type from ServiceProviderFactory and associated record field. + + Returns: + The python type for this record object + + :note: We only need to get info from this, not access specific data (class specific info) + ''' + return self.provider.getType().getServiceByType(self.data_type) + + def __unicode__(self): + return "{0} of type {1} (id:{2})".format(self.name, self.data_type, self.id) + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + # Only tries to get instance if data is not empty + if toDelete.data != '': + s = toDelete.getInstance() + s.destroy() + s.env().clearRelatedData() + + logger.debug('Before delete service '.format(toDelete)) + +#: Connects a pre deletion signal to Service +signals.pre_delete.connect(Service.beforeDelete, sender = Service) + + +class OSManager(models.Model): + ''' + An OS Manager represents a manager for responding requests for agents inside services. + ''' + name = models.CharField(max_length=128, unique = True) + data_type = models.CharField(max_length=128) + data = models.TextField(default='') + comments = models.CharField(max_length = 256) + + class Meta: + ''' + Meta class to declare default order + ''' + ordering = ('name',) + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def getInstance(self, values = None): + ''' + Instantiates the object this record contains. + + Every single record of Provider model, represents an object. + + Args: + values (list): Values to pass to constructor. If no values are especified, + the object is instantiated empty and them de-serialized from stored data. + + Returns: + The instance Instance of the class this provider represents + + Raises: + ''' + osType = self.getType() + env = self.getEnvironment() + os = osType(env, values) + # Only unserializes if this is not initialized via user interface and + # data contains something + if values == None and self.data != None and self.data != '': + os.unserialize(self.data) + return os + + def getType(self): + ''' + Get the type of the object this record represents. + + The type is Python type, it obtains this type from ServiceProviderFactory and associated record field. + + Returns: + The python type for this record object + + :note: We only need to get info from this, not access specific data (class specific info) + ''' + # We only need to get info from this, not access specific data (class specific info + return OSManagersFactory.factory().lookup(self.data_type) + + + def __unicode__(self): + return "{0} of type {1} (id:{2})".format(self.name, self.data_type, self.id) + + def remove(self): + ''' + Removes this OS Manager only if there is no associated deployed service using it. + + Returns: + True if the object has been removed + + False if the object can't be removed because it is being used by some DeployedService + + Raises: + ''' + if self.deployedServices.all().count() > 0: + return False + self.delete() + return True + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + # Only tries to get instance if data is not empty + if toDelete.data != '': + s = toDelete.getInstance() + s.destroy() + s.env().clearRelatedData() + + logger.debug('Before delete os manager '.format(toDelete)) + +#: Connects a pre deletion signal to OS Manager +signals.pre_delete.connect(OSManager.beforeDelete, sender = OSManager) + +class Transport(models.Model): + ''' + A Transport represents a way of connecting the user with the service. + + Sample of transports are RDP, Spice, Web file uploader, etc... + ''' + name = models.CharField(max_length=128, unique = True) + data_type = models.CharField(max_length=128) + data = models.TextField(default='') + comments = models.CharField(max_length = 256) + priority = models.IntegerField(default=0, db_index=True) + nets_positive = models.BooleanField(default=False) + + class Meta: + ''' + Meta class to declare default order + ''' + ordering = ('name',) + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def getInstance(self, values = None): + ''' + Instantiates the object this record contains. + + Every single record of Provider model, represents an object. + + Args: + values (list): Values to pass to constructor. If no values are especified, + the object is instantiated empty and them de-serialized from stored data. + + Returns: + The instance Instance of the class this provider represents + + Raises: + ''' + tType = self.getType() + env = self.getEnvironment() + tr = tType(env, values) + # Only unserializes if this is not initialized via user interface and + # data contains something + if values == None and self.data != None and self.data != '': + tr.unserialize(self.data) + return tr + + def getType(self): + ''' + Get the type of the object this record represents. + + The type is Python type, it obtains this type from ServiceProviderFactory and associated record field. + + Returns: + The python type for this record object + + :note: We only need to get info from this, not access specific data (class specific info) + ''' + return TransportsFactory.factory().lookup(self.data_type) + + def validForIp(self, ip): + ''' + Checks if this transport is valid for the specified IP. + + Args: + ip: Numeric ip address to check validity for. (xxx.xxx.xxx.xxx). + + Returns: + True if the ip can access this Transport. + + False if the ip can't access this Transport. + + The ip check is done this way: + * If The associated network is empty, the result is always True + * If the associated network is not empty, and nets_positive (field) is True, the result will be True if + the ip is contained in any subnet associated with this transport. + * If the associated network is empty, and nets_positive (field) is False, the result will be True if + the ip is NOT contained in ANY subnet associated with this transport. + + Raises: + + :note: Ip addresses has been only tested with IPv4 addresses + ''' + if self.networks.count() == 0: + return True + ip = Network.ipToLong(ip) + if self.nets_positive: + return self.networks.filter(net_start__lte=ip, net_end__gte=ip).count() > 0 + else: + return self.networks.exclude(net_start__lte=ip, net_end__gte=ip).count() > 0 + + def __unicode__(self): + return "{0} of type {1} (id:{2})".format(self.name, self.data_type, self.id) + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + + # Only tries to get instance if data is not empty + if toDelete.data != '': + s = toDelete.getInstance() + s.destroy() + s.env().clearRelatedData() + + logger.debug('Before delete transport '.format(toDelete)) + +#: Connects a pre deletion signal to OS Manager +signals.pre_delete.connect(Transport.beforeDelete, sender = Transport) + +# Authenticators +class Authenticator(models.Model): + ''' + This class represents an Authenticator inside the platform. + Sample authenticators are LDAP, Active Directory, SAML, ... + ''' + name = models.CharField(max_length=128, unique = True) + data_type = models.CharField(max_length=128) + data = models.TextField(default='') + comments = models.TextField(default='') + priority = models.IntegerField(default=0, db_index = True) + + class Meta: + ''' + Meta class to declare default order + ''' + ordering = ('name',) + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def getInstance(self, values = None): + ''' + Instantiates the object this record contains. + + Every single record of Provider model, represents an object. + + Args: + values (list): Values to pass to constructor. If no values are especified, + the object is instantiated empty and them de-serialized from stored data. + + Returns: + The instance Instance of the class this provider represents + + Raises: + ''' + auType = self.getType() + env = self.getEnvironment() + auth = auType(self, env, values) + # Only unserializes if this is not initialized via user interface and + # data contains something + if values == None and self.data != None and self.data != '': + auth.unserialize(self.data) + return auth + + def getType(self): + ''' + Get the type of the object this record represents. + + The type is Python type, it obtains this type from ServiceProviderFactory and associated record field. + + Returns: + The python type for this record object + + :note: We only need to get info from this, not access specific data (class specific info) + ''' + return auths.factory().lookup(self.data_type) + + def getOrCreateUser(self, username, realName = None): + ''' + Used to get or create a new user at database associated with this authenticator. + + This user has all parameter default, that are: + * 'real_name':realName + * 'last_access':NEVER + * 'state':State.ACTIVE + + Args: + username: The username to create and associate with this auhtenticator + + realName: If None, it will be the same that username. If otherwise especified, it will be the default real_name (field) + + Returns: + True if the ip can access this Transport. + + False if the ip can't access this Transport. + + The ip check is done this way: + * If The associated network is empty, the result is always True + * If the associated network is not empty, and nets_positive (field) is True, the result will be True if + the ip is contained in any subnet associated with this transport. + * If the associated network is empty, and nets_positive (field) is False, the result will be True if + the ip is NOT contained in ANY subnet associated with this transport. + + Raises: + + + ''' + if realName is None: + realName = username + user, _ = self.users.get_or_create( name = username, defaults = { 'real_name':realName, 'last_access':NEVER, 'state':State.ACTIVE } ) + return user + + def isValidUser(self, username, falseIfNotExists = True): + ''' + Checks the validity of an user + + Args: + username: Name of the user to check + + falseIfNotExists: Defaults to True. It is used so we can return a value defined by caller. + + One example of falseIfNotExists using as True is for checking that the user is active or it doesn't exists. + + Returns: + True if it exists and is active, falseIfNotExists (param) if it doesn't exists + + This is done so we can check non existing or non blocked users (state != Active, or do not exists) + ''' + try: + u = self.users.get(name=username) + return State.isActive(u.state) + except Exception: + return falseIfNotExists + + def __unicode__(self): + return "{0} of type {1} (id:{2})".format(self.name, self.data_type, self.id) + + @staticmethod + def all(): + ''' + Returns all authenticators ordered by priority + ''' + return Authenticator.objects.all().order_by('priority') + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + # Only tries to get instance if data is not empty + if toDelete.data != '': + s = toDelete.getInstance() + s.destroy() + s.env().clearRelatedData() + + logger.debug('Before delete auth '.format(toDelete)) + +# Connects a pre deletion signal to Authenticator +signals.pre_delete.connect(Authenticator.beforeDelete, sender = Authenticator) + +class User(models.Model): + ''' + This class represents a single user, associated with one authenticator + ''' + manager = models.ForeignKey(Authenticator, on_delete=models.CASCADE, related_name='users') + name = models.CharField(max_length = 128, db_index = True) + real_name = models.CharField(max_length = 128) + comments = models.CharField(max_length = 256) + state = models.CharField(max_length = 1, db_index = True) + password = models.CharField(max_length = 128, default = '') # Only used on "internal" sources + staff_member = models.BooleanField(default = False) # Staff members can login to admin + is_admin = models.BooleanField(default = False) # is true, this is a super-admin + last_access = models.DateTimeField(default=NEVER) + + class Meta: + ''' + Meta class to declare default order and unique multiple field index + ''' + unique_together = (("manager", "name"),) + ordering = ('name',) + + def getUsernameForAuth(self): + ''' + Return the username transformed for authentication. + This transformation is used for transports only, not for transforming + anything at login time. Transports that will need the username, will invoke + this method. + The manager (an instance of uds.core.auths.Authenticator), can transform the database stored username + so we can, for example, add @domain in some cases. + ''' + return self.getManager().getForAuth(self.name) + + def getManager(self): + ''' + Returns the authenticator object that owns this user. + + :note: The returned value is an instance of the authenticator class used to manage this user, not a db record. + ''' + return self.manager.getInstance() + + def isStaff(self): + ''' + Return true if this user is admin or staff member + ''' + return self.staff_member or self.is_admin + + def prefs(self, modName): + ''' + Returns the preferences for this user for the provided module name. + + Usually preferences will be associated with transports, but can be preferences registered by ANY module. + + Args: + modName: name of the module to get preferences for + + + Returns: + + The preferences for the module specified as a dictionary (can be empty if module is not found). + + If the module exists, the preferences will always contain something, but may be the values are the default ones. + + ''' + from uds.core.managers.UserPrefsManager import UserPrefsManager + return UserPrefsManager.manager().getPreferencesForUser(modName, self) + + def updateLastAccess(self): + ''' + Updates the last access for this user with the current time of the sql server + ''' + self.last_access = getSqlDatetime() + self.save() + + + def __unicode__(self): + return "User {0} from auth {1}".format(self.name, self.manager.name) + + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + In this case, this method ensures that the user has no userServices assigned and, if it has, + mark those services for removal + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + + # first, we invoke removeUser. If this raises an exception, user will not + # be removed + toDelete.getManager().removeUser(toDelete.name) + + # Removes all user services assigned to this user (unassign it and mark for removal) + for us in toDelete.userServices.all(): + us.assignToUser(None) + us.remove() + + + logger.debug('Deleted user {0}'.format(toDelete)) + +signals.pre_delete.connect(User.beforeDelete, sender = User) + +class Group(models.Model): + ''' + This class represents a group, associated with one authenticator + ''' + manager = models.ForeignKey(Authenticator, on_delete=models.CASCADE, related_name='groups') + name = models.CharField(max_length = 128, db_index = True) + state = models.CharField(max_length = 1, default = State.ACTIVE, db_index = True) + comments = models.CharField(max_length = 256, default = '') + users = models.ManyToManyField(User, related_name='groups') + + class Meta: + ''' + Meta class to declare default order and unique multiple field index + ''' + unique_together = (("manager", "name"),) + ordering = ('name',) + + def getManager(self): + ''' + Returns the authenticator object that owns this user. + + :note: The returned value is an instance of the authenticator class used to manage this user, not a db record. + ''' + return self.manager.getInstance() + + def __unicode__(self): + return "Group {0} from auth {1}".format(self.name, self.manager.name) + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + In this case, this is a dummy method, waiting for something useful to do :-) + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + # Todelete is a group + + # We invoke removeGroup. If this raises an exception, group will not + # be removed + toDelete.getManager().removeGroup(toDelete.name) + + + logger.debug('Deleted group {0}'.format(toDelete)) + +signals.pre_delete.connect(Group.beforeDelete, sender = Group) + +class UserPreference(models.Model): + ''' + This class represents a single user preference for an user and a module + ''' + module = models.CharField(max_length=32, db_index = True) + name = models.CharField(max_length=32, db_index = True) + value = models.CharField(max_length=128, db_index = True) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = 'preferences') + +# Provisioned services +class DeployedService(models.Model): + ''' + A deployed service is the Service produced element that is assigned finally to an user (i.e. a Virtual Machine, etc..) + ''' + name = models.CharField(max_length=128, default = '') + comments = models.CharField(max_length = 256, default = '') + service = models.ForeignKey(Service, null=True, blank=True, related_name = 'deployedServices') + osmanager = models.ForeignKey(OSManager, null=True, blank=True, related_name = 'deployedServices') + transports = models.ManyToManyField(Transport, related_name='deployedServices', db_table = 'uds__ds_trans') + assignedGroups = models.ManyToManyField(Group, related_name='deployedServices', db_table = 'uds__ds_grps') + state = models.CharField(max_length = 1, default = State.ACTIVE, db_index = True) + state_date = models.DateTimeField(default=NEVER) + initial_srvs = models.PositiveIntegerField(default = 0) + cache_l1_srvs = models.PositiveIntegerField(default = 0) + cache_l2_srvs = models.PositiveIntegerField(default = 0) + max_srvs = models.PositiveIntegerField(default = 0) + current_pub_revision = models.PositiveIntegerField(default = 1) + + class Meta: + ''' + Meta class to declare the name of the table at database + ''' + db_table = 'uds__deployed_service' + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def activePublication(self): + ''' + Returns the current valid publication for this deployed service. + + Returns: + Publication db record if this deployed service has an valid active publication. + + None if there is no valid publication for this deployed service. + ''' + try: + return self.publications.filter(state=State.USABLE)[0] + except Exception: + return None + + def setState(self, state, save = True): + ''' + Updates the state of this object and, optionally, saves it + + Args: + state: new State to store at record + + save: Defaults to true. If false, record will not be saved to db, just modified + + ''' + self.state = state + self.state_date = getSqlDatetime() + if save is True: + self.save() + + def remove(self): + ''' + Marks the deployed service for removing. + + The background worker will be the responsible for removing the deployed service + ''' + self.setState(State.REMOVABLE) + + def removed(self): + ''' + Mark the deployed service as removed. + + A background worker will check for removed deloyed services and clean database of them. + ''' + self.transports.clear() + self.assignedGroups.clear() + self.osmanager = None + self.service = None + self.setState(State.REMOVED) + + + def markOldDeployedServicesAsRemovables(self, activePub): + ''' + Used when a new publication is finished. + + Marks all user deployed services that belongs to this deployed service, that do not belongs + to "activePub" and are not in use as removable. + + Also cancels all preparing user services + + Better see the code, it's easier to understand :-) + + Args: + activePub: Active publication used as "current" publication to make checks + ''' + now = getSqlDatetime() + if activePub == None: + logger.error('No active publication, don\'t know what to erase!!! (ds = {0})'.format(self)) + return + for ap in self.publications.exclude(id=activePub.id): + for u in ap.userServices.filter(state=State.PREPARING): + u.cancel() + ap.userServices.exclude(cache_level=0).filter(state=State.USABLE).update(state=State.REMOVABLE, state_date = now) + ap.userServices.filter(cache_level=0, state=State.USABLE, in_use=False).update(state=State.REMOVABLE, state_date = now) + + def validateUser(self, user): + ''' + Validates that the user has access to this deployed service + + Args: + user: User (db record) to check if has access to this deployed service + + Returns: + True if has access + + Raises: + InvalidUserException() if user do not has access to this deployed service + + InvalidServiceException() if user has rights to access, but the deployed service is not ready (no active publication) + + ''' + # We have to check if at least one group from this user is valid for this deployed service + logger.debug('User: {0}'.format(user.id)) + logger.debug('DeployedService: {0}'.format(self.id)) + if len( set(user.groups.all()) & set(self.assignedGroups.all()) ) == 0: + raise auths.Exceptions.InvalidUserException() + if self.activePublication() is None and self.service.getType().publicationType is not None: + raise InvalidServiceException() + return True + + @staticmethod + def getDeployedServicesForGroups(groups): + ''' + Return deployed services with publications for the groups requested. + + Args: + groups: List of groups to check + + Returns: + List of accesible deployed services + ''' + list1 = DeployedService.objects.filter(assignedGroups__in=groups, assignedGroups__state__exact=State.ACTIVE, state = State.ACTIVE).distinct().annotate(cuenta=models.Count('publications')).exclude(cuenta__eq=0) + # Now get deployed services that DO NOT NEED publication + doNotNeedPublishing = [ t.type() for t in ServiceProviderFactory.factory().servicesThatDoNotNeedPublication() ] + list2 = DeployedService.objects.filter(assignedGroups__in=groups, assignedGroups__state__exact=State.ACTIVE, service__data_type__in=doNotNeedPublishing, state = State.ACTIVE) + return [ r for r in list1 ] + [ r for r in list2 ] + + + def publish(self): + ''' + Launches the publication of this deployed service. + + No check is done, it simply redirects the request to PublicationManager, where checks are done. + ''' + from uds.core.managers.PublicationManager import PublicationManager + PublicationManager.manager().publish(self) + + def unpublish(self): + ''' + Unpublish (removes) current active publcation. + + It checks that there is an active publication, and then redirects the request to the publication itself + ''' + pub = self.activePublication() + if pub is not None: + pub.unpublish() + + def cachedUserServices(self): + ''' + Utility method to access the cached user services (level 1 and 2) + + Returns: + A list of db records (userService) with cached user services + ''' + return self.userServices.exclude(cache_level=0) + + def assignedUserServices(self): + ''' + Utility method to access the assigned user services + + Returns: + A list of db records (userService) with assinged user services + ''' + return self.userServices.filter(cache_level=0) + + def erroneousUserServices(self): + ''' + Utility method to locate invalid assigned user services. + + If an user deployed service is assigned, it MUST have an user associated. + + If it don't has an user associated, the user deployed service is wrong. + ''' + return self.userServices.filter(cache_level=0, user=None) + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + toDelete.getEnvironment().clearRelatedData() + + logger.debug('Deleting Deployed Service {0}'.format(toDelete)) + + def __unicode__(self): + return "Deployed service {0}({1}) with {2} as initial, {3} as L1 cache, {4} as L2 cache, {5} as max".format( + self.name, self.id, self.initial_srvs, self.cache_l1_srvs, self.cache_l2_srvs, self.max_srvs) + + +# Connects a pre deletion signal to Authenticator +signals.pre_delete.connect(DeployedService.beforeDelete, sender = DeployedService) + +class DeployedServicePublication(models.Model): + ''' + A deployed service publication keep track of data needed by services that needs "preparation". (i.e. Virtual machine --> base machine --> children of base machines) + ''' + deployed_service = models.ForeignKey(DeployedService, on_delete=models.CASCADE, related_name = 'publications') + publish_date = models.DateTimeField(db_index = True) + # data_type = models.CharField(max_length=128) # The data type is specified by the service itself + data = models.TextField(default='') + # Preparation state. The preparation of a service is a task that runs over time, we need to: + # * Prepare it + # * Use it + # * Remove it + # * Mark as failed + # The responsible class will notify when we have to change state, and a deployed service will only be usable id it has at least + # a prepared service "Usable" or it doesn't need to prepare anything (needsDeployment = False) + state = models.CharField(max_length = 1, default = State.PREPARING, db_index = True) + state_date = models.DateTimeField() + revision = models.PositiveIntegerField(default = 1) + + class Meta: + ''' + Meta class to declare default order and unique multiple field index + ''' + db_table = 'uds__deployed_service_pub' + ordering = ('publish_date',) + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def getInstance(self): + ''' + Instantiates the object this record contains. + + Every single record of Provider model, represents an object. + + Args: + values (list): Values to pass to constructor. If no values are especified, + the object is instantiated empty and them de-serialized from stored data. + + Returns: + The instance Instance of the class this provider represents + + Raises: + ''' + serviceInstance = self.deployed_service.service.getInstance() + osManagerInstance = self.deployed_service.osmanager + if osManagerInstance is not None: + osManagerInstance = osManagerInstance.getInstance() + # Sanity check, so it's easier to find when we have created + # a service that needs publication but do not have + + if serviceInstance.publicationType is None: + raise Exception('Tried to get a publication instance for a service that do not needs it') + + if serviceInstance.publicationType is None: + raise Exception('Class {0} do not have defined publicationType but needs to be published!!!'.format(serviceInstance.__class__.__name)) + + dpl = serviceInstance.publicationType(self.getEnvironment(), service = serviceInstance, osManager = osManagerInstance, revision = self.revision, dsName = self.deployed_service.name ) + # Only invokes deserialization if data has something. '' is nothing + if self.data != '' and self.data is not None: + dpl.unserialize(self.data) + return dpl + + def updateData(self, dsp): + ''' + Updates the data field with the serialized uds.core.services.Publication + + Args: + dsp: uds.core.services.Publication to serialize + + :note: This method do not saves the updated record, just updates the field + ''' + self.data = dsp.serialize() + + def setState(self, state): + ''' + Updates the state of this object and, optionally, saves it + + Args: + state: new State to store at record + + save: Defaults to true. If false, record will not be saved to db, just modified + + ''' + self.state_date = getSqlDatetime() + self.state = state + + def unpublish(self): + ''' + Tries to remove the publication + + No check is done, it simply redirects the request to PublicationManager, where checks are done. + ''' + from uds.core.managers.PublicationManager import PublicationManager + PublicationManager.manager().unpublish(self) + + def cancel(self): + ''' + Invoques the cancelation of this publication + ''' + from uds.core.managers.PublicationManager import PublicationManager + PublicationManager.manager().cancel(self) + + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + toDelete.getEnvironment().clearRelatedData() + + # Destroy method is invoked directly by PublicationManager, + # Destroying a publication is not obligatory an 1 step action. + # It's handled as "publish", and as so, it + + logger.debug('Deleted publication {0}'.format(toDelete)) + + + +# Connects a pre deletion signal to Authenticator +signals.pre_delete.connect(DeployedServicePublication.beforeDelete, sender = DeployedServicePublication) + +class UserService(models.Model): + ''' + This is the base model for assigned user service and cached user services. + This are the real assigned services to users. DeployedService is the container (the group) of this elements. + ''' + # The reference to deployed service is used to accelerate the queries for different methods, in fact its redundant cause we can access to the deployed service + # through publication, but queries are much more simple + deployed_service = models.ForeignKey(DeployedService, on_delete=models.CASCADE, related_name = 'userServices') + publication = models.ForeignKey(DeployedServicePublication, on_delete=models.CASCADE, null=True, blank=True, related_name = 'userServices') + + unique_id = models.CharField(max_length=128, default ='', db_index = True) # User by agents to locate machine + friendly_name = models.CharField(max_length=128, default = '') + # We need to keep separated two differents os states so service operations (move beween caches, recover service) do not affects os manager state + state = models.CharField(max_length=1, default=State.PREPARING, db_index = True) # We set index so filters at cache level executes faster + os_state = models.CharField(max_length=1, default=State.PREPARING) # The valid values for this field are PREPARE and USABLE + state_date = models.DateTimeField(auto_now_add=True) + creation_date = models.DateTimeField(db_index = True) + data = models.TextField(default='') + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = 'userServices', null=True, blank=True, default = None) + in_use = models.BooleanField(default=False) + in_use_date = models.DateTimeField(default=NEVER) + cache_level = models.PositiveSmallIntegerField(db_index=True, default=0) # Cache level must be 1 for L1 or 2 for L2, 0 if it is not cached service + + objects = LockingManager() + + class Meta: + ''' + Meta class to declare default order and unique multiple field index + ''' + db_table = 'uds__user_service' + ordering = ('creation_date',) + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents. + + In the case of the user, there is an instatiation of "generators". + Right now, there is two generators provided to child instance objects, that are + valid for generating unique names and unique macs. In a future, there could be more generators + + To access this generators, use the Envirnment class, and the keys 'name' and 'mac'. + + (see related classes uds.core.util.UniqueNameGenerator and uds.core.util.UniqueMacGenerator) + ''' + from uds.core.util.UniqueMacGenerator import UniqueMacGenerator + from uds.core.util.UniqueNameGenerator import UniqueNameGenerator + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id, {'mac' : UniqueMacGenerator, 'name' : UniqueNameGenerator } ) + + def getInstance(self): + ''' + Instantiates the object this record contains. In this case, the instantiated object needs also + the os manager and the publication, so we also instantiate those here. + + Every single record of UserService model, represents an object. + + Args: + values (list): Values to pass to constructor. If no values are especified, + the object is instantiated empty and them de-serialized from stored data. + + Returns: + The instance Instance of the class this provider represents + + Raises: + ''' + # We get the service instance, publication instance and osmanager instance + ds = self.deployed_service + serviceInstance = ds.service.getInstance() + if serviceInstance.needsManager is False: + osmanagerInstance = None + else: + osmanagerInstance = ds.osmanager.getInstance() + # We get active publication + publicationInstance = None + try: # We may have deleted publication... + if self.publication != None: + publicationInstance = self.publication.getInstance() + except Exception, e: + # The publication to witch this item points to, does not exists + self.publication = None + logger.error("Got exception at getInstance of an userService {0} : {1}".format(e.__class__, e)) + if serviceInstance.deployedType is None: + raise Exception('Class {0} needs deployedType but it is not defined!!!'.format(serviceInstance.__class__.__name__)) + us = serviceInstance.deployedType(self.getEnvironment(), service = serviceInstance, publication = publicationInstance, osmanager = osmanagerInstance, dbservice = self) + if self.data != '' and self.data is not None: + us.unserialize(self.data) + return us + + def updateData(self, us): + ''' + Updates the data field with the serialized :py:class:uds.core.services.UserDeployment + + Args: + dsp: :py:class:uds.core.services.UserDeployment to serialize + + :note: This method do not saves the updated record, just updates the field + ''' + self.data = us.serialize() + + def setState(self, state): + ''' + Updates the state of this object and, optionally, saves it + + Args: + state: new State to store at record + + save: Defaults to true. If false, record will not be saved to db, just modified + + ''' + self.state_date = getSqlDatetime() + self.state = state + + def setOsState(self, state): + ''' + Updates the os state (state of the os) of this object and, optionally, saves it + + Args: + state: new State to store at record + + save: Defaults to true. If false, record will not be saved to db, just modified + + ''' + self.state_date = getSqlDatetime() + self.os_state = state + + def assignToUser(self, user): + ''' + Assigns this user deployed service to an user. + + Args: + user: User to assing to (db record) + ''' + self.cache_level = 0 + self.user = user + + def setInUse(self, state): + ''' + Set the "in_use" flag for this user deployed service + + Args: + state: State to set to the "in_use" flag of this record + + :note: If the state is Fase (set to not in use), a check for removal of this deployed service is launched. + ''' + from uds.core.managers.UserServiceManager import UserServiceManager + self.in_use = state + self.in_use_date = getSqlDatetime() + if state is False: # Service released, check y we should mark it for removal + # If our publication is not current, mark this for removal + UserServiceManager.manager().checkForRemoval(self) + + + def isUsable(self): + ''' + Returns if this service is usable + ''' + return State.isUsable(self.state) + + def isPreparing(self): + ''' + Returns if this service is in preparation (not ready to use, but in its way to be so...) + ''' + return State.isPreparing(self.state) + + def isReady(self): + ''' + Returns if this service is ready (not preparing or marked for removal) + ''' + # Call to isReady of the instance + from uds.core.managers.UserServiceManager import UserServiceManager + return UserServiceManager.manager().isReady(self) + + def remove(self): + ''' + Mark this user deployed service for removal + ''' + self.setState(State.REMOVABLE) + self.save() + + def cancel(self): + ''' + Asks the UserServiceManager to cancel the current operation of this user deployed service. + ''' + from uds.core.managers.UserServiceManager import UserServiceManager + UserServiceManager.manager().cancel(self) + + def removeOrCancel(self): + ''' + Marks for removal or cancels it, depending on state + ''' + if self.isUsable(): + self.remove() + else: + self.cancel() + + def moveToLevel(self, cacheLevel): + ''' + Moves cache items betwen levels, managed directly + + Args: + cacheLevel: New cache level to put object in + ''' + from uds.core.managers.UserServiceManager import UserServiceManager + UserServiceManager.manager().moveToLevel(self, cacheLevel) + + @staticmethod + def getUserAssignedServices(user): + ''' + Return DeployedUserServices (not deployed services) that this user owns and are assignable + For this to happen, we locate all user services assigned to this user, and we keep those that: + * Must assign service manually + This method is probably slow, but i don't think a user will have more than a bunch of services assigned + @returns and array of dicts with id, name and transports + ''' + logger.debug("Filtering assigned services for user {0}".format(user)) + res = [] + for us in UserService.objects.filter(user=user): + if us.deployed_service.state != State.ACTIVE: # Do not show removing or removed services + continue; + usi = us.getInstance() + if usi.service().mustAssignManually is False: + continue + res.append({ 'id' : us.id, 'name' : usi.getName(), 'transports' : us.deployed_service.transports }) + return res + + def __unicode__(self): + return "User service {0}, cache_level {1}, user {2}".format(self.id, self.cache_level, self.user) + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to invoke the Service class "Destroy" before deleting it from database. + + The main purpuse of this hook is to call the "destroy" method of the object to delete and + to clear related data of the object (environment data such as own storage, cache, etc... + + :note: If destroy raises an exception, the deletion is not taken. + ''' + toDelete = kwargs['instance'] + toDelete.getEnvironment().clearRelatedData() + + # TODO: Check if this invokation goes here + #toDelete.getInstance() + + logger.debug('Deleted user service {0}'.format(toDelete)) + + + +# Connects a pre deletion signal to Authenticator +signals.pre_delete.connect(UserService.beforeDelete, sender = UserService) + + +# General utility models, such as a database cache (for caching remote content of slow connections to external services providers for example) +# We could use django cache (and maybe we do it in a near future), but we need to clean up things when objecs owning them are deleted +class Cache(models.Model): + ''' + General caching model. This model is managed via uds.core.util.Cache.Cache class + ''' + owner = models.CharField(max_length = 128, db_index = True) + key = models.CharField(max_length = 64, primary_key = True) + value = models.TextField(default = '') + created = models.DateTimeField() # Date creation or validation of this entry. Set at write time + validity = models.IntegerField(default = 60) # Validity of this entry, in seconds + + class Meta: + ''' + Meta class to declare the name of the table at database + ''' + db_table = 'uds_utility_cache' + + @staticmethod + def cleanUp(): + ''' + Purges the cache items that are no longer vaild. + ''' + from django.db import connection, transaction + con = connection + cursor = con.cursor() + logger.info("Purging cache items") + cursor.execute('DELETE FROM uds_utility_cache WHERE created + validity < now()') + transaction.commit_unless_managed() + + + def __unicode__(self): + expired = datetime.now() > self.created + timedelta(seconds = self.validity) + if expired: + expired = "Expired" + else: + expired = "Active" + return "{0} {1} = {2} ({3})".format(self.owner, self.key, self.value, expired) + +class Config(models.Model): + ''' + General configuration values model. Used to store global and specific modules configuration values. + This model is managed via uds.core.util.Config.Config class + ''' + section = models.CharField(max_length=128) + key = models.CharField(max_length=64) + value = models.TextField(default = '') + crypt = models.BooleanField(default = False) + + class Meta: + ''' + Meta class to declare default order and unique multiple field index + ''' + db_table = 'uds_configuration' + unique_together = (('section', 'key'),) + + def __unicode__(self): + return "Config {0} = {1}".format(self.key, self.value) + +class Storage(models.Model): + ''' + General storage model. Used to store specific instances (transport, service, servicemanager, ...) persinstent information + not intended to be serialized/deserialized everytime one object instance is loaded/saved. + ''' + owner = models.CharField(max_length = 128, db_index = True) + key = models.CharField(max_length = 64, primary_key = True) + data = models.CharField(max_length = 1024, default='') + attr1 = models.CharField(max_length = 64, db_index = True, null=True, blank=True, default = None) + + objects = LockingManager() + + def __unicode__(self): + return "{0} {1} = {2}, {3}".format(self.owner, self.key, self.data, str.join( '/', [self.attr1])) + +class UniqueId(models.Model): + ''' + Unique ID Database. Used to store unique names, unique macs, etc... + Managed via uds.core.util.UniqueIDGenerator.UniqueIDGenerator + ''' + owner = models.CharField(max_length = 128, db_index = True, default = '') + basename = models.CharField(max_length = 32, db_index = True) + seq = models.BigIntegerField(db_index=True) + assigned = models.BooleanField(db_index=True, default = True) + + objects = LockingManager() + + class Meta: + ''' + Meta class to declare default order and unique multiple field index + ''' + unique_together = (('basename', 'seq'),) + ordering = ('-seq',) + + + def __unicode__(self): + return "{0} {1}.{2}, assigned is {3}".format(self.owner, self.basename, self.seq, self.assigned) + + +class Scheduler(models.Model): + ''' + Class that contains scheduled tasks. + + The scheduled task are keep at database so: + * We can access them from any host + * We have a persistence for them + + The Scheduler contains jobs, that are clases that manages the job. + Jobs are not serialized/deserialized, they are just Task delegated to certain clases. + + In order for a task to work, it must first register itself for "names" that that class handles using the + JobsFactory + ''' + + DAY = 60*60*24 + HOUR = 60*60 + MIN = 60 + + name = models.CharField(max_length = 64, unique = True) + frecuency = models.PositiveIntegerField(default = DAY) + last_execution = models.DateTimeField(auto_now_add = True) + next_execution = models.DateTimeField(default = NEVER, db_index = True) + owner_server = models.CharField(max_length=64, db_index = True, default = '') + state = models.CharField(max_length = 1, default = State.FOR_EXECUTE, db_index = True) + + #objects = LockingManager() + + def getEnvironment(self): + ''' + Returns an environment valid for the record this object represents + ''' + return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) + + def getInstance(self): + ''' + Returns an instance of the class that this record of the Scheduler represents. This clas is derived + of uds.core.jobs.Job.Job + ''' + jobInstance = JobsFactory.factory().lookup(self.name) + if jobInstance != None: + env = self.getEnvironment() + return jobInstance(env) + else: + return None + + @staticmethod + def beforeDelete(sender, **kwargs): + ''' + Used to remove environment for sheduled task + ''' + toDelete = kwargs['instance'] + logger.debug('Deleting sheduled task {0}'.format(toDelete)) + toDelete.getEnvironment().clearRelatedData() + + + def __unicode__(self): + return "Scheduled task {0}, every {1}, last execution at {2}, state = {3}".format(self.name, self.frecuency, self.last_execution, self.state) + +# Connects a pre deletion signal to Scheduler +signals.pre_delete.connect(Scheduler.beforeDelete, sender = Scheduler) + + +class DelayedTask(models.Model): + ''' + A delayed task is a kind of scheduled task. It's a task that has more than is executed at a delay + specified at record. This is, for example, for publications, service preparations, etc... + + The delayed task is different from scheduler in the fact that they are "one shot", meaning this that when the + specified delay is reached, the task is executed and the record is removed from the table. + + This table contains uds.core.util.jobs.DelayedTask references + ''' + type = models.CharField(max_length=128) + tag = models.CharField(max_length=64, db_index = True) # A tag for letting us locate delayed publications... + instance = models.TextField() + insert_date = models.DateTimeField(auto_now_add = True) + execution_delay = models.PositiveIntegerField() + execution_time = models.DateTimeField(db_index = True) + + #objects = LockingManager() + + def __unicode__(self): + return "Run Queue task {0} owned by {3},inserted at {1} and with {2} seconds delay".format(self.type, self.insert_date, self.execution_delay, self.owner_server) + + +class Network(models.Model): + ''' + This model is used for keeping information of networks associated with transports (right now, just transports..) + ''' + name = models.CharField(max_length = 64, unique = True) + net_start = models.BigIntegerField(db_index = True) + net_end = models.BigIntegerField(db_index = True) + transports = models.ManyToManyField(Transport, related_name='networks', db_table='uds_net_trans') + + @staticmethod + def ipToLong(ip): + ''' + convert decimal dotted quad string to long integer + ''' + + hexn = ''.join(["%02X" % long(i) for i in ip.split('.')]) + return long(hexn, 16) + + @staticmethod + def longToIp(n): + ''' + convert long int to dotted quad string + ''' + + d = 256 * 256 * 256 + q = [] + while d > 0: + m,n = divmod(n,d) + q.append(str(m)) + d = d/256 + + return '.'.join(q) + + @staticmethod + def networksFor(ip): + ''' + Returns the networks that are valid for specified ip in dotted quad (xxx.xxx.xxx.xxx) + ''' + ip = Network.ipToLong(ip) + return Network.objects.filter(net_start__lte=ip, net_end__gte=ip) + + @staticmethod + def create(name, netStart, netEnd): + ''' + Creates an network record, with the specified net start and net end (dotted quad) + + Args: + netStart: Network start + + netEnd: Network end + ''' + return Network.objects.create(name=name, net_start = Network.ipToLong(netStart), net_end = Network.ipToLong(netEnd)) + + @property + def netStart(self): + ''' + Property to access the quad dotted format of the stored network start + + Returns: + string representing the dotted quad of this network start + ''' + return Network.longToIp(self.net_start) + + @property + def netEnd(self): + ''' + Property to access the quad dotted format of the stored network end + + Returns: + string representing the dotted quad of this network end + ''' + return Network.longToIp(self.net_end) + + def update(self, name, netStart, netEnd): + ''' + Updated this network with provided values + + Args: + name: new name of the network + + netStart: new Network start (quad dotted) + + netEnd: new Network end (quad dotted) + ''' + self.name = name + self.net_start = Network.ipToLong(netStart) + self.net_end = Network.ipToLong(netEnd) + self.save() + + def __unicode__(self): + return 'Network {0} from {1} to {2}'.format(self.name, Network.longToIp(self.net_start), Network.longToIp(self.net_end)) + diff --git a/server/src/uds/osmanagers/LinuxOsManager/LinuxOsManager.py b/server/src/uds/osmanagers/LinuxOsManager/LinuxOsManager.py new file mode 100644 index 000000000..008abfa7f --- /dev/null +++ b/server/src/uds/osmanagers/LinuxOsManager/LinuxOsManager.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.ui.UserInterface import gui +from uds.core.osmanagers.BaseOsManager import BaseOSManager, State + +import logging +from uds.core.managers.UserServiceManager import UserServiceManager + +logger = logging.getLogger(__name__) + +class LinuxOsManager(BaseOSManager): + typeName = _('Linux OS Manager') + typeType = 'LinuxManager' + typeDescription = _('Os Manager to control linux virtual machines (basically renames machine and notify state)') + iconFile = 'losmanager.png' + + onLogout = gui.ChoiceField( label = _('On Logout'), order = 10, rdonly = False, tooltip = _('What to do when user logout from service'), + values = [ {'id' : 'keep', 'text' : _('Keep service assigned') }, + {'id' : 'remove', 'text' : _('Remove service') } + ], defvalue = 'keep' ) + + def __init__(self,environment, values): + super(LinuxOsManager, self).__init__(environment, values) + if values is not None: + self._onLogout = values['onLogout'] + else: + self._onLogout = '' + + + def release(self, service): + pass + + def getName(self, service): + ''' + gets name from deployed + ''' + si = service.getInstance() + name = si.getName() + service.updateData(si) + return name + + def infoVal(self,service): + return 'rename:' + self.getName(service) + + def notifyIp(self, uid, si, data): + # Notifies IP to deployed + pairs = data.split(',') + for p in pairs: + key, val = p.split('=') + if key.lower() == uid.lower(): + si.setIp(val) + break + + def process(self,service,msg, data): + ''' + We understand this messages: + * msg = info, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) + * msg = logon, data = Username, Informs that the username has logged in inside the machine + * msg = logoff, data = Username, Informs that the username has logged out of the machine + * msg = ready, data = None, Informs machine ready to be used + ''' + logger.info("Invoked LinuxOsManager for {0} with params: {1},{2}".format(service, msg, data)) + # We get from storage the name for this service. If no name, we try to assign a new one + ret = "ok" + inUse = False + notifyReady = False + doRemove = False + state = service.os_state + if msg == "info": + ret = self.infoVal(service) + state = State.PREPARING + elif msg == "login": + si = service.getInstance() + si.userLoggedIn(data) + service.updateData(si) + inUse = True + elif msg == "logout": + si = service.getInstance() + si.userLoggedOut(data) + service.updateData(si) + if self._onLogout == 'remove': + doRemove = True + elif msg == "ip": + # This ocurss on main loop inside machine, so service is usable + state = State.USABLE + si = service.getInstance() + self.notifyIp(service.unique_id, si, data) + service.updateData(si) + elif msg == "ready": + state = State.USABLE + si = service.getInstance() + notifyReady = True + self.notifyIp(service.unique_id, si, data) + service.updateData(si) + service.setInUse(inUse) + service.setOsState(state) + # If notifyReady is not true, save state, let UserServiceManager do it for us else + if doRemove is True: + service.remove() + else: + if notifyReady is False: + service.save() + else: + UserServiceManager.manager().notifyReadyFromOsManager(service, '') + logger.debug('Returning {0}'.format(ret)) + return ret + + def checkState(self,service): + logger.debug('Checking state for service {0}'.format(service)) + return State.RUNNING + + def marshal(self): + ''' + Serializes the os manager data so we can store it in database + ''' + return str.join( '\t', [ 'v1', self._onLogout ] ) + + def unmarshal(self, s): + data = s.split('\t') + if data[0] == 'v1': + self._onLogout = data[1] + + def valuesDict(self): + return { 'onLogout' : self._onLogout } diff --git a/server/src/uds/osmanagers/LinuxOsManager/__init__.py b/server/src/uds/osmanagers/LinuxOsManager/__init__.py new file mode 100644 index 000000000..e93c0d115 --- /dev/null +++ b/server/src/uds/osmanagers/LinuxOsManager/__init__.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.osmanagers.OSManagersFactory import OSManagersFactory +from uds.core.managers.DownloadsManager import DownloadsManager +from uds.osmanagers.LinuxOsManager.LinuxOsManager import LinuxOsManager +import os.path, sys + +OSManagersFactory.factory().insert(LinuxOsManager) + +DownloadsManager.manager().registerDownloadable('udsactor_1.0_all.deb', + _('UDS Actor for linux machines (Requires python 2.6 or greater)'), + os.path.dirname(sys.modules[__package__].__file__) + '/files/udsactor_1.0_all.deb', + 'application/x-debian-package') diff --git a/server/src/uds/osmanagers/LinuxOsManager/files/udsactor_1.0_all.deb b/server/src/uds/osmanagers/LinuxOsManager/files/udsactor_1.0_all.deb new file mode 100644 index 0000000000000000000000000000000000000000..c6b5476f52ef398e47476882ca101c1c3505b6e7 GIT binary patch literal 9072 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/osmanagers/LinuxOsManager/losmanager.png b/server/src/uds/osmanagers/LinuxOsManager/losmanager.png new file mode 100644 index 0000000000000000000000000000000000000000..9a8c58642d436ca9d8ba2ed364b0665a7267f27c GIT binary patch literal 1203 zcmYk4e@s(X6vxjiucd2fD8I_@00Jfu9ViR}nuW585CI99{3wM@*mMioKgP&}Y*BtJ zk{On5B!a;)ri86iC~k`~1c5r3uB`z!Bsf9_fwi^t*DJKIeec~}$P#$Teed4;a=zzt zzUQ=;6rU7}B18ZnF36Xc;;&?92?h8aL!q+(WMhF`_V&j;>660&S8{~pgwN>FI5lZv zJ0tRwnp?xO;v3o$K21moxcs9i!2aIG@^V*q>Q8GH_;z$uB?31VLa4Si90KY*56)@? zYXhfyivt4#?>AB#V$F?|cftmhBU9?SnU1==ygdHrp($@3j*BBQ$=yJ5a+sKi6_Pm@ z<8P){>TV`}pwvV!wr2D>KC!mm0Yk$3diwg2an*sRK>;td+lp5Yk5$z_R*iQjK{kx+ zN*tQ(3%HW%_t~ucc6QOW@%Zw=*F|A{(n)E^7_w+=-xOS!__ybz{P-EZ`h}wBMbSCO z%Q`=;d2=jtB9Zppp$8d^BOVs>LHN+w*;!+=*%H>**BP_f%r_c0Kr{6iHVj5E7*@e( zH1RDK3&Z2_Vuyx?YUk$W0PlTvVRof}Wm!H=(-0LE^(ZDLhAS*AWbWR*hqT%mM0+nG z)|8JNt0SnVw;vG%FcOJ`6N|;ab#!!K9JF6*hp4-|JDwm2E6&2z*4A=nvk5sU8nN^Y zq83|_-R?#PqXCtdmvcg)kc*6rv}I*w9i%7V97_OvuwePPr)pz2>} zMEiI=_`!|iluE^b(P&t~EF|TybCkmdxZi+h0Zb+Xo&f{CbCA#VI#ZZb`dzLp7vVT| zPm09V;=U4uQdw`Wsi`QM(#(vBCBeZi+RcOq`Qh^1u(-4e4!fDasVqPL^04aGo&FVn zi9g`9`=|(nIAYVh=1bY`HQ1cyGy$`m$+yzdXHQ>)1y?)>NEXtPXh=-SfNlGqR^#08 zlldosQxPZ5f2~kfc=k~N5TMupAy}Del%GmZ&vO3la9d|>mVXwv=6l!3M}ZQdH{uxN z1<&eI=`FXwyS8=eSM|KqwanbAs;;E>qzI1VaDUv;*3u4o%}6xsMIsMFu%htL)vC%; zPjqw)+*dz<=Tl>G48sW+f}}$eW7jTUy1-_>k%5))RQiC-qfq}a_hr}4x!^>xd@%Rj G=Klbpz-1l) literal 0 HcmV?d00001 diff --git a/server/src/uds/osmanagers/NoneOsManager/Manager.py b/server/src/uds/osmanagers/NoneOsManager/Manager.py new file mode 100644 index 000000000..d3584c1d6 --- /dev/null +++ b/server/src/uds/osmanagers/NoneOsManager/Manager.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.ui.UserInterface import gui +from uds.core.osmanagers.BaseOsManager import BaseOSManager, State + +import logging + +logger = logging.getLogger(__name__) + +class NoneOSManager(BaseOSManager): + typeName = _('None OS Manager') + typeType = 'NoneOSManager' + typeDescription = _('Os Manager with no actions') + iconFile = 'osmanager.png' + + def __init__(self,environment, values): + super(NoneOSManager, self).__init__(environment, values) + + def process(self,service,msg, data): + logger.info("Invoked NoneOsManager for {0} with params: {1}, {2}".format(service, msg, data)) + return "noneos" + + def checkState(self,service): + logger.debug('Checking state for service {0}'.format(service)) + return State.FINISHED + + def marshal(self): + ''' + Serializes the os manager data so we can store it in database + ''' + return str.join( '\t', [ 'v1' ] ) + + def unmarshal(self, str): + data = str.split('\t') + if data[0] == 'v1': + pass + + def valuesDict(self): + return {} + \ No newline at end of file diff --git a/server/src/uds/osmanagers/NoneOsManager/__init__.py b/server/src/uds/osmanagers/NoneOsManager/__init__.py new file mode 100644 index 000000000..0db986528 --- /dev/null +++ b/server/src/uds/osmanagers/NoneOsManager/__init__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.osmanagers.OSManagersFactory import OSManagersFactory +from uds.osmanagers.NoneOsManager.Manager import NoneOSManager + +# This os manager exists for testing purposes only, do not register nor use it on a production environment +#OSManagersFactory.factory().insert(NoneOSManager) diff --git a/server/src/uds/osmanagers/NoneOsManager/osmanager.png b/server/src/uds/osmanagers/NoneOsManager/osmanager.png new file mode 100644 index 0000000000000000000000000000000000000000..4ec28db8920a5ff1f6c53f22039c90cf74df1363 GIT binary patch literal 1248 zcmYk*Yitx%6bJBgA3L+pc6YJtwp+Uvv_+t$k2DxtB7IP)7A!;q5fiZxA_yr_s+edj zwGH+IR%07NL!b#HL{Wl~4^{z7c&b<`0SbNKE?Zh!x^%a*-Pg?A>ueLCliZn{$-TeK z|4dFx&FWVz#xx@USgKZ5tks^@#L^4e-7WF=0O+x*iqbcCAB0jk%TXW@fa+CiU}C}# zWyMZ#q^OXQ>dv#<#JX$O2lj=2)u4ZTCC@}r7bY=)xeN{tP9X4<`K zTj_?n#!u8;ZjVE0f2Qu+wZ4~+ENm!#BYS7x-nz^CM;bcc?wCG)V7@+eP5oD?zP3nx z)!vrv!jzck6Fpa@F;?1r$hOqp8qYuM-LIRxmpRW_Ua<9eTVw8?4KFT#q5eSgcj9N) z&VRVcYHH&+@Z0nqN<(7x*>RX#;CBqshg9=|Oj5D2H2g46k!hn*k+` zb3nl4Frq|NO%`WzVUA;8wu?c=ToFB`sMdTGmv}%`c(3+;QN}zg%ypWHpd(x_5+uu7 z25l3jBA6^z%t!r9v=?Vw?3ze7r3Z$eQdrwXAc8KMF{x8zMW=DZWYz%{v6eeAQ~-)1 zbDgz_$0Hn%kNA+&W-F*(S#}H|NDTAispD81yA%~EkL0m<82u{8ST3fJDhdP6Ee%q@H1%grs$72%?=lFKJ%{Dbd)$W$VIbU{m>WOyENvv$~$k9{u z1HC)>m)E~BO}=F!hq8o9VVNKZIi;DI?#jx_kGE{uqLso~k7r|+#%SpXEV_55{atC( Q`-!@0#p;R^Wg8p+2fm>kKmY&$ literal 0 HcmV?d00001 diff --git a/server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py b/server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py new file mode 100644 index 000000000..3a2ecd659 --- /dev/null +++ b/server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.ui.UserInterface import gui +from uds.core.managers.CryptoManager import CryptoManager +from uds.core.osmanagers.BaseOsManager import BaseOSManager, State +from WindowsOsManager import WindowsOsManager, scrambleMsg + +import logging + +logger = logging.getLogger(__name__) + +class WinDomainOsManager(WindowsOsManager): + typeName = _('Windows Domain OS Manager') + typeType = 'WinDomainManager' + typeDescription = _('Os Manager to control windows machines with domain. (Basically renames machine)') + iconFile = 'wosmanager.png' + + # Apart form data from windows os manager, we need also domain and credentials + domain = gui.TextField(length=64, label = _('Domain'), order = 1, tooltip = _('Domain to join machines to (better use dns form of domain)'), required = True) + account = gui.TextField(length=64, label = _('Account'), order = 2, tooltip = _('Account with rights to add machines to domain'), required = True) + password = gui.PasswordField(length=64, label = _('Password'), order = 3, tooltip = _('Password of the account'), required = True) + ou = gui.TextField(length=64, label = _('OU'), order = 4, tooltip = _('Organizational unit where to add machines in domain (check it before using it)')) + # Inherits base "onLogout" + onLogout = WindowsOsManager.onLogout + + def __init__(self,environment, values): + super(WinDomainOsManager, self).__init__(environment, values) + if values != None: + if values['domain'] == '': + raise BaseOSManager.ValidationException(_('Must provide a domain!!!')) + if values['account'] == '': + raise BaseOSManager.ValidationException(_('Must provide an account to add machines to domain!!!')) + if values['password'] == '': + raise BaseOSManager.ValidationException(_('Must provide a password for the account!!!')) + self._domain = values['domain'] + self._ou = values['ou'] + self._account = values['account'] + self._password = values['password'] + else: + self._domain = "" + self._ou = "" + self._account = "" + self._password = "" + + def release(self, service): + super(WinDomainOsManager,self).release(service) + # TODO: remove machine from active directory os, under ou or default location if not specified + + def infoVal(self, service): + return 'domain:{0}\t{1}\t{2}\t{3}\t{4}'.format( self.getName(service), self._domain, self._ou, self._account, self._password) + + def marshal(self): + base = super(WinDomainOsManager,self).marshal() + ''' + Serializes the os manager data so we can store it in database + ''' + return str.join( '\t', [ 'v1', self._domain, self._ou, self._account, CryptoManager.manager().encrypt(self._password), base.encode('hex') ] ) + + def unmarshal(self, s): + data = s.split('\t') + if data[0] == 'v1': + self._domain = data[1] + self._ou = data[2] + self._account = data[3] + self._password = CryptoManager.manager().decrypt(data[4]) + super(WinDomainOsManager, self).unmarshal(data[5].decode('hex')) + + def valuesDict(self): + dict = super(WinDomainOsManager,self).valuesDict() + dict['domain'] = self._domain + dict['ou'] = self._ou + dict['account'] = self._account + dict['password'] = self._password + return dict + diff --git a/server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py b/server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py new file mode 100644 index 000000000..3893d8e4f --- /dev/null +++ b/server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.ui.UserInterface import gui +from uds.core.osmanagers.BaseOsManager import BaseOSManager, State +from uds.core.managers.UserServiceManager import UserServiceManager + +import logging + +logger = logging.getLogger(__name__) + + +def scrambleMsg(data): + ''' + Simple scrambler so password are not seen at source page + ''' + res = [] + n = 0x32 + for c in data[::-1]: + res.append( chr(ord(c) ^ n) ) + n = (n + ord(c)) & 0xFF + return "".join(res).encode('hex') + + +class WindowsOsManager(BaseOSManager): + typeName = _('Windows Basic OS Manager') + typeType = 'WindowsManager' + typeDescription = _('Os Manager to control windows machines without domain. (Basically renames machine)') + iconFile = 'wosmanager.png' + + onLogout = gui.ChoiceField( label = _('On Logout'), order = 10, rdonly = False, tooltip = _('What to do when user logout from service'), + values = [ {'id' : 'keep', 'text' : _('Keep service assigned') }, + {'id' : 'remove', 'text' : _('Remove service') } + ], defvalue = 'keep' ) + + + @staticmethod + def validateLen(len): + try: + len = int(len) + except Exception: + raise BaseOSManager.ValidationException(_('Length must be numeric!!')) + if len > 6 or len < 1: + raise BaseOSManager.ValidationException(_('Length must be betwen 1 and six')) + return len + + def __init__(self,environment, values): + super(WindowsOsManager, self).__init__(environment, values) + if values is not None: + self._onLogout = values['onLogout'] + else: + self._onLogout = '' + + def release(self, service): + pass + + def getName(self, service): + ''' + gets name from deployed + ''' + si = service.getInstance() + name = si.getName() + service.updateData(si) + return name + + def infoVal(self,service): + return 'rename:' + self.getName(service) + + def notifyIp(self, uid, si, data): + # Notifies IP to deployed + pairs = data.split(',') + for p in pairs: + key, val = p.split('=') + if key.lower() == uid.lower(): + si.setIp(val) + break + + def process(self,service,msg, data): + ''' + We understand this messages: + * msg = info, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) + * msg = logon, data = Username, Informs that the username has logged in inside the machine + * msg = logoff, data = Username, Informs that the username has logged out of the machine + * msg = ready, data = None, Informs machine ready to be used + ''' + logger.info("Invoked WindowsOsManager for {0} with params: {1},{2}".format(service, msg, data)) + # We get from storage the name for this service. If no name, we try to assign a new one + ret = "ok" + inUse = False + notifyReady = False + doRemove = False + state = service.os_state + if msg == "info": + ret = self.infoVal(service) + state = State.PREPARING + elif msg == "logon": + si = service.getInstance() + si.userLoggedIn(data) + service.updateData(si) + inUse = True + elif msg == "logoff": + si = service.getInstance() + si.userLoggedOut(data) + service.updateData(si) + if self._onLogout == 'remove': + doRemove = True + elif msg == "ip": + # This ocurss on main loop inside machine, so service is usable + state = State.USABLE + si = service.getInstance() + self.notifyIp(service.unique_id, si, data) + service.updateData(si) + elif msg == "ready": + state = State.USABLE + si = service.getInstance() + notifyReady = True + self.notifyIp(service.unique_id, si, data) + service.updateData(si) + service.setInUse(inUse) + service.setOsState(state) + # If notifyReady is not true, save state, let UserServiceManager do it for us else + if doRemove is True: + service.remove() + else: + if notifyReady is False: + service.save() + else: + UserServiceManager.manager().notifyReadyFromOsManager(service, '') + logger.debug('Returning {0}'.format(ret)) + return scrambleMsg(ret) + + def checkState(self,service): + logger.debug('Checking state for service {0}'.format(service)) + return State.RUNNING + + def marshal(self): + ''' + Serializes the os manager data so we can store it in database + ''' + return str.join( '\t', [ 'v1', self._onLogout ] ) + + def unmarshal(self, s): + data = s.split('\t') + if data[0] == 'v1': + self._onLogout = data[1] + + def valuesDict(self): + return { 'onLogout' : self._onLogout } + \ No newline at end of file diff --git a/server/src/uds/osmanagers/WindowsOsManager/__init__.py b/server/src/uds/osmanagers/WindowsOsManager/__init__.py new file mode 100644 index 000000000..b3b6bc0db --- /dev/null +++ b/server/src/uds/osmanagers/WindowsOsManager/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.osmanagers.OSManagersFactory import OSManagersFactory +from uds.core.managers.DownloadsManager import DownloadsManager +from uds.osmanagers.WindowsOsManager.WindowsOsManager import WindowsOsManager +from uds.osmanagers.WindowsOsManager.WinDomainOsManager import WinDomainOsManager +import os.path, sys + +OSManagersFactory.factory().insert(WindowsOsManager) +OSManagersFactory.factory().insert(WinDomainOsManager) + +DownloadsManager.manager().registerDownloadable('UDSActorSetup.exe', + _('UDS Actor for windows machines (Important!! Requires .net framework 3.5 sp1)'), + os.path.dirname(sys.modules[__package__].__file__) + '/files/UDSActorSetup.exe', + 'application/x-msdos-program') diff --git a/server/src/uds/osmanagers/WindowsOsManager/files/UDSActorSetup.exe b/server/src/uds/osmanagers/WindowsOsManager/files/UDSActorSetup.exe new file mode 100644 index 0000000000000000000000000000000000000000..4ed87919079d6b049ba5d568e3210b2ac6b00f88 GIT binary patch literal 323233 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%0D8jA6z~ND|Ho~T@da-8+D*6 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 tHsn(?>r-sIIJd$}yx&m|k~omXl7 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 bmnP)Wgj|}COA~VcyO4|Nr$Xc%(L?_S=E7L9 literal 0 HcmV?d00001 diff --git a/server/src/uds/osmanagers/WindowsOsManager/wosmanager.png b/server/src/uds/osmanagers/WindowsOsManager/wosmanager.png new file mode 100644 index 0000000000000000000000000000000000000000..cefdb8071de3d91cf033b49a33e5422d49c739e8 GIT binary patch literal 6211 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/osmanagers/__init__.py b/server/src/uds/osmanagers/__init__.py new file mode 100644 index 000000000..81f89c513 --- /dev/null +++ b/server/src/uds/osmanagers/__init__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +import os.path, pkgutil +import sys +import uds.core + +# Dynamically import children of this package. The __init__.py files must register, if needed, inside ServiceProviderFactory +pkgpath = os.path.dirname(sys.modules[__name__].__file__) +for _, name, _ in pkgutil.iter_modules([pkgpath]): + __import__(name, globals(), locals(), [], -1) diff --git a/server/src/uds/services/PhysicalMachines/IPMachineDeployed.py b/server/src/uds/services/PhysicalMachines/IPMachineDeployed.py new file mode 100644 index 000000000..16f1fc156 --- /dev/null +++ b/server/src/uds/services/PhysicalMachines/IPMachineDeployed.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_lazy as _ +from uds.core import services +from uds.core.util.State import State +from uds.core.util.AutoAttributes import AutoAttributes +import logging + +logger = logging.getLogger(__name__) + +class IPMachineDeployed(AutoAttributes, services.UserDeployment): + suggestedTime = 10 + + def __init__(self, environment, **kwargs): + AutoAttributes.__init__(self, ip=str, reason=str, state=str) + services.UserDeployment.__init__(self,environment, **kwargs) + self._state = State.FINISHED + + def setIp(self, ip): + logger.debug('Setting IP to %s (ignored)' % ip) + + def getIp(self): + return self._ip + + def getName(self): + return _("IP ") + self._ip + + def getUniqueId(self): + return self._ip + + def setReady(self): + self._state = State.FINISHED + return self._state + + def __deploy(self): + ip = self.service().getUnassignedMachine() + if ip is None: + self._reason = 'No machines left' + self._state = State.ERROR + else: + self._ip = ip + self._state = State.FINISHED + return self._state + + def deployForUser(self, user): + logger.debug("Starting deploy of {0} for user {0}".format(self._ip, user)) + return self.__deploy() + + def checkState(self): + return self._state + + def finish(self): + pass + + def reasonOfError(self): + ''' + If a publication produces an error, here we must notify the reason why it happened. This will be called just after + publish or checkPublishingState if they return State.ERROR + ''' + return self._reason + + def cancel(self): + return self.destroy() + + def destroy(self): + if self._ip != '': + self.service().unassignMachine(self._ip) + self._state = State.FINISHED + return self._state diff --git a/server/src/uds/services/PhysicalMachines/IPMachinesService.py b/server/src/uds/services/PhysicalMachines/IPMachinesService.py new file mode 100644 index 000000000..828b2865c --- /dev/null +++ b/server/src/uds/services/PhysicalMachines/IPMachinesService.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_lazy as _ +from uds.core import services +from uds.core.util.AutoAttributes import AutoAttributes +from uds.core.ui.UserInterface import gui +from IPMachineDeployed import IPMachineDeployed +import logging, cPickle + +logger = logging.getLogger(__name__) + +class IPMachinesService(services.Service): + + # Gui + ipList = gui.EditableList(label=_('List of IPS')) + + # Description of service + typeName = _('Physical machines accesed by ip') + typeType = 'IPMachinesService' + typeDescription = _('This service provides access to POWERED-ON Machines by ip') + iconFile = 'machine.png' + + # Characteristics of service + maxDeployed = -1 # If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy + usesCache = False # Cache are running machine awaiting to be assigned + usesCache_L2 = False # L2 Cache are running machines in suspended state + needsManager = False # If the service needs a s.o. manager (managers are related to agents provided by services itselfs, i.e. virtual machines with agent) + mustAssignManually = False # If true, the system can't do an automatic assignation of a deployed user service from this service + + deployedType = IPMachineDeployed + + def __init__(self, environment, parent, values = None): + super(IPMachinesService, self).__init__(environment, parent, values) + if values is None: + self._ips = [] + else: + self._ips = list(set(values['ipList'])) # Avoid duplicates :-) + self._ips.sort() + + + def valuesDict(self): + return { 'ipList' : gui.convertToChoices(self._ips) } + + def marshal(self): + self.storage().saveData('ips', cPickle.dumps(self._ips)) + return 'v1' + + def unmarshal(self, vals): + if vals == 'v1': + self._ips = cPickle.loads( self.storage().readData('ips') ) + + + def getUnassignedMachine(self): + # Search first unassigned machine + try: + self.storage().lock() + for ip in self._ips: + if self.storage().readData(ip) == None: + self.storage().saveData(ip, ip) + return ip + return None + except Exception: + logger.exception("Exception at getUnassignedMachine") + return None + finally: + self.storage().unlock() + + def unassignMachine(self, ip): + try: + self.storage().lock() + self.storage().remove(ip) + except Exception: + logger.exception("Exception at getUnassignedMachine") + finally: + self.storage().unlock() diff --git a/server/src/uds/services/PhysicalMachines/ServiceProvider.py b/server/src/uds/services/PhysicalMachines/ServiceProvider.py new file mode 100644 index 000000000..59f6506df --- /dev/null +++ b/server/src/uds/services/PhysicalMachines/ServiceProvider.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext as _ +from uds.core.ui.UserInterface import gui +from uds.core import services + + +class PhysicalMachinesProvider(services.ServiceProvider): + # No extra data needed + + # What services do we offer? + offers = [] + typeName = 'Physical Machines Provider' + typeType = 'PhysicalMachinesServiceProvider' + typeDescription = 'Provides connection to Virtual Center Services' + iconFile = 'provider.png' + + from IPMachinesService import IPMachinesService + offers = [IPMachinesService] + + def __init__(self, environment, values = None): + ''' + Initializes the Physical Machines Service Provider + @param values: a dictionary with the required values, that are the ones declared for gui + ''' + super(PhysicalMachinesProvider, self).__init__(environment, values) + + def marshal(self): + ''' + Serializes the service provider data so we can store it in database + ''' + return str.join( '\t', [ 'v1' ] ) + + def unmarshal(self, str): + data = str.split('\t') + if data[0] == 'v1': + pass + + def __str__(self): + return "Physical Machines Provider: " + self.marshal() diff --git a/server/src/uds/services/PhysicalMachines/__init__.py b/server/src/uds/services/PhysicalMachines/__init__.py new file mode 100644 index 000000000..b9de8ef6a --- /dev/null +++ b/server/src/uds/services/PhysicalMachines/__init__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from ServiceProvider import PhysicalMachinesProvider + +# Now we use __subclasses__ method to locate Service Providers +# and register them inside factory +#ServiceProviderFactory.factory().insert(PhysicalMachinesProvider) diff --git a/server/src/uds/services/PhysicalMachines/machine.png b/server/src/uds/services/PhysicalMachines/machine.png new file mode 100644 index 0000000000000000000000000000000000000000..4d89bea6fc2dc3605b9fa9496242d19664db308d GIT binary patch literal 1111 zcmYjQ3rv$&6uxc$zMw5Xbn<8s3Y5wi358}gKnze@usAwffoT+mJQD<+n@l&!(xTfi z#w{qwz#;6x7>_wRdAiB^0}@4sfEyUL)?w$yA{MYvM*FvSKh-6>$-VcS+CauCwCdrJZ~q)BJj2hf5{Y%let!L=S6g{ple6Cx3;={684N;% z<&T1e#}ic@gT_n;WH>yiBnogr;KW_P<#YiOk({_o*tpxLP!Js7cuHOPhx`?pOMU0r zu^WoQpaYi=Cs3B9-YP;l;G5r<0lvBcY}R$)*uViG;9L$IkO*iJVLx89g89E60BD=$ zIUoH>AQ71mY&SfEK#F<$t0IH!>XbTe&EScC z>LvPO!=yu}QVi0L`Hfx}`(vi+d9a?X6|(@=ckxot)OW1m=@`~2Z6NwlzNJL{sQ2TR zjNbC1M3ra@UUFP={pD%857RP|U+{xKOn&b<|4jfC!JKvJNzjOD&V+07J`@CuZlL>N-CW7{6>ad!&@IW_2cLNOT*#-g88N~@cd1b!0 zV>^>;nbV<*O`+S2g*mIHiXGABE4Suemu2lkK)H0=xtce~yS7US?e5xRY^?xA48}XR zcXop(KZe)F@7Y;th&P{@=(g9q)EJpM)H{)#9tu1;{-4D(Pfpd1Nxg0AwYAUtt?u;C zdMZ$;qCYq$%Vyf(=?t~h5Oi1-D-pJ}Uwv}V icp#XOPww~Hyh{O@!s}s&;&Wn%Y(S|{$*;Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L00VXa00VXbebs`@00007bV*G`2iOQ1 z2_gW8yBe7Q00KBkL_t(|+LcpHiyA=?t)3abRt0rTApal-@unwXbBMXe+ujy(O@u%$ zf|osKAz<q2*ARjr1W)2s0uuJ%?(FD}x_*q)dDSzc$wCfkEW2yE-mBNu)uP+& zo|&fkF_lW0grR8~2_ck)LrNLW$~@1b^?Gehr_(p0-|ydaIvvxpEJ`F26pO_u8jVsU z5+PmJ8K(fQ>(Xwwqs?Z+V9I8*RIAl2BbiK^i9tQqJ&}cNCR;$(bkbR0Hr04TFOXj2S+gRXjd>;;n7xjAmM~E^0BtXVz>2&%< zE|-h)6=gCRzHbH-{1hJCb13?P&;x;Zs0iT4l}hEbR4P3wm&+Ms7lXlI*6a1IzG2K} evuni35bzuGg&>iM2rO~{0000= 5: + return State.FINISHED + + # random fail + if random.randint(0, 9) == 9: + self.storage().saveData('error', 'Random error at checkState :-)') + return State.ERROR + + self.storage().saveData('count', str(count)) + return State.RUNNING + + def finish(self): + ''' + Invoked when the core notices that the deployment of a service has finished. + (No matter wether it is for cache or for an user) + + This gives the oportunity to make something at that moment. + :note: You can also make these operations at checkState, this is really + not needed, but can be provided (default implementation of base class does + nothing) + ''' + # Note that this is not really needed, is just a sample of storage use + self.storage().remove('count') + + def assignToUser(self, user): + ''' + This method is invoked whenever a cache item gets assigned to an user. + This gives the User Deployment an oportunity to do whatever actions + are required so the service puts at a correct state for using by a service. + + In our sample, the service is always ready, so this does nothing. + + This is not a task method. All level 1 cache items can be diretly + assigned to an user with no more work needed, but, if something is needed, + here you can do whatever you need + ''' + pass + + def userLoggedIn(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged into a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responability of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actor. + ''' + # We store the value at storage, but never get used, just an example + self.storage().saveData('user', user) + + def userLoggedOut(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged out if a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responability of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actor. + ''' + # We do nothing more that remove the user + self.storage().remove('user') + + def reasonOfError(self): + ''' + Returns the reason of the error. + + Remember that the class is responsible of returning this whenever asked + for it, and it will be asked everytime it's needed to be shown to the + user (when the administation asks for it). + ''' + return self.storage().readData('error') or 'No error' + + def destroy(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + Invoked for destroying a deployed service + Do whatever needed here, as deleting associated data if needed (i.e. a copy of the machine, snapshots, etc...) + @return: State.FINISHED if no more checks/steps for deployment are needed, State.RUNNING if more steps are needed (steps checked using checkState) + ''' + return State.FINISHED + + def cancel(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + This can be invoked directly by an administration or by the clean up + of the deployed service (indirectly). + When administrator requests it, the cancel is "delayed" and not + invoked directly. + ''' + return State.FINISHED + \ No newline at end of file diff --git a/server/src/uds/services/Sample/SampleUserDeploymentTwo.py b/server/src/uds/services/Sample/SampleUserDeploymentTwo.py new file mode 100644 index 000000000..687971e7f --- /dev/null +++ b/server/src/uds/services/Sample/SampleUserDeploymentTwo.py @@ -0,0 +1,469 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.services import UserDeployment +from uds.core.util.State import State +import logging + +logger = logging.getLogger(__name__) + +class SampleUserDeploymentTwo(UserDeployment): + ''' + This class generates the user consumable elements of the service tree. + + This is almost the same as SampleUserDeploymentOne, but differs that this one + uses the publication to get data from it, in a very basic way. + + After creating at administration interface an Deployed Service, UDS will + create consumable services for users using UserDeployment class as + provider of this elements. + + At class instantiation, this will receive an environment with"generator", + that are classes that provides a way to generate unique items. + + The generators provided right now are 'mac' and 'name'. To get more info + about this, look at py:class:`uds.core.util.UniqueMacGenerator.UniqueNameGenerator` + and py:class:`uds.core.util.UniqueNameGenerator.UniqueNameGenerator` + + As sample also of environment storage usage, wi will use here the provider + storage to keep all our needed info, leaving marshal and unmarshal (needed + by Serializable classes, like this) empty (that is, returns '' first and does + nothing the second one) + + Also Remember, if you don't include this class as the deployedType of the + SampleServiceTwo, or whenever you try to access a service of SampleServiceTwo, + you will get an exception that says that you haven't included the deployedType. + ''' + + #: Recheck every five seconds by default (for task methods) + suggestedTime = 2 + + def initialize(self): + ''' + Initialize default attributes values here. We can do whatever we like, + but for this sample this is just right... + ''' + self._name = '' + self._ip = '' + self._mac = '' + self._error = '' + self._count = 0 + + # Serializable needed methods + def marshal(self): + ''' + Marshal own data, in this sample we will marshal internal needed + attributes. + + In this case, the data will be store with the database record. To + minimize database storage usage, we will "zip" data before returning it. + Anyway, we should keep this data as low as possible, we also have an + storage for loading larger data. + + :note: It's a good idea when providing marshalers, to store a 'version' + beside the values, so we can, at a later stage, treat with old + data for current modules. + ''' + data = '\t'.join(['v1', self._name, self._ip, self._mac, self._error, + str(self._count)]) + return data.encode('zip') + + def unmarshal(self, str_): + ''' + We unmarshal the content. + ''' + data = str_.decode('zip').split('\t') + # Data Version check + # If we include some new data at some point in a future, we can + # add "default" values at v1 check, and load new values at 'v2' check. + if data[0] == 'v1': + self._name, self._ip, self._mac, self._error, count = data[1:] + self._count = int(count) + + def getName(self): + ''' + We override this to return a name to display. Default implementation + (in base class), returns getUniqueIde() value + This name will help user to identify elements, and is only used + at administration interface. + + We will use here the environment name provided generator to generate + a name for this element. + + The namaGenerator need two params, the base name and a length for a + numeric incremental part for generating unique names. This are unique for + all UDS names generations, that is, UDS will not generate this name again + until this name is freed, or object is removed, what makes its environment + to also get removed, that makes all unique ids (names and macs right now) + to also get released. + + Every time get method of a generator gets called, the generator creates + a new unique name, so we keep the first generated name cached and don't + generate more names. (Generator are simple utility classes) + ''' + if self._name == '': + self._name = self.nameGenerator().get( self.publication().getBaseName(), + 3 ) + # self._name will be stored when object is marshaled + return self._name + + def setIp(self, ip): + ''' + In our case, there is no OS manager associated with this, so this method + will never get called, but we put here as sample. + + Whenever an os manager actor notifies the broker the state of the service + (mainly machines), the implementation of that os manager can (an probably will) + need to notify the IP of the deployed service. Remember that UDS treats with + IP services, so will probable needed in every service that you will create. + :note: This IP is the IP of the "consumed service", so the transport can + access it. + ''' + self._ip = ip + + def getUniqueId(self): + ''' + Return and unique identifier for this service. + In our case, we will generate a mac name, that can be also as sample + of 'mac' generator use, and probably will get used something like this + at some services. + + The get method of a mac generator takes one param, that is the mac range + to use to get an unused mac. + + The mac generated is not used by anyone, it will not depend on + the range, the generator will take care that this mac is unique + and in the range provided, or it will return None. The ranges + are wide enough to ensure that we always will get a mac address + in this case, but if this is not your case, take into account that + None is a possible return value, and in that case, you should return an + invalid id right now. Every time a task method is invoked, the core + will try to update the value of the unique id using this method, so + that id can change with time. (In fact, it's not unique at database level, + it's unique in the sense that you must return an unique id that can, for + example, be used by os managers to identify this element). + + :note: Normally, getting out of macs in the mac pool is a bad thing... :-) + ''' + if self._mac == '': + self._mac = self.macGenerator().get( '00:00:00:00:00:00-00:FF:FF:FF:FF:FF' ) + return self._mac + + def getIp(self): + ''' + We need to implement this method, so we can return the IP for transports + use. If no IP is known for this service, this must return None + + If our sample do not returns an IP, IP transport will never work with + this service. Remember in real cases to return a valid IP address if + the service is accesible and you alredy know that (for example, because + the IP has been assigend via setIp by an os manager) or because + you get it for some other method. + + Storage returns None if key is not stored. + + :note: Keeping the IP address is responsibility of the User Deployment. + Every time the core needs to provide the service to the user, or + show the IP to the administrator, this method will get called + + ''' + if self._ip == '': + return '192.168.0.34' # Sample IP for testing purposes only + return self._ip + + def setReady(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + The method is invoked whenever a machine is provided to an user, right + before presenting it (via transport rendering) to the user. + + This method exist for this kind of situations (i will explain it with a + sample) + + Imagine a Service tree (Provider, Service, ...) for virtual machines. + This machines will get created by the UserDeployment implementation, but, + at some time, the machine can be put at in an state (suspend, shut down) + that will make the transport impossible to connect with it. + + This method, in this case, will check the state of the machine, and if + it is "ready", that is, powered on and accessible, it will return + "State.FINISHED". If the machine is not accessible (has been erased, for + example), it will return "State.ERROR" and store a reason of error so UDS + can ask for it and present this information to the Administrator. + + If the machine powered off, or suspended, or any other state that is not + directly usable but can be put in an usable state, it will return + "State.RUNNING", and core will use checkState to see when the operation + has finished. + + I hope this sample is enough to explain the use of this method.. + ''' + + # In our case, the service is always ready + return State.FINISHED + + def deployForUser(self, user): + ''' + Deploys an service instance for an user. + + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + The user parameter is not realy neded, but provided. It indicates the + Database User Object (see py:mod:`uds.modules`) to which this deployed + user service will be assigned to. + + This method will get called whenever a new deployed service for an user + is needed. This will give this class the oportunity to create + a service that is assigned to an user. + + The way of using this method is as follows: + + If the service gets created in "one step", that is, before the return + of this method, the consumable service for the user gets created, it + will return "State.FINISH". + If the service needs more steps (as in this case), we will return + "State.RUNNING", and if it has an error, it wil return "State.ERROR" and + store an error string so administration interface can show it. + + We do not use user for anything, as in most cases will be. + ''' + import random + + self._count = 0 + + # random fail + if random.randint(0, 9) == 9: + # Note that we can mark this string as translatable, and return + # it translated at reasonOfError method + self._error = 'Random error at deployForUser :-)' + return State.ERROR + + return State.RUNNING + + def deployForCache(self, cacheLevel): + ''' + Deploys a user deployment as cache. + + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + In our sample, this will do exactly the same as deploy for user, + except that it will never will give an error. + + See deployForUser for a description of what this method should do. + + :note: deployForCache is invoked whenever a new cache element is needed + for an specific user deployment. It will also indicate for what + cache level (L1, L2) is the deployment + ''' + self._count = 0 + return State.RUNNING + + def moveToCache(self, newLevel): + ''' + This method is invoked whenever the core needs to move from the current + cache level to a new cache level an user deployment. + + This is a task method. As that, the expected return values are + State values RUNNING, FINISHED or ERROR. + + We only provide newLevel, because there is only two cache levels, so if + newLevel is L1, the actual is L2, and if it is L2, the actual is L1. + + Actually there is no possibility to move assigned services again back to + cache. If some service needs that kind of functionallity, this must be + provided at service level (for example, when doing publishing creating + a number of services that will be used, released and reused by users). + + Also, user deployments that are at cache level 2 will never get directly + assigned to user. First, it will pass to L1 and then it will get assigned. + + A good sample of a real implementation of this is moving a virtual machine + from a "suspended" state to "running" state to assign it to an user. + + In this sample, there is L2 cache also, but moving from L1 to L2 and + from L2 to L1 is doing really nothing, so this method will do nothing. + + In a real scenario, we will, for example, suspend or resume virtual machine + and, return State.RUNNING and at checkState check if this task is completed. + ''' + pass + + def checkState(self): + ''' + Our deployForUser method will initiate the consumable service deployment, + but will not finish it. + + So in our sample, we will only check if a number reaches 5, and if so + return that we have finished, else we will return that we are working + on it. + + One deployForUser returns State.RUNNING, this task will get called until + checkState returns State.FINISHED. + + Also, we will make the user deployment fail one of every 10 calls to this + method. + + Note: Destroying, canceling and deploying for cache also makes use of + this method, so you must keep the info of that you are checking if you + need it. + + In our case, destroy is 1-step action so this will no get called while + destroying, and cancel will simply invoke destroy. Cache deployment is + exactly as user deployment, except that the core will not assign it to + anyone, and cache moving operations is + ''' + import random + + self._count += 1 + # Count is always a valid value, because this method will never get + # called before deployForUser, deployForCache, destroy or cancel. + # In our sample, we only use checkState in case of deployForUser, + # so at first call count will be 0. + if self._count >= 5: + return State.FINISHED + + # random fail + if random.randint(0, 9) == 9: + self._error = 'Random error at checkState :-)' + return State.ERROR + + return State.RUNNING + + def finish(self): + ''' + Invoked when the core notices that the deployment of a service has finished. + (No matter whether it is for cache or for an user) + + This gives the opportunity to make something at that moment. + + :note: You can also make these operations at checkState, this is really + not needed, but can be provided (default implementation of base class does + nothing) + ''' + # We set count to 0, not needed but for sample purposes + self._count = 0 + + def assignToUser(self, user): + ''' + This method is invoked whenever a cache item gets assigned to an user. + This is not a task method right now, simply a notification. This means + that L1 cache items must be directly usable (except for the readyness part) + by users in a single step operation. + + Note that there will be an setReady call before letting the user consume + this user deployment, so this is more informational (so, if you keep at + what cache level is this instance, you can update it) than anything else. + + This is not a task method. All level 1 cache items can be dircetly + assigned to an user with no more work needed, but, if something is needed, + here you can do whatever you need. + + user is a Database user object. + ''' + logger.debug('Assigned to user {0}'.format(user)) + + def userLoggedIn(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged into a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responsibility of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actors. + ''' + # We store the value at storage, but never get used, just an example + self.storage().saveData('user', user) + + def userLoggedOut(self, user): + ''' + This method must be available so os managers can invoke it whenever + an user get logged out if a service. + + Default implementation does nothing, so if you are going to do nothing, + you don't need to implement it. + + The responability of notifying it is of os manager actor, and it's + directly invoked by os managers (right now, linux os manager and windows + os manager) + + The user provided is just an string, that is provided by actor. + ''' + # We do nothing more that remove the user + self.storage().remove('user') + + def reasonOfError(self): + ''' + Returns the reason of the error. + + Remember that the class is responsible of returning this whenever asked + for it, and it will be asked everytime it's needed to be shown to the + user (when the administation asks for it). + + :note: Remember that you can use ugettext to translate this error to + user language whenever it is possible. (This one will get invoked + directly from admin interface and, as so, will have translation + environment correctly set up. + ''' + return self._error + + def destroy(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + Invoked for destroying a deployed service + Do whatever needed here, as deleting associated data if needed (i.e. a copy of the machine, snapshots, etc...) + @return: State.FINISHED if no more checks/steps for deployment are needed, State.RUNNING if more steps are needed (steps checked using checkState) + ''' + return State.FINISHED + + def cancel(self): + ''' + This is a task method. As that, the excepted return values are + State values RUNNING, FINISHED or ERROR. + + This can be invoked directly by an administration or by the clean up + of the deployed service (indirectly). + When administrator requests it, the cancel is "delayed" and not + invoked directly. + ''' + return State.FINISHED diff --git a/server/src/uds/services/Sample/__init__.py b/server/src/uds/services/Sample/__init__.py new file mode 100644 index 000000000..38a9126af --- /dev/null +++ b/server/src/uds/services/Sample/__init__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +Sample Service module. + +This package simply shows how a new service can be implemented. + + +The first thing to do in every package that is a module is register the +class that is responsible of providing the module with the system. + +For this, we must simply import the class at __init__, UDS will take care +of the rest +''' + +from SampleProvider import Provider + diff --git a/server/src/uds/services/Sample/provider.png b/server/src/uds/services/Sample/provider.png new file mode 100644 index 0000000000000000000000000000000000000000..d2a954d44526a447e6f4e693324450868f7443b4 GIT binary patch literal 491 zcmV@I z6tZ>cs#3?$MGAIGp@M&a*2RKp$X#+d-yK9MULgrQczO4}pZ6m4M zO;r>{>SMK1(R3jAkU$c z(ue|C=n(p<8K9a`77{=;?4T$LkSNe1Vn%?5fE)o*{&oS%K|tex5Zy(6z;}}H5(OFOaM9~Q zx7&rU^;%d7NZx5-7`_aL!@KIe_FWhZ2B&COuIuhI#@gB+jYenIeO0r?aeN&F!KL=o zG(F6?Xp54<9?3z011HBvpSaL>eLkDbZue{YI{B7A; zx^zG+3`~(Sh76=N$^gPH@B}t~{eE8{Qq_s}Bc0B^clW(J|M0A}@Cg0^OH^2+eWX=z z8HV;9tA?$Y#~XCs8)eiD`C;wl#NCMX9_3J#DwZG;yA(cv%$jyaC7s6 zE3I*NH|N>gxtL5Qa(_%f^G&E%3b9(P{uT&=6^lLX z&UHLKeamS)yuRv7&0Gw~Ajab{`u+a9fTeNw4=^TXvZa_O&nYAfl2g!g-+^ei+c;q* z`(?de-|OLAfZmE8=2uN%Mx)WT+wE3y0khd`tJ?F=^odr-WyG{^|NT4v2BY2&XM6rZ QqyPW_07*qoM6N<$f=DO_-T(jq literal 0 HcmV?d00001 diff --git a/server/src/uds/services/__init__.py b/server/src/uds/services/__init__.py new file mode 100644 index 000000000..de391673c --- /dev/null +++ b/server/src/uds/services/__init__.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +Authentication modules for uds are contained inside this module. +To create a new authentication module, you will need to follow this steps: + 1.- Create the authentication module, probably based on an existing one + 2.- Insert the module as child of this module + 3.- Import the class of your authentication module at __init__. For example:: + from Authenticator import SimpleAthenticator + 4.- Done. At Server restart, the module will be recognized, loaded and treated + +The registration of modules is done locating subclases of :py:class:`uds.core.auths.Authentication` + +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' + +def __init__(): + ''' + This imports all packages that are descendant of this package, and, after that, + it register all subclases of service provider as + ''' + import os.path, pkgutil + import sys + from uds.core import services + + # Dinamycally import children of this package. The __init__.py files must register, if needed, inside ServiceProviderFactory + pkgpath = os.path.dirname(sys.modules[__name__].__file__) + for _, name, _ in pkgutil.iter_modules([pkgpath]): + __import__(name, globals(), locals(), [], -1) + + p = services.ServiceProvider + # This is marked as error in IDE, but it's not (__subclasses__) + for cls in p.__subclasses__(): + services.factory().insert(cls) + +__init__() \ No newline at end of file diff --git a/server/src/uds/static/css/reset.css b/server/src/uds/static/css/reset.css new file mode 100644 index 000000000..e2f0075b6 --- /dev/null +++ b/server/src/uds/static/css/reset.css @@ -0,0 +1,46 @@ +@CHARSET "UTF-8"; + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ + +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/server/src/uds/static/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png b/server/src/uds/static/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5dab2ab7b1c50dea9cfe73dc5a269a92d2d4b4 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FscKIb$B>N1x91EQ4=4yQ7#`R^ z$vje}bP0l+XkK DSH>_4 literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png b/server/src/uds/static/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..ac8b229af950c29356abf64a6c4aa894575445f0 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FsY*{5$B>N1x91EQ4=4yQYz+E8 zPo9&<{J;c_6SHRil>2s{Zw^OT)6@jj2u|u!(plXsM>LJD`vD!n;OXk;vd$@?2>^GI BH@yG= literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png b/server/src/uds/static/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..ad3d6346e00f246102f72f2e026ed0491988b394 GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnour0hLi978O6-<~(*I$*%ybaDOn z{W;e!B}_MSUQoPXhYd^Y6RUoS1yepnPx`2Kz)7OXQG!!=-jY=F+d2OOy?#DnJ32>z UEim$g7SJdLPgg&ebxsLQ09~*s;{X5v literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png b/server/src/uds/static/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..42ccba269b6e91bef12ad0fa18be651b5ef0ee68 GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouqzpV=978O6-=0?FV^9z|eBtf= z|7WztIJ;WT>{+tN>ySr~=F{k$>;_x^_y?afmf9pRKH0)6?eSP?3s5hEr>mdKI;Vst E0O;M1& literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png b/server/src/uds/static/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..1d43b47e608e15b5fecdb75918178142c80fc53a GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAGVZ#9R3S1=2iS978O6-(KkLDcI7TBVw{vDV26o)#~38k_!`W=^oo1w6ixmPC4R1b Tyd6G3lNdZ*{an^LB{Ts5`idse literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/server/src/uds/static/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..7c9fa6c6edcfcdd3e5b77e6f547b719e6fc66e30 GIT binary patch literal 101 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-icons_2e83ff_256x240.png b/server/src/uds/static/css/smoothness/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..09d1cdc856c292c4ab6dd818c7543ac0828bd616 GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-icons_454545_256x240.png b/server/src/uds/static/css/smoothness/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..59bd45b907c4fd965697774ce8c5fc6b2fd9c105 GIT binary patch literal 4369 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 oBo%DI*Kv;w;*%(i9W@f3_WCF#rGn literal 0 HcmV?d00001 diff --git a/server/src/uds/static/css/smoothness/images/ui-icons_cd0a0a_256x240.png b/server/src/uds/static/css/smoothness/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..2ab019b73ec11a485fa09378f3a0e155194f6a5d GIT binary patch literal 4369 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 Z`4_pP6Ybfj$}Io@002ovPDHLkV1g@u0D%Ai literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/2uparrow.png b/server/src/uds/static/img/2uparrow.png new file mode 100644 index 0000000000000000000000000000000000000000..7bc9ab5240659f893b315edc37662b5e25bc3199 GIT binary patch literal 656 zcmV;B0&o3^P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iySy z5g7ourT35k00Iz6L_t(2&rOm`NK|0}h0p(w;$zZrdN`V5p;IbRU<6r2fe=DaWN2ou zg|sPxT4axfQ4yk9B=jJRaA9o>siY)?NQ4PWVKOaOAv(_7D@KmjF&*#y|IG$KhNI5D_BI5Fs1*2HR$APE47%otKiNz~w)RxY4>#qErlx)qo`d3fKW7?KoU2)Z`Iemucu*|_SoNX zhN58_mT8}Ub&eO`T&W|8P&Awfl6V?;e(LhO>ohFWHY}5?(1vB&$6uUf!>0$;B;hJP zy5mUKw-2w+l%7B=J|`w-B4TRp4<=`)5U~sqi^s$kpI@}Kw^c4=*g^3)(0a+3G53s4 zk8N6v#Y+ov@~NnWqaBds$lx!L;!O>>*R2WXW#oPVNeqO7sWmTazxVwPD8n+;F1FkVF!$>I)~=w)8#uTvJk2vMsB4rE0N^%ue^g<4wI6~?ZD9rDT&GUaPN;~|GvUzF%e+)yMrhx_$&}2 zGQX;_sIY>HcMCtQ+@AAnc2+Dj5j>9j-5!Rn-s#%9?>g)M`l0;*QPeIFh}@-edxYXE z6J@&xMQhLO{O0E|RpB434n{i$n9|%$mA`waVj5)1uU;h3}skdYjTx#AUY%y`je8ipVc#Azj5 z>i0?dq)e8RuP;4&u48Qos&z;<6ulP1uGIIiKFb7hvvl~dY9%iS7#{P$<5jVLdm3yG zFTOn2jVa#Os||DX@yC+HbGN2S7f^zmvE`!TBAw=k#3j378J@)D-z`W=kgz&C4(nIx z;B?CP>H2+GC#K~f9!%E;!NW+OPGyc5OhxSX= z7@Lr>ZQoS{0s+KC0rCvTg+;&8ARIg7M3PC-ya>?};vzt^rzlkss4C z)i)rcva$$W{WhF9(S(}AMx>=AA~`o3b0c27P+6?REX^Vv7F*+LCjaBzMV+B2 zE*zK3tvOAM+aM9!*K8!blponLjd-5jWKtvT{)Va}SWDxJ7QJFg#%&#Ew*Ch+5#wkF5u z{R7P7Z1qzj@;o7OoKVT)}ko;nbkb`kzsB m*&E;sfC!REz=D|I#p{CO)O6EH!{F`UyJ1s#$>j|@4*d_Va6YF1 literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/bg_barra_pie.png b/server/src/uds/static/img/bg_barra_pie.png new file mode 100644 index 0000000000000000000000000000000000000000..70b3697d3185e9249248a847c8c8bf1d2262c531 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^OhBB%0V0(v%S?b2V{wqX6T`Z5GB1G~&H|6fVg?3o zVGw3ym^DWND9B#o>FdgVhly2G(!%%V(%nEI+02lL66gHf+|;}hAeVu`xhOTUBsE2$ zJhLQ2!QIn0AVn{g9VqVO>EaloasF-ZY0(A;jw41of2XH2nl-Wi{JLPdO@i8sUCIYk zzc3X@b4)+z!V_@+OvKk$PbU>+alJZGBzJX6zOwSg(_3V=u`){h`Jo`!{fCn!E-T~* O$U;w7KbLh*2~7Y-lSVNB literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/bg_barra_superior2.png b/server/src/uds/static/img/bg_barra_superior2.png new file mode 100644 index 0000000000000000000000000000000000000000..288cc78cac74b2b5a526926881e7e74c6521fd50 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI}!2~3QW~$u<2^0spJ29*~C-V}>;VkfoEM{Qf z76xHPhFNnYfP(BLp1!W^x4ES`6*>2n8*&4MBuiW)N}Tg^b5rw57@Uhz6H8K46v{J8 zG8EiBeFMT9`NV;WY&=~YLpZLl?cB(Dz<}Yf%+sBJ>itqZ+Do)bRv%Qj8JRt?<;{eN whu_!Ltj)+gy)-1Ve|5yl_W89;#ZSK9v0>57d*bJ01~iAk)78&qol`;+0EYTM$^ZZW literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/bg_uds.png b/server/src/uds/static/img/bg_uds.png new file mode 100644 index 0000000000000000000000000000000000000000..b29cb791880bf3bb4d8d221d2f86f5b1de874232 GIT binary patch literal 65759 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/down.png b/server/src/uds/static/img/down.png new file mode 100644 index 0000000000000000000000000000000000000000..72708b992fd582e35362de2fe1a3ddf77be0b2d3 GIT binary patch literal 198 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 O7(8A5T-G@yGywn-Ye13! literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/flags/es.png b/server/src/uds/static/img/flags/es.png new file mode 100644 index 0000000000000000000000000000000000000000..5ddb8e855358e5381f379e0ce84ede0eedd27fbb GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^A|N&kGmwm~3(f^njKx9jP7LeL$-D$|3<7*YT!A!0 zLqh|IVEE6#@c%y$0co5-;KvOmpd@3Gx4R2N2dk_Hki%Kv5n0T@z*P#uj9#jD{{jWs zOFVsD*>7_Tb4V&3R5Hr|3K@F3IEHXsPfl=P5EIuZa7f$K=H$rG@$wN@@~mB!hiAHR oEK_cJDa3PKjK`F5wf4u@pObhHwBu4M$1`kk47*5n0T@ zz;^_M8K-LVNdpDVJzX3_B&H^(II#2b{Fjsff>$6?QX=t?j|5jCgOJES_vRA}o^u4> ksbxi;?ND@bj~C`-&}|cLKIq5)7-$58r>mdKI;Vst0F(40r~m)} literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/ico-menos.png b/server/src/uds/static/img/ico-menos.png new file mode 100644 index 0000000000000000000000000000000000000000..fd37a9259ea16ea4bd9b7a93ae6817643b3613d2 GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u@pObhHwBu4M$1`kk47*5n0T@ zz;^_M8K-LVNdpDtJzX3_B&H@ONEjSA@nQZ$dqb{B38_xwiJd~vdCV9XcCs?He-HT) Q0#wT2>FVdQ&MBb@02fdmBLDyZ literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/mas.png b/server/src/uds/static/img/mas.png new file mode 100644 index 0000000000000000000000000000000000000000..64fb7f9356d09b7a21750afa8d7147b800db0612 GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m=s_PZ!4!i{9h}iHrjs{+A9McwpaC`|uF!nykozOuL$F#+c%~8!U6C z89XdzwCi<|bl`FL$h2wxMaEq+IzA8B6n;y}`ox7c9CH6A#jVE~lk}qP;9|Df+(n1g z5;78iU_+?)0qMU5%)C52{|;xwywd1n?czP~Qv8X5;;Hk@Yz%_hdYj(v{oN0A3WKMs KpUXO@geCy?TS$Qb literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/menos.png b/server/src/uds/static/img/menos.png new file mode 100644 index 0000000000000000000000000000000000000000..c50a5255fbd12e17e18294a28cd18a45cbc6912b GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+91PZ!4!i{9h}307u)iHVkMY;6BKuU0%f#OelQvWd+&@GzRYrD`A)X3F(~ok5X>e^x_FUn|f+22WQ%mvv4FO#p0` BDnCjiND@>lJh8iRsP+D7K5*;3^ zJDm{6(KbY5MQUyc412^AQ{P`Z+ITUhZ}+K<^t!lu)85y2n?KV!mr0_n*(9WOz=SC< zaHP*}hhr$xK`9lpbtU5T=yiJetyMkOx;Oc2zI?%E5x5}<;c0!hv-s@6v(1vw8ew%f zY0%?!SF$PgKD$Q(Sjs={*m}Do^SncJ-Y9Iqr?_lm4tsrYFsG)#}`#>wPZ5rw@BIs?gIZ6cx>-{yBWOXtd#z z&WddiSc_eA=y;F4e?;xYT0*Av{T5fMn60{VZQNWx;q1C_Y9>wP7}omYt-e7RT&3K6RghBK z?~_xeADU+G)Lj{}Mv2wI9Nk(CPz*m(3|Ira%plqBkcm8Fpn`JSK~b?&suMs^s{pfz z_ahmmk(*z_n^Mq6_J?HF9SmVYlD36V3>7y(5T6D>G6Vt@0r(h@`}CBTvd+xKfxq$q z^$cOZzyh0BR$=QIXnW)nxo}lz&K~O@6vo^=kdM{_ni^ry!We;-r4qp5Y9z`8L79LA zO#;XxVzGc^R75zdr~A7d(#>v^Vz0B-QGgi0?m@zi)p6uOsUQy=s3gJ-KxhWYd7@4J+V*Z~M15!VzOer7_j+Z^qm{I(gfue6H@%9tu%T7vu zUF20>1yU>s$QQ#xML|mTz=(?z1hMT2N}7Itc*$K@rv0s*zP80Cs(9h2mG)Aev(ZF% zc8+MSn`kf#8G-h5#VuUoG|jTcGJBjC)R(JoYx3R+~;o`Qaqr`S*$!61A;cRoYLV&o4Z^ILH~Jvc##P&+e}-NWqBqaY6*{ X&m7)7pT89PgrIcKKEoF;m|Fe@n6m>4 literal 0 HcmV?d00001 diff --git a/server/src/uds/static/img/up.png b/server/src/uds/static/img/up.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe3b4890143e45d4861d2e932d78cc15fb3b203 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m=r|PZ!4!i{9h}i7N*V9C)z5vGHL2CR6!+TOa&?7|rcr&azDM8Pg=5 zgUcE-oG*wfwKF<8Z^(4xXWrBHU=#2B)doBYW;`9=*ftg&(Ns|7`M}te=)j|3U3r;> afkEQK#J&FS_1%HCFnGH9xvX4nJ z@ErkR#;MwT(m+91PZ!4!i{9h}i7O92tY!Vr%fs_8c*#s1=eB8SZYgJXCJIZat=Cdm zp=qAObhwpUTtZSpLSlwTk&>dr#Q*I(6;m>o0gd^Rkl@9$b$tZTKn71&KbLh*2~7Yo Cm@K~l literal 0 HcmV?d00001 diff --git a/server/src/uds/static/js/PluginDetect_Java.js b/server/src/uds/static/js/PluginDetect_Java.js new file mode 100644 index 000000000..8241ace3d --- /dev/null +++ b/server/src/uds/static/js/PluginDetect_Java.js @@ -0,0 +1,7 @@ +/* +PluginDetect v0.7.8 www.pinlady.net/PluginDetect/ +www.pinlady.net/PluginDetect/license/ +[ getVersion isMinVersion onDetectionDone onWindowLoaded ] +[ Java(OTF & NOTF) ] +*/ +var PluginDetect={version:"0.7.8",name:"PluginDetect",handler:function(c,b,a){return function(){c(b,a)}},isDefined:function(b){return typeof b!="undefined"},isArray:function(b){return(/array/i).test(Object.prototype.toString.call(b))},isFunc:function(b){return typeof b=="function"},isString:function(b){return typeof b=="string"},isNum:function(b){return typeof b=="number"},isStrNum:function(b){return(typeof b=="string"&&(/\d/).test(b))},getNumRegx:/[\d][\d\.\_,-]*/,splitNumRegx:/[\.\_,-]/g,getNum:function(b,c){var d=this,a=d.isStrNum(b)?(d.isDefined(c)?new RegExp(c):d.getNumRegx).exec(b):null;return a?a[0]:null},compareNums:function(h,f,d){var e=this,c,b,a,g=parseInt;if(e.isStrNum(h)&&e.isStrNum(f)){if(e.isDefined(d)&&d.compareNums){return d.compareNums(h,f)}c=h.split(e.splitNumRegx);b=f.split(e.splitNumRegx);for(a=0;ag(b[a],10)){return 1}if(g(c[a],10)c||!(/\d/).test(e[a])){e[a]="0"}}return e.slice(0,4).join(",")},$$hasMimeType:function(a){return function(c){if(!a.isIE&&c){var f,e,b,d=a.isArray(c)?c:(a.isString(c)?[c]:[]);for(b=0;b0&&!f[g]){f[g]=f[a](f);delete f[a]}}catch(d){}}}},initObj:function(e,b,d){var a,c;if(e){if(e[b[0]]==1||d){for(a=0;a=0;f=f-2){if(d[f]&&new RegExp(d[f],"i").test(b)){c.OS=d[f+1];break}}}c.convertFuncs(c);c.head=(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]||document.body||null);c.isIE=(new Function("return "+e+"*@cc_on!@*"+e+"false"))();c.verIE=c.isIE&&(/MSIE\s*(\d+\.?\d*)/i).test(i)?parseFloat(RegExp.$1,10):null;c.ActiveXEnabled=false;if(c.isIE){var f,j=["Msxml2.XMLHTTP","Msxml2.DOMDocument","Microsoft.XMLDOM","ShockwaveFlash.ShockwaveFlash","TDCCtl.TDCCtl","Shell.UIHelper","Scripting.Dictionary","wmplayer.ocx"];for(f=0;f0&&c.isFunc(b[0])))){a.push(b)}},callArray:function(b){var c=this,a;if(c.isArray(b)){for(a=0;a0&&b.isFunc(c[0])){c[0](b,a>1?c[1]:0,a>2?c[2]:0,a>3?c[3]:0)}else{if(b.isFunc(c)){c(b)}}},$$isMinVersion:function(a){return function(h,g,d,c){var e=a.init(h),f,b=-1,j={};if(e.status<0){return e.status}f=e.plugin;g=a.formatNum(a.isNum(g)?g.toString():(a.isStrNum(g)?a.getNum(g):"0"));if(f.getVersionDone!=1){f.getVersion(g,d,c);if(f.getVersionDone===null){f.getVersionDone=1}}a.cleanup();if(f.installed!==null){b=f.installed<=0.5?f.installed:(f.installed==0.7?1:(f.version===null?0:(a.compareNums(f.version,g,f)>=0?1:-0.1)))};return b}},getVersionDelimiter:",",$$getVersion:function(a){return function(g,d,c){var e=a.init(g),f,b,h={};if(e.status<0){return null};f=e.plugin;if(f.getVersionDone!=1){f.getVersion(null,d,c);if(f.getVersionDone===null){f.getVersionDone=1}}a.cleanup();b=(f.version||f.version0);b=b?b.replace(a.splitNumRegx,a.getVersionDelimiter):b;return b}},cleanup:function(){var a=this;if(a.garbage&&a.isDefined(window.CollectGarbage)){window.CollectGarbage()}},addWinEvent:function(d,c){var e=this,a=window,b;if(e.isFunc(c)){if(a.addEventListener){a.addEventListener(d,c,false)}else{if(a.attachEvent){a.attachEvent("on"+d,c)}else{b=a["on"+d];a["on"+d]=e.winHandler(c,b)}}}},winHandler:function(d,c){return function(){d();if(typeof c=="function"){c()}}},WLfuncs0:[],WLfuncs:[],runWLfuncs:function(a){var b={};a.winLoaded=true;a.callArray(a.WLfuncs0);a.callArray(a.WLfuncs);if(a.onDoneEmptyDiv){a.onDoneEmptyDiv()}},winLoaded:false,$$onWindowLoaded:function(a){return function(b){if(a.winLoaded){a.call(b)}else{a.fPush(b,a.WLfuncs)}}},$$onDetectionDone:function(a){return function(h,g,c,b){var d=a.init(h),k,e,j={};if(d.status==-3){return -1}e=d.plugin;if(!a.isArray(e.funcs)){e.funcs=[]}if(e.getVersionDone!=1){k=a.isMinVersion?a.isMinVersion(h,"0",c,b):a.getVersion(h,c,b)}if(e.installed!=-0.5&&e.installed!=0.5){a.call(g);return 1}if(e.NOTF){a.fPush(g,e.funcs);return 0}return 1}},div:null,divID:"plugindetect",divWidth:50,pluginSize:1,emptyDiv:function(){var d=this,b,h,c,a,f,g;if(d.div&&d.div.childNodes){for(b=d.div.childNodes.length-1;b>=0;b--){c=d.div.childNodes[b];if(c&&c.childNodes){for(h=c.childNodes.length-1;h>=0;h--){g=c.childNodes[h];try{c.removeChild(g)}catch(f){}}}if(c){try{d.div.removeChild(c)}catch(f){}}}}if(!d.div){a=document.getElementById(d.divID);if(a){d.div=a}}if(d.div&&d.div.parentNode){try{d.div.parentNode.removeChild(d.div)}catch(f){}d.div=null}},DONEfuncs:[],onDoneEmptyDiv:function(){var c=this,a,b;if(!c.winLoaded){return}if(c.WLfuncs&&c.WLfuncs.length&&c.WLfuncs[c.WLfuncs.length-1]!==null){return}for(a in c){b=c[a];if(b&&b.funcs){if(b.OTF==3){return}if(b.funcs.length&&b.funcs[b.funcs.length-1]!==null){return}}}for(a=0;a=i){return -1}try{if(l==c.pluginSize&&(!c.isIE||c.getDOMobj(m).readyState==4)){if(!m.winLoaded&&c.winLoaded){return 1}if(m.winLoaded&&c.isNum(b)){if(!c.isNum(m.count)){m.count=b}if(b-m.count>=10){return 1}}}}catch(f){}return 0},getDOMobj:function(g,a){var f,d=this,c=g?g.span:0,b=c&&c.firstChild?1:0;try{if(b&&a){d.div.focus()}}catch(f){}return b?c.firstChild:null},setStyle:function(b,g){var f=b.style,a,d,c=this;if(f&&g){for(a=0;ao'+c+"/div>");d=j.getElementById(b)}catch(h){}}g=(j.getElementsByTagName("body")[0]||j.body);if(g){if(g.firstChild&&f.isDefined(g.insertBefore)){g.insertBefore(a,g.firstChild)}else{g.appendChild(a)}if(d){g.removeChild(d)}}else{}},insertHTML:function(g,b,h,a,l){var m,n=document,k=this,q,p=n.createElement("span"),o,j,f="<";var c=["outlineStyle","none","borderStyle","none","padding","0px","margin","0px","visibility","visible"];var i="outline-style:none;border-style:none;padding:0px;margin:0px;visibility:visible;";if(!k.isDefined(a)){a=""}if(k.isString(g)&&(/[^\s]/).test(g)){g=g.toLowerCase().replace(/\s/g,"");q=f+g+' width="'+k.pluginSize+'" height="'+k.pluginSize+'" ';q+='style="'+i+'display:inline;" ';for(o=0;o'}}q+=a+f+"/"+g+">"}else{q=a}if(!k.div){j=n.getElementById(k.divID);if(j){k.div=j}else{k.div=n.createElement("div");k.div.id=k.divID}k.setStyle(k.div,c.concat(["width",k.divWidth+"px","height",(k.pluginSize+3)+"px","fontSize",(k.pluginSize+3)+"px","lineHeight",(k.pluginSize+3)+"px","verticalAlign","baseline","display","block"]));if(!j){k.setStyle(k.div,["position","absolute","right","0px","top","0px"]);k.insertDivInBody(k.div)}}if(k.div&&k.div.parentNode){k.setStyle(p,c.concat(["fontSize",(k.pluginSize+3)+"px","lineHeight",(k.pluginSize+3)+"px","verticalAlign","baseline","display","inline"]));try{p.innerHTML=q}catch(m){};try{k.div.appendChild(p)}catch(m){};return{span:p,winLoaded:k.winLoaded,tagName:g,outerHTML:q}}return{span:null,winLoaded:k.winLoaded,tagName:"",outerHTML:q}},file:{$:1,any:"fileStorageAny999",valid:"fileStorageValid999",save:function(d,f,c){var b=this,e=b.$,a;if(d&&e.isDefined(c)){if(!d[b.any]){d[b.any]=[]}if(!d[b.valid]){d[b.valid]=[]}d[b.any].push(c);a=b.split(f,c);if(a){d[b.valid].push(a)}}},getValidLength:function(a){return a&&a[this.valid]?a[this.valid].length:0},getAnyLength:function(a){return a&&a[this.any]?a[this.any].length:0},getValid:function(c,a){var b=this;return c&&c[b.valid]?b.get(c[b.valid],a):null},getAny:function(c,a){var b=this;return c&&c[b.any]?b.get(c[b.any],a):null},get:function(d,a){var c=d.length-1,b=this.$.isNum(a)?a:c;return(b<0||b>c)?null:d[b]},split:function(g,c){var b=this,e=b.$,f=null,a,d;g=g?g.replace(".","\\."):"";d=new RegExp("^(.*[^\\/])("+g+"\\s*)$");if(e.isString(c)&&d.test(c)){a=(RegExp.$1).split("/");f={name:a[a.length-1],ext:RegExp.$2,full:c};a[a.length-1]="";f.path=a.join("/")}return f},z:0},Plugins:{java:{mimeType:["application/x-java-applet","application/x-java-vm","application/x-java-bean"],classID:"clsid:8AD9C840-044E-11D1-B3E9-00805F499D93",navigator:{a:window.navigator.javaEnabled(),javaEnabled:function(){return this.a},mimeObj:0,pluginObj:0},OTF:null,minIEver:7,debug:0,debugEnable:function(){var a=this,b=a.$;a.debug=1},isDisabled:{$:1,DTK:function(){var a=this,c=a.$,b=a.$$;if((c.isGecko&&c.compareNums(c.verGecko,c.formatNum("1.6"))<=0)||(c.isSafari&&c.OS==1&&(!c.verSafari||c.compareNums(c.verSafari,"5,1,0,0")<0))||c.isChrome||(c.isIE&&!c.ActiveXEnabled)){return 1}return 0},AXO:function(){var a=this,c=a.$,b=a.$$;return(!c.isIE||!c.ActiveXEnabled||(!b.debug&&b.DTK.query().status!==0))},navMime:function(){var b=this,d=b.$,c=b.$$,a=c.navigator;if(d.isIE||!a.mimeObj||!a.pluginObj){return 1}return 0},navPlugin:function(){var b=this,d=b.$,c=b.$$,a=c.navigator;if(d.isIE||!a.mimeObj||!a.pluginObj){return 1}return 0},windowDotJava:function(){var a=this,c=a.$,b=a.$$;if(!window.java){return 1}if(c.OS==2&&c.verOpera&&c.verOpera<9.2&&c.verOpera>=9){return 1}if(c.verGecko&&c.compareNums(c.verGecko,"1,9,0,0")<0&&c.compareNums(c.verGecko,"1,8,0,0")>=0){return 1}return 0},allApplets:function(){var b=this,d=b.$,c=b.$$,a=c.navigator;if(d.OS>=20){return 0}if(d.verOpera&&d.verOpera<11&&!a.javaEnabled()&&!c.lang.System.getProperty()[0]){return 1}if((d.verGecko&&d.compareNums(d.verGecko,d.formatNum("2"))<0)&&!a.mimeObj&&!c.lang.System.getProperty()[0]){return 1}return 0},AppletTag:function(){var b=this,d=b.$,c=b.$$,a=c.navigator;return d.isIE?!a.javaEnabled():0},ObjectTag:function(){var a=this,c=a.$,b=a.$$;return c.isIE?!c.ActiveXEnabled:0},z:0},getVerifyTagsDefault:function(){var a=this,c=a.$,b=[1,0,1];if(c.OS>=20){return b}if((c.isIE&&(c.verIE<9||!c.ActiveXEnabled))||(c.verGecko&&c.compareNums(c.verGecko,c.formatNum("2"))<0)||(c.isSafari&&(!c.verSafari||c.compareNums(c.verSafari,c.formatNum("4"))<0))||(c.verOpera&&c.verOpera<10)){b=[1,1,1]}return b},getVersion:function(j,g,i){var b=this,d=b.$,e,a=b.applet,h=b.verify,k=b.navigator,f=null,l=null,c=null;if(b.getVersionDone===null){b.OTF=0;k.mimeObj=d.hasMimeType(b.mimeType);if(k.mimeObj){k.pluginObj=k.mimeObj.enabledPlugin}if(h){h.begin()}}a.setVerifyTagsArray(i);d.file.save(b,".jar",g);if(b.getVersionDone===0){if(a.should_Insert_Query_Any()){e=a.insert_Query_Any();b.setPluginStatus(e[0],e[1],f)}return}if((!f||b.debug)&&b.DTK.query().version){f=b.DTK.version}if((!f||b.debug)&&b.navMime.query().version){f=b.navMime.version}if((!f||b.debug)&&b.navPlugin.query().version){f=b.navPlugin.version}if((!f||b.debug)&&b.AXO.query().version){f=b.AXO.version}if(b.nonAppletDetectionOk(f)){c=f}if(!c||b.debug||a.VerifyTagsHas(2.2)||a.VerifyTagsHas(2.5)){e=b.lang.System.getProperty();if(e[0]){f=e[0];c=e[0];l=e[1]}}b.setPluginStatus(c,l,f);if(a.should_Insert_Query_Any()){e=a.insert_Query_Any();if(e[0]){c=e[0];l=e[1]}}b.setPluginStatus(c,l,f)},nonAppletDetectionOk:function(b){var d=this,e=d.$,a=d.navigator,c=1;if(!b||(!a.javaEnabled()&&!d.lang.System.getPropertyHas(b))||(!e.isIE&&!a.mimeObj&&!d.lang.System.getPropertyHas(b))||(e.isIE&&!e.ActiveXEnabled)){c=0}else{if(e.OS>=20){}else{if(d.info&&d.info.getPlugin2Status()<0&&d.info.BrowserRequiresPlugin2()){c=0}}}return c},setPluginStatus:function(d,f,a){var c=this,e=c.$,b;a=a||c.version0;if(c.OTF>0){d=d||c.lang.System.getProperty()[0]}if(c.OTF<3){b=d?1:(a?-0.2:-1);if(c.installed===null||b>c.installed){c.installed=b}}if(c.OTF==2&&c.NOTF&&!c.applet.getResult()[0]&&!c.lang.System.getProperty()[0]){c.installed=a?-0.2:-1};if(c.OTF==3&&c.installed!=-0.5&&c.installed!=0.5){c.installed=(c.NOTF.isJavaActive(1)==1||c.lang.System.getProperty()[0])?0.5:-0.5}if(c.OTF==4&&(c.installed==-0.5||c.installed==0.5)){if(d){c.installed=1}else{if(c.NOTF.isJavaActive(1)==1){if(a){c.installed=1;d=a}else{c.installed=0}}else{if(a){c.installed=-0.2}else{c.installed=-1}}}};if(a){c.version0=e.formatNum(e.getNum(a))}if(d){c.version=e.formatNum(e.getNum(d))}if(f&&e.isString(f)){c.vendor=f}if(!c.vendor){c.vendor=""}if(c.verify&&c.verify.isEnabled()){c.getVersionDone=0}else{if(c.getVersionDone!=1){if(c.OTF<2){c.getVersionDone=0}else{c.getVersionDone=c.applet.can_Insert_Query_Any()?0:1}}}},DTK:{$:1,hasRun:0,status:null,VERSIONS:[],version:"",HTML:null,Plugin2Status:null,classID:["clsid:CAFEEFAC-DEC7-0000-0001-ABCDEFFEDCBA","clsid:CAFEEFAC-DEC7-0000-0000-ABCDEFFEDCBA"],mimeType:["application/java-deployment-toolkit","application/npruntime-scriptable-plugin;DeploymentToolkit"],disabled:function(){return this.$$.isDisabled.DTK()},query:function(){var k=this,g=k.$,d=k.$$,j,l,h,m={},f={},a,c=null,i=null,b=(k.hasRun||k.disabled());k.hasRun=1;if(b){return k}k.status=0;if(g.isIE&&g.verIE>=6){for(l=0;l0?1:-1;for(l=0;l=b.minIEver){i=a.search(j,j,d);if(i.length>0&&d){i=a.search(k,k,d)}}else{if(d){i=a.search(f,f,true)}if(i.length==0){i=a.search(h,g,false)}}if(i.length){a.version=i[0];a.VERSIONS=[].concat(i)};return a},search:function(a,j,p){var h,d,f=this,e=f.$,k=f.$$,n,c,l,q,b,o,r,i=[];if(e.compareNums(a.join(","),j.join(","))>0){j=a}j=e.formatNum(j.join(","));var m,s="1,4,2,0",g="JavaPlugin."+a[0]+""+a[1]+""+a[2]+""+(a[3]>0?("_"+(a[3]<10?"0":"")+a[3]):"");for(h=0;h=0;l--){r="JavaWebStart.isInstalled."+b+l+".0";if(e.compareNums(d[0]+","+d[1]+","+l+",0",j)>=0&&!e.getAXO(r)){continue}m=e.compareNums(d[0]+","+d[1]+","+l+",0",s)<0?true:false;for(q=d[3];q>=0;q--){c=l+"_"+(q<10?"0"+q:q);o=n+c;if(e.getAXO(o)&&(m||e.getAXO(r))){i.push(b+c);if(!p){return i}}if(o==g){return i}}if(e.getAXO(n+l)&&(m||e.getAXO(r))){i.push(b+l);if(!p){return i}}if(n+l==g){return i}}}return i}},navMime:{$:1,hasRun:0,mimetype:"",version:"",length:0,mimeObj:0,pluginObj:0,disabled:function(){return this.$$.isDisabled.navMime()},query:function(){var i=this,f=i.$,a=i.$$,b=(i.hasRun||i.disabled());i.hasRun=1;if(b){return i};var n=/^\s*application\/x-java-applet;jpi-version\s*=\s*(\d.*)$/i,g,l,j,d="",h="a",o,m,k={},c=f.formatNum("0");for(l=0;l0){c=g}}}g=k[h+c];if(g){o=f.hasMimeType(g);i.mimeObj=o;i.pluginObj=o?o.enabledPlugin:0;i.mimetype=g;i.version=c};return i}},navPlugin:{$:1,hasRun:0,version:"",disabled:function(){return this.$$.isDisabled.navPlugin()},query:function(){var m=this,e=m.$,c=m.$$,h=c.navigator,j,l,k,g,d,a,i,f=0,b=(m.hasRun||m.disabled());m.hasRun=1;if(b){return m};a=h.pluginObj.name||"";i=h.pluginObj.description||"";if(!f||c.debug){g=/Java.*TM.*Platform[^\d]*(\d+)(?:[\.,_](\d*))?(?:\s*[Update]+\s*(\d*))?/i;if((g.test(a)||g.test(i))&&parseInt(RegExp.$1,10)>=5){f="1,"+RegExp.$1+","+(RegExp.$2?RegExp.$2:"0")+","+(RegExp.$3?RegExp.$3:"0")}}if(!f||c.debug){g=/Java[^\d]*Plug-in/i;l=g.test(i)?e.formatNum(e.getNum(i)):0;k=g.test(a)?e.formatNum(e.getNum(a)):0;if(l&&(e.compareNums(l,e.formatNum("1,3"))<0||e.compareNums(l,e.formatNum("2"))>=0)){l=0}if(k&&(e.compareNums(k,e.formatNum("1,3"))<0||e.compareNums(k,e.formatNum("2"))>=0)){k=0}d=l&&k?(e.compareNums(l,k)>0?l:k):(l||k);if(d){f=d}}if(!f&&e.isSafari&&e.OS==2){j=e.findNavPlugin("Java.*\\d.*Plug-in.*Cocoa",0);if(j){l=e.getNum(j.description);if(l){f=l}}};if(f){m.version=e.formatNum(f)};return m}},lang:{$:1,System:{$:1,hasRun:0,result:[null,null],disabled:function(){return this.$$.isDisabled.windowDotJava()},getPropertyHas:function(a){var b=this,d=b.$,c=b.getProperty()[0];return(a&&c&&d.compareNums(d.formatNum(a),d.formatNum(c))===0)?1:0},getProperty:function(){var f=this,g=f.$,d=f.$$,i,h={},b=(f.hasRun||f.disabled());f.hasRun=1;if(!b){var a="java_qqq990";g[a]=null;try{var c=document.createElement("script");c.type="text/javascript";c.appendChild(document.createTextNode("(function(){var e;try{if (window.java && window.java.lang && window.java.lang.System){"+g.name+"."+a+'=[window.java.lang.System.getProperty("java.version")+" ",window.java.lang.System.getProperty("java.vendor")+" "]}}catch(e){}})();'));if(g.head.firstChild){g.head.insertBefore(c,g.head.firstChild)}else{g.head.appendChild(c)}g.head.removeChild(c)}catch(i){}if(g[a]&&g.isArray(g[a])){f.result=[].concat(g[a])}}return f.result}}},applet:{$:1,results:[[null,null],[null,null],[null,null]],getResult:function(){var c=this.results,a,b=[];for(a=0;a3){c[a]=3}b.allowed[a]=c[a]}}}},setVerifyTagsArray:function(d){var b=this,c=b.$,a=b.$$;if(a.getVersionDone===null){b.saveAsVerifyTagsArray(a.getVerifyTagsDefault())}if(a.debug||(a.verify&&a.verify.isEnabled())){b.saveAsVerifyTagsArray([3,3,3])}else{if(d){b.saveAsVerifyTagsArray(d)}}},allDisabled:function(){return this.$$.isDisabled.allApplets()},isDisabled:function(d){var b=this,c=b.$,a=b.$$;if(d==2&&!c.isIE){return 1}if(d===0||d==2){return a.isDisabled.ObjectTag()}if(d==1){return a.isDisabled.AppletTag()}},can_Insert_Query:function(b){var a=this;if(a.HTML[b]){return 0}return !a.isDisabled(b)},can_Insert_Query_Any:function(){var b=this,a;for(a=0;a=2||(b.allowed[a]==1&&!b.getResult()[0]))&&e.isAppletActive(a)>=0){return 1}}return 0},isJavaActive:function(d){var f=this,c=f.$$,a,b,e=-9;for(a=0;ae){e=b}}return e},isAppletActive:function(c,a){var d=this,b=d.$$.applet.active;if(!a){b[c]=d.isAppletActive_(c)}return b[c]},isAppletActive_:function(d){var g=this,f=g.$,b=g.$$,l=b.navigator,a=b.applet,h=a.HTML[d],i,k,c=0,j=f.getTagStatus(h,a.DummySpanTagHTML,a.DummyObjTagHTML,g.count);if(j==-2){return -2}try{if(f.isIE&&f.verIE>=b.minIEver&&f.getDOMobj(h).object){return 1}}catch(i){}for(k=0;k0){c=1}}if(j==1&&(f.isIE||((b.version0&&l.javaEnabled()&&l.mimeObj&&(h.tagName=="object"||c))||b.lang.System.getProperty()[0]))){return 1}if(j<0){return -1}return 0},winOnLoadQuery:function(c,d){var b=d.$$,a;if(b.OTF==3){a=d.queryAllApplets();d.queryCompleted(a[1],a[2])}},$$onIntervalQuery:function(d){var c=d.$,b=d.$$,a;if(b.OTF==3){a=d.queryAllApplets();if(!d.shouldContinueQuery()||(c.winLoaded&&d.count>d.countMax)){d.queryCompleted(a[1],a[2])}}d.count++;if(b.OTF==3){setTimeout(d.onIntervalQuery,d.intervalLength)}},queryAllApplets:function(){var g=this,f=g.$,e=g.$$,d=e.applet,b,a,c;for(b=0;b=4){return}b.OTF=4;var a=e.isJavaActive();b.setPluginStatus(c,f,0);if(b.funcs){d.callArray(b.funcs)}if(d.onDoneEmptyDiv){d.onDoneEmptyDiv()}}},zz:0},zz:0}};PluginDetect.initScript(); \ No newline at end of file diff --git a/server/src/uds/static/js/jquery-1.7.1.js b/server/src/uds/static/js/jquery-1.7.1.js new file mode 100644 index 000000000..8ccd0ea78 --- /dev/null +++ b/server/src/uds/static/js/jquery-1.7.1.js @@ -0,0 +1,9266 @@ +/*! + * jQuery JavaScript Library v1.7.1 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Mon Nov 21 21:11:03 2011 -0500 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7.1", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!memory; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + return this; + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + marginDiv, + fragment, + tds, + events, + eventName, + i, + isSupported, + div = document.createElement( "div" ), + documentElement = document.documentElement; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + fragment.removeChild( div ); + + // Null elements to avoid leaks in IE + fragment = select = opt = marginDiv = div = input = null; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + conMarginTop, ptlm, vb, style, html, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + conMarginTop = 1; + ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;"; + vb = "visibility:hidden;border:0;"; + style = "style='" + ptlm + "border:5px solid #000;padding:0;'"; + html = "

" + + "" + + "
"; + + container = document.createElement("div"); + container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Figure out if the W3C box model works as expected + div.innerHTML = ""; + div.style.width = div.style.paddingLeft = "1px"; + jQuery.boxModel = support.boxModel = div.offsetWidth === 2; + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.style.cssText = ptlm + vb; + div.innerHTML = html; + + outer = div.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + body.removeChild( container ); + div = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + return support; +})(); + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = ++jQuery.uuid; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ internalKey ] : internalKey; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ internalKey ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + } else { + elem[ internalKey ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, attr, name, + data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { + attr = this[0].attributes; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + jQuery._data( this[0], "parsedAttrs", true ); + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var self = jQuery( this ), + args = [ parts[0], value ]; + + self.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, + i = 0; + + if ( value && elem.nodeType === 1 ) { + attrNames = value.toLowerCase().split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + + // See #9699 for explanation of this approach (setting first, then removal) + jQuery.attr( elem, name, "" ); + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /\bhover(\.\S+)?\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + var attrs = elem.attributes || {}; + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || (attrs.id || {}).value === m[2]) && + (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + quick: quickParse( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, origType, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + old = null; + for ( ; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + handlerQueue = [], + i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Determine handlers that should run if there are delegated events + // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { + + // Pregenerate a single jQuery object for reuse with .is() + jqcur = jQuery(this); + jqcur.context = this.ownerDocument || this; + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + ret; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on.call( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + {% block script %}{% endblock %} + + + + + +
+ {% block content %} + Contenido + {% endblock %} +
+ + + + + \ No newline at end of file diff --git a/server/src/uds/templates/uds/detectJava.html b/server/src/uds/templates/uds/detectJava.html new file mode 100644 index 000000000..eb52b774a --- /dev/null +++ b/server/src/uds/templates/uds/detectJava.html @@ -0,0 +1,40 @@ +{% extends "uds/base.html" %} +{% load i18n %} +{% load static %} + +{% block title %} +{% trans "Login redirection" %} +{% endblock %} + +{% block script %} + + +{% endblock %} + +{% block top %} + + {% include "uds/snippets/lang.html" %} +{% endblock %} + +{% block content %} + + +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/downloads.html b/server/src/uds/templates/uds/downloads.html new file mode 100644 index 000000000..a48a858ac --- /dev/null +++ b/server/src/uds/templates/uds/downloads.html @@ -0,0 +1,25 @@ +{% extends "uds/internal_page.html" %} +{% load i18n %} +{% load static %} + +{% block content %} +{% spaceless %} +

+{% trans "Downloads" %} +

+
+

{% trans "This page contains a list of downloadables provided by different modules" %}

+
+
+ +
+{% endspaceless %} +{% include "uds/snippets/back_to_list.html" %} +{% endblock %} + diff --git a/server/src/uds/templates/uds/error.html b/server/src/uds/templates/uds/error.html new file mode 100644 index 000000000..a02a6fafb --- /dev/null +++ b/server/src/uds/templates/uds/error.html @@ -0,0 +1,16 @@ +{% extends "uds/internal_page.html" %} +{% load i18n %} +{% load static %} + +{% block title %} +{% trans "Error" %} +{% endblock %} + +{% block content %} +
+{% trans "Error" %}: {{ errorString }} +
+
+
+{% include "uds/snippets/back_to_list.html" %} +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/index.html b/server/src/uds/templates/uds/index.html new file mode 100644 index 000000000..6a428e630 --- /dev/null +++ b/server/src/uds/templates/uds/index.html @@ -0,0 +1,85 @@ +{% extends "uds/internal_page.html" %} +{% load i18n %} +{% load static %} +{% block script %} +{{ block.super }} + +{% endblock script %} + +{% block content %} +
+

{% trans "Services" %}

+
    + {% for ser in services %} + {% if ser.transports %} +
  • + {% with trans=ser.transports|first %} + {{ ser.name }} + {% endwith %} +
      + {% for trans in ser.transports %} +
    • {{ trans.name }}{{ trans.name }}
    • + {% endfor %} +
    +
  • + {% endif %} + {% endfor %} +
+
+{% if not java %} +
+

{% trans "Java is not available on your browser, and the selected transport needs it." %}

+

{% trans "Please, install latest version from" %} {% trans "Java website" %} {% trans "and restart browser" %}

+
+{% endif %} + +{% if user.isStaff %} +
+
{% trans "Ip" %}: {{ ip }} +
{% trans "Networks" %}: {{ nets }}
+
{% trans "Transports" %}: {{ transports }}
+
+ +
+{% endif %} +{% endblock content %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/internal_page.html b/server/src/uds/templates/uds/internal_page.html new file mode 100644 index 000000000..2442219a5 --- /dev/null +++ b/server/src/uds/templates/uds/internal_page.html @@ -0,0 +1,44 @@ +{% extends "uds/base.html" %} +{% load i18n %} +{% load static %} +{% block script %} + +{% endblock script %} +{% block top %} + +
+ {% trans "User" %} {{ user.real_name }} +
+ {% if user.staff_member or user.is_admin %} + {% include "uds/snippets/admin_user.html" %} + {% endif %} + + {% if not nolang %} + {% include "uds/snippets/lang.html" %} + {% endif %} +
+ {% trans "Log out" %} +
+ + +{% endblock top %} diff --git a/server/src/uds/templates/uds/login.html b/server/src/uds/templates/uds/login.html new file mode 100644 index 000000000..5d65d855a --- /dev/null +++ b/server/src/uds/templates/uds/login.html @@ -0,0 +1,98 @@ +{% extends "uds/base.html" %} +{% load i18n %} +{% load static %} + +{% block title %} +{% trans "Login to UDS" %} +{% endblock %} + +{% block script %} + + +{% endblock %} + +{% block top %} + + {% include "uds/snippets/lang.html" %} +{% endblock %} + +{% block content %} + +
+

{% trans "Login" %}

+
+
+ {% csrf_token %} +
+ {% trans "Login data" %} + {{ form.as_p }} +
+ +
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/prefs.html b/server/src/uds/templates/uds/prefs.html new file mode 100644 index 000000000..930d84cca --- /dev/null +++ b/server/src/uds/templates/uds/prefs.html @@ -0,0 +1,21 @@ +{% extends "uds/internal_page.html" %} +{% load i18n %} +{% load static %} + +{% block title %} +{% trans "UDS User Preferences" %} +{% endblock %} + +{% block content %} +
+ +

{% trans "Preferences" %}

+
+ {% csrf_token %} + {% autoescape off %}{{ prefs_form }}{% endautoescape %} + +
+ {% include "uds/snippets/back_to_list.html" %} + +
+{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/service_not_ready.html b/server/src/uds/templates/uds/service_not_ready.html new file mode 100644 index 000000000..95ef2ec5c --- /dev/null +++ b/server/src/uds/templates/uds/service_not_ready.html @@ -0,0 +1,8 @@ +{% extends "uds/internal_page.html" %} +{% load i18n %} +{% load static %} + +{% block content %} +{% trans "Service not ready at this moment. Please, try again in a while." %} +{% include "uds/snippets/back_to_list.html" %} +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/show_transport.html b/server/src/uds/templates/uds/show_transport.html new file mode 100644 index 000000000..58e35f527 --- /dev/null +++ b/server/src/uds/templates/uds/show_transport.html @@ -0,0 +1,13 @@ +{% extends "uds/internal_page.html" %} +{% load i18n %} +{% load static %} + +{% block content %} +
+{% autoescape off %} +{{ transport }} +{% endautoescape %} +
+
+{% include "uds/snippets/back_to_list.html" %} +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/snippets/admin_user.html b/server/src/uds/templates/uds/snippets/admin_user.html new file mode 100644 index 000000000..04acc0b7f --- /dev/null +++ b/server/src/uds/templates/uds/snippets/admin_user.html @@ -0,0 +1,10 @@ +{% load i18n static %} +{% spaceless %} +
+ {% trans "Admin" %} + +
+{% endspaceless %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/snippets/back_to_list.html b/server/src/uds/templates/uds/snippets/back_to_list.html new file mode 100644 index 000000000..38aeb36b7 --- /dev/null +++ b/server/src/uds/templates/uds/snippets/back_to_list.html @@ -0,0 +1,4 @@ +{% load i18n static %} + \ No newline at end of file diff --git a/server/src/uds/templates/uds/snippets/lang.html b/server/src/uds/templates/uds/snippets/lang.html new file mode 100644 index 000000000..705232d59 --- /dev/null +++ b/server/src/uds/templates/uds/snippets/lang.html @@ -0,0 +1,21 @@ +{% load i18n static %} + +{% get_current_language as LANGUAGE_CODE %} +{% get_language_info for LANGUAGE_CODE as lang %} +{% spaceless %} +
+
+{% csrf_token %} +{% trans "Language" %}: + +{% get_language_info_list for LANGUAGES as languages %} +{% for language in languages %} +{% if language.code != lang.code %} + + {{ language.name_local }} + +{% endif %} +{% endfor %} +
+
+{% endspaceless %} \ No newline at end of file diff --git a/server/src/uds/tests.py b/server/src/uds/tests.py new file mode 100644 index 000000000..f77f651a2 --- /dev/null +++ b/server/src/uds/tests.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase +from uds.xmlrpc.util.TestTransport import rpcServer +from uds.xmlrpc.ServiceProviders import * +from models import * + +class XmlRpcTest(TestCase): + def setUp(self): + #self.Provider1 = ServiceProviders.objects.create(name="Dummy 1", type="DummyProvider") + #self.Provider2 = ServiceProviders.objects.create(name="Dummy 2", type="DummyProvider") + return + + def testProviders(self): + """ + Test the services providers api + """ + credentials = rpcServer.login('','','') + credentials = credentials['credentials'] + #rpcServer.createServiceProvider('prueba', '', r['type'], result ) + data = [ { 'name' : 'host' ,'value' : '192.168.0.15' }, + { 'name' : 'port' ,'value' : '443' }, + { 'name' : 'username' ,'value' : 'admin' }, + { 'name' : 'password' ,'value' : 'temporal' }, + ] + rpcServer.testServiceProvider(credentials, 'VmwareVCServiceProvider', data) + + \ No newline at end of file diff --git a/server/src/uds/transports/NX/NXTransport.py b/server/src/uds/transports/NX/NXTransport.py new file mode 100644 index 000000000..ddbd27315 --- /dev/null +++ b/server/src/uds/transports/NX/NXTransport.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' +Created on Jul 29, 2011 + +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from django.utils.translation import ugettext_noop as _ +from uds.core.managers.UserPrefsManager import CommonPrefs +from uds.core.ui.UserInterface import gui +from uds.core.transports.BaseTransport import BaseTransport +from uds.core.util import connection +from web import generateHtmlForNX, getHtmlComponent + +import logging + +logger = logging.getLogger(__name__) + +READY_CACHE_TIMEOUT = 30 + +class NXTransport(BaseTransport): + ''' + Provides access via RDP to service. + This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password + ''' + typeName = _('NX Transport (direct)') + typeType = 'NXTransport' + typeDescription = _('NX Transport for direct connection') + iconFile = 'nx.png' + needsJava = True # If this transport needs java for rendering + + useEmptyCreds = gui.CheckBoxField(label = _('Empty creds'), order = 1, tooltip = _('If checked, the credentials used to connect will be emtpy')) + fixedName = gui.TextField(label=_('Username'), order = 2, tooltip = _('If not empty, this username will be always used as credential')) + fixedPassword = gui.PasswordField(label=_('Password'), order = 3, tooltip = _('If not empty, this password will be always used as credential')) + listenPort = gui.NumericField(label=_('Listen port'), length = 5, order = 4, tooltip = _('Listening port of NX (ssh) at client machine'), defvalue = '22') + connection = gui.ChoiceField(label=_('Connection'), order = 6, tooltip = _('Connection speed for this transport (quality)'), values = [ + {'id' : 'modem', 'text' : 'modem'}, + {'id' : 'isdn', 'text' : 'isdn'}, + {'id' : 'adsl', 'text' : 'adsl'}, + {'id' : 'wan', 'text' : 'wan'}, + {'id' : 'lan', 'text' : 'lan'}, + ] ) + session = gui.ChoiceField(label=_('Session'), order = 7, tooltip = _('Desktop session'), values = [ + {'id' : 'gnome', 'text' : 'gnome'}, + {'id' : 'kde', 'text' : 'kde'}, + {'id' : 'cde', 'text' : 'cde'}, + ] ) + cacheDisk = gui.ChoiceField(label=_('Disk Cache'), order = 8, tooltip = _('Cache size en Mb stored at disk'), values = [ + {'id' : '0', 'text' : '0 Mb'}, + {'id' : '32', 'text' : '32 Mb'}, + {'id' : '64', 'text' : '64 Mb'}, + {'id' : '128', 'text' : '128 Mb'}, + {'id' : '256', 'text' : '256 Mb'}, + {'id' : '512', 'text' : '512 Mb'}, + ] ) + cacheMem = gui.ChoiceField(label=_('Memory Cache'), order = 9, tooltip = _('Cache size en Mb keept at memory'), values = [ + {'id' : '4', 'text' : '4 Mb'}, + {'id' : '8', 'text' : '8 Mb'}, + {'id' : '16', 'text' : '16 Mb'}, + {'id' : '32', 'text' : '32 Mb'}, + {'id' : '64', 'text' : '64 Mb'}, + {'id' : '128', 'text' : '128 Mb'}, + ] ) + + + def __init__(self, environment, values = None): + super(NXTransport, self).__init__(environment, values) + if values != None: + self._useEmptyCreds = gui.strToBool(values['useEmptyCreds']) + self._fixedName = values['fixedName'] + self._fixedPassword = values['fixedPassword'] + self._listenPort = values['listenPort'] + self._connection = values['connection'] + self._session = values['session'] + self._cacheDisk = values['cacheDisk'] + self._cacheMem = values['cacheMem'] + else: + self._useEmptyCreds = '' + self._fixedName = '' + self._fixedPassword = '' + self._listenPort = '' + self._connection = '' + self._session = '' + self._cacheDisk = '' + self._cacheMem = '' + + def marshal(self): + ''' + Serializes the transport data so we can store it in database + ''' + return str.join( '\t', [ 'v1', gui.boolToStr(self._useEmptyCreds), self._fixedName, self._fixedPassword, self._listenPort, + self._connection, self._session, self._cacheDisk, self._cacheMem ] ) + + def unmarshal(self, string): + data = string.split('\t') + if data[0] == 'v1': + self._useEmptyCreds = gui.strToBool(data[1]) + self._fixedName, self._fixedPassword, self._listenPort, self._connection, self._session, self._cacheDisk, self._cacheMem = data[2:] + + + def valuesDict(self): + return { 'useEmptyCreds' : gui.boolToStr(self._useEmptyCreds), 'fixedName' : self._fixedName, + 'fixedPassword' : self._fixedPassword, 'listenPort': self._listenPort, + 'connection' : self._connection, 'session' : self._session, 'cacheDisk' : self._cacheDisk, + 'cacheMem' : self._cacheMem } + + def isAvailableFor(self, ip): + ''' + Checks if the transport is available for the requested destination ip + Override this in yours transports + ''' + logger.debug('Checking availability for {0}'.format(ip)) + ready = self.cache().get(ip) + if ready is None: + # Check again for readyness + if connection.testServer(ip, self._listenPort) == True: + self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT) + return True + else: + self.cache().put(ip, 'N', READY_CACHE_TIMEOUT) + return ready == 'Y' + + def renderForHtml(self, theId, ip, os, user, password): + + prefs = user.prefs('nx') + + username = user.getUsernameForAuth() + proc = username.split('@') + username = proc[0] + if self._fixedName is not '': + username = self._fixedName + if self._fixedPassword is not '': + password = self._fixedPassword + if self._useEmptyCreds is True: + username, password = '','' + + width, height = CommonPrefs.getWidthHeight(prefs) + + # Extra data + extra = { 'width': width, 'height' : height, + 'port' : self._listenPort, 'connection' : self._connection, + 'session' : self._session, 'cacheDisk': self._cacheDisk, + 'cacheMem' : self._cacheMem } + + + return generateHtmlForNX(self, theId, ip, username, password, extra) + + def getHtmlComponent(self, theId, os, componentId): + # We use helper to keep this clean + return getHtmlComponent(self.__module__, componentId) + \ No newline at end of file diff --git a/server/src/uds/transports/NX/__init__.py b/server/src/uds/transports/NX/__init__.py new file mode 100644 index 000000000..ce28dde61 --- /dev/null +++ b/server/src/uds/transports/NX/__init__.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.transports.TransportsFactory import TransportsFactory +from uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs +from uds.core.managers.DownloadsManager import DownloadsManager +from NXTransport import NXTransport +from django.utils.translation import ugettext_noop as _ +import os.path, sys + +TransportsFactory.factory().insert(NXTransport) +UserPrefsManager.manager().registerPrefs('nx', _('NX Protocol'), + [ + CommonPrefs.screenSizePref + ]) + +DownloadsManager.manager().registerDownloadable('udsactor-nx_1.0_all.deb', + _('UDS Actor connector for NX (requires nomachine packages)'), + os.path.dirname(sys.modules[__package__].__file__) + '/files/udsactor-nx_1.0_all.deb', + 'application/x-debian-package') diff --git a/server/src/uds/transports/NX/applet/nxtransport.jar b/server/src/uds/transports/NX/applet/nxtransport.jar new file mode 100644 index 0000000000000000000000000000000000000000..c859f30cdcaefc2451c74939737fae6e389423d0 GIT binary patch literal 24418 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/transports/NX/files/udsactor-nx_1.0_all.deb b/server/src/uds/transports/NX/files/udsactor-nx_1.0_all.deb new file mode 100644 index 0000000000000000000000000000000000000000..96b3f9d6cef4d04c3b0cbcee73b7b03a6a265482 GIT binary patch literal 2584 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 literal 0 HcmV?d00001 diff --git a/server/src/uds/transports/NX/nx.png b/server/src/uds/transports/NX/nx.png new file mode 100644 index 0000000000000000000000000000000000000000..aec556e8c448a7fe35b74e24a201cdab56329ade GIT binary patch literal 1027 zcmV+e1pNDnP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf1DHufK~y+TZInw> z+f^9G`2^b0iZFwSmDY4RR2QxsH)tgUC{PkeFXE`5!iBofMMn#^UKTpk$|QjRr5#r= zbt;t%Q+p9wDCO2%APFfT41|z`i}3R~iLOXy-kkTG|2fZj-ph0Hw$X1}|BFiynS)uG z|4DY73qc?NAv4ZuEvoUU<}sQ4kXh5WdHJiWuqm7!$dhT3X1w(Ry)`H4b=dJXHR85A z=#bxSZ^&4uqY010iMR0_-HtPO>a^b2$ko#hE}yDp_}lMcJq5{4@=`lV*h=uT%?bkp zFfEo5-Ru{GCw?(8tc{7)TsUinp@|}-_22{y-{HrKDp(NP^fq~Wh;llC+h!ApHZCk? z4<&SKVj~L}oDzuEX5RfvfZ|>s{VV;T|7+7D|fXv(K3;dxN_No`Fw4fr=9h6%@ezhp;5jnretD znhfZ!3bm1>y`lmp6jFa59*c!|hd{#-h6{DDyc`}+Fn0ERx?e4U(Fbt8h5nZg!vmdJ zT&3Nr^gUHqq&5U<$}gtvl?L#xYaFW-PxiKziq9aJd&=es`8(=MKiAJWX??vcE8CV! zv>ks_jjxpLYpJcYs=q;JwfZ!m&CAQGx>S?YWpN3>sLE)2O-;Ta-G|jSYb)@#5sA{3 zisxwlUFB4TYP0?)(@;?THlLMg(3n7Hls8pBBULl^8{bG+V=kFy`c!%yZEc~ap`IQ+ zBJR36yv`=vj(V;(+39iG@#s-;JDs>rpW%x3bm@DSqapW$vS&cI6zYn?j)<9g$_yLx xV1$+9;QxBpQy7u9BZ?xbbj->RDyke$-@h{nKe)EJd{6)Y002ovPDHLkV1ksm<^=!% literal 0 HcmV?d00001 diff --git a/server/src/uds/transports/NX/web.py b/server/src/uds/transports/NX/web.py new file mode 100644 index 000000000..fc7c7f22c --- /dev/null +++ b/server/src/uds/transports/NX/web.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext as _ +from django.core.urlresolvers import reverse +import logging, os, sys + +logger = logging.getLogger(__name__) + + +def simpleScrambler(data): + ''' + Simple scrambler so password are not seen at source page + ''' + res = [] + n = ord('M') + pos = 0 + for c in data: + res.append( chr(ord(c) ^ n) ) + n = n ^ pos + pos = pos + 1 + return "".join(res).encode('hex') + + + +def generateHtmlForNX(transport, id, ip, user, password, extra): + applet = reverse('uds.web.views.transcomp', kwargs = { 'idTransport' : id, 'componentId' : '1' }) + # Gets the codebase, simply remove last char from applet + codebase = applet[:-1] + # We generate the "data" parameter + data = simpleScrambler( '\t'.join([ + 'user:' + user, + 'pass:' + password, + 'ip:' + ip, + 'port:' + extra['port'], + 'session:' + extra['session'], + 'connection:' + extra['connection'], + 'cacheDisk:' + extra['cacheDisk'], + 'cacheMem:' + extra['cacheMem'], + 'width:' + str(extra['width']), + 'height:' + str(extra['height']) + ])) + res = '

' % (codebase, '1', data ) + res += '

' + _('In order to use this transport, you need to install first Nomachine Nx Client version 3.5.x') + '

' + res += '

' + _('you can obtain it for your platform from') + '' + _('nochamine web site') + '

' + return res + + +def getHtmlComponent(module, componentId): + dict = { '1' : ['nxtransport.jar', 'application/java-archive' ]} + + if dict.has_key(componentId) == False: + return ['text/plain', 'no component'] + fname = os.path.dirname(sys.modules[module].__file__) + '/applet/' + dict[componentId][0] + logger.debug('Loading component {0} from {1}'.format(componentId, fname)) + + f = open(fname, 'rb') + data = f.read() + f.close() + return [ dict[componentId][1], data ] \ No newline at end of file diff --git a/server/src/uds/transports/RDP/RDPTransport.py b/server/src/uds/transports/RDP/RDPTransport.py new file mode 100644 index 000000000..e8f0ffa45 --- /dev/null +++ b/server/src/uds/transports/RDP/RDPTransport.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.managers.UserPrefsManager import CommonPrefs +from uds.core.ui.UserInterface import gui +from uds.core.transports.BaseTransport import BaseTransport +from uds.core.util import connection +from web import generateHtmlForRdp, getHtmlComponent + +import logging + +logger = logging.getLogger(__name__) + +READY_CACHE_TIMEOUT = 30 + +class RDPTransport(BaseTransport): + ''' + Provides access via RDP to service. + This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password + ''' + typeName = _('RDP Transport (direct)') + typeType = 'RDPTransport' + typeDescription = _('RDP Transport for direct connection') + iconFile = 'rdp.png' + needsJava = True # If this transport needs java for rendering + + useEmptyCreds = gui.CheckBoxField(label = _('Empty creds'), order = 1, tooltip = _('If checked, the credentials used to connect will be emtpy')) + fixedName = gui.TextField(label=_('Username'), order = 2, tooltip = _('If not empty, this username will be always used as credential')) + fixedPassword = gui.PasswordField(label=_('Password'), order = 3, tooltip = _('If not empty, this password will be always used as credential')) + fixedDomain = gui.TextField(label=_('Domain'), order = 4, tooltip = _('If not empty, this domain will be always used as credential (used as DOMAIN\\user)')) + allowSmartcards = gui.CheckBoxField(label = _('Allow Smartcards'), order = 5, tooltip = _('If checked, this transport will allow the use of smartcards')) + allowPrinters = gui.CheckBoxField(label = _('Allow Printers'), order = 6, tooltip = _('If checked, this transport will allow the use of user printers')) + allowDrives = gui.CheckBoxField(label = _('Allow Drives'), order = 7, tooltip = _('If checked, this transport will allow the use of user drives')) + allowSerials = gui.CheckBoxField(label = _('Allow Serials'), order = 8, tooltip = _('If checked, this transport will allow the use of user serial ports')) + + def __init__(self, environment, values = None): + super(RDPTransport, self).__init__(environment, values) + if values != None: + self._useEmptyCreds = gui.strToBool(values['useEmptyCreds']) + self._fixedName = values['fixedName'] + self._fixedPassword = values['fixedPassword'] + self._fixedDomain = values['fixedDomain'] + self._allowSmartcards = gui.strToBool(values['allowSmartcards']) + self._allowPrinters = gui.strToBool(values['allowPrinters']) + self._allowDrives = gui.strToBool(values['allowDrives']) + self._allowSerials = gui.strToBool(values['allowSerials']) + else: + self._useEmptyCreds = False + self._fixedName = '' + self._fixedPassword = '' + self._fixedDomain = '' + self._allowSmartcards = False + self._allowPrinters = False + self._allowDrives = False + self._allowSerials = False + + def marshal(self): + ''' + Serializes the transport data so we can store it in database + ''' + return str.join( '\t', [ 'v1', gui.boolToStr(self._useEmptyCreds), gui.boolToStr(self._allowSmartcards), gui.boolToStr(self._allowPrinters), + gui.boolToStr(self._allowDrives), gui.boolToStr(self._allowSerials), + self._fixedName, self._fixedPassword, self._fixedDomain ] ) + + def unmarshal(self, str): + data = str.split('\t') + if data[0] == 'v1': + self._useEmptyCreds = gui.strToBool(data[1]) + self._allowSmartcards = gui.strToBool(data[2]) + self._allowPrinters = gui.strToBool(data[3]) + self._allowDrives = gui.strToBool(data[4]) + self._allowSerials = gui.strToBool(data[5]) + self._fixedName = data[6] + self._fixedPassword = data[7] + self._fixedDomain = data[8] + + def valuesDict(self): + return { 'allowSmartcards' : gui.boolToStr(self._allowSmartcards), 'allowPrinters' : gui.boolToStr(self._allowPrinters), + 'allowDrives': gui.boolToStr(self._allowDrives), 'allowSerials': gui.boolToStr(self._allowSerials), + 'fixedName' : self._fixedName, 'fixedPassword' : self._fixedPassword, 'fixedDomain' : self._fixedDomain, + 'useEmptyCreds' : gui.boolToStr(self._useEmptyCreds) } + + def isAvailableFor(self, ip): + ''' + Checks if the transport is available for the requested destination ip + Override this in yours transports + ''' + logger.debug('Checking availability for {0}'.format(ip)) + ready = self.cache().get(ip) + if ready is None: + # Check again for readyness + if connection.testServer(ip, '3389') == True: + self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT) + return True + else: + self.cache().put(ip, 'N', READY_CACHE_TIMEOUT) + return ready == 'Y' + + def renderForHtml(self, id, ip, os, user, password): + # We use helper to keep this clean + username = user.getUsernameForAuth() + prefs = user.prefs('rdp') + + proc = username.split('@') + if len(proc) > 1: + domain = proc[1] + else: + domain = '' + username = proc[0] + if self._fixedName is not '': + username = self._fixedName + if self._fixedPassword is not '': + password = self._fixedPassword + if self._fixedDomain is not '': + domain = self._fixedDomain; + if self._useEmptyCreds is True: + username, password, domain = '','','' + + width, height = CommonPrefs.getWidthHeight(prefs) + depth = CommonPrefs.getDepth(prefs) + + # Extra data + extra = { 'width': width, 'height' : height, 'depth' : depth, + 'printers' : self._allowPrinters, 'smartcards' : self._allowSmartcards, + 'drives' : self._allowDrives, 'serials' : self._allowSerials, 'compression':True } + + return generateHtmlForRdp(self, id, os, ip, '3389', username, password, domain, extra) + + def getHtmlComponent(self, id, os, componentId): + # We use helper to keep this clean + return getHtmlComponent(self.__module__, componentId) + \ No newline at end of file diff --git a/server/src/uds/transports/RDP/TSRDPTransport.py b/server/src/uds/transports/RDP/TSRDPTransport.py new file mode 100644 index 000000000..9c2ff1516 --- /dev/null +++ b/server/src/uds/transports/RDP/TSRDPTransport.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.utils.translation import ugettext_noop as _ +from uds.core.managers.UserPrefsManager import CommonPrefs +from uds.core.ui.UserInterface import gui +from uds.core.transports.BaseTransport import BaseTransport +from uds.core.util import connection +from uds.core.util.Cache import Cache +from web import generateHtmlForRdp, getHtmlComponent + +import logging, random, string, time + +logger = logging.getLogger(__name__) + +READY_CACHE_TIMEOUT = 30 + +class TSRDPTransport(BaseTransport): + ''' + Provides access via RDP to service. + This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password + ''' + typeName = _('RDP Transport (tunneled)') + typeType = 'TSRDPTransport' + typeDescription = _('RDP Transport for tunneled connection') + iconFile = 'rdp.png' + needsJava = True # If this transport needs java for rendering + + tunnelServer = gui.TextField(label=_('Tunnel server'), order = 1, tooltip = _('IP or Hostname of tunnel server send to client device ("public" ip) and port. (use HOST:PORT format)')) + tunnelCheckServer = gui.TextField(label=_('Tunnel host check'), order = 2, tooltip = _('If not empty, this server will be used to check if service is running before assigning it to user. (use HOST:PORT format)')) + + useEmptyCreds = gui.CheckBoxField(label = _('Empty creds'), order = 3, tooltip = _('If checked, the credentials used to connect will be emtpy')) + fixedName = gui.TextField(label=_('Username'), order = 4, tooltip = _('If not empty, this username will be always used as credential')) + fixedPassword = gui.PasswordField(label=_('Password'), order = 5, tooltip = _('If not empty, this password will be always used as credential')) + fixedDomain = gui.TextField(label=_('Domain'), order = 6, tooltip = _('If not empty, this domain will be always used as credential (used as DOMAIN\\user)')) + allowSmartcards = gui.CheckBoxField(label = _('Allow Smartcards'), order = 7, tooltip = _('If checked, this transport will allow the use of smartcards')) + allowPrinters = gui.CheckBoxField(label = _('Allow Printers'), order = 8, tooltip = _('If checked, this transport will allow the use of user printers')) + allowDrives = gui.CheckBoxField(label = _('Allow Drives'), order = 9, tooltip = _('If checked, this transport will allow the use of user drives')) + allowSerials = gui.CheckBoxField(label = _('Allow Serials'), order = 10, tooltip = _('If checked, this transport will allow the use of user serial ports')) + + def __init__(self, environment, values = None): + super(TSRDPTransport, self).__init__(environment, values) + if values != None: + self._tunnelServer = values['tunnelServer'] + self._tunnelCheckServer = values['tunnelCheckServer'] + self._useEmptyCreds = gui.strToBool(values['useEmptyCreds']) + self._fixedName = values['fixedName'] + self._fixedPassword = values['fixedPassword'] + self._fixedDomain = values['fixedDomain'] + self._allowSmartcards = gui.strToBool(values['allowSmartcards']) + self._allowPrinters = gui.strToBool(values['allowPrinters']) + self._allowDrives = gui.strToBool(values['allowDrives']) + self._allowSerials = gui.strToBool(values['allowSerials']) + else: + self._tunnelServer = '' + self._tunnelCheckServer = '' + self._useEmptyCreds = False + self._fixedName = '' + self._fixedPassword = '' + self._fixedDomain = '' + self._allowSmartcards = False + self._allowPrinters = False + self._allowDrives = False + self._allowSerials = False + + def marshal(self): + ''' + Serializes the transport data so we can store it in database + ''' + return str.join( '\t', [ 'v1', gui.boolToStr(self._useEmptyCreds), gui.boolToStr(self._allowSmartcards), gui.boolToStr(self._allowPrinters), + gui.boolToStr(self._allowDrives), gui.boolToStr(self._allowSerials), + self._fixedName, self._fixedPassword, self._fixedDomain, self._tunnelServer, self._tunnelCheckServer ] ) + + def unmarshal(self, str): + data = str.split('\t') + if data[0] == 'v1': + self._useEmptyCreds = gui.strToBool(data[1]) + self._allowSmartcards = gui.strToBool(data[2]) + self._allowPrinters = gui.strToBool(data[3]) + self._allowDrives = gui.strToBool(data[4]) + self._allowSerials = gui.strToBool(data[5]) + self._fixedName = data[6] + self._fixedPassword = data[7] + self._fixedDomain = data[8] + self._tunnelServer = data[9] + self._tunnelCheckServer = data[10] + + def valuesDict(self): + return { 'allowSmartcards' : gui.boolToStr(self._allowSmartcards), 'allowPrinters' : gui.boolToStr(self._allowPrinters), + 'allowDrives': gui.boolToStr(self._allowDrives), 'allowSerials': gui.boolToStr(self._allowSerials), + 'fixedName' : self._fixedName, 'fixedPassword' : self._fixedPassword, 'fixedDomain' : self._fixedDomain, + 'useEmptyCreds' : gui.boolToStr(self._useEmptyCreds), 'tunnelServer' : self._tunnelServer, + 'tunnelCheckServer' : self._tunnelCheckServer } + + def isAvailableFor(self, ip): + ''' + Checks if the transport is available for the requested destination ip + Override this in yours transports + ''' + logger.debug('Checking availability for {0}'.format(ip)) + ready = self.cache().get(ip) + if ready is None: + # Check again for readyness + if connection.testServer(ip, '3389') == True: + self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT) + return True + else: + self.cache().put(ip, 'N', READY_CACHE_TIMEOUT) + return ready == 'Y' + + def renderForHtml(self, id, ip, os, user, password): + # We use helper to keep this clean + username = user.getUsernameForAuth() + prefs = user.prefs('rdp') + + proc = username.split('@') + if len(proc) > 1: + domain = proc[1] + else: + domain = '' + username = proc[0] + if self._fixedName is not '': + username = self._fixedName + if self._fixedPassword is not '': + password = self._fixedPassword + if self._fixedDomain is not '': + domain = self._fixedDomain; + if self._useEmptyCreds is True: + username, password, domain = '','','' + + width, height = CommonPrefs.getWidthHeight(prefs) + depth = CommonPrefs.getDepth(prefs) + cache = Cache('pam') + + tunuser = ''.join(random.choice(string.letters + string.digits) for i in xrange(12)) + ("%f" % time.time()).split('.')[1] + tunpass = ''.join(random.choice(string.letters + string.digits) for i in xrange(12)) + cache.put(tunuser, tunpass, 60*10) # Credential valid for ten minutes, and for 1 use only + + sshHost, sshPort = self._tunnelServer.split(':') + + logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass)) + tun = "{0} {1} {2} {3} {4} {5} {6}".format(tunuser, tunpass, sshHost, sshPort, ip, '3389', '9') + ip = '127.0.0.1' + + # Extra data + extra = { 'width': width, 'height' : height, 'depth' : depth, + 'printers' : self._allowPrinters, 'smartcards' : self._allowSmartcards, + 'drives' : self._allowDrives, 'serials' : self._allowSerials, + 'tun': tun, 'compression':True } + + return generateHtmlForRdp(self, id, os, ip, '-1', username, password, domain, extra) + + def getHtmlComponent(self, id, os, componentId): + # We use helper to keep this clean + return getHtmlComponent(self.__module__, componentId) + \ No newline at end of file diff --git a/server/src/uds/transports/RDP/__init__.py b/server/src/uds/transports/RDP/__init__.py new file mode 100644 index 000000000..b94beae19 --- /dev/null +++ b/server/src/uds/transports/RDP/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from uds.core.transports.TransportsFactory import TransportsFactory +from uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs +from uds.transports.RDP.RDPTransport import RDPTransport +from uds.transports.RDP.TSRDPTransport import TSRDPTransport +from django.utils.translation import ugettext_noop as _ + +TransportsFactory.factory().insert(RDPTransport) +TransportsFactory.factory().insert(TSRDPTransport) +UserPrefsManager.manager().registerPrefs('rdp', _('Remote Desktop Protocol'), + [ + CommonPrefs.screenSizePref, + CommonPrefs.depthPref + ]) \ No newline at end of file diff --git a/server/src/uds/transports/RDP/applet/launcher.jar b/server/src/uds/transports/RDP/applet/launcher.jar new file mode 100644 index 0000000000000000000000000000000000000000..1b81d28caf331988b415b901ed1ce0f873a538e5 GIT binary patch literal 316188 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
    Q*Xa8dg>UdP~#~+ zp+JwpeGvX0eUNh3Y7_U2^Q(`CxB7j#vzo9PV=K-PW9g{pK1P}3tui9#nQ6QtCGD0u z2X~-(DyL#-16SXmF?KwXe1B*I!N}kxR14wt&>Y3?nTzgLt7?BL!6+ddQ_NGh4}|$7 zaIoBUjm;5y@;}|7Uv3xq)0~^3T8Uv4d=>Tv0H<2dtA>?>D-jOvQ04nvOyl;6g6(Y{ z8J;6DpW<%5Do9o;9X}kG?X)G@*{t9e#LB_0=(!^g4LWCv1vxHGZqfTilb6rbT$WGD zdSN0%<>vTlXhnbpZPV{E4F{mGEhbGJNX;sgaNX)TB6AyvC=|cKw44dP@%h^bY{0zo z(pPoNo+<)mOi@mqv7N7!y}_-7J)Ts{9Y}zh$zD}XZ_wM_)ps_4tbPopDKX3+DK*Fqe!UybmO{=w$Z7Xz`0oBMcJe|bK870lO2 z?(2ZwxZr!dcaYDmuU>Yfc3u(_(VM45girqO`)|n%uSW~-eSX#Z!vJ|9N$J%X@_k|4 zN`3yYJP6vR>IRZ_V@_QNY6>mVcDOFdgRgz1WXluPdc8qY4ZM8cYHAK4FFw^SZ=}n= z<%5uyL9uyK7WPEQ=}xXM<^oM!HyenaLTrJ!7mD)xEATm;Ke>Bfa)Z(2Mr1(<4s0!K zBbxKWzptStoy%?%Y22XG90kT4aaT>SS0BQ48P~YBhqRkWdbY2RWX4S}eU5t?I!MI$ zKfV(wnXt!^2V$lO1lxDVljMv*^2A@iqVV5RzTYC!PSks%^%-;fkAZ#liw}-{)tl63 zOyQ6Y2!XJ~A04%7q@-$s$+;SYUk-nI;%O&JDQY=}#tt1)<=v8HrZwP{M%X#=bE8(G?|Gq@XwAi{2hpi}1{pH~a^V9u0vD!T&k!ODI{|Axc%@ zlhr;cn`Bk<$GYk`X!juTQWnUfVSSXhjQjV$UL`O!$XqEHFfbLw|E()x`5zu|wf{C9 zQ%T!8m>Aotnmf6fySS>FJKFwVN^GjCg2U23sDoRQ!eK^2>o+;8H(Racq?*zM1HHbvhFt@eG`LR?uIB`1n1Ty=)q?5x zO;~slT-X#$)V!NngIPg{d05QGuZi5MK%#bEUCspQzy>>Jv^l9UKXkX;UgnbXdAH9z zOs_`8Y;CI)W6Q)q-t&Y96F`!EmgGzhvUn#G$}EdU6DuZ^7!VK_U`ey_jPZvqL6Yx-8)sxw#?P z9JJzn`Y>4n7~V_Afncdo0s>XMZsRZqOe!tXsZus8)^RGWiYlOWJjOIGsp~G2`h_L{ z=No*~CvftUxK3G#0S)!C9l^`ah<#;SiBnG1rFYrq_!aioXK|8FZhyHdwYmPJ;n0)% za+!v^^D1hMZ}ucT-e1>Q6?+C3@lx^$R5j`C&DXK%kUwuZ@IvM;soCn?p<6EU%e@8( zZ)`S26n*B5)DD<@#qu0VDt$i=B|w~R4%KJ+8O;~gW{?U$0{#m((IDe+hxZRPp$hfC zRUPsFf7ShW8`RtbYosoT$s@G2+ofk9BLnA%whx9aQ*4#;)95EyArw_%@PP~p992g~ zm+}=fwvRY{6j|CjdI*}t0K)g>x!o-&7YRfJzaW9kbnI9_6%qK8T9}2vGe=% zIvy|VfU$^NvjwvMQYNUl>Z)iSA-lGU$(ITl}6J?XpIo3VA)IF%ynfEyC zmi$^wfQJb_F58}H(a`Ai!uE$b#Rx!d89KaEIFT&Oy03x`#;1ybQ->Dst(sRil&sx@ zzC$`3!->U9USaMEc#fnst}ZHKrN)esN{<215wL<9SOO$iA-|KMsgD*Yv^N{M9t=;s zr9?NBhq;tv;mBuj2nC4w8#{f;FktbRlni|ys7a~OC{~E`G|>gzF9)H&Q71nrS?JD_!a9AcY_xy-AQhbjvssz%sW(#&dI7BzSceEQzm(NKtlrd+=ca7Kh z9>l>?Wpp3XeHawkc?f{9Vr7&<=ExRJPjf57#)`|~sikr_UZ;Q7J01RMVP@b&lA4QN z9Q=7c@)k<@AT7m^Dp6}xvN)`w&hal4KO{*xssvb^e=BOhVu~5O>&6>V;bDqZA>w>u zD;fIcMLBwcn=U_|ZJ?^og4slULyV>-7ln7LJ>=#J<&T)dY!U7B=mvW&Xs8;uQxQ~I z3K_(Qgwn5K-m5t2o6AOX96N6nwSFS{H}k|Dt^YfsHHdk5zD7%r3`I93O_TX?jcg#! zVZux57e-YWWk@ytddXt=QAul6JerX?B;zH5-BlJf=a-S4WJSPK3^C>;k$qNEP)b;A>ZU(*7^rP<0mMTM*L>N>US=l7) zf&EJ_vmBPL@^J+~S6`H?EKh}Ak!9FR#&)o&>?OI!+Pgo9maDNYtFZ=^X8e~}XGjdX z`Kv`#7~|Sk?_IH$L1CZ~0x;ygIw-=zG216lU$iZ47EkSoW?~)LSAZ~K8ChTCbrT_C zr|~1m0YAv#^g;8m43YP8*8&uv4WF0DS%^VFRFVv8zgZTBSqaQafYeBhN6=sCHFC5n zfFYEHh+3_u&M}Ra#l^Uos+MhVAS!IFS}wd#LI}Wi3WdlKd+6p$6x6{02qQ{cV{XJM zhM|YG@4T)ifh`OM5o~j1)~?O&>W{_N!A(DC$Uo;t)=+S480w3{wc&jHSrHmkHb>K( zE%9Q0J|ghad14=x;;mgwSd6-_x}R4jYWB#ljEF)HWJ_VPmtLp%lWWm7Gitx>2)k`Q zn5(sIt&k`MXeYdASTm}y=}6IO;>0SX&OF$WZjpRnV3L8(1Id&}Rd0 zSUy8Ri2GtuJ>~-ljw)Bgh)f`I&$%GE>vdQhJ&TvmL}Cd-RK@5vK&55Lk5*W_7;s}wi;QPZ6u8)o6XVnc8av?tAh%Q5ni~Z0Y+P_8 zWq=DM_O>>Y>B+LXi#9eHQR2oM7&OiP0dp^eZIPHc#~(DIJK=SY`>kmKz7 z&7L}bW)_`02$T5p3{}CsBowdYI0A9mf`xy@Ox%RU#KZ}Qa58KonnS3;2>hFIF4|$5 z#&zSs9UwlIW#bla9b}_rOEooiGa3B$9>dTC?^HIC5a98HckZC&1l#*2DU2ZXpj2G{g*UTPTvPzLkk6X3 zYCZmxZ^dM3-BsY80Rr(?1VcoN=xv25JCG}SrVNwhMRVNnc)Y5|q+bV(ghw{0bjBqN z+I-)mslzsJ!q<4wp~?)TD%FTsr=B7osfCQGg=n1Yo#g<6MuJY0u*Cve7un*iRAr(O z%;A6#l7VqfR9wk$R)eV^57!uY)q3zOQO@Aq9$oCQJaEpcLVU81OP3m{85)s?x|a2S z)O%>*ZK{#dmC(S%F9g!N#a(tQ=@K@mzW{fxNF!jw2FOER`ReP!an&M()2m z8Fc($1 zWi>N2z0@2WBq8H$jVp6;qGJxV_(rPQh!PPQduUxLV$dM#<~o78@QE}xIZOqpucqg1uFKQ0`d{(BWO1pziPH z#CS`O?-i-mrE86f<7`bU-@7J$@`K4m3``S{95p+FYD-)aDhP{dM_aXR`M1l`8>?a- zQIi5HjlxF=))lFIbR+Q`T_cZvK+|nC=;Qdr2*yR%q25o*k~$enoH^od1t*E(QC9p4 zg(wY-(yNaa+}M_t8fABg?OTrL6T=68ZXU@qo;#FsW*ZV==XneGiOz{B;E797_9?UY zvHn2ggMvC_s@}AYD1ptiNlJdaB$3OkeP!O7FS@n3nMI&}YQ-o|S6@L^h-k^1=Iy^? zQV$9sr>Op%5*x$XvFKbU~DY%AXck<$YpGl9SVWmg;^4;lBAr z`}GS^MbC%@oFB64XcFT^zmZ@nxazPzgGPj`DDYCkmH!}iu3M^9pK57k*KYO!pP@h4 ztXFcNUu}3J9!g54=KYAvTn32u=8Xt0n>A!TM{%?>tTS*Kf9?%v| zOW85=l3GmlP1RVkUo*^2S(7!FhAD3ITlwivpWv3#QvU$_*7NH_V*Fd74g1|lVm2>J zEMb$#f#d|mNk8mmC5beTJEjPwNeZHrcSgN#LWpv9II3h1hgOtU=!Kb{ZrZ5lPacdJ zlT5pW3*HNs$XV9Q7CQl++TC%U?zl#&of2Jv;72yh_#cbzbj(wS8&n$!ufnRjEp%<9 z7y{d27vQ8ucY)0<8nL7{WFM!r+5+MEF+Ka_R-~bgFOG+h(ywIEXGus#Mk=DIb%Mi( z<@bf_eZZj~*is9i5Rv?_j7Lp!+WjG*P*D7EQYq)jvMEy7uM7gtp@U+8P*#C+dMa#g z)9NwZqla}EN#ls5aRAZ1nPs*Gf9$`W5Hq3heh??CU!{_XVtI(zH)%wve52CK^34W< zMq08!6X0CHNm95b0D-D~Ms(@4z8Z6AyG}q{YGcwXY=UZ6N35#N`vn|KHuop{X=n$q zwIh$WNHW7-wQw8k6;3N{&8&<9tT%fkxYE;szTs}IzQ4wO~I%eyLc z49L`3jXAP~5mRP1q=+=wIhF{+0hgMETH}L^LdXOycC>twZSxCd)oB(-NpnNSf5eXY zo*%;ZTH6tFc+u6ga;N`R=!JSZ(!%JTNatL7q`YW{_eRXMyK|8#VQJPWYn%7N5zcRH zR=lp)`DuwEt3s&!dW@_6X(A)?&REazz$|W@XlOE^)tE_qkgn}Zoyy8~e8&i&`?)~p zb6X9_7_6tkHeGex4rh(S#O+J-G#Uv4yBQ5l-DUV21ZE=NBxa({FINfg2QNvZx`mF5F7 z@Pbx}TFX;IQ)=2^@)t&_q2-+Io31Z^EEpjzqVJHS++d1}5{)-~>d2@vA8SJDT{(rQ%rhFnwAqL*(lk z#RRgs&6FolqETEi%!R0FNhx2oyb1*cp;SdiSbng0lRU8rEsq$+bP}L2!pV4%CpmDo zKF*Y5uBHF$a)Xn`KjmbH81oke0zk37Yeny<(T&S}(S3@zW9`nRS6*$3^ec!fmg8Z@6$eYA?xuRsxMW{9rwNk%n}ut%Hx=G(QQGi?_G`I56sd>V6U1 z^C)Noj}uuEQGcuLAMQ~KKSASmy~g8-PGrNpZ5;N^cMEXrRP`@1TnMc%dkx6oN+0m} zv!+^YF6LbJ;pjo(cJatg)+uD+T@U8Ll1`G}#`u@&{lpNU&N_-0)fi6an`VkNL zJENO|S-&ZMmULq$)=XLXIF|!dQDOA@>g#Zq(QV?eFbw}T3~Desmbg-|#ELJ}GB!}T zi@CWe0?QlMXkXx978g2w0p7t*SwpO>m?=ukwg@O+*3ug`u!QJZRkm?oG-#h_XpiqU zGE7ZPItssuxhg-3!ilx!3gF!yN(7%YZ!{Cdq^d}X5A<|#!S18z-RS>9^u}F*iS(e@ zYDTp^j$!hL(?Hnn-F?d^&EPTfpn`LAinyi>JI)Q6G(J)9C(I<mdWI1*^|RXqEmzo?uGucO&3uhTN7j}aB5u+2Q#^53kVfHUUP&&8(Ge-uR`*X zm^CSe(cqdS!C~GWWRv{-C+j<>TO@)+B*{otdL}RCkpdc698;#kGnaLfs5g`Fh~h0* z7U8)vm=c4e^TdC9;}>ayJ;5bLg$Oj#%~A6ufD!9uXy71qhKzk%hVqj+ENY@&l)wcV zYrv$vW>3eAt)4gY0F?1%s_v4P&)vWZohHB|z6%xYr-Y|rCOt7cI!ES3WEhkvKQ7YR zrv%2X-gug!5Oghs1Xmwn^eFGn3>E+UPOWt*F*t>}Iyd;jx9*uC_b3Wht`0!9qMLjvx^t!M8BXn;7ZbR%o5Y4pq?d-9q*fIB;-4@K-hwaJ z3FJ_blErloc@O&)x|xm_a%e{ik+^vzARdC)r91(Gg&96F?6_#621~6O&I5xvd!p4ljD(RN1FP#4YyRc_7Wf+hh*p zssd6acnT>+-+D8i=iy|TVaB4We^g8DlT=Z}l0QqzFr%Fx}Q( zeq?T{AsG#;g6VkLbS+y^_~}?vG4-T>*uHMK2zCV(;-NCUlR>jcfx{31*2 z^VWme-DS+5UHFANRrnrX)5}?*BeuY_dP3=Q# zRRaK9PBN`}qPqk-9NUBg@+?PgFYpmi_Aq7II!!Aj+n?i>rDKY+5y`W`acXz6A0;gd zPQ>o9F44Ir>GUMA5Oh_E)Few56f<}XRgSsC_HtK+wLf9ERF^sFFTcqNV6*x&BP}Ng zFy(=Uo2YIt2q}9;^#2hDueOgVV5<8;*X<2zkzqbuZb0M55Z2G5Wnqua&^}KYzW;oQTrFJq){!XjXI>g3g)ne zv3)fR=N~eE?8d~Ye-yQ^peJi`gw=%&i^>sE_+e`s+5JV69^dETFbHa&PXUDO zfpd)BG8w=*+I)E8J#sG*k8z3&69Q&~(Kn7B=m3iG@ zy!-&W$YE8NH}3i=%%rWu1d4IA{D689k(t|~_$VS&UhGgG>oHgb?e=Del2SSmvmd9# zgfzPwNoovYGrGEan;B|Q&}`?eU$sD-DD#+CD%YoKzWdLD>*83d6cN@;d8Q47tAK6$ zo75iQhIHAIxkTump@Qtix{%t@ux+N*1fV^~{Y&+pyriZ#?Mgb08nA*mqO|uOO+$Disk~n-N&FWH)=R;3i&5a~Fm?DmFjJ78@8E0&3g@Hx)+|{Z z$Sa>GH4gyfE0yeZf#JJ=w-DP;pdUa&XG(dwb>xqSO!&&hQ4lt&6vd7&&)~`7$Y2#r zZ!(lqHoZ7rSXI%FvH#kniu)0-v}y6xNQYR9zN;H!R4@f>l+7$Xkdt%P8`vR&mH<&t zzR3tIAFD_2$nHmEZ88*90dcgQSz8>pfC;RC z;_s={bo&0L(xaycwrw;)IB^!|$hwGo-*K$aZZ`Y% zh(T5j?2yv6upvSf@0d+$8`rffLwCn>xS2j_k5N0>0(1mg{905iCvlA5wVzOEW^p<( zwsxD~S0Qk7W#i);vTjS4_+Br!J45CZQlEx|-OErfEWepaxA>Zd#$NGAo?Zb5Rn=o& zabqQ2Y3mhF{oAgSewjGy{_8euq3mYdJrcuA{>`XKK|@Pdg_XV_Fm_YD&S zIEm53+))&S(}$nyJ09{&S79(=w5E7sTop;7eSn%UVN*)VK9u938eTo=mW;IJ^@n@0 zt`Ln!_Wd26>MK=>N!goNDb>uR;BkgY+8ZO?8Zf^^N{N-{i%!o=J)=EJTAm@}IBjmc zr_7ftZ1a3Mf48rX`egyn&dzjzZqFoO;~($eiC2Gm;tco})a$8ha8pkgZCZJolnr|_ zH7xH?eC*F%pUP}1-)k`u0B$lYQF5U$eL9(bv=Mr5&P@o|Ydqd^^~yCu#dK;S|1PRV zi0x&=lDE8o$7D6^mFUppFyV0w3$h~s>5@NUw|;b0Rjy)CBJ8DEn^kd8&e3m-iX70Nd;};x1H&Y? zD|zHPBBM52#rRTnLbOXR2L2)uFHGV0M&`>V^|-5$)qFij4Qe(Oj5KkTrzE6^$%ujX zz5d1c#tJLl@vVin?@EH8s<>2OGq{K0r>Y|Kgv@p|pDsh}aZpG@U&i$_cwbdr+C^FS zbE>A6$q$QFcc?HSHT^1B_kz+afb?V`N?gdWu`p)tW;~^JB2A9pYz0S(fbj+|98nZ zfpmfhXV#G-bCz_zNUTsAO#F*(xle3+w?+c9Dz%214l*VGBL+?6Ri#?%pvRm`x)<<& z+_M5j$sUD7OzGbcJ&370T^Y6a08YKGau~|VCSa06BWa;qRAJY-=8t7$uZ?wkx3VV6 zUz6bGX9as+HfW02zs9Ut9hawL2>Zx7-F1*S`7WFYMPo2vV&jV<7@F0EHkfj3b&I^D za_uWsjKK+;hz}qUqkx>D7nmm-9q(3o#N-XMl6egq8dqm{#`WfQP~!6HA2wnqUj;*C z7TVUGSv$uVJBN1pquQwf^j6@Bw6Q=Rww3LMnJO=rHip33SC!H<1~n@5&AgDjhLGCo zGtHIn?>5artULxl<&!e8Yf4TF=>Gs#JphO&ny-xCUi!Zn0UDK6zbc8m#Q)|N>trk# z4Oy$htz>Stfi&KPFG9djbQmO7i-tpc_d`Tt_rL)%s1&A3YW&c^`0E9<%O~)~KKCKY zm#aFRZ^)cS&q&^%xi-9RhX_)6K^P06*O(W+5+3V6%eSZ)%Bv9w zsvlX6LAC#ASQvt`8rlO8X0@9ESHCpb<5*>XPv7P>z7%(mbt_>8CnCC?dHpyHO5?o_ zHg?SJ!Kaq(+f+TXzOEIxPiU6BE^8~f#vQ&Y(-2jQ9*(FscvWWk+{B!pAy`pJ%jviG zs9br3pqVVCUbCrcnTc6e`_n9B-vaZ3Jm_0px_YU*Oec*8P$vYmsO;Dq=JG7NW0lWU zs{cWe+xaFPx10t)-M*#jc0uB;N<43I_kA#bX}k1h$TVbHWRsWr>SvIL%_>*9vRrOWL6MT!Yu6{2=XlczH)-fjEC+$nAM_fJ{WmmJuH- zLvF}z#qJg}JL*fm3 z>P_VNQPJX<>3c|pE3^Ddob1bhtG7BIhOvQ@uYKa#0^!wZc~%%TR2{ zA`EeRE|<@#8`Q(6j5<)}f|~BO&}SnD_c~rZNOJ0raQj2~y5X2s4-l@R2jz0!46DSy zF~_@kQ|r}z^oXrNdYjb@Rp&gec<+ihIJ^D1B=W2eCv^2JM&T7?Sv^ot{Kz(LQLGPa z!E-NyJ>m01+>Sb+y7Cj;4r5>cSqVTo&u>4DMQ%7pZ?}xD^CtD*!3)CgzYsj|&z1Wt z0ETLEMJ|XAlU<3~3Xcz+>YvgJ*;+y?93*HLetmAje^oFy>)J}}u5#t{epNV^obuxO z&V<=otA9CoDB3N zu1Xkl>_&j0C4{l2MO7OqBQYXF$DSZFE0RRFa@>GnV;BhKiJi2~DWkq-a{iRz1F1K& zbO6JOw;Dy>pn??IHNxWA-;V2^WjSE4cUbO}_;Q315HTfJy}WAxe`;~(-6OPkJoJ+l z5w~Y#lDz963+!-lscqos2~aq4Z;FZgy(A9URkXxfoW2I^vT?zKC&zD7rg(0(1U7F# zXIqvuln?{FP$}54?>3G3FWXbyHevl1)wFa3FBkhemBwoLL%w!q#B+0mu#2>_ zRntkojMj8qrJm?XY{Xp3`P^ya?hWJ29qE%}?p+c~LoV6W|9(5#yj9@m>JH&9wlSI; zt10Qd2_~LfftR7)3<8aQ`{Jv7AA=G6Qe*?eu!zjcq#c z+aGiU+SCvrj2+yNZux$htm}ffSNVaSgJyO$iWhNZ>#9Qh7X>qi?3*2JUrCPJ4 zZ;XDgh(g?8S>LGmIG&P@WO-dk{o3*Vp3gNC)%o6Chwa@P1sy)FLNR94L35w!U@Me- zdEd@a{>HRc6YSo>bP?3pD8O{cNrLGy@xqIs`xyhQ@J9G(3H*~sIOe<)LRLTK8}vtG z4&}5{@TMnW-)XIhvcrBeooUj!YAl+lI*!xxP_-5thcF?V%y}~+XPEES^H2>F))V8R zLkrP~tz3b)SvzrD6;tr8-KJN@t_cM`J(n~z#6`6|Gdsh2qA{n0k0+Mq>YMGp6OZ|o zkkwBGWi85Yvrog*h=CuCOlI1I9K3Etp#EursLd8f1Ew-m$5%`{ScSm5K`nc^v5`wn;lp1 zoD{-BT=a1I9rH-s4GS-_wQuFaH6~4L(VZTw99M(8Ybd9G(fNDrK|X5rp*Igo0A=;a zV-8Apej)elXuZyRH@`Kw=8RVL$MS<3T?sojb(6PHhAO>pMsh>;ap~H_CtpISj>MF2 zQoJ@|-8Ahh-oH2>arlf6MRr`gq$m63o2O;hNvkASuk}7=%dZowE?T}0XpP~#id7_w z4M3!VIlHod#(;$yU59)UeS@|Ey+gW3qixF?3o`c<*L_S=?8B=WvB4z&`K_^#RQQ&5 zYo_(P0#^7;=&1?90Y=kcP5#%t%9|&YMyws(tZ-1#M=@QpU|i9&y=@1qjSx68ol6hW zTtDnBz!!FIp#D~XC#ct^@Y39KtYXM?)Mk8c=*x=FbADz0x%bTT{WM4Pao{Tz{w5{q6QAlRoK=Pu_rq*oQ zkRi4l@6wLko;{ry)6U$UI-Szo5PRceO#j^=dC%Ue7EMO3=} z#(+LjYvx{fG0vnq))cMb&?;tb#22n#2Gf*kP1mIpYra+yd8Nadt6x5AwpQJ=fqVfY zUvF8xe0@dZ1{?O$ki7zF+p-yZuwXgZ}+Dbm|q?XTvl5 znp?=I9?uY@8Ed$HIcoeIc5dR34S%K2p4wws26~yGwm?gfz2ho%U1!P;F$G zoq9#s6y|5cWcins*=R4DaRc?#?MnKA>p9s=_+!I2;RgBxm<Gn*S9EaB?N-#-AU_8t9|=_Z7DapH!p_b1Uwxq>gbrQ2z?=rqI9_$J$I)kD~Xm z>5VAHF7)UXzrv6{GwNsRZ3kXgNWh36p9A$@?)-38af1orlhSS^`Hs!NN$THW63D4? zHzyu0!VF?eZw!5@bJDTQioLUI_s|(XE_W&4YmS(;f<1^zU-Vtj4@J zU|X}6AQSmHibiwdENEED$!eI?t5P8n(p+X2|^*#^=iZf?q z)-l-TkJiGEDj8g~ar$!gsq9x72XNx!xRHz+-8KH0k)i7PzCt_=>@A zdk8KNqUbeZMGK<_kF!MHa-WoSp0j-r?m{xW@5~e3H~~Djks{IJ*x$J<_)JJ%FEGYh z(;IDD*FyAbpazF*1HWMBA(8pzp$|Ag685q4j0(Z^{st+(1H76gD2F+XaXqjAxf4@# zjOVW!KQQ#?P1sqWK$zkpGwTEN_Hp^OU26G988Ml#yQ?UGGEeQt{DX~ma4tNhrF*4s z1Dv#$M=$qBN2kA@#8a|Ot(BfSXWQ^{`|g+vKkxu83qi16$(M-4)# ztB%26VsVKi69ohwnGFG!n_p5MVy#JDyJXu?tar4v zQ1I=)H(_EA3|c~;gFZh%UoxNPUBmOjbcJmPcW;B6H{!3}kf-?B`RNnC9uIdcR4|~Gz8gh9BkLokkFTch=n;=KYEA>mwc{I~x-N{_D zQB61%;KDL}`-)A}_CcdY2%9#As5{XPQyx&d(a;UKLt6CN32})p80rJtyS7&8K zg4H*#h!Ot1=QU?|$+469<_??-OHE(etmCv5Nx~yKa~30*>RnlP?Ck*+Z}_5#=l*&! zBtKSKN4>*_&wrDcza+c~oDazfcculVeHj6VCA|bm%XjXyQDWgX(D>l@FBbA+_xe!y zaAx<2;vWTjlDy!y{Dxh@a)BN3$~Pm44%7(`vfM}8z5j?w&T4L*-vd)~2ied`x$!SX zPwI3#laBr|rp$a+VvzPJcK=Df52oowuhZ;K)9IyNJ?f0mnW$L%{UP1#zfvoMv3wg> zygX*?Yx>N-_;+ymU+M#xyqGJlAEpsbj1l#VFZV5|w1J2>>#;=Ii0G{vy=~J^G0}iCPT&%R?aK#q+W%M&l<4N-e07Xk~%69}tEt;S}130Z+suC#caCj#Ih zpD$nv8Erk$csvX^?fCtVK$2&_0r{p{gJRf~b>L?=dQY;O;C9zw=wJs8=dajp&^zVv+@%`!__5b?}a}((0q4e7h9YDNtJuydhaByRas0{%ba_zvKKQn zvU4Y*>_yL?io`;O3_AQ~a29+}K2SPX!Sx~l?S}i2VU0!GD=Kwu&lMDnU`$zT;n+-h z4W~y)^C(Fb58DX0v5#b7wZO?nuk(BogJZ818ZN%i9Lm)q_$5>w z$`L;M1?UXqn64Oo{r1M+=+80gc)7Qx^Yix^)-&Dt_j&2{1)ch#vK`(t+=X#7_I&UB z#d$NavwQHO_2K+Q3oUgY}W9dL83cZV)Nu|Cst+++Ac{vCsV z{4MbIrAzV;fAAMT?wBuf;e{?g?3?Q2qlakp8)@PN$N`U!qI%fA>DQDkn*h@7OszZl za+t##bAMVl{Sk`OUhdLFRel}3oV0u1+5!ocdz@V@IdS1g2 zLY3Wc-&%eoJMJShTcwfoEG7oLTDp9`p?48s!dI949{;06TtSV9seE#s@2d$|uP?>gMx?Yek2tXR#|CH*GRHGO4n zCCKjeJS3jE(`AY$eGEP<+PBzJunz7w^&rDHX#DyO0*x^Qf7T|4$?2?6T(AgK^x>b| z*gkNw;_$O7S|T+AsL$!ITjLS!ldyb4aEGEOa0V+uPt&fnNI#Qwcscz-W0HdMHR62Z z(*Z3CdN3*sA~;S+w!(1tf6y?VVXU)CjZ2G%O%UM}Fe8z#fB$msdeS1m*mPl129e7x zdg67ZSr*N9MGp#pBLh;pWoXu60uc^xM21-v{2gzEp(*uB*2oOGcWzKrkeU-&#Z1&W zytsN0sDq#S;{cN{ZMrlL=NTY!qz>(Ig%;!!XB<^DGLefKqaBr%CC|nrZMB$B_1<)V zf?r(gZAzI_{z$SL^y*wgV2qr62|kEA@JIw(nF&xylYo{96t1Bn@YRAsb|Zui;KsA5KV z+<_Mj-K)hzBSp}(3F{4-PGK@c_*SXGdnXJDIw@s??1bSy3jJ&h;&dmRy%77Y!Zq2) z{sK&9b@#ERFt>wTDZ*o1RhM9n%cAKARjMkQL{dN{v3WqR(&*ZT@nkf>U5i z?mDMSRk`kjd*;c`$<*M@5`!Hv*Q#LRg$Jz$uPfIC?>$L$;>qpPHtVNT-~M6E8fb?d z;+f|SFMwDU+CSB?T|jUA0l7dC7f2zKwFfZ>M7IYK8`Nudp<7b+cH$cB>tq=y*&?qQ z)~yoYSQc?PI&|dD*y6-SP;3)it3&v-Bc!ZfB0cXmUT+ZMtu6qrX+fR0;``2+?AbX| zKa`roA)W;tLbg3QNV5FMtqY{fX)?r%FsEt1Y)e*Y`j)e~^5X>Fe?-F8f30yzY*Dq8 zgnhcqON~I}@qCid=){U8Q5H=Jj<{u?ax5@B46%kBQABg5SsqrbktUuvQ>#l#!cH3H zjrFj(D486?r7Ia!%3(vPm5S;R?+l}7Nm_96wn$pK5by{cO9044X|FzMGM1mzglBhkty^Mq93SE0Rn-A|=Qr!Wl0nfg@DThkt{1a? zS>=ao!8r@22ub*RV#7%L@G2q=ZX(Vw&pG60$#!?;2h@(ivV8{z$xF0@Av^{VZbH-W z6X9@o-G*6~IPs|8MB1-78<^;%F;~_+F`qH419oEZsu?SLQlpFgamG19{-#X{*!BUm zD%_!Sd6FK9`mOHhMiFwsg!?=+EHBohh&M))hgyU>-uy*GnCo|x?k`l@d&)MfYJUyj z1CxYG)$oHl4Z=frNn9bp%{@xCW&`Eduj!=?D9k^=ZbN%xO$y{`_paatiYZNvF6r)U z@^cRCU)uclOdVo*d!_7=4u4knkFe6YhC%^ENFyar&63kQzpg90^o@_O$|5iXHDbyG zy*YnTRJB=m!d>temE$l?f{T=gOOF{{1~=mkjLllps1*G*an9@NzGa}2b&gXHlklscrZWRjZBr`#5rp!<)H+0`kgw})JboKbIb!AFPweqQ;A`!& zX+&2F`p3Mt>B|Dx*Z23}jNh0earYJk?tFtQ-|;$a_f$UesaCv+))8id2uKLyIS_Qi z`H+E4@VHa;UK#808ZBFl8sdVPHgUFt`?%z^euuIwtICcJS-fH9Tbbn(KgaXie$Pek z2mZy#XYAv__sAR6x6!BYdjQc2$$NzlqCuQT%A=>CcC*f=JcLxN|Mi0n<5#J=>+qUgT_%ju9#&&(Ham}%!+`8sia%{z!7cZATv9KeQ;BrSVS%uV zq66AL;;DXAFSHZu6=2w}vB9i*;uTmmtGvGyRzmlOS5Kc)bN>+Ew7dsDiM0f2${=|K;dp(EHPXa}MjW;L@$pSIGf+&E^Vn8qO>X_A_XkV-tf|4pQnI#zyg<{x7g?12?0%UoaB;zZe$+XJMPJ z0cQa~jBt)c*dY_{805dxsE~67oBfLzp;X6>)qgAP3ar6l8GEggbM=PS6MO&W>cg*R zl8UpaEVvx^9J9StJ~J^ghG&&|GETl@ZPmr(XIp z4u;uGN5JRZl&<|KhgC{bj~3R~ah%wmofMjiN0^5I70@k`qK+3Ek9= zM?r!Uv@6gG))QTtf~C2)fnH{+cZvrL(*rTeJtH=1`+EjRBlzUkbmCTyE?rk@Ce4*s z+t-!JX88s|m48CtVQG!ZK zf9)jZhB50oE+bb%)}7r(=MFGX@$T;eZ@fG7(z$s_=9~8hjeEh{;fIbatcRB?|1jhcUJli z?ftNLSw=`Uew}l3(Bg2>kylTUULAZ= zhg!-rgyfFn6r5=W;GA(5n_b2ccT|G3krFQ@W2IWUmZcW3*t1qYvJA&l8_H$2OU|N> zmkDjHpe2#i?+LSO#0YViuqK_(La4OXdbE~4n|tl+aCSmSr1!V1g0e!TdLU8H8^mQz z*pB=IZWaw~soijP=juol6k~lybBq^8^QWZFjjjyJ7N=dS#}ea31|P%1AQFlrPbJ{e z09QJ63_7&_H+G*ZnGLgqkxe=i>cr3q7)ZV&YSq;Rw2Y(HLT&I6sPz}zIl8Org3XLo z?1^~5UZ$?}TitQGOXhYUEv*5@}>c4$w@#JGL zXwQU_G96}FXLm*>k8`S$-WUmZ@Nr4;{;51CbPe8|v`v6- z(4zmQZ42EZ&zaTos;jS){(6w%H zNE+`?He8s^W@zs36c_0Cl+seyAf>k*Kw~`%+yMJ4n|5`Zye=-y?R@nW&N1IM+9i1> zEUD%G6>G0BK#{E%1JM4%IR~`c{L$vZ2=jLXF*cm_0l|qCh=rY1$)Jwn6nDYEr10GT zwNq);xw&Qpm?p`Tmom*b82JRn7{+zohjjW|zZ>eo**~78+tMf*G9`KTR;%Vh3>eaiR&Gc^y>t=r# z;0;f)CXaE|#}oY39!E7A?hf}$s4CXiqavBLu8)Du%xY&7O0LLkae_#3?@6$H`SAP* zg%RnBgm`XR`jADGYzxhya90-+Qy{fW2pd9&0sk;mycyr0lrIG370Nj!r~fuBmGm3; zk&&E>F~{1A7wbv59f*A!ltB?027;_4c*mU7bXOvXLRIW%*gbI;!!``+I6S)9r<^w$ zf5+ffEr6eRhP}t>;_GS3Q<1~(z)rei(*fHgC$b0F-U5XZmtj&Htm9;vGHn{$OJL8+ zIB~cc18CK5;;ranNc-M-DQ#ir`Mq5Ia`b$P=&n(=(dxtuv-4z%J1=gVa2rkLr$g#; zBOZ!RlJ|@Tzq78|_TQ`wAA>U^GBwj@hmkY$++xEXfMCcB^x7A*(FZp^{6(5Z+iy>s z;x;@Rx@UngJ@AhjAXV4Hi;-f$j;SK{q+DGg`}TkwB<=YAovAQfC|z%Bk|3x0;)HfZ zxG|Z0#vpH*>@@7P$h(PMA3B1vc34iw<%yErN0h;Me)LnbrlX%rJvnM*ai*Kj2XmC; z0yXfOWKxQUQvMqp<`w@7ozneDeL7V#l&(W&V(g4M~^`{^%-L*_yyFK?HH} zey^jds*9hh;X)I2q)C#nl$D*m6^^z1`P0vehl-_6m-e2bO(#1VszNtgZDa(eA%~q)s+cH za34XUh-0kGW~Cn26G}0$AeH~1GiD2mhbTrQ>xismG&$S&YDzwnsAhJqNz-?u1umpy zv?AXzTMy#%5;a?9>%ycDEahR1s;2BDFsm02;n*VqdyvMaz6#yR0{7?<<#z!DEM8OK z^Qg>ip*bsoT70Cq?ZcVQ`e&LmxSE&Z>641i6#|xOYrAruYcgav6uaZ?A=k@c2;r^ z-QozoL9ULTOW(dBb&ljHTEC*&muhCkzTndDZ6zIUMenS=u|VVx=oA;#;OAXWa_Ze+^60-GWp5Y~^d-(SHZn z%8-0h|Bl=!6?r#M9c(1$@Kqn*mn;H*RZ^Y2FA;tHEkCp?qx$NG{wJrx@-3P*<|j}1 zRlynht(G+cUw*GJ-@%V@E;uKuL!!6{J*S{U$yremtfoU8qqY(OaLK|bRSg$7Cu5M_ zqPbhbV$iKZ6|u}p%drknllE!cyed- zSoG2ICh1$yox)dccZh$k86GfJZ}Yo99;%j#GRR#-)` zp5wiCtkZl~xMGc2rpD)I)32AMMkzm*&*}R3@3n~jeOo&%|FU|J_roGD9SC`1Hf<5a zPw2M>iqV0jZgL5@?XDe+39q6bi5aW@SBR@r(s%0$u;*O|pUUfc(}eK@=01nH`a`tF z*s9w>K(URh%3e2;mxcTej&zx3jvNjf_x7<9#BOVFs2>fbFxCD0}OQ zdo#?8%OIf}E$l?8pMe2#{s_$zjxeOp!I3)R5{d}FZJPK2QcxSx;?0#pf*T0=2)UIWLG=nHiduoj7ABpWa7mW9-2YA2a`t3IzK1I{- zfILSB;&5Z(sHZ!#K08Qg`dq0JwG6*vf;Mm6dWh!;+}8QD=Ly{Qc93758(DFp$fb@J zya?L_cuRJU#-rcdWEe;F<8B}Dq({%1H4HGoHVz}b2>Zg9CQ>g~zr?q{JYOSly;u^S z%cl$U>dno*PlcMl`k-GuadArgbOk|f-(+9G14|u%CXU>rYGWSKjlLm+yq(t3gvX^z z=XxBJB1;g1@dsM|@r3LQ#By?@`-bunPrIbFx}2F_rBnGBn&TSxg@PHLG(1v*7T)%H^au?!2l_oji$VI&&l zD1g348@i9Af#YK@>{$W)7)|?ghH+jd(qO~@_{hWFrkF=EYC|-H$jrjB|E0UI}HZiB#M2@cet;%)DVMXVQBSiZ3~Hf*O_ZYy4}zG_Z^l6MB`LP zG3pMR50R~Jnrl^lqoEg_v5lljqHnoN;!c^*`nPA878+#|e1?R_aZ zYt=Ov@3XExB2Th`=NYyz1=KwPub$6txhKrQoj~)p@9FfhcSXM(Y{=zS4vh z$>pXYDlCcF+1MM0Z1Bu2KA0?V`k?X5f^jyB{Sw1SB(Wy+{pxr}e)0(Ek^;t5VDV)r z!o`Za(!qv1dHsUu3_^#`cMzGvVy891zc#)J@Ky~lEXPNf-QxM0zhto3qqA=fPVdl$=8tH87vvDwF4_4T2{I!Vz}~H-Y-PiTI0z&W2VkBpM@?=y zA5S0pDTrHf@FyM!2YxgJ9|Sol07jt5;PZx;>odxlTd0s&h1x|ex#p1;r3j~6FDV8; z6#)BRvxL_|rO3@VQ!5dzTCi>$tU2EgAG;QoH62rq=BF%0xF3&chMZ&Wz% zM+H|^_w3tvk72HRl3J3?D*&JhTsHtDF_@R5z9Fgq6z<4!FUL?D=62X+cDa@Je9O=g zG~{sfFp)S>E=HPnEuDLb*c?S*K=WN8c43(u^SqD$Cifs`kROy_s-VjiS6{?ObA~%8+U?@>uB_sz`1+EnoIHyU9=*m+sKsqvf z#aXzgMKIUOV(F7ebU-FzpMlb6lMupxiXlMYzYr_j67&asbxtb1l#J1RYp~SP4VSvSTYW_w8KpViFH1Qf{K-jY>sNq(?!{=UG3GuDte7Vk|k+J zyMR_@^qtukht_xH)J=rPAj!FlyvAbu8A5y~;z(L<5=%;_Jd~NKXnVHJ$h>f*ez~yN zoLzL>_0V1ocfcP9zPot{f;D%9c8rX?k~*;Zs4V(kca)=G9r}59z%W*nzyUWnfC^dbEV;-YUa5~KI z06)x{0XO#Tnqe)^A+w8}gZJO{`#mD6A8ckriF-O#K2oQ^?@dotx#Zj>^N$Aze8!eM z^Fb=~GA5@LkfkDK)EZb*%l!J5|JJip!^c%| z7`pN5XkbG?MiLZEK@cYZs<&$q2j$*{5Q*?+?f~s08%;e_r%{MS3&XVb2?$` zX>&5$-h7uXn^t^;s~eLN@mZL)foH~xaK}-{@qx8{fF3MzTyV#Li2epfn+>#lt!XF- z&AJmNA(K1blr6dwR0@m-W=NO24J%U$Jcl*NWl+c9CaOLJ<$i@SMMDiAN+5S+FFAaa zm%B^{t}uvxqN&+)6U<{WPTeV}d*?k2=KYgDtAT;Y34xl2=SVy~@G#3~;981)9LyW^ zHY+xm8EYVOifj(}gjo*$x@str!HOmGC?6!u#M&&HpOy{{znhf!!%T_HRQTH_2q&GD zkl@`7I_R!D2pSWQNwh07nSo|H?a&|b{6sq)nBY~Lm)P(&wFR7idd2z;>rJSe5BAj? z`OX6CE*$*u{guRpAP!19^k>>5f~71=4xKjxxd;P#egb;#2f6r-REiF@NC&xC^K(Hh z@~Q{H(TJj}B33PgGzfsK<@#67@rR(sMZWi?!U%xvIEKUgQ-fVXl$WuhnBAu9EjRYJIy6koDzlO149y@$Cq{Z|%+XXyPvbC?<>=5fu0bY$&))J5 z$Wd}VZzPF{Brx-8%tC&$?!sw1DKS-G8m?$Yn_-4%q)UH%Y=$6IDWyL1b^jeC_NpBn zaDyDW-YIv&Te7yCMynJa0Fbn|M3+@=>+|R(JFzRyG{8Kx=ftp;js|zb+7Mk%EO>nuyf#%2RscRWPWOE1howt`A|8#*FR@79g^F?WJ_5S%MwlMK zxp^V~R$EbEbI}yFiz?_8(+R|4EgK30IdWA+F z83qp)>(VII^AZQICj6Gpx^YL8&HA%Bs!-=RCS|C?m6^|ph4h}w?J8X9x+^D}1DN7N zh4XB5MQE7~gmdu1{dB1U`R!x&h7Xhu?|_6C3UT@eJOYzs`uA;4w}Dp%`{xx|}} z#TM+`hBG|h6}sSU6<%s9uS|6_T0xf0HeaIMiW#2NKW$zjP1y;P@Z&t?a9F-%p-6#N z)Eg|Q;q+ky$y#uW8=ma?#AD~Qrf5Um%sI3fTu>1T#r2Sm_f@-&t@1@Y3xN1SW{S7|ZfOcB`2Kq8((_zAa+E(NS8X_%gtTGIgw%}LNJR!_7O1XIw0Lz<^ zO6>IY@Kj>cj?zhpmltssZzEYMgw=GyCSo*4WRkRi)HJ6tY0mi{34T@Pi(S;D3`{C zs@Quh6`PoIPeRMd-W7s%W?|CNbHw-#JJ$kvU4;yGhl%RdGGN|59_Wx>$^jdqG9}9i zp{+@Cm(oucNt}{QOw&PbgMRw@)l-04gN{?0616?dC0^B3xSUnEtRo!TVF1_$IdH(8 zD;O)$m99`LTf$CPobl267-STs{hPIHjZ4h8lyyz#y-d@&t?Ay-^v|Q=-{*3_R;5tK z2+lfoWpImS^&^wKUAnMW9AHrhGqYGG!hEB$%6-CmNVI}z<2S2mj3OnM;yj^iE#es~ ztDQa5SrZuefohKJmrh>}HTpT?Rro8~_?7*-^@(dj3vnRfQ&Oc^)gQNvKYWHZC$d(1 z3#NM`mBIK<%WImq)oT$tixK2Zjr$I}F7X%FuFoVU95$BN^^u)Teau$8bAl>(4_*JL zmgqVMtDXCCUIQ*l-6OmMQd(0d<0i;|7N$nUE9ohe`oc@J2)L+j1{*R0WDV$Dz1{TS z+u*#6NxkRB;81ifkJO!fXZf_Al75BC#mJQ*z@bH0CZCCM-z*p>+Cz)5i|=;~^gRsH zE6Q-c^h>tu2F?t`-agl&>HRUa z4z^hZ@A&uY0|~yr<`w~`ct^I~4|juf)M?G*4=t=?s=EMOIQ6cATd+CUm+LTEonCQQ zJK8XroXu>du9Pi^SyF*Zhj&Vp4lZn9L)LiBSWu@vY_;(Dl~LiCg3uWd zqE8MOIWS&}oddi>M-8ZrC4H@(_^5(;9!g;X9XSfz#9bTAkp&8jAUC=D%6Og&A1{3z zPr7U{Z^2QtXMLbKbN#b9RmGCHzlw{tJNdft<`VGs%9{P4zO9OUaZPwx7V-89e0d_f zy+XOYx{;6l{)2tij<^%TRf~N+=%&w;9dh+T%N=gEi|GyTdhl)^fKBs3q&Kot8-GzE<%5hUZjE%J*1HzQ6Z-fxE)3O^GL|@_tcIw>=SJr2T_n=(}(& zOErcn=+IvSk|zsHp%~=t>M4% z&hpM4KX?BH-}O@R8&BWzyb{iE&?qR*UE|l0US<@ zMW=mH_ym7lB0sowNS~Hlz5e1A$tirwVB0Sg-@uKd&?4;rlX(O3HIhzG-m;D3DETKWm4vc%hv1~=1UFVo_oAj`` zc^veI?8;k|pM>M#t^-)0O>6fAjYm#uWTKse`kp((PXfjNF^J!MeyYtef_~1bqS5-V zq5hD(b!2OSD%*%^a*Qf|%4e~^^92%b zw}scZ?96C9%L?O^Xj|U4`|5_C6XQpjQIB0#ZaA33B-izQ&!C>MEf)>)N>Nf6X`<9u+fEUkjyO!< zrnhm;+7x5L8uh4ipn-|*l?Mue3tAh+{vI@t7#T6zuGTHPoE&9vx(OCNthGUQqkw!F zN5RO1`YCY-QjM7SUIR`AIY8yUmaBkgfrSe1{OIx{Xks;{dq^ukO#E9qeOBWQz&jSd zG*M?FxxvG3jna6$Jkc!E*PvJMZ*%8G+vE;Ek)GHQHZI*!HUCWbF!J~li+K6A zek=G_deZGhEF~>JPC^LEsxl0*Z+*8sLU>N>5@j*uKxrI@_!j2RQip)k2kY=>7)L+-Mm7g~W31K< zeRp$g_>fzgcI1`OdCYQwiUZ$rO7*^gb!kK!6`B$5gr8wiLn>irxC~yfF3(b$8~EhLnDWX%ym0n}sp0EL``38B3ax5EM)WE>YG~}|2iY@n z-Y9yvg47`H^Lyp;R%y*wanq<~uW!EN-vv!l@erR%GVt&GwZT#7qkMW-D$tv#omQ2tK$!q79x6Hi5w z2naoa+mifY4dqq|CpIT$+PP=xVCt1k)WC{>SIk=N<~?d665tBr^=4r-kF>Vz)e9^K zXF=RikI+&Mh37HY7;SaO++P2ZJO7ydja}=K>}Tb1u`yMWvIvg2F>il3t2u<)Ym z&#M>k7v}U6bygO&y#wj(S1851)4+tH7AOei8wp_gyVrFq$N*P8H&XJ{4CyoY`g940UvK()V zlqV_}ug2N=AN+s9sWXAv4(NwE-w*P?g%jcb5KjNy{zTkOjQ+d%DQzes3n1_$TBjkT zM1~Q9D$quZ35F55!)yl#&;1c6DN-!5O@Yz?WVCi5KGJzVz<4)H1{S5@?}~-pCZ5$0$hBMy9gtwK~-V_vO();O+PJ zj@6fGS^Y5ln1v>yEYg40vG}c7aA%k(8j@KM1MqFA*pS0U&GN{4$6*Uhu2Vmw&8e*<&>76d?c&q+9jE z9Hc;@$GX`0=LtCja})N?LViW$6YWjen=PZBZ^}3#@M3&IFFWpMnspSH2>Ip*>~n(# z5^TDVY(YQ(NzIT_Fo-?Yd<2R!w*391LOOjIvF<2Cw5aha!Q(&i-}Hvm+BCQv?4j>WhXO5-K#rq7-a%zJWfBD_I~T= z?7iIw>Pq2mszWg0Y3q}5pAc${$+vu(lg)yr+>qJ=<4ROS1tepmVs!|qaH50G5bP$& zAFcK?Q>v%>(v%(dUpb><4OF%gf38F&Fz6VY5jgIv;^3L6Y!BcZ8ci}7osea*2fd{V76OIiN%M+}7L+t9mI!oAV%-Dbg+!HA07@AGH;9<$fJblzIc)?`dXO=SF?EMRzlBWw zmjtew!m{+IO>dcL@_;s(u*hpv(ExXF9T<&LF6j7@S43VMB;sTvFC5mMKE?ztns}U& zd@G;Jm{0%TT+iV}-pJ#)-GA~<52-ww@+aq_!TxXa?!QIc|DU{5eRo6t5ntKjud!<9 zCZYK&Qb01xK<80F7IUx9&kN%7OBE>(+SE5vo)ufOHV%=OQUpLp5K(la=7=|u;9Veo z4&i?!E*4B%r!dPT+QVgZxE*J_UQfE2y`S{(eM9Rr?L*&zQhn?p-qh~M%cX7ah53zc zFc+S;qgmpud_Y5ks+u~SE)&J;tE0=08VE0e0DF8!1IN7O;xW5HA9h%Jfi0Z$)0^srhYHH8H@_7@F2xx9xa@XGGr{~1)IAwJACC6iOxD^E|HWqrd?CaeP% z0cOlU^A}n#D@9|V+#3g=HdRe7gi9(r5UL^@sQDSm)F8tfW(70}6T`?F@|2fNitelE zM^aNaShL(MJy4Y*s&iLnt9B8p;6FS8F!va$?Gd?w?-e7?gfUs^$D)C4)!qSx8_WT3 zxivgO2>3aLfD8;(a`|i^)rE;EKkQzz4G^=EH6kbuK z$Z=$j00X-WUMv>M=;D|cluHDPbP)w55ZOf|^u!IBU|CW(it>vmpfueAVdST{2pO2+ z1Zj8?Oic>$w^yu!K6!qoh*`<}T}D+>L1m7)C>hyHvCx1gfK9U(`CK@yB~y4U%cKo0 zG1xRE5d;^71LW4A0xs^-%eKv^@)d^`s{r+&LAdU;s&NRiX!Q(YIInFs(%hu1i!e3W z^e0m60mjzapYrIS$Ajkyq8P^g*1<%1un4HS5y#w%bIx(i(ons86cOk~je4nVZb@Hm z$M5Ff6XmDUtnY?#GUgJPlQFW*uUp0Ed~10z>rR6bBpFBr<50K-(CYqYb?_N|af6f? zgLH;i84zUB{j_7s2HCDfPS0ha+(2!f8(D=%4B-yakTkg;l!B=0=i?OmX#2k0q_@o3 zd6ZZ&31}%KJRSMRQPiP+vilyHMHZ5KO3}*fYNjZ$CRkdg4F)8x?%7=ww@9IsCIjx8 zg-04h8d+%W!F4AR;QqC?Z$f*JD$;PpB)eQ{iAT-du6lUZXMb7JcH&y2B7q~pf6F#R zjBsOvvus&IVH=er)-q?twvbfU61A<0nPzOqw~mw1D#w~efx56P8u(>aOKBgZXW9w7 z`1`~84fa$=~tP?D8B z^w5m#ks{N6VBdc4N7)X;A#vlO&OZZ&<_Zg4i*|qNM(9QMp%5UVs{I%1uq$mD>_K%{ zK?J!9(_;v?V1hsPXrc7dNlsw6uDoDbh|9`0uespKElEdA;mVGpL(FQ$g(gi@L$9oZ zHD=u_+yrOaD`=BDxXLegjwkw!cYVVH9ky|UKKi(OzRRJ}!*CUSz#qfY(0!6AxBw** z*Bx^JxiiW@KY;=*AD9}EAi#T2loL{3rh`RzE85Y0v>m9THeOZtZNduKH5bn7OvoKU z%O0S`Ad3E~VLbr$)~QpxHfG|m5P#O|ssZf)MNTiiHcol&2~Y@E?F%Ldv2aP?F5i8L zMLPOXLBS$;f`0iIp5*t)f3RWU1i>n73ujHEtlA)IB-l5G z|0_p8gdW&aEpg83!m*VLQI8HXmkd7=A9hs7+W*RM>WHD$l1kpBz5YPSXUNR<;6ff~ zINP_(+v$23(XfhWrAplA3~g|t9Qh~6dGsk<$pd48HMx8{v)74R5zXjiJ9MRiI52V6 z#|gD)0#+f}N*YM%O0TO!K>5!@Z)hky()+Vg!B2rEnyG2h%3!;zF+t>0X#d9 zwc~LtQFZY4*ceMw{gfNViG}I-N;}O;0&!BG+o3Z9#)+7Nc9|3&ZN#gMfQ?D%od7-L zxGvg3$pE?y|2hIS`3TIxI{`*aXl-~YVaY&-3I1Rl=HP{ht)5nip?fkoZ_U=I+{{?` z{(NIIcLP&k=LB44{v6^Rt;FGjfjU9etxDz6?>|k<38akF^+%M&1NXl*F`WP5@TlT! zVNJsDU*|>_r5(8id6X|W>WU~Lr+!dS0pV3>+kltEA@l%)F?k2cd=kSI9d$8_)Mnx@ z>MtQ@J?~bk9vK&E~=}Hx9y` zyY8|NasTL@n)la`hORy8wLoo_Y9e?MXt8D;Wb+L`e8k0_$N2dG;i9mBU2k9pZC3g) z2SFO*0SKuWay&M8DkAr7Te0%Pjg$@(u$WCZxTezMprClg=ahzg_1On!?SrWAQ3-vl z6_Y8AYq8?U=F1f4F^tiEk_WE6@*59s1_c!oy|ES;a9i>DuI7tSL9y&d*E7hLoH4lX zk~5H_(fVh$N>MT8{7W_vEpEp}HG{_pPm^q2hV z@#ZjOlP-xGldi9gQAAR(syfsFkuXX;l z(TmO-6E4%~mN-UqO$1sNoouNl-pys+0}k8i(NczBFQnvB#rVl_Kcj2QdLynWKIkQ1cC#C5Mv??rSNc7gC|UZIC}+uBpEj#Vcx*?b(_`3>|{QA2UQxmPicaV^WTA zU|ZbC;HXwnqiy12#0^PTrYCAt=$Az^`dP^+Dm9eap8mRA>@?(A9(b|YRBt<8x%|ip zwp6_ap4xHqwxVVBj#HLLv%74raObA6jc$ckx<4V{wj1>gB+l}OOr%n1sVQ{#fRZ1G zAb4$28)cLnA!sQZ}F(G>r;lM8-yZk=tzOb>>YLHf3ZuEIIpUljo2WLh&C4N%NEYPW)6h0r2-@lg z!&Js|J>e^mb|&{F%BvHR4p%VE4l7*~-aGk~oY=)wRpq>&y^@5DWJs1szyh8)ONFNMHr=(Buv8s|F34G9*| z(VeKk@*@?>jtWr9nr)qFP(>c32qX_vt+qnac}ZGgCe#QGLvn5;Jk^SQe>p`GE3h%t7iHyp3*CVPFaZD{HkCOlv5&JL+HP!K2lQM_GDXP!sICF&Tp^ zW0i&M+X9#m^RGH$!)SEJ`5XyG;A&L(tV>)c?>kE$eO*x++#5?N#Ez}DV%m<(mc8_I z>Ib*6c)O>4wyl{nCSeHi)Q9`tPrR&EEJ>09M*O)`$mUR!cZhBdl11i3q0WR(cjJJ? zI17Ti{leF@0QMgCg?5{Z_hKuFKk!LV+H#JJ1_Lt2#WlquP>{nu+w86)!MMjoKbCU# z{9DTQcDc!;d;M~2V|zKGg>GsMHqp+*2I=jRQ7s)jPcT{%N@5BC!AqO?S ztg;moPb9ymLL_YY*LWoV0X^thj=69hkDZZkb#!lnEK2;deG|0;pOWoYZ}%&~rwk!6 zvL4RW>2HcR{yu0rIs>O%{0yt|^4!=hWH zN?zR9P|X3s))~)*8uYys6WFhwJMnz0P*Or^xY$VI+`!4Bl#HLn80?Xn&clFlUpcPJ zDng!bd@mXD*5(Xe^w3u9h-P{g9B+Qt663B`S|$_d%p#geVH9Pc&Ba|eIF7WlTr#QA zwxv2HyA2yIB86qSq#_-@Ou{MXb94mG&6?RSJB_rPYHy!D`DuxtEp>!KdDBv*YKj&H z!*oi~P-OUiw&SD3F*3DC+aFh+Q%bq8#WVF>zR-fosYx|ep*TUGbU^dG|FuTyMSoXO zY>73`D!oYQL}?0{9`s5FFT9bpw7O@*z~OFLpi@6``yTdK3?nq1dQ|j5<*8+DqeX#_ zFv^(7xHD{)46}F`-k%|+=#`>GTD)33-2*%G(69^xa!-FfiTh`zFfA?ni{sOUZZ|1| zYY?NX-08#7>RfvFF@tN8y|85FSw}-D)#knCc=dkzc=dVuh! zZC83%(3kvG@@@K_w#EZh%6sxrc9HeTA400a5TjCgsaAea)De4n;~;0Gf;X;#P5ja@M4>c8@V3;X1-Hq5M0@z zH#+Vm$sqTHn~gYHYBy3Q2HO&2Ia!H6!)>C*H5sejv{pm+Lb_m*gVei zIbd)le>29~B(yVMxk>hh%{)hSh7DV^)C8{up2Mj~#bGU5+c<_s@)fuP8wiV!Za?Fa0bN+}^dg znFb5HRe-(v{8?)>#I~^cOI0cTs5J`T`qK+Kn!o{jom!N-d+nfgi`V@9n{)fSzIY@| zCTdM0YON5nJunMY`L;xr5T+6^i0vp(y6r<7?+9n-j;D8eF)zByIi_`ee1#9l`ta~V zFktQpu=Iw}x+Tedods}xBCj6cs&xq*c7Gl2qIJ0>@P+vqbk(N};aI`%NG}IvW83jH>l9A?cCcFl?F;_9o$1>3JJDjr@s?x^+adxihgaHAJdG4l8{yb=mwTP0^|m(WkDqp zGvx~MYX~}n2?M}<3lXACYAZtWmEjn?AyK6MY2-5`T&KgeA;P z%BZC;M@j%K+M&Hw^9{)j>Mhz(_M$OJIM+f<&Er%SvHN#mnFBaAEm|ST3=uSH@R;WW zRw5T_x$C3Q%l~8*wIFEK6L8!6HNG^SklEZ_WG~LqeKd#O9B8~eRAH@E+7U)8;#gFI zY`HPnm8)ubgWDbCwY;ioi%77U?fCKd5TvwN{IY!jZZMaDfjj3ht#o799+;-eOhf?u zbpKbAPn&DNb4F_ew|{*l_1{lnT(Utafrndv^}~M|vx6WOSg0O6JRj(vEv%YT$>8mU zcnGwfg5!$D>KEYBG@YZEhG#0n91Uz26#_UH8vNQG)v-pCP2C6!&kl3r&Ub{kAoy+J znuyOYY}ut`h35Q>GjGW{itDId{Uo*6jLia!dWF8go%S{^Z5eb+l*}G3mv%xEyzwHqj7= z8Bn%~Hee15pVB=PKMA}&rNppJQAOxlICofzPkso+m|{Cz@?|TB^1}bqA?3*vldt!( zIV#qOsv5F3Yp!Gdc1d5!$( zZL?e5Ysryd#VL+iu?sNwWJQ$55)0YK z1b*Ly{COyJq-=DcZDcaGcGWi3=l%15v*P`Wb#8*+V^NnX2#BO1qB?FF=dR^vV2AH& z_hVrFC}4peaI)uT38-aB^TkPsY}vyljcZB6J4C&mo7D|&wk}k@iIfHRI7F9XA|P#E zEY4M8@Vz&4m>6Z-h;k5oz;6MED7?8>$R>Hx?e)rxc=|kBeUr7SXXYfoHj*%OE<2L} z+BHU{a~K|X_;`F9oLi%v}1VO2@7~c$}cm-Wluq3H33zDZh0kqO*SY#2~r_ zxU-_TrJJ2grA0gI`o&C~Ajz{hY-o+y3r0H2D6ws_e9Rj(DkZag^<-2`>oWL#uwlTW z!#{_Kz|b=V`_fYo5Wfk#FOg*Q$A()r@NGzojf~Bco%5)@dIikjEi`$0Uv~wDb4^{% zNqWg zJ$l>D&2y$ALUu%Gw`VCkkMmai(vFxFPnLQ;Ce)G{MRftIW&(M$B!fOH8$PGn`I-59 zoaM+0QMs(GT;ZW!d+$-(&scw{iNCUR&z{IIRJ|pQ+j8NhJsZd!{!jGFnRyowLS-hI zL1^)LA|WxD#@-Y)vuAXw0EylP4NfqK0ay2h21X`@z^w5u+6?|UA!ypliOHpHit2+R z^%*i_C-rW>4_?Zs~C2Dlb9{E#4%^C4&Z=y4Jh+vz45RFe2edML<~ zOao3?po=t59Zt(nVL;Q6%%mZ1nXK2KHS?TMj{l;yB)#j#zLLHdCE(8{86bD|z9GSb z{#C-js96id5a{Zez;aF`LQg^gI;w^=K66l-{S|rJO(I{DNaED;DR7q-h#pBJlN|cG z)lsH&Hq)fc%p7;@96;wDVfy_~oal)W!0nK_w-?bBAb|HAWa}8PC^gQ}L3@}mT4xt^ z%)zHow1OmbG46cRzg%Gni*I`2B)EG1xUR#K8yG6hH5(GYH3B14rnobQ59lMdk;-}p>)bnA0R=I4#5N^~!w>r>_T zeGXE(8HJGpojjs6=ZRrHBI_-kpxte7{2-ZG$sU??0b=g6=GK2YQ2Tmy@$mfpMWS2z zkbN=Q3-NJF;N#wn&_T`3!0rA`W)|(7T!Z(5FQD z{aQ?Ir5JSUM^dzfph>?@7EEfPRQg90UxU^xqToqMFaK|2%Bmw<;FRu2Qq!BDWN|+; zu7nLGT`O$L3M;8*ltd*^yE&P?F8Z`;m`NoRx;c=ddXy_<@#TKLA(*gP#AQFO1F5x= z<60R?awnAd{1?75#GQ%@WGR+qu4*V&ik%Th51!dFo_ln@n(Y%AT(Z-lEBQLVScn_b ziW6wkmL>Md#-S$$+1(6SH@Kq%So&XnBprYHO4ya4{@yY1-xPYG5XxkQ{t_h|6%A>- zTHcU5g=%ZwSz7~Uy6}a4qSNZnaernEn+`+FSfX|X@Ov-caTY>PHFE$8l(a{UsAtP| z02(vu)C?}}P)8^675LlE1mhbizA@E{PVr#oWsF4hOci4#H;iB{uQ3D9cemE=S>!1dQin zX?mTPhu~~P=jVs-Xyan?o^xopG??;@bZGXoX=?IZU>tnuIZN`w(-INyb^q#vXKNF0 z7d!7G-j}sK)f$#-1K?$1SSLU^+qfojJ{SbHYg;uBA+gq}@}Lv{KrviYE<}JnKGW6A z&Ve&E`Ip_2jq)LxHpVDMvO?Lbaptnkx1l^oF)D~UY9ZB=QyPMZjD5MgM=T-tFdU=v z5=VH6qP~!zwJv+BWF>9Hf2KQ7)f@2;^)f2aL{Jj6%t23W?8c?3{!%UH-Q zsFz|X^>82CmXN(I>>qf4jKxN>Y1~uH<(9%VrjNWinw*JYb!B~+%Wy4hS#Y*p z-Nt_$77w3ShQ=D_nvxx+jNn*zO@w%OYh!x(&v_szEz@J}&&B{HCCVDUBIN*{5`3sZ z`7MJJEbhf#QlI8;d;ePjouVY}K>hXWp5*`1_J98e1yst!L*Br`QBg_Ye|1nbFMH)> ztnX~E7o-6=5<&rg&_Y9THhNGnLPSEseL-ZD1}I^~hV?CDjD&4>w?xE)r%`w-*qeXGwM`d*_|)83w1HfY~xd3Jr6!7A2V+| zW_sV}>qx&I?rLx%`lkLAby`dH-MS4;vE<`Cve5>F2<>In0$`-i8QQeg5j#fyYhmaKxU~CeN$Dcd0gC^OXz-S+C4z)x$I<| zqbDXtV5J?6Nc&gaJ+lrA>R>y@O@b6K8u@PiiCRt5m2Vv0J)yt259OZ3HWH=S($H6C&LPTCx%};HhKhmTCL? zX`{o#(MX1T>&E&K9jC1&ys9{PjRnSfaMRnth)6?kEgKwx42O>@@nwQtqEE$(3KLQQ z%xNYO{bNw+Bnf9DUzO=~J~!CDmFney?pk;#U)}`vl%qj~65DQziV-JEUrP)i1y;g5 zz{i)+DSi~BN+}qKHjyAAGGWz@Ig+lI>K;&aUk^$_00Eg-1<#VF@?G zVO1DnWv!ZRY_k%FZ1%?5v|i;oZn?@pIdeWYXVHjFLS012Im2+l|w1EYU!X`8^Wh$l5A|V7zdt;vk6ub{XT)gLiEMds#Ficz1?>Gw=H~V-k#T$ z@!K%o-Fc*w2kBmT3~&w!W3_~1od#2+eIsh8$WM0@Du#KHKfe#ElJtS1Oqsa{ zR+H6uwzlZfXxCXKXh`U*8IKXHtDj!fhi5D!RYq{~q&(H1`&h7eh?hpOi_VqbpXlJ+ z4)9Hle=p#~ES~~q)ERVhbjN$-24m|(1HMElO4wD|SyWb47^}CyaHzUhaz0pj{Bj}9 zkMZrYYduw-G;`=%t;=X0C*bj*Mj!2?>SII<|IrQP(DinuGo#wBwvIRNhTSj7LZj)@ zg7oMO7B)P!y{~l>hu}c0Ym9u9HnbKBu`4?K;!g#74KB{pPy}cFR4j=T&9u7c2B@1* zpvw#=a$JrO`ad!{V26!7+QNz5qG|z9nq4+j6mL(WzmosoR&4HpH%Q71H94wgt z_GJd_wTA3Bi(^FE)7{IPI|`6yd8SCxQzoPwQ*wnE%7uW)6*?{h4+Hx4s#~8eX4|Wx!AYocpR9M_ta=|x^NJy2;C7d-8VWb++p)YQb zQk1`X0WLiYJUUx?C zWW+?b;q4y1DTJJQz4BS`@6EuL>G^Hzk6IYteUPF+z7k?rwrgF#VKj#a#X_H-1 zJ($zZ$l>~kF|EUlOsQYKu|HAdA4q8bP|XaJ}B#;SQG|wKE|_?l)5MWiA*h zX_ZVUH)JLY&NA|6W*k2ZST@GM&{@B$?p)g=DR1sh5*;(r8UojWRR$5|X zYz&7#7+|Ko)j!f4rpL&$q|~L=16*J;VqMx|+E0z>YK;E9rTx7~|I25oGwc0u$622P z^qBA8-kwqyZ%+(h4YCOW(w^>m5Fm#|5rs4g>R#(p3q$w{>V&lW-4%SRD!D7_&JEBB zaznW$+>)ApF=EE=C%X(Kh*kJFSo+7({9^}bo%83vEq_D`c*P~muTv~FtQ-?ar7-R$ zp%OyXpmgQG9tt!yxjZT7CoPe`kXajTQZDzxz$cP=(r<;o7Tq2Q0N>sF8rcF@dD5HY(-FyZX17t zn7OJRn(D94bdSx-^!lC%hH5$>E;=qMwM1W=SC6?~x@LLYPK|a2I1yxhhlXTrt(fd; zt|fZ$1h=3ajUB2wh29N;mOZ4Od!rQB3@fiPDVzhBbo2?A!(^NX{QTNfUgcFdhgHgE zR5*W^aruIBfdEvfK;Q))@99;pII*xD@S$8QSjm{jNE*~({Q{v`x}urF%fec%Prn+n z{s*2u8IV27)_}?0Ah128He$s4dAB`1X;pf~}{PH2CMP!5JpSbnNl^z9}^zjiJ}$R+*ymmhRjz7YDC ziu*3T0V9_h0iZW=@G9tmY?ed}Jn6bRbzNq2DbgNDJpJofWNosD>0ijHT;-buP$>jO;p~Kx3W)4 zn^31QR>C**PGM#nZJQl0m81)qHuYrj_pvlG^Z@Q8_Wzn6NlE~1A@=V!Fgl+CUFp+` z;xlR48~E-c58{aCVSJKmQ?fo}Iaap^v<8^H)~IYD+VT+dbECck)`z%HNHxhC+h&|# zXO~gCfz9R#*N1h3m2eY4_95@ZwpLJvZFqIJsz-0VZ5TC#I23UIqNLd;28rDj;GW!# z8kTz}I^=N%LnMkYHV9LiMlu~ndQgz^>1js|Pfem30(I>pGkbd6;b_b|H86!tpH83b zrN&2iStBPcrb$;Sws9n10#f*|0c3Pi_x336V6~bJ!zoq!xVYr)?omYvai1!J4O0ou z3sA)e!IPtq+$aOJq`nQx>I2wDw;h*ngRf6*xfq66PE`T;UEWc2wD}h{PA}ZwVE;vm zTle^fN1=cHN=5s>Nb!HFmX>$4bN5h^6OvM5{U25w{KJZqmeIbm?Fe>FA4q>Znjiuo zB3ssw0{BQMeTazK;TjA;phc#QZ{ln;oel(oRZrn9s8;=A)iv8sgzgF;0;w+fqB z-&Y)q=`l9hD=Sx>Nj^8G8l5XbMH|^-rp}i|%PGm})S|pGyes)FSp(d&~ z=gH6=w)|q%N;8lt*rWjl@PKgf1U0OfZ&^=Xo-rlj0cz4HZ&Bjyt))1ylH0pabqy-- z-pIAo8i!#ZX4n~E_~WK%lE@b4@@4j)#mLK0q_ribh6yb!Kn;q5|;U z9A1R^4KL@K`lz&;T#0>YMxa&423lC89>M|Za!}rp^_!!`(1Q3 zpc*b;yM|1<^22{+XDjEx(56&`bnucSgTzC+uP%P<#vQO$7`_Sj0gl2L!_ z$6$>Sg382|_E$3>1a`}>8+3KYm}QzX@58MLG+7gv`$*I~cKI@5#87+i3vAlZ*PwRf z1#T-!t4(ou%3ZEQ4XrD0XvYTJb`&q`QCkDu4X$W5-jbDDv#Sk-f6ig`b%QldGBfHS zl%o%fDoZ*9f+*aVjXth+=p0+7A6Tj-6&||{hmVaFS*l1H;o2bf5?ah?dEUKQg|I>; z^45?SUV^fE!GRv)OgN7=-Mng3+S8stpQ^TpMb#<1tzTM|eA<}|OHReh0W@(*1H?!1XNC?%x=>yLv$DBFLsC7Nt12fI(f(P*I)6XAI1CX;E(&fpP2CIqT@Mp zP={a--^m$Q4vv~Jw`XE_w<5tXZQy=NuH5bCA#hqE0K4o!WMHbkyOR0tCk>%{Mc4d( z=F(XuO`?7Kw}ME_!jlC8R|jiGz(x#ax6;zevA5gQn4KS{g_$#dz#od>P?3Pi9A^=}$lo~gPHz8CtyXsbs4na3-NC3|+hRe}=L|hQRsRE$sq{e|F zWp`Lpba3VUEU){-7w!hv6}p%ePP3aWwU7rvV6THi5O5xLZeF1&!pV&CBC?-wU?XRo zfaIj3tht@UlOOCzacn=p{W=SV&~?uQSj{B+Qi~pb&XQ`tZf~fFpX|Y&wCRCng??dq zRo`^ZS-z+Eek`9ksx=ykGpstSO%?n7i`|kzsX9P|^TxVgZqJzGkCQ%VquyXtaFp=?}v zhSa)$#jq||BxWTBoEY^|9?juus`*H%ci*NzlOnRB0y*haN?52z?LF@$iT|<|=R@5? zYxRFH=VhZkafypHe?rawndnMx@z_+K>NiSH!zjZO;y%xHbUC;D*K+G&wRUEOmSHJH z6IwDQN~~NjC=~V0^pPg>%sA{Q?4;k1)D(rMP|>ndpSju?L4Ci3ak7L_xk)ObQYJ+V z?#LN?;XXay`1BL|8`BQ^4Jv%_odf$Dw3uN^`OFkT;tPnQ3u;(%1yr-OUqQOU@9+P) zqPSqxKJlxYkUYigV1;buy@+3z&3$mdY}j)$ohJv4y(^dP0$4b6Vs9nW<$CpI^5C%n zut{-^sA-xT7mjY*8^xhhs*~wfA)h(1A0<!cNr7 z-dONY@{fGlARNBe#%Jc8g2*E?;U^uY#T+VyR<4E%*K!NFoeBzUl?zDa{_7b|;0T2Z z_%>+6Dw6w)nVgdQm*i}Z)BkC6;xA7?ZWh%Z=}B5D`fY!7j^u16oXB&Am-C{Ub6ep; zq5N9}>TkVy%=dLA9#|h|B3&NsvBeI!2SU*MJrA%QDBK-Y-(5)jxGx&tjN!q6)`g=P zoe5VJqC8}y7GAx+zcww;l(wq;VVhUwiDqo$3`yu}rTeG2XcTITGSO4^74xKVvQDBg zgih4TvDC^MJn6$qu$A%AzA&?VK}`KReXtktP8X4qs05Yg`oi?ObFF8Z3+0vK8fVkbotcH=<0 z98uU4xa0eIKZCpwz3xbAd!jlS2IPtlIQ--s5PMJ9X9>~{tat+d?rUTnJ?n;?oC(4% z3~x=S9e{02&~gQ&Qu-%46T4M!+DAMe=y3VHtOYDS{%TDpzZLVu{#%fGYlb~?;*9(^ z-|bfI{g>Vx`74f3MDOCsYeLTu|0!(GLf5Nirfl*CZbtE2xulkKV{@^yKYBsCW(M7o zZSmU%o^s)~Q1ixO(;u&x^JYTLBDh6GTB*qT36JHLDI$V=4t- z7@-?Qdl=cn-M1BNbClVBsDa*9Aq-BsXIcA=QA^3A#7i(%9v`q1ubV_sX7Ynx-yxq3 z00Jdfn_7r+=eV}*cgvcJPLudr)huIm*p@_bUr98?9ZrR|;=EgjEQ;T_85CV5dAHl` z82yD)v??yieSC1Wuo_*2998QxG_o4y>KRfZW>$Vhc(-Py-hx7se}B2OxA3XyQZ~UC zH{q)|gPosB(iExEa+`{1;!HvqJ1K_~EOQDE}8< z#s3c?u7t6Pt+R!*$N!L3ow#1vK?cMT{?sP_7Jp!{jyiIfcy(^1a`8{3-y(*2i3b@= z3K_~Rfn$}7x4(E$_mQE+-P`2c{%yOJe!O~nLDIQ3Tsi2Px!w;VQJ-AS( z`HR&c`xCM_lG+;M9hnU5z4r4MVlK#4Ed*>zF8b**=aqNDP&#*);I(|Z5;pdx;YR5so+(KH!cvMoVC910RUK2ed)_aK!2ZM-Qnl$3+TdWmz;#Bb$5`4hCuRB?v zmgE2Z^H=$b8KqANa(sMhtKDwa=X&e&;bzwkXlfrKJgJYgw_a=Qj@XWw@^a{(tAWbb zf}%qM>a~PAFoxVbjCy`+C8*+Z!JpY2H9LjVZj2y-``|wt>Dr~^h6m)y0@y9le2*_e z`eUL9o=;~{aN)ZC07t}%P5xH*7Yz5H?xZTQHFNM6LKq=~Vctd~cqeUQk#6MM! z5`Vas4V%jZ#!eCMx|{~u3-&q8gJ8%}BR6$1E8YWQ;xyiu)uw}7>xWGIow#>$)XKa7 zssQoA$}Y@>Wd(4JHRRO}OrE>XT$O}=P+Ri`TWD=Qs8*EVyr%6? zHr&U=Px0XmW|-9x)DjKH#$;4Ik_^_;vS>4Vt%AAYObC;WSI9=-=M+iRWC9itw4XsM zqM^xR|D0&R3o}Dux=4(55$6%*KMvbD0t;84NrfW5L8Wl? zfDcYL0ubAGncZB>OY1@U=nst=;<~ouHd*~&s}v`%AeS-Jy(cmOFqpiMA9%bj1wd|+ z5Ly59+6W6f1hm;LyeESuT&7|L;V_p5vRG`R6AFiYvfR*%rMTkT{`BJSShp!Heze_V zubX|0Rd^6^7v)LZaE^VYu$+gDY9mx6~CidQH;OHIy)L;J%ZTjbz{7*)! zt(L(YwU%LOdwEB%m!&G3^*fzSE!16G^=F!dhxU=Rj+p;~&6Gn#iun_!Tb*e6ME(5o zvqM3G%Gt7=)U*g4*Hp_rgJPjXuPm}HB;;&b-( zm4iq`r~avn(96p=(0}bt;9}N(`5&+4)&J?WB>xY)Q^m=|QNYF7T;9dd+QR53vGqTj zG+XUM4|NsodnY}3ee@Sh$Iz2XE*c_9PaTjI72hg(MBZzT4mhT^Yb$djb`>jqqPca( z8VPo_Xmv9S)QCB(xVOIdJjn=;W+K}a{S$%t-*a*c>z0$5Uqm!3a>Ls{wfrYJUNbK| z*W0l@f4_vkZQ(Nr-j_?T^1l7|BgXq)71lJ@j5mvS+M z&Yfus(wQ-bvbH3_N2|=6&qE+nqfm>OjA<5#&J)~)P!q6Lug4c*HKikUt!rAr*~ghCABdl0LUSXu+lAb2N0M7 zB^Jw;Lrc;!tQ@~n3>=hx1lI~e=89Bt%;DcvlrBcFz7z&Dfvv4kQ4V8-R4o1NEDe`GUW6)dzBf`=|cp>F-&}QsXHAWkx||h^6E9 zgYlG@-yH0SRRSel^y*R>7o~34Ffux+g0<9`dO$I(B8_c|<}0V8q!aa9iwRRoVb)v$ zDUKKyzZqOLmqLG>Ev5@tzB!4YyTS-({Q~llxV0i}ScP%b2=>J90CUqe*Ko9bf6n&t zfUIa2rWGR)yn2zrl2aIi1x(NBa#WZ zoQxn{y&De>_Q|z?QLadV$R%McaB>?4AWiB!ed-p8R}KEFn8~qM&K73(sE*T=;U&~0 z&17<~gpIPpdQZWTME8h8RwG%GBK8sKWpKmhAUxQ?1lF+YnZuz*SPQfxrxWFOj-Cj@zz>!_Es5&N!Jic zSm1PHDE+78$fSF)VZo3g6@sb&Q|@~m*DN)F>Um;qbeU_qta}WwMSt+iSLO+Ku@H{& zp+@2>PqYo!5Hl4P!xv&ok%*9WtI&<$kj`cp2}|s4-J;9JIk!KXm+YXGLzX(kbM{Pi zy!sexndIQ5>$g&XmJ08L)t(HN-Q858$MJeLzx-7}5)a|QPr<$dtzxyJX1yOy>1|EW zqR!R2YsoyrGB}pmEq-xr!G)v|1-4buPS#>8Ie0=HaC`D+n<~~%7BUXIi7K-5p(o0T zW7i@>6-QyJO?Icq(QYM4vTZCb5w?q|gN~JSit*Ba2kf$lv z>eeojSMyPI zDg;qFS4MUEg|RABJVm8lJ%bbDL&u9s20A8oY#WlAc5{(f=LV2Tz${!&0e?vXyvm;KE7C>7VXCPgnJA6WJkkCx&89k44hqenR8uL`-0|7JD85Z+#emC(a#OTG^0eG3<1;JsIqMhU=4RP8+;59m81JVM)OSR}!9NAdew?k> zNRfuaL{2)V**uTJ#`ms9j_F*ocjKEp8!y$@MO+anjrZ@LEs_ z>*>F}F1x%Qfs$>g?%%sU0KB0vDk&?fO|3U^X>$8$ZjUn#Om$GmKCO|{$}NNO#P|ik zGblYrg}2%6ezE(6cVWXA9w_==`N+H}l3uuIK6K!}V|}fp^R|PBe9}x4&+Mr11&~Y^ zdF#@0#wQtJR9IjZh+R>R7o^Pac@d6{*nyqt>G~XejSMI^IdBevilM*X}X3-M!7Y6g%q{>#q0hvDp(d=*L1k)`)q$xda`+VS zWKYO}j}JX{KSVuJZV1yhBKlQ(D~5h(JB{jk5cH1XpDF7RH!756Yqm_fQtB;`-OPUF zXDcr2DbT)Dn>4L5ZuN^38Xtk#oI09q&lOmBWHes%y4NF z6^x5D%di*f0%UpSYgU=_fI(F;XW$&BrNuKc8K0CBSv~`(o_b}p^9F%LB*x!@OYO*f zC`kbc;KVpugQ%eR$J{D{L4btWH_%sKj%XEVYey=k%m{-QmOUv2x0h62sc^t<p%K~rSA(1T|F$60_rA@^wV7YE24G*xaYJ}?@0gffijcqFK{5C-mA}~UE!BM zdjVO*fJ#tOaY;KQ-|6pRPgw7-{|?EzCa0G3pnmJ^hSIHPY3I z5+>h?XfpqJx+tPXBe0smP~JrO_;qKbX}1j4r9*8<*qfc^b;V~M@Y4a8j%{ww@YE&a*R9qjjI{4 zT!!(ZOw?eOa&_J#P0oPYDn=Vu0=jQ%luF%ZE%77k_Q(_nm;H&UA_d%8e5ii!%Eu9t z6=lAVpfmrEOhnOg*ZTFPm}boys8xXVx1p(s86wh1RE_1TfgP8%+rVucpj+sev1K>*9M?s1LnkyK&WP&MPkYQCxBYOR%DSwG zYU@o5_kNza6De45yI+^XZ~?fQARX3e*hk`OQs~dBt+(BSt+<1K z1f}4I>d?P&L|Excj+W4W4wQJK4gm&B*K?_OY}vFsXGm18Qp7u@&RL@IgebT6w3cCt zUF5a|7C|Xr(-q6nlotrJUjX|K#rIOdjIGAj4D$-=N?K09g_1 zynQ19rxXWKA)Hf{ZMQpXEcjrAy9*9Tsbzt#voG!B<5gTW)Sd!%1$KApK$Vd4kCcP^6eQOuKQCINJ#6gr8s$X9 z%hE=DRLvf9>*x^r&)R9t!o>%hp8YZWkpm5+Uuc2vxg^aA;$aX1s)9Ta?TbcvI;Rq` zA^W0Z9WuV6UNheQ3e4?+4vAO4sZ+LErY06MQZ>L)aSN#yF^k{0#dpE(&lULK55g`N zA=2`y5XKcxjTLwHiC0tpGSbfhGRIMviWR^s{@;3C%uEO1a7<}{|xmk zL+0Rs6b+w{C1JK&h_5enpdf6H+87TBQMp?(zx~zZ$RfbWzZvOw^OYCDdd~-0T<6nS9W)s;`>x8ZF zQs;+=dP*Ao@b?i8zo1r&*~sciYkX|*7yGvJaZ39gFme;QOuU0x8&KD~X_o4H?Ufa5 zE7FWdo$Sn~FtHV1j{o~$J(`cQ8u<|?vVZz${Qpab|LI^A78he=`JdyUS;Ng+S>^eA z$Ag|Zaa^hm1=!f{Pg%(?|2dRDTtUBx1Xld8$qbNzEsV(o(NjeFLn3OrtH974bZGpb zgwpjQu;|A@ln9ohl->(Hp`@hHK&mDOqbjEdSY4~bTcRSv zK$Bs}P^T#;@dF<35+m)!nyd5xtTSld3vlxsl^QB!> z7);Tl;t4r$_2hpG73+(hTfI(TT1Ld_JYZ0e$k?>sYxA*omi3J|4DonnksA%4MD2Gd z1|Om$#8gw$!Y$t$xXIKRD^M(COu>4EFzq0bq8+Im#VahOrtoD(;;8ZRqfUao)FFbj zMtN%7_EpcBT6(CMd+L}tl)0+bwFO9tXKCsnSEb=q(WYLU%Dg~oT4O>c4OFk-T?K%>|zvoR8{9vDr zu7nI3L@zPKeo@xc;pSDBn0$FBz`iQA4lY$s^H)OKuMm{Fzl@^;JV;oKO6DqdF(@OC zKlv%7I`6lT#ZsoxlyFQBSbzHW3vp94*%>IuPeu<)qiT*N$frdz^Ku9Y5Hv2l+mk3; z0BBGZ6v|psJT9vWySS{t-H-sfg8h;;JYmQP5wWjtuNE*5{W9*BhpJ#YTXfY&whLxn ze>Pj~crCmsL_(~s@`GhhF9k%X3uLcf>mXH-ca~{rX#-g^g3cO6HVt7YPxnO5ZC7>K_N%!w z@BX-N?nJzZeSV)f5qqDVYh|v?GMFHf!=l!aeoSrZq4J2xsuW7&Vuc=Q9ZQ3%<0nVU zwaCu$&|go7sGt5+r3MliK#aNGs>#Dx$`7Xr@dDtbziaRY)x33sSnC_ z<12R1oFh=s6`iLhb-MTUZxMwtD%f@!?;a9`#nV~pHeJ&x6x@o8z5snS*UI6Bo7{6< zC3L*ebW*rB1+XfsplaZ|ZKGPzCG5HO_IJ3)ER|NI+2x$z7F+3N6Q4_a>+ka2YCgT+ z*2}XK(tXbKi#5JrDL9&|=i}t*z<% zNVv#!35rt9c~ouWQ*?%ZXn41FR#KvuRlq##*K>#aXdI!E{Y5}vm2()==THv5RUfNJ zHKKvOE6!Jp885Eh)mOY(4 z6kn-mhPF&1uH!yhHd?J%6NBO6nk7HNtl)TvS}i&TBTzz8Y6JSy1eqrI-1OB5K5Yp zcVo3ANLrvURYOGi)&3+RlX=>O@td_*exnKyII&K9feIQ#VF@)5t4%sXig60o5c zrwt!&%6DD8xQ8Nskp)3z4y~xk>!@iVrmNEy&6=@=ei)A)lYTJYi8c@z#f&M`JC`0r zUkf5m4A((9bT&t~MVDyzGc4PodW5*Jqrage!OFVeLrxQRH6^{)M&6LtwIkYJW@GTXCMGjNBcbj0wS`>_+ky!GC9xro+g}p);YUxNz&!Hi$pCkbuJ@M)S zE~TZH=-`1lvsM^_XC&mvT$gkW!*zwu^T$JV9vT43I0UXpeV3fkD?)t2-|9Hx$GJAZ zRy)lNu@4c0j2l_;nNk>rCnUqX>W0`d(%Weh!1pn{fjjW?6=v_p2L^+do(y&=FhIZ) z%AwXbnqBw^KIGH^V9<`_(0VC1F`EX^z95R8Ndm;Z!aF3=5@uH>gcdx=@1grZI#l{* zu*<@cMzC;?!{GqsV_)TPpSYO zk3s?;6m{xwM9C|Vf6ws32zWx&%jpP{Pqadz?mZ3Lx>H(bx*Nowh*iz`>J;h7R8PDO zn=U0QXRnaZ&bSS$WQl@ZC>RVg3T1ywbFZ@wA@ZcOD#s8ntru&WT4`0cAbb@rBd8rC z?&UlTG(di+pP+Dy>5(~C3)kXR+#BaDdWOz;AV&LU*2p2X^Y@TD=XDDl6Y9`5ingG& ztLIQVhjmLGD}FT0Xs)K=rJV2xrbNttJV)q~I0wN(=#)((w@W_3w`<1t##r57e2*%6 z1y;{5gX>&X9)dd5;<2651$~yAL2VW)1=%LsU+<&xC>^VI>6mAAYaFwF)X!YREBkKb z@d%!|M9pMPVX?A1rvzr={Z4?s+qboW_|P$&i;0R4_1z${*dOll44LtX2Dxfcv7OTc zw@J>tH;Sl+_vBwMww>t+#B`~jQ7uFAwMHU)nJsJx!d*y#vPt>zXcUr7eh;@PcT6x$ z0e7}37$a`@b|dG*f2?4Z5_3W#$mN2UH=KpWlZOJA@bBOA#Dd~sTaW5e_&Cctg zdYXbaEg}C>w_Z1kd-V72l`#*Ec=>&$0uRk|_GU}!)hREtZ& z<_gVI2htz!YiSz%VtyO^+q7~p=Ht~lkIe-vuO+jorz!-hYd;h_mic;LXW01a1TJXH z?Jw^PRM2O^HWFv)I)qQ@6Qoa}7nINW@jKvVx#SfrZ`yjAH5=6!T1$L4)-XuzMl%VX?*!x&GZD@-ky ztd0w=6PNqa(}4ae*dDRkbf5?6#p{)Q3#mQEP%B_?h(*=j`s-soI3b`p9YSNQq(O87l=!OyfwFY#KqFJ;_fB@c0~ zX{~7H*ZiC+3aAeXoHvF=pz1F>;UF;NNA?N?MPn`b z^*>qC!kWm+A=q-Gfm~$N2`xK0F8{n4wAEqoWYjKzm~C`HjB~gWe^oxi7@-WaM6t^? zp3z3N%7eaFS^`LXvIE;F<`G)0v8tIf?7N_riSYLUTIsb@e|WU*;Fz43L9JJeTPWJl zMLLl8wKK(7t;efPlz*7jl~Lx?9MuUx)_+hGJBQvCB~tAx9im4CrAA63=Y}^#aFmPi z^cZA?aBBY{Q4iqT!=xUtVF!DtwxnuUi(QIu8sTQ4@3jPWRr$uy5EVTC&2Lbh+$nz27? zJ;b?pg{^|y<@Phj@zSOdOY#X4uHGmGX#$UdH_P&}uZv7Ho-NsP{d!1K(|2Ig^~+3` zobN@HPa-X<)ehMp0+^!xjOG|nyBa;z53td?(U&CpoDKcWy60eI1Zt9P-Ag;?#8Iq$ z00{j8zW;kCX0$637NiDvigKsH1mcKt=i$U&;>KR&#-8QgSmaI|i8JVpgnjlP;j{)| zaiM2CG6O%+guVY+2XDo*?B^=(LO1Pyt4|FW5;saTzwBHq0zEw_3d5^0?_Q8X;?t|uT_-4iia1Pa-b#}jZ&Mg9I3>=SMkI3?R_@xo3tKLx3|2k; z{+l7ksQgcjM^1NU>-WpfuL_o4eHPLw$+jF^9T!$1{C=&k;tXEMlsD|3|8j8kXXoP8 z2i*gzLj8mqKtt6Zu2GUyE7eX%Yrt!pS<^|DtE|Jn$$Xw7->MuMyZxx4vM z@mioBKCGVQrg-YGbVKI;Kz#P37EY>dO%NVu&V>WpP}= zYZQ^|C3P82*T0gBe^pWfs*vXL$ZAZXuNc8-6!j;7v*&#?aRSRce1 z)^6AzKyQX_Q<|~6@*L&wdoFV;#=i|C9cJ;i?#YhQj}Q#Zq?Tk$pgR)fb=LBklDCAs zXLIqwfy#=BFrs7A8^f94FCqx7)ptOrCDhVE7b?)l4G;>I(EvMq6aB)@176)TsWXSZ zDPoFO2?LS0#2U=&Uz4&+EN5=`9Tk(~Yu+4i!R28f#u(^*QGW6%+XKopUR&&0c`vGQW&ya59c8TyF_syE3NkWGxB_t$7V|G*a0fCkIK}Ui zPx*}NV1!?f?J#RQGIGg*#DPv)&Tzw4hc;2u2H^v@8z#e28;$Lo%Vsr?Oos}#{vutZ zSm<_hjWsC!fZm3|xl(Rr&i>nuRi+YX5vUPJ@R9MB!e8#kOCEfxmz(Ti!$ePPFjnkM zu~LYmh{_<7KpXZy##-M53TM;#4kSxcJ8IQHC-a9>PFI58(kSZ zkDdfp3tZw0=$>b=^k3^pj26Go@8wRNC3~ETXonPwvu`eWj<)WyuDCB}KkxXqec|-+ zJ`Hjm(NL1Ge;U3yNpo0PC;>}7X!np<7#)upfmf;xhf9JdB2JG;m^x)mn2 z2(5^SBTB`Zp);Jb+V=$GWB~K*bAlkjhHbg!BJ&VC}TFYanok$i>|&DwZR9IrTSus%n;v4k01xx7?zYoz;No)0iT@UlfkG~ zHHm4X6%YUhT&xpE$SNKeFO-OEPu5|Ed}#*-}a2q7W|6E15qZ4SM&ZC%sAZ zYk+x`vzEym@PLtV9+YC*pQz8mEUME*8Jcf3-vN5NyxbD+D_54*?l&nmFIJvXhdOWN z`2thWpy;(g;y7TjrdXRUT*R4L&9IWk`9%TD1Y?MaHx|ui0DA|T=@rqT?Qj8tq$aO# zc+MDsT;r55YdU5@d8W75!9&zYY%|;A0lhz(cwG2BoMepgYfqqvU@PRo_cURTiR!3IJBbRyT3?sYo58isU zjXN*IbS?JN`f{*t?6_iJYsHKP|5IIr|RpXZCgH2CZ%R$ zGduddqnVSEcUK(Na?LrMq$Gjn6jaoS4W}J{f-?la#d3qYWyS*O>adih#KcL1g_r&e z{5ev|G1%#ILHtHOGsso)$jf>|=H0KL&}=qZG$uHty8PlCFq{1CPdB?_`SWJXiQu7F ztbbgws?j5UA~|`C`($11{%-!mJ#{k)W> zJOx;iR)c5mU)2GH>1TgQ)Cl4I;rlc2-$7F@+`dYHKZsAE=p4TD5iOdFMAn%Eidb7a zxeb4JLdV+e-dHW=+^ZEE6O?EGxcB4xZ!Xf{ucT8(Som7pQ*wLCFvnc}QkZW|VdAbh zW=jl75|#b0T<2j}D@iYq4J~!{w2GPo(`9rud#9AiUW5Cb*&PzdcnMqKXjVU)7@2z6 zn}*%BqLs4wfiMR1y${py6B?5t7n?!gKKwp|*zp#IPNeae1JQw(>csB~#&<|O`w^h7> z|MVfjT%f~?7I~n%+<2^Um$47E`Qc0QY@eK+V>q8fR6$Yila5z{Rttva@sqCZ7*)#! zQ{%wHGJFjKWzjvU25r%Vt~}ydS?5%i7g818f8u+%l1=EDCBHYRVJK%PI;g3Tg@p zum3B3aI6>9kf9b>f)o_?8>f7G-$PrEczs{f!QapSA8t`Hb+-Q>7}G~d#|}{dnMWxX zsx>JYRi28bsW~X9eklgc%DZ+}-2at-UU<8B{D+i&3boXOnT285EC6pOhH)!xQyvnK z(uLO!fWcMTHMtT5IXv^`XyCP#yYCQTPUu7nI&XS-SehIA zS)1vi{@~CUc+T?H=~!#1FCBZG_?Nh7nY3u7S7LYMHY_ALz6j$19FJsoql%=>6+sok zS|LFM&oU`vi}|sF&4egL(x0m%G~v7ox=U4utmaug;ar~A!S-6_~`y;jZKaZ5W+||NZ;X5E7dAc)xAs=uMKT)szzn!pA z|NQq2e?)3dbPPmhZ+J|9sc^=-xvDUPupvU^qA-!fI6o@(oNXdeLK4#MXAvYgEWK$`#tgDNTt=s!ZB|I!$vz ze^Z_}OB-7bwf>!!%ciJ}dgLsm+3gtJ6uE&;Kc9yi!wT!^PhQZ)jpc#1F2$g#6f|OV z-QcSRX zIu*(&tj#;G(9RmZ(I0|z!NOvxHHFlj@ae`B0$G~qE%+He$*Wdgjsz-Fpp$WwsxIKs zkV0cepP;jteV??(KFe!OeR<1Kj#|q<*P7y{wxm}hP+VqsQfGEl6yH9GY(K$)+O(Ow zLB=Wi$ENNGG6(77-uG=JCr03eyP6*dhW_zVR7Bi^FS}DEF>NvjMFyEBEqJ_;Fj9j& zmxiRVm1n6m_@=?Jf*eKk%Mp((_IiyXM;!O&K9p=a5OAXt&;qTp8&f*kDg}ozg|(@TTQPF4t?_-pvT=Gb5Ze3EWu9!Q%6uUx<4 zmotgtm{B{m`9eb(rFz}LM0vkp7qjbR`W9wZNaMvO*u{NoZPrlaT|vz0e8g84%Yklw zb?#}{66K36Eg#28w$i3LtpFXV+S~SW5O458qy(kS;Iar~2IbW@oD3tQg0j_hWn9BS zRB;C%?JLWP{R%RmRYiu0_t$z8P$$vEF+Vxxx$f4*uDstv?>zqeK?h4t#|z2M`ki*6 zSs+U^x6(*LUwqhQ|G@5AIc?|>4{Yfu%cuE(oaRlPc1N4ZU)?)Ebr z)3JY^Y$lf3HLFC&F;S_#*&pAf{-x3tOcE_PhnuQ7xBShD_RS`tm3hNG zDzARUcg}L>9(X>jV6V-uQi%qG9f{CM=4z=W5x6Vn&w%+BO3MBGB1TVT6Ye~C&Ol>_ ziQCh$L63364oz>x3yB(TFlpD zk%5%gSm=t=3rW2@Eff}G5c;EQU*8NTKmcdATB+i`RXDMPxC-b`uDK{e2ZHN!5nC`s zG&;8sTNU`uA8@>WJYKVc@QJ@%9q?0iBPr;%{-9NMCS3BmLHCx_^g%73(t2JZ_Pki^ z?=?~R7~(H^Zu3KHL{&4TXy&a};bs`N2eypEpP)QHAqL(vO1FlecYwm_)rFoEGS@?X zm?B%p&S^Bd-(#S}Fkr-ZHvhb_*8%rFH(brO0lKwdf5P~+*O2^Yq2kZE^3ODR=;7Jv z+cKIXYxx7~+5`;BhewZyEz6`W(ygc5^zy_F7~zu$IPOw)^Cz5^dR0&dk8t)y448>s^OVEd@?Ja)uyp9!BGngWS1(nKdo1arg<}?)TaFU%>cQ+G zuWBXHj_6yCMcwLZnTxaLGOy}_X3&i4n5HurR;@Qq*5W?ba-Vr^U@zHw9DQH3=WQYG zZa5}e0;R3~6*&1-U+}K&A%t5Z1J9JvYH2<}r~Z4)jhA{tU*8SMr*KAN{I= zuwJi)DhpX;>nPMjF}hK7wiKgtSP)dZc%s_pUhwUoFPPw0hE~6U@^b_#pJFHYT7+9Q z(%Q5{TQ5JsuV&N@p&ARL#qai1h9B=mox6V@ispOHgG0SHE2{BNyn^)p4s+@8-|lG! zZFTG<7 zXlG~BC0buUTsSDHmTkL@675vys4DsDAP4>ZNA)X|>L8~y;nRBeGJb={Flwl&sWg&2 zr#u=`Z36QS?a$XDP?JYxl09J>I2eh4!lbK<>QY;eMl#ppt<$NtkH!{OL8td7JX)!p zVuX9cbX_AmrUC*StOzn+7iHHM7vUEiF@Wy|>q_r=^}#NpJwKd@@{0F?e9N_nR+!j- z>aPDB>I>{2*olNqhyM0EGguq(Kh-Ow{{}GrOL(m0V(8@ILZo@Y53 z0X>OW7Jr7=CVr?4o?!~@Bh+Fpoqs=BJ{+kdHYB=Imw#cT+F568`q=2VKn~M7 z-VEO2f%a%GH0yjCuX_90CXd(Dmd?lBRYlLwjltmWX=Rm~CasMdK{q87H$uCPR?^?6 zBjPGtQRaM#j>~}r_BV;-esRb2^7t*C`=95i8Md`5mE|*V&5}SucfLCnAwU*h?WFqD zjlQuOg$$N`fNStyTXnI0vAt;~e6dtno59dIZBfMwe`uy{mNsUVU9aSXY~JE}9}d3^ zr}j+UNAAKl(e#oO4qE&bbBqkuVSrYtAI4W_5bGm)-0Q1SG1F4v*?(iZ9*in37Y0#u zQqf~eG?A`P`Z%_P<@%)19N3HKU;dt%!=4}yLrmpF`JALaSD!S91d3OyLy zojFcw3n`sBLE*;AW|As`TR>V2_N%geD@M`8z(`c@ipEds0|+TG-Zn5w14;xM%2*KN zWPA*SwtJ4vHpS%{`@6?9`^Vs1qV5`k7p1n@xr3+YVw``_NNu*31nTZN z@=kmu+!fu`q@6WV8a1cDdJoDEip(#EDyJ06y^>BL$PJ4j3!>vUeX(AmOB@N5-X27Y zzX&2+(kk89Xwh$zT`TH@-R!f9BGnZvNXCtsOBASW{UivlyqYs;bDgl5SGSx!Syw6vTc`J7BG?XAv{Sw`H~KQT6Y$>~n9ZHJzp5+-oFdAmZM_R~2S zTZgQD<|OBCPBg+5b55V-WES>R%N_Yk?|s z>xlph+8F4$E{z!UpBFh~OC7qRpX)gVKY~*ao1P7m(;J#xFf^t3xsgS{-e^W226QqJ zuNk*w6W$&mLOV-&nJ0RtZvG+ukJUxy42|L)!LpPJAX2`GoXei)3t7Ob79R?vS72f= zBebjMta`_KeZ!{cej(^{b9h?b{;w1=1OA6E{cmls!++EUe;f4vKSkcZLSmInoSgof z$O=+}a93VL`Ltt{V&v|JM-;>-H}o?mW%vak>BAQwFAJnW09SWl^dJBpmCj82QKwS2 z*uJ1ir2-YYs2EX&EFu9PP%0o`p<`)trn_!=VOoVnnSb*8(hH;C_%@C1?lSFdis$V2 z6pz#Mc*qlQYChwpxDq=Bx&@66P9$jhUQvZn-`_cnNk97)>oOLjI(g#rU?BoKc-)}K zB!&n%0vnMef415&=wUThk^D2D*&%uNB!A879gP~d$|z^1PE`W8t<&B$ehV+|1w~Fa zc&lhIOw9?%Zkq@R55P!JX;kSW9ItkkiE0DgVl+n znN|!Nm>^&pDlMWY;=;4U(-Y%6l`BvjDk%S9E8X``$*k{)3n|9Ctu5SW@hsgpk?9-jfJ~br+%{2*Qb^56b2h!RH)Jg^ zTiTyUA3vXcH_=>*0S%ZG_0MO;1+OlHXgsOys8cr(4iB(hc<7obtFj{Pq{bpG68C_F zr@o;VmV#N($GA~x&9@Xu8q#K;v6SovqPZxp)i&SBXbsJ-tmhe}qLw@f2aB+;^ZS8W z$hx*KZTedZmAR5N;3nLUO9Z%O!#=DrO!XaAG7E-C!E9M7iWbc*&!q0#^AASHGMs4f zy&+S}C>Nv(BA8RB0KPak43A1ju<`}PX8s4g`~Rg$OfLEl%P<*(pk6}IhYdM z9~I{Ul%^7?fsAxmx75oS=zK9W$R4+Cu%qZ5LCTQYwWZVVnP>>d3|p!(0Q|D&Lu)J0XB&g#540Zg4mV2H4DsDsGYvC+r`FayMvKsdJ}^fBN7c1VE>BG8-L>@ zY?_&R0y{Rl-7SACCS{^Tn@9kK?WYP_T|=E$Kkid^B&+x-Xkf88{JRIpu4LF`bncOc7TIs355XcxT6F7mBilWp1Zd8 zn&bE6a%^ZNXoGt`5CY2EKdjt9i-{OVv6hW8-q)eqWHLYo$~$PbfNIzLegT)n*)=Tq zimlh`aQ;hNou2W!s5Jv48XzQt!=zQH#yHfu2sWO%O=e!* zK=ZI5x>g|IBrAUTh(H!iQUbv>G~4G4J0`^L=8#Wx7+WS?llC|kCS4Q!kwuiM%kk&L zrM1g+{S4e|a`v{9J1HV=Az|2#MEj7A$!kFf{R4mKVlRa`@@Rel<=gIZ|0VDf6a)35 zfXkv3TlaW90HXU|RytlRPR^yDPC1OOqY=r3_o`o3`oTlY#W3B@eJxlv^8~AU>m0D! zud(a(W1rI@x(4fL=}JI_%oL|3SDTA9saP)e4(Y`PF^i&~{FwtAZ9Wb* z<7me@GsHsu4^!h{unAswbelFx`3=naqogJLZ|!b|My}N($zb2$Wcs3*Z@xQ$8KJ2xrb(+HP3lH<@Su^QXy9MYd^wAbw$Z?m!jz)1>)(!=aa-LEe^zvdu+ zJD{o3(jfendVPOjJli2ttNHk_Ib@Q~tGaKf%6aV!eB~~8vv}}OzRKsm@s4!o1%kcs zkpIGSkP66Hj)bG0|ZQt;EJ53^EP0)|hq_YafKh+oZ-)qGw&6 z4O%ReV(r^M6Le?7?r29aq24>3@-CU2&AvJqe|Y!y;>!FsMnhKn)4-5rtL}hVld(ke zYoKbP-^RNmZLuo|#ha_`G8f6sf{2Rd0fNYEM{<_XFfe~!f^$yJXWA~4~;Y3=k3KTD6{y-}gddYeK{BK|K!MgpsWJ6p9Gs$D6? zqtg@E2K8J9o5S7T{fvpXb6e|!zceg^F$M*_`A{uu2g<(1H%_E{W^M*ZDd7Pl_1mh> zyN`kRUjmtQQfQZ{Jve5`=Y!M8V>tCma=$i}F3*Qx%U3BZU7v4pyzF!6wnxp-a2pG+ zCv1ZzyTIH?#%ds5ym@86U~A8_@$++xBXjyG+|$v94EoP7bRnl1tEcO5_+lbB0;~F= z-bEe>mz>21d&rIP&JraphIJ)2rpBaXS~8QLEzp-n=odrXnMS3K z`$GlxkM#vw7nXQl6Rf%sU6qB*USj%#ZCk;HJZeA1qrV)^w*v`2hih|e-h8Y34x%4W zi@IcUOkuT<71v+ew@N!$$+)bDowxh)PaTkb=kM?`VHr+m7v;& z1GACUgf;!*w*Rz6?Q!{J%Hny3*>OEQR+q867L8#vf4P8j*mR*T`N(G%zlpg8^!ve! z?zDe>&g^S7=<9iSE#~Q0=OC{lo;cB3pu+*XBm7JLIX<0?)KT76MG*)@ZFC~nEe;q1 z<+g&}ud-2r|?F<;Ogi$r; z^T_kGDjNw_i%FZlKYQs4v(m?7$#spDRmB z+)WR_j-?dW!t9qGG6_mKvY}+tfyndNVyo_l7|XJkNv;{vy+uh0iTBu3k$qiw#h#D~ z=$A$Q({L^EBRF0k>c@|+|7Z~W?{b6xG0pc+exm6+H&}Tw>nq#K&XhEPYd7}S$S)uQ zZfR0}eD16tx11H@^>-#|Szjb9NS;YK5c*Y!qW`Js-mL#J7V*-sg*s>xN# zr?SbaLglo;N=nVDNm<8Y(sw!y;wRz-UWc!Kch5G@k(=*R=Jwe;ZRYd2*YMB8uoqc$ zAdZLGr8%01I(?&jXQ|HaHfM>!TMJorAR@ZVGDsKNi&8sS(R4wee2Lzb#!%@DepP&V zg$~Nv&SuM#Ln%s2DmAt0BjKVyW)94Va=(bePW7d{Mh?8hpp0|qx@=F@l2|M)BmcC~ z*2>r6cM!wNSHjmKX4!a~V6u>iM+KW+dL8ygr8ud$*zoTg8W#PfLZNG&sJ-# zSMV}2A|D!zZr1-;k~gGJYw`?ctZsXt*%fDJ$axJ9uV`xw&Z z>5Wa=6*qVBveF@M=2c(rr!lUITmsW752&m4l!f8^YZ+vldc8_6=r_kODvCO{xSBEG z;I|uq?e@ZaIjm>}GK2;L4<5L8W6!MEZJ}@*vsRe_3*AJ*j_qd1sZ&SN%A>v}n39~G zrC^t1VT5W5#l9~=0<<-~SaU$v%)ssO=!iuWzrl^wCUJ>~rOU3ZUh2Jqoyk2JHK=|H zK=eb%DOen0q(&3BgCs#0j4}{i2jnMxbifcB?usjU-1HK#Q7Rk)Ak%w(+d4?m1vam|35L@>r$!ZhePp?{-aU)nZIiOgUkt0VyKucW` zVbkc0D-pXV%hx7x!R2p-we#}j@YxjnYJOvM0%4nZi>PNo8U)d zf@HCF`)5srxRciB?x^7`bf&99e3IhniN#e-14v#^>+JL?rh6u-@*-zjgKv zMUm&mtCLr|ZiQypZ1~qu8a0ujx|9!chgq&UXi0pt8NpOPBvfI7NYPEvr zBPOxli29w|Ztra&oRkCVk~p>(MY}O>jjcWmwU;L@Xz|^=R+hYl(;-o`^;$b(ZGZ+QFBOVsqpCgp9i-~w zMp!_nRR6URh^_r574m$TZX~xsQ~`0=hTD&`VuKx>Va&JL`Dc=3O161u~`ZPyLgeHFpIqaYH$4Zee!IjeoqOg5+@J zSOl8Qdn27fa+zPw>hpBBaG9c|ukAjXWK^l5q^!JWw zI5SE5;&H~<8kdTL*ZNS+jb5XumsUXcT$c3mm}FAz*)vx0_j|tA#X6kJH(*sgH=9x%Z@51U1_qzji z?&Be{=Y_B@j-GQ%fp5yg%$ByX>bt1_%FG`vT@ReLzIPlKhZ{2Nn|d|ZD*0mve=;Tz zYz~>C@Afw=B!jR|*o&GuXx$OA*`xq;Bh`oSJc}`K)RA0zw=qbHeqc_8!@9=kESb5! zIy`e)9(SdTJ%)yH5I)=7iyYPVTsmngik2ZUDEP7TFv2Rq>C?YqUBQ5^9%F$}Z18qy zI>sagJ)6;5C{re6fTZ&%ODZZnPS3fT4hMPEa&IC+p z{eH%J=di--Mh|%=1}d2ID!;>T4!C@gTb%Wc@Od`orZ=Qofaip{f&gHJJ;4}IiLcYKF(|Jb3A z|FZKh5{_b8+ONiBviTq)#}2m|)f#$DXa2-~=Xi0tAzKVkmQ&;_9>+u4bOdJ*>XvLg z{1A@8mYPVM1Q6U(zP3LVY^s9&HNw3ZN@4%EU14siGEUoag4u|+15=n%W=Q&Uq! z(J8)7GbPX%^tJ@CX);2M4jKSJY5379{y~fZ?6SCI(J63kd{PsOb_F0xpO&yXr6d%q zF|Papmp0)Ffi6WWjAbnrD=CLOVjWHbNIl@{G;oQ758Xnw?nhKPvR4R05Va0z4TV(^3LK&9WV(emNU6dYG=w9^ zMGXZr3U#`rHmXfrM}E`^=}Mg|V||7DXFQ|!?sVrryXQUjQcu*M?a=r`RF zm?C2srk)cmT;rCid7C3%<6)gR2*o$M3&yFVW-(ho> zXQLM740X_!-f#zBakXjf!Et-`!CA}OzFFECbtmLW9q@!rO`)=Dg+|xBDqsratR9tbk$@mO$v51rh3z3 zoqnd3rb%6qEccF*dZRBDn@#MaE!{z9?iW{s6z2n8+Aq8(78`+b4h^oR1=Z(fic`uP z)DugvyaiP4c^oS9GLvQHwidFB56qWrXkU?b33|Pf*&)9PmOWk1MXM#FasH+gh_7D= zYrN9>y99$FKC5{j^{+S-DP6$Bo|p8&`Dk5@wM%>$DV(M#%|%^Jk#WZddUO26jWo{X z4!%sUG!nb%dbfSoV(Z?H!b=bFNqkX2la1v*YFP;2lkwP)BKsWHxaukF{HnioWz&`5 z*xZg%Q##M|b@k=rYRTc5oi0~bs9ct0c<;(I?6`T|j}+yZl=l%DY+p9;#2NOwPA*~_ zSotkx^EZ)WIPx<|fN9ed_Kj9ny?;hxI7 zG1TY}%)h6o8j<)}Qy~Do&M9jKX{^gccg4t^&&n^fI}({gm#3Whi|J>_h2q~FmKStl zRV44(g$+(hQ&ab*mS#s3El;5e5@u$U|5#p~$!r4|)j$M%FuHPcyCBZ3VjSM*FR&ps z+q*%)*svLv4TJ8e@JVDL!ELY^AqMCgqG=3BCZ;tkLzL%KN0CFSszo2vo?t5rTSHiH zuE|SW>WWtYLvJW42R7AR{PE|2QiD9(UZIuX{bT*Qtr)VKAKlZlCC?XMz}(XrA1Lp! zCT|$tfADshGWBUb!|1Oo(Ibuk%5xTlcM@nj9tuHd0L;!2+#M1J5MKg+ca`%#y1`}f z9$8Pe6CQUN?(pS7hD>C>;Tix}VGjY1%qzmycblx3;n!8I-E$QKYDpyGdvuBSa%$95 zq|`N-5eP3m>yD5SlPng^bHZ$JOk`JK%Z}3ik12Gw8{qF;4|F;3dH3vlkhx`XImq$n z0&yl@Q&3nmStp27q_i zy;-4Md^b(uOBlN4r^Z2QmtkuXg{XQP;W8z%$C5k4TsrY>l_SHNCh3rRN_gE;QvM8N zxPxk@a|wCzH0vE1tvATkS@RV?G zyE^jOSVysgrrBhz2aFuM`)O`d+J~Hji52+XBsp9!ZzM^ex`}sL8 z5sWtIwGJU^G$#G|Q;ygE`a`Eu_*yS4iZ=7%}v7-PP@_oq_^DMv|s3{f2OLMczC z7U%Ze>a7v^DmWIOxtV;jslI0}msX!eHnn?s{v>Hud=L<9Oh+7LclE50O`fEekPdv%gd_RT6CdiVU`JTWv^m7)*jlNwUb9V9G^6&1VvY*6(Afs-O>fmCUN&< zEqQM3KBPlNcJ#sU54jl){Kl#e{@XXzFLKTQZjb$nYW;8073shKhF#Szl~tb5{NQQ5 z4cqsiYkq#y{lPviX8Ht*@l@S`0Vj!Lyk5j$$ul228uc+Z zb(5c)72mAP^Jr}Tir`u@-+KK1D5a8Oy5sI=fBckn%zIq-_xV~s2h;AIh$vyO&vRT`(_;HGeF-lJD_)rt2@y#5>yhV ze;kK~#!hr5(Q{RM>?_kIvpTxv_913YpMl4hIGI{u7b7RIcioV8sufZBnW2uCLAapH z@x7{Z&2M-V!Sq~NkLwbR?{C$Ph|x5Uw;ilLQjnPxS1VkAF#97(g<5R%_b{=UD0Lf` z0{NLN%d=Q3PvJOym?cS}iGy(xUnZEFE{Lu3^5`AZl7}11kVYO7 zf3c(qrYu!x9xWg`myTm+XJ+JEahupFmnT2&)AEK#+(|u@SiI6OOMnKkaljPM(4ynn z0!_`5vS;EM>V@`AxakrVjvL*i@c3rLF$=Qb8sLitGD}udJZITO=I-jdB!=u;ord9( zHLm>HKIx%b_{Vz`V7e$cF6CGF2fdGYq#rWi#F>-5vRk{(5sd(PElX->bs}XfGvMIP z;y87Avq~V_uFj;FH4`ex2r;5V^!pcHf~9tFtI2UZudGeA*b8=?y} zk2QUuqXElCPF!Iq1iZ(;D1b!jH9Le2+a-ukI;qM6l)t6kQiy{qd`J0xr9U>OeEX)Y z&<`hfHz#kAVX?VyN$jQrRk%T2iVRtH$tWGduOvC@>%Xhto%|{ed%c(bE12NYHI}N z4@)y+bM<96kxI$Tc&ycki4ifdk2*Mz*|HY)nC!KcwhDLdSy2H<%R^7S53)p(^)NE= z*4b>Is4bH__SeL($nPsf&3vE9Np1ByrkBOmGyGrtqw&1lFkF-Tp&7E!8In!RIDFVK zrcQuv=G%P?SO^!qRX*?$>Jrgi3@(IjO%`{4lmb&*ieKmgi5!1FsQVMF)aCHTxU-zo zg0DY)Vr1Szoc>Rm_s=FC!dwHF^1HWK$18j2`qiJfDpm(*E>Z+BW8Xp!8Dqwg-0C@} z#uRnph`!$`;QjpOoW|NCdkc2VWgYyEgF)Ll@Y2EA4)f{rttav%B;(DmHH}rQECgoT zX)&L6+}a`;tPfV;$wK|6OzWD3$0*11`q11YlT;1|nHUl(isxTe&?rvG#|NnN zW31Z8mb9Z-I;pL3sm^UQRBCsyS9cc}PFsx5`S_>sEc!;HMZ(pwBZ`UjHM3R#CQfoq zBCB)mU;PJ`ps6?CwG0z`>4Vk_{|Y8inUF&kf9PWtG>&%^Q)Ua&U$Yst{KW~vr+#DG zP$($muyR-^lU}Meq)U6~G1yw#b?GYdXb36b-nDnvhfAizorwGBcwLZy%UuAU_$zg> z1kT>UJx*1vOT>@>0P)OqO%Y^ZLrUd}@XC>WCsuO$(b({NydkXe76jS}P4op3O|#$J zX_FC$W6_VnK#l5o!=gsw?-MZb=j|mFUfpLnI83fsd(#ot~uQ>y}caqYvl;FO60 zZs(A)&u!%r)RYjd0r^BCHzc^pz3+JU$_TbmEF*9sZjrVoSZ`#ZWmN^*OPEL8F(bB} z)Zh{2BiHQ3$5Q6RP{snjTcw0&JL`xUjf~VOevxnO<-PhlsSxj^e!<%_5oy#cx&Io} zk~Q;qDu3B3sJ@1^|NHsG_+QQ^Sx*H^2h%STy@RWZvWt_c;r|$;l(pqi6cIm5bSXFK zXXwX7p$qa_V3Y=7NU>!}Gi;PZ=*oe&I}N)1Mys|h9Z(*giyq!j=5Y#=Q9#J&!YJny ztvDropOeRlEUsRCEm$ z6``1E2_O7Dy%*ZKRyL39KegO-31ZCRYDv=Gh2S^5Nu%z+`1NBJ+(%u33ak5JhFJ={ z!({a0tup{-1aZ%2j{f=~4YD9I#{wD`q|8VLFA1wl?1Jty;KO9E za>kMXW4DvOq^k`krMpoy1IVB9=TK+=YWTVyXA{zg;qU>;F#oF0j=inSGf~v5K(s735)3lNFeTH#K- zfEIY?S6-~y)v8Ch<8Gm~r2;niXO{fSJbCZVzzdX9uH%A89p)DaiSQ7mkX5h~JfnMj zynB%Bdn9V#eoEgX>!-usl?GSM)c!_NyFQnb6Xt1snPdWn8Y773e+G?JlBYw9zak`Z z|Fst3zkLDMcI5-R_h7!#of`V*i%}8qX zf!Q)&2i0NXZn^Nw)W~)(I92a!`I`giEJ5%4BPX+B-KU(V(my_qhvmOLDRT5-;c{TI zLD*5k?*j;(-lEO)pSy8WoM_O)HzX*P zu))f{*6taGj2V6YIuOEB0pT!MMF!d$Vq!x}bVc=pbxV&vFFwp?qc_DTaAbj-@H$Kv z5i`nu$gB$%un(ei)2mFARw7#%eL3vV3Aqb_THNehkjwCe8w+NPAF2vLyEu5`WjPN`dqjsNh zW2nkeJ<}AuSZln)-TDyVpfa3Ru(LbZhGz)K?HYye*wT(MgZ=ksfi^ z*u9S5Ct!6FPMwyIe*qg^_&)KQoBMke&$hyIa1?iU;Xz$cGWWn)-vNDtPC+jM+xV+H zhhcJaj%oP;-R6<)#%wBAFpa~Anak4ef08aQRm~iczt$Vwe~m0L{g*BG-|J1@)#d-% zbA6R$<@{<}p} z3A>_i3@4m0-~^7Xarq|)yFUYDk?ljj$HU) z_vuiSzBmruNO_2jL?S)1{oOyX6pGeWcX6-^q_XPaIls{_I-IAGIha*u)i>n^V8D35 zIv<0yV6=Ya=A(W!W>jH)3pJ69()%`kD5T#X#*c=h5LZAcrx7SOOJ^!%_Gi|o&usr0 zZPh#g`qss)KY=)2!Ny{Y1UiXEDf(mu0ACjsL8&O$cXg|Wh4yMq+FKV&ee=%()DzFj zBh#<+RrG(QHT)-h@xL93|B^V1wP1Xb7L)(x+M6*a?!p9tA%lwaQpg}HASQ)E1%VZK zvi&gli4$r9T4+cb%{te* znU3yF;OWhbIelW~(`~@M+xxoxDaU*3c%!oR^P?T(+eGT{dmUJE7tr>O{zNwDP5F*q z;?XW!y@w4qQWWC-23m>%N*_0e12M)nefjGJOW2Ra5(}dY(mxS3j83p>kedkb63`)XSoD~peh)s(cT`Lq6zI@_-|p~jW2MnHfS3)SBIR|+n(9j zoZp&lX|YsQRv)m^r8(ejOi^iJUvN+Vo^3hKH!V|Z&DYUvD{OtVQ*-4eoFvZ3)>u}U z-)O5V?kg;Ryw(y^lWaW!9WjFu{UGWF)fotU{${#t6U31e0Y9NF?+-wc23Kp6SI7#x z7lSN}*NBKz=7FSAww|XJbTIH{_L3|z*G|)j$nx(MsI#3pMI+OR%Mx`UhHEKs>?=Cb zxnyq!b`~rwiI8rj<^#$a#(O!nal@aUU8lE zsRU_|10YH2@j+h;!)T?3`z|;JIZPP&><5L=YE=yrn8<` z0ucM!Dveb!v%>vX_9ktzplJ}>h>=fYgd2@~n=feUW zS9u3aT|+P{+7zTo@nEz%9)uehdZH8jcoIRl<|>XshP8@x`Vh#Rt!5zn9RsXhZrNS= zlShtgsCJ5{x%bvtj%Xgo^&u1-Z8v{m>CX*%#uGGKs)^IN&eh$%PhU7VV%5T8G;r|I zp&-@>x^6QnmgV^gFCOu-7 z!Z%@Kuj)wB3W*j)ozYKJcK}ReWg<*K*y$@69ywPNS3663M4Y+S=Y&w^TxEcS(W(W4 z>tqyD6ARi^R~;GeiPlm7sx0_sX`Nz)x{lF4Nt=qVA7MlEoL;)poplDw(ziuJek)^Y z(6x?!099HoW=(@5n_D@r%u2pG^N4g(&z6r~HvdL!?BOUM{jA>xB_#n`#@;%f8lUf( z@4PvN2(NjU(t{*Uu2L@bY@4fNQQs~-6haEsOKuAjoc}jFlKxK*-Y}lJ^;jM(M(5cY zW7K*_j4QJX$r9u{j_UWz6#Br6o*2Hnf z^O;^vJHGEW**7VD3Wx@WD$CPr0)F++?4VzrI~S`EezUBTwT))xUmcaGAfeHm)my5x z=rP)_#5Rh|_ULl@ozc)5rB~UPTFQ2D5)ErrBJm2WooH@15~yr+w>E>j3(DjSDEi1` z@Agk^ehP`^0Mbz~x|g7gHmw@d@n6CS>cn2JJkxhmU}DHF#TGGUTmrs*X4m&kO0X1; zruzafqsnS`Lo>Mjl1NgTmX_au1gv2jBJBP zRRz>_9O&I7 zVFr$=t%Ir0S-{^N(<8ho_Ht*PSbOvZiiCwg4y6T=sp$z58j_?3e zg4zJ8nmYTlo2|w!%C!iYw(Y&#rI_rsP>h1(7zxBZs+298CBa%131{)}>#4%pBuulz zYN5)8*fA6JWJN?hVT{I{#LeuVqS?1}>DJ@Cs@--P$5S~*<35Xy?LTm}BnECem4>K0MQLDOJd5JHk`=SxWK*Of{KS^)gi92!Fxafpk3Ug@N(iv@)GL zI{tZ0QlWoDDGEJyt2T;q>f{uZp?tm59)CHSu)6Cu2rA~1XsBSihK%aG%8X@bd1QcC zMIxP2*2}IWRIX8J2H+N>^wiZ=QxDEk33AC#slN_r<)fAKIFJ>FGx+YN$vk9gkA|H$ zokZ8!(DK5hU5X#d0Nc03E2UwF79a~@Kl-^>rr&244$>}v)2*W(5l#Dr$3mM_3xEH9 z2`md~tolg~3ly@7lm$HvzmTBHNum>m-~bjGuG_8}Mu2Pi#-6L;Z?R&*rl-4Zq_$3t zFEq23jhAi9f**>@)ZE`gui+3l1=`$(OgC4Cmb&E^UBb)_{GyqfGED(XoKi!&558QoTkB+ai|Ej=#F&1UQcg>>m&++ z%!?#ik#EwCE4R`NV2A=4{t6Nj0yRpHgA?$6I|3O*%TW^Lblu?7d&@gJ%oB-L5-LDY z!36FHw!*MfN(9D*pUSpm9+Xf8^oXG_iI(QdNn@kmZ{xM*4c{Nt+=qUoc?skyA48Q^ zp7aRT)4tc0yv8*1iGs=9ZDH9=S%#j+HwVnn1Y3IMGdk!|$ly4wW4Bhju5-6$pYLTJ zw@_p*&8_G5wf07#c~PS$1d8Hj13W)6cRV3V@do#-0F5Ie=Rk6WYP~-*vo?Thw<8FNEh);3FhhRqkD%=N+-6YgXfd$1mT-4XrnWVNbW+ku7YQmg|h z?6OW?R)4~c=hwahZ~ejtOT*eO^t@5p5J1&soQwCm0c>-J(CPT9BhCrj#W}{#z5$!Q$v_u zDK}NAd9pd>7g{xYeD%%)(0<>PL#xSi&)JXzo1h3kUJDkb0OBj+Lg#`NPjvv80olN9 zc>D_JtyWL(J$|%NsJLDGZD2rO4*0$Se_HH)*@*)4_dYy04_0IOh*o2V@9#YE`15lq z9vamJrcca!_IUET7|xFK{Bqpe;P|5F|8yPCH$Uz(hdv0eW)0VkJ2>L;n`wpKO-7OS zic8*_X<0Im&5BC|0Sj{B+mihVibLp0jlVA#M|tlptco^Gg000BxrLUkUN&vuZTM2T z>rAPt?~t@1tJnYfUNe<&5Y5S}m9QKge<0Jv*!H?{a^`Ggks;+skkfuOWO~yj z<*NPsC%ipi88!p^(>!O^Q$VZ3 zaJ+XrVfu@gfK&aLZC-=$Hg1uO`tIKd>|xH&E0YvdTgm2(Ey5(!>AguB4v>-H8FE(GNKw!M&4CTtxWVlUQ`bYc(R(B{K z=pMkhGW_Ung2X{}Vjy=G6;&)E?)9rTkGDriIUm%e%n2&=-l>GjVy8bSW(%bTz(rmN z9ws;f{ygQ`NQR%PCgU<+(r?es)IC?dF$uP>MfaL)o5yVFq7^m2z3{Q7sGGld1Sdsq z|9&+5t6NB<=h|RMwH1h>jTei9-f=bV5lyks6}Ak2y7$L0UBUe)`FyEQ8_Ju*&yug|0ljR|g(64@g@Vj=RUriQcCfFoJ^%VU2)L zEM^f2!oV>D5LMdaiQ6d8Tm+0^|qR_b(6?G*HjHZMKe9l^IDo&ZW~;i>|67n#+651q07Yq znWH)R(uUvs*b>`Pal7cXvY$tmE16?F>%%s>h*?%);$ndX&10N7K2^9h!;ZDDb{yn* zg5b`*iV!@|LTEg%dLP?l{IV zN%(yxet!$_$%e;&*okPIsNoyyyo74biZajsr2NZ;Epoqjni~sae;5hlMBJT9LC=?t zlj@hnK(A;n;LhQ$Y$Ro>T4ttUh9Di?(zKPh2@F88$|8J4iTEgm%@Z&NefC7pRK;Si zj6zPH6m`n*o}hk8Bee9X0xGEkb2IALrvW9YL0J(-_Nj{Bah z_q>O9GEb{;1LeKVm58E>8-&!*3tUv`kG7HTuAvpy2%|&h&{+S1CdKqULGHa7gGk}| ztR~e^$(*j~Ok5-1<$i(`TSF^zLu|hdJ{|wcm}@+^Kq5&sa|WPe=Nn}>Drj{GAI|yx ziD}ppRpbLzhev_G5(T}8{0up4pWr-gU$2GZO6XaSb zvqyztD(Thf4O$!7kj|%P-!5f3byv)x3`6H=oliWu{&XmU{Gx(BDT=j;)-H-r#2}CZ z=>*((N4OEW-S=d~n5H8LGcVzPGrQqz(L~MYW9Gz|E`{0STG~0Co^kHB{+677goEbCGZv(&aW%nR#Oy3_coI<`kcobD>?cpZ*dY+-9Z#1|*Zj6XfU)wsYL}8_Z zFdkerpW^YvJ^PIe*;({V<0&wRH|WQ$!;)w%tFxNrQI!%_5iad~$B530%AlVHonJWB zb@;D{v;#=wJ4&M1__XN{f$-g>rkbhA4Y5uy1`@mhCm_B@=2ONpVP@%&*y;)jEWJGP zpVYS8sk!69Ds4o0qkLp#y6cH7mmrIz9d`WNK|7D9vv7G6s|T~{MQe>pYsJ5j8FP1< z<;xb~;}N_Io3F_3mt8RXO&7-j?&nRkH9S6HE?!}NaMsc{%QkDsO?hO>`Sf4Zj$aTa zX}530)Q4Q$tAe-;`a%*p!#Q!+h8sjax zF?+%($z)OR>X?lhkKFxGyMwfbSzz`%lxhRkSG_eN6SAD*q{OKQbkI*nsKF^5@`{)S zzz-z#9L2k^?>dAGW5ts>Ecd5*4`j#n$DR_6=JCs(BpDa31mQla-M($S_fS&SCD#o_ zW6R}!$Loj5qg(eRvpCd?#8RsIX%`u1FP$8xBy;y)SBpxg8h{cl1p!%-J;i;vMYbg# z7sD{v3Cr(k=w5rY!a?YQIqXoYN-!d8UUUtVx((_9=zcA(o~?>r@=9dG7RAajht7@N zF<-xXB4UE%@sUP?E~j?KoAOS*)9;C?+-WBGE2kVV_f$#Z0dQrVWyI7`IjY} zROco_IThSh_2ysQ?U1)hg!{bpBMukdHM!x;91Omey~! zrBjV@*U2U1Gb1MMs$xy_<#e=_5@H81(lSyp-3?C@zG$#WF2fcn13# zPw+(RiR>*krpu7&x33hI|HcBcP@*39Xb|s}4)MDp-!3reEEmVu>fTor;p|ZTNwDP~3w7%eA&dOid+#0eM z$y*l8^#E=gIl^$-?{*qGMva(_(72ffAmBoRDFiK1bXcc99E3+fB`xQpOKdYzep({S zXwNlhrqwk|LK6|0SekJsj>?n@JP8;eZ(Q?^HrT)fcwTS0U%5~59d9)1_&PjYR(>()L7Gvfj%H|ua-l9n_aeytcSHnRVwon209XA&QZj2c=;DJDJ4?6Or?EkJ ziU0UKB<$?cwv)2TiwEvt^&BHKis2 zg1Po0Qpa<-2jpS}j;Esm;GFrUU2R_#2qr-$Tqz4)k@1G8NK(!y{WGr+L`jgu+Ju_j zle)`)Y9Ya<&uYlS=le+$xdZOoLR6c)i_#V+^B-XW(Tf%{bz=E0q;_3cR|`NeY_xmC z{dLxi`M&INfDErmaHmF#nm7`my&fI}ZW8tsi4gBxuzI)&%S^3=O6ErGKt?eBMHi17 zTJ*>aPx}V7IDwA`EnP_SeIP*v%twA0Gm5U)t;quF=w_BtJrYF-I`#n8Ur{VlMYzfc z-m|MP#aWw7=F&)7BY)3NW(e%1ODP{v~D>kT+kn! z!ttmngV=BEFoZEd5PQ-r+OIEKb8FZvdyaS=^`YVN$4gJF5FKfd-8xvSagpt6LG$k0 z!}G9XEwH*RH7iQByO6vG@eOAvyHjc;M}sU2oHPQ~W1B0X$3l}y@YEe{Xd#xws+8|*DXcX zCfq7L>lAt-wymExZ^pJzIHKwC#BZ?WnJ^>4y@h74a*$q&y{xJQAV(wejX7&}5dw*r zi6LCyk!oo;9|!vjT8=iAa`(JYy0GP7mkxG!YxYS$2RU~-*#`FA_ZWER3`6l-cMf+w zJj+T~9yT)^^h|v$3D-CpA zR!(jthCFR(!Ko{o8rK;od5B|3g~vN1>m4f{{z|VWNjqU(*0NpM#FZTs0z}lYZWmMy zq>&lj5Jt8|$9}Oa4r>PAOma4$s_F?pCdmz>s~(m{#7v@xEOo-!{)E#9ht2`+DiaFd z=K-t7xdTBg=yekThrml=e(IawqTu;e;+6n;!&2^_+1H@txkEw0$SLKRxMFFOYpdqvT%oteN{gOx zD>OqsDFMTh)nukfR&{B=TvN>WiOV68Vzo;aG-C`siHbw6d8LKu4)of7pQFTa5bm*2 z!X4g@6lrN5(aB`ChTE{m7!UF4AI7x=ep0o#>Gr$xJ!wmoGBh?($=91UVP!6fnSrOU zN4zq~+25IXWrD62OmP$19NaB}4CY z#gp}d8S4KL`kmz!sd9F#iS%)a({`~&FR2K`R4&$9ZYNLLQ^$0wav1V+Int7CjjU?J z<;NR4+I7!w^We)DQg||8BJ(AvpI7wjg6{AD^Zh;sdpzkKso!Gkw%O4-3-=l*sV$xJ z3h#>~E}fH_b&Hib!{A)Je!5GO`1V8x>y+dX56A@>&FDe`;v@ATvuh*B;M~_Q<$S8v=8qjeyq*IyX^{TF zus^1bIqu$Na5aNSCZImoTmd~s&5G=TBX3@z(k<#Ud|Qa16<^PgHrnWsvNbD+?r7=QlC)8T4tFrQSHas-flznEgeyshMD^L2VH`42?v|Ho9LdyOl)z;b6=^y1C;>yZ` z3XoIU7E^3du9njyXiL~6tO~OUfB?JwQseldWTwv&N#uxqFZ^I z-|bxVp&q@*kXq# z6fHIoW73wxSA}cQq4_hz!TSS+%b{g{K&Z2;fT^=q!M|EdsfDVs7fs+;z4x6}jOFS7 z1Op369WpLKi_vV}Z7IrUjv4|*32lH=-8`&Kl9C3y4NYYCAS z6EllhXvo~9cikIZ4r-hs%d&w)x6%(sV-YK6W-r4!m73CeHUiM_k3XDEQ%tw9XGP9; zFwP{3Q^k)BTDKYqFFTk|tQm46Ojp>QXTsaZidsZw12;?hRjy>nEqCSEkDRuHV}Q&m z!-5@4H>OPyce;(u#7)~!5+Yp9<@)yZDy1*VOkU8nI_+vpu>f3*P4j=+nn5P zSe=cQl=3@qOMxwS1Q8QQwTR+Adn7;L6@q(Idbe9-SV>c^CdR>}urCXMAW^=S;;@K( zljX2J3}2JKFBe|IVwe^}=F6~L4@%R17(H6jhnq^CnGpqsS1Z&W(gER^H8-Q^F9lLf zVwt@5!s%1&**sUJHN07dG?GV|jIsQ*sC*_SVGv~5f-16CpN15#(a|@CFELOXF_9Ra7s{Wxap|G95-@7{rAN zry@_1;7(GO;ldh7N7cAmH@0_qxw``KrjjgWSQ!&-VN8y?mac#p2MWMW1&s#%QING> zL;`~D2XX8*>-?;N+0dK7&2Aj|8()sJe;qqYcGLmKm90S9XIV1Ymb*LVO~bNbEJDMs zVU7}4!=?ed<-Cmmp~#vC+d2Z~0M=^{G>Hy7B(zR=fFtx|Oi6rrz(d3!Z54T2sSoZ4 zhciM0B#kT1$FR%A;C{KyYDs3ngxjmZS|DDV{Vor7_vG1WVsdk^tnzT8M?@Lx5K8i+ zW40SgY)kT`LuyPZkPkq>(gJp&O(OKh)cv`p8eL7hDT>`KUsp5iuN&^E0Z_wld3g4Z z>1uH0)Y>aygYBr-A7*(bk|yeRN<8E;j+=q_E{*+HV;BUth+~zu-F#!|=kH`9)9KUa zAAVyzL{_g@ZpPXRHcU!vZE`8e@&%ja`*Brtx=U(jOITV-aHIXq3hhT?GA z35v`TldFG;^7A2oG5qkze!ua1hq@(s1LEHnz5+E5Ucv~oNpit(Rcn~w_se(5T9=o; z3&p6%E0=nOWINuSLoagiV&!v}QV6IV-|}Kt%=6L+yV^;5>g4>rsq}1kcI9~v+f#=p(a#s28BwiR;@55966SDabZVSfW5) z-TPdkAK4IBxN|Zg5RT<*r~7C!CL&Ko7<*OBq>r#bO?XMjq5a44MFN2Yl;zeuXE2NH zWmwS#8itya{;HoD-cMc;12iFRF_@am47_|+y7sCt*06t-zDhCGjhJRqzQ}fQpdyXZ zbo$YUndPumPvuB@Zaetq$Fp)06ExowgM$eVuQNh_IX-#)GocWbp^L1QO^@9LrCSAz zPIlUXt5diQMquls;WN{Q(4Phr5(2ky8+EIPSZI1;leg?&jLEZbACJiNwRfm3=+*~4 zj``-vxSd)Y_$o82jT(Hk5!oy-e{N2wcm-9}F}l6+J0i2QXCidZ3~ODKS?@R>U5s-h z?yI~2x)XT}-r0JoXRg_ofLPo(7Oi?QoMrHhrC;Auia36H)t&{
      MMLEnJS(Rn&< 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

rH; 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