Protect an excel sheet allowing the insertion of images via VBA code - vba

I'm trying to protect a WorkSheet allowing the insertion of images. Then I learned how to protect a WorkSheet with VBA code, but the WorkSheet aren't allowed to insert images.
When I manually protect the WorkSheet I discovered that if I check the option "Modify Objects", the WorkSheet allows the insertion of images.
Now, I'm searching about of parameters of the Protect method, then I have the following code:
With Sheets("Sheet1")
.Protect Password:="password", DrawingObjects:=True, Contents:=False, Scenarios:=False
End With
I assumed that the code occupies the parameter DrawingObjects, which should allow the insertion of images, but the code doesn't works properly, the WorkSheet is protected, but still doesn't allow the insertion of images.

I believe your code protects DrawingObjects, rather than allowing them to be inserted. I think it should be DrawingObjects:=False in order to permit this to work.
Whenever I use VBA with a protected worksheet, I first unprotect it, do whatever I want in VBA, then reapply the protection. Or at at least, remove specific protections and then reapply them.

Related

How check if Excel worksheet is protected?

I want to check if Excel worksheet is protected or not. I know that I can check .protectcontents, but what if worksheet is protected like this:
activesheet.protect contents:=false
.protectcontents returns false but shapes are locked.
Please help.
The three protection properties of a worksheet are the following:
Sheets(1).ProtectContents
Sheets(1).ProtectDrawingObjects
Sheets(1).ProtectScenarios
You can check whether both 3 are False. If this is the case, it is not protected.
.ProtectionMode says whether "User-Interface-only" protection is turned on. You may add it to the function, if you need it. MSDN - ProtectionMode

Copy Excel Sheet with VBA Loses Protection

Is there a more secure way of protecting excel sheets without embedding the password in the vba code with sheet.Protect code? Required functionality is to copy excel sheets with a macro and retain protection so formulas remain hidden.
Two template sheets have hidden formulas. Copy protection on these sheets allow users to do everything except Edit objects and Edit scenarios.
The sheets need grouping functionality which requires VBA code (added to workbook_Open) to set sheet.Protect UserInterfaceOnly:=True for any sheets with ProtectedContents = True, and .EnableOutlining:=True. All other Protect attributes are set to True in the code except DrawingObjects and Scenarios.
Copying either protected sheet with the excel interface maintains protection on the copy. Copying a sheet in VBA code results in a new sheet with no protection.
My concern is despite protecting the code with a secure password in excel 2010 the code and the embedded password is easily displayed by opening the file in other software. Is there a more secure way of protecting the sheets without embedding the password in the code?
Reference: Retaining Existing Excel Worksheet Protection When Enabling Grouping
By nature, Excel security is not particularly strong. The protection functions exists mainly to prevent lay users from making inadvertent mistakes. However any determined and capable person won't take too long to hack/crack any security you put in place. As an example of how easy it is, take a look at one of the most up-voted questions here:
Is there a way to crack the password on an Excel VBA Project?
Therefore even if you protect the actual VBA project, it's relatively simple to get around this.
In your specific case, you're using a Worksheet_Open event to apply protection - however this is the easiest thing in the world for anyone to bypass, by simply opening the workbook in question via a sub in another workbook, after having turned events off. You may also have some users whose macro security settings are not automatically enabled, and if they dismiss the "Enable Macros?" prompt when they open your workbook, the Open event will never fire.
If you're happy with this level of security - and happy that your workbook won't be the target of hacks from determined users - then you needn't worry too much about storing the protection password in the code itself. However there are a couple of simple ways you can at least make it less visible (in case a user accidentally presses Alt+F11, or an error causes the VBE to open:
Instead of typing the password in the code in plain text, use the Chr() function. So instead of:
Dim pw As String
pw = "hello!"
... you could write:
Dim pw As String
pw = Chr(104) & Chr(101) & Chr(108) & Chr(108) & Chr(111) & Chr(33)
Another way is to store the password within a cell (for example A1) of another sheet whose visibility is set to xlVeryHidden, and reference Sheets("HiddenSheetName").Cells(1,1).Value.
As I said - these are not perfect ways of securing sensitive data and formulæ: however if your goal is simply to stop casual users from making a mistake, or poking around where they shouldn't, then they will do the job.
Additional: for multiple instances requiring the password, I use something like this myself:
Function pw() As String
pw = Chr(104) & Chr(101) & Chr(108) & Chr(108) & Chr(111) & Chr(33)
End Function
Then call it as needed. You can even name the function something else slightly cryptic, not "pw" etc.

Updating a macro to be identical across all worksheets (or making the code more global?)

I have a workbook with a few dozen worksheets, which is growing. I might also end up with copies of this workbook pretty soon.
Each sheet is based on the same worksheet template, which includes a macro on its Worksheet_Change event for auto-calculations. As I improve the workbook and add capabilities, and sheets are added, it's taking longer and longer to copy-paste the updated macro to all sheets.
I'm wondering if there's a way to either:
Make another macro that copies the updated version of the macro from the template to all other worksheets, overwriting the old versions in the process
And/or
Move the code from worksheet_change to a more global place where I'd only need to update one version of it per workbook (which is fine and much better than updating 20+ worksheets manually across soon-to-be 3 workbooks...)
Solution #2 would be better because more elegant I think? But I have no clue if it's possible. Barring that I'd gladly take #1 for the time-saving aspect!
Thank you in advance.
If we are talking about one workbook with multiple worksheets, then an easy approach (which solves the updating issue) would be:
Add a Module and write a procedure containing the original change events code:
Option Explicit
Public Sub MyGlobalWorksheet_Change(ByVal Target As Range)
' here the code from your orignal Worksheet_Change.
' make sure you reference worksheets correctly
' the worksheet can eg be addressed like
' set ws = Target.Parent
End Sub
So in your worksheets you only need to add a generic call like
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
MyGlobalWorksheet_Change Target
End Sub
to call the global procedure. Therefore the Worksheet_Change event never needs to be changed, however you just need to add it once.
Whenever you need to change something at the code you just need to change one procedure which is MyGlobalWorksheet_Change and it affects all your desired sheets at once (but only sheets you added the call to your global event).
Remember it is always a bad idea to copy the same code over and over again, because it is hard to maintain. Instead always use one procedure you call again and again.
Another way would be using the Workbook_SheetChange event within the ThisWorkbook scope. But this will affect any sheet within the workbook. The previous solution will only affect the workbooks you choose by adding a call.

Removing links from copied worksheet

What I want to do
I want a code in my workbook (wbDestination) that opens another workbook (wbBosOriginal) and copies an entire sheet as values (wbBosOriginal has a lot of code in it, in modules and in the worksheet in question, and I do not want this code because it references stuff in wbB that doesn't exist in wbDestination). I have had great problems pasting as values, because it will not paste columns and rows that are currently hidden. So this is why I decided to import the whole sheet instead.
What I tried and what's wrong with it
Here is a block of code I used to copy the worksheet in the destination workbook, in the last index position. The problem with it is that some links still exist to the old workbook (Formulas, validation lists, conditionnal formatting). I have deleted all these links but STILL when I paste the sheet successfully, save and reopen, I have an error saying some content is unreadable. I believe there are still some elements linked to the old workbook.
Set wbBosOriginal = Workbooks.Open(strChosenPath, ReadOnly:=True)
With wbBosOriginal.Sheets("BOS")
.Visible = True
'Pastes the ws in last position in wbDestination
.Copy after:=wbDestination.Sheets(wbDestination.Worksheets.Count)
Set wsNewBos = Worksheets(Worksheets.Count)
'Deletes VBA code in the copied sheet
ThisWorkbook.VBProject.VBComponents.Item(wsNewBos.CodeName).CodeModule.DeleteLines 1, _
ThisWorkbook.VBProject.VBComponents.Item(wsNewBos.CodeName).CodeModule.CountOfLines
End With
The worksheet is successfully pasted with no code in it, with everything else it had previously. I then remove all formulas, conditionnal formatting, and validation lists. Even after removing those as well, I still get an error when opening the workbook.
My question
Apart from conditional formatting, validation lists, VBA code, and formulas linking a worksheet that was pasted to a new workbook, what other elements could cause the workbook from opening in repair mode every time due to existing links to the old workbook?
If my question is not clear, comment and I will clarify.
Dealing directly with VBE seems a bit heavy-handed to me. If your code is manipulating several workbooks, I would put the code in an add-in and not have it in any workbook. (Technically *.xlam addins are workbooks, but when I say "workbook" I mean normal *.xls, *.xlsx, *.xlsm, etc.)
That said, if you're just copying cell values (which may be formulas) between different workbooks, you shouldn't have any dependencies other than cell references, named ranges, and user-defined functions in the original workbook. I would make sure there are none of those. Please also share how you are ensuring your formulas do not have broken references.
If the issue you are having is caused by trying to avoid hidden columns and rows not allowing pastevalues, why not unhide the rows and columns and then copy only the values to the new book?
Just cycle through each of the sheets in the original book and use the method .UsedRange.Hidden = False. As far as I am aware, this should unhide every cell on the sheet and allow you to do the original pastevalues calls
This works fast and smooth (it's harder to delete ALL the data Imo):
Sub tests()
Dim AllRange As Range: Set AllRange = ActiveSheet.UsedRange
Dim ItemRange As Range
Dim myWbDestination As Workbook: Set myWbDestination = ThisWorkbook
Dim SheetDestination As String: SheetDestination = ("Sheet2")
For Each ItemRange In AllRange
With myWbDestination.Sheets(SheetDestination)
.Range(ItemRange.Address) = ItemRange.Value
End With
Next ItemRange
End Sub
Repair mode can be triggered by many factors, you would need to post the code you are getting to look for an explanation, it would be like asking why vba may broke

VBA: How do I allow filters to be turned on and off on a protected sheet?

I have the following piece of code, which removes filters and then re-applies them from selected cells:
Range("A10:AM10").Select
Selection.AutoFilter
Selection.AutoFilter
The problem I have is that when the sheet is protected and I try to run the code it errors, so is there a way of allowing filters to be turned on and off on a protected sheet?
How are you?
It will depend on the setup of the protection. When you protect a worksheet, there is an option to allow filtering (AutoFilter).
If you're the one who's protecting the sheet, you can use any of the worksheet's protect method's parameters to set the protection options. Here are the protect method's parameters:
sheetName.Protect(Password, DrawingObjects, Contents, Scenarios, UserInterfaceOnly, AllowFormattingCells, AllowFormattingColumns, AllowFormattingRows, AllowInsertingColumns, AllowInsertingRows, AllowInsertingHyperlinks, AllowDeletingColumns, AllowDeletingRows, AllowSorting, AllowFiltering, AllowUsingPivotTables)
If you want to be able to implement filters only by code, then you can set the UserInterfaceOnly parameter to TRUE. The protection will block only the users changes. Code changes will be fully allowed.
sheetName.Protect UserInterfaceOnly:=True
Or, if you want to allow users to filter the contents of the worksheet from the interface, just set the AllowFiltering parameter to TRUE as well as the UserInterfaceOnly parameter.
sheetName.Protect UserInterfaceOnly:=True, AllowFiltering:=True
Your code should work with both options.
Hope it helps!