how to allow only specific users to unhide a worksheet - vba

I already have a macro below that un-hides a worksheet at the click of a button and works okay. However I want this macro to be changed so that ONLY two users (whose usernames are "JSMITH" AND "DTAYLOR") are able to unhide this sheet called "Rates".
If someone else (whose username is not one of the two mentioned above) tries to unhide the sheet, I want Excel to display a message "you're not authorised to open this".
Moreover, I need to make sure that only those two users are able to un-hide in a traditional way without vba (eg by right-clicking on a visible worksheet tab and choose Unhide or from any worksheet tab, choose Format, Sheet, and then Unhide).
Could you please advise how to modify the following code to do the all the things described above?
I came up with this but it doesn't work:
Sub GoToRates_WS()
Select Case Environ$("username")
Case "jsmith", "taylor"
Worksheets("Rates").Visible = True
ThisWorkbook.Sheets("Rates").Activate
Case Else MsgBox "you're not authorised to open this"
End Select
End Sub

1- Open your ThisWorkbook code Module.
2- Paste this line at the top of it:
Private RatesVisible As Variant
3- find the following routine:
Private Sub Workbook_Open()
...
...
End Sub
Insert the following line just before the line End Sub:
RatesVisible = Worksheets("Rates").Visible
4- Delete your old routine GoToRates_WS
5- Copy the following code and paste it at the end of the code module:
Private Function privilegedUser() As Boolean
Select Case UCase(Environ$("username"))
Case "JSMITH", "DTAYLOR"
privilegedUser = True
Case Else
End Select
End Function
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
RatesVisible = Worksheets("Rates").Visible
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
If Sh.Name <> "Rates" Then
RatesVisible = Worksheets("Rates").Visible
Exit Sub
End If
If privilegedUser Then
RatesVisible = Worksheets("Rates").Visible
Else
Worksheets("Rates").Visible = RatesVisible
End If
End Sub
Private Sub GoToRates_WS()
If privilegedUser Then
RatesVisible = xlSheetVisible
Worksheets("Rates").Visible = xlSheetVisible
Else
MsgBox "You are not authorized to open this worksheet"
End If
End Sub

Related

Hide sheets VBA - Excel bug?

I am currently doing a VBA code that needs to hide some sheets when the Excel file is closed and almost everything is working fine expects when I do the following steps:
make some change/insert data in the sheets
click the save button
make another change (that I do not want to save)
click to close the file and click not to save it
The problem is that I hide the sheets but since I do not save the file (because i do not want to save the changes made at step 3) the sheets are not hidden when I reopen the file. I cannot do this with the Workbook_open method because it is not allowed (at my project).
To do this I am rewriting the beforeclose method, as follows:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim Msg As String
Dim ireply As Integer
If Not Me.Saved Then
Msg = "Do you want to save the file?"
ireply = MsgBox(Msg, vbQuestion + vbYesNoCancel)
Select Case ireply
Case vbYes
Call hidesheets
Me.Save
Case vbNo
Me.Saved = True
Application.Quit
Case vbCancel
Cancel = True
Exit Sub
End Select
Else
Call hidesheets
Me.Save
End If
End Sub
Sub hidesheets()
ThisWorkbook.Sheets("Cars").Visible = xlVeryHidden
ThisWorkbook.Sheets("Brands").Visible = xlVeryHidden
ThisWorkbook.Sheets("Models").Visible = xlVeryHidden
ThisWorkbook.Sheets("Price").Visible = xlVeryHidden
End Sub
My questions is, it is possible just to save the hidden sheets configurations/settings without saving the information/data changed/inserted by the user?
PS: when I save the file and make any change the code works fine, i.e. hides the sheets.
Thank you all in advance
Regards
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
ThisWorkbook.Sheets("Cars").Visible = xlVeryHidden
ThisWorkbook.Sheets("Brands").Visible = xlVeryHidden
ThisWorkbook.Sheets("Models").Visible = xlVeryHidden
ThisWorkbook.Sheets("Price").Visible = xlVeryHidden
End Sub
You have to do the other way around
1) Set your workbook having those four sheets as very hidden per default
set them as such and then save your workbook to have it assume its default configuration
2) When you open it you make those sheets visible
Private Sub Workbook_Open()
ThisWorkbook.Sheets("Cars").Visible = True
ThisWorkbook.Sheets("Brands").Visible = True
ThisWorkbook.Sheets("Models").Visible = True
ThisWorkbook.Sheets("Price").Visible = True
End Sub
3) When you close it, you set those sheets back invisible
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'Your code to be processed upon closing the sheet
'...
Call hidesheets '<--| hide your sheets
End Sub

Run Macro Dropdown List Excel

I wrote this Macro out to copy and paste info from a previous sheet to the active sheet. I want to make this into a dropdown list but when using data validation, the macro doesn't run when it is picked. Attached is my code and I am wondering should I make a list box or should I stick with data validation? I know there's a way to make a macro run once clicked in a click box
Sub WorkDay1()
ActiveSheet.Range("A6:H44").Value = Worksheets("Route Sheet - Manhattan 1").Range("A6:H44").Value
End Sub
Sub WorkDay2()
ActiveSheet.Range("A6:H44").Value = Worksheets("2").Range("A6:H44").Value
End Sub
Sub WorkDay3()
ActiveSheet.Range("A6:H44").Value = Worksheets("3").Range("A6:H44").Value
End Sub
Sub WorkDay4()
ActiveSheet.Range("A6:H44").Value = Worksheets("4").Range("A6:H44").Value
End Sub
Assuming your dropdown list is in cell A1.
Paste this code into the worksheets code module.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Application.EnableEvents = False
If Not Intersect(Target, Range("A1")) Is Nothing Then
Select Case Target.Value
Case "WorkDay1"
WorkDay1
Case "WorkDay2"
WorkDay2
Case "WorkDay3"
WorkDay3
Case "WorkDay4"
WorkDay4
End Select
End If
Application.EnableEvents = True
End Sub
In the VBA Project explore double click the Sheet that you wish to run the macro on. This will open up the code module for that sheet. Then paste the code into that module.
Adding this colud will update the values when you select the worksheet.
Private Sub Worksheet_Activate()
Select Case Range("A1")
Case "WorkDay1"
WorkDay1
Case "WorkDay2"
WorkDay2
Case "WorkDay3"
WorkDay3
Case "WorkDay4"
WorkDay4
End Select
End Sub

Can't Edit Cell Values After Clicking Excel UserForm

When I click a button on my UserForm it goes to the relevant sheet (via .activate and then End Sub, but I have also tried .select) but I cannot edit the cell. However, when I click into the sheet normally via the bottom pane I can edit it again.
I have not found an excel log or process manager so I cannot see what macros could be running or data that is loading* and could be affecting this - does anyone have any idea of the possible reasons I can't edit cell values after using the UserForm?
Here's my code for the button in question (I added the Unload Me part in the hope it would stop any additional UserForm subs leftover:
Private Sub CommandButton1_Click()
Sheets("2H Campaigns View").Select
Unload Me
End Sub
*Our sheets use quite a bit of external data but if this were the case I assume clicking the bottom pane to edit wouldn't work either...
I have also tried shutting out of the UserForm (and nay possible macros its running) immediately after click with Unload Me but to no avail.
EDIT: I put a print to cell function (on a separate debug sheet) in at the end of each sub to check if there are any others running after the button is clicked, but it reads that the button (the above sub) is the last sub to run. Thus its safe to assume that the problem is unrelated to the below subs; isolated to either the button sub or something which the running of the button sub does to the settings for the workbook....
Other Subs used in this sheet (all under UserForm - cbSector_Change and UserForm_initalize draw sheet names in for two menus that categorize the pages; one is a sub menu of the other):
Private Sub cbSector_Change()
If cbSector.Value = "DIST" Then
With cbCampaign
.RowSource = Worksheets("Master Data").Range("G13").Value
.ListRows = Worksheets("Master Data").Range("H14").Value
.Value = Worksheets("Master Data").Range("b16").Value
End With
ElseIf cbSector.Value = "INDU" Then
With cbCampaign
.RowSource = Worksheets("Master Data").Range("gl7").Value
.ListRows = Worksheets("Master Data").Range("h17").Value
.Value = Worksheets("Master Data").Range("b16").Value
End With
ElseIf cbSector.Value = "CS" Then
With cbCampaign
.RowSource = Worksheets("Master Data").Range("gl8").Value
.ListRows = Worksheets("Master Data").Range("h18").Value
.Value = Worksheets("Master Data").Range("b16").Value
End With
End If
End Sub
Private Sub EButton_Click()
ThisWorkbook.Saved = True
ThisWorkbook.Close
End Sub
Private Sub SEButton_Click()
ThisWorkbook.Save
ThisWorkbook.Saved = True
ThisWorkbook.Close
End Sub
Private Sub UserForm_Initialize()
With cbSector
.RowSource = Worksheets("Master Data").Range("b13").Value
.ListRows = Worksheets("Master Data").Range("b14").Value
.Value = Worksheets("Master Data").Range("b12").Value
End With
End Sub
Private Sub cbSelect_Click()
If cbSector.Value = "(none)" Then
errormsg = "Please Select Sector"
ElseIf cbCampaign.Value = "(none)" Then
errormsg = "Please Select Campaign"
Else: errormsg = "nothing"
End If
If errormsg = "nothing" Then
Sheets(cbSector.Value & "_" & cbCampaign.Value).Select
Unload Me
Else: MsgBox (errormsg)
End If
End Sub
To launch the UserForm this code is attached to a button on all but one of the sheets in the workbook:
Public SheetSelected As Worksheet
Public errormsg As String
Sub CallUserForm()
nav.Show
End Sub
This is a partial answer to the last question you asked in the comments ("would there be any way to hack a manual sheet tab click in vba"?) I don't know how to do that directly, but here is an ugly hack which simulates using the Ctrl+PgUp and Ctrl+PgDn keyboard shortcuts to tab from one worksheet to another:
Sub PageToSheet(SheetName As String)
Dim here As Long, there As Long, i As Long
here = ActiveSheet.Index
there = Sheets(SheetName).Index
If here = there Then
Exit Sub
ElseIf here < there Then
For i = 1 To there - here
Application.SendKeys "^{PGDN}"
Next i
Else
For i = 1 To here - there
Application.SendKeys "^{PGUP}"
Next i
End If
End Sub
This won't work when the VBA editor is the active window. But if you launch the following test sub while the main Excel window is active it seems to work:
Sub test()
Dim s As String
s = InputBox("Enter name of sheet to go to")
PageToSheet s
End Sub
It would be better to try to track down the source of the bug that you are seeing and even experiment with transferring all data and code to a new workbook to make sure that you don't have an inexplicable corruption in the file itself (which is sometimes what is behind truly weird behavior). Still -- if you want to simulate manual page tabs you can via SendKeys.

How can I run a macro as a workbook opens for the first time only?

I've got a workbook which runs a macro to show the userform Open1 as it opens, using the (very basic) code:
Private Sub Workbook_Open()
Open1.Show
End Sub
This does its job fine - each time I open the workbook, the userform pops up and runs perfectly.
But, I want the userform to appear the first time the workbook is opened only. Is there a way to allow this to happen?
You could use a dummy module which gets deleted the first time you open the spreadsheet...
Something like:
If ModuleExists("DummyModule") Then
Open1.Show
DoCmd.DeleteObject acModule, "DummyModule"
End If
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In CurrentProject.AllModules
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Update: as stated, DoCmd isn't used in excel vba. That will teach me to write code without testing it!
The following updated code will work, but in order to access the VB environment, excel needs to be trusted.
There is a setting in the Trust Center>Macro Settings that you can tick for this code to work under Developer Macro Settings
As such, this may not be the way to go as it opens up the possibility of security issues...
Sub RemoveModule()
If ModuleExists("DummyModule") Then
Open1.Show
Dim vbCom As Object: Set vbCom = Application.VBE.ActiveVBProject.VBComponents
vbCom.Remove VBComponent:=vbCom.Item("DummyModule")
End If
End Sub
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In Application.VBE.ActiveVBProject.VBComponents
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Try this:
If Sheets("Hide").Cells(1,1) = "1" Then
Open1.Show
Sheets("Hide").Cells(1,1) = "0"
End if
You must create the sheet Hide, and give the cell A1 the value 1, in that case the form will be shown.
After you create the sheet, hide it with this
Sheets("Hide").Visible = xlVeryHidden
And show it with this
Sheets("Hide").Visible = True
Here's an alternative bit of code that will persist between saves and allow you to reset it. No need to create a hidden sheet.
Put this in a module (invoke the DisplayFormIfFirstTime from your Workbook_Open event handler....)
Option Explicit
Private Const cMoniker As String = "FormHasBeenDisplayed"
Private Sub DisplayFormIfFirstTime()
If HasBeenOpened = False Then DisplayForm
End Sub
Public Sub DisplayForm()
MsgBox "Ok, its not a form but a dialog box...", vbInformation
End Sub
Public Function HasBeenOpened() As Boolean
Dim oName As Name
On Error Resume Next
Set oName = Application.Names(cMoniker)
On Error GoTo 0
If Not oName Is Nothing Then
HasBeenOpened = True
Else
Call Application.Names.Add(cMoniker, True, False)
End If
End Function
'Call this to remove the flag...
Public Sub ResetOpenOnce()
On Error Resume Next
Application.Names(cMoniker).Delete
End Sub
Based on the idea supplied by PaulG, I have coded an upgrade that will check for the name and if not found run a function, add the name and save the workbook for a more seemless approach to this problem...
Placed in ThisWorkbook
Private Sub Workbook_Open()
Run "RunOnce"
End Sub
Placed in a module
Sub RunOnce()
Dim Flag As Boolean: Flag = False
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then Flag = True
Next
If Flag = False Then
Call Application.Names.Add("FunctionHasRun", True, False)
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
Call RunOnceFunction
End If
End Sub
Private Function RunOnceFunction()
Open1.Show
End Function
Sub ResetRunOnce()
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then
Application.Names.Item("FunctionHasRun").Delete
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
End If
Next
End Sub

Run Macros if Sheet Does Not Exist

I am trying to only run a set of macros if a sheet doesn't already exist. I have a macro that creates a sheet and combines data from two sheets into it, and another that formats the new sheet. Since it needs to run on workbook open, I can't have it recreating the sheet again and again. I have been trying the following, but it gives the error: "sub or Function not defined":
Private Sub Workbook_Open()
If SheetExist("MyNewSheet") Then
End Sub
Else
Combine
Format
End Sub
You aren't doing anything if the sheet exists, so change your test.
Private Sub Workbook_Open()
If Not SheetExist("MyNewSheet") Then
Combine
Format
End If
End Sub
Function SheetExist(sheetname As String) As Boolean
SheetExist = True ' replace this with code from link below
End Function
Use the answers here: Excel VBA If WorkSheet("wsName") Exists for examples of functions that determine whether the sheet exists.
Yea, the problem is "End Sub" should be "Exit Sub" You can also use the solution above/below.
Your fixed code would be:
Private Sub Workbook_Open()
If SheetExists("MyNewSheet") Then
Exit Sub
Else
Combine
Format
End If
End Sub
Also:
Public Function SheetExists(ByVal WorksheetName As String) As Boolean
On Error Resume Next
WorksheetExists = (Sheets(WorksheetName).Name <> "")
On Error GoTo 0
End Function