(Vba) Ms Word: Work around the 255 characters limit - forms

I'm new to programming and I'm trying to copy the content of a form field to another form field in the same Word document like this:
Sub Copyfield()
Dim Temp As String
Temp = ActiveDocument.FormFields("Field1").Result
ActiveDocument.FormFields("Field2").Result = Temp
End Sub
My problem is that my "Field1" is a piece of text of more than 255 characters which seems to be a problem with "Result". I know there is a very similar topic here: Passing MS-Access string >255 characters to MS-Word field but I still don't have the 50 reputation to comment on that thread.
Could anyone please help me understand how to implement the changes in my code?

Well, here's one possibility. Since I don't have your environment it was easier for me to test text in the document rather than another form field with so much content. You'll need to adjust the code accordingly.
The key is to get the Selection "inside" the form field so that it doesn't hit the "protection barrier". Just using FormField.Select puts the focus at the beginning of the field, which VBA is seeing as "protected". Moving one character to the right corrects that and long text can then be assigned to the Selection. But the field needs to have content.
So what my code is doing is "slicing off" the first word of the text to go into the form field. That's short enough to assign to the Result property and lets the Selection move to its right. Then the rest - the long text - can be assigned to the Selection.
You'll probably want to assign the entire FormField.Result to a string variable, then manipulate that string.
Sub WriteLongTextToFormField()
Dim ffld As word.FormField
Dim doc As word.Document
Dim rng As word.Range
Dim s1 As String, s2 As String
Set doc = ActiveDocument
'Get the long text
Set rng = doc.Range(doc.Paragraphs(1).Range.Start, doc.Paragraphs(6).Range.End)
'Split off a bit to go into FormField.Result
s1 = rng.Words(1)
rng.MoveStart wdWord, 1
'The rest of the long text, to be assigned to Selection.Text
s2 = rng.Text
Set ffld = doc.FormFields("Text1")
ffld.result = s1
ffld.Select
Selection.MoveRight wdCharacter, 1
Selection.Text = s2
End Sub

Ok, after 3 days at the border of madness, finally thanks to the help of #Cindy Meister (and some serious personal digging), I made it work. Maybe it's not a big deal for you geniouses out there but believe me for me it was like seeing everything in Matrix code (from the movie guys, the movie).
I want to post it and share because I tried to find it in every corner of our Internet and part of the extraterrestrial one and I couldn't. So hopefully it will be useful for another programming illiterate / dumb person (as myself).
Here is the code:
Sub CopyField()
Dim ffld As Word.FormField
Dim doc As Word.Document
Dim rng As String
Dim s1 As String, s2 As String
Set doc = ActiveDocument
'Get the long text
rng = ActiveDocument.FormFields("Field1").Result
'Split off a bit to go into FormField.Result
s1 = Left(rng, 4) 'Keeps the first 4 characters of the rng string starting from left to right this can be adapted
'The rest of the long text, to be assigned to Selection.Text
s2 = Mid(rng, 5) 'Starting from the 5th character from the left keeps the rest of the string
Set ffld = doc.FormFields("Field2")
ffld.Result = s1
ffld.Select
Selection.MoveRight wdCharacter, 1
ActiveDocument.Unprotect 'Unprotects the document!
Selection.Text = s2
ActiveDocument.Protect Type:=wdAllowOnlyFormFields, NoReset:=True 'Protects the document again!
ActiveDocument.Bookmarks("Field1").Select ' "Sends cursor" back to Field1
End Sub
Big part of the code is originally by #Cindy Meister... I just adapted it to my situation where I had 2 form fields instead of paragraphs. I also had to add some lines to unprotect the document at a certain point in order to make it work (ask the Pros for the reason) and a final instruction to come back to "Field1" (which is some pages up) after the process. Finally just a note for my dumb fellows: I added the macro "on exit" on the "Field1" properties to automatize the process.
Huge thanks Cindy again and I hope you help in my dark programming moments again ! (please do)
:)

Related

What function do I use in a Salesforce apex trigger to trim a text field?

I'm new to writing apex triggers. I've looked through a lot of the apex developer documentation, and I can't seem to find the combination of functions that I should use to automatically trim characters from a text field.
My org has two text fields on the Case object, which automatically store the email addresses that are included in an email-to-case. The text fields have a 255 character limit each. We are seeing errors pop up because the number of email addresses that these fields contain often exceeds 255 characters.
I need to write a trigger that can trim these text fields to the last ".com" before it hits the 255 character limit.
Perhaps I'm going about this all wrong. Any advice?
You can use replace() function in Apex.
String s1 = 'abcdbca';
String target = 'bc';
String replacement = 'xy';
String s2 = s1.replace(target, replacement);
If you need to use regular expression to find the pattern, then you can use replaceAll()
String s1 = 'a b c 5 xyz';
String regExp = '[a-zA-Z]';
String replacement = '1';
String s2 = s1.replaceAll(regExp, replacement);
For more information please refer Apex Reference Guide
The following code I think that covers what you are searching:
String initialUrl = 'https://stackoverflow.com/questions/69136581/what-function-do-i-use-in-a-salesforce-apex-trigger-to-trim-a-text-field';
Integer comPosition = initialUrl.indexOf('.com');
System.debug(initialUrl.left(comPosition + 4));
//https://stackoverflow.com
The main problems that I see are that other extensions are not covered (like ".net" urls) and that urls that have a ".com" appearing previous to the last one (something like "https://www.comunications.com"). But I think that this covers most of the use cases.
Why not increasing the length of that specific field? Trimming the text might cause data loss or unusable. Just go to object manager find that object and that specific field. Edit field and increase the length.

ForAll Loop breaks inline images of email in Lotus Notes

I do have a Lotus Notes email containing inline images. When I run the following lines of code which simply changes the subject of the email the inline images is lost.
Sub Initialize
Dim ns As New NotesSession
Dim nc As NotesDocumentCollection
Dim nd As NotesDocument
Set nc = ns.Currentdatabase.Unprocesseddocuments()
Set nd = nc.GetFirstDocument()
ForAll Items In nd.Items
End ForAll
Call nd.Replaceitemvalue("Subject", "Forall")
Call nd.Save(True, False)
End Sub
The problem is caused by the ForAll loop which actually doesn't do anything. If the loop is commented out the inline images still remain on the email.
I'm hoping any of you guys has an idea or advise how to solve this issue.
Cheers,
Martin
Your message body is probably being converted from Notes rich text to MIME when the item is accessed and then being re-saved, with a loss of fidelity.
Try setting ns.ConvertMIME = false at the beginning of your code.

How to run a spell check on a string without user input?

I'm trying to run a spell check on a string using word, but I don't want to have to go through each word manually and determine the correct spelling. Is there a way to do this automatically, so that it corrects all the misspelled words on its own? Here is my code so far, this works but I have to go through each word and hit change...
If Address.Length > 0 Then
Dim word_server As New Word.Application
word_server.Visible = False
Dim doc As Word.Document = word_server.Documents.Add()
Dim rng As Word.Range
rng = doc.Range()
rng.Text = Address
doc.Activate()
doc.CheckSpelling()
Address = doc.Range().Text
doc.Close(SaveChanges:=False)
word_server.Quit()
doc = Nothing
rCell.Value = Address
End If
Use the GetSpellingSuggestions function to bypass the GUI. to see if there are any spelling suggestions, and then you can set it to the first suggestion (which could be a bad idea).
How do you want to determine what the correct spelling is? Should "spon" be spoon, span, spin, spun, or son? This code optimistically assumes that the first suggestion (if one exists) is the correct solution.
Declare:
Dim oError As Word.Range
And then replace:
doc.Activate()
doc.CheckSpelling()
with:
For Each oError In rng.SpellingErrors
If oError.GetSpellingSuggestions.Count > 0 Then
oError.Text = oError.GetSpellingSuggestions().Item(1).Name
Else
'Uh oh, no suggestions, do something here.
End If
Next
These websites might help. The example code in the website shows how to call Word to spell check. You should be able to modify it, to use with your code.
http://www.vb-helper.com/howto_net_spellcheck.html
http://www.vbforums.com/showthread.php?307151-SPELL-CHECK-and-WORD

AppleScripting an MSWord find operation

I'm trying to convert a Word VBA procedure to an AppleScript and only having partial luck.
Here's the first version of the VBA procedure:
Public Sub postprocessMerges1()
Dim rng As Range
Selection.HomeKey unit:=wdStory
With Selection.Find
.ClearFormatting
.Forward = True
.Wrap = wdFindStop
.Format = False
Do
.Text = "..."
.Execute
If .Found Then
.Parent.Select
Set rng = Selection.Range
rng.MoveStart unit:=wdParagraph, Count:=-1
rng.MoveEndUntil cset:=Chr(13)
rng.Text = formatAmounts(rng.Text)
End If
Loop While .Found
End With
End Sub
And here's my corresponding AppleScript:
on postprocessMerges()
tell application "Microsoft Word"
home key selection move unit a story extend by moving
set selFind to find object of selection
clear formatting selFind
set foundIt to true
repeat while foundIt
set foundIt to execute find selFind find text "..." wrap find stop with match forward without find format
if foundIt then
set foundRng to text object of selection
set foundRng to move start of range foundRng by a paragraph item count -1
set foundRng to move range end until foundRng characters {return}
set tt to (content of foundRng)
set (content of foundRng) to my formatAmounts(tt)
end if
end repeat
end tell
end postprocessMerges
Okay, so that works just fine. However, I'd like it to be better. The way the script is currently written, it actually jumps from hit to hit, highlighting the found text and performing the replacement generated by the formatAmounts subroutine. Not bad, but when you're working with a 200+ page document, it gets a little tedious to see that happening onscreen.
So in VBA, I can do this:
Public Sub postprocessMerges2()
With ActiveDocument.Content.Find
.ClearFormatting
.Forward = True
.Wrap = wdFindStop
.Format = False
Do
.Text = "..."
.Execute
If .Found Then
With .Parent
.MoveStart unit:=wdParagraph, Count:=-1
.MoveEndUntil cset:=Chr(13)
.Text = formatAmounts(.Text)
.Collapse direction:=wdCollapseEnd
End With
End If
Loop While .Found
End With
End Sub
This will perform the exact same action as the first procedure, but it does so on the document's content range rather than the selection range and so I don't have to watch Word jump around from page to page to page. Much more preferable, but not something I've been able to emulate in AS.
Specifically, I can't seem to get the hit range each time through the loop without selecting it first. The find object in Word's AS dictionary doesn't have a Parent property I can access like I do in VBA.
Is there anything I'm missing? Is what I do in the second VBA proc actually replicable in AS?
This is using Word 2011 and AppleScript 2.3 on OS X 10.9.3.
This should probably be a comment, but...
Is what I do in the second VBA proc actually replicable in AS?
Let me put it this way. I think you will probably end up using the Selection object a lot more in AS than you are probably used to doing in VBA. In this case, in VBA, the Find.Execute should return a new Range, but in AS it just returns true/false. If you look it up in the AS dictionary, it looks as if the execute find should return a "text range/insertion point" but what it actually returns is what is stated in the description at the top of the entry for "execute find", i.e. a boolean. The find object does not have a range. So it is difficult to see how to get the range of the thing you just found. As far as I can tell, using execute find in AS with a find object derived from a range object is only useful in the situation where you can specify the replacment object and do the entire replace in the execute.
It might also be useful to read Matt Neuberg's comments in this conversation

Creating a 'calendar matrix' in Access

I’m trying to create either a report or form that displays data in essentially a “calendar” form.
I have a course query that is (simplified) as “Course name”; “course days”; “course times”---
Course; Days; Times
PSY 1; MW; 8A-9A
SOC 150; M; 8A-11A
ANTH 2; Tu; 8A-9A
ANTH 199; MW; 8A-9A
In Access, I’m trying to create a form based on the query that would give me a matrix of the following:
Columns: Times in hour increments
Rows: Days of week
So, for example, with the above data, it would appear like this:
Edit: Yargh, I can't submit an image unfortunately. So, here is a link to a "course schedule" that is essentially what I'm trying to do: Schedule
I have no idea where to start with this. Any tips (or links)?
Edit:
One idea I have is to create a form with a field for every possible cell in the matrix (so, for example, there would be one "Monday, 8-9A" field--and that field would be a filter on the query that ONLY displays results where "day" contains "M" and BeginTime or EndTime or between 8A and 9A). Unfortunately, I'm not sure how to do that.
You can do something close to what you seem to want as an Access form, but it's not easy. This screen capture displays your sample data in a Datasheet View form whose record source is an ADO disconnected recordset. It uses conditional formatting to set the text box background color when the text box value is not Null. Your picture suggested a different color for each Course, but I didn't want to deal with that when more than one Course can be scheduled in the same time block ... my way was simpler for me to cope with. :-)
The code to create and load the disconnected recordset is included below as GetRecordset(). The form's open event sets its recordset to GetRecordset().
Private Sub Form_Open(Cancel As Integer)
Set Me.Recordset = GetRecordset
End Sub
Note I stored your sample data differently. Here is my Class_sessions table:
Course day_of_week start_time end_time
------ ----------- ---------- -----------
PSY 1 2 8:00:00 AM 9:00:00 AM
PSY 1 4 8:00:00 AM 9:00:00 AM
SOC 150 2 8:00:00 AM 11:00:00 AM
ANTH 2 3 8:00:00 AM 9:00:00 AM
ANTH 199 2 8:00:00 AM 9:00:00 AM
ANTH 199 4 8:00:00 AM 9:00:00 AM
This is the function to create the disconnected recordset which is the critical piece for this approach. I developed this using early binding which requires a reference for "Microsoft ActiveX Data Objects [version] Library"; I used version 2.8. For production use, I would convert the code to use late binding and discard the reference. I left it as early binding so that you may use Intellisense to help you understand how it works.
Public Function GetRecordset() As Object
Dim rsAdo As ADODB.Recordset
Dim fld As ADODB.Field
Dim db As DAO.Database
Dim dteTime As Date
Dim i As Long
Dim qdf As DAO.QueryDef
Dim rsDao As DAO.Recordset
Dim strSql As String
Set rsAdo = New ADODB.Recordset
With rsAdo
.Fields.Append "start_time", adDate, , adFldKeyColumn
For i = 2 To 6
.Fields.Append WeekdayName(i), adLongVarChar, -1, adFldMayBeNull
Next
.CursorType = adOpenKeyset
.CursorLocation = adUseClient
.LockType = adLockPessimistic
.Open
End With
strSql = "PARAMETERS block_start DateTime;" & vbCrLf & _
"SELECT day_of_week, Course, start_time, end_time" & vbCrLf & _
"FROM Class_sessions" & vbCrLf & _
"WHERE [block_start] BETWEEN start_time AND end_time" & vbCrLf & _
"ORDER BY day_of_week, Course;"
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, strSql)
dteTime = #7:00:00 AM#
Do While dteTime < #6:00:00 PM#
'Debug.Print "Block start: " & dteTime
rsAdo.AddNew
rsAdo!start_time = dteTime
rsAdo.Update
qdf.Parameters("block_start") = dteTime
Set rsDao = qdf.OpenRecordset(dbOpenSnapshot)
Do While Not rsDao.EOF
'Debug.Print WeekdayName(rsDao!day_of_week), rsDao!Course
rsAdo.Fields(WeekdayName(rsDao!day_of_week)) = _
rsAdo.Fields(WeekdayName(rsDao!day_of_week)) & _
rsDao!Course & vbCrLf
rsAdo.Update
rsDao.MoveNext
Loop
dteTime = DateAdd("h", 1, dteTime)
Loop
rsDao.Close
Set rsDao = Nothing
qdf.Close
Set qdf = Nothing
Set GetRecordset = rsAdo
End Function
Actually, if you look at the following video of mine, you can see a calendar created in Access that runs inside of a browser with the new Access Web publishing feature.
http://www.youtube.com/watch?v=AU4mH0jPntI
So, all you really need to do here is format a form with text boxes and setup some code to fill them. VBA or even the above video shows this is quite easy for Access.
I doubt that you will find an easy solution for this problem in Access forms or reports.
The issue is that you need to format different cells differently, and that cells can span multiple rows and have to be merged.
If I were you, I would go in either of these 2 directions:
Drive Excel from Access, because you can format and merge cells independently
Use the Web Browser Control to display HTML that you construct yourself using tables, or a more high-level library like FullCalendar
I would be partial to tryingthe Web Browser and find the right library that can properly format the data.
I know this post is quite old, but I had the same requirement but I got round it by doing the following:
I created a module that would write HTML code (as a text file) to produce the data using a table.
I used the colspan feature of tables to enable me to dynamically produce the view I needed.
The HTML file was created on the Form_Open event and by pointing the webbrowser control to the HTML file the view shows the latest data.
A css file is used to handle the visual display of the HTML file so that it looks similar to the Access forms already in use.
If anyone is interested I can post some code to further illustrate.