-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathViewLock.bas
1397 lines (1142 loc) · 48.5 KB
/
ViewLock.bas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Attribute VB_Name = "ViewLock"
Option Explicit
' v2025-01-31
'
' =====================================================================
' ViewLock - Outlook Lock and Unlock Views Module
'
' Github Home
' https://github.com/Hornblower409/Outlook-ViewLock
'
' Github Releases
' https://github.com/Hornblower409/Outlook-ViewLock/releases
'
' =====================================================================
'
' Copyright (C) 2024, 2025 Lycon Of Texas
'
' This program is free software: you can redistribute it
' and/or modify it under the terms of the GNU General Public
' License Version 3 as published by the Free Software
' Foundation.
'
' 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, see
' <https://www.gnu.org/licenses/>.
'
'==============================================================
' ---------------------------------------------------------------------
' Pseudo ENums shared between the Module and the Form
' ---------------------------------------------------------------------
' Actions
'
Private Const Actions_First As Long = 0
Private Const Actions_Unlock As Long = 0
Private Const Actions_Lock As Long = 1
Private Const Actions_State As Long = 2
Private Const Actions_Save As Long = 3
Private Const Actions_Status As Long = 4
Private Const Actions_Form As Long = 5
Private Const Actions_Last As Long = 5
' Scopes
'
Private Const Scopes_First As Long = 0
Private Const Scopes_Stores As Long = 0
Private Const Scopes_Store As Long = 1
Private Const Scopes_Folders As Long = 2
Private Const Scopes_Folder As Long = 3
Private Const Scopes_View As Long = 4
Private Const Scopes_Last As Long = 4
' View Types
'
Private Const VTypes_First As Long = 0
Private Const VTypes_None As Long = 0
Private Const VTypes_Shared As Long = 1
Private Const VTypes_Public As Long = 2
Private Const VTypes_Private As Long = 4
Private Const VTypes_All As Long = 7
Private Const VTypes_Last As Long = 7
' ---------------------------------------------------------------------
' Module Level Pseudo ENums
' ---------------------------------------------------------------------
' WhatIfMsgs - Controls the WhatIf line in MsgBox
'
Private Const WhatIfMsgs_First As Long = 0
Private Const WhatIfMsgs_None As Long = 0
Private Const WhatIfMsgs_Before As Long = 1
Private Const WhatIfMsgs_After As Long = 2
Private Const WhatIfMsgs_Last As Long = 2
' Index into the MsgHdr Array
'
Private Const MsgHdrs_First As Long = 0
Private Const MsgHdrs_Macro As Long = 0
Private Const MsgHdrs_Proc As Long = 1
Private Const MsgHdrs_Action As Long = 2
Private Const MsgHdrs_Folder As Long = 3
Private Const MsgHdrs_Scope As Long = 4
Private Const MsgHdrs_WhatIf As Long = 5
Private Const MsgHdrs_ErrNum As Long = 6
Private Const MsgHdrs_ErrDesc As Long = 7
Private Const MsgHdrs_Warning As Long = 8
Private Const MsgHdrs_Counts As Long = 9
Private Const MsgHdrs_ViewName As Long = 10
Private Const MsgHdrs_Text As Long = 11
Private Const MsgHdrs_Last As Long = 11
' Counts List
'
Private Const Counts_First As Long = 0
Private Const Counts_ActionFirst As Long = 0
Private Const Counts_ViewsUnlocked As Long = 0
Private Const Counts_ViewsLocked As Long = 1
Private Const Counts_ViewsSaved As Long = 2
Private Const Counts_ActionLast As Long = 2
Private Const Counts_SkippedFirst As Long = 3
Private Const Counts_ViewsSkipped_NoChange As Long = 3
Private Const Counts_ViewsSkipped_TypeFilter As Long = 4
Private Const Counts_ViewsSkipped_SharedSeen As Long = 5
Private Const Counts_ViewsSkipped_Error As Long = 6
Private Const Counts_ViewsSkipped_CacheGhost As Long = 7
Private Const Counts_FoldersSkipped_Pooled As Long = 8
Private Const Counts_FoldersSkipped_SharePoint As Long = 9
Private Const Counts_FoldersSkipped_Config As Long = 10
Private Const Counts_SkippedLast As Long = 10
Private Const Counts_ScannedFirst As Long = 11
Private Const Counts_ViewCount As Long = 11
Private Const Counts_FolderCount As Long = 12
Private Const Counts_StoreCount As Long = 13
Private Const Counts_ScannedLast As Long = 13
Private Const Counts_Last As Long = 13
' Counts Type and Desc Table
'
Private Const CountNames_First As Long = 0
Private Const CountNames_Type As Long = 0
Private Const CountNames_Desc As Long = 1
Private Const CountNames_Last As Long = 1
' ---------------------------------------------------------------------
' Module Level Constants
' ---------------------------------------------------------------------
Private Const ThisModule As String = "ViewLock" ' Name of this Module
' Hidden Pooled Search Folders
'
Private Const PooledNamePrefix As String = "MS-OLK"
' PropTags
'
Private Const PR_CONTAINER_CLASS As String = "http://schemas.microsoft.com/mapi/proptag/0x3613001E"
Private Const PR_EXTENDED_FOLDER_FLAGS As String = "http://schemas.microsoft.com/mapi/proptag/0x36DA0102"
' Misc
'
Private Const BlankLine As String = vbNewLine & vbNewLine ' Blank Line for MsgBox
Private Const PAPropertyNotFound As Long = -2147221233 ' PropertyAccessor Error - Does not have Property
Private Const ErrViewNotFound As String = "The view cannot be found." ' Error - Cached View renamed or deleted
' View XML
'
' ViewReadOnly XML elements
' ViewTime XML element end tag
'
Private Const XMLReadOnlyLocked As String = "<viewreadonly>1</viewreadonly>"
Private Const XMLReadOnlyUnlocked As String = "<viewreadonly>0</viewreadonly>"
Private Const XMLTimeEnd As String = "</viewtime>" & vbNewLine
' ---------------------------------------------------------------------
' Module Level Globals
' ---------------------------------------------------------------------
' Passed Param and Derived
'
Private Action As Long ' ENum Value
Private ActionBool As Boolean ' Only Lock and Unlock as Boolean
Private ActionName As String ' From ViewLock_ActionNames(Action)
Private Scope As Long ' ENum Value
Private ScopeName As String ' From ViewLock_ScopeNames
Private ScopeShortName As String ' From ViewLock_ScopeShortNames
Private ScopeString As String ' From ViewLock_ScopeString
Private WhatIf As Boolean ' Value as a Boolean
Private Form As ViewLockForm ' Form (If Open)
Private Caller As String ' Calling Macro Name
Private VTypes As Long ' VTypes ENum Value
Private VTypesList As String ' VTypes From ViewLock_VTypesList
' Current View
'
Private CurrentFolder As Outlook.Folder ' Current View Folder Object
Private CurrentView As Outlook.View ' Current View Object
' Collections
'
' Share Views I've already Seen. Don't need to be updated.
' Item = "", Key = View.Name & vbFormFeed & Folder.IPFRoot
'
Private SharedSeen As VBA.Collection
' Arrays
'
Private Counts() As Long ' Counters Count
Private CountNames() As String ' Counters Type and Desc
' ---------------------------------------------------------------------
' Public - Macros
' ---------------------------------------------------------------------
Public Sub ViewLock_Lock(): ViewLock_Xeq Actions_Lock, Scopes_View, "ViewLock_Lock": End Sub
Public Sub ViewLock_Unlock(): ViewLock_Xeq Actions_Unlock, Scopes_View, "ViewLock_Unlock": End Sub
Public Sub ViewLock_State(): ViewLock_Xeq Actions_State, Scopes_View, "ViewLock_State": End Sub
Public Sub ViewLock_Save(): ViewLock_Xeq Actions_Save, Scopes_View, "ViewLock_Save": End Sub
Public Sub ViewLock_Form(): ViewLock_Xeq Actions_Form, Scopes_View, "ViewLock_Form": End Sub
' ---------------------------------------------------------------------
' Public Main - Called from Macros and the Form
' ---------------------------------------------------------------------
'
Public Sub ViewLock_Xeq( _
ByVal ActionEnum As Long, _
ByVal ScopeENum As Long, _
ByVal CallerName As String, _
Optional ByVal VTypesENum As Long = -1, _
Optional ByVal WhatIfBool As Boolean = False _
)
Const ThisProc = "ViewLock_Xeq"
' Check/Set the current Environment
'
If Not ViewLock_CurrentEnv() Then Exit Sub
' Init Globals
Action = ActionEnum
ActionBool = IIf(Action = Actions_Lock, True, False)
ActionName = ViewLock_ActionNames()
Scope = ScopeENum
ScopeName = ViewLock_ScopeNames()
ScopeShortName = ViewLock_ScopeShortNames()
VTypes = VTypesENum
If VTypes = -1 Then VTypes = ViewLock_ViewVType(CurrentView) ' If no VTypes from Caller - Default to Current View
VTypesList = ViewLock_VTypesList(VTypes)
ScopeString = ViewLock_ScopeString()
Caller = CallerName
WhatIf = WhatIfBool
ReDim Counts(Counts_First To Counts_Last)
ViewLock_CountNames
Counts(Counts_StoreCount) = IIf(Scope <> Scopes_Stores, 1, 0)
Counts(Counts_FolderCount) = IIf(Scope <> Scopes_View, 0, 1)
Set SharedSeen = New VBA.Collection
' Dispatch to the appropriate Proc
'
ViewLock_XeqDispatch
End Sub
' Dispatch based on Action and Scope
'
Private Sub ViewLock_XeqDispatch()
' Called from the Form to update the Default Form
' Status Display after any possible updates.
'
If Action = Actions_Status Then
' Continue
' Create and Show the Form. Wait for Hide.
' - Form calls back me to execute commands.
'
ElseIf Action = Actions_Form Then
Set Form = New ViewLockForm
Form.Show
Set Form = Nothing
' Do any View Scope commands.
'
ElseIf Scope = Scopes_View Then
ViewLock_ViewScope
' Do any Store, Folders, Folder Scope commands
'
Else
ViewLock_StoreFolderScope
End If
' Update the Default Form Status Display
'
ViewLock_FormStatusDisplayDefault
End Sub
' ---------------------------------------------------------------------
' Wide Scope (Folder, Folders, Store, Stores)
' ---------------------------------------------------------------------
'
Private Sub ViewLock_StoreFolderScope()
Const ThisProc = "ViewLock_StoreFolderScope"
' Warn about changes and get permission
Dim WarningMsg As String
' Shared Views Warning
'
If (VTypes And VTypes_Shared) <> 0 Then
WarningMsg = "Shared Views - Changes to a Shared View in the Current Scope will affect" & _
" all apperances of that View across the entire system."
End If
' Current Scope Warning
'
WarningMsg = WarningMsg & BlankLine & "You are about to " & ActionName & " all Views in the Current Scope. "
Select Case ViewLock_MsgBox(ThisProc, _
WhatIfMsg:=WhatIfMsgs_Before, _
Warning:=WarningMsg, _
Text:="Continue?", _
Buttons:=vbOKCancel, Default:=vbDefaultButton2, _
Icon:=vbQuestion)
Case vbOK
' Continue
Case vbCancel
Exit Sub
Case Else
Stop: Exit Sub
End Select
' And awaaaaaay we go!
'
ViewLock_FormStatusDisplayCounts
Select Case Scope
Case Scopes_Stores
If Not ViewLock_Stores() Then Exit Sub
Case Scopes_Store
If Not ViewLock_Store(CurrentFolder.Store) Then Exit Sub
Case Scopes_Folders
If Not ViewLock_Folders(CurrentFolder) Then Exit Sub
Case Scopes_Folder
If Not ViewLock_Folder(CurrentFolder) Then Exit Sub
Case Else
Stop: Exit Sub
End Select
' Show the Wide Scope Show and Tell Msg
'
ViewLock_ShowAndTell
End Sub
' Walk all Stores
'
Private Function ViewLock_Stores() As Boolean
Const ThisProc = "ViewLock_Stores"
ViewLock_Stores = False
Counts(Counts_StoreCount) = 0
Dim oStore As Outlook.Store
For Each oStore In Session.Stores
If Not ViewLock_Store(oStore) Then Exit Function
Counts(Counts_StoreCount) = Counts(Counts_StoreCount) + 1
Next oStore
ViewLock_Stores = True
End Function
' Walk all Folders and Search Folders in a Store
'
Private Function ViewLock_Store(ByVal oStore As Outlook.Store) As Boolean
Const ThisProc = "ViewLock_Store"
ViewLock_Store = False
' Do the Normal Folders
' Do the Search Folders Collection
'
If Not ViewLock_Folders(oStore.GetRootFolder) Then Exit Function
If Not ViewLock_SearchFolders(oStore) Then Exit Function
ViewLock_Store = True
End Function
' Do a Store level Search Folders Collection
'
Private Function ViewLock_SearchFolders(ByVal oStore As Outlook.Store) As Boolean
ViewLock_SearchFolders = False
Dim oFolders As Outlook.Folders
Set oFolders = oStore.GetSearchFolders
Dim oFolder As Outlook.Folder
For Each oFolder In oFolders: Do
' Skip any Pooled Search Folders
'
' Must meet all three criteria.
' (All I could find that differ from a normal Search Folder)
'
' 1) No PR_CONTAINER_CLASS
' 2) No PR_EXTENDED_FOLDER_FLAGS
' 3) Name prefix is "MS-OLK"
'
Dim Property As Variant
If Not ViewLock_GetProperty(oFolder, PR_CONTAINER_CLASS, Property) Then
If Not ViewLock_GetProperty(oFolder, PR_EXTENDED_FOLDER_FLAGS, Property) Then
If Left(oFolder.Name, 6) = PooledNamePrefix Then
Counts(Counts_FoldersSkipped_Pooled) = Counts(Counts_FoldersSkipped_Pooled) + 1
Exit Do ' Next oFolder
End If
End If
End If
' Search Folders have no subfolders. Go straight to ViewLock_Folder.
'
If Not ViewLock_Folder(oFolder) Then Exit Function
Loop While False: Next oFolder
ViewLock_SearchFolders = True
End Function
' Do the current Folder and then a Recursive Descent into all SubFolders
'
Private Function ViewLock_Folders(ByVal oFolder As Outlook.Folder) As Boolean
Const ThisProc = "ViewLock_Folders"
ViewLock_Folders = False
' Skip any SharePoint Folders
'
If oFolder.IsSharePointFolder Then
Counts(Counts_FoldersSkipped_SharePoint) = Counts(Counts_FoldersSkipped_SharePoint) + 1
ViewLock_Folders = True
Exit Function
End If
' Skip any Config Folders
'
If ViewLock_FolderIPFRoot(oFolder) = "Configuration" Then
Counts(Counts_FoldersSkipped_Config) = Counts(Counts_FoldersSkipped_Config) + 1
ViewLock_Folders = True
Exit Function
End If
' Do the current folder
'
If Not ViewLock_Folder(oFolder) Then Exit Function
' If has no Folders Collection - Done
'
Dim Dummy As Outlook.Folders
On Error Resume Next
Set Dummy = oFolder.Folders
If Err.Number <> 0 Then
ViewLock_Folders = True
Exit Function
End If
On Error GoTo 0
' Call myself for any subfolders
'
For Each oFolder In oFolder.Folders
If Not ViewLock_Folders(oFolder) Then Exit Function
Next oFolder
ViewLock_Folders = True
End Function
' Do all Views in a Folder
'
Private Function ViewLock_Folder(ByVal oFolder As Outlook.Folder) As Boolean
Const ThisProc = "ViewLock_Folder"
ViewLock_Folder = False
' Skip any SharePoint Folders
'
If oFolder.IsSharePointFolder Then
Counts(Counts_FoldersSkipped_SharePoint) = Counts(Counts_FoldersSkipped_SharePoint) + 1
ViewLock_Folder = True
Exit Function
End If
Counts(Counts_FolderCount) = Counts(Counts_FolderCount) + 1
' Call StateChange for each View in the Folder
'
Dim oView As Outlook.View
For Each oView In oFolder.Views
ViewLock_StateChange oView
Next oView
ViewLock_Folder = True
End Function
' Show the Wide Scope Show and Tell
'
Private Sub ViewLock_ShowAndTell()
Const ThisProc = "ViewLock_ShowAndTell"
' Put back the Default Form Status Diaplay
'
ViewLock_FormStatusDisplayDefault
' Show Counts and an Information Icon
'
ViewLock_MsgBox ThisProc, _
WhatIfMsg:=WhatIfMsgs_After, _
Counts:=ViewLock_ShowAndTellCounts(), _
Icon:=vbInformation
End Sub
' Build the Counters section of the Wide Scope Show and Tell
'
Private Function ViewLock_ShowAndTellCounts() As String
Dim Inx As Long
Dim CountsLit(Counts_First To Counts_Last) As String
For Inx = Counts_First To Counts_Last
CountsLit(Inx) = _
CStr(Counts(Inx)) & " " & _
CountNames(Inx, CountNames_Type) & IIf(Counts(Inx) <> 1, "s", "") & " " & _
CountNames(Inx, CountNames_Desc)
Next Inx
Dim CountsLine(Counts_First To Counts_Last) As String
For Inx = Counts_First To Counts_Last
Select Case Inx
' Either Unlocked or Locked. Even if Zero.
'
Case Counts_ActionFirst To Counts_ActionLast
CountsLine(Inx) = _
IIf(Inx = Action, CountsLit(Inx), "")
' Skipped Lines - Only if Non Zero
'
Case Counts_SkippedFirst To Counts_SkippedLast
If Counts(Inx) <> 0 Then CountsLine(Inx) = CountsLit(Inx)
' Last Line - Scanned Line of concats
'
Case Counts_ScannedFirst To Counts_ScannedLast
CountsLine(Counts_Last) = CountsLine(Counts_Last) & CountsLit(Inx) & " "
Case Else
' Oops
Stop: Exit Function
End Select
Next Inx
' Remove trailing space from the Last (Scanned) line
'
CountsLine(Counts_Last) = Left(CountsLine(Counts_Last), Len(CountsLine(Counts_Last)) - 1)
Dim CountsBlock As String
For Inx = Counts_First To Counts_Last
If CountsLine(Inx) <> "" Then CountsBlock = CountsBlock & CountsLine(Inx) & "." & vbNewLine
If Inx = Action Then CountsBlock = CountsBlock & vbNewLine
Next Inx
' Remove trailing vbNewLine from the Block and Return
'
ViewLock_ShowAndTellCounts = Left(CountsBlock, Len(CountsBlock) - 2)
End Function
' =====================================================================
' View Scope
' =====================================================================
'
Private Sub ViewLock_ViewScope()
Select Case Action
Case Actions_State
ViewLock_ViewState
Case Actions_Lock, Actions_Unlock
ViewLock_ViewSet
Case Actions_Save
ViewLock_ViewSave
Case Else
Stop: Exit Sub
End Select
End Sub
' Save the Current View
'
Private Sub ViewLock_ViewSave()
Const ThisProc = "ViewLock_ViewSave"
' Warnings
'
Dim WarningMsg As String
If (VTypes And VTypes_Shared) <> 0 Then
WarningMsg = "Current View is Shared - Changes to a Shared View will affect" & _
" all apperances of that View across the entire system."
End If
' Ask Permission to Save
'
Select Case ViewLock_MsgBox(ThisProc, _
WhatIfMsg:=WhatIfMsgs_Before, _
ViewName:=CurrentView.Name, _
Warning:=WarningMsg, _
Text:= _
"You are about to " & ActionName & " any changes to the Current View and Lock it." & BlankLine & _
"Continue?", _
Buttons:=vbOKCancel, Default:=vbDefaultButton2, _
Icon:=vbQuestion)
Case vbOK
' Continue
Case vbCancel
Exit Sub
Case Else
' Oops
Stop: Exit Sub
End Select
' Ignore Current State and just Lock and Save
' (oView.Save ignores oView.LockUserChanges)
'
ActionBool = True
If Not ViewLock_StateSave(oView:=CurrentView, IncChangedCount:=False, SetLock:=True, SetXML:=False) Then Stop: Exit Sub
' Show and Tell
'
ViewLock_MsgBox Proc:=ThisProc, _
WhatIfMsg:=WhatIfMsgs_After, _
ViewName:=CurrentView.Name, _
Warning:="If the View appears hoarked - Just close and reopen the current Explorer or switch to a different View and back again.", _
Text:="View is now Saved and " & ViewLock_LockStateName(CurrentView) & ".", _
Icon:=vbInformation
End Sub
' Show the State of the Current View
'
Private Sub ViewLock_ViewState()
Const ThisProc = "ViewLock_ViewState"
' Show and Tell (SAT)
'
ViewLock_MsgBox Proc:=ThisProc, _
ViewName:=CurrentView.Name, _
Text:="View is " & ViewLock_LockStateName(CurrentView) & ".", _
Icon:=vbInformation
End Sub
' Set the State of the Current View
'
Private Sub ViewLock_ViewSet()
Const ThisProc = "ViewLock_ViewSet"
' If already in the requested State - done
'
If CurrentView.LockUserChanges = ActionBool Then
ViewLock_MsgBox Proc:=ThisProc, _
ViewName:=CurrentView.Name, _
Text:="View is already " & ViewLock_LockStateName(CurrentView) & ".", _
Icon:=vbInformation
Exit Sub
End If
' Warning if a Shared View and get Permisison
'
If Not ViewLock_SharedWarning(ThisProc) Then Exit Sub
' Set it
'
ViewLock_StateChange CurrentView
' Setup the current Environment
' Show and Tell
'
ViewLock_MsgBox Proc:=ThisProc, _
WhatIfMsg:=WhatIfMsgs_After, _
ViewName:=CurrentView.Name, _
Warning:="If the View appears hoarked - Just close and reopen the current Explorer or switch to a different View and back again.", _
Text:="View is now " & ViewLock_LockStateName(CurrentView) & ".", _
Icon:=vbInformation
End Sub
' Show a View Scope Shared View Lock/Unlock Warning and get Permission
'
Private Function ViewLock_SharedWarning(ByVal Caller As String) As Boolean
ViewLock_SharedWarning = False
' If not a Shared View - True and Done
'
If (VTypes And VTypes_Shared) = 0 Then
ViewLock_SharedWarning = True
Exit Function
End If
' Ask for Permission. Return True if OK.
'
Dim WarningMsg As String
WarningMsg = "Current View is Shared - Changes to a Shared View will affect" & _
" all apperances of that View across the entire system."
Select Case ViewLock_MsgBox(Caller, _
WhatIfMsg:=WhatIfMsgs_Before, _
ViewName:=CurrentView.Name, _
Warning:=WarningMsg, _
Text:="Continue?", _
Buttons:=vbOKCancel, Default:=vbDefaultButton2, _
Icon:=vbQuestion)
Case vbOK
' Continue
Case vbCancel
Exit Function
Case Else
Stop: Exit Function
End Select
ViewLock_SharedWarning = True
End Function
' =====================================================================
' State Change
' =====================================================================
' Handle a possible View State Change
'
Private Sub ViewLock_StateChange(ByVal oView As Outlook.View)
Const ThisProc = "ViewLock_StateChange"
DoEvents
Counts(Counts_ViewCount) = Counts(Counts_ViewCount) + 1
ViewLock_FormStatusDisplayCounts
' If it doesn't pass the VTypes filter - Done
'
If Not ViewLock_StateVTypeFilter(oView) Then Exit Sub
' ---------------------------------------------------------------------
' Break out Explorer.CurrentView handling because it's so
' different from the normal flow.
'
Select Case ViewLock_StateExplorerCurrent(oView)
Case True
' Continue
Case False
' If it's a Shared View I've seen before - Inc Counter and Done
' If there is no State Change - Inc Counter and Done
' Save the View
'
If ViewLock_StateSharedSeen(oView) Then Exit Sub
If Not ViewLock_StateChanging(oView) Then Exit Sub
If Not ViewLock_StateSave(oView:=oView, IncChangedCount:=True, SetLock:=False, SetXML:=True) Then Stop: Exit Sub
Case Else
' Oops
Stop: Exit Sub
End Select
' ---------------------------------------------------------------------
End Sub
' Update the View.XML
'
' SPOS - Testing has shown that just setting oView.LockUserChanges is not
' reliable for Views other than Explorer.CurrentView, so we update the XML as well.
'
Private Function ViewLock_StateXML(ByVal oView As Outlook.View) As Boolean
ViewLock_StateXML = False
' SPOS - Stupid doesn't look at the value of <viewreadonly>.
' Only if it exist. If it exist then LockUserChanges is True Else False.
'
' Read in the View's XML.
' Remove any Read Only True and any spurious Read Only False elements
' If Action is Lock - Insert a Read Only element just after the </viewtime> tag
'
Dim ViewXML As String
ViewXML = oView.XML
ViewXML = Replace(ViewXML, XMLReadOnlyLocked, "")
ViewXML = Replace(ViewXML, XMLReadOnlyUnlocked, "")
If ActionBool Then ViewXML = Replace(ViewXML, XMLTimeEnd, XMLTimeEnd & XMLReadOnlyLocked)
oView.XML = ViewXML
ViewLock_StateXML = True
End Function
' Save (with trap in case something goes sideways)
'
Private Function ViewLock_StateSave( _
ByVal oView As Outlook.View, _
ByVal IncChangedCount As Boolean, _
ByVal SetLock As Boolean, _
ByVal SetXML As Boolean _
) As Boolean
Const ThisProc = "ViewLock_StateSave"
ViewLock_StateSave = True
' If WhatIf - pretend we made a change and done
'
If WhatIf Then
If IncChangedCount Then Counts(Action) = Counts(Action) + 1
Exit Function
End If
' If called for - Update LockUserChanges
' If called for - Update the View.XML
'
If SetLock Then oView.LockUserChanges = ActionBool
If SetXML Then If Not ViewLock_StateXML(oView) Then Stop: Exit Function
' Finally !
'
On Error Resume Next
oView.Save
If Err.Number = 0 Then
If IncChangedCount Then Counts(Action) = Counts(Action) + 1
Exit Function
End If
On Error GoTo 0
Select Case ViewLock_MsgBox( _
Proc:=ThisProc, _
ErrNum:=Err.Number, _
ErrDesc:=Err.Description, _
ViewName:=oView.Name, _
Folder:=oView.Parent.Parent, _
Text:="Error saving View. Continue processing?", _
Buttons:=vbYesNo, _
Default:=vbDefaultButton2, _
Icon:=vbCritical)
Case vbYes
Counts(Counts_ViewsSkipped_Error) = Counts(Counts_ViewsSkipped_Error) + 1
Exit Function
Case vbNo
' Continue
Case Else
' Oops
Stop: Exit Function
End Select
ViewLock_StateSave = False
End Function
' Handle Explorer.CurrentView
'
' WTF?
'
' Testing shows that I need to update LockUserChanges in all Explorers where
' Explorer.CurrentView is using oView. (I assume Explorer.CurrentView is a
' cached copy of the oView from Folder.Views).
'
' But only one Explorer.CurrentView IS oView. All the others will match on
' FolderPath and View.Name, but have a different Object reference.
'
' https://learn.microsoft.com/en-us/office/vba/api/outlook.explorer.currentview
'
' To obtain a View object for the view of the current Explorer, use
' Explorer.CurrentView instead of the CurrentView property of the current Folder
' object returned by Explorer.CurrentFolder.
'
' You must save (Set) a reference to the View object returned by CurrentView before
' you proceed to use it for any purpose.
'
Private Function ViewLock_StateExplorerCurrent(ByVal oView As Outlook.View) As Boolean
ViewLock_StateExplorerCurrent = False
Dim ExplorersUpdated As Long ' How many Explorers with this View have we updated?
' Walk all Explorers looking for a match to oView
'
Dim oExplorerView As Outlook.View
Dim oExplorer As Outlook.Explorer
For Each oExplorer In Application.Explorers: Do
' Ignore Explorers with no View (e.g. Outlook Today)
'
Dim GetViewError As Boolean
On Error Resume Next
Set oExplorerView = oExplorer.CurrentView
GetViewError = (Err.Number <> 0)
On Error GoTo 0
If GetViewError Then Exit Do ' Next oExplorer
' Have to look for the View by View.Name (and FolderPath if not Shared)
' because if there are multiple Explorers open to the same View, only
' one of them has the same Object reference as oView.
' If Name doesn't match - Next oExplorer
'
If oView.Name <> oExplorerView.Name Then Exit Do ' Next oExplorer
' If Not Shared
' - If FolderPath doesn't match - Next Explorer
'
If (ViewLock_ViewVType(oView) And VTypes_Shared) = 0 Then
If oView.Parent.Parent.FolderPath <> oExplorerView.Parent.Parent.FolderPath Then Exit Do ' Next oExplorer
End If
' We've found oView in at least one Explorer - will Return True
'
ViewLock_StateExplorerCurrent = True
' If first occurance
' - Run it through SharedSeen
' - If Not SharedSeen - Update Skipped Count
'
If ExplorersUpdated = 0 Then
If Not ViewLock_StateSharedSeen(oView) Then
If oExplorerView.LockUserChanges = ActionBool Then
Counts(Counts_ViewsSkipped_NoChange) = Counts(Counts_ViewsSkipped_NoChange) + 1
End If
End If
End If
' If No Change - Next oExplorer
'
' - Using LockUserChanges instead of XML because in this case the
' - XML is unreliable.
'
If oExplorerView.LockUserChanges = ActionBool Then
ExplorersUpdated = ExplorersUpdated + 1
Exit Do ' Next oExplorer
End If
' Make the changes
' - Inc Chaged Count for the first occurance only.
' - Update only LockUserChanges, not the XML. Stupid will do that (hopefully).
'
If Not ViewLock_StateSave( _
oView:=oExplorerView, _
SetLock:=True, _
SetXML:=False, _
IncChangedCount:=(ExplorersUpdated = 0) _
) Then Stop: Exit Function
ExplorersUpdated = ExplorersUpdated + 1
Loop While False: Next oExplorer
End Function
' Is the State of oView changing?
'
' If Is Changing - Return True. Else - Update ViewsSkipped and Return False.
'
' SPOS - Testing has shown that the State of oView.LockUserChanges is not
' reliable for Views other than Explorer.CurrentView, so we check the XML.
'
Private Function ViewLock_StateChanging(ByVal oView As Outlook.View) As Boolean
On Error Resume Next
' If the XML ReadOnly <> Current Action Boolean - ViewLock_StateChanging = True
'
ViewLock_StateChanging = ((InStr(1, oView.XML, XMLReadOnlyLocked) <> 0) <> ActionBool)
' If the Get oView.XML threw an Error
'
If Err.Number <> 0 Then
' If a Cached View that was renamed or deleted but Stupid is holding a copy
' - Inc the skipped count and Return False
'
If Err.Description = ErrViewNotFound Then
Counts(Counts_ViewsSkipped_CacheGhost) = Counts(Counts_ViewsSkipped_CacheGhost) + 1
ViewLock_StateChanging = False
Exit Function
End If
Stop: Exit Function
End If
On Error GoTo 0
' If changing - Return True
' Else - Inc the skipped count and Return False
'
If ViewLock_StateChanging Then Exit Function
Counts(Counts_ViewsSkipped_NoChange) = Counts(Counts_ViewsSkipped_NoChange) + 1
End Function
' Does oView pass the current VTypes filter?
'
' If oView passes the current VTypes filter - Return True
' Else Inc Skipped Counter and Return False