Access: 2 roles on the same DB with different roles - ms-access

I have an Access Database on the SharePoint and two user's roles that have to work on it:
Role1 is given to a single user. He can manage all the forms of the DB with the possibility to change all the tables. These forms will require many future releases.
Role2 is given to many users. They can manage only one form that is effective only on a single table of the DB. He doesn't (and mustn't) need to see/manage the others forms/tables (they are a lot). The form used for this role will not require future releases.
What it the best practice to manage this? I thought about the following 2 solutions but probably there is a third one that is better.
Solution #1
Having 2 different Access files linked to the same DB: one with all the forms (that I give to Role1) and one with only one form (that I give to Role2).
Solution #2
Having a single Access file that I give to both the users. Role1 has read/write access to all forms. Role2 can see only a specific form and has no read/write access to other forms.

I would recommend solution #2 otherwise if you change the logic in the front end you have to change it always twice and this is really annoying. Also, if you forget to change both versions and you had a huge change in the logic, you have people working with the old version and people working with the new version.
What I normally do is the following. I have a table tblDeveloperAccess with the ID of your Role1. At the start of the first form load event I have the following code:
Dim db As Database
Dim rs As Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("tbDeveloperAccess", dbOpenSnapshot)
rs.FindFirst "ID = '" & Environ("USERNAME") & "'"
If rs.NoMatch Then
DoCmd.NavigateTo "acNavigationCategoryObjectType", "acNavigationGroupTables"
DoCmd.SelectObject acForm, vbNullString, True
DoCmd.RunCommand acCmdWindowHide
DoCmd.ShowToolbar "Ribbon", acToolbarNo
End If
Basically I am hiding the ribbon and the object navigation. You can go even further and disable the context menu and the "Shift + DblClick"-Trick (on your database file). And of course I disable the possibilty on the start form to get into the design view.

Related

How can I refresh a linked table in VBA when it contains a multi-valued field?

I have some VBA that programmatically updates the location of linked tables so that the front end of my database can be easily pointed at a different back end, without needing to click around in the Linked Table Manager. The technique is explained in this Microsoft blog post. This should be as simple as running the following code for each linked table in CurrentDb.TableDefs:
tableDef.Connect = ";DATABASE=" & newBackEndPath
tableDef.RefreshLink
However, when the table contains a multi-valued field, Access provides the following unhelpful and misleading error message:
Run-time error '3125':
'[table name]' is not a valid name. Make sure that it does not include invalid characters or punctuation and that it is not too long.
The same error appears when manually updating the links from the Linked Table Manager, although sometimes after a few attempts, the update will work without an error message. To work around the problem, I'm trying to refresh the link by creating a new TableDef and then renaming it over the old one:
Dim tdf As TableDef
Set tdf = CurrentDb.CreateTableDef(originalTableName & "_Copy")
tdf.Connect = ";DATABASE=" & newBackEndPath
tdf.SourceTableName = tableName
With CurrentDb.TableDefs
.Append tdf
.Refresh ' Required?
End With
RefreshDatabaseWindow ' Required?
DoEvents ' Required?
DoCmd.Rename originalTableNameName, acTable, originalTableNameName & "_Copy"
This usually works, but sometimes DoCmd.Rename throws the following run-time error:
Run-time error '7874':
Microsoft Access can't find the object '[table name]_Copy'.
If I enter the debugger, this usually triggers some kind of refresh and the _Copy table appears in the navigation pane. If I press F5 at this point, DoCmd.Rename executes with no problems. Clearly, there is some delay after the TableDef is appended before it can actually be found and renamed. I've added the TableDefs.Refresh, RefreshDatabaseWindow and DoEvents lines in an attempt to force the new table to appear, but they don't seem to help. Even forcing a delay of a few seconds doesn't seem to work, but somehow, opening the debugger does.
Is there any reliable way to refresh a linked table that contains a multi-valued field?
(I ignored the advice to avoid using multi-valued fields when I started this project, because I wanted users to be able to filter on multi-valued fields using the Quick Filters on a split form datasheet. This function is very helpful, but I'm beginning to wish I just built my own filtering UI after encountering this and numerous other bugs in the implementation of multi-valued fields.)
You could try a different approach altogether, by using DoCmd.TransferDatabase:
DoCmd.TransferDatabase acLink, "Microsoft Access", newBackEndPath, acTable, tableName, tableName
DoCmd.TransferDatabase is similar to using the GUI to link the table. It handles steps like refreshing the database pane for you. I usually recommend using TableDefs instead, but this situation might be an exception.
Of course, I support Sergey's advice to not use a multi-valued field. I'm a fan of the user experience of multi-valued fields and the associated comboboxes, but it has far-going consequences to use a MVF, like not being able to migrate your database to SQL server in the future

Edits getting lost

Background:
Split access database, maximum two users. The back end is located on one of the two computers on a mapped drive and the front end is local. The computers are connected to the router by wifi. (I am trying unsuccessfully to get them to connect the computers via cable)
Edit: This problem is now happening in a single user environment.
Problem:
This is happening on one specific form only. Its underlying table has a multi-value field.
Issue 1: We have a situation where a field will be edited, but as soon as the focus moves to another field the edit reverts back to its original value.
Issue 2: When editing one field, some of the other fields are getting the values of the previously shown record.
Note: Navigation from one record to the other is done as follows:
Me.RecordsetClone.FindFirst "ID = " & cmbLocateRecipientID
Me.Bookmark = Me.RecordsetClone.Bookmark
The issues only happen occassionaly.
This is an extremely weird behaviour on the part of Access, so when the users first reported the issues I was convinced that they were entering information mistakenly into another record. However, they have since shown me the issue happening live.
Closing and reopening the form solves the issue. However, they can obviously not work in such a fashion.
I cannot reproduce the problem on my development machine.
Me would think you have to specify and use the recordset object:
Dim rs As DAO.Recordset
Set rs = Me.RecordsetClone
rs.FindFirst "ID = " & Me!cmbLocateRecipientID.Value & ""
If Not rs.NoMatch Then
Me.Bookmark = rs.Bookmark
End If
Set rs = Nothing
A similar change may be needed in other parts of your code.
You can't use MS Access on a wireless network as "wireless" does not keep a continuous link between the frontend and backend. There are occasional dropouts which you don't notice with most things, however MS Access is very sensitive to this and you will have all sorts of problems over wireless. You must use a physical cable as the connection.

Acess support please

Access Docment scanned with Macfee and Microsoft on a server platform One drive so completly safe.
I want a user login page which has different levels so that different users will have access to different objects or functionality.
Please have a look at the coding below I have also attached a link to onedrive for the access 2013 document.
code:
Private Sub Form_Load()
Dim Security As Integer
Me.txtLogin = Environ(“userName”)
Me.txtUser = DLookup(“userName”, “tblUser”, “Username = ‘” & User & “‘”)Then
If IsNull(DLookup(“userSecurity”, “tblUser”, “UserLogin = ‘” & Me.txtLogin & “‘”)) Then
MsgBox (“No User security set up for this user. Please contact the Admin”, vbOKOnly, “Login Info”
Me.NavigationButton13.Enabled = False
Else
Security = DLookup(“userSecurity”, “tblUser”, “UserLogin = ‘” & Me.txtLogin & “‘”)
If Security = 1 Then
Me.NavigationButton15.Enabled = True
Else
Me.NavigationButton15.Enabled = False
End If
End If
End Sub
I am guessing you have a main login page for the entire app, which is the startup form. Keep in mind that a user can hold down the Shift key when opening your app to bypass any startup code, but you can disable that. You can also hide the database container. Users can always use another DB to link your tables, or import objects, so you will also want to lock the entire database with a password.
The form startup code can check your custom security table and then you can use Cancel = True to short-circuit the opening event. It seems like you are trying to accomplish that by disabling navigation buttons, but you may want to disallow the opening of the form.
As HansUp and Tim have suggested, tt would be helpful if you can describe your situation a bit more so that we can respond more concisely. If you have a situation where you need true security that is hard to breech, you may want to consider moving your tables into SQL Server (or SQL Express) and linking them to your Access container. Ultimately, there's nothing you can do in Access that I could not get around.

User management in Microsoft Access 2013 databases

I am building a system using Microsoft Access 2013 that uses MySQL as a backend. I am trying to figure out a way to do some kind of basic user management, ideally in such a way that users would have to "log in" when they launch the database, and then their username would be easily accessible by the system while they are using it. I've tried searching for solutions, but most of them just tell me to use Office 365 or sharepoint, which are not options at the moment. Does anyone have an idea of how to accomplish this? Thanks in advance!
I recommend building your own user storage and login system. You'll basically need to create your own users table (in MySQL in your case), make forms to manage users, make a Login form, and write code to control the login process.
Logging in usually consists of checking some kind of credentials they type in against existing data in your users table. You can usually do this in Access with DLookup or DCount statements but I usually end up using a DAO or ADO recordset instead since I like to pull out more than one value from the User's table and I also like to write things back to it right away, like the LastLogin datetime, LastLogin computername, etc.
I actually wrote an example database which you can download here. It needs a rewrite. I've changed quite a few of my practices since Jan, 2011. But give me another year and it would need another rewrite.
I usually program the login form so that the user enters their initials and then a password. If you go this route you need to have a unique index setup on the Initials field to prevent duplicates. If you're going to have a lot of users you need to use a Username instead, which could still theoretically be the users initials.
Here's what my code would look like to authenticate a user. Be aware that this is far from truly secure. This assumes that passwords are stored in plain text. Users could theoretically try to do SQL Inject from here because I'm not use a parametrized query or stripping out special characters from their input such as # or ;.
Private Function AuthenticateUser() As Boolean
Dim sInitials As String
Dim sPassword As String
sInitials = Trim(Nz(Me.txtInitials, ""))
sPassword = Trim(Nz(Me.txtPassword, ""))
If sInitials = "" Or sPassword = "" Then
'Logging in with blank passwords is not allowed
AuthenticateUser = False
Exit Function
End If
If DCount("EmployeeID", "tblEmployees", "[Initials] = '" & Replace(sInitials, "'", "''") & "' AND Password = '" & Replace(sPassword, "'", "''") & "'", True) = 0 Then
MsgBox "Invalid Credentials."
AuthenticateUser = False
Exit Function
Else
Dim rs As New DAO.Recordset
Dim sSQL As String
sSQL = "SELECT * FROM tblEmployees WHERE initials = '" & Replace(sInitials, "'", "''") & "'"
Set rs = CurrentDb.OpenRecordset(sSQL)
If Not (rs.EOF And rs.BOF) Then
'Config is an instance of a User Defined Type. It could also be a class object.
Config.UserInitials = rs("Initials")
Config.UserFullName = rs("EmployeeName")
Config.UserID = rs("EmployeeID")
rs.Edit
rs("LastLoginDateTime") = Now()
rs("LastLoginComputer") = "Function Required to Get Computer Name"
rs("ProgVersion") = "Your Program Version Number"
rs("CurrentDbPath") = Left(CurrentProject.path & "\" & CurrentProject.Name, 254)
rs.Update
End If
rs.Close
Set rs = Nothing
AuthenticateUser = True
End If
End Function
In my applications I use a global object, in this case an instance of a User Defined Type, which I call Config. I store any kind of application runtime related settings in there for the duration of the runtime of the application. Of course this object gets destroyed when the user closes out of the application or when a code reset happens (which cannot happen in Access runtime, but does happen frequently during development). You could use a class object instead of a User Defined Type. Or you could use individual global variables for everything, which I don't recommend (that's what I used to do). A User Defined Type simply allows you to group global variables together and gives you an easy way to refer to them in your code during design time by typing in Config., which then brings up every option using Intellisense (assuming you have it enabled).
If you want your settings to survive a code reset, you need to use TempVars. TempVars became available with Access 2007. I do not use them now (contrary to my example database) because they are not strongly typed. There's no Intellisense to help you get the correct TempVar and you can technically refer to a TempVar that doesn't even exist and Access won't throw an error. I think TempVars is really just a Dictionary object with all of it's shortcomings, and the single benefit of surviving a code reset. I can imagine storing a Connection String in there, but I wonder if it's worth using TempVars for anything at all. If a code reset happens, my entire application needs to be reloaded anyway since I setup a lot of global objects and variables when the application first opens and the user first logs in.
FYI, in previous versions of Access there was user security built in. I think Microsoft discontinued that starting in 2007. I never really used it so I didn't miss it when it got discontinued.

Display multiple attachments in microsoft access 2010 forms and reports

I was initially very pleased to discover the attachment field in Access 2010. It's a feature that aesthetically irks my inner database purist but my inner lazy sod is in charge here and it does look, on the face of it, like it could make one of my current projects much easier/simpler. Happily it displays pictures/icons automatically on the forms and reports but (why is there always a but eh!) it only displays the first one and I need it to display all of them.
You can of course scroll through the attachments one at a time but I'm pretty sure my client won't wear that, despite his request that I complete the project in MS-Access which, seemingly, only has very rudimentary built in options for display :/ BUT...
I may well be wrong, I've got almost no MS-Access experience. My coding background is firmly LAMP stack and web so I'm deeply ignorant of what's on offer in the Windows/Access ecosystem. I suspect there are excellent 3rd party reporting tools that give very flexible layout but I need to see all the attachments on the form, not just the reports.
So, blundering blindly into the void my initial strategy is this...
Create a separate table for attachments where each field is an "attachment" containing a single item only. Then use scripting in the forms and reports to...
Query that table for all attachments belonging to the record in question
Display/Format those fields as some sort of list
Dynamically append a fresh attachment field to the end of that list so the user has somewhere to upload a next attachment
Make the form page refresh whenever an attachment is added so there's aways a free one.
So, my questions are...
Is what I describe feasible in Access?
Am I missing a much simpler / better / canonical solution?
How powerful is Access's scripting language(s) with reference to display? i.e clunky or pixel perfect?
It's not still Visual Basic is it? (noooooo! ;)
If so are there any other scripting languages I can use within forms/reports?
Sorry, I know it's a bit of a long wooly question but I'm a fish out of water here!
Thanks,
Roger
Let us say I have a table with an attachment:
Let us say that I have three images in one of those attachment fields that I wish to display. I can create a query:
After which I can create a continuous form:
I searched this for a similar problem. I have multiple attachments per field. I intend to use the field to store a .jpg and a .pdf for two image controls which I created by dragging the field from the properties to the form. I have named them ctlImage and ctlFeatures. I am using this form as a non modal popup from a more info button on the product catalog form. So far I am able to search records in the product catalog and use the selected listbox search result to open the form with a where clause to set the details form to the current record. When I try to manipulate the ctlImage looking for image type the only property which shows in intellisense is ctlImage.value. I was hoping to assign ctlImage to jpg and ctlFeatures to pdf Here is the frmProducts btnMoreInfo_Click code and the frmCatalogDetails code in case one is messing up the other.
Private Sub btnMoreInfo_Click()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim str As String
Dim row As Long
Set db = CurrentDb
Set rs = db.OpenRecordset("tblProducts", dbOpenSnapshot)
If Me.lstSearchResults.ItemsSelected.Count > 0 Then
' Debug.Print Me.lstSearchResults.Column(0)
row = Me.lstSearchResults.Column(0)
rs.MoveFirst
rs.FindFirst "ProductID=" & row
Debug.Print rs("Description")
DoCmd.OpenForm "frmCatalogDetails", acNormal, , "ProductID=" & Me.lstSearchResults.Column(0, Me.lstSearchResults.ItemsSelected), acFormReadOnly, acWindowNormal
Else
MsgBox "You must select a product"
Exit Sub
End If
End Sub
and Catalog details
Option Compare Database
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim fld As DAO.Field
Private Sub Form_Load()
Set db = CurrentDb
'Set rs = db.OpenRecordset("tblProducts", dbOpenSnapshot)
'Set fld = rs("image")
Debug.Print rs("ProductID")
Debug.Print rs("Description")
'me.ctlImage.ControlSource =
'Me.ctlImage.CurrentAttachment = fld.Value
End Sub
The rs statements are commented out because I am having trouble with this form recognizing the rs from the parent form. I am getting a with block variable not set, but if I do rs.openRecordSet then the recordset goes back to the first row. Aside from your answer above, I have seen very little about manipulating the attachment object and the help has been difficult as it doesn't even cover accessing inside the attachment field. I am at a point where I will do more asking than answering on this topic, and I very much appreciate the time many of you take to craft an answer.
Rollin
Motto: Ask for help when needed, give help when asked, and remember where you came from.