diff --git a/.pylintrc b/.pylintrc index 594768d7c..0cf962b8d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -66,7 +66,7 @@ load-plugins= # C0112: Empty docstrin # W0401: Wildcard import # R0801: Similar lines (refactor) -disable-msg=W0703,R0903,R0904,W0511,W0611,C0111,C0112,W0401,R0801 +disable=W0703,R0903,R0904,W0511,W0611,C0111,C0112,W0401,R0801 # W diff --git a/bauble/utils/web.py b/bauble/utils/web.py index dd580d005..cb4354026 100644 --- a/bauble/utils/web.py +++ b/bauble/utils/web.py @@ -31,12 +31,13 @@ -def _open_link(func, data=None): +def _open_link(data=None): + """Open a web link""" # windows generates odd characters in the uri unless its in ascii import sys if sys.platform == 'win32': - udata=data.decode("utf-8") - asciidata=udata.encode("ascii","ignore") + udata = data.decode("utf-8") + asciidata = udata.encode("ascii", "ignore") desktop.open(asciidata) else: desktop.open(data) diff --git a/data/nsis/Include/NsisMultiUser.nsh b/data/nsis/Include/NsisMultiUser.nsh new file mode 100755 index 000000000..dbea75972 --- /dev/null +++ b/data/nsis/Include/NsisMultiUser.nsh @@ -0,0 +1,513 @@ +/* +SimpleMultiUser.nsh - Installer/Uninstaller that allows installations "per-user" (no admin required) or "per-machine" (asks elevation *only when necessary*) +By Ricardo Drizin (contact at http://drizin.com.br) + +This plugin is based on [MultiUser.nsh (by Joost Verburg)](http://nsis.sourceforge.net/Docs/MultiUser/Readme.html) but with some new features and some simplifications: +- Installer allows installations "per-user" (no admin required) or "per-machine" (as original) +- If running user IS part of Administrators group, he is not forced to elevate (only if necessary - for per-machine install) +- If running user is NOT part of Administrators group, he is still able to elevate and install per-machine (I expect that power-users will have administrator password, but will not be part of the administrators group) +- UAC Elevation happens only when necessary (when per-machine is selected), not in the start of the installer +- Uninstaller block is mandatory (why shouldn't it be?) +- If there are both per-user and per-machine installations, user can choose which one to remove during uninstall +- Correctly creates and removes shortcuts and registry (per-user and per-machine are totally independent) +- Fills uninstall information in registry like Icon and Estimated Size. +- If running as non-elevated user, the "per-machine" install can be allowed (automatically invoking UAC elevation) or can be disabled (suggesting to run again as elevated user) +- If elevation is invoked for per-machine install, the calling process automatically hides itself, and the elevated inner process automatically skips the choice screen (cause in this case we know that per-machine installation was chosen) +- If uninstalling from the "add/remove programs", automatically detects if user is trying to remove per-machine or per-user install + +*/ + +!verbose push +!verbose 3 + +;Standard NSIS header files +!include MUI2.nsh +!include nsDialogs.nsh +!include LogicLib.nsh +!include WinVer.nsh +!include FileFunc.nsh +!include UAC.nsh + +;Variables +Var MultiUser.Privileges ; Current user level: "Admin", "Power" (up to Windows XP), or else regular user. +Var MultiUser.InstallMode ; Current Install Mode ("AllUsers" or "CurrentUser") +Var IsAdmin ; 0 (false) or 1 (true) +Var HasPerUserInstallation ; 0 (false) or 1 (true) +Var HasPerMachineInstallation ; 0 (false) or 1 (true) +Var PerUserInstallationFolder +Var PerMachineInstallationFolder +Var HasTwoAvailableOptions ; 0 (false) or 1 (true) +Var RadioButtonLabel1 +;Var RadioButtonLabel2 +;Var RadioButtonLabel3 + +!ifdef MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY & MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY & MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME & MULTIUSER_INSTALLMODE_INSTDIR & UNINSTALL_FILENAME & VERSION & PROGEXE & PRODUCT_NAME & COMPANY_NAME +!else + !error "Should define all variables: MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY & MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY & MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME & MULTIUSER_INSTALLMODE_INSTDIR & UNINSTALL_FILENAME & VERSION & PROGEXE & PRODUCT_NAME & COMPANY_NAME" +!endif + +!define MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY2 "Software\${MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY}" +!define MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2 "Software\Microsoft\Windows\CurrentVersion\Uninstall\${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY}" + +!ifndef MULTIUSER_INSTALLMODE_DISPLAYNAME + !define MULTIUSER_INSTALLMODE_DISPLAYNAME "${PRODUCT_NAME} ${VERSION}" +!endif + + +RequestExecutionLevel user ; will ask elevation only if necessary + +; Sets install mode to "per-machine" (all users). +!macro MULTIUSER_INSTALLMODE_ALLUSERS UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX + ;Install mode initialization - per-machine + StrCpy $MultiUser.InstallMode AllUsers + + SetShellVarContext all + + !if "${UNINSTALLER_PREFIX}" != UN + ;Set default installation location for installer + StrCpy $INSTDIR "$PROGRAMFILES\${MULTIUSER_INSTALLMODE_INSTDIR}" + !endif + + ; Checks registry for previous installation path (both for upgrading, reinstall, or uninstall) + ReadRegStr $PerMachineInstallationFolder HKLM "${MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY2}" "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME}" + ${if} $PerMachineInstallationFolder != "" + StrCpy $INSTDIR $PerMachineInstallationFolder + ${endif} + + !ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION + Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}" + !endif +!macroend + +; Sets install mode to "per-user". +!macro MULTIUSER_INSTALLMODE_CURRENTUSER UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX + StrCpy $MultiUser.InstallMode CurrentUser + + SetShellVarContext current + + !if "${UNINSTALLER_PREFIX}" != UN + ;Set default installation location for installer + ${if} ${AtLeastWin2000} + StrCpy $INSTDIR "$LOCALAPPDATA\${MULTIUSER_INSTALLMODE_INSTDIR}" + ${else} + StrCpy $INSTDIR "$PROGRAMFILES\${MULTIUSER_INSTALLMODE_INSTDIR}" + ${endif} + !endif + + ; Checks registry for previous installation path (both for upgrading, reinstall, or uninstall) + ReadRegStr $PerUserInstallationFolder HKCU "${MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY2}" "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME}" + ${if} $PerUserInstallationFolder != "" + StrCpy $INSTDIR $PerUserInstallationFolder + ${endif} + + !ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION + Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}" + !endif +!macroend + +Function MultiUser.InstallMode.AllUsers + !insertmacro MULTIUSER_INSTALLMODE_ALLUSERS "" "" +FunctionEnd + +Function MultiUser.InstallMode.CurrentUser + !insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER "" "" +FunctionEnd + +Function un.MultiUser.InstallMode.AllUsers + !insertmacro MULTIUSER_INSTALLMODE_ALLUSERS UN un. +FunctionEnd + +Function un.MultiUser.InstallMode.CurrentUser + !insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER UN un. +FunctionEnd + +/****** Installer/uninstaller initialization ******/ + +!macro MULTIUSER_INIT_QUIT UNINSTALLER_FUNCPREFIX + !ifdef MULTIUSER_INIT_${UNINSTALLER_FUNCPREFIX}FUNCTIONQUIT + Call "${MULTIUSER_INIT_${UNINSTALLER_FUNCPREFIX}FUCTIONQUIT}" + !else + Quit + !endif +!macroend + +!macro MULTIUSER_INIT_TEXTS + !ifndef MULTIUSER_INIT_TEXT_ADMINREQUIRED + !define MULTIUSER_INIT_TEXT_ADMINREQUIRED "$(^Caption) requires administrator privileges." + !endif + + !ifndef MULTIUSER_INIT_TEXT_POWERREQUIRED + !define MULTIUSER_INIT_TEXT_POWERREQUIRED "$(^Caption) requires at least Power User privileges." + !endif + + !ifndef MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE + !define MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE "Your user account does not have sufficient privileges to install $(^Name) for all users of this computer." + !endif +!macroend + +!macro MULTIUSER_INIT_CHECKS UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX + + ;Installer initialization - check privileges and set default install mode + !insertmacro MULTIUSER_INIT_TEXTS + + UserInfo::GetAccountType + Pop $MultiUser.Privileges + ${if} $MultiUser.Privileges == "Admin" + ${orif} $MultiUser.Privileges == "Power" + StrCpy $IsAdmin 1 + ${else} + StrCpy $IsAdmin 0 + ${endif} + + ; Checks registry for previous installation path (both for upgrading, reinstall, or uninstall) + StrCpy $HasPerMachineInstallation 0 + StrCpy $HasPerUserInstallation 0 + ;Set installation mode to setting from a previous installation + ReadRegStr $PerMachineInstallationFolder HKLM "${MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY2}" "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME}" + ${if} $PerMachineInstallationFolder != "" + StrCpy $HasPerMachineInstallation 1 + ${endif} + ReadRegStr $PerUserInstallationFolder HKCU "${MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY2}" "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME}" + ${if} $PerUserInstallationFolder != "" + StrCpy $HasPerUserInstallation 1 + ${endif} + + ${if} $HasPerUserInstallation == "1" ; if there is only one installation... set it as default... + ${andif} $HasPerMachineInstallation == "0" + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser + ${elseif} $HasPerUserInstallation == "0" ; if there is only one installation... set it as default... + ${andif} $HasPerMachineInstallation == "1" + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers + ${else} ; if there is no installation, or there is both per-user and per-machine... + ${if} ${IsNT} + ${if} $IsAdmin == "1" ;If running as admin, default to per-machine installation if possible (unless default is forced by MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER) + !if MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser + !else + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers + !endif + ${else} ;If not running as admin, default to per-user installation (unless default is forced by MULTIUSER_INSTALLMODE_DEFAULT_ALLUSERS and elevation is allowed MULTIUSER_INSTALLMODE_ALLOW_ELEVATION) + !ifdef MULTIUSER_INSTALLMODE_DEFAULT_ALLUSERS & MULTIUSER_INSTALLMODE_ALLOW_ELEVATION + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers + !else + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser + !endif + ${endif} + ${else} ; Not running Windows NT, (so it's Windows XP at best), so per-user installation not supported + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers + ${endif} + ${endif} + +!macroend + +!macro MULTIUSER_INIT + !verbose push + !verbose 3 + + ; se for inner (sub processo) e ainda assim não for admin... algo errado + ${If} ${UAC_IsInnerInstance} + ${AndIfNot} ${UAC_IsAdmin} + ;MessageBox MB_OK "This account doesn't have admin rights" + SetErrorLevel 0x666666 ;special return value for outer instance so it knows we did not have admin rights + Quit + ${EndIf} + + !insertmacro MULTIUSER_INIT_CHECKS "" "" + !verbose pop +!macroend + +!macro MULTIUSER_UNINIT + !verbose push + !verbose 3 + !insertmacro MULTIUSER_INIT_CHECKS Un un. + !verbose pop +!macroend + +/****** Modern UI 2 page ******/ +!macro MULTIUSER_INSTALLMODEPAGE_INTERFACE + Var MultiUser.InstallModePage + Var MultiUser.InstallModePage.Text + Var MultiUser.InstallModePage.AllUsers + Var MultiUser.InstallModePage.CurrentUser + Var MultiUser.InstallModePage.ReturnValue +!macroend + +!macro MULTIUSER_PAGEDECLARATION_INSTALLMODE + !insertmacro MUI_SET MULTIUSER_${MUI_PAGE_UNINSTALLER_PREFIX}INSTALLMODEPAGE "" + !insertmacro MULTIUSER_INSTALLMODEPAGE_INTERFACE + !insertmacro MULTIUSER_FUNCTION_INSTALLMODEPAGE MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID} "" "" + !insertmacro MULTIUSER_FUNCTION_INSTALLMODEPAGE MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID} UN un. + + PageEx custom + PageCallbacks MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID} + Caption " " + PageExEnd + + UninstPage custom un.MultiUser.InstallModePre_${MUI_UNIQUEID} un.MultiUser.InstallModeLeave_${MUI_UNIQUEID} +!macroend + +!macro MULTIUSER_PAGE_INSTALLMODE + ;Modern UI page for install mode + !verbose push + !verbose 3 + !insertmacro MUI_PAGE_INIT + !insertmacro MULTIUSER_PAGEDECLARATION_INSTALLMODE + !verbose pop +!macroend + +!macro MULTIUSER_FUNCTION_INSTALLMODEPAGE PRE LEAVE UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX + Function "${UNINSTALLER_FUNCPREFIX}${PRE}" + + ${If} ${UAC_IsInnerInstance} + ${AndIf} ${UAC_IsAdmin} + ;MessageBox MB_OK + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers ; Inner Process (and Admin) - skip selection, inner process is always used for elevation (machine-wide) + Abort ; // next page + ${EndIf} + + ; If uninstalling, will check if there is both a per-user and per-machine installation. If there is only one, will skip the form. + ; If uninstallation was invoked from the "add/remove programs" Windows will automatically requests elevation (depending if uninstall keys are in HKLM or HKCU) + ; so (for uninstallation) just checking UAC_IsAdmin would probably be enought to determine if it's a per-user or per-machine. However, user can run the uninstall.exe from the folder itself, do I'd rather check. + !if "${UNINSTALLER_PREFIX}" == UN + ${if} $HasPerUserInstallation == "1" ; if there is only one installation... skip form.. only one uninstall available + ${andif} $HasPerMachineInstallation == "0" + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser ; Uninstaller has only HasPerUserInstallation + Abort ; // next page + ${elseif} $HasPerUserInstallation == "0" ; if there is only one installation... skip form.. only one uninstall available + ${andif} $HasPerMachineInstallation == "1" + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers ; Uninstaller has only HasPerMachineInstallation + Abort ; // next page + ${endif} + !endif + + ${GetParameters} $R0 + ${GetOptions} $R0 "/allusers" $R1 + IfErrors notallusers + ${if} $IsAdmin == "0" + ShowWindow $HWNDPARENT ${SW_HIDE} ; HideWindow would work? + !insertmacro UAC_RunElevated + Quit ;we are the outer process, the inner process has done its work (ExitCode is $2), we are done + ${endif} + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers ; Uninstaller has only HasPerMachineInstallation + Abort ; // next page + notallusers: + ${GetOptions} $R0 "/currentuser" $R1 + IfErrors notcurrentuser + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser ; Uninstaller has only HasPerUserInstallation + Abort ; // next page + notcurrentuser: + + + !insertmacro MUI_PAGE_FUNCTION_CUSTOM PRE + ;!insertmacro MUI_HEADER_TEXT_PAGE $(MULTIUSER_TEXT_INSTALLMODE_TITLE) $(MULTIUSER_TEXT_INSTALLMODE_SUBTITLE) ; "Choose Users" and "Choose for which users you want to install $(^NameDA)." + + !if "${UNINSTALLER_PREFIX}" != UN + !insertmacro MUI_HEADER_TEXT "Choose Installation Options" "Who should this application be installed for?" + !else + !insertmacro MUI_HEADER_TEXT "Choose Uninstallation Options" "Which installation should be removed?" + !endif + + nsDialogs::Create 1018 + Pop $MultiUser.InstallModePage + + ; default was MULTIUSER_TEXT_INSTALLMODE_TITLE "Choose Users" + !if "${UNINSTALLER_PREFIX}" != UN + ${NSD_CreateLabel} 0u 0u 300u 20u "Please select whether you wish to make this software available to all users or just yourself" + StrCpy $8 "Anyone who uses this computer (&all users)" ; this was MULTIUSER_INNERTEXT_INSTALLMODE_ALLUSERS "Install for anyone using this computer" + StrCpy $9 "Only for &me" ; this was MULTIUSER_INNERTEXT_INSTALLMODE_CURRENTUSER "Install just for me" + !else + ${NSD_CreateLabel} 0u 0u 300u 20u "This software is installed both per-machine (all users) and per-user. $\r$\nWhich installation you wish to remove?" + StrCpy $8 "Anyone who uses this computer (&all users)" ; this was MULTIUSER_INNERTEXT_INSTALLMODE_ALLUSERS "Install for anyone using this computer" + StrCpy $9 "Only for &me" ; this was MULTIUSER_INNERTEXT_INSTALLMODE_CURRENTUSER "Install just for me" + !endif + Pop $MultiUser.InstallModePage.Text + + ; criando os radios (disabled se não for admin/power) e pegando os hwnds (handles) + ${NSD_CreateRadioButton} 10u 30u 280u 20u "$8" + Pop $MultiUser.InstallModePage.AllUsers + ${if} $IsAdmin == "0" + !ifdef MULTIUSER_INSTALLMODE_ALLOW_ELEVATION ; if elevation is allowed.. "(will prompt for admin credentials)" (will appear at bottom when option is chosen) + StrCpy $HasTwoAvailableOptions 1 + !else + SendMessage $MultiUser.InstallModePage.AllUsers ${WM_SETTEXT} 0 "STR:$8 (must run as admin)" ; since radio button is disabled, we add that comment to the disabled control itself + EnableWindow $MultiUser.InstallModePage.AllUsers 0 # start out disabled + StrCpy $HasTwoAvailableOptions 0 + !endif + ${else} + StrCpy $HasTwoAvailableOptions 1 + ${endif} + + ;${NSD_CreateRadioButton} 20u 70u 280u 10u "$9" + System::Call "advapi32::GetUserName(t.r0,*i${NSIS_MAX_STRLEN})i" + ${NSD_CreateRadioButton} 10u 50u 280u 20u "$9 ($0)" + Pop $MultiUser.InstallModePage.CurrentUser + + + nsDialogs::SetUserData $MultiUser.InstallModePage.AllUsers 1 ; Install for All Users (1, pra exibir o icone SHIELD de elevation) + nsDialogs::SetUserData $MultiUser.InstallModePage.CurrentUser 0 ; Install for Single User (0 pra não exibir) + + ${if} $HasTwoAvailableOptions == "1" ; if there are 2 available options, bind to radiobutton change + ${NSD_OnClick} $MultiUser.InstallModePage.CurrentUser ${UNINSTALLER_FUNCPREFIX}InstModeChange + ${NSD_OnClick} $MultiUser.InstallModePage.AllUsers ${UNINSTALLER_FUNCPREFIX}InstModeChange + ${endif} + + ${NSD_CreateLabel} 0u 110u 280u 50u "" + Pop $RadioButtonLabel1 + ;${NSD_CreateLabel} 0u 120u 280u 20u "" + ;Pop $RadioButtonLabel2 + ;${NSD_CreateLabel} 0u 130u 280u 20u "" + ;Pop $RadioButtonLabel3 + + + + ${if} $MultiUser.InstallMode == "AllUsers" ; setting defaults + SendMessage $MultiUser.InstallModePage.AllUsers ${BM_SETCHECK} ${BST_CHECKED} 0 ; set as default + SendMessage $MultiUser.InstallModePage.AllUsers ${BM_CLICK} 0 0 ; trigger click event + ${else} + SendMessage $MultiUser.InstallModePage.CurrentUser ${BM_SETCHECK} ${BST_CHECKED} 0 ; set as default + SendMessage $MultiUser.InstallModePage.CurrentUser ${BM_CLICK} 0 0 ; trigger click event + ${endif} + + !insertmacro MUI_PAGE_FUNCTION_CUSTOM SHOW + nsDialogs::Show + + FunctionEnd + + Function "${UNINSTALLER_FUNCPREFIX}${LEAVE}" + SendMessage $MultiUser.InstallModePage.AllUsers ${BM_GETCHECK} 0 0 $MultiUser.InstallModePage.ReturnValue + + ${if} $MultiUser.InstallModePage.ReturnValue = ${BST_CHECKED} + ${if} $IsAdmin == "0" + !ifdef MULTIUSER_INSTALLMODE_ALLOW_ELEVATION ; if it's not Power or Admin, but elevation is allowed, then elevate... + ;MessageBox MB_OK "Will elevate and quit" + ShowWindow $HWNDPARENT ${SW_HIDE} ; HideWindow would work? + !insertmacro UAC_RunElevated + ;MessageBox MB_OK "[$0]/[$1]/[$2]/[$3]" + + ;http://www.videolan.org/developers/vlc/extras/package/win32/NSIS/UAC/Readme.html + ;http://nsis.sourceforge.net/UAC_plug-in + ${Switch} $0 + ${Case} 0 + ${If} $1 = 1 + Quit ;we are the outer process, the inner process has done its work (ExitCode is $2), we are done + ${EndIf} + ${If} $1 = 3 ;RunAs completed successfully, but with a non-admin user + ${OrIf} $2 = 0x666666 ;our special return, the new process was not admin after all + MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "You need to login with an account that is a member of the admin group to continue..." + ${EndIf} + ${Break} + ${Case} 1223 ;user aborted + ;MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "This option requires admin privileges, aborting!" + ;Quit ; instead of quit just abort going to the next page, and stay in the radiobuttons + ${Break} + ${Case} 1062 + MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Logon service not running, aborting!" ; "Unable to elevate, Secondary Logon service not running!" + ;Quit ; instead of quit just abort going to the next page, and stay in the radiobuttons + ${Break} + ${Default} + MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Unable to elevate, error $0" + ;Quit ; instead of quit just abort going to the next page, and stay in the radiobuttons + ${Break} + ${EndSwitch} + + ShowWindow $HWNDPARENT ${SW_SHOW} + BringToFront + Abort ; Stay on page - http://nsis.sourceforge.net/Abort + !else + ;se não é Power ou Admin, e não é permitida elevation, então nem deveria ter chegado aqui... o radiobutton deveria estar disabled + !endif + ${else} + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers ; if it's Power or Admin, just go on with installation... + ${endif} + ${else} + Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser + ${endif} + + !insertmacro MUI_PAGE_FUNCTION_CUSTOM LEAVE + FunctionEnd + + Function "${UNINSTALLER_FUNCPREFIX}InstModeChange" + pop $1 + nsDialogs::GetUserData $1 + pop $1 + GetDlgItem $0 $hwndParent 1 ; get item 1 (next button) at parent window, store in $0 - (0 is back, 1 is next .. what about CANCEL? http://nsis.sourceforge.net/Buttons_Header ) + + StrCpy $7 "" + ${if} "$1" == "0" ; current user + ${if} $HasPerUserInstallation == "1" + !if "${UNINSTALLER_PREFIX}" != UN + StrCpy $7 "There is already a per-user installation. ($PerUserInstallationFolder)$\r$\nWill reinstall/upgrade." + !else + StrCpy $7 "There is a per-user installation. ($PerUserInstallationFolder)$\r$\nWill uninstall." + !endif + ${else} + StrCpy $7 "Fresh install for current user only" + ${endif} + SendMessage $0 ${BCM_SETSHIELD} 0 0 ; hide SHIELD + ${else} ; all users + ${if} $HasPerMachineInstallation == "1" + !if "${UNINSTALLER_PREFIX}" != UN + StrCpy $7 "There is already a per-machine installation. ($PerMachineInstallationFolder)$\r$\nWill reinstall/upgrade." + !else + StrCpy $7 "There is a per-machine installation. ($PerMachineInstallationFolder)$\r$\nWill uninstall." + !endif + ${else} + StrCpy $7 "Fresh install for all users" + ${endif} + ${if} $IsAdmin == "0" + StrCpy $7 "$7 (will prompt for admin credentials)" + SendMessage $0 ${BCM_SETSHIELD} 0 1 ; display SHIELD + ${else} + SendMessage $0 ${BCM_SETSHIELD} 0 0 ; hide SHIELD + ${endif} + ${endif} + SendMessage $RadioButtonLabel1 ${WM_SETTEXT} 0 "STR:$7" + ;SendMessage $RadioButtonLabel2 ${WM_SETTEXT} 0 "STR:$8" + ;SendMessage $RadioButtonLabel3 ${WM_SETTEXT} 0 "STR:$9" + FunctionEnd + +!macroend + +; SHCTX is the hive HKLM if SetShellVarContext all, or HKCU if SetShellVarContext user +!macro MULTIUSER_RegistryAddInstallInfo + !verbose push + !verbose 3 + + ; Write the installation path into the registry + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY2}" "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME}" "$INSTDIR" ; "InstallLocation" + + ; Write the uninstall keys for Windows + ${if} $MultiUser.InstallMode == "AllUsers" ; setting defaults + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "DisplayName" "${MULTIUSER_INSTALLMODE_DISPLAYNAME}" + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME}" '"$INSTDIR\${UNINSTALL_FILENAME}" /allusers' ; "UninstallString" + ${else} + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "DisplayName" "${MULTIUSER_INSTALLMODE_DISPLAYNAME} (only current user)" ; "add/remove programs" will show if installation is per-user + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME}" '"$INSTDIR\${UNINSTALL_FILENAME}" /currentuser' ; "UninstallString" + ${endif} + + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "DisplayVersion" "${VERSION}" + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "DisplayIcon" "$INSTDIR\${PROGEXE},0" + WriteRegStr SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "Publisher" "${COMPANY_NAME}" + WriteRegDWORD SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "NoModify" 1 + WriteRegDWORD SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "NoRepair" 1 + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 ; get folder size, convert to KB + IntFmt $0 "0x%08X" $0 + WriteRegDWORD SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" "EstimatedSize" "$0" + + !verbose pop +!macroend + +!macro MULTIUSER_RegistryRemoveInstallInfo + !verbose push + !verbose 3 + + ; Remove registry keys + DeleteRegKey SHCTX "${MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY2}" + DeleteRegKey SHCTX "${MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY2}" + + !verbose pop +!macroend + + + +!verbose pop diff --git a/data/nsis/Include/UAC.nsh b/data/nsis/Include/UAC.nsh new file mode 100755 index 000000000..08979aba9 --- /dev/null +++ b/data/nsis/Include/UAC.nsh @@ -0,0 +1,299 @@ +/*** UAC Plug-in *** + +Interactive User (MediumIL) Admin user (HighIL) +***[Setup.exe]************* ***[Setup.exe]************** +* * * * +* +++[.OnInit]+++++++++++ * * +++[.OnInit]++++++++++++ * +* + UAC_RunElevated >---+-+----> * + + * +* + NSIS.Quit + * * + + * +* +++++++++++++++++++++++ * * ++++++++++++++++++++++++ * +* * * * +* * * * +* +++[Section]+++++++++++ * * +++[Section]++++++++++++ * +* + + * /--+-+- +** +** Get integrity level of current process +** +**/ +!macro UAC_GetIntegrityLevel outvar +UAC::_ 6 +!if "${outvar}" != "s" + Pop ${outvar} +!endif +!macroend + + + +/* UAC_IsAdmin +** +** Is the current process running with administrator privileges? Result in $0 +** +** ${If} ${UAC_IsAdmin} ... +** +**/ +!macro UAC_IsAdmin +UAC::_ 2 +!macroend +!define UAC_IsAdmin `"" UAC_IsAdmin ""` +!macro _UAC_IsAdmin _a _b _t _f +!insertmacro _UAC_MakeLL_Cmp _!= 0 2s +!macroend + + + +/* UAC_IsInnerInstance +** +** Does the current process have a NSIS/UAC parent process that is part of the elevation operation? +** +** ${If} ${UAC_IsInnerInstance} ... +** +**/ +!macro UAC_IsInnerInstance +UAC::_ 3 +!macroend +!define UAC_IsInnerInstance `"" UAC_IsInnerInstance ""` +!macro _UAC_IsInnerInstance _a _b _t _f +!insertmacro _UAC_MakeLL_Cmp _!= 0 3s +!macroend + + + +/* UAC_PageElevation_OnInit, UAC_PageElevation_OnGuiInit, +** +** Helper macros for elevation on a custom elevation page, see the DualMode example for more information. +** +**/ +!macro UAC_Notify_OnGuiInit +UAC::_ 4 +!macroend +!macro UAC_PageElevation_OnGuiInit +!insertmacro UAC_Notify_OnGuiInit +!macroend +!macro UAC_PageElevation_OnInit +UAC::_ 5 +${IfThen} ${Errors} ${|} Quit ${|} +!macroend + + + +/* UAC_AsUser_Call +** +** Calls a function or label in the user process instance. +** All the UAC_AsUser_* macros use this helper macro. +** +**/ +!define UAC_SYNCREGISTERS 0x1 +;define UAC_SYNCSTACK 0x2 +!define UAC_SYNCOUTDIR 0x4 +!define UAC_SYNCINSTDIR 0x8 +;define UAC_CLEARERRFLAG 0x10 +!macro UAC_AsUser_Call type name flags +push $0 +Get${type}Address $0 ${name} +!verbose push +!verbose ${UAC_VERBOSE} +!insertmacro _UAC_ParseDefineFlagsToInt _UAC_AsUser_Call__flags ${flags} +!verbose pop +StrCpy $0 "1$0:${_UAC_AsUser_Call__flags}" +!undef _UAC_AsUser_Call__flags +Exch $0 +UAC::_ +!macroend + + + +/* +** UAC_AsUser_GetSection +*/ +!macro UAC_AsUser_GetSection secprop secidx outvar +!insertmacro _UAC_AsUser_GenOp ${outvar} SectionGet${secprop} ${secidx} "" +!macroend + + + +/* +** UAC_AsUser_GetGlobalVar +** UAC_AsUser_GetGlobal +*/ +!macro UAC_AsUser_GetGlobalVar var +!insertmacro _UAC_AsUser_GenOp ${var} StrCpy "" ${var} +!macroend +!macro UAC_AsUser_GetGlobal outvar srcvar +!insertmacro _UAC_AsUser_GenOp ${outvar} StrCpy "" ${srcvar} +!macroend + + + +/* +** UAC_AsUser_ExecShell +** +** Call ExecShell in the user process instance. +** +*/ +!macro UAC_AsUser_ExecShell verb command params workdir show +!insertmacro _UAC_IncL +goto _UAC_L_E_${__UAC_L} +_UAC_L_F_${__UAC_L}: +ExecShell "${verb}" "${command}" '${params}' ${show} +return +_UAC_L_E_${__UAC_L}: +!if "${workdir}" != "" + push $outdir + SetOutPath "${workdir}" +!endif +!insertmacro UAC_AsUser_Call Label _UAC_L_F_${__UAC_L} ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR} #|${UAC_CLEARERRFLAG} +!if "${workdir}" != "" + pop $outdir + SetOutPath $outdir +!endif +!macroend + + + +!macro _UAC_MakeLL_Cmp cmpop cmp pluginparams +!insertmacro _LOGICLIB_TEMP +UAC::_ ${pluginparams} +pop $_LOGICLIB_TEMP +!insertmacro ${cmpop} $_LOGICLIB_TEMP ${cmp} `${_t}` `${_f}` +!macroend +!macro _UAC_definemath def val1 op val2 +!define /math _UAC_definemath "${val1}" ${op} ${val2} +!ifdef ${def} + !undef ${def} +!endif +!define ${def} "${_UAC_definemath}" +!undef _UAC_definemath +!macroend +!macro _UAC_ParseDefineFlags_orin parse outflags +!searchparse /noerrors ${${parse}} "" _UAC_ParseDefineFlags_orin_f1 "|" _UAC_ParseDefineFlags_orin_f2 +!define _UAC_ParseDefineFlags_orin_this ${_UAC_ParseDefineFlags_orin_f1} +!undef ${parse} +!define ${parse} ${_UAC_ParseDefineFlags_orin_f2} +!define _UAC_ParseDefineFlags_orin_saveout ${${outflags}} +!undef ${outflags} +!define /math ${outflags} "${_UAC_ParseDefineFlags_orin_saveout}" | "${_UAC_ParseDefineFlags_orin_this}" +!undef _UAC_ParseDefineFlags_orin_saveout +!undef _UAC_ParseDefineFlags_orin_this +!ifdef _UAC_ParseDefineFlags_orin_f1 + !undef _UAC_ParseDefineFlags_orin_f1 +!endif +!ifdef _UAC_ParseDefineFlags_orin_f2 + !undef _UAC_ParseDefineFlags_orin_f2 +!endif +!macroend +!macro _UAC_ParseDefineFlags_Begin _outdef _in +!define _UAC_PDF${_outdef}_parse "${_in}" +!define _UAC_PDF${_outdef}_flags "" +!define _UAC_PDF${_outdef}_r 0 +!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x1 +!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x2 +!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x4 +!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x8 +!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x10 +!macroend +!macro _UAC_ParseDefineFlags_End _outdef +!define ${_outdef} ${_UAC_PDF${_outdef}_r} +!undef _UAC_PDF${_outdef}_r +!undef _UAC_PDF${_outdef}_flags +!undef _UAC_PDF${_outdef}_parse +!macroend +!macro _UAC_ParseDefineFlags_IncludeFlag _outdef flag +!if ${_UAC_PDF${_outdef}_flags} & ${flag} + !insertmacro _UAC_definemath _UAC_PDF${_outdef}_r ${_UAC_PDF${_outdef}_r} | ${flag} +!endif +!macroend +!macro _UAC_ParseDefineFlagsToInt _outdef _in +!insertmacro _UAC_ParseDefineFlags_Begin _UAC_ParseDefineFlagsToInt_tmp "${_in}" +!define ${_outdef} ${_UAC_PDF_UAC_ParseDefineFlagsToInt_tmp_flags} +!insertmacro _UAC_ParseDefineFlags_End _UAC_ParseDefineFlagsToInt_tmp +!undef _UAC_ParseDefineFlagsToInt_tmp +!macroend +!macro _UAC_IncL +!insertmacro _UAC_definemath __UAC_L "${__UAC_L}" + 1 +!macroend +!macro _UAC_AsUser_GenOp outvar op opparam1 opparam2 +!define _UAC_AUGOGR_ID _UAC_AUGOGR_OP${outvar}${op}${opparam1}${opparam2} +!ifndef ${_UAC_AUGOGR_ID} ;Has this exact action been done before? + !if ${outvar} == $0 + !define ${_UAC_AUGOGR_ID} $1 + !else + !define ${_UAC_AUGOGR_ID} $0 + !endif + !if "${opparam1}" == "" + !define _UAC_AUGOGR_OPP1 ${${_UAC_AUGOGR_ID}} + !define _UAC_AUGOGR_OPP2 ${opparam2} + !else + !define _UAC_AUGOGR_OPP1 ${opparam1} + !define _UAC_AUGOGR_OPP2 ${${_UAC_AUGOGR_ID}} + !endif + goto ${_UAC_AUGOGR_ID}_C + ${_UAC_AUGOGR_ID}_F: + ${op} ${_UAC_AUGOGR_OPP1} ${_UAC_AUGOGR_OPP2} + return + ${_UAC_AUGOGR_ID}_C: + !undef _UAC_AUGOGR_OPP1 + !undef _UAC_AUGOGR_OPP2 +!endif +push ${${_UAC_AUGOGR_ID}} +!insertmacro UAC_AsUser_Call Label ${_UAC_AUGOGR_ID}_F ${UAC_SYNCREGISTERS} +StrCpy ${outvar} ${${_UAC_AUGOGR_ID}} +pop ${${_UAC_AUGOGR_ID}} +!undef _UAC_AUGOGR_ID +!macroend + + + +!verbose pop +!endif /* UAC_HDR__INC */ \ No newline at end of file diff --git a/data/nsis/Inetc.zip b/data/nsis/Inetc.zip new file mode 100644 index 000000000..0da83c6e6 Binary files /dev/null and b/data/nsis/Inetc.zip differ diff --git a/data/nsis/Md5dll.zip b/data/nsis/Md5dll.zip new file mode 100644 index 000000000..1d807717b Binary files /dev/null and b/data/nsis/Md5dll.zip differ diff --git a/data/nsis/NsisMultiUser.zip b/data/nsis/NsisMultiUser.zip new file mode 100755 index 000000000..dbb908164 Binary files /dev/null and b/data/nsis/NsisMultiUser.zip differ diff --git a/data/nsis/Nsisunz.zip b/data/nsis/Nsisunz.zip new file mode 100644 index 000000000..e7ee2d91d Binary files /dev/null and b/data/nsis/Nsisunz.zip differ diff --git a/data/nsis/Plugins/amd64-unicode/INetC.dll b/data/nsis/Plugins/amd64-unicode/INetC.dll new file mode 100644 index 000000000..fad7ad42e Binary files /dev/null and b/data/nsis/Plugins/amd64-unicode/INetC.dll differ diff --git a/data/nsis/Plugins/x86-ansi/INetC.dll b/data/nsis/Plugins/x86-ansi/INetC.dll new file mode 100644 index 000000000..d064bc1ac Binary files /dev/null and b/data/nsis/Plugins/x86-ansi/INetC.dll differ diff --git a/data/nsis/Plugins/x86-ansi/UAC.dll b/data/nsis/Plugins/x86-ansi/UAC.dll new file mode 100755 index 000000000..57a58c539 Binary files /dev/null and b/data/nsis/Plugins/x86-ansi/UAC.dll differ diff --git a/data/nsis/Plugins/x86-ansi/md5dll.dll b/data/nsis/Plugins/x86-ansi/md5dll.dll new file mode 100644 index 000000000..003e16378 Binary files /dev/null and b/data/nsis/Plugins/x86-ansi/md5dll.dll differ diff --git a/data/nsis/Plugins/x86-ansi/nsisunz.dll b/data/nsis/Plugins/x86-ansi/nsisunz.dll new file mode 100644 index 000000000..5466f1568 Binary files /dev/null and b/data/nsis/Plugins/x86-ansi/nsisunz.dll differ diff --git a/data/nsis/Plugins/x86-unicode/INetC.dll b/data/nsis/Plugins/x86-unicode/INetC.dll new file mode 100644 index 000000000..b766595f2 Binary files /dev/null and b/data/nsis/Plugins/x86-unicode/INetC.dll differ diff --git a/data/nsis/Plugins/x86-unicode/UAC.dll b/data/nsis/Plugins/x86-unicode/UAC.dll new file mode 100755 index 000000000..97e7d15ea Binary files /dev/null and b/data/nsis/Plugins/x86-unicode/UAC.dll differ diff --git a/data/nsis/Plugins/x86-unicode/md5dll.dll b/data/nsis/Plugins/x86-unicode/md5dll.dll new file mode 100644 index 000000000..bde153f70 Binary files /dev/null and b/data/nsis/Plugins/x86-unicode/md5dll.dll differ diff --git a/data/nsis/Plugins/x86-unicode/nsisunz.dll b/data/nsis/Plugins/x86-unicode/nsisunz.dll new file mode 100644 index 000000000..5466f1568 Binary files /dev/null and b/data/nsis/Plugins/x86-unicode/nsisunz.dll differ diff --git a/doc/build_win.rst b/doc/build_win.rst new file mode 100644 index 000000000..21a0678e2 --- /dev/null +++ b/doc/build_win.rst @@ -0,0 +1,141 @@ +============================ +Building a Windows installer +============================ + +For building a Windows installer you will need an installation of Windows. The +method described here has been used successfully on Windows 7, 8 and 10. +Windows Vista should also work but has not been tested. + + +.. admonition:: technical note + :class: note + + If you do not have Windows, yet still wish to produce an installer, + a virtual machine will suffice. Virtual machine images are available `here + from Microsoft + `_. + +.. admonition:: already installed? + :class: note + + There are many similarities to the usual installation process here. If you + have already installed ghini.desktop you can skip the first two steps and + proceed to step 4 + +#. Download and install the git, Python and PyGTK external dependencies as + outlined in the installation instructions. You can use the direct links + provided here. (`git `_, `Python `_, `PyGTK `_) but + remember to select to put Python in the PATH and install **all** components + of PyGTK. + +#. Download and install `NSIS v3 `_. + (Unless you only wish to freeze the code - see below) + +#. A **reboot** is recommended. + +#. Clone ghini.desktop to wherever you want to keep it and checkout the branch + you wish to build. To do this, open a command prompt and type these + commands:: + + cd + git clone https://github.com/Ghini/ghini.desktop.git + cd ghini.desktop + git checkout ghini-1.0 + + .. admonition:: already installed? + :class: note + + If you have already installed ghini.desktop following the installation + procedure you can just use that copy instead. In this situation you would + only need to enter ``cd %HOMEPATH%\Local\github\Ghini\ghini.desktop`` for + this step. + +#. Build the installer: Assuming the command prompt from the last step is + still open (or just open a new one and ``cd ``) + run this command:: + + scripts\build_win.bat + + This should leave a file named ``ghini.desktop--setup.exe`` in the + ``scripts`` folder of ghini.desktop. This is your Windows installer. + + .. admonition:: technical note + :class: note + + If working in a virtual machine this would be a good place to take + a snapshot. You will be able to immediately return to this point, where + all requirements are installed and a command prompt is open at the + correct place, without waiting for the virtual machine to boot. + + It is also worth noting that the ``dist`` folder will contain a full working + copy of the software in a frozen, self contained state, that can be + transferred however you like and will work in place. (e.g. placed on a USB + flash drive for demonstration purposes or copied manually to ``C:\Program + Files``). If all you want is this frozen copy then you will not need to + install NSIS and can run the above command with the '/f' switch (i.e. + ``scripts\build_win.bat /f``). To start ghini.desktop double click + ``ghini.exe`` in explorer (or create a shortcut to it). If you have issues + with PyGTK not displaying correctly you need to run the script + ``win_gtk.bat`` from the ``dist`` folder to set up paths correctly. Runing + ``build_win /f`` will place this script in the dist folder for you. You + will only need to run this once each time the location of the folder + changes. There after ``ghini.exe`` will run as expected. + +In future if you wish to build further installers just open a command prompt +and enter:: + + cd + git pull + scripts\build_win.bat + +.. admonition:: technical note + :class: note + + If you have been using a virtual machine as descibed here you would just + restore the snapshot and use ``git pull`` followed by + ``scripts\build_win.bat`` + + +.. admonition:: about the installer + :class: note + + - Capable of single user or global installs. + + - At this point in time ghini.desktop installed this way will not check + or or notify you of any updated version. You will need to check + yourself. + + - Capable of downloading and installing optional extra components: + + - Apache FOP - If you plan on using xslt templates to produce PDFs then + install FOP. FOP requires the Java Runtime. If you do not currently + have it installed the installer will let you know and offer to open + the Oracle web site for you to download and install it from. + + - MS Visual C runtime - You most likely don't need this but if you have + any trouble getting ghini.desktop to run try installing the MS Visual + C runtime (e.g. rerun the installer and select this component only). + + - Can be run silently using switches (e.g. for remote deployment) where: + + - ``/S`` for silent; + + - ``/AllUser`` or ``/CurrentUser`` + + - ``/C=[gFC]`` to specify components where: + + ``g`` = Deselect the main ghini.desktop component (used for + component only installs) + + ``F`` = select Apache FOP + + ``C`` = select MS Visual C runtime + + +.. _Direct link to download git: https://github.com/git-for-windows/git/releases/download/v2.13.3.windows.1/Git-2.13.3-32-bit.exe +.. _Direct link to download Python: https://www.python.org/ftp/python/2.7.12/python-2.7.12.msi +.. _Direct link to download lxml: https://pypi.python.org/packages/2.7/l/lxml/lxml-3.6.0.win32-py2.7.exe +.. _Direct link to download PyGTK: http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.24/pygtk-all-in-one-2.24.2.win32-py2.7.msi +.. _Direct link to download psycopg2: http://www.stickpeople.com/projects/python/win-psycopg/2.6.1/psycopg2-2.6.1.win32-py2.7-pg9.4.4-release.exe + diff --git a/scripts/Add_to_PATH.vbs b/scripts/add_to_path.vbs similarity index 100% rename from scripts/Add_to_PATH.vbs rename to scripts/add_to_path.vbs diff --git a/scripts/build-multiuser.nsi b/scripts/build-multiuser.nsi index 641f9e693..0cf5e99e0 100755 --- a/scripts/build-multiuser.nsi +++ b/scripts/build-multiuser.nsi @@ -37,7 +37,7 @@ ; A component has failed to install (e.g. FOP mirror is down) so you rerun for that component only ;--- -; Plugins, required to compile: +; Plugins, required to compile: (included in data\nsis) ; - ; nsExec (included in NSIS v3.0) for executing commands ; WordFunc.nsh (included in NSIS v3.0) for comparing versions @@ -49,6 +49,9 @@ ; Inetc (http://nsis.sourceforge.net/Inetc_plug-in) ; MD5 (http://nsis.sourceforge.net/MD5_plugin) ;--- +!addplugindir /x86-ansi "..\data\nsis\Plugins\x86-ansi\" +!addplugindir /x86-unicode "..\data\nsis\Plugins\x86-unicode\" +!addincludedir "..\data\nsis\Include\" ;------------------------------ ; GENERAL @@ -56,14 +59,14 @@ ; Global Name "ghini.desktop" !define VERSION "1.0.75" ; :bump -!define src_dir "..\dist" +!define SRC_DIR "..\dist" !define PRODUCT_NAME "ghini.desktop" Outfile "${PRODUCT_NAME}-${VERSION}-setup.exe" !define PROGEXE "ghini.exe" !define COMPANY_NAME "" -!define license_file "LICENSE" -!define readme "README.rst" -!define startmenu "$SMPROGRAMS\${PRODUCT_NAME}" +!define LICENSE_FILE "LICENSE" +!define README "README.rst" +!define START_MENU "$SMPROGRAMS\${PRODUCT_NAME}" !define UNINSTALL_FILENAME "uninstall.exe" ; FOP @@ -113,11 +116,11 @@ CRCCheck on ; Modern User Interface v2 Settings !define MUI_ABORTWARNING !define MUI_UNABORTWARNING -!define MUI_ICON "${src_dir}\bauble\images\icon.ico" -!define MUI_UNICON "${src_dir}\bauble\images\icon.ico" +!define MUI_ICON "${SRC_DIR}\bauble\images\icon.ico" +!define MUI_UNICON "${SRC_DIR}\bauble\images\icon.ico" !define MUI_HEADERIMAGE -!define MUI_HEADERIMAGE_BITMAP "${src_dir}\bauble\images\ghini_logo.bmp" -!define MUI_HEADERIMAGE_UNBITMAP "${src_dir}\bauble\images\ghini_logo.bmp" +!define MUI_HEADERIMAGE_BITMAP "${SRC_DIR}\bauble\images\ghini_logo.bmp" +!define MUI_HEADERIMAGE_UNBITMAP "${SRC_DIR}\bauble\images\ghini_logo.bmp" !define MUI_HEADERIMAGE_RIGHT !define MUI_COMPONENTSPAGE_SMALLDESC !define MUI_COMPONENTSPAGE_TEXT_COMPLIST "Or, select the optional components you wish to install: \ @@ -139,8 +142,8 @@ CRCCheck on ; NsisMultiUser - all settings need to be set before including the NsisMultiUser.nsh header file. ; thanks to Richard Drizin https://github.com/Drizin/NsisMultiUser !include "NsisMultiUser.nsh" -!include "MUI2.nsh" !include "UAC.nsh" +!include "MUI2.nsh" !include "WordFunc.nsh" !include "FileFunc.nsh" @@ -149,7 +152,7 @@ CRCCheck on ; PAGES ; Installer -!insertmacro MUI_PAGE_LICENSE "${src_dir}\${license_file}" +!insertmacro MUI_PAGE_LICENSE "${SRC_DIR}\${LICENSE_FILE}" !insertmacro MULTIUSER_PAGE_INSTALLMODE ; this will show the 2 install options, unless it's an elevated inner process ; (in that case we know we should install for all users) @@ -169,7 +172,6 @@ CRCCheck on ; LANGUAGES ; MUIv2 macros (must be after scripts and pages) -; TODO add more languages? !insertmacro MUI_LANGUAGE English @@ -196,7 +198,7 @@ Section "!Ghini.desktop" SecMain SetOverwrite on ; package all files, recursively, preserving attributes ; assume files are in the correct places - File /a /r "${src_dir}\*.*" + File /a /r "${SRC_DIR}\*.*" ; Create uninstaller WriteUninstaller "$INSTDIR\${UNINSTALL_FILENAME}" @@ -204,8 +206,8 @@ Section "!Ghini.desktop" SecMain ; add registry keys !insertmacro MULTIUSER_RegistryAddInstallInfo ; create shortcuts - CreateDirectory "${startmenu}" - CreateShortcut "${startmenu}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PROGEXE}" \ + CreateDirectory "${START_MENU}" + CreateShortcut "${START_MENU}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PROGEXE}" \ "" "$INSTDIR\${PROGEXE}" "" SW_SHOWNORMAL \ "" "Ghini biodiversity collection manager" ; desktop shortcut @@ -375,7 +377,7 @@ Section /o "MS Visual C runtime DLL (1.73MB Download)" SecMSC Goto DoneMSVC ; Install MS Visual C Runtime - ; TODO there seems to be a bug in the installer that leaves junk files in the root directory of the largest drive + ; there seems to be a bug in the installer that leaves junk files in the root directory of the largest drive. InstalMSVC: ; run installer silently (no user input, no cancel button) ExecWait '"$PLUGINSDIR\${MSVC_FILE}" /qb!' @@ -405,7 +407,6 @@ SectionGroupEnd ; UNINSTALLER SECTIONS ; ; All section names prefixed by "Un" will be in the uninstaller -; TODO include a FOP uninstaller ; Settings UninstallText "This will uninstall ${PRODUCT_NAME}." @@ -415,11 +416,11 @@ UninstallText "This will uninstall ${PRODUCT_NAME}." Section "Uninstall" SecUnMain ; Remove registry keys !insertmacro MULTIUSER_RegistryRemoveInstallInfo - Delete "${startmenu}\*.*" + Delete "${START_MENU}\*.*" Delete "$DESKTOP\${PRODUCT_NAME}.lnk" SetOutPath $TEMP RMDir /r "$INSTDIR" - RMDir /r "${startmenu}" + RMDir /r "${START_MENU}" SectionEnd @@ -479,8 +480,8 @@ Function AddFOPtoPATH ; copy the script to a temp dir SetOutPath "$PLUGINSDIR\" SetOverwrite on - File /a "Add_to_PATH.vbs" - ExecWait '"$SYSDIR\wscript.exe" //E:vbscript "$PLUGINSDIR\Add_to_PATH.vbs" /path:"$R0\fop-${FOP_VERSION}\" /env:"$R1"' + File /a "add_to_path.vbs" + ExecWait '"$SYSDIR\wscript.exe" //E:vbscript "$PLUGINSDIR\add_to_path.vbs" /path:"$R0\fop-${FOP_VERSION}\" /env:"$R1"' DetailPrint "Apache FOP added to $R1 PATH as: $R0\fop-${FOP_VERSION}\" SetRebootFlag True DetailPrint "Reboot flag = True" diff --git a/scripts/build.nsi b/scripts/build.nsi deleted file mode 100755 index 722bb927a..000000000 --- a/scripts/build.nsi +++ /dev/null @@ -1,145 +0,0 @@ -; Copyright (c) 2004-2017 lauchpad and github authors -; -; This file is part of ghini.desktop. -; -; ghini.desktop 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 3 of the License, or -; (at your option) any later version. -; -; ghini.desktop 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 ghini.desktop. If not, see . -; -; NSIS Install Script for ghini.desktop -; -; TODO: should create two version of this installer, one that bundles GTK and -; one that doesn't -; could also create one that asks the use if they would like to download and -; install the GTK libs if they aren't available, ala slickrun - -; include Modern UI -!include "MUI.nsh" - -; general -Name "Ghini" - -!define version "1.0.75" ; :bump -!define src_dir "../dist" -Outfile "ghini.desktop-${version}-setup.exe" - -!define prodname "ghini.desktop" -!define exec "ghini.exe" -!define license_file "LICENSE" -!define readme "README.rst" - -; icons must be Microsoft .ICO files -; !define icon "${src_dir}/bauble/images/icon.ico" - -; file containing list of file-installation commands -; !define files "files.nsi" - -; file containing list of file-uninstall commands -; !define unfiles "unfiles.nsi" - -; registry stuff -!define regkey "Software\${prodname}" -!define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prodname}" - -!define startmenu "$SMPROGRAMS\${prodname}" -!define uninstaller "uninstall.exe" - -SetCompressor /SOLID lzma -SetDateSave on -SetDatablockOptimize on -CRCCheck on -SilentInstall normal - -InstallDir "$PROGRAMFILES\${prodname}" -InstallDirRegKey HKCU "${regkey}" "" - -;-------------------------------- -;Interface Settings -; MUI Settings - -!define MUI_ABORTWARNING -!define MUI_UNABORTWARNING -!define MUI_ICON "${src_dir}/bauble/images/icon.ico" -!define MUI_UNICON "${src_dir}/bauble/images/icon.ico" -!define MUI_HEADERIMAGE -!define MUI_HEADERIMAGE_BITMAP "${src_dir}\bauble\images\ghini_logo.bmp" -!define MUI_HEADERIMAGE_UNBITMAP "${src_dir}\bauble\images\ghini_logo.bmp" -!define MUI_HEADERIMAGE_RIGHT -; allow users to check install log before continuing -!define MUI_FINISHPAGE_NOAUTOCLOSE -!define MUI_FINISHPAGE_NOREBOOTSUPPORT -!define MUI_FINISHPAGE_RUN_TEXT "Start Ghini" -!define MUI_FINISHPAGE_RUN $INSTDIR\${exec} -!define MUI_FINISHPAGE_LINK "Visit the Ghini home page" -!define MUI_FINISHPAGE_LINK_LOCATION http://ghini.github.io/ - -;-------------------------------- -;Pages - -!insertmacro MUI_PAGE_LICENSE "${src_dir}/${license_file}" -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH - -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES -;-------------------------------- -;Languages - -!insertmacro MUI_LANGUAGE "English" -;-------------------------------- -;Installer Sections - -Section "Dummy Section" SecDummy - SetOutPath "$INSTDIR" - ;Store installation folder - WriteRegStr HKCU "${regkey}" "" $INSTDIR - ; Uninstall reg keys - WriteRegStr HKLM "${uninstkey}" "DisplayName" "${prodname} ${version} (remove only)" - WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"' - ;Create uninstaller - WriteUninstaller "$INSTDIR\${uninstaller}" - - ; package all files, recursively, preserving attributes - ; assume files are in the correct places - File /a /r "${src_dir}\*.*" - -SectionEnd - -; create shortcuts -Section - SetOutPath $INSTDIR ; for working directory - CreateDirectory "${startmenu}" - CreateShortCut "${startmenu}\${prodname}.lnk" "$INSTDIR\${exec}" - ; run gdk-pixbuf-query-loaders, gtk-query-immodules & pango-querymodules - ReadEnvStr $0 COMSPEC - nsExec::ExecToLog '$0 /C gtk\bin\pango-querymodules.exe > gtk\etc\pango\pango.modules' - nsExec::ExecToLog '$0 /C gtk\bin\gtk-query-immodules-2.0.exe > gtk\etc\gtk-2.0\gtk.immodules' - nsExec::ExecToLog '$0 /C gtk\bin\gdk-pixbuf-query-loaders.exe > gtk\etc\gtk-2.0\gdk-pixbuf.loaders' - nsExec::ExecToLog '$0 /C gtk\bin\gdk-pixbuf-query-loaders.exe > gtk\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache' -SectionEnd - -; Uninstaller -; All section names prefixed by "Un" will be in the uninstaller - -UninstallText "This will uninstall ${prodname}." - -Section "Uninstall" - DeleteRegKey HKLM "${uninstkey}" - DeleteRegKey HKLM "${regkey}" - Delete "${startmenu}\*.*" - Delete "${startmenu}" - SetOutPath $TEMP - RMDir /r "$INSTDIR" - RMDir /r ${startmenu} -SectionEnd - diff --git a/scripts/build_win.bat b/scripts/build_win.bat new file mode 100644 index 000000000..25cb3b04f --- /dev/null +++ b/scripts/build_win.bat @@ -0,0 +1,43 @@ +@echo off + +IF NOT EXIST "%HOMEDRIVE%%HOMEPATH%"\.virtualenvs\ghi2exe\Scripts\activate.bat ( + ECHO creating an isolated virtual environment to build in + pip install virtualenv + virtualenv --system-site-packages "%HOMEDRIVE%%HOMEPATH%"\.virtualenvs\ghi2exe +) + +IF "%VIRTUAL_ENV%"=="" ( + ECHO Activating virtual environment to build in. + call "%HOMEDRIVE%%HOMEPATH%"\.virtualenvs\ghi2exe\Scripts\activate.bat +) else ( + ECHO Currently using virtual environment: "%VIRTUAL_ENV%" + IF NOT "%VIRTUAL_ENV%"=="%HOMEDRIVE%%HOMEPATH%\.virtualenvs\ghi2exe" ( + ECHO deactivating previous virtual environment and activating one to build in + deactivate + call "%HOMEDRIVE%%HOMEPATH%"\.virtualenvs\ghi2exe\Scripts\activate.bat + ) +) + +ECHO Installing dependancies +pip install py2exe_py2 +pip install psycopg2 +pip install Pygments + +ECHO cleaning up +python setup.py clean + +ECHO installing without eggs +pip install . + +ECHO building frozen +python setup.py py2exe + +REM Freeze only? +if "%1"=="/f" GOTO SKIP_NSIS + +ECHO building NSIS installer +python setup.py nsis +GOTO :EOF + +:SKIP_NSIS +copy scripts\win_gtk.bat dist diff --git a/scripts/bump_version.py b/scripts/bump_version.py index e29acaf22..deb7c6357 100755 --- a/scripts/bump_version.py +++ b/scripts/bump_version.py @@ -125,7 +125,7 @@ def bump_desktop_file(filename): bump_file(filename, rx) -def bump_nsi_file(filename, varname='version'): +def bump_nsi_file(filename, varname='VERSION'): """ bump NSIS installer files """ @@ -136,8 +136,7 @@ def bump_nsi_file(filename, varname='version'): bump_py_file(os.path.join(root_of_clone(), 'bauble/version.py')) bump_py_file(os.path.join(root_of_clone(), 'doc/conf.py'), 'release') bump_desktop_file(os.path.join(root_of_clone(), 'data/ghini.desktop')) -bump_nsi_file(os.path.join(root_of_clone(), 'scripts/build.nsi')) -bump_nsi_file(os.path.join(root_of_clone(), 'scripts/build-multiuser.nsi'), 'VERSION') +bump_nsi_file(os.path.join(root_of_clone(), 'scripts/build-multiuser.nsi')) rx = "(^VERSION=\").*?\..*?\..*?(\".*?%s.*?$)" % bump_tag bump_file(os.path.join(root_of_clone(), 'packages/builddeb.sh'), rx) diff --git a/scripts/win_gtk.bat b/scripts/win_gtk.bat new file mode 100644 index 000000000..0c43cd908 --- /dev/null +++ b/scripts/win_gtk.bat @@ -0,0 +1,4 @@ +gtk\bin\pango-querymodules.exe > gtk\etc\pango\pango.modules' +gtk\bin\gtk-query-immodules-2.0.exe > gtk\etc\gtk-2.0\gtk.immodules' +gtk\bin\gdk-pixbuf-query-loaders.exe > gtk\etc\gtk-2.0\gdk-pixbuf.loaders' +gtk\bin\gdk-pixbuf-query-loaders.exe > gtk\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache' diff --git a/setup.py b/setup.py index 79bd49499..17d474e24 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # # Copyright (c) 2005,2006,2007,2008,2009 Brett Adams # Copyright (c) 2015 Mario Frasca -# Copyright (c) 2016 Ross Demuth +# Copyright (c) 2016,2017 Ross Demuth # # This file is part of ghini.desktop. # @@ -58,11 +58,12 @@ exclude=['test', 'bauble.*.test', 'ghini.*.test']) plugins_pkgs = ['bauble.plugins.%s' % p for p in plugins] all_packages = setuptools.find_packages(exclude=['test', 'bauble.*.test', - 'ghini.*.test']) + 'ghini.*.test']) package_data = {'': ['README.rst', 'CHANGES', 'LICENSE'], - 'bauble': ['*.ui', '*.glade', 'images/*.png', 'pixmaps/*.png', - 'images/*.svg', 'images/*.gif', 'images/*.ico', 'images/*.bmp']} + 'bauble': ['*.ui', '*.glade', 'images/*.png', 'pixmaps/*.png', + 'images/*.svg', 'images/*.gif', 'images/*.ico', + 'images/*.bmp']} # ceate a list of the data patterns to look for in the packages data_patterns = ['default/*.txt', '*.ui', '*.glade', '*.xsl', '*.xsd', @@ -85,8 +86,9 @@ sqlalchemy_includes = ['sqlalchemy.dialects.sqlite', 'sqlalchemy.dialects.postgresql'] py2exe_includes = ['sqlite3', 'lxml', 'gdata', 'fibra', 'psycopg2', - 'encodings', 'mako', 'mako.cache', 'pygments.styles.default', - 'pyparsing'] + gtk_pkgs + plugins_pkgs + sqlalchemy_includes + 'encodings', 'mako', 'mako.cache', + 'pygments.styles.default', 'pyparsing'] + py2exe_includes += gtk_pkgs + plugins_pkgs + sqlalchemy_includes py2exe_setup_args = { 'windows': [{'script': 'scripts/ghini', 'icon_resources': [(1, "bauble/images/icon.ico")]}]} @@ -105,7 +107,7 @@ "libgtk-win32-2.0-0.dll", "libpango-1.0-0.dll", "libpangowin32-1.0-0.dll", "libxml2-2.dll", # windows dlls - "DNSAPI.DLL","MSIMG32.DLL", "Secur32.dll", "SHFOLDER.dll", + "DNSAPI.DLL", "MSIMG32.DLL", "Secur32.dll", "SHFOLDER.dll", "CRYPT32.dll", "MPR.dll" ] } @@ -127,6 +129,7 @@ [m.replace(os.sep, '/') for m in matches])) class py2exe_cmd(_py2exe_cmd): + description = 'build standalone Windows executable' def run(self): # TODO: make sure we have everything installed that we need to # bundle e.g. sqlite, psycopg2, others... @@ -176,25 +179,47 @@ def run(self): file_util.copy_file(gtheme, dest) # copy LICENSE to dist\share\LICENSE.ghini (for help>about) - file_util.copy_file("LICENSE", - os.path.join(self.dist_dir, 'share', 'LICENSE.ghini')) - - class nsis_cmd(Command): - # 1. copy the gtk dist to the dist directory - # 2. run the script to update the pixbuf paths - # 3. run the nsis command to create the installer - # 4. do everything silent, don't use the NSIS compiler GUI - user_options = [] + file_util.copy_file("LICENSE", os.path.join(self.dist_dir, 'share', + 'LICENSE.ghini')) + + class NsisCmd(Command): + """Command to create a Windows NSIS installer""" + + description = 'build windows NSIS installer' + + user_options = [ + ('makensis=', 'm', 'path to makensis'), + ('nsis-script=', 's', 'script to build installer from') + ] def initialize_options(self): - pass + envars = ['programw6432', 'programfiles', 'localappdata'] + mns = 'nsis\\makensis' + locs = [os.path.join(os.getenv(ev, 'c:\\'), mns) for ev in envars] + self.makensis = ( + [loc for loc in locs if spawn.find_executable(loc)] + + [spawn.find_executable('makensis')])[0] + self.nsis_script = 'scripts\\build-multiuser.nsi' def finalize_options(self): - pass + if self.makensis: + is_exe = spawn.find_executable(self.makensis) is not None + exe_name = os.path.split(self.makensis)[-1] + is_makensis = exe_name in ('makensis', 'makensis.exe') + if not is_exe or not is_makensis: + raise Exception('makensis not found at: %s' % self.makensis) + else: + raise Exception('can not find makensis, NSIS needs to be ' + 'installed in the default location, on the ' + 'path or provided using --makensis=') + + if not os.path.exists(self.nsis_script): + raise Exception('can not find nsis build script at: %s' + % self.nsis_script) def run(self): - print "**Error: Can't run this command." - print sys.exit(1) + print 'using %s to build %s' % (self.makensis, self.nsis_script) + os.system('"%s" %s' % (self.makensis, self.nsis_script)) else: py2exe_options = {} @@ -215,9 +240,11 @@ def run(self): print sys.exit(1) class py2exe_cmd(_empty_cmd): + description = 'build Windows executable *ONLY AVAILABLE IN WINDOWS' pass - class nsis_cmd(_empty_cmd): + class NsisCmd(_empty_cmd): + description = 'build Windows NSIS installer *ONLY AVAILABLE IN WINDOWS' pass @@ -422,13 +449,13 @@ def run(self): scripts = ["scripts/ghini"] if sys.platform == 'win32': scripts = ["scripts/ghini", "scripts/ghini.bat", "scripts/ghini.vbs", - "scripts/ghini-update.bat"] + "scripts/ghini-update.bat"] # TODO: images in bauble/images should really be in data and copied as # package_data or data_files setuptools.setup(name="ghini.desktop", cmdclass={'build': build, 'install': install, - 'py2exe': py2exe_cmd, 'nsis': nsis_cmd, + 'py2exe': py2exe_cmd, 'nsis': NsisCmd, 'docs': docs, 'clean': clean, 'run': run}, version=version, scripts=scripts,