• 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

     

  • Tobias,

    Tobias,

    Thanks for the response!

    1. Yes, we will just have to be careful when deciding to use versioning with regard to storage.
    2. OK.
    3. Ah, thanks for pointing out that right-click menu option. Handy.
    4. That is unfortunate. Not sure why DocuWare would want to make working with versions just that much more difficult by not persisting settings related to it. Other settings (like sorting) are preserved, so I wonder why this isn't?

    Thanks again!

     

    Joe Kaufman

  • Some questions about Versioning

    Hey all,

    I had a few quick (hopefully simple) questions about DocuWare versioning (when turned on for a file cabinet).

    1. How efficient is storage for versions? As in, if I have a large document and make one small change, are the versions stored as differential layers as opposed to the whole file being saved multiple times? In other words, is it a bad idea to turn on versioning if the file cabinet is expected to house very large files with lots of changes? EDIT: Did some research on my server and all versions are the full file at the time, both in the database and in the file storage location. So versioning can definitely eat through disk space in a hurry.
    2. Is there any sort of "diff" utility to compare versions? Not sure how that would work since most documents are binary, but if I stored, say, text documents, is there a way to "diff" the versions without downloading them and using a diff tool?
    3. Is the only visible web client change related to versioning the "Hide/Show old versions" button in the result list toolbar? Is there any other way to cycle through older versions beside displaying all versions and finding the one you want in the result list (e.g. a navigation tool I am not seeing in the viewer that can cycle through)? I see that Result Lists and Search Dialogs can include the version-related fields, so that is nice.
    4. Is there a way to make the "Hide/Show old versions" setting remain persistent? I see that every time I do a search it starts in the "hidden" position. If we wanted to always see all versions, it seems like a pain to always have to toggle that button.

    We might want to use versioning on some future DW endeavors, and I just want to have as much practical knowledge about them as I can.

     

    Thanks,

    Joe Kaufman

  • DocuWare .NET Packages (NuGet) - Backwards compatible?

    Hey all,

    I see that I can update my DocuWare packages via NuGet, as there are versions for 6.12 now.

    We are still on 6.11.

    Is it a bad idea to update these packages, or are they backwards-compatible to use with a 6.11 Platform SDK? I like to stay updated, by default, but will hold off if it is a bad idea to use .NET packages that are out of step with the Platform's version.

     

    Thanks,

    Joe Kaufman

     

  • Tom,

    Tom,

    Looks good. Yeah, casting can be tricky with regard to seeing your way from point A to point B -- I am still not used to it, especially coming from Voisual Foxpro where there is no strict typing at all.

    The foreach methodology isn't really all that onerous -- there's no other way, really, to iterate through a collection using procedural programming, whether it be an array, a list of strings, or rows in a DataTable.

    If you do need to do more interesting things with a list of strings (or any IEnumerable), you can take a look at LINQ and other functional programming techniques. In many situations (a true LINQer might even say "all"), a foreach loop can be replaced with a single line of code. That line might be long and complicated, but LINQ lets you do set-based manipulation of enumerable elements without needing to use an explicit iterator construct.

    Dictionaries are fun as well, just remember that you can't generally use the same key twice. When it comes to storing data in .NET apps, I tend to fall back on the good ol' DataTable type, another sign of my Foxpro origins where the cursor is supreme...

     

    Thanks,

    Joe Kaufman

  • Yeah, that looks about right.

    Yeah, that looks about right. Just hard to know what it all means without an example. I found the type by inspecting the variable, but that documentation would have told me straight away which type to cast into and that the "Keyword" property was a list of strings containing the results I ultimately wanted.

     

    Thanks,

    Joe Kaufman