• Tobias,

    Tobias,

    I am well aware of the pros and cons of accessing the database directly, and we do not write anything to the data.

    But there are certain things (like details of profiles and roles to obtain permission information) that cannot be accessed via the Platform SDK. If you wanted a report that simply listed users and the groups they were in, along with file cabinet permissions, how would you do that with existing client tools? The Admin tool has almost no reporting whatsoever, and the web client is even less effective when it coes to a reporting standpoint. Client tools are meant for active configuration, not reporting.

    That's not even getting into what this thread is about -- visibility of configurations where users forget to give my department access. I know of no way to see all file cabinets and who has access to them without doing a raw pull from th eunderlying data. Even the Platform SDK won;t work there, because it will only show what you have permissions to view -- the very thing I am trying to report on. The alternative is to never grant anyone but my department access and force them to run all changes through me. That's not very efficient either.

    I have need for such reporting regularly, so I have no choice but to hit up the data model when the Platform Service does not have what I need. How do you obtain such information? Is there something I am missing?

     

    Thanks,

    Joe Kaufman

  • FullText OCR not recognizing text in PDFs

    Hey all,

    I am starting to develop a file cabinet for our packaging group, sort of like what the Rawlings company did and showed off in that DocuWare marketing video (http://info.docuware.com/free-webinar-rawlings).

    The first set of files I am uploading are PDF files that are considered the "print version" or "final print" images. These images are not stored as JPG, TIFF, or PNG, but as PDFs. The packaging supervisor says these PDFs have the text "stripped out of them" to keep the files pure for print usage. I am not sure about the details on that, but it sounded like standard practice which our print vendors dictate (or at least appreciate).

    When I uploaded these PDFs to the new cabinet I created (which has full text turned on). nothing got recognized as a fulltext keyword. I changed the OCR file list to a "black" list, keeping out only audio and video files. Though, I did notice that PDF files were not specifically in the list of file types to white- or black- list.

    When I convert the PDF files to PNG and upload those, full text finds everything inside and I can search the documents as if they had been scanned in. Works beautifully.

    But we need that to work with PDFs. Can DocuWare not fulltext OCR these PDF files that are "image only"? This will be pretty sad if we cannot, as it was one of the biggest reasons I was pushing for this for people (and it was a big selling point in the Rawlings business case scenario).

    Let me know what can be done. Greatly appreciated!

     

    Thanks,

    Joe Kaufman

  • Stephane,

    Stephane,

    Yes, you can update indexes from the Platform SDK API. 

    Did you read my responses on the other thread you posted about using the Platform SDK? That would be a good place to start, as it lays the foundation for non-.NET Platform SDK usage (Visual Foxpro, in my examples).

    Once that code/methodology is understood, here is our routine that updates indexes:

    =====================
    PROCEDURE DWUpdateDocumentIndexes
        LPARAMETERS pcFileCabinetName, pnDWDocID, paIndexes, pcError
        EXTERNAL ARRAY paIndexes
        pcError = ""
        IF (VARTYPE(pcFileCabinetName) == "C") AND (NOT EMPTY(pcFileCabinetName))
            pcFileCabinetName = ALLTRIM(pcFileCabinetName)
        ELSE
            * File cabinet name is required.
            MESSAGEBOX("File cabinet name (parameter 1) must be a non-empty string passed to DWUpdateDocumentIndexes().", 64, "Invalid Parameter")
            RETURN .F.
        ENDIF
        IF (VARTYPE(pnDWDocID) == "N") AND (pnDWDocID > 0)
            pnDWDocID = INT(pnDWDocID)
        ELSE
            * Positive integer representing document ID must be passed in.
            MESSAGEBOX("DW Document ID (parameter 2) must be a positive integer passed to DWUpdateDocumentIndexes().", 64, "Invalid Parameter")
            RETURN .F.
        ENDIF
        IF (TYPE("ALEN(paIndexes)") <> "N")
            * Index array (passed by reference) is required.
            MESSAGEBOX("Index array (parameter 3) must be passed by reference to DWUpdateDocumentIndexes().", 64, "Invalid Parameter")
            RETURN .F.
        ENDIF
        IF (ALEN(paIndexes, 2) <> 2)
            * Index array needs to have 2 columns.
            MESSAGEBOX("Index array must have two columns for use in DWUpdateDocumentIndexes().", 64, "Invalid Parameter")
            RETURN .F.
        ENDIF
        SET PROCEDURE TO GlobalProc ADDITIVE
        LOCAL llSuccess, lcFileCabinetGUID, i, lcSDKResource, lcResponseError, lnDocumentID, loErr, lcPostData, ;
                    lcFieldName, luFieldValue, lcFieldType, lcFieldValueAsString
        llSuccess = .T.
        lcFileCabinetGUID = DWGetFileCabinetGUID(pcFileCabinetName)
        IF EMPTY(lcFileCabinetGUID)
            pcError = 'File cabinet "' + pcFileCabinetName + '" could not be found.'
            llSuccess = .F.        
        ENDIF
        lcPostData = ""
        IF llSuccess
            * Create index data as an XML string. Header information, etc. (e.g. XML namespace values) need to match what
            * would come back from a query that returns the same type of data we are sending up (in this case, document index fields).
            lcPostData = lcPostData + [<?xml version="1.0" encoding="utf-8"?>]
            lcPostData = lcPostData + [<?xml-stylesheet type="text/xsl" href="/DocuWare/Platform/Content/standard.xslt"?>]
            lcPostData = lcPostData + [<DocumentIndexFields xmlns:s="http://dev.docuware.com/schema/public/services" xmlns="http://dev.docuware.com/schema/public/services/platform">]
            FOR i = 1 TO ALEN(paIndexes, 1)
                lcFieldName = ALLTRIM(TRANSFORM(paIndexes[i, 1]))
                luFieldValue = paIndexes[i, 2]
                lcFieldType = VARTYPE(luFieldValue)
                lcFieldValueAsString = TRANSFORM(luFieldValue)
                * 10/31/2017 - JLK - Discovered that trying to use numeric tags in the XML doesn't work. No error
                * is thrown, but the index does not save. Passing numeric indexes as strings, however, works.
                * So, for all text and numeric indexes, just pass them in as strings and let DocuWare convert them.
                DO CASE
                    CASE INLIST(lcFieldType, "C", "I", "N", "B", "F")
                        lcFieldType = "String"
                        lcFieldValueAsString = EscapeXmlLiteral(TRANSFORM(luFieldValue))
                    CASE (lcFieldType = "D")
                        lcFieldType = "Date"
                        lcFieldValueAsString = ALLTRIM(STR(YEAR(luFieldValue))) + [-]
                        lcFieldValueAsString = lcFieldValueAsString + PADL(MONTH(luFieldValue), 2, "0") + [-]
                        lcFieldValueAsString = lcFieldValueAsString + PADL(DAY(luFieldValue), 2, "0")
                    CASE (lcFieldType = "T")
                        * Date/time fields need to be converted to UTC for storage in DocuWare.
                        luFieldValue = LocalToUTC(luFieldValue)
                        lcFieldType = "DateTime"
                        lcFieldValueAsString = ALLTRIM(STR(YEAR(luFieldValue))) + [-]
                        lcFieldValueAsString = lcFieldValueAsString + PADL(MONTH(luFieldValue), 2, "0") + [-]
                        lcFieldValueAsString = lcFieldValueAsString + PADL(DAY(luFieldValue), 2, "0") + [T]
                        lcFieldValueAsString = lcFieldValueAsString + PADL(HOUR(luFieldValue), 2, "0") + [:]
                        lcFieldValueAsString = lcFieldValueAsString + PADL(MINUTE(luFieldValue), 2, "0") + [:]
                        lcFieldValueAsString = lcFieldValueAsString + PADL(SEC(luFieldValue), 2, "0") + [Z]
                    OTHERWISE
                        * Unknown field type. Treat it as a string.
                        lcFieldType = "String"
                        lcFieldValueAsString = luFieldValue
                ENDCASE
                lcPostData = lcPostData + [<Field FieldName="] + lcFieldName + [">]
                lcPostData = lcPostData + [<] + lcFieldType + [>] + lcFieldValueAsString + [</] + lcFieldType + [>]
                lcPostData = lcPostData + [</Field>]
            ENDFOR
            lcPostData = lcPostData + [</DocumentIndexFields>]
        ENDIF
        IF llSuccess
            lcSDKResource = "/DocuWare/Platform/FileCabinets/" + lcFileCabinetGUID + "/Documents/" + ALLTRIM(STR(pnDWDocID)) + "/Fields" 
            TRY
                IF DWInitPlatformService(, @pcError)
                    * We are authenticated. We will continue to manipulate the request object here.
                    * Indexing requires us to POST to the SDK resource with data representing updated indexes (created above).
                    _DWPlatformService.Open("POST", DW_PROTOCOL + "://" + DW_SERVER + lcSDKResource)
                    _DWPlatformService.Send(lcPostData)
                    * Check the response to the request to see if we were successful.
                    IF ParseXML(_DWPlatformService.ResponseText, "_tempResponse")
                        * We were able to parse the response XML. Make sure the response has a document ID, and grab it.
                        * First, check for an error.
                        lcResponseError = DWGetNodeTextFromSDKResponseCursor("_tempResponse", "/Error/", "Message")
                        IF EMPTY(lcResponseError)
                            * No error found. Verify document ID in response.
                            lnDocumentID = VAL(DWGetNodeTextFromSDKResponseCursor("_tempResponse", "/DocumentIndexFields/", "Field", "FieldName", "DWDOCID"))
                            IF (lnDocumentID <> pnDWDocID)
                                pcError = "Index operation did not return an error, but correct Document ID could not be found in response."
                                llSuccess = .F.
                            ENDIF
                        ELSE
                            * Error found.
                            pcError = lcResponseError
                            llSuccess = .F.
                        ENDIF                    
                    ELSE
                        * We could not parse the response XML. Something must be wrong.
                        pcError = "Indexing response was not valid XML. An unknown error occurred. ResponseText was:" + CHR(10) + CHR(10) + _DWPlatformService.ResponseText
                        llSuccess = .F.
                    ENDIF
                    USE IN SELECT("_tempResponse")
                ELSE
                    llSuccess = .F.            
                ENDIF
            CATCH TO loErr
                SET CONSOLE OFF
                pcError = loErr.Message
                llSuccess = .F.
            ENDTRY
        ENDIF
        RETURN llSuccess
    ENDPROC
    =====================

    The basics here are that you pass in a file cabinet name which then converts to a GUID (or you can just use GUID). Then, an array of index/value pairs is assembled into XML. You can also use JSON for the POST data, but I had better luck with XML when I was first trying to get this stuff to work -- I am just not as well-versed in JSON habits.

    Once you have the resource URL created and tghe XML assembled, you post it. You can then analyze the reponse to make sure an error did not occur (the XML response should list all the indexes for the document now in effect).

    You appear to be using the correct URL for all this.

    I know the above code works, because we use it all the time.

    Good luck,

    Joe Kaufman

  • Yeah, saw the big blob... The

    Yeah, saw the big blob... The most I would ever want to do is link configurations to permissions to make sure people are always giving AppDev access to what they are creating. Otherwise folks could be going nuts with creating new things and we'd never know... Good to know where things are, and I appreciate the help!

     

    Thanks,

    Joe Kaufman

  • Phil,

    Phil,

    It is mighty confusing for all document trays other than default inboxes (and even default inboxes, actually)... Especially with sometihng like C2O configurations that target storage to a document tray -- there is literally no way to know which target is which if trays have the same name and if someone with full access (like an admin) is setting up the C2O configurations.

    What if I wanted to set up C2O configurations for every user so that they could store emails directly to their own inbox? If everyone named their inbox "Inbox", I would see just "Inbox" listed dozens of times and have no idea which was which.

    How does anyone do centralized configuration and administration of inboxes if all of them have the same name? As I mentioned before, best practices offer a way around that, but we are going to have to watch this like a hawk to make sure we don't end up with indistinguishable duplicates within our organization...

     

    Thanks,

    Joe Kaufman

  • Document Tray names and store targets

    Hey all,

    As we are trying to make sure one of our departments doesn;t start adding a lot of new cabinets and document trays without at least us being granted permissions, I found something sort of weird about document trays.

    I just created three document trays, all with the name "Test", and all pointing to the same underlying file cabinet for default storage.

    That is SUPER confusing, and I do not see how it should be allowed. There is no way to determine which cabinet is which when selecting it from the "Document trays" toolbar button, and the same goes for selecting a document tray for something like a Commect To Outlook storage target.

    All three document trays are definitely their own entities, as I was able to place different documents in each and they stay separate.

    For now, we are just doing our best to insure a "best practice" of specifically naming document trays, but why is this allowed? Anything user-facing should be unique within the DW system such that such confusion cannot be made manifest, no?

     

    Thanks,

    Joe Kaufman

     

    PS. I verified that you cannot duplicate the user-friendly name for file cabinets, something I was pretty sure of but actually had never tried... That is why I was surprised I could duplicate a document tray name.

  • Thanks, Phil -- but I was

    (Never mind: Since C2O configs have been added recently, I see them at the bottom of the table you mention, DWOrganizationSettings. Looks like I can go from there if I want to dig further. Looks like Import Configs are in there as well, though they show up as "PrinterConfiguration" in the type...interesting!)

    Thanks, Phil -- but I was hoping for something a bit more specific. I have found where document tray information is stored and am even linking it to user and role permissions, but I would need more to go on before wading through all org settings to try to find and decipher C2O settings. Can you provide any additional details?

     

    Thanks,

    Joe Kaufman

  • Tim,

    Tim,

    Thanks, you got me looking in the right place. They are not in DWFileCabinet table -- that only houses the backing file cabinet for storing documents from the tray. For the best list of document trays, as far as I can see, the DWFCSettings gets you ther, looking for "WebBasket" in the "type" column.

    From there, yes, it is the long walk through various Profile, Role, and User tables. I might make a report that shows all that stuff at some point.

    I cannot find anything about C2O in the DWUserSettings table. That table seems to only relate to stored user settings, not various C2O configurations that have been created.

    Would still love someone from DocuWare to weigh in on this (assuming Tim is not with DW based on low post count -- apologies if that is incorrect!)

     

    Thanks,

    Joe Kaufman

  • Where are C2O and Document Trays stored in database?

    Hey all,

    As we work through the visibility of various configuration options (cabinets, document trays, etc) we want to make sure one of our users who has configuration cabailities is always giving the App Dev department full access to what is being created.

    One way of coming at that is to check the underlying SQL Server database (we are on-premise) to see what has been created.

    But I still need to know where configurations get stored, assuming they are in the database. Can anyone tell me what database and table(s) include information about:

    • Import Configurations
    • Document Trays
    • Connect To Outlook Configurations

    It would ne nice to find these configurations in the underlying data to make sure we have been granted access by the creator.

     

    Thanks,

    Joe Kaufman

  • Non-.NET access of Platform SDK

    Stephane,

    First off, are you on-premise or using the Cloud version of DocuWare?

    Next, I assume by REST you mean using the Platform SDK?

    If you are talking about the Platform Services, then yes, you can manipulate DocuWare in any tool that can automate the "MSXML2.ServerXMLHTTP" request object built into Windows (at least from Windows 7 onward, I think). Not sure if Delphi has a wrapper around that or if you'll just need to use COM automation. I have it working in Visual Foxpro, and my Delphi experience tells me that it should be at least as capable.

    This was my first question on these forums about using non-.NET tools to integrate with the Platform SDK:

    https://www.docuware.com/forum/english-forums/docuware-help-technical-pr...

    I didn't get much help. And when I put in a support request, I was told we had not paid for Platform SDK support (it is a whole other layer of support, apparently), so they could not help me.

    The biggest issue was with authenticating. I could perform requests and get responses, but it always told me I was not authenticated, and authentication did not stay persistent. So I started messing around with POSTing credentials, just shots in the dark, really (but bnased on experience with using URL Integration). As you can see in the final sample further down, you need to pass in the Organization, Username, and Password credentials as part of the POST data when you run the "Logon" Platform resource. A report of my initial success can be found here:

    https://www.docuware.com/forum/english-forums/docuware-help-technical-pr...

    I have refined the SDK request object code into something more streamlined and better commented. I also use a global instance for the connection so that subsequent calls to Platform Service operations do not burn a new connection/license. Here is what it looks like, and we have been using this for several months now in Visual Foxpro:

    FUNCTION DWGetSDKRequestObject
        * This routine returns an authenticated XMLHTTP request/response object to interact with
        * DocuWare's Platform Service (SDK). It also has output parameters to house error and connected
        * user information. RETURNS: An object if successful, otherwise NULL.
        *
        * NOTES:
        *
        * -- Windows authentication only appears to work on Windows 10. It does not work on Windows 7
        *     or on Windows Server 2012 boxes such as used for Terminal Services.
        * -- While the initial logon call provides for running a GET immediately (via a "returnUrl"
        *     parameter), that also does not work reliably on Windows 7 or Server 2012. So, this routine
        *     should be called to get an authenticated request object and then subsequent GETs and POSTs
        *     need to be run from the calling program.
        * -- This routine to directly create a Platform Service connection object should rarely be used.
        *    That's because every instance of a request object (even if using the same username from the 
        *    same workstation) uses up a license when created fron non-.NET based applications. What should
        *    be used instead is the DWInitPlatformService() procedure that establishes a Platform Service
        *    connection via a global variable, _DWPlatformService.
        *
        LPARAMETERS plUseWindowsAuth, pcError, pcDBName, pcNetworkID
        * Our output variables should start out empty.
        pcError = ""
        pcDBName = ""
        pcNetworkID = ""
        LOCAL loRequest, lcLogonVerb, lcLoginURL, lcLoginData, loErr, lcResponse, lcUserInfoResource
        lcLoginData = "Organization=" + DW_ORG
        IF plUseWindowsAuth
            lcLogonVerb = "LogonNTLM"
        ELSE
            lcLogonVerb = "Logon"
            lcLoginData = lcLoginData + "&UserName=" + DW_USERNAME + "&Password=" + DW_PASSWORD
        ENDIF
        lcLoginURL = DW_SDK_ENTRY_POINT + "/Account/" + lcLogonVerb
        TRY
            loRequest = CREATEOBJECT("MSXML2.ServerXMLHTTP")
            IF plUseWindowsAuth
                loRequest.Open("POST", lcLoginURL)
                loRequest.Send(lcLoginData)
            ELSE
                loRequest.Open("POST", lcLoginURL)
                loRequest.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
                loRequest.Send(lcLoginData)
            ENDIF
            lcResponse = loRequest.ResponseText
            IF ((LEFT(lcResponse, 6) == "<html>") AND (OCCURS("YOU ARE REDIRECTED", UPPER(lcResponse)) > 0))
                * Response is HTML, not XML, and there is a string inside that makes it look like we are not connected.
                pcError = "Authentication failed. Full response was:" + CHR(10) + CHR(10) + lcResponse
                loRequest = .NULL.
            ELSE
                * It looks like we are authenticated. Now gather connected user information to be the default
                * content of the connected SDK object.
                lcUserInfoResource = "/DocuWare/Platform/Organizations/" + DW_ORG_ID + "/UserInfo"
                loRequest.Open("GET", DW_PROTOCOL + "://" + DW_SERVER + lcUserInfoResource)
                loRequest.Send()
                SET PROCEDURE TO GlobalProc ADDITIVE
                IF ParseXML(loRequest.ResponseText, "_tempDWGetSDKRequestObject")
                    pcDBName = DWGetAttrValueFromSDKResponseCursor("_tempDWGetSDKRequestObject", "/UserInfo/", "User", "DBName")
                    pcNetworkID = DWGetAttrValueFromSDKResponseCursor("_tempDWGetSDKRequestObject", "/UserInfo/", "User", "NetworkID")
                ELSE
                    pcError = "Connection made, but user information response is not valid. Response text is:" + CHR(10) + CHR(10) + loRequest.ResponseText
                    loRequest = .NULL.
                ENDIF
                USE IN SELECT("_tempDWGetSDKRequestObject")            
            ENDIF
        CATCH TO loErr
            SET CONSOLE OFF
            pcError = loErr.Message
            loRequest = .NULL.
        ENDTRY
        RETURN loRequest
    ENDFUNC

    Here is the DWInitPlatformService() routine, including some good comments on all I have learned about connecting in non-.NET environments:

    PROCEDURE DWInitPlatformService
        * A few notes about connecting to the DocuWare Platform Service (SDK):
        *
        *    -- When connecting to the DocuWare Platform Service from .NET applications using the same username
        *       on the same workstation, an existing connection/license will be used, if found.
        *    -- When connecting to the DocuWare Platform Service from NON-.NET applications using the same username
        *       on the same workstation, an NEW connection/license is created every time an SDK connection object
        *       is created. So, every Platform SDK operation (document download, indexing, etc.) uses up a license.
        *    -- The "Logoff" operation can be performed on a connection via the following code:
        *
        *            _DWPlatformService.Open("POST", DW_LOGOFF_URL)
        *            _DWPlatformService.Send()
        *
        *       The problem with this methodology is that even after successfully logging off, a license remains in
        *       use for two minutes from the time of log-off. It has been confirmed with DocuWare support that
        *       *** THIS IS THE INTENDED BEHAVIOR OF CONNECTIONS AND LICENSE USAGE ***. In other words, we are
        *       stuck with it.
        *    -- When making a standard connection to DocuWare, a license is allotted for 30 minutes from the time of login.
        *    -- Due to the two-minute period after a log-off where a license is still in use, if we do many Platform 
        *       Service operations within a two-minute span, we will still run out of licenses (even if we log off after
        *       each operation). That is why this global scheme has been developed for connecting to the DocuWare Platform Service.
        *    -- Through testing, it has been determined that an idle connection/license will log itself off after 10 minutes.
        *       Since a log-off has an additional period of two minutes built in, that means that an idle license will 
        *       expire after roughly 12 minutes.
        *    -- By calling this procedure, a global Platform Service connection (_DWPlatformService) is created that can
        *       then be used to consume Platform Service resources. The global instance will be created if it doesn't already
        *       exist or if it fails at running a simple Platform Service operation. If the existing instance works OK,
        *       then it can be used without re-creating it (which would consume an additional license).
        *    -- This global connection will release itself in approximately 12 minutes after last use.
        *    -- What we would ideally want is for a log-off operation to immediately release the connection (and license),
        *       but it does not sound like that is something that is going to happen.
        *
        * What this all means is that this initialization routine should be used to create a global Platform Service connection
        * whenever possible, as opposed to creating a new SDK request object directly with DWGetSDKRequestObject().
        *
        * *** NOTE ***: If you need to change a connection from being a Windows logon to the non-Windows login (or vice versa),
        * you will need to release the global instance (_DWPlatformService), otherwise this routine will try to use the existing
        * connection which may or may not be the logon you want to be using.

        LPARAMETERS plUseWindowsAuth, pcError, pcDBName, pcNetworkID
        LOCAL llCreate, lcUserInfoResource, lcDBName
        llCreate = .F.
        IF (VARTYPE(_DWPlatformService) == "O")
            * Global object exists, but we need to see if the connection is working.
            SET PROCEDURE TO GlobalProc ADDITIVE
            lcUserInfoResource = "/DocuWare/Platform/Organizations/" + DW_ORG_ID + "/UserInfo"
            TRY
                _DWPlatformService.Open("GET", DW_PROTOCOL + "://" + DW_SERVER + lcUserInfoResource)
                _DWPlatformService.Send()
                IF ParseXML(_DWPlatformService.ResponseText, "_tempDWInitPlatformService")
                    lcDBName = DWGetAttrValueFromSDKResponseCursor("_tempDWInitPlatformService", "/UserInfo/", "User", "DBName")
                    llCreate = EMPTY(lcDBName)
                ELSE            
                    llCreate = .T.
                ENDIF
            CATCH
                * An error occurred while trying to manipulate DW connection object. So we will need to re-create it.
                llCreate = .T.
            ENDTRY
            USE IN SELECT("_tempDWInitPlatformService")                    
        ELSE
            * Global connection object does not exist, so it will need to be created.
            llCreate = .T.
        ENDIF
        IF llCreate
            RELEASE _DWPlatformService
            PUBLIC _DWPlatformService
            _DWPlatformService = DWGetSDKRequestObject(plUseWindowsAuth, @pcError, pcDBName, pcNetworkID)
        ENDIF
        RETURN (NOT ISNULL(_DWPlatformService))
    ENDPROC

    The constants in play (capitalized stuff) are:

    #DEFINE DW_ORG                      <your org name>
    #DEFINE DW_ORG_ID                <your org ID, probably "1" if you are on-premise>
    #DEFINE DW_SERVER                <server name, either cloud or on-premise name>
    #DEFINE DW_USERNAME                <a global username to use for Platform access>
    #DEFINE DW_PASSWORD                <password for global username>
    #DEFINE DW_USERNAME_URL_INTEGRATION        <we use a different username for URL Integration>
    #DEFINE DW_PASSWORD_URL_INTEGRATION        <password for URL integration>
    #DEFINE DW_DBUSERNAME                <we also access SQL Server directly at times>
    #DEFINE DW_DBPASSWORD                <SQL Server password>
    #DEFINE DW_SYSTEMDB                "dwsystem"
    #DEFINE DW_DATADB                      "dwdata"
    #DEFINE DW_LOGGINGDB              "dwlogging"
    #DEFINE DW_PROTOCOL                "http"    <use "https" if you are using SSL>
    #DEFINE DW_INTEGRATION_ENTRY_POINT        DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform/WebClient/" + DW_ORG_ID + "/Integration"
    #DEFINE DW_INTEGRATION_ENTRY_POINT_NTLM        DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform/WebClient/NTLM/" + DW_ORG_ID + "/Integration"
    #DEFINE DW_SDK_ENTRY_POINT            DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform"
    #DEFINE DW_LOGOFF_URL                DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform/Account/Logoff"

    As you can tell from a couple of the above constants, we access the on-premise database server (MSSQL) quite a bit to gather data that either cannot be accessed by the Platform, or for quicker, easier access. I am sure Delphi can speak to SQL Server easily. We primarily do only readonly operations in this direct fashion because writing data to SQL Server without going through the Platform is not a good idea.

    This scheme also uses global variables, something Visual Foxpro is pretty "fast and loose" with in terms of instantiatiojn and release. Not sure how Delphi handles such things, so your mileage may vary on how well you can determine whether to keep using an existing connection or create one anew. Just know that licenses can get eaten up really quickly if you create a lot of instances, and they don't release themself (even when idle) for at last 12 minutes. Even if you explicitly invoke a "Logoff" resource, the connection will remain in place (and license used up) for 2 minutes.

    Between SQL Server queries and Platform SDK requests, we have all the following working in both .NET (C#) and non-.NET (Visual Foxpro) environments, currently:

    • Uploading documents, with indexes.
    • Downloading documents.
    • Changing indexes on an existing document.
    • Creating a data-only document (just the header information).
    • Querying a file cabinet, diretly (SQL Server), via the Platform SDK, and via URL Integration.
    • Manipulating "keyword" indices.

    Note that before I got stuff working from Visual Foxpro, I wrote routines in C# (.NET) and made them externally callable via an executable. That works, too, though is obviously more of a kludge. I just use command-line parameters and create files that denote success or failure (I did not know how to create a COM object in C#, or just didn't have the time to figure it out). The .NET libraries are quite nice, so if you have someone who knows C#, that is always a fine route to go to perhaps move beyond Delphi (says the guy who runs a department still coding rigorously in Visual Foxpro).

    Also be aware that manipulating date/time fields can be tricky, as all date/time values stored in as DocuWare system fields or indexes are normalized to Universal Time (UTC). So, you will want to be aware that you may need to massage timestamps to and from your local time when pulling and pushing such values. You must be especially aware of this if you are pullling date/time values directly from SQL Server, as in some (most?) cases the Platform will convert values back to local time for you. It is just something to always be aware of.

    Hope this helps, and please do not hesitate to ask more questions. I have all kinds of sample code that I struggled mightily to get working.

    Thanks,
    Joe Kaufman