﻿//Copyright (c) Fabasoft Austria GmbH, Linz, Austria, 2016-2024
//
//All rights reserved. All hardware and software names used are registered
//trade names and/or registered trademarks of the respective manufacturers.
//
//The user of this computer program acknowledges that the above copyright
//notice, which constitutes the Universal Copyright Convention, will be attached
//at the position in the function of the computer program which the author has
//deemed to sufficiently express the reservation of copyright. It is prohibited for
//customers, users and/or third parties to remove, modify or move this
//copyright notice.

// Optional Parameters
@verbose=false; // Don't change unless explicitly guided to by Fabasoft Support!
@allversions=false;

// Internal Parameters
@specattribs = [#objname, #objclass, #objaclobj];
@skipattribs = [#FSCPDFGEN@1.1001:pdfcontent, #FSCPDFGEN@1.1001:pdfcontentprint];
@revision=strreplace(strreplace(strreplace("$Rev: 31586 $", "Rev:"), "$"), " ");

// Prepare output file
@jsondoc = coort.CreateDictionary();
@jsondoc.format = "com.fabasoft.support.objectinfo.json"; 
@jsondoc.tool = "DumpDomainConfig [svn-" + @revision + "]";
@jsondoc.errors = [];
@jsondoc.objects = coort.CreateDictionary();

// Check if we are running inside a bulk operation
@batchmode = false;
if(COUNT(@objects) == 0) {
  try {
    @batchmode = cooobj.HasClass(#Object);
  }
  catch (@ex) { }
}

// Parse the domains
@domains = coort.SearchObjects(cootx, "SELECT COOSYSTEM@1.1:objname FROM COOSYSTEM@1.1:CurrentDomain");
@configattrs = #COOATTREDIT@1.1:PageCurrentDomainSWCConfig.COOATTREDIT@1.1:formpageitems.COOATTREDIT@1.1:pageitemitem[HasClass(#AttributeObjectDef)] - [#domaintype, #dtsuperdomaintype];
@objects = [];

for(@domain : @domains) {
  @objects *= @domain;

  for(@cfgattr : @configattrs) {
    if(@domain.HasAttributeValue(cootx, @cfgattr)) {
      @cfg = @domain.GetAttributeValue(cootx, @cfgattr);
      do {
        @objects *= @cfg;
        if(@cfg.HasClass(#COOWF@1.1:WorkflowConfiguration)) {
            @objects *= @cfg.COOWF@1.1:domainprescriptionrules;
            @objects *= @cfg.COOWF@1.1:domainwfpreferences;
            @objects *= @cfg.COOWF@1.1:domainnotificationdefs;
        } else if(@cfg.HasClass(#FSCVAPP@1.1001:DefaultConfiguration)) {
          @objects *= @cfg.FSCVAPP@1.1001:defaultdispatcher;
        } else if(@cfg.HasClass(#COOSIGNATURE@1.1:SignaturesConfig)) {
          @objects *= @cfg.COOSIGNATURE@1.1:signconfigs;
        }

        if(@cfg.HasAttributeValue(cootx, #FSCCONFIG@1.1001:cfgbasedon)) {
          @cfg = @cfg.FSCCONFIG@1.1001:cfgbasedon;
        } else {
          @cfg = null;
        }
      } while(@cfg != null && @cfg not in @objects);
    }

    @type = @domain.domaintype;
    while(@type != null) {
      @objects *= @type;
      if(@type.HasAttributeValue(cootx, @cfgattr)) {
        @cfg = @type.GetAttributeValue(cootx, @cfgattr);
        do {
          @objects *= @cfg;
          if(@cfg.HasClass(#COOWF@1.1:WorkflowConfiguration)) {
            @objects *= @cfg.COOWF@1.1:domainprescriptionrules;
            @objects *= @cfg.COOWF@1.1:domainwfpreferences;
            @objects *= @cfg.COOWF@1.1:domainnotificationdefs;
          } else if(@cfg.HasClass(#FSCVAPP@1.1001:DefaultConfiguration)) {
            @objects *= @cfg.FSCVAPP@1.1001:defaultdispatcher;
          } else if(@cfg.HasClass(#COOSIGNATURE@1.1:SignaturesConfig)) {
            @objects *= @cfg.COOSIGNATURE@1.1:signconfigs;
          }

          if(@cfg.HasAttributeValue(cootx, #FSCCONFIG@1.1001:cfgbasedon)) {
            @cfg = @cfg.FSCCONFIG@1.1001:cfgbasedon;
          } else {
            @cfg = null;
          }
        } while(@cfg != null && @cfg not in @objects);
      }
      @type = @type.dtsuperdomaintype;
    }

    if(@cfgattr.HasAttributeValue(cootx, #component)) {
      if(@cfgattr.attruseableclass != #Object) {
        @objects *= @cfgattr.component.compobjmodelobjs[HasClass(@cfgattr.attruseableclass)];
      } else {
        @objects *= @cfgattr.component.compobjmodelobjs[HasClass(#FSCCONFIG@1.1001:ClientConfigurationObject)];
      }
    }
  }
}

// Build object dumps
for(@object : @objects) {
  try {
    @versions = [coort.GetCurrentDateTime(coouser)];
    if(@allversions) {
      try {
        for(@version : @object.objversions) {
          @versions += @version.verschangedat;
        }
      }
      catch (@ex) { 
        coort.Trace("Error: Could not read all versions, only tracing current version!");
      }
    } else {
      coort.Trace("Info: Only tracing current version!");
    }

    @jsonobj = coort.CreateDictionary();
    @jsonobj.current = @object.objactversnr;
    @jsonobj.versions = coort.CreateDictionary();

    for(@versdate : @versions) {
      @jsonver = coort.CreateDictionary();;
    
      // Trace a version
      @vobject = @object.ObjectGetVersion(@versdate);

      // Build list of all object attributes
      @class = @vobject.objclass;
      @classattribs = @specattribs;
      do {
        @classattribs *= @class.classattributes;
        @class = @class.classsuperclass;
      } while(@class != null);
      @classattribs -= @skipattribs;
      
      // Add any userforms attributes if defined
      if(coort.GetObject("COO.1.1001.1.179597").HasClass(#AttributeObjectDef) && @vobject.COOTC@1.1001:objcategory.HasClass(Object)) {
        @classattribs *= @vobject.COOTC@1.1001:objcategory.GetAttribute(cootx, coort.GetObject("COO.1.1001.1.179597"));
      }

      // Trace object attributes
      for(@attr : @classattribs) {
        if(@vobject.HasAttributeValue(cootx, @attr) || @verbose) {
          @jsonname = strjoin([@attr.component.reference, "@", @attr.component.objdomain.domainmajorid, ".", @attr.component.objdomain.domainminorid, ":", @attr.reference, "/", @attr.attrtype.component.reference, "@", @attr.attrtype.component.objdomain.domainmajorid, ".", @attr.attrtype.component.objdomain.domainminorid, ":", @attr.attrtype.reference]);

          try {
            @count = @vobject.GetAttributeValueCount(cootx, @attr);
            for(@idx = 0; @idx < @count; @idx++) {
              if(@attr.HasClass(#AttributeObjectDef)) {
                @value = @vobject.GetAttributeValue(cootx, @attr, @idx);
                if(@value != null) {
                  @jsonref = coort.CreateDictionary();
                  @jsonref.SetEntry("objaddress", @value.GetAddress());

                  try {
                    @jsonref.SetEntry("objclass", strjoin([@value.objclass.component.reference, "@", @value.objclass.component.objdomain.domainmajorid, ".", @value.objclass.component.objdomain.domainminorid, ":", @value.objclass.reference]));
                  }
                  catch (@ex) {
                    @jsonref.SetEntry("objclass", @value.objclass.GetAddress());
                  }

                  if(@value.HasClass(#ComponentObject)) {
                    try {
                      if(@value.component != null && @value.reference != null) {
                        @jsonref.SetEntry("objname", strjoin([@value.component.reference, "@", @value.component.objdomain.domainmajorid, ".", @value.component.objdomain.domainminorid, ":", @value.reference]));
                      } else {
                        @jsonref.SetEntry("objname", @value.objname);
                      }
                    }
                    catch (@ex) {
                      @jsonref.SetEntry("objname", strjoin(["Unknown: ", coort.GetErrorText(@ex)]));
                    }
                  } else {
                    try {
                      @jsonref.SetEntry("objname", @value.objname);
                    }
                    catch (@ex) {
                      @jsonref.SetEntry("objname", strjoin(["Unknown: ", coort.GetErrorText(@ex)]));
                    }
                  }
                  
                  @jsonver.SetEntryValue(@jsonname, @idx, @jsonref);
                } else {
                  @jsonver.SetEntryValue(@jsonname, @idx, "<null>");
                }
              } else if(@attr.attrtype in [#CONTENT]) {
                @contval = @vobject.GetAttributeValue(cootx, @attr, @idx);
                try {
                  @jsonver.SetEntryValue(@jsonname, @idx, strjoin([@contval.GetID(), " [", @contval.GetHash(), "]"]));
                }
                catch (@ex) {
                  @jsonver.SetEntryValue(@jsonname, @idx, STRING(@contval.GetID()));
                }
              } else if(@attr.HasClass(#AttributeEnumDef)) {
                @enumval = @vobject.GetAttributeValue(cootx, @attr, @idx);
                try {
                  @jsonver.SetEntryValue(@jsonname, @idx, strjoin([@attr.attrtype.typeenumvalues[typeenumval==@enumval].typeenumref, " (", @enumval, ")"]));
                }
                catch (@ex) {
                  @jsonver.SetEntryValue(@jsonname, @idx, STRING(@enumval));
                }
              } else if(@attr.HasClass(#AttributeAggregateDef)) {
                // Trace aggregate children
                @agg = coort.CreateDictionary();
                @agg.validx = 0;
                @agg.attridx = 0;
                @agg.valcount = 0;
                @agg.value = @vobject.GetAttributeValue(cootx, @attr, @idx);
                @agg.attribs = @attr.attrtype.typecompattrs;
                @agg.pidx = @idx;
                @agg.pcount = @count;
                @agg.json = coort.CreateDictionary();
                @agg.jsonname = "";
                @aggstack = [];

                do {
                  // Trace child values
                  @aggattr = @agg.attribs[@agg.attridx];
                  if(@agg.value.HasAttributeValue(@aggattr) || @verbose) {
                    @agg.jsonname = strjoin([@aggattr.component.reference, "@", @aggattr.component.objdomain.domainmajorid, ".", @aggattr.component.objdomain.domainminorid, ":", @aggattr.reference, "/", @aggattr.attrtype.component.reference, "@", @aggattr.attrtype.component.objdomain.domainmajorid, ".", @aggattr.attrtype.component.objdomain.domainminorid, ":", @aggattr.attrtype.reference]);

                    try {
                      @agg.valcount = @agg.value.GetAttributeValueCount(@aggattr);
                      for(;@agg.validx < @agg.valcount; @agg.validx++) {
                        if(@aggattr.HasClass(#AttributeAggregateDef)) {
                          // Push current context
                          @aggstack += @agg;
                          
                          @pagg = @agg;
                          @agg = coort.CreateDictionary();
                          @agg.validx = 0;
                          @agg.attridx = 0;
                          @agg.valcount = -1;
                          @agg.value = @pagg.value.GetAttributeValue(@aggattr, @pagg.validx);
                          @agg.attribs = @aggattr.attrtype.typecompattrs;
                          @agg.pidx = @pagg.validx++;
                          @agg.pcount = @pagg.valcount;
                          @agg.json = coort.CreateDictionary();
                          @agg.jsonname = "";

                          // Interrupt the current loop
                          break;
                        } else if(@aggattr.HasClass(#AttributeObjectDef)) {
                          @value = @agg.value.GetAttributeValue(@aggattr, @agg.validx);
                          if(@value != null) {
                            @jsonref = coort.CreateDictionary();
                            @jsonref.SetEntry("objaddress", @value.GetAddress());

                            try {
                              @jsonref.SetEntry("objclass", strjoin([@value.objclass.component.reference, "@", @value.objclass.component.objdomain.domainmajorid, ".", @value.objclass.component.objdomain.domainminorid, ":", @value.objclass.reference]));
                            }
                            catch (@ex) {
                              @jsonref.SetEntry("objclass", @value.objclass.GetAddress());
                            }

                            if(@value.HasClass(#ComponentObject)) {
                              try {
                                if(@value.component != null && @value.reference != null) {
                                  @jsonref.SetEntry("objname", strjoin([@value.component.reference, "@", @value.component.objdomain.domainmajorid, ".", @value.component.objdomain.domainminorid, ":", @value.reference]));
                                } else {
                                  @jsonref.SetEntry("objname", @value.objname);
                                }
                              }
                              catch (@ex) {
                                @jsonref.SetEntry("objname", strjoin(["Unknown: ", coort.GetErrorText(@ex)]));
                              }
                            } else {
                              try {
                                @jsonref.SetEntry("objname", @value.objname);
                              }
                              catch (@ex) {
                                @jsonref.SetEntry("objname", strjoin(["Unknown: ", coort.GetErrorText(@ex)]));
                              }
                            }

                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, @jsonref);
                          } else {
                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, "<null>");
                          }
                        } else if(@aggattr.HasClass(#AttributeEnumDef)) {
                          @enumval = @agg.value.GetAttributeValue(@aggattr, @agg.validx);
                          try {
                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, strjoin([@aggattr.attrtype.typeenumvalues[typeenumval==@enumval].typeenumref, " (", @enumval, ")"]));
                          }
                          catch (@ex) {
                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, STRING(@enumval));
                          }
                        } else if(@aggattr.attrtype in [#CONTENT]) {
                          @contval = @agg.value.GetAttributeValue(@aggattr, @agg.validx);
                          try {
                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, strjoin([@contval.GetID(), " [", @contval.GetHash(), "]"]));
                          }
                          catch (@ex) {
                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, STRING(@contval.GetID()));
                          }
                        } else if(@aggattr.attrtype in [#DICTIONARY]) {
                          @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, coouser.FSCEXPEXT@1.1001:Value2JSON(@agg.value.GetAttributeValue(@aggattr, @agg.validx)));
                        } else {
                          if(COUNT(@aggattr.COOATTREDIT@1.1:attrrepresentation[COOATTREDIT@1.1:uiaction==COOATTREDIT@1.1:CTRLPassword]) > 0) {
                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, "****");
                          } else {
                            @agg.json.SetEntryValue(@agg.jsonname, @agg.validx, @agg.value.GetAttributeValue(@aggattr, @agg.validx));
                          }
                        }
                      }

                      // Reset index if we reached the end
                      if(@agg.validx == @agg.valcount) {
                        @agg.validx = 0;
                        @agg.valcount = 0;
                        @agg.attridx++;
                      }
                    }
                    catch (@ex) {
                      @jsonerror = coort.CreateDictionary();
                      @jsonerror.SetEntry("message", coort.GetErrorText(@ex));
                      @jsonerror.SetEntry("statement", coort.GetErrorStatement(@ex));
                      @jsonerror.SetEntry("line", coort.GetErrorLine(@ex));
                      @jsonerror.SetEntry("column", coort.GetErrorColumn(@ex));
                      @jsonerror.SetEntry("callstack", coort.GetErrorCallStack(@ex));

                      @jsondoc.SetEntryValue("errors", @jsondoc.GetEntryValueCount("errors"), @jsonerror);
                    }
                  } else {
                    @agg.attridx++;
                  }

                  // Pop from stack
                  if(@agg.attridx == COUNT(@agg.attribs) && COUNT(@aggstack) > 0) {
                    @pagg = @aggstack[-1];
                    @pagg.json.SetEntryValue(@pagg.jsonname, @agg.pidx, @agg.json);

                    @agg = @pagg;
                    DELETE(@aggstack, COUNT(@aggstack) - 1);
                  }
                } while(@agg.attridx < COUNT(@agg.attribs));

                @jsonver.SetEntryValue(@jsonname, @idx, @agg.json);
              } else if(@attr.attrtype in [#DICTIONARY]) {
                @jsonver.SetEntryValue(@jsonname, @idx, coouser.FSCEXPEXT@1.1001:Value2JSON(@vobject.GetAttributeValue(cootx, @attr, @idx)));
              } else {
                if(COUNT(@attr.COOATTREDIT@1.1:attrrepresentation[COOATTREDIT@1.1:uiaction==COOATTREDIT@1.1:CTRLPassword]) > 0) {
                  @jsonver.SetEntryValue(@jsonname, @idx, "****");
                } else {
                  @jsonver.SetEntryValue(@jsonname, @idx, @vobject.GetAttributeValue(cootx, @attr, @idx));
                }
              }
            }
          }
          catch (@ex) {
            @jsonerror = coort.CreateDictionary();
            @jsonerror.SetEntry("message", coort.GetErrorText(@ex));
            @jsonerror.SetEntry("statement", coort.GetErrorStatement(@ex));
            @jsonerror.SetEntry("line", coort.GetErrorLine(@ex));
            @jsonerror.SetEntry("column", coort.GetErrorColumn(@ex));
            @jsonerror.SetEntry("callstack", coort.GetErrorCallStack(@ex));

            @jsondoc.SetEntryValue("errors", @jsondoc.GetEntryValueCount("errors"), @jsonerror);
          }
        }
      }

      @jsonobj.versions.SetEntry(STRING(@vobject.objactversnr), @jsonver);
    }

    @jsondoc.objects.SetEntry(@object.GetAddress(), @jsonobj);
  }
  catch (@ex) {
    @jsonerror = coort.CreateDictionary();
    @jsonerror.SetEntry("message", coort.GetErrorText(@ex));
    @jsonerror.SetEntry("statement", coort.GetErrorStatement(@ex));
    @jsonerror.SetEntry("line", coort.GetErrorLine(@ex));
    @jsonerror.SetEntry("column", coort.GetErrorColumn(@ex));
    @jsonerror.SetEntry("callstack", coort.GetErrorCallStack(@ex));

    @jsondoc.SetEntryValue("errors", @jsondoc.GetEntryValueCount("errors"), @jsonerror);
  }
}

// Serialize to JSON
@jsoncontent = coort.CreateContent();
if(coort.GetObject("COO.1.1001.1.322609").HasClass(#Action)) {
  coouser.CallAction(cootx, "FSCEXPEXT@1.1001:Value2JSONContent", @jsondoc, &@jsoncontent);
} else {
  @jsontext = coouser.FSCEXPEXT@1.1001:Value2JSON(@jsondoc);
  @jsoncontent.SetContent(cootx, 1, 65001, @jsontext);
}

@message = "";
if(@batchmode) {
  try new transaction {
    @note = #NOTE@1.1:NoteObject.ObjectCreate();
    @note.content = { contcontent: @jsoncontent, contextension: "tro" };
    @note.objname = strhead(strjoin(["Domain configuration info dump of ", coodomain.GetAddress()]), 254);

    cooroot.ObjectLock(true, true);
    cooroot.objchildren += @note;

    cootx.Commit();

    @message = strjoin(["Domain configuration dump created and saved in '", @note.objname, "' (", @note.GetAddress(), ") at the root desktop!"]);
  }
  catch(@ex) {
    @message = strjoin(["Could not create note object on desktop, please see the file '", @jsoncontent.GetFile("object.tro", true), "' for the contents of the dump!"]);
  }
} else {
  @path = strjoin(["./", strhead(strjoin(["Domain configuration info dump of ", coodomain.GetAddress()]), 250), ".tro"]);
  @path = @jsoncontent.GetFile(@path, false);

  @message = strjoin(["Created domain configuration dump at '", @path, "', please upload this file to the service desk!"]);
}
@message;