tdaq-develop-2025-02-12
ConfigurationGUISupervisor.cc
1 #include "otsdaq-utilities/ConfigurationGUI/ConfigurationGUISupervisor.h"
2 
3 #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
4 #include "otsdaq/Macros/CoutMacros.h"
5 #include "otsdaq/MessageFacility/MessageFacility.h"
6 #include "otsdaq/TablePlugins/IterateTable.h"
7 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
8 
9 #include <boost/stacktrace.hpp>
10 
11 #include "otsdaq/GatewaySupervisor/GatewaySupervisor.h" //for saveModifiedVersionXML()
12 #include "otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.h" //for artdaq extraction
13 #include "otsdaq/TablePlugins/XDAQContextTable/XDAQContextTable.h" //for context relaunch
14 
15 #include <xdaq/NamespaceURI.h>
16 
17 #include <iostream>
18 #include <map>
19 #include <utility>
20 
21 using namespace ots;
22 
23 #undef __MF_SUBJECT__
24 #define __MF_SUBJECT__ "CfgGUI"
25 
26 #define TABLE_INFO_PATH std::string(__ENV__("TABLE_INFO_PATH")) + "/"
27 #define TABLE_INFO_EXT std::string("Info.xml")
28 
29 #define ARTDAQ_CONFIG_LAYOUTS_PATH \
30  std::string(__ENV__("SERVICE_DATA_PATH")) + "/ConfigurationGUI_artdaqLayouts/"
31 
34 xdaq::Application* ConfigurationGUISupervisor::instantiate(xdaq::ApplicationStub* stub)
35 {
36  return new ConfigurationGUISupervisor(stub);
37 }
38 
39 //==============================================================================
44  : CoreSupervisorBase(stub)
45 {
46  __SUP_COUT__ << "Constructor started." << __E__;
47 
48  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
49 
50  // make macro directories in case they don't exist
51  mkdir(((std::string)ARTDAQ_CONFIG_LAYOUTS_PATH).c_str(), 0755);
52 
53  init();
54  __SUP_COUT__ << "Constructor complete." << __E__;
55 } // end constructor()
56 
57 //==============================================================================
58 ConfigurationGUISupervisor::~ConfigurationGUISupervisor(void) { destroy(); }
59 
60 //==============================================================================
61 void ConfigurationGUISupervisor::init(void)
62 {
63  __SUP_COUT__ << "Initializing..." << __E__;
64 
65  __SUP_COUT__ << "Activating saved context, which may prepare for normal mode..."
66  << __E__;
67  try
68  {
69  testXDAQContext(); // test context group activation
70  // theRemoteWebUsers_.sendSystemMessage("tracker:10","My Subject","This is my
71  // body",false /*doEmail*/); theRemoteWebUsers_.sendSystemMessage("Ryan","My
72  // Subject Dude","This is my body",false /*doEmail*/);
73  // theRemoteWebUsers_.sendSystemMessage("*","My Rad Subject","This is my
74  // body",false /*doEmail*/);
75 
76  __SUP_COUT__ << "Done with test context." << __E__;
77  }
78  catch(...)
79  {
80  __COUT_WARN__ << "Failed test context group activation. otsdaq, in Normal mode, "
81  "will not launch when this test fails. "
82  << "Check the active context group from within Wizard Mode."
83  << __E__;
84  }
85 } // end init()
86 
87 //==============================================================================
88 void ConfigurationGUISupervisor::destroy(void)
89 {
90  __SUP_COUT__ << "Destructing..." << __E__;
91 
92  // called by destructor
93  for(std::map<std::string, ConfigurationManagerRW*>::iterator it =
94  userConfigurationManagers_.begin();
95  it != userConfigurationManagers_.end();
96  ++it)
97  {
98  delete it->second;
99  it->second = 0;
100  }
101  userConfigurationManagers_.clear();
102 
103  if(ConfigurationInterface::getInstance() != nullptr)
104  delete ConfigurationInterface::getInstance();
105 
106 } // end destroy()
107 
108 //==============================================================================
109 void ConfigurationGUISupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
110 {
111  cgicc::Cgicc cgiIn(in);
112  std::string configWindowName =
113  CgiDataUtilities::getData(cgiIn, "configWindowName"); // from GET
114  if(configWindowName == "tableEditor")
115  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
116  "src='/WebPath/html/ConfigurationTableEditor.html?urn="
117  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
118  if(configWindowName == "iterate")
119  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
120  "src='/WebPath/html/Iterate.html?urn="
121  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
122  else
123  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
124  "src='/WebPath/html/ConfigurationGUI.html?urn="
125  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
126 } // end defaultPage()
127 
128 //==============================================================================
132 {
133  CorePropertySupervisorBase::setSupervisorProperty(
134  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
135  "*=10 | deleteTreeNodeRecords=255 | saveTableInfo=255 | "
136  "deleteTableInfo=255"); // experienced users to edit, admins to delete
137  CorePropertySupervisorBase::setSupervisorProperty(
138  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
139  "*"); // all
140  //to enable read-only access to the Configuration Tree:
141  CorePropertySupervisorBase::setSupervisorProperty(
142  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes, "get*");
143 } // end setSupervisorPropertyDefaults()
144 
145 //==============================================================================
149 {
150  CorePropertySupervisorBase::addSupervisorProperty(
151  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
152  "getActiveTableGroups");
153  CorePropertySupervisorBase::setSupervisorProperty(
154  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes,
155  "!get*"); // all except read-only requests
156 } // end forceSupervisorPropertyValues()
157 
158 //==============================================================================
159 void ConfigurationGUISupervisor::request(const std::string& requestType,
160  cgicc::Cgicc& cgiIn,
161  HttpXmlDocument& xmlOut,
162  const WebUsers::RequestUserInfo& userInfo)
163 try
164 {
165  // Commands
166 
167  __COUTTV__(requestType);
168 
169  // gatewayLaunchOTS -- and other StartOTS commands
170 
171  // saveTableInfo
172  // deleteTableInfo
173  // flattenToSystemAliases
174  // versionTracking
175  // getColumnTypes
176  // getGroupAliases
177  // setGroupAliasInActiveBackbone
178  // setTableAliasInActiveBackbone
179  // setAliasOfGroupMembers
180  // getVersionAliases
181  // getTableGroups
182  // getTableGroupType
183  // getTables
184  // getContextMemberNames
185  // getBackboneMemberNames
186  // getIterateMemberNames
187  // getSpecificTableGroup
188  // saveNewTableGroup
189  // getSpecificTable
190  // saveSpecificTable
191  // clearTableTemporaryVersions
192  // clearTableCachedVersions
193  //
194  // ---- associated with JavaScript Table API
195  // getTreeView
196  // getTreeNodeCommonFields
197  // getUniqueFieldValuesForRecords
198  // getTreeNodeFieldValues
199  // setTreeNodeFieldValues
200  // addTreeNodeRecords
201  // deleteTreeNodeRecords
202  // renameTreeNodeRecords
203  // copyTreeNodeRecords
204  // getTableStructureStatusAsJSON
205  // ---- end associated with JavaScript Table API
206  //
207  // ---- associated with JavaScript artdaq API
208  // getArtdaqNodes
209  // saveArtdaqNodes
210  // getArtdaqNodeLayout
211  // saveArtdaqNodeLayout
212  // ---- end associated with JavaScript artdaq API
213  //
214  // activateTableGroup
215  // getActiveTableGroups
216  // copyViewToCurrentColumns
217  // saveTreeNodeEdit
218  // getAffectedActiveGroups
219  // getLinkToChoices
220  // getLastTableGroups
221  // getSubsytemTableGroups
222  // diffWithActiveGroup
223  // diffWithGroupKey
224  // diffTableVersions
225  // mergeGroups
226  //
227  // ---- associated with JavaScript Iterate App
228  // savePlanCommandSequence
229  // ---- end associated with JavaScript Iterate App
230 
231  // acquire user's configuration manager based on username& activeSessionIndex
232  std::string refresh = CgiDataUtilities::getData(cgiIn, "refresh"); // from GET
233 
234  // refresh to reload from info files and db (maintains temporary views!)
235  ConfigurationManagerRW* cfgMgr =
236  refreshUserSession(userInfo.username_, (refresh == "1"));
237 
238  if(requestType == "saveTableInfo")
239  {
240  std::string tableName =
241  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
242  std::string columnCSV =
243  CgiDataUtilities::postData(cgiIn, "columnCSV"); // from POST
244  std::string allowOverwrite =
245  CgiDataUtilities::getData(cgiIn, "allowOverwrite"); // from GET
246  std::string tableDescription =
247  CgiDataUtilities::postData(cgiIn, "tableDescription"); // from POST
248  std::string columnChoicesCSV =
249  CgiDataUtilities::postData(cgiIn, "columnChoicesCSV"); // from POST
250 
251  __SUP_COUT__ << "tableName: " << tableName << __E__;
252  __SUP_COUT__ << "columnCSV: " << columnCSV << __E__;
253  __SUP_COUT__ << "tableDescription: " << tableDescription << __E__;
254  __SUP_COUT__ << "columnChoicesCSV: " << columnChoicesCSV << __E__;
255  __SUP_COUT__ << "allowOverwrite: " << allowOverwrite << __E__;
256 
257  if(!allSupervisorInfo_.isWizardMode())
258  {
259  __SUP_SS__ << "Improper permissions for saving table info." << __E__;
260  xmlOut.addTextElementToData("Error", ss.str());
261  }
262  else
263  handleSaveTableInfoXML(xmlOut,
264  cfgMgr,
265  tableName,
266  columnCSV,
267  tableDescription,
268  columnChoicesCSV,
269  allowOverwrite == "1");
270  }
271  else if(requestType == "deleteTableInfo")
272  {
273  std::string tableName =
274  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
275  __SUP_COUT__ << "tableName: " << tableName << __E__;
276  handleDeleteTableInfoXML(xmlOut, cfgMgr, tableName);
277  }
278  else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz" ||
279  requestType == "flattenToSystemAliases")
280  {
281  // NOTE: similar to Supervisor version but does not keep active sessions
282  __SUP_COUT_WARN__ << requestType << " command received! " << __E__;
283  __COUT_WARN__ << requestType << " command received! " << __E__;
284 
285  // now launch
286  __SUP_COUT_INFO__ << "Launching " << requestType << "... " << __E__;
287 
288  __SUP_COUT__ << "Extracting target context hostnames... " << __E__;
289  std::vector<std::string> hostnames;
290 
291  // flattenToSystemAliases should always work in wiz mode!
292  if(requestType == "flattenToSystemAliases" &&
293  CorePropertySupervisorBase::allSupervisorInfo_.isWizardMode())
294  {
295  hostnames.push_back(__ENV__("OTS_CONFIGURATION_WIZARD_SUPERVISOR_SERVER"));
296  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
297  }
298  else
299  {
300  try
301  {
302  cfgMgr->init(); // completely reset to re-align with any changes
303 
304  const XDAQContextTable* contextTable =
305  cfgMgr->__GET_CONFIG__(XDAQContextTable);
306 
307  auto contexts = contextTable->getContexts();
308  unsigned int i, j;
309  for(const auto& context : contexts)
310  {
311  if(!context.status_)
312  continue;
313 
314  // find last slash
315  j = 0; // default to whole string
316  for(i = 0; i < context.address_.size(); ++i)
317  if(context.address_[i] == '/')
318  j = i + 1;
319  hostnames.push_back(context.address_.substr(j));
320  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
321  }
322  }
323  catch(...)
324  {
325  __SUP_SS__ << "The Configuration Manager could not be initialized to "
326  "extract contexts."
327  << __E__;
328  try
329  {
330  throw;
331  } //one more try to printout extra info
332  catch(const std::exception& e)
333  {
334  ss << "Exception message: " << e.what();
335  }
336  catch(...)
337  {
338  }
339 
340  __SUP_COUT_ERR__ << "\n" << ss.str();
341  return;
342  }
343  }
344 
345  if(hostnames.size() == 0)
346  {
347  __SUP_SS__ << "No hostnames found to launch command '" + requestType +
348  "'... Is there a valid Context group activated?"
349  << __E__;
350  __SUP_COUT_ERR__ << "\n" << ss.str();
351 
352  xmlOut.addTextElementToData("Error", ss.str());
353  }
354 
355  for(const auto& hostname : hostnames)
356  {
357  std::string fn = (std::string(__ENV__("SERVICE_DATA_PATH")) +
358  "/StartOTS_action_" + hostname + ".cmd");
359  FILE* fp = fopen(fn.c_str(), "w");
360  if(fp)
361  {
362  if(requestType == "gatewayLaunchOTS")
363  fprintf(fp, "LAUNCH_OTS");
364  else if(requestType == "gatewayLaunchWiz")
365  fprintf(fp, "LAUNCH_WIZ");
366  else if(requestType == "flattenToSystemAliases")
367  {
368  fprintf(fp, "FLATTEN_TO_SYSTEM_ALIASES");
369  fclose(fp);
370  break; // only do at one host
371  }
372 
373  fclose(fp);
374  }
375  else
376  __SUP_COUT_ERR__ << "Unable to open command file: " << fn << __E__;
377  }
378  }
379  else if(requestType == "versionTracking" || requestType == "getVersionTracking")
380  {
381  std::string type;
382  if(requestType == "getVersionTracking")
383  type = "Get";
384  else
385  type = CgiDataUtilities::getData(cgiIn, "Type"); // from GET
386  __SUP_COUT__ << "type: " << type << __E__;
387 
388  if(type == "Get")
389  xmlOut.addTextElementToData(
390  "versionTrackingStatus",
391  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
392  else if(type == "ON")
393  {
394  ConfigurationInterface::setVersionTrackingEnabled(true);
395  xmlOut.addTextElementToData(
396  "versionTrackingStatus",
397  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
398  }
399  else if(type == "OFF")
400  {
401  ConfigurationInterface::setVersionTrackingEnabled(false);
402  xmlOut.addTextElementToData(
403  "versionTrackingStatus",
404  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
405  }
406  }
407  else if(requestType == "getColumnTypes")
408  {
409  // return the possible column types and their defaults
410  std::vector<std::string> allTypes = TableViewColumnInfo::getAllTypesForGUI();
411  std::vector<std::string> allDataTypes =
412  TableViewColumnInfo::getAllDataTypesForGUI();
413  std::map<std::pair<std::string, std::string>, std::string> allDefaults =
414  TableViewColumnInfo::getAllDefaultsForGUI();
415  // TODO maybe here a new function will be needed to get allminmaxforGUI
416  for(const auto& type : allTypes)
417  xmlOut.addTextElementToData("columnTypeForGUI", type);
418  for(const auto& dataType : allDataTypes)
419  xmlOut.addTextElementToData("columnDataTypeForGUI", dataType);
420 
421  for(const auto& colDefault : allDefaults)
422  {
423  xmlOut.addTextElementToData("columnDefaultDataType", colDefault.first.first);
424  xmlOut.addTextElementToData("columnDefaultTypeFilter",
425  colDefault.first.second);
426  xmlOut.addTextElementToData("columnDefaultValue", colDefault.second);
427  }
428  // TODO add min and max responses.
429  }
430  else if(requestType == "getGroupAliases")
431  {
432  // Since this is called from setting up System View in the table GUI
433  // give option for reloading "persistent" active configurations
434  bool reloadActive =
435  1 == CgiDataUtilities::getDataAsInt(cgiIn, "reloadActiveGroups"); // from GET
436 
437  __SUP_COUT__ << "reloadActive: " << reloadActive << __E__;
438  if(reloadActive)
439  {
440  try
441  {
442  cfgMgr->clearAllCachedVersions();
443  cfgMgr->restoreActiveTableGroups(true);
444  }
445  catch(std::runtime_error& e)
446  {
447  __SUP_SS__ << ("Error loading active groups!\n\n" + std::string(e.what()))
448  << __E__;
449  __SUP_COUT_ERR__ << "\n" << ss.str();
450  xmlOut.addTextElementToData("Error", ss.str());
451  }
452  catch(...)
453  {
454  __SUP_SS__ << ("Error loading active groups!\n\n") << __E__;
455  try
456  {
457  throw;
458  } //one more try to printout extra info
459  catch(const std::exception& e)
460  {
461  ss << "Exception message: " << e.what();
462  }
463  catch(...)
464  {
465  }
466  __SUP_COUT_ERR__ << "\n" << ss.str();
467  xmlOut.addTextElementToData("Error", ss.str());
468  }
469  }
470 
471  handleGroupAliasesXML(xmlOut, cfgMgr);
472  }
473  else if(requestType == "setGroupAliasInActiveBackbone")
474  {
475  std::string groupAliasCSV =
476  CgiDataUtilities::getData(cgiIn, "groupAlias"); // from GET
477  std::string groupNameCSV =
478  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
479  std::string groupKeyCSV =
480  CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
481 
482  __SUP_COUTV__(groupAliasCSV);
483  __SUP_COUTV__(groupNameCSV);
484  __SUP_COUTV__(groupKeyCSV);
485 
486  handleSetGroupAliasInBackboneXML(
487  xmlOut, cfgMgr, groupAliasCSV, groupNameCSV, groupKeyCSV, userInfo.username_);
488  }
489  else if(requestType == "setTableAliasInActiveBackbone")
490  {
491  std::string tableAlias =
492  CgiDataUtilities::getData(cgiIn, "tableAlias"); // from GET
493  std::string tableName =
494  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
495  std::string version = CgiDataUtilities::getData(cgiIn, "version"); // from GET
496 
497  __SUP_COUT__ << "tableAlias: " << tableAlias << __E__;
498  __SUP_COUT__ << "tableName: " << tableName << __E__;
499  __SUP_COUT__ << "version: " << version << __E__;
500 
501  handleSetTableAliasInBackboneXML(xmlOut,
502  cfgMgr,
503  tableAlias,
504  tableName,
505  TableVersion(version),
506  userInfo.username_);
507  }
508  else if(requestType == "setAliasOfGroupMembers")
509  {
510  std::string versionAlias =
511  CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET
512  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
513  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
514 
515  __SUP_COUT__ << "versionAlias: " << versionAlias << __E__;
516  __SUP_COUT__ << "groupName: " << groupName << __E__;
517  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
518 
519  handleAliasGroupMembersInBackboneXML(xmlOut,
520  cfgMgr,
521  versionAlias,
522  groupName,
523  TableGroupKey(groupKey),
524  userInfo.username_);
525  }
526  else if(requestType == "getVersionAliases")
527  {
528  handleVersionAliasesXML(xmlOut, cfgMgr);
529  }
530  else if(requestType == "getTableGroups")
531  {
532  bool doNotReturnMembers =
533  CgiDataUtilities::getDataAsInt(cgiIn, "doNotReturnMembers") == 1
534  ? true
535  : false; // from GET
536 
537  __SUP_COUT__ << "doNotReturnMembers: " << doNotReturnMembers << __E__;
538  handleTableGroupsXML(xmlOut, cfgMgr, !doNotReturnMembers);
539  }
540  else if(requestType == "getTableGroupType")
541  {
542  std::string tableList =
543  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
544  __SUP_COUT__ << "tableList: " << tableList << __E__;
545 
546  handleGetTableGroupTypeXML(xmlOut, cfgMgr, tableList);
547  }
548  else if(requestType == "getTables")
549  {
550  handleTablesXML(xmlOut, cfgMgr);
551  }
552  else if(requestType == "getContextMemberNames")
553  {
554  std::set<std::string> members = cfgMgr->getContextMemberNames();
555 
556  for(auto& member : members)
557  xmlOut.addTextElementToData("ContextMember", member);
558  }
559  else if(requestType == "getBackboneMemberNames")
560  {
561  std::set<std::string> members = cfgMgr->getBackboneMemberNames();
562 
563  for(auto& member : members)
564  xmlOut.addTextElementToData("BackboneMember", member);
565  }
566  else if(requestType == "getIterateMemberNames")
567  {
568  std::set<std::string> members = cfgMgr->getIterateMemberNames();
569 
570  for(auto& member : members)
571  xmlOut.addTextElementToData("IterateMember", member);
572  }
573  else if(requestType == "getSpecificTableGroup")
574  {
575  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
576  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
577 
578  __SUP_COUT__ << "groupName: " << groupName << __E__;
579  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
580 
581  ConfigurationSupervisorBase::handleGetTableGroupXML(
582  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey));
583  }
584  else if(requestType == "saveNewTableGroup")
585  {
586  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
587  bool ignoreWarnings =
588  CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings"); // from GET
589  bool allowDuplicates =
590  CgiDataUtilities::getDataAsInt(cgiIn, "allowDuplicates"); // from GET
591  bool lookForEquivalent =
592  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
593  std::string tableList =
594  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
595  std::string comment =
596  CgiDataUtilities::getData(cgiIn, "groupComment"); // from GET
597 
598  __SUP_COUT__ << "saveNewTableGroup: " << groupName << __E__;
599  __SUP_COUT__ << "tableList: " << tableList << __E__;
600  __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__;
601  __SUP_COUT__ << "allowDuplicates: " << allowDuplicates << __E__;
602  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
603  __SUP_COUT__ << "comment: " << comment << __E__;
604 
605  ConfigurationSupervisorBase::handleCreateTableGroupXML(xmlOut,
606  cfgMgr,
607  groupName,
608  tableList,
609  allowDuplicates,
610  ignoreWarnings,
611  comment,
612  lookForEquivalent);
613  }
614  else if(requestType == "getSpecificTable")
615  {
616  std::string tableName =
617  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
618  std::string versionStr = CgiDataUtilities::getData(cgiIn, "version"); // from GET
619  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
620  int chunkSize = CgiDataUtilities::getDataAsInt(cgiIn, "chunkSize"); // from GET
621  //chunkSize is currently ignored, could use to get a few rows at a time
622 
623  std::string allowIllegalColumns =
624  CgiDataUtilities::getData(cgiIn, "allowIllegalColumns"); // from GET
625  __SUP_COUT__ << "allowIllegalColumns: " << (allowIllegalColumns == "1") << __E__;
626 
627  std::string rawData = CgiDataUtilities::getData(cgiIn, "rawData"); // from GET
628  __SUP_COUT__ << "rawData: " << (rawData == "1") << __E__;
629 
630  __SUP_COUT__ << "getSpecificTable: " << tableName << " versionStr: " << versionStr
631  << " chunkSize: " << chunkSize << " dataOffset: " << dataOffset
632  << __E__;
633 
634  TableVersion version;
635  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
636 
637  if(allTableInfo.find(tableName) != allTableInfo.end())
638  {
639  if(versionStr == "" && // take latest version if no version specified
640  allTableInfo.at(tableName).versions_.size())
641  {
642  // Start from the last element
643  auto it = allTableInfo.at(tableName).versions_.rbegin();
644  if(it->isScratchVersion()) //do not allow SCRATCH_VERSION as default selection
645  ++it; // Move to the second-to-last element
646  version = *it;
647  }
648  else if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
649  {
650  // convert alias to version
651  std::map<std::string /*table*/,
652  std::map<std::string /*alias*/, TableVersion>>
653  versionAliases = cfgMgr->getVersionAliases();
654 
655  std::string versionAlias;
656  versionAlias = versionStr.substr(
657  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
658  // if(versionAlias ==
659  // ConfigurationManager::SCRATCH_VERSION_ALIAS)
661  // {
662  // version = TableVersion::SCRATCH;
663  // __SUP_COUT__ << "version alias translated to: " << version
664  //<<
665  //__E__;
666  // }
667  // else
668  if(versionAliases.find(tableName) != versionAliases.end() &&
669  versionAliases[tableName].find(versionAlias) !=
670  versionAliases[tableName].end())
671  {
672  version = versionAliases[tableName][versionAlias];
673  __SUP_COUT__ << "version alias translated to: " << version << __E__;
674  }
675  else
676  __SUP_COUT_WARN__
677  << "version alias '"
678  << versionStr.substr(
679  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
680  << "'was not found in active version aliases!" << __E__;
681  }
682  else // else take specified version
683  version = atoi(versionStr.c_str());
684  }
685 
686  __SUP_COUT__ << "version: " << version << __E__;
687 
688  handleGetTableXML(xmlOut,
689  cfgMgr,
690  tableName,
691  TableVersion(version),
692  (allowIllegalColumns == "1"),
693  (rawData == "1"));
694  // append author column default value
695  xmlOut.addTextElementToData("DefaultRowValue", userInfo.username_);
696  }
697  else if(requestType == "saveSpecificTable")
698  {
699  std::string tableName =
700  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
701  int version = CgiDataUtilities::getDataAsInt(cgiIn, "version"); // from GET
702  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
703  bool sourceTableAsIs =
704  CgiDataUtilities::getDataAsInt(cgiIn, "sourceTableAsIs"); // from GET
705  bool lookForEquivalent =
706  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
707  int temporary = CgiDataUtilities::getDataAsInt(cgiIn, "temporary"); // from GET
708  std::string comment =
709  CgiDataUtilities::getData(cgiIn, "tableComment"); // from GET
710 
711  std::string data = CgiDataUtilities::postData(cgiIn, "data"); // from POST
712  // data format: commas and semi-colons indicate new row
713  // r0c0,r0c1,...,r0cN,;r1c0,...
714 
715  __SUP_COUT__ << "tableName: " << tableName << " version: " << version
716  << " temporary: " << temporary << " dataOffset: " << dataOffset
717  << __E__;
718  __SUP_COUT__ << "comment: " << comment << __E__;
719  __SUP_COUT__ << "data: " << data << __E__;
720  __SUP_COUT__ << "sourceTableAsIs: " << sourceTableAsIs << __E__;
721  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
722 
723  ConfigurationSupervisorBase::handleCreateTableXML(xmlOut,
724  cfgMgr,
725  tableName,
726  TableVersion(version),
727  temporary,
728  data,
729  dataOffset,
730  userInfo.username_,
731  comment,
732  sourceTableAsIs,
733  lookForEquivalent);
734  }
735  else if(requestType == "clearTableTemporaryVersions")
736  {
737  std::string tableName =
738  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
739  __SUP_COUT__ << "tableName: " << tableName << __E__;
740 
741  try
742  {
743  cfgMgr->eraseTemporaryVersion(tableName);
744  }
745  catch(std::runtime_error& e)
746  {
747  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
748  xmlOut.addTextElementToData(
749  "Error", "Error clearing temporary views!\n " + std::string(e.what()));
750  }
751  catch(...)
752  {
753  __SUP_COUT__ << "Error detected!\n\n " << __E__;
754  xmlOut.addTextElementToData("Error", "Error clearing temporary views! ");
755  }
756  }
757  else if(requestType == "clearTableCachedVersions")
758  {
759  std::string tableName =
760  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
761  __SUP_COUT__ << "tableName: " << tableName << __E__;
762 
763  try
764  {
765  if(tableName == "*")
766  cfgMgr->clearAllCachedVersions();
767  else
768  cfgMgr->clearCachedVersions(tableName);
769 
770  // Force manual reload... not cfgMgr->getAllTableInfo(true /*refresh*/);
771  }
772  catch(std::runtime_error& e)
773  {
774  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
775  xmlOut.addTextElementToData(
776  "Error", "Error clearing cached views!\n " + std::string(e.what()));
777  }
778  catch(...)
779  {
780  __SUP_COUT__ << "Error detected!\n\n " << __E__;
781  xmlOut.addTextElementToData("Error", "Error clearing cached views! ");
782  }
783  }
784  else if(requestType == "getTreeView")
785  {
786  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
787  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
788  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
789  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
790  std::string filterList = CgiDataUtilities::postData(cgiIn, "filterList");
791  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
792  bool hideStatusFalse = CgiDataUtilities::getDataAsInt(cgiIn, "hideStatusFalse");
793  std::string diffGroup = CgiDataUtilities::getData(cgiIn, "diffGroup");
794  std::string diffGroupKey = CgiDataUtilities::getData(cgiIn, "diffGroupKey");
795 
796  __SUP_COUTT__ << "tableGroup: " << tableGroup << __E__;
797  __SUP_COUTT__ << "tableGroupKey: " << tableGroupKey << __E__;
798  __SUP_COUTT__ << "startPath: " << startPath << __E__;
799  __SUP_COUTT__ << "depth: " << depth << __E__;
800  __SUP_COUTT__ << "hideStatusFalse: " << hideStatusFalse << __E__;
801  __SUP_COUTT__ << "modifiedTables: " << modifiedTables << __E__;
802  __SUP_COUTT__ << "filterList: " << filterList << __E__;
803 
804  handleFillTreeViewXML(xmlOut,
805  cfgMgr,
806  tableGroup,
807  TableGroupKey(tableGroupKey),
808  startPath,
809  depth,
810  hideStatusFalse,
811  modifiedTables,
812  filterList,
813  diffGroup,
814  TableGroupKey(diffGroupKey));
815  }
816  else if(requestType == "getTreeNodeCommonFields")
817  {
818  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
819  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
820  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
821  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
822  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
823  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
824  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
825 
826  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
827  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
828  __SUP_COUT__ << "startPath: " << startPath << __E__;
829  __SUP_COUT__ << "depth: " << depth << __E__;
830  if(depth == -1)
831  depth = 10; // protect users who probably do not actually mean -1
832  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
833  __SUP_COUT__ << "recordList: " << recordList << __E__;
834  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
835 
836  handleFillTreeNodeCommonFieldsXML(xmlOut,
837  cfgMgr,
838  tableGroup,
839  TableGroupKey(tableGroupKey),
840  startPath,
841  depth,
842  modifiedTables,
843  recordList,
844  fieldList);
845  }
846  else if(requestType == "getUniqueFieldValuesForRecords")
847  {
848  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
849  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
850  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
851  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
852  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
853  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
854 
855  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
856  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
857  __SUP_COUT__ << "startPath: " << startPath << __E__;
858  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
859  __SUP_COUT__ << "recordList: " << recordList << __E__;
860  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
861 
862  handleFillUniqueFieldValuesForRecordsXML(xmlOut,
863  cfgMgr,
864  tableGroup,
865  TableGroupKey(tableGroupKey),
866  startPath,
867  modifiedTables,
868  recordList,
869  fieldList);
870  }
871  else if(requestType == "getTreeNodeFieldValues")
872  {
873  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
874  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
875  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
876  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
877  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
878  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
879 
880  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
881  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
882  __SUP_COUT__ << "startPath: " << startPath << __E__;
883  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
884  __SUP_COUT__ << "recordList: " << recordList << __E__;
885  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
886 
887  handleFillGetTreeNodeFieldValuesXML(xmlOut,
888  cfgMgr,
889  tableGroup,
890  TableGroupKey(tableGroupKey),
891  startPath,
892  modifiedTables,
893  recordList,
894  fieldList);
895  }
896  else if(requestType == "setTreeNodeFieldValues")
897  {
898  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
899  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
900  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
901  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
902  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
903  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
904  std::string valueList = CgiDataUtilities::postData(cgiIn, "valueList");
905 
906  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
907  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
908  __SUP_COUT__ << "startPath: " << startPath << __E__;
909  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
910  __SUP_COUT__ << "valueList: " << valueList << __E__;
911  __SUP_COUT__ << "recordList: " << recordList << __E__;
912  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
913 
914  handleFillSetTreeNodeFieldValuesXML(xmlOut,
915  cfgMgr,
916  tableGroup,
917  TableGroupKey(tableGroupKey),
918  startPath,
919  modifiedTables,
920  recordList,
921  fieldList,
922  valueList,
923  userInfo.username_);
924  }
925  else if(requestType == "addTreeNodeRecords")
926  {
927  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
928  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
929  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
930  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
931  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
932 
933  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
934  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
935  __SUP_COUT__ << "startPath: " << startPath << __E__;
936  __SUP_COUT__ << "recordList: " << recordList << __E__;
937  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
938 
939  handleFillCreateTreeNodeRecordsXML(xmlOut,
940  cfgMgr,
941  tableGroup,
942  TableGroupKey(tableGroupKey),
943  startPath,
944  modifiedTables,
945  recordList,
946  userInfo.username_);
947  }
948  else if(requestType == "deleteTreeNodeRecords")
949  {
950  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
951  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
952  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
953  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
954  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
955 
956  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
957  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
958  __SUP_COUT__ << "startPath: " << startPath << __E__;
959  __SUP_COUT__ << "recordList: " << recordList << __E__;
960  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
961 
962  handleFillDeleteTreeNodeRecordsXML(xmlOut,
963  cfgMgr,
964  tableGroup,
965  TableGroupKey(tableGroupKey),
966  startPath,
967  modifiedTables,
968  recordList);
969  }
970  else if(requestType == "renameTreeNodeRecords")
971  {
972  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
973  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
974  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
975  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
976  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
977  std::string newRecordList = CgiDataUtilities::postData(cgiIn, "newRecordList");
978 
979  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
980  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
981  __SUP_COUT__ << "startPath: " << startPath << __E__;
982  __SUP_COUT__ << "recordList: " << recordList << __E__;
983  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
984  __SUP_COUTV__(newRecordList);
985 
986  handleFillRenameTreeNodeRecordsXML(xmlOut,
987  cfgMgr,
988  tableGroup,
989  TableGroupKey(tableGroupKey),
990  startPath,
991  modifiedTables,
992  recordList,
993  newRecordList);
994  }
995  else if(requestType == "copyTreeNodeRecords")
996  {
997  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
998  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
999  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
1000  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1001  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
1002  unsigned int numberOfCopies =
1003  CgiDataUtilities::getDataAsInt(cgiIn, "numberOfCopies");
1004  if(!numberOfCopies)
1005  numberOfCopies = 1; // default to 1
1006 
1007  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1008  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1009  __SUP_COUT__ << "startPath: " << startPath << __E__;
1010  __SUP_COUT__ << "recordList: " << recordList << __E__;
1011  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1012  __SUP_COUTV__(numberOfCopies);
1013 
1014  handleFillCopyTreeNodeRecordsXML(xmlOut,
1015  cfgMgr,
1016  tableGroup,
1017  TableGroupKey(tableGroupKey),
1018  startPath,
1019  modifiedTables,
1020  recordList,
1021  numberOfCopies);
1022  }
1023  else if(requestType == "getTableStructureStatusAsJSON")
1024  {
1025  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1026  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1027  std::string tableName = CgiDataUtilities::getData(cgiIn, "tableName");
1028  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1029 
1030  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1031  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1032  __SUP_COUT__ << "tableName: " << tableName << __E__;
1033  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1034 
1035  // setup active tables based on active groups and modified tables
1036  setupActiveTablesXML(xmlOut,
1037  cfgMgr,
1038  tableGroup,
1039  TableGroupKey(tableGroupKey),
1040  modifiedTables,
1041  false /* refreshAll */);
1042 
1043  try
1044  {
1045  xmlOut.addTextElementToData(
1046  "StructureStatusAsJSON",
1047  cfgMgr->getTableByName(tableName)->getStructureStatusAsJSON(cfgMgr));
1048  }
1049  catch(const std::runtime_error& e)
1050  {
1051  __SUP_SS__ << "The table plugin feature getStructureStatusAsJSON(), does not "
1052  "seem to be supported for the table '"
1053  << tableName
1054  << ".' Make sure you have the expected table plugin in your path, "
1055  "or contact system admins."
1056  << __E__;
1057  ss << "Here is the error: " << e.what() << __E__;
1058  __SUP_SS_THROW__;
1059  }
1060  }
1061  else if(requestType == "getArtdaqNodes")
1062  {
1063  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1064 
1065  __SUP_COUTV__(modifiedTables);
1066 
1067  handleGetArtdaqNodeRecordsXML(xmlOut, cfgMgr, modifiedTables);
1068  }
1069  else if(requestType == "saveArtdaqNodes")
1070  {
1071  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1072  std::string nodeString = CgiDataUtilities::postData(cgiIn, "nodeString");
1073  std::string subsystemString =
1074  CgiDataUtilities::postData(cgiIn, "subsystemString");
1075 
1076  __SUP_COUTV__(modifiedTables);
1077  __SUP_COUTV__(nodeString);
1078  __SUP_COUTV__(subsystemString);
1079 
1080  handleSaveArtdaqNodeRecordsXML(
1081  nodeString, subsystemString, xmlOut, cfgMgr, modifiedTables);
1082  }
1083  else if(requestType == "getArtdaqNodeLayout")
1084  {
1085  std::string contextGroupName =
1086  CgiDataUtilities::getData(cgiIn, "contextGroupName");
1087  std::string contextGroupKey = CgiDataUtilities::getData(cgiIn, "contextGroupKey");
1088 
1089  __SUP_COUTV__(contextGroupName);
1090  __SUP_COUTV__(contextGroupKey);
1091 
1092  handleLoadArtdaqNodeLayoutXML(
1093  xmlOut, cfgMgr, contextGroupName, TableGroupKey(contextGroupKey));
1094  }
1095  else if(requestType == "saveArtdaqNodeLayout")
1096  {
1097  std::string layout = CgiDataUtilities::postData(cgiIn, "layout");
1098  std::string contextGroupName =
1099  CgiDataUtilities::getData(cgiIn, "contextGroupName");
1100  std::string contextGroupKey = CgiDataUtilities::getData(cgiIn, "contextGroupKey");
1101 
1102  __SUP_COUTV__(layout);
1103  __SUP_COUTV__(contextGroupName);
1104  __SUP_COUTV__(contextGroupKey);
1105 
1106  handleSaveArtdaqNodeLayoutXML(
1107  xmlOut, cfgMgr, layout, contextGroupName, TableGroupKey(contextGroupKey));
1108  }
1109  else if(requestType == "getAffectedActiveGroups")
1110  {
1111  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1112  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1113  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1114  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1115  __SUP_COUT__ << "groupName: " << groupName << __E__;
1116  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
1117 
1118  handleGetAffectedGroupsXML(
1119  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey), modifiedTables);
1120  }
1121  else if(requestType == "saveTreeNodeEdit")
1122  {
1123  std::string editNodeType = CgiDataUtilities::getData(cgiIn, "editNodeType");
1124  std::string targetTable = CgiDataUtilities::getData(cgiIn, "targetTable");
1125  std::string targetTableVersion =
1126  CgiDataUtilities::getData(cgiIn, "targetTableVersion");
1127  std::string targetUID = CgiDataUtilities::getData(cgiIn, "targetUID");
1128  std::string targetColumn = CgiDataUtilities::getData(cgiIn, "targetColumn");
1129  std::string newValue = CgiDataUtilities::postData(cgiIn, "newValue");
1130 
1131  __SUP_COUT__ << "editNodeType: " << editNodeType << __E__;
1132  __SUP_COUT__ << "targetTable: " << targetTable << __E__;
1133  __SUP_COUT__ << "targetTableVersion: " << targetTableVersion << __E__;
1134  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
1135  __SUP_COUT__ << "targetColumn: " << targetColumn << __E__;
1136  __SUP_COUT__ << "newValue: " << newValue << __E__;
1137 
1138  handleSaveTreeNodeEditXML(xmlOut,
1139  cfgMgr,
1140  targetTable,
1141  TableVersion(targetTableVersion),
1142  editNodeType,
1143  StringMacros::decodeURIComponent(targetUID),
1144  StringMacros::decodeURIComponent(targetColumn),
1145  newValue,
1146  userInfo.username_);
1147  }
1148  else if(requestType == "getLinkToChoices")
1149  {
1150  std::string linkToTableName = CgiDataUtilities::getData(cgiIn, "linkToTableName");
1151  std::string linkToTableVersion =
1152  CgiDataUtilities::getData(cgiIn, "linkToTableVersion");
1153  std::string linkIdType = CgiDataUtilities::getData(cgiIn, "linkIdType");
1154  std::string linkIndex = StringMacros::decodeURIComponent(
1155  CgiDataUtilities::getData(cgiIn, "linkIndex"));
1156  std::string linkInitId = CgiDataUtilities::getData(cgiIn, "linkInitId");
1157 
1158  __SUP_COUT__ << "linkToTableName: " << linkToTableName << __E__;
1159  __SUP_COUT__ << "linkToTableVersion: " << linkToTableVersion << __E__;
1160  __SUP_COUT__ << "linkIdType: " << linkIdType << __E__;
1161  __SUP_COUT__ << "linkIndex: " << linkIndex << __E__;
1162  __SUP_COUT__ << "linkInitId: " << linkInitId << __E__;
1163 
1164  handleGetLinkToChoicesXML(xmlOut,
1165  cfgMgr,
1166  linkToTableName,
1167  TableVersion(linkToTableVersion),
1168  linkIdType,
1169  linkIndex,
1170  linkInitId);
1171  }
1172  else if(requestType == "activateTableGroup")
1173  {
1174  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1175  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1176  bool ignoreWarnings = CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings");
1177 
1178  __SUP_COUT__ << "Activating group: " << groupName << "(" << groupKey << ")"
1179  << __E__;
1180  __SUP_COUTV__(ignoreWarnings);
1181 
1182  // add flag for GUI handling
1183  xmlOut.addTextElementToData("AttemptedGroupActivation", "1");
1184  xmlOut.addTextElementToData("AttemptedGroupActivationName", groupName);
1185  xmlOut.addTextElementToData("AttemptedGroupActivationKey", groupKey);
1186 
1187  try
1188  {
1189  std::string accumulatedErrors, groupTypeString;
1190 
1191  // if ignore warnings,
1192  // then only print errors, do not add to xml
1193 
1194  cfgMgr->activateTableGroup(
1195  groupName, TableGroupKey(groupKey), &accumulatedErrors, &groupTypeString);
1196 
1197  if(accumulatedErrors != "")
1198  {
1199  if(!ignoreWarnings)
1200  {
1201  __SS__ << "Throwing exception on accumulated errors: "
1202  << accumulatedErrors << __E__;
1203  __SS_ONLY_THROW__;
1204  }
1205  // else just print
1206  __COUT_WARN__ << "Ignoring warnings so ignoring this error:"
1207  << accumulatedErrors << __E__;
1208  __COUT_WARN__ << "Done ignoring the above error(s)." << __E__;
1209  }
1210  xmlOut.addTextElementToData("AttemptedGroupActivationType", groupTypeString);
1211  }
1212  catch(std::runtime_error& e)
1213  {
1214  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1215  // single quotes around the groupName and groupKey and have them be
1216  // the first single quotes encountered in the error mesage!
1217  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1218  xmlOut.addTextElementToData(
1219  "Error",
1220  "Error activating table group '" + groupName + "(" + groupKey + ")" +
1221  ".' Please see details below:\n\n" + std::string(e.what()));
1222  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1223  << " (" << groupKey << ")" << __E__;
1224  try // just in case any lingering pieces, lets deactivate
1225  {
1226  cfgMgr->destroyTableGroup(groupName, true);
1227  }
1228  catch(...)
1229  {
1230  }
1231  }
1232  catch(cet::exception& e)
1233  {
1234  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1235  // single quotes around the groupName and groupKey and have them be
1236  // the first single quotes encountered in the error mesage!
1237 
1238  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1239  xmlOut.addTextElementToData("Error",
1240  "Error activating table group '" + groupName +
1241  "(" + groupKey + ")" + "!'\n\n" +
1242  std::string(e.what()));
1243  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1244  << " (" << groupKey << ")" << __E__;
1245  try // just in case any lingering pieces, lets deactivate
1246  {
1247  cfgMgr->destroyTableGroup(groupName, true);
1248  }
1249  catch(...)
1250  {
1251  }
1252  }
1253  catch(...)
1254  {
1255  __SUP_COUT__ << "Unknown error detected!" << __E__;
1256  try // just in case any lingering pieces, lets deactivate
1257  {
1258  cfgMgr->destroyTableGroup(groupName, true);
1259  }
1260  catch(...)
1261  {
1262  }
1263 
1264  throw; // unexpected exception!
1265  }
1266  }
1267  else if(requestType == "getActiveTableGroups")
1268  ; // do nothing, since they are always returned
1269  else if(requestType == "copyViewToCurrentColumns")
1270  {
1271  std::string tableName =
1272  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1273  std::string sourceVersion = CgiDataUtilities::getData(cgiIn, "sourceVersion");
1274 
1275  __SUP_COUT__ << "tableName: " << tableName << __E__;
1276  __SUP_COUT__ << "sourceVersion: " << sourceVersion << __E__;
1277  __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__;
1278 
1279  // copy source version to new temporary version
1280  TableVersion newTemporaryVersion;
1281  try
1282  {
1283  newTemporaryVersion =
1284  cfgMgr->copyViewToCurrentColumns(tableName, TableVersion(sourceVersion));
1285 
1286  __SUP_COUT__ << "New temporary version = " << newTemporaryVersion << __E__;
1287  }
1288  catch(std::runtime_error& e)
1289  {
1290  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1291  xmlOut.addTextElementToData("Error",
1292  "Error copying view from '" + tableName + "_v" +
1293  sourceVersion + "'! " +
1294  std::string(e.what()));
1295  }
1296  catch(...)
1297  {
1298  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1299  xmlOut.addTextElementToData(
1300  "Error",
1301  "Error copying view from '" + tableName + "_v" + sourceVersion + "'! ");
1302  }
1303 
1304  handleGetTableXML(xmlOut, cfgMgr, tableName, newTemporaryVersion);
1305  }
1306  else if(requestType == "getLastTableGroups")
1307  {
1308  // std::string timeString;
1309  std::map<std::string /* group type */,
1310  std::tuple<std::string /*group name*/,
1311  TableGroupKey,
1312  std::string /* time string*/>>
1313  theGroups;
1314 
1315  theRemoteWebUsers_.getLastTableGroups(theGroups);
1316 
1317  for(const auto& theGroup : theGroups)
1318  {
1319  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupName",
1320  std::get<0>(theGroup.second));
1321  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupKey",
1322  std::get<1>(theGroup.second).toString());
1323  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupTime",
1324  std::get<2>(theGroup.second));
1325  }
1326 
1327  // theGroup = theRemoteWebUsers_.getLastTableGroup("Configured", timeString);
1328  // xmlOut.addTextElementToData("LastConfiguredGroupName", theGroup.first);
1329  // xmlOut.addTextElementToData("LastConfiguredGroupKey", theGroup.second.toString());
1330  // xmlOut.addTextElementToData("LastConfiguredGroupTime", timeString);
1331  // theGroup = theRemoteWebUsers_.getLastTableGroup("Started", timeString);
1332  // xmlOut.addTextElementToData("LastStartedGroupName", theGroup.first);
1333  // xmlOut.addTextElementToData("LastStartedGroupKey", theGroup.second.toString());
1334  // xmlOut.addTextElementToData("LastStartedGroupTime", timeString);
1335  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedConfig", timeString);
1336  // xmlOut.addTextElementToData("LastActivatedConfigGroupName", theGroup.first);
1337  // xmlOut.addTextElementToData("LastActivatedConfigGroupKey",
1338  // theGroup.second.toString());
1339  // xmlOut.addTextElementToData("LastActivatedConfigGroupTime", timeString);
1340  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedContext", timeString);
1341  // xmlOut.addTextElementToData("LastActivatedContextGroupName", theGroup.first);
1342  // xmlOut.addTextElementToData("LastActivatedContextGroupKey",
1343  // theGroup.second.toString());
1344  // xmlOut.addTextElementToData("LastActivatedContextGroupTime", timeString);
1345  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedBackbone", timeString);
1346  // xmlOut.addTextElementToData("LastActivatedBackboneGroupName", theGroup.first);
1347  // xmlOut.addTextElementToData("LastActivatedBackboneGroupKey",
1348  // theGroup.second.toString());
1349  // xmlOut.addTextElementToData("LastActivatedBackboneGroupTime", timeString);
1350  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedIterator", timeString);
1351  // xmlOut.addTextElementToData("LastActivatedIteratorGroupName", theGroup.first);
1352  // xmlOut.addTextElementToData("LastActivatedIteratorGroupKey",
1353  // theGroup.second.toString());
1354  // xmlOut.addTextElementToData("LastActivatedIteratorGroupTime", timeString);
1355 
1356  //check other subsystems active groups
1357  handleOtherSubsystemActiveGroups(xmlOut, cfgMgr, false /* getFullList */);
1358  }
1359  else if(requestType == "getSubsytemTableGroups")
1360  {
1361  std::string subsystem =
1362  CgiDataUtilities::getData(cgiIn, "subsystem"); // from GET
1363  __SUP_COUTV__(subsystem);
1364  handleOtherSubsystemActiveGroups(
1365  xmlOut, cfgMgr, true /* getFullList */, subsystem);
1366  }
1367  else if(requestType == "diffWithActiveGroup")
1368  {
1369  std::string groupName =
1370  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
1371  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
1372  __SUP_COUTV__(groupName);
1373  __SUP_COUTV__(groupKey);
1374 
1375  handleGroupDiff(
1376  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey)); //diff with active group
1377  }
1378  else if(requestType == "diffWithGroupKey")
1379  {
1380  std::string groupName =
1381  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
1382  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
1383  std::string diffKey = CgiDataUtilities::getData(cgiIn, "diffKey"); // from GET
1384  std::string diffGroupName =
1385  CgiDataUtilities::getData(cgiIn, "diffGroupName"); // from GET
1386  __SUP_COUTV__(groupName);
1387  __SUP_COUTV__(groupKey);
1388  __SUP_COUTV__(diffKey);
1389  __SUP_COUTV__(diffGroupName);
1390 
1391  handleGroupDiff(xmlOut,
1392  cfgMgr,
1393  groupName,
1394  TableGroupKey(groupKey),
1395  TableGroupKey(diffKey),
1396  diffGroupName);
1397  }
1398  else if(requestType == "diffTableVersions")
1399  {
1400  std::string tableName =
1401  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1402  std::string vA = CgiDataUtilities::getData(cgiIn, "vA"); // from GET
1403  std::string vB = CgiDataUtilities::getData(cgiIn, "vB"); // from GET
1404  __SUP_COUTV__(tableName);
1405  __SUP_COUTV__(vA);
1406  __SUP_COUTV__(vB);
1407 
1408  TableVersion versionA, versionB;
1409  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
1410 
1411  //convert aliases if specified
1412  if(allTableInfo.find(tableName) != allTableInfo.end())
1413  {
1414  if(vA.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
1415  {
1416  // convert alias to version
1417  std::map<std::string /*table*/,
1418  std::map<std::string /*alias*/, TableVersion>>
1419  versionAliases = cfgMgr->getVersionAliases();
1420 
1421  std::string versionAlias;
1422  versionAlias =
1423  vA.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
1424 
1425  if(versionAliases.find(tableName) != versionAliases.end() &&
1426  versionAliases[tableName].find(versionAlias) !=
1427  versionAliases[tableName].end())
1428  {
1429  versionA = versionAliases[tableName][versionAlias];
1430  __SUP_COUT__ << "version alias translated to: " << versionA << __E__;
1431  }
1432  else
1433  __SUP_COUT_WARN__ << "version alias '" << versionAlias
1434  << "'was not found in active version aliases!"
1435  << __E__;
1436  }
1437  else // else take specified version
1438  versionA = atoi(vA.c_str());
1439 
1440  if(vB.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
1441  {
1442  // convert alias to version
1443  std::map<std::string /*table*/,
1444  std::map<std::string /*alias*/, TableVersion>>
1445  versionAliases = cfgMgr->getVersionAliases();
1446 
1447  std::string versionAlias;
1448  versionAlias =
1449  vB.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
1450 
1451  if(versionAliases.find(tableName) != versionAliases.end() &&
1452  versionAliases[tableName].find(versionAlias) !=
1453  versionAliases[tableName].end())
1454  {
1455  versionB = versionAliases[tableName][versionAlias];
1456  __SUP_COUT__ << "version alias translated to: " << versionB << __E__;
1457  }
1458  else
1459  __SUP_COUT_WARN__ << "version alias '" << versionAlias
1460  << "'was not found in active version aliases!"
1461  << __E__;
1462  }
1463  else // else take specified version
1464  versionB = atoi(vB.c_str());
1465  }
1466  else
1467  {
1468  versionA = atoi(vA.c_str());
1469  versionB = atoi(vB.c_str());
1470  }
1471 
1472  __SUP_COUTV__(versionA);
1473  __SUP_COUTV__(versionB);
1474 
1475  handleTableDiff(xmlOut, cfgMgr, tableName, versionA, versionB);
1476  }
1477  else if(requestType == "savePlanCommandSequence")
1478  {
1479  std::string planName = CgiDataUtilities::getData(cgiIn, "planName"); // from GET
1480  std::string commands =
1481  CgiDataUtilities::postData(cgiIn, "commands"); // from POST
1482  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1483  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1484  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1485 
1486  __SUP_COUTV__(modifiedTables);
1487  __SUP_COUTV__(planName);
1488  __SUP_COUTV__(commands);
1489  __SUP_COUTV__(groupName);
1490  __SUP_COUTV__(groupKey);
1491 
1492  handleSavePlanCommandSequenceXML(xmlOut,
1493  cfgMgr,
1494  groupName,
1495  TableGroupKey(groupKey),
1496  modifiedTables,
1497  userInfo.username_,
1498  planName,
1499  commands);
1500  }
1501  else if(requestType == "mergeGroups")
1502  {
1503  std::string groupANameContext =
1504  CgiDataUtilities::getData(cgiIn, "groupANameContext");
1505  std::string groupAKeyContext =
1506  CgiDataUtilities::getData(cgiIn, "groupAKeyContext");
1507  std::string groupBNameContext =
1508  CgiDataUtilities::getData(cgiIn, "groupBNameContext");
1509  std::string groupBKeyContext =
1510  CgiDataUtilities::getData(cgiIn, "groupBKeyContext");
1511  std::string groupANameConfig =
1512  CgiDataUtilities::getData(cgiIn, "groupANameConfig");
1513  std::string groupAKeyConfig = CgiDataUtilities::getData(cgiIn, "groupAKeyConfig");
1514  std::string groupBNameConfig =
1515  CgiDataUtilities::getData(cgiIn, "groupBNameConfig");
1516  std::string groupBKeyConfig = CgiDataUtilities::getData(cgiIn, "groupBKeyConfig");
1517  std::string mergeApproach = CgiDataUtilities::getData(cgiIn, "mergeApproach");
1518 
1519  __SUP_COUTV__(groupANameContext);
1520  __SUP_COUTV__(groupAKeyContext);
1521  __SUP_COUTV__(groupBNameContext);
1522  __SUP_COUTV__(groupBKeyContext);
1523  __SUP_COUTV__(groupANameConfig);
1524  __SUP_COUTV__(groupAKeyConfig);
1525  __SUP_COUTV__(groupBNameConfig);
1526  __SUP_COUTV__(groupBKeyConfig);
1527  __SUP_COUTV__(mergeApproach);
1528 
1529  handleMergeGroupsXML(xmlOut,
1530  cfgMgr,
1531  groupANameContext,
1532  TableGroupKey(groupAKeyContext),
1533  groupBNameContext,
1534  TableGroupKey(groupBKeyContext),
1535  groupANameConfig,
1536  TableGroupKey(groupAKeyConfig),
1537  groupBNameConfig,
1538  TableGroupKey(groupBKeyConfig),
1539  userInfo.username_,
1540  mergeApproach);
1541  }
1542  else
1543  {
1544  __SUP_SS__ << "requestType '" << requestType << "' request not recognized."
1545  << __E__;
1546  __SUP_COUT__ << "\n" << ss.str();
1547  xmlOut.addTextElementToData("Error", ss.str());
1548  }
1549 
1550  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
1551  // always add active table groups to xml response
1552  ConfigurationSupervisorBase::getConfigurationStatusXML(
1553  xmlOut, cfgMgr, userInfo.username_);
1554  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
1555 
1556 } // end ::request()
1557 catch(const std::runtime_error& e)
1558 {
1559  __SS__ << "A fatal error occurred while handling the request '" << requestType
1560  << ".' Error: " << e.what() << __E__;
1561  __COUT_ERR__ << "\n" << ss.str();
1562  xmlOut.addTextElementToData("Error", ss.str());
1563 
1564  try
1565  {
1566  // always add version tracking bool
1567  xmlOut.addTextElementToData(
1568  "versionTracking",
1569  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1570  }
1571  catch(...)
1572  {
1573  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1574  }
1575 } // end ::request() catch
1576 catch(...)
1577 {
1578  __SS__ << "An unknown fatal error occurred while handling the request '"
1579  << requestType << ".'" << __E__;
1580  try
1581  {
1582  throw;
1583  } //one more try to printout extra info
1584  catch(const std::exception& e)
1585  {
1586  ss << "Exception message: " << e.what();
1587  }
1588  catch(...)
1589  {
1590  }
1591  __COUT_ERR__ << "\n" << ss.str();
1592  xmlOut.addTextElementToData("Error", ss.str());
1593 
1594  try
1595  {
1596  // always add version tracking bool
1597  xmlOut.addTextElementToData(
1598  "versionTracking",
1599  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1600  }
1601  catch(...)
1602  {
1603  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1604  }
1605 
1606 } // end ::request() catch
1607 
1608 //==============================================================================
1616 void ConfigurationGUISupervisor::handleGetAffectedGroupsXML(
1617  HttpXmlDocument& xmlOut,
1618  ConfigurationManagerRW* cfgMgr,
1619  const std::string& rootGroupName,
1620  const TableGroupKey& rootGroupKey,
1621  const std::string& modifiedTables)
1622 try
1623 {
1624  // determine type of rootGroup
1625  // replace the matching type in considered groups
1626  // for each considered table group
1627  //
1628  // check if there is a modified table that is also a member of that group
1629  // if so,
1630  // make xml entry pair
1631 
1632  std::map<std::string, std::pair<std::string, TableGroupKey>> consideredGroups =
1633  cfgMgr->getActiveTableGroups();
1634 
1635  // check that there is a context and table group to consider
1636  // if there is not, then pull from failed list
1637  if(consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT].second.isInvalid())
1638  {
1639  __SUP_COUT__ << "Finding a context group to consider..." << __E__;
1640  if(cfgMgr->getFailedTableGroups().find(
1641  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT) !=
1642  cfgMgr->getFailedTableGroups().end())
1643  {
1644  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT] =
1645  cfgMgr->getFailedTableGroups().at(
1646  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT);
1647  }
1648  else if(cfgMgr->getFailedTableGroups().find(
1649  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN) !=
1650  cfgMgr->getFailedTableGroups().end())
1651  {
1652  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT] =
1653  cfgMgr->getFailedTableGroups().at(
1654  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN);
1655  }
1656  }
1657  if(consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION]
1658  .second.isInvalid())
1659  {
1660  __SUP_COUT__ << "Finding a table group to consider..." << __E__;
1661  if(cfgMgr->getFailedTableGroups().find(
1662  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION) !=
1663  cfgMgr->getFailedTableGroups().end())
1664  {
1665  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION] =
1666  cfgMgr->getFailedTableGroups().at(
1667  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION);
1668  }
1669  else if(cfgMgr->getFailedTableGroups().find(
1670  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN) !=
1671  cfgMgr->getFailedTableGroups().end())
1672  {
1673  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION] =
1674  cfgMgr->getFailedTableGroups().at(
1675  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN);
1676  }
1677  }
1678 
1679  __SUP_COUTV__(StringMacros::mapToString(consideredGroups));
1680 
1681  // determine the type of table group
1682  try
1683  {
1684  std::map<std::string /*name*/, TableVersion /*version*/> rootGroupMemberMap;
1685 
1686  cfgMgr->loadTableGroup(rootGroupName,
1687  rootGroupKey,
1688  0,
1689  &rootGroupMemberMap,
1690  0,
1691  0,
1692  0,
1693  0,
1694  0, // defaults
1695  true); // doNotLoadMember
1696 
1697  const std::string& groupType = cfgMgr->getTypeNameOfGroup(rootGroupMemberMap);
1698 
1699  consideredGroups[groupType] =
1700  std::pair<std::string, TableGroupKey>(rootGroupName, rootGroupKey);
1701  }
1702  catch(const std::runtime_error& e)
1703  {
1704  // if actual group name was attempted re-throw
1705  if(rootGroupName.size())
1706  {
1707  __SUP_SS__ << "Failed to determine type of table group for " << rootGroupName
1708  << "(" << rootGroupKey << ")! " << e.what() << __E__;
1709  __SUP_COUT_ERR__ << "\n" << ss.str();
1710  //__SS_THROW__;
1711  }
1712 
1713  // else assume it was the intention to just consider the active groups
1714  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1715  "name - assuming this was intentional."
1716  << __E__;
1717  }
1718  catch(...)
1719  {
1720  // if actual group name was attempted re-throw
1721  if(rootGroupName.size())
1722  {
1723  __SUP_COUT_ERR__ << "Failed to determine type of table group for "
1724  << rootGroupName << "(" << rootGroupKey << ")!" << __E__;
1725  // throw;
1726  }
1727 
1728  // else assume it was the intention to just consider the active groups
1729  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1730  "name - assuming this was intentional."
1731  << __E__;
1732  }
1733 
1734  std::map<std::string /*name*/,
1735  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>
1736  modifiedTablesMap;
1737  std::map<std::string /*name*/,
1738  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>::iterator
1739  modifiedTablesMapIt;
1740  {
1741  std::istringstream f(modifiedTables);
1742  std::string table, version;
1743  while(getline(f, table, ','))
1744  {
1745  getline(f, version, ',');
1746  modifiedTablesMap.insert(
1747  std::pair<
1748  std::string /*name*/,
1749  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>(
1750  table,
1751  std::make_pair(false /*foundAffectedGroup*/, TableVersion(version))));
1752  }
1753  __SUP_COUT__ << modifiedTables << __E__;
1754  for(auto& pair : modifiedTablesMap)
1755  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second.second
1756  << __E__;
1757  }
1758 
1759  bool affected;
1760  xercesc::DOMElement* parentEl;
1761  std::string groupComment;
1762  std::vector<std::string> orderedGroupTypes(
1763  {ConfigurationManager::GROUP_TYPE_NAME_CONTEXT,
1764  ConfigurationManager::GROUP_TYPE_NAME_BACKBONE,
1765  ConfigurationManager::GROUP_TYPE_NAME_ITERATE,
1766  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION});
1767  for(auto groupType : orderedGroupTypes)
1768  {
1769  if(consideredGroups.find(groupType) == consideredGroups.end())
1770  continue; // skip missing
1771 
1772  const std::pair<std::string, TableGroupKey>& group = consideredGroups[groupType];
1773 
1774  if(group.second.isInvalid())
1775  continue; // skip invalid
1776 
1777  __SUP_COUT__ << "Considering " << groupType << " group " << group.first << " ("
1778  << group.second << ")" << __E__;
1779 
1780  affected = false;
1781 
1782  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
1783  cfgMgr->loadTableGroup(group.first,
1784  group.second,
1785  0,
1786  &memberMap,
1787  0,
1788  0,
1789  &groupComment,
1790  0,
1791  0, // mostly defaults
1792  true /*doNotLoadMember*/);
1793 
1794  __SUP_COUT__ << "groupComment = " << groupComment << __E__;
1795 
1796  for(auto& table : memberMap)
1797  {
1798  if((modifiedTablesMapIt = modifiedTablesMap.find(table.first)) !=
1799  modifiedTablesMap
1800  .end() && // check if version is different for member table
1801  table.second != (*modifiedTablesMapIt).second.second)
1802  {
1803  __SUP_COUT__ << "Affected by " << (*modifiedTablesMapIt).first << ":"
1804  << (*modifiedTablesMapIt).second.second << __E__;
1805  affected = true;
1806  memberMap[table.first] = (*modifiedTablesMapIt).second.second;
1807  (*modifiedTablesMapIt).second.first = true; // found affected group
1808  }
1809  }
1810 
1811  if(groupType == ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION)
1812  {
1813  __SUP_COUT__ << "Considering mockup tables for Configuration Group..."
1814  << __E__;
1815  for(auto& table : modifiedTablesMap)
1816  {
1817  if(table.second.first) // already found affected group
1818  continue;
1819 
1820  if(table.second.second.isMockupVersion() &&
1821  memberMap.find(table.first) == memberMap.end())
1822  {
1823  __SUP_COUT__ << "Found mockup table '" << table.first
1824  << "' for Configuration Group." << __E__;
1825  memberMap[table.first] = table.second.second;
1826  affected = true;
1827  }
1828  }
1829  }
1830 
1831  if(affected)
1832  {
1833  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1834  xmlOut.addTextElementToParent("GroupName", group.first, parentEl);
1835  xmlOut.addTextElementToParent("GroupKey", group.second.toString(), parentEl);
1836  xmlOut.addTextElementToParent("GroupComment", groupComment, parentEl);
1837 
1838  for(auto& table : memberMap)
1839  {
1840  xmlOut.addTextElementToParent("MemberName", table.first, parentEl);
1841  xmlOut.addTextElementToParent(
1842  "MemberVersion", table.second.toString(), parentEl);
1843  }
1844  }
1845  } // end affected group loop
1846 }
1847 catch(std::runtime_error& e)
1848 {
1849  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1850  xmlOut.addTextElementToData(
1851  "Error", "Error getting affected groups! " + std::string(e.what()));
1852 }
1853 catch(...)
1854 {
1855  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1856  xmlOut.addTextElementToData("Error", "Error getting affected groups! ");
1857 }
1858 
1859 //==============================================================================
1867 void ConfigurationGUISupervisor::setupActiveTablesXML(
1868  HttpXmlDocument& xmlOut,
1869  ConfigurationManagerRW* cfgMgr,
1870  const std::string& groupName,
1871  const TableGroupKey& groupKey,
1872  const std::string& modifiedTables,
1873  bool refreshAll,
1874  bool doGetGroupInfo,
1875  std::map<std::string /*name*/, TableVersion /*version*/>* returnMemberMap,
1876  bool outputActiveTables,
1877  std::string* accumulatedErrors)
1878 try
1879 {
1880  xmlOut.addTextElementToData("tableGroup", groupName);
1881  xmlOut.addTextElementToData("tableGroupKey", groupKey.toString());
1882 
1883  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
1884 
1885  // reload all tables so that partially loaded tables are not allowed
1886  if( //usingActiveGroups ||
1887  refreshAll)
1888  {
1889  __SUP_COUT__ << "Refreshing all table info, ignoring warnings..." << __E__;
1890  std::string accumulatedWarnings = "";
1891  cfgMgr->getAllTableInfo(true /* refresh */,
1892  &accumulatedWarnings,
1893  "" /* errorFilterName */,
1894  false /* getGroupKeys */,
1895  false /* getGroupInfo */,
1896  true /* initializeActiveGroups */);
1897  }
1898 
1899  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
1900 
1901  std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap;
1902  std::map<std::string /*name*/, TableVersion /*version*/>::iterator
1903  modifiedTablesMapIt;
1904 
1905  if(usingActiveGroups)
1906  {
1907  // no need to load a target group
1908  __SUP_COUT__ << "Using active groups." << __E__;
1909  }
1910  else
1911  {
1912  __SUP_COUT__ << "Loading group '" << groupName << "(" << groupKey << ")'"
1913  << __E__;
1914 
1915  std::string groupComment, groupAuthor, tableGroupCreationTime, groupType;
1916 
1917  // only same member map if object pointer was passed
1918  cfgMgr->loadTableGroup(groupName,
1919  groupKey,
1920  false /*doActivate*/,
1921  returnMemberMap,
1922  0 /*progressBar*/,
1923  accumulatedErrors,
1924  doGetGroupInfo ? &groupComment : 0,
1925  doGetGroupInfo ? &groupAuthor : 0,
1926  doGetGroupInfo ? &tableGroupCreationTime : 0,
1927  false /*doNotLoadMembers*/,
1928  doGetGroupInfo ? &groupType : 0);
1929 
1930  if(doGetGroupInfo)
1931  {
1932  xmlOut.addTextElementToData("tableGroupComment", groupComment);
1933  xmlOut.addTextElementToData("tableGroupAuthor", groupAuthor);
1934  xmlOut.addTextElementToData("tableGroupCreationTime", tableGroupCreationTime);
1935  xmlOut.addTextElementToData("tableGroupType", groupType);
1936  }
1937 
1938  if(accumulatedErrors && *accumulatedErrors != "")
1939  __SUP_COUTV__(*accumulatedErrors);
1940  }
1941 
1942  // extract modified tables
1943  {
1944  std::istringstream f(modifiedTables);
1945  std::string table, version;
1946  while(getline(f, table, ','))
1947  {
1948  getline(f, version, ',');
1949  modifiedTablesMap.insert(
1950  std::pair<std::string /*name*/, TableVersion /*version*/>(
1951  table, TableVersion(version)));
1952  }
1953  //__SUP_COUT__ << modifiedTables << __E__;
1954  for(auto& pair : modifiedTablesMap)
1955  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second
1956  << __E__;
1957  }
1958 
1959  // add all active table pairs to xmlOut
1960  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
1961  xmlOut.addTextElementToData("DefaultNoLink",
1962  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
1963 
1964  // construct specially ordered table name set
1965  std::set<std::string, StringMacros::IgnoreCaseCompareStruct> orderedTableSet;
1966  for(const auto& tablePair : allActivePairs)
1967  orderedTableSet.emplace(tablePair.first);
1968 
1969  std::map<std::string, TableInfo>::const_iterator tableInfoIt;
1970  for(auto& orderedTableName : orderedTableSet)
1971  {
1972  tableInfoIt = allTableInfo.find(orderedTableName);
1973  if(tableInfoIt == allTableInfo.end())
1974  {
1975  __SS__ << "Impossible missing table in map '" << orderedTableName << "'"
1976  << __E__;
1977  __SS_THROW__;
1978  }
1979 
1980  if(outputActiveTables)
1981  xmlOut.addTextElementToData("ActiveTableName", orderedTableName);
1982 
1983  // check if name is in modifiedTables
1984  // if so, activate the temporary version
1985  if((modifiedTablesMapIt = modifiedTablesMap.find(orderedTableName)) !=
1986  modifiedTablesMap.end())
1987  {
1988  __SUP_COUT__ << "Found modified table " << (*modifiedTablesMapIt).first
1989  << ": trying... " << (*modifiedTablesMapIt).second << __E__;
1990 
1991  try
1992  {
1993  tableInfoIt->second.tablePtr_->setActiveView(
1994  (*modifiedTablesMapIt).second);
1995  }
1996  catch(...)
1997  {
1998  __SUP_SS__ << "Modified table version v" << (*modifiedTablesMapIt).second
1999  << " failed. Reverting to v"
2000  << tableInfoIt->second.tablePtr_->getView().getVersion() << "."
2001  << __E__;
2002  __SUP_COUT_WARN__ << "Warning detected!\n\n " << ss.str() << __E__;
2003  xmlOut.addTextElementToData(
2004  "Warning",
2005  "Error setting up active tables!\n\n" + std::string(ss.str()));
2006  }
2007  }
2008 
2009  if(outputActiveTables)
2010  {
2011  xmlOut.addTextElementToData(
2012  "ActiveTableVersion",
2013  tableInfoIt->second.tablePtr_->getView().getVersion().toString());
2014  xmlOut.addTextElementToData(
2015  "ActiveTableComment",
2016  tableInfoIt->second.tablePtr_->getView().getAuthor() + ": " +
2017  tableInfoIt->second.tablePtr_->getView().getComment());
2018  }
2019 
2020  } // end ordered table loop
2021 } // end setupActiveTablesXML()
2022 catch(std::runtime_error& e)
2023 {
2024  __SUP_SS__ << ("Error setting up active tables!\n\n" + std::string(e.what()))
2025  << __E__;
2026  __SUP_COUT_ERR__ << "\n" << ss.str();
2027  xmlOut.addTextElementToData("Error", ss.str());
2028  throw; // throw to get info from special errors at a parent level
2029 }
2030 catch(...)
2031 {
2032  __SUP_SS__ << ("Error setting up active tables!\n\n") << __E__;
2033  try
2034  {
2035  throw;
2036  } //one more try to printout extra info
2037  catch(const std::exception& e)
2038  {
2039  ss << "Exception message: " << e.what();
2040  }
2041  catch(...)
2042  {
2043  }
2044  __SUP_COUT_ERR__ << "\n" << ss.str();
2045  xmlOut.addTextElementToData("Error", ss.str());
2046  throw; // throw to get info from special errors at a parent level
2047 } // end setupActiveTablesXML() throw
2048 
2049 //==============================================================================
2064 void ConfigurationGUISupervisor::handleFillCreateTreeNodeRecordsXML(
2065  HttpXmlDocument& xmlOut,
2066  ConfigurationManagerRW* cfgMgr,
2067  const std::string& groupName,
2068  const TableGroupKey& groupKey,
2069  const std::string& startPath,
2070  const std::string& modifiedTables,
2071  const std::string& recordList,
2072  const std::string& author)
2073 {
2074  // setup active tables based on input group and modified tables
2075  setupActiveTablesXML(xmlOut,
2076  cfgMgr,
2077  groupName,
2078  groupKey,
2079  modifiedTables,
2080  true /* refresh all */,
2081  false /* getGroupInfo */,
2082  0 /* returnMemberMap */,
2083  false /* outputActiveTables */);
2084 
2085  try
2086  {
2087  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2088  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2089 
2090  __SUP_COUT__ << table->getTableName() << __E__;
2091  TableVersion temporaryVersion;
2092 
2093  // if current version is not temporary
2094  // create temporary
2095  // else re-modify temporary version
2096  // edit temporary version directly
2097  // then after all edits return active versions
2098  //
2099 
2100  bool firstSave = true;
2101 
2102  // save current version
2103  TableView backupView(targetNode.getTableName());
2104 
2105  // extract record list
2106  {
2107  std::istringstream f(recordList);
2108  std::string recordUID;
2109 
2110  while(getline(f, recordUID, ',')) // for each record
2111  {
2112  recordUID = StringMacros::decodeURIComponent(recordUID);
2113 
2114  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2115 
2116  if(firstSave) // handle version bookkeeping
2117  {
2118  if(!(temporaryVersion = targetNode.getTableVersion())
2119  .isTemporaryVersion())
2120  {
2121  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2122  // create temporary version for editing
2123  temporaryVersion = table->createTemporaryView(temporaryVersion);
2124  cfgMgr->saveNewTable(targetNode.getTableName(),
2125  temporaryVersion,
2126  true); // proper bookkeeping for temporary version with the new version
2127 
2128  __SUP_COUT__ << "Created temporary version " << temporaryVersion
2129  << __E__;
2130  }
2131  else // else table is already temporary version
2132  __SUP_COUT__ << "Using temporary version " << temporaryVersion
2133  << __E__;
2134 
2135  firstSave = false;
2136 
2137  // copy original to backup before modifying
2138  backupView.copy(table->getView(), temporaryVersion, author);
2139  }
2140 
2141  // at this point have valid temporary version to edit
2142 
2143  // copy "table-newRow" type edit from handleSaveTreeNodeEditXML()
2144  // functionality
2145 
2146  // add row
2147  unsigned int row = table->getViewP()->addRow(
2148  author, true /*incrementUniqueData*/); // increment all unique data fields to void conflict
2149 
2150  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
2151  try
2152  {
2153  unsigned int col = table->getViewP()->getColStatus();
2154  table->getViewP()->setURIEncodedValue("1", row, col);
2155  }
2156  catch(...)
2157  {
2158  } // if not, ignore
2159 
2160  // set UID value
2161  table->getViewP()->setURIEncodedValue(
2162  recordUID, row, table->getViewP()->getColUID());
2163  }
2164  }
2165 
2166  if(!firstSave) // only test table if there was a change
2167  {
2168  try
2169  {
2170  table->getViewP()->init(); // verify new table (throws runtime_errors)
2171  }
2172  catch(...)
2173  {
2174  __SUP_COUT_INFO__ << "Reverting to original view." << __E__;
2175  __SUP_COUT__ << "Before:" << __E__;
2176  table->getViewP()->print();
2177  table->getViewP()->copy(backupView, temporaryVersion, author);
2178  __SUP_COUT__ << "After:" << __E__;
2179  table->getViewP()->print();
2180 
2181  throw; // rethrow
2182  }
2183  }
2184 
2185  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2186  }
2187  catch(std::runtime_error& e)
2188  {
2189  __SUP_SS__ << ("Error creating new record(s)!\n\n" + std::string(e.what()))
2190  << __E__;
2191  __SUP_COUT_ERR__ << "\n" << ss.str();
2192  xmlOut.addTextElementToData("Error", ss.str());
2193  }
2194  catch(...)
2195  {
2196  __SUP_SS__ << ("Error creating new record(s)!\n\n") << __E__;
2197  try
2198  {
2199  throw;
2200  } //one more try to printout extra info
2201  catch(const std::exception& e)
2202  {
2203  ss << "Exception message: " << e.what();
2204  }
2205  catch(...)
2206  {
2207  }
2208  __SUP_COUT_ERR__ << "\n" << ss.str();
2209  xmlOut.addTextElementToData("Error", ss.str());
2210  }
2211 } //end handleFillCreateTreeNodeRecordsXML()
2212 
2213 //==============================================================================
2216 void ConfigurationGUISupervisor::handleFillModifiedTablesXML(
2217  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr)
2218 try
2219 {
2220  // return modified <modified tables>
2221  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
2222  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
2223  for(auto& activePair : allActivePairs)
2224  {
2225  xmlOut.addTextElementToData("NewActiveTableName", activePair.first);
2226  xmlOut.addTextElementToData("NewActiveTableVersion",
2227  allTableInfo.at(activePair.first)
2228  .tablePtr_->getView()
2229  .getVersion()
2230  .toString());
2231  xmlOut.addTextElementToData(
2232  "NewActiveTableComment",
2233  allTableInfo.at(activePair.first).tablePtr_->getView().getAuthor() + ": " +
2234  allTableInfo.at(activePair.first).tablePtr_->getView().getComment());
2235  }
2236 } //end handleFillModifiedTablesXML()
2237 catch(std::runtime_error& e)
2238 {
2239  __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
2240  __SUP_COUT_ERR__ << "\n" << ss.str();
2241  xmlOut.addTextElementToData("Error", ss.str());
2242 }
2243 catch(...)
2244 {
2245  __SUP_SS__ << ("Error!\n\n") << __E__;
2246  try
2247  {
2248  throw;
2249  } //one more try to printout extra info
2250  catch(const std::exception& e)
2251  {
2252  ss << "Exception message: " << e.what();
2253  }
2254  catch(...)
2255  {
2256  }
2257  __SUP_COUT_ERR__ << "\n" << ss.str();
2258  xmlOut.addTextElementToData("Error", ss.str());
2259 } //end handleFillModifiedTablesXML() catch
2260 
2261 //==============================================================================
2276 void ConfigurationGUISupervisor::handleFillDeleteTreeNodeRecordsXML(
2277  HttpXmlDocument& xmlOut,
2278  ConfigurationManagerRW* cfgMgr,
2279  const std::string& groupName,
2280  const TableGroupKey& groupKey,
2281  const std::string& startPath,
2282  const std::string& modifiedTables,
2283  const std::string& recordList)
2284 {
2285  // setup active tables based on input group and modified tables
2286  setupActiveTablesXML(xmlOut,
2287  cfgMgr,
2288  groupName,
2289  groupKey,
2290  modifiedTables,
2291  true /* refresh all */,
2292  false /* getGroupInfo */,
2293  0 /* returnMemberMap */,
2294  false /* outputActiveTables */);
2295 
2296  try
2297  {
2298  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2299  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2300 
2301  __SUP_COUT__ << table->getTableName() << __E__;
2302  TableVersion temporaryVersion;
2303 
2304  // if current version is not temporary
2305  // create temporary
2306  // else re-modify temporary version
2307  // edit temporary version directly
2308  // then after all edits return active versions
2309  //
2310 
2311  bool firstSave = true;
2312 
2313  // extract record list
2314  {
2315  std::istringstream f(recordList);
2316  std::string recordUID;
2317  // unsigned int i;
2318 
2319  while(getline(f, recordUID, ',')) // for each record
2320  {
2321  recordUID = StringMacros::decodeURIComponent(recordUID);
2322 
2323  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2324 
2325  if(firstSave) // handle version bookkeeping
2326  {
2327  if(!(temporaryVersion = targetNode.getTableVersion())
2328  .isTemporaryVersion())
2329  {
2330  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2331  // create temporary version for editing
2332  temporaryVersion = table->createTemporaryView(temporaryVersion);
2333  cfgMgr->saveNewTable(targetNode.getTableName(),
2334  temporaryVersion,
2335  true); // proper bookkeeping for temporary version with the new version
2336 
2337  __SUP_COUT__ << "Created temporary version " << temporaryVersion
2338  << __E__;
2339  }
2340  else // else table is already temporary version
2341  __SUP_COUT__ << "Using temporary version " << temporaryVersion
2342  << __E__;
2343 
2344  firstSave = false;
2345  }
2346 
2347  // at this point have valid temporary version to edit
2348 
2349  // copy "delete-uid" type edit from handleSaveTreeNodeEditXML()
2350  // functionality
2351  unsigned int row =
2352  table->getViewP()->findRow(table->getViewP()->getColUID(), recordUID);
2353  table->getViewP()->deleteRow(row);
2354  }
2355  }
2356 
2357  if(!firstSave) // only test table if there was a change
2358  table->getViewP()->init(); // verify new table (throws runtime_errors)
2359 
2360  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2361  }
2362  catch(std::runtime_error& e)
2363  {
2364  __SUP_SS__ << ("Error removing record(s)!\n\n" + std::string(e.what())) << __E__;
2365  __SUP_COUT_ERR__ << "\n" << ss.str();
2366  xmlOut.addTextElementToData("Error", ss.str());
2367  }
2368  catch(...)
2369  {
2370  __SUP_SS__ << ("Error removing record(s)!\n\n") << __E__;
2371  try
2372  {
2373  throw;
2374  } //one more try to printout extra info
2375  catch(const std::exception& e)
2376  {
2377  ss << "Exception message: " << e.what();
2378  }
2379  catch(...)
2380  {
2381  }
2382  __SUP_COUT_ERR__ << "\n" << ss.str();
2383  xmlOut.addTextElementToData("Error", ss.str());
2384  }
2385 } // end handleFillDeleteTreeNodeRecordsXML()
2386 
2387 //==============================================================================
2403 void ConfigurationGUISupervisor::handleFillRenameTreeNodeRecordsXML(
2404  HttpXmlDocument& xmlOut,
2405  ConfigurationManagerRW* cfgMgr,
2406  const std::string& groupName,
2407  const TableGroupKey& groupKey,
2408  const std::string& startPath,
2409  const std::string& modifiedTables,
2410  const std::string& recordList,
2411  const std::string& newRecordList)
2412 {
2413  // setup active tables based on input group and modified tables
2414  setupActiveTablesXML(xmlOut,
2415  cfgMgr,
2416  groupName,
2417  groupKey,
2418  modifiedTables,
2419  true /* refresh all */,
2420  false /* getGroupInfo */,
2421  0 /* returnMemberMap */,
2422  false /* outputActiveTables */);
2423 
2424  try
2425  {
2426  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2427  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2428 
2429  __SUP_COUT__ << table->getTableName() << __E__;
2430  TableVersion temporaryVersion;
2431 
2432  // if current version is not temporary
2433  // create temporary
2434  // else re-modify temporary version
2435  // edit temporary version directly
2436  // then after all edits return active versions
2437  //
2438 
2439  // extract record list
2440  std::vector<std::string> recordArray =
2441  StringMacros::getVectorFromString(recordList);
2442  std::vector<std::string> newRecordArray =
2443  StringMacros::getVectorFromString(newRecordList);
2444 
2445  __SUP_COUTV__(StringMacros::vectorToString(recordArray));
2446  __SUP_COUTV__(StringMacros::vectorToString(newRecordArray));
2447 
2448  if(recordArray.size() == 0 || recordArray.size() != newRecordArray.size())
2449  {
2450  __SUP_SS__
2451  << "Invalid record size vs new record name size, they must be the same: "
2452  << recordArray.size() << " vs " << newRecordArray.size() << __E__;
2453  __SUP_SS_THROW__;
2454  }
2455 
2456  // handle version bookkeeping
2457  {
2458  if(!(temporaryVersion = targetNode.getTableVersion()).isTemporaryVersion())
2459  {
2460  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2461  // create temporary version for editing
2462  temporaryVersion = table->createTemporaryView(temporaryVersion);
2463  cfgMgr->saveNewTable(targetNode.getTableName(),
2464  temporaryVersion,
2465  true); // proper bookkeeping for temporary version with the new version
2466 
2467  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
2468  }
2469  else // else table is already temporary version
2470  __SUP_COUT__ << "Using temporary version " << temporaryVersion << __E__;
2471  }
2472 
2473  // at this point have valid temporary version to edit
2474 
2475  // for every record, change name
2476  unsigned int row;
2477  for(unsigned int i = 0; i < recordArray.size(); ++i)
2478  {
2479  row = table->getViewP()->findRow(
2480  table->getViewP()->getColUID(),
2481  StringMacros::decodeURIComponent(recordArray[i]));
2482 
2483  table->getViewP()->setValueAsString(
2484  newRecordArray[i], row, table->getViewP()->getColUID());
2485  }
2486 
2487  table->getViewP()->init(); // verify new table (throws runtime_errors)
2488 
2489  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2490  }
2491  catch(std::runtime_error& e)
2492  {
2493  __SUP_SS__ << ("Error renaming record(s)!\n\n" + std::string(e.what())) << __E__;
2494  __SUP_COUT_ERR__ << "\n" << ss.str();
2495  xmlOut.addTextElementToData("Error", ss.str());
2496  }
2497  catch(...)
2498  {
2499  __SUP_SS__ << ("Error renaming record(s)!\n\n") << __E__;
2500  try
2501  {
2502  throw;
2503  } //one more try to printout extra info
2504  catch(const std::exception& e)
2505  {
2506  ss << "Exception message: " << e.what();
2507  }
2508  catch(...)
2509  {
2510  }
2511  __SUP_COUT_ERR__ << "\n" << ss.str();
2512  xmlOut.addTextElementToData("Error", ss.str());
2513  }
2514 } // end handleFillRenameTreeNodeRecordsXML()
2515 
2516 //==============================================================================
2533 void ConfigurationGUISupervisor::handleFillCopyTreeNodeRecordsXML(
2534  HttpXmlDocument& xmlOut,
2535  ConfigurationManagerRW* cfgMgr,
2536  const std::string& groupName,
2537  const TableGroupKey& groupKey,
2538  const std::string& startPath,
2539  const std::string& modifiedTables,
2540  const std::string& recordList,
2541  unsigned int numberOfCopies /* = 1 */)
2542 {
2543  if(!numberOfCopies)
2544  numberOfCopies = 1; // force 0 to 1, assuming user meant to get one copy
2545 
2546  // setup active tables based on input group and modified tables
2547  setupActiveTablesXML(xmlOut,
2548  cfgMgr,
2549  groupName,
2550  groupKey,
2551  modifiedTables,
2552  true /* refresh all */,
2553  false /* getGroupInfo */,
2554  0 /* returnMemberMap */,
2555  false /* outputActiveTables */);
2556 
2557  try
2558  {
2559  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2560  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2561 
2562  __SUP_COUT__ << table->getTableName() << __E__;
2563  TableVersion temporaryVersion;
2564 
2565  // if current version is not temporary
2566  // create temporary
2567  // else re-modify temporary version
2568  // edit temporary version directly
2569  // then after all edits return active versions
2570  //
2571 
2572  // extract record list
2573  std::vector<std::string> recordArray =
2574  StringMacros::getVectorFromString(recordList);
2575  __SUP_COUTV__(StringMacros::vectorToString(recordArray));
2576 
2577  // handle version bookkeeping
2578  {
2579  if(!(temporaryVersion = targetNode.getTableVersion()).isTemporaryVersion())
2580  {
2581  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2582  // create temporary version for editing
2583  temporaryVersion = table->createTemporaryView(temporaryVersion);
2584  cfgMgr->saveNewTable(targetNode.getTableName(),
2585  temporaryVersion,
2586  true); // proper bookkeeping for temporary version with the new version
2587 
2588  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
2589  }
2590  else // else table is already temporary version
2591  __SUP_COUT__ << "Using temporary version " << temporaryVersion << __E__;
2592  }
2593 
2594  // at this point have valid temporary version to edit
2595 
2596  // for every record, copy spec'd number of times
2597  unsigned int row;
2598  for(const auto& recordUID : recordArray)
2599  {
2600  row = table->getViewP()->findRow(table->getViewP()->getColUID(),
2601  StringMacros::decodeURIComponent(recordUID));
2602  for(unsigned int i = 0; i < numberOfCopies; ++i)
2603  table->getViewP()->copyRows(
2604  cfgMgr->getUsername(),
2605  table->getView(),
2606  row,
2607  1 /*srcRowsToCopy*/,
2608  -1 /*destOffsetRow*/,
2609  true /*generateUniqueDataColumns*/,
2610  recordUID /*baseNameAutoUID*/); // make the name similar end record loop
2611  }
2612 
2613  table->getViewP()->init(); // verify new table (throws runtime_errors)
2614 
2615  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2616  }
2617  catch(std::runtime_error& e)
2618  {
2619  __SUP_SS__ << ("Error copying record(s)!\n\n" + std::string(e.what())) << __E__;
2620  __SUP_COUT_ERR__ << "\n" << ss.str();
2621  xmlOut.addTextElementToData("Error", ss.str());
2622  }
2623  catch(...)
2624  {
2625  __SUP_SS__ << ("Error copying record(s)!\n\n") << __E__;
2626  try
2627  {
2628  throw;
2629  } //one more try to printout extra info
2630  catch(const std::exception& e)
2631  {
2632  ss << "Exception message: " << e.what();
2633  }
2634  catch(...)
2635  {
2636  }
2637  __SUP_COUT_ERR__ << "\n" << ss.str();
2638  xmlOut.addTextElementToData("Error", ss.str());
2639  }
2640 } // end handleFillCopyTreeNodeRecordsXML()
2641 
2642 //==============================================================================
2659 void ConfigurationGUISupervisor::handleFillSetTreeNodeFieldValuesXML(
2660  HttpXmlDocument& xmlOut,
2661  ConfigurationManagerRW* cfgMgr,
2662  const std::string& groupName,
2663  const TableGroupKey& groupKey,
2664  const std::string& startPath,
2665  const std::string& modifiedTables,
2666  const std::string& recordList,
2667  const std::string& fieldList,
2668  const std::string& valueList,
2669  const std::string& author)
2670 {
2671  // setup active tables based on input group and modified tables
2672  setupActiveTablesXML(xmlOut,
2673  cfgMgr,
2674  groupName,
2675  groupKey,
2676  modifiedTables,
2677  true /* refresh all */,
2678  false /* getGroupInfo */,
2679  0 /* returnMemberMap */,
2680  false /* outputActiveTables */);
2681 
2682  // for each field
2683  // return field/value pair in xml
2684 
2685  try
2686  {
2687  std::vector<std::string /*relative-path*/> fieldPaths;
2688  // extract field list
2689  {
2690  std::istringstream f(fieldList);
2691  std::string fieldPath;
2692  while(getline(f, fieldPath, ','))
2693  {
2694  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2695  }
2696  __SUP_COUT__ << fieldList << __E__;
2697  for(const auto& field : fieldPaths)
2698  __SUP_COUT__ << "fieldPath " << field << __E__;
2699  }
2700 
2701  std::vector<std::string /*relative-path*/> fieldValues;
2702  // extract value list
2703  {
2704  std::istringstream f(valueList);
2705  std::string fieldValue;
2706  while(getline(f, fieldValue, ','))
2707  {
2708  fieldValues.push_back(fieldValue); // setURIEncodedValue is expected
2709  // StringMacros::decodeURIComponent(fieldValue));
2710  }
2711 
2712  // if last value is "" then push empty value
2713  if(valueList.size() && valueList[valueList.size() - 1] == ',')
2714  fieldValues.push_back("");
2715 
2716  __SUP_COUT__ << valueList << __E__;
2717  for(const auto& value : fieldValues)
2718  __SUP_COUT__ << "fieldValue " << value << __E__;
2719  }
2720 
2721  if(fieldPaths.size() != fieldValues.size())
2722  {
2723  __SUP_SS__;
2724  __THROW__(ss.str() + "Mismatch in fields and values array size!");
2725  }
2726 
2727  // extract record list
2728  {
2729  TableBase* table;
2730  TableVersion temporaryVersion;
2731  std::istringstream f(recordList);
2732  std::string recordUID;
2733  unsigned int i;
2734 
2735  while(getline(f, recordUID, ',')) // for each record
2736  {
2737  recordUID = StringMacros::decodeURIComponent(recordUID);
2738 
2739  /*xercesc::DOMElement* parentEl =*/
2740  xmlOut.addTextElementToData("fieldValues", recordUID);
2741 
2742  // for each field, set value
2743  for(i = 0; i < fieldPaths.size(); ++i)
2744  {
2745  __SUP_COUT__ << "fieldPath " << fieldPaths[i] << __E__;
2746  __SUP_COUT__ << "fieldValue " << fieldValues[i] << __E__;
2747 
2748  // doNotThrowOnBrokenUIDLinks so that link UIDs can be edited like
2749  // other fields
2750  ConfigurationTree targetNode =
2751  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPaths[i],
2752  true /*doNotThrowOnBrokenUIDLinks*/);
2753 
2754  // need table, uid, columnName to set a value
2755 
2756  // assume correct table version is loaded by setupActiveTablesXML()
2757  // table = cfgMgr->getTableByName(
2758  // targetNode.getTableName());
2759  //
2760  //__SUP_COUT__ << "Active version is " << table->getViewVersion() <<
2761  //__E__;
2762 
2763  // mimic handleSaveTreeNodeEditXML L 1750
2764  // Actually call it! ..
2765  // with a modifier?
2766  // or
2767  // handleSaveTreeNodeEditXML(xmlOut,
2768  // cfgMgr,
2769  // targetNode.getTableName(),
2770  // targetNode.getTableVersion(),
2771  // "value",
2772  // targetNode.getUIDAsString(),
2773  // targetNode.getValueName(), //col name
2774  // fieldValues[i]
2775  // );
2776 
2777  // or
2778  // (because problem is this would create a new temporary version each
2779  // time) if current version is not temporary
2780  // create temporary
2781  // else re-modify temporary version
2782  // edit temporary version directly
2783  // then after all edits return active versions
2784  //
2785 
2786  __SUP_COUT__ << "Getting table " << targetNode.getFieldTableName()
2787  << __E__;
2788 
2789  // if link must get parent table name
2790  table = cfgMgr->getTableByName(
2791  targetNode.getFieldTableName()); // NOT getTableName!
2792  if(!(temporaryVersion = table->getViewP()->getVersion())
2793  .isTemporaryVersion())
2794  {
2795  // create temporary version for editing
2796  temporaryVersion =
2797  table->createTemporaryView(table->getViewP()->getVersion());
2798  cfgMgr->saveNewTable(table->getTableName(),
2799  temporaryVersion,
2800  true); // proper bookkeeping for temporary
2801  // version with the new version
2802 
2803  __SUP_COUT__ << "Created temporary version "
2804  << table->getTableName() << "-v" << temporaryVersion
2805  << __E__;
2806  }
2807  // else //else table is already temporary version
2808  __SUP_COUT__ << "Using temporary version " << table->getTableName()
2809  << "-v" << temporaryVersion << __E__;
2810 
2811  // copy "value" type edit from handleSaveTreeNodeEditXML()
2812  // functionality
2813  table->getViewP()->setURIEncodedValue(fieldValues[i],
2814  targetNode.getFieldRow(),
2815  targetNode.getFieldColumn(),
2816  author);
2817 
2818  table->getViewP()
2819  ->init(); // verify new table (throws runtime_errors)
2820  }
2821  }
2822  }
2823 
2824  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2825  }
2826  catch(std::runtime_error& e)
2827  {
2828  __SUP_SS__ << ("Error setting field values!\n\n" + std::string(e.what()))
2829  << __E__;
2830  __SUP_COUT_ERR__ << "\n" << ss.str();
2831  xmlOut.addTextElementToData("Error", ss.str());
2832  }
2833  catch(...)
2834  {
2835  __SUP_SS__ << ("Error setting field values!\n\n") << __E__;
2836  try
2837  {
2838  throw;
2839  } //one more try to printout extra info
2840  catch(const std::exception& e)
2841  {
2842  ss << "Exception message: " << e.what();
2843  }
2844  catch(...)
2845  {
2846  }
2847  __SUP_COUT_ERR__ << "\n" << ss.str();
2848  xmlOut.addTextElementToData("Error", ss.str());
2849  }
2850 } //end handleFillSetTreeNodeFieldValuesXML()
2851 
2852 //==============================================================================
2867 void ConfigurationGUISupervisor::handleFillGetTreeNodeFieldValuesXML(
2868  HttpXmlDocument& xmlOut,
2869  ConfigurationManagerRW* cfgMgr,
2870  const std::string& groupName,
2871  const TableGroupKey& groupKey,
2872  const std::string& startPath,
2873  const std::string& modifiedTables,
2874  const std::string& recordList,
2875  const std::string& fieldList)
2876 {
2877  // setup active tables based on input group and modified tables
2878  setupActiveTablesXML(
2879  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
2880 
2881  // for each field
2882  // return field/value pair in xml
2883 
2884  try
2885  {
2886  std::vector<std::string /*relative-path*/> fieldPaths;
2887  // extract field list
2888  {
2889  std::istringstream f(fieldList);
2890  std::string fieldPath;
2891  while(getline(f, fieldPath, ','))
2892  {
2893  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2894  }
2895  __SUP_COUT__ << fieldList << __E__;
2896  }
2897 
2898  // extract record list
2899  {
2900  std::istringstream f(recordList);
2901  std::string recordUID;
2902  while(getline(f, recordUID, ',')) // for each record
2903  {
2904  recordUID = StringMacros::decodeURIComponent(recordUID);
2905 
2906  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2907 
2908  xercesc::DOMElement* parentEl =
2909  xmlOut.addTextElementToData("fieldValues", recordUID);
2910 
2911  // for each field, get value
2912  for(const auto& fieldPath : fieldPaths)
2913  {
2914  // __SUP_COUT__ << "fieldPath " << fieldPath << __E__;
2915  // __SUP_COUT__ << "fullPath " << (startPath + "/" + recordUID + "/" + fieldPath) << __E__;
2916 
2917  ConfigurationTree node =
2918  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPath);
2919 
2920  xmlOut.addTextElementToParent("FieldPath", fieldPath, parentEl);
2921 
2922  xmlOut.addTextElementToParent(
2923  "FieldValue",
2924  node.getValueAsString(true /*returnLinkTableValue*/),
2925  parentEl);
2926  }
2927  }
2928  }
2929  }
2930  catch(std::runtime_error& e)
2931  {
2932  __SUP_SS__ << ("Error getting field values!\n\n" + std::string(e.what()))
2933  << __E__;
2934  __SUP_COUT_ERR__ << "\n" << ss.str();
2935  xmlOut.addTextElementToData("Error", ss.str());
2936  }
2937  catch(...)
2938  {
2939  __SUP_SS__ << ("Error getting field values!\n\n") << __E__;
2940  try
2941  {
2942  throw;
2943  } //one more try to printout extra info
2944  catch(const std::exception& e)
2945  {
2946  ss << "Exception message: " << e.what();
2947  }
2948  catch(...)
2949  {
2950  }
2951  __SUP_COUT_ERR__ << "\n" << ss.str();
2952  xmlOut.addTextElementToData("Error", ss.str());
2953  }
2954 } //end handleFillGetTreeNodeFieldValuesXML()
2955 
2956 //==============================================================================
2975 void ConfigurationGUISupervisor::handleFillTreeNodeCommonFieldsXML(
2976  HttpXmlDocument& xmlOut,
2977  ConfigurationManagerRW* cfgMgr,
2978  const std::string& groupName,
2979  const TableGroupKey& groupKey,
2980  const std::string& startPath,
2981  unsigned int depth,
2982  const std::string& modifiedTables,
2983  const std::string& recordList,
2984  const std::string& fieldList)
2985 {
2986  // setup active tables based on input group and modified tables
2987  setupActiveTablesXML(
2988  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
2989 
2990  try
2991  {
2992  xercesc::DOMElement* parentEl = xmlOut.addTextElementToData("fields", startPath);
2993 
2994  if(depth == 0)
2995  {
2996  __SUP_SS__ << "Depth of search must be greater than 0." << __E__;
2997  __SUP_COUT__ << ss.str();
2998  __SS_THROW__; // done if 0 depth, no fields
2999  }
3000 
3001  // do not allow traversing for common fields from root level
3002  // the tree view should be used for such a purpose
3003  // if(startPath == "/")
3004  // return;
3005 
3006  std::vector<ConfigurationTree::RecordField> retFieldList;
3007 
3008  {
3009  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3010  if(startNode.isLinkNode() && startNode.isDisconnected())
3011  {
3012  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
3013  __SUP_SS_THROW__;
3014  return; // quietly ignore disconnected links at depth
3015  // note: at the root level they will be flagged for the user
3016  }
3017 
3018  std::vector<std::string /*relative-path*/> fieldAcceptList, fieldRejectList;
3019  if(fieldList != "")
3020  {
3021  // extract field filter list
3022  {
3023  std::istringstream f(fieldList);
3024  std::string fieldPath, decodedFieldPath;
3025  while(getline(f, fieldPath, ','))
3026  {
3027  decodedFieldPath = StringMacros::decodeURIComponent(fieldPath);
3028 
3029  if(decodedFieldPath[0] == '!') // reject field
3030  fieldRejectList.push_back(decodedFieldPath.substr(1));
3031  else
3032  fieldAcceptList.push_back(decodedFieldPath);
3033  }
3034  __SUP_COUT__ << fieldList << __E__;
3035  for(auto& field : fieldAcceptList)
3036  __SUP_COUT__ << "fieldAcceptList " << field << __E__;
3037  for(auto& field : fieldRejectList)
3038  __SUP_COUT__ << "fieldRejectList " << field << __E__;
3039  }
3040  }
3041 
3042  std::vector<std::string /*relative-path*/> records;
3043  if(recordList == "*") // handle all records case
3044  {
3045  records.clear();
3046  records = startNode.getChildrenNames();
3047  __SUP_COUT__ << "Translating wildcard..." << __E__;
3048  for(auto& record : records)
3049  __SUP_COUT__ << "recordList " << record << __E__;
3050  }
3051  else if(recordList != "")
3052  {
3053  // extract record list
3054  {
3055  std::istringstream f(recordList);
3056  std::string recordStr;
3057  while(getline(f, recordStr, ','))
3058  {
3059  records.push_back(StringMacros::decodeURIComponent(recordStr));
3060  }
3061  __SUP_COUT__ << recordList << __E__;
3062  for(auto& record : records)
3063  __SUP_COUT__ << "recordList " << record << __E__;
3064  }
3065  }
3066 
3067  //=== get common fields call!
3068  retFieldList = startNode.getCommonFields(
3069  records, fieldAcceptList, fieldRejectList, depth);
3070  //=== end get common fields call!
3071  }
3072 
3073  xercesc::DOMElement* parentTypeEl;
3074  for(const auto& fieldInfo : retFieldList)
3075  {
3076  xmlOut.addTextElementToParent(
3077  "FieldTableName", fieldInfo.tableName_, parentEl);
3078  xmlOut.addTextElementToParent(
3079  "FieldColumnName", fieldInfo.columnName_, parentEl);
3080  xmlOut.addTextElementToParent(
3081  "FieldRelativePath", fieldInfo.relativePath_, parentEl);
3082  xmlOut.addTextElementToParent(
3083  "FieldColumnType", fieldInfo.columnInfo_->getType(), parentEl);
3084  xmlOut.addTextElementToParent(
3085  "FieldColumnDataType", fieldInfo.columnInfo_->getDataType(), parentEl);
3086  xmlOut.addTextElementToParent("FieldColumnDefaultValue",
3087  fieldInfo.columnInfo_->getDefaultValue(),
3088  parentEl);
3089  // again, should min and max be included here?
3090  parentTypeEl =
3091  xmlOut.addTextElementToParent("FieldColumnDataChoices", "", parentEl);
3092 
3093  // if there are associated data choices, send info
3094  auto dataChoices = fieldInfo.columnInfo_->getDataChoices();
3095  xmlOut.addTextElementToParent(
3096  "FieldColumnDataChoice", // add default to list to mimic tree handling
3097  fieldInfo.columnInfo_->getDefaultValue(),
3098  parentTypeEl);
3099  for(const auto& dataChoice : dataChoices)
3100  xmlOut.addTextElementToParent(
3101  "FieldColumnDataChoice", dataChoice, parentTypeEl);
3102  }
3103  }
3104  catch(std::runtime_error& e)
3105  {
3106  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
3107  << __E__;
3108  __SUP_COUT_ERR__ << "\n" << ss.str();
3109  xmlOut.addTextElementToData("Error", ss.str());
3110  }
3111  catch(...)
3112  {
3113  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
3114  try
3115  {
3116  throw;
3117  } //one more try to printout extra info
3118  catch(const std::exception& e)
3119  {
3120  ss << "Exception message: " << e.what();
3121  }
3122  catch(...)
3123  {
3124  }
3125  __SUP_COUT_ERR__ << "\n" << ss.str();
3126  xmlOut.addTextElementToData("Error", ss.str());
3127  }
3128 } //end handleFillTreeNodeCommonFieldsXML()
3129 
3130 //==============================================================================
3158 void ConfigurationGUISupervisor::handleFillUniqueFieldValuesForRecordsXML(
3159  HttpXmlDocument& xmlOut,
3160  ConfigurationManagerRW* cfgMgr,
3161  const std::string& groupName,
3162  const TableGroupKey& groupKey,
3163  const std::string& startPath,
3164  const std::string& modifiedTables,
3165  const std::string& recordList,
3166  const std::string& fieldList)
3167 {
3168  // setup active tables based on input group and modified tables
3169  setupActiveTablesXML(
3170  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
3171 
3172  try
3173  {
3174  // do not allow traversing for common fields from root level
3175  // the tree view should be used for such a purpose
3176  if(startPath == "/")
3177  return;
3178 
3179  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3180  if(startNode.isLinkNode() && startNode.isDisconnected())
3181  {
3182  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
3183  __SUP_COUT_ERR__ << "\n" << ss.str();
3184  __SS_THROW__;
3185  }
3186 
3187  // extract records list
3188  std::vector<std::string /*relative-path*/> records;
3189  if(recordList == "*") // handle all records case
3190  {
3191  records.clear();
3192  records = startNode.getChildrenNames();
3193  __SUP_COUT__ << "Translating wildcard..." << __E__;
3194  for(auto& record : records)
3195  __SUP_COUT__ << "recordList " << record << __E__;
3196  }
3197  else if(recordList != "")
3198  {
3199  // extract record list
3200  {
3201  std::istringstream f(recordList);
3202  std::string recordStr;
3203  while(getline(f, recordStr, ','))
3204  {
3205  records.push_back(StringMacros::decodeURIComponent(recordStr));
3206  }
3207  __SUP_COUT__ << recordList << __E__;
3208  for(auto& record : records)
3209  __SUP_COUT__ << "recordList " << record << __E__;
3210  }
3211  } // end records extraction
3212 
3213  // extract fields to get
3214  std::vector<std::string /*relative-path*/> fieldsToGet;
3215  if(fieldList != "")
3216  {
3217  // extract field filter list
3218 
3219  if(fieldList == "AUTO")
3220  {
3221  // automatically choose 3 fields, with preference
3222  // for GroupID, On/Off, and FixedChoice fields.
3223 
3224  __SUP_COUT__ << "Getting AUTO filter fields!" << __E__;
3225 
3226  std::vector<ConfigurationTree::RecordField> retFieldList;
3227  std::vector<std::string /*relative-path*/> fieldAcceptList,
3228  fieldRejectList;
3229  fieldRejectList.push_back("*" + TableViewColumnInfo::COL_NAME_COMMENT);
3230  retFieldList = startNode.getCommonFields(
3231  records, fieldAcceptList, fieldRejectList, 5, true /*auto*/);
3232 
3233  for(const auto& retField : retFieldList)
3234  fieldsToGet.push_back(retField.relativePath_ + retField.columnName_);
3235  }
3236  else
3237  {
3238  std::istringstream f(fieldList);
3239  std::string fieldPath;
3240  while(getline(f, fieldPath, ','))
3241  {
3242  fieldsToGet.push_back(StringMacros::decodeURIComponent(fieldPath));
3243  }
3244  __SUP_COUTV__(fieldList);
3245  }
3246  } // end fields extraction
3247 
3248  __SUP_COUTV__(StringMacros::vectorToString(fieldsToGet));
3249 
3250  // loop through each field and get unique values among records
3251  {
3252  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3253  std::string fieldGroupIDChildLinkIndex;
3254  for(auto& field : fieldsToGet)
3255  {
3256  __SUP_COUTV__(field);
3257 
3258  xercesc::DOMElement* parentEl =
3259  xmlOut.addTextElementToData("field", field);
3260 
3261  // if groupID field, give child link index
3262  // this can be used to pre-select particular group(s)
3263 
3264  // use set to force sorted unique values
3265  std::set<std::string /*unique-values*/> uniqueValues =
3266  startNode.getUniqueValuesForField(
3267  records, field, &fieldGroupIDChildLinkIndex);
3268 
3269  if(fieldGroupIDChildLinkIndex != "")
3270  xmlOut.addTextElementToParent(
3271  "childLinkIndex", fieldGroupIDChildLinkIndex, parentEl);
3272 
3273  for(auto& uniqueValue : uniqueValues)
3274  {
3275  __SUP_COUT__ << "uniqueValue " << uniqueValue << __E__;
3276 
3277  xmlOut.addTextElementToParent("uniqueValue", uniqueValue, parentEl);
3278  }
3279  }
3280  }
3281  }
3282  catch(std::runtime_error& e)
3283  {
3284  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
3285  << __E__;
3286  __SUP_COUT_ERR__ << "\n" << ss.str();
3287  xmlOut.addTextElementToData("Error", ss.str());
3288  }
3289  catch(...)
3290  {
3291  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
3292  try
3293  {
3294  throw;
3295  } //one more try to printout extra info
3296  catch(const std::exception& e)
3297  {
3298  ss << "Exception message: " << e.what();
3299  }
3300  catch(...)
3301  {
3302  }
3303  __SUP_COUT_ERR__ << "\n" << ss.str();
3304  xmlOut.addTextElementToData("Error", ss.str());
3305  }
3306 } // end handleFillUniqueFieldValuesForRecordsXML()
3307 
3308 //==============================================================================
3328 void ConfigurationGUISupervisor::handleFillTreeViewXML(
3329  HttpXmlDocument& xmlOut,
3330  ConfigurationManagerRW* cfgMgr,
3331  const std::string& groupName,
3332  const TableGroupKey& groupKey,
3333  const std::string& startPath,
3334  unsigned int depth,
3335  bool hideStatusFalse,
3336  const std::string& modifiedTables,
3337  const std::string& filterList,
3338  const std::string& diffGroupName /* = "" */,
3339  const TableGroupKey& diffGroupKey /* = TableGroupKey() */)
3340 {
3341  __SUP_COUTT__ << "get Tree View: " << groupName << "(" << groupKey << ")" << __E__;
3342 
3343  // return xml
3344  // <groupName="groupName"/>
3345  // <tree="path">
3346  // <node="...">
3347  // <node="...">
3348  // <node="...">
3349  // <value="...">
3350  // </node>
3351  // <node="...">
3352  // <value="...">
3353  // </node>
3354  // </node>
3355  // <node="...">
3356  // <value="..">
3357  // </node>
3358  // ...
3359  // </node>
3360  // </tree>
3361 
3362  // return the startPath as root "tree" element
3363  // and then display all children if depth > 0
3364 
3365  //------------------
3366  //First, if doing diff, load tables into cache and copy.
3367  // Loading will leave tables active in the user cfgMgr..
3368  // which will mess up diff. So order:
3369  // 1. load diff tables in user cfgMgr
3370  // 2. copy from cfgMgr cache to diffCfgMgr
3371  // 3. load tree tables in user cfgMgr
3372 
3373  bool doDiff = (diffGroupName != "" && !diffGroupKey.isInvalid());
3374 
3375  std::map<std::string /*name*/, TableVersion /*version*/> diffMemberMap;
3376  ConfigurationManagerRW tmpCfgMgr("TreeDiff");
3377  ConfigurationManagerRW* diffCfgMgr = &tmpCfgMgr;
3378  std::string diffAccumulateErrors;
3379  if(doDiff)
3380  {
3381  //Load diff tables in cfgMgr so that tables are cached,
3382  // then copy to diffCfgMgr as active tables for tree comparison.
3383  // This is more efficient than loading diff tables from db every tree access.
3384 
3385  for(auto& activeTable : cfgMgr->getActiveVersions())
3386  __SUP_COUT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3387  << __E__;
3388 
3389  cfgMgr->loadTableGroup(diffGroupName,
3390  diffGroupKey,
3391  false /*doActivate*/,
3392  &diffMemberMap,
3393  0 /*progressBar*/,
3394  0 /*accumulateErrors*/,
3395  0 /*groupComment*/,
3396  0 /*groupAuthor*/,
3397  0 /*groupCreationTime*/,
3398  false /*doNotLoadMember*/,
3399  0 /*groupTypeString*/
3400  );
3401 
3402  for(auto& activeTable : cfgMgr->getActiveVersions())
3403  __SUP_COUT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3404  << __E__;
3405 
3406  __SUP_COUTT__ << "Diff Group tables loaded." << __E__;
3407  diffCfgMgr->copyTableGroupFromCache(
3408  *cfgMgr, diffMemberMap, diffGroupName, diffGroupKey);
3409  __SUP_COUTT__ << "Diff Group tables copied to local diff config manager."
3410  << __E__;
3411 
3412  //now activate diff table for tree traversal (without calling init())
3413  for(auto& memberPair : diffMemberMap)
3414  diffCfgMgr->getTableByName(memberPair.first)
3415  ->setActiveView(memberPair.second);
3416 
3417  for(const auto& lastGroupLoaded : cfgMgr->getLastTableGroups())
3418  __SUP_COUT__ << "cfgMgr Last loaded " << lastGroupLoaded.first << ": "
3419  << lastGroupLoaded.second.first.first << "("
3420  << lastGroupLoaded.second.first.second << ")";
3421 
3422  for(const auto& lastGroupLoaded : diffCfgMgr->getLastTableGroups())
3423  __SUP_COUT__ << "diffCfgMgr Last loaded " << lastGroupLoaded.first << ": "
3424  << lastGroupLoaded.second.first.first << "("
3425  << lastGroupLoaded.second.first.second << ")";
3426 
3427  //for complete tree traversal, if config type, then load context tables in diff, if context type, then load config tables in diff
3428  if(diffCfgMgr->getLastTableGroups().size() == 1)
3429  {
3430  __SUP_COUT__ << "Type already loaded to diff = "
3431  << diffCfgMgr->getLastTableGroups().begin()->first << __E__;
3432  try
3433  {
3434  auto groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONTEXT;
3435  if(diffCfgMgr->getLastTableGroups().begin()->first ==
3436  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT)
3437  groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION;
3438  else if(diffCfgMgr->getLastTableGroups().begin()->first ==
3439  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION)
3440  groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONTEXT;
3441 
3442  __SUP_COUTT__
3443  << "Loading " << groupTypeToLoad
3444  << cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.first << "("
3445  << cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.second
3446  << ")" << __E__;
3447 
3448  diffCfgMgr->copyTableGroupFromCache(
3449  *cfgMgr,
3450  cfgMgr->getLastTableGroups().at(groupTypeToLoad).second,
3451  cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.first,
3452  cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.second);
3453 
3454  //now activate diff table for tree traversal (without calling init())
3455  for(auto& memberPair :
3456  cfgMgr->getLastTableGroups().at(groupTypeToLoad).second)
3457  diffCfgMgr->getTableByName(memberPair.first)
3458  ->setActiveView(memberPair.second);
3459  }
3460  catch(...)
3461  {
3462  } //ignore extra group loading errors
3463  }
3464 
3465  for(auto& activeTable : cfgMgr->getActiveVersions())
3466  __SUP_COUTT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3467  << __E__;
3468  for(auto& activeTable : diffCfgMgr->getActiveVersions())
3469  __SUP_COUTT__ << "diffCfgMgr " << activeTable.first << "-v"
3470  << activeTable.second << __E__;
3471 
3472  __SUP_COUTT__ << "Diff Group tables are setup: " << diffAccumulateErrors << __E__;
3473  } // end do diff load
3474 
3475  //------------------
3476  //Setup active tables based on input group and modified tables
3477  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
3478  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
3479 
3480  std::string accumulatedErrors = "";
3481  setupActiveTablesXML(xmlOut,
3482  cfgMgr,
3483  groupName,
3484  groupKey,
3485  modifiedTables,
3486  (startPath == "/"), // refreshAll, if at root node, reload all tables so that partially loaded tables are not allowed
3487  (startPath == "/"), // get group info
3488  &memberMap, // get group member map
3489  true, // output active tables (default)
3490  &accumulatedErrors // accumulate errors
3491  );
3492 
3493  if(memberMap.size() >
3494  ConfigurationManager::contextMemberNames_.size() + 1 /* for optional table */
3495  && startPath == "/")
3496  {
3497  __COUTT__ << "Checking for orphaned tables..." << __E__;
3498 
3499  //check Tree for orphaned tables
3500  std::set<std::string /* table name that is linked to */> linkingTables;
3501  for(const auto& tableInfo : cfgMgr->getAllTableInfo())
3502  {
3503  //for each existing active table, check table for links to tables
3504 
3505  __COUT_TYPE__(TLVL_DEBUG + 30)
3506  << __COUT_HDR__ << "Table " << tableInfo.first << __E__;
3507  if(!tableInfo.second.tablePtr_->isActive())
3508  continue; //skip if no active view for table
3509  else
3510  __COUT_TYPE__(TLVL_DEBUG + 30)
3511  << __COUT_HDR__ << "Table active " << tableInfo.first << __E__;
3512 
3513  const TableView& view = tableInfo.second.tablePtr_->getView();
3514 
3515  bool addedThisTable = false;
3516  for(unsigned int col = 0; col < view.getNumberOfColumns(); ++col)
3517  {
3518  if(!view.getColumnInfo(col).isChildLink())
3519  continue;
3520 
3521  __COUT_TYPE__(TLVL_DEBUG + 30)
3522  << __COUT_HDR__ << "Table " << tableInfo.first
3523  << " col: " << view.getColumnInfo(col).getName() << __E__;
3524 
3525  for(unsigned int r = 0; r < view.getNumberOfRows(); ++r)
3526  {
3527  if(view.getDataView()[r][col] == "" ||
3528  view.getDataView()[r][col] ==
3529  TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
3530  continue;
3531 
3532  if(!addedThisTable) //add this table to since it seems to have a link!
3533  {
3534  linkingTables.emplace(tableInfo.first);
3535  addedThisTable = true;
3536  }
3537  linkingTables.emplace(
3538  view.getDataView()[r][col]); //add linked table name to set
3539  }
3540  }
3541  }
3542  __COUTTV__(StringMacros::setToString(linkingTables));
3543 
3544  std::string missingTables = "";
3545  for(const auto& member : memberMap)
3546  {
3547  //if not in linking tables set, then note
3548  if(linkingTables.find(member.first) != linkingTables.end())
3549  continue; //linked-to table, so no warning
3550 
3551  if(missingTables.size())
3552  missingTables += ", ";
3553  missingTables += member.first;
3554  }
3555 
3556  if(missingTables.size())
3557  {
3558  __COUTV__(missingTables);
3559  std::stringstream ss;
3560  ss << "The following member tables of table group '" << groupName << "("
3561  << groupKey
3562  << ")' were identified as possibly orphaned (i.e. no active tables link "
3563  "to these tables, and these tables have no links to other tables):\n\n"
3564  << missingTables << "." << __E__;
3565  xmlOut.addTextElementToData("NoTreeLinkWarning", ss.str());
3566  }
3567  } //end orphaned table check
3568 
3569  if(accumulatedErrors != "")
3570  {
3571  xmlOut.addTextElementToData("Warning", accumulatedErrors);
3572 
3573  __SUP_COUT__ << "Active tables are setup. Warning string: '" << accumulatedErrors
3574  << "'" << __E__;
3575 
3576  __SUP_COUT__ << "Active table versions: "
3577  << StringMacros::mapToString(cfgMgr->getActiveVersions()) << __E__;
3578  }
3579  else
3580  {
3581  __SUP_COUTT__ << "Active tables are setup. No issues found." << __E__;
3582  __SUP_COUTT__ << "Active table versions: "
3583  << StringMacros::mapToString(cfgMgr->getActiveVersions()) << __E__;
3584  }
3585 
3586  try
3587  {
3588  xercesc::DOMElement* parentEl = xmlOut.addTextElementToData("tree", startPath);
3589 
3590  if(depth == 0)
3591  return; // already returned root node in itself
3592 
3593  std::vector<std::pair<std::string, ConfigurationTree>> rootMap;
3594  std::map<std::string, ConfigurationTree> diffRootMap;
3595 
3596  if(startPath == "/")
3597  {
3598  // then consider the configurationManager the root node
3599 
3600  std::string accumulateTreeErrs;
3601 
3602  if(usingActiveGroups)
3603  rootMap = cfgMgr->getChildren(0, &accumulateTreeErrs);
3604  else
3605  rootMap = cfgMgr->getChildren(&memberMap, &accumulateTreeErrs);
3606 
3607  if(doDiff)
3608  {
3609  diffRootMap =
3610  diffCfgMgr->getChildrenMap(&diffMemberMap, &diffAccumulateErrors);
3611  __SUP_COUTV__(diffRootMap.size());
3612  for(auto& diffChild : diffRootMap)
3613  __SUP_COUTV__(diffChild.first);
3614  }
3615 
3616  __SUP_COUTV__(accumulateTreeErrs);
3617 
3618  if(accumulateTreeErrs != "")
3619  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
3620  }
3621  else
3622  {
3623  ConfigurationTree startNode =
3624  cfgMgr->getNode(startPath, true /*doNotThrowOnBrokenUIDLinks*/);
3625  if(startNode.isLinkNode() && startNode.isDisconnected())
3626  {
3627  xmlOut.addTextElementToData("DisconnectedStartNode", "1");
3628  return; // quietly ignore disconnected links at depth
3629  // note: at the root level they will be flagged for the user
3630  }
3631 
3632  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap;
3633  StringMacros::getMapFromString(
3634  filterList,
3635  filterMap,
3636  std::set<char>({';'}) /*pair delimiters*/,
3637  std::set<char>({'='}) /*name/value delimiters*/);
3638 
3639  __COUTV__(StringMacros::mapToString(filterMap));
3640 
3641  rootMap = cfgMgr->getNode(startPath).getChildren(filterMap);
3642 
3643  if(doDiff)
3644  {
3645  try
3646  {
3647  ConfigurationTree diffStartNode = diffCfgMgr->getNode(
3648  startPath, true /*doNotThrowOnBrokenUIDLinks*/);
3649 
3650  if(diffStartNode.isLinkNode() && diffStartNode.isDisconnected())
3651  __SUP_COUTT__ << "Diff Group disconnected node." << __E__;
3652  else
3653  diffRootMap =
3654  diffCfgMgr->getNode(startPath).getChildrenMap(filterMap);
3655  }
3656  catch(const std::runtime_error& e)
3657  {
3658  //if diff node does not exist, user was already notified at parent diff, so ignore error.
3659  __SUP_COUTT__ << "Diff Group node does not exist." << __E__;
3660  }
3661  }
3662  }
3663 
3664  if(!doDiff)
3665  {
3666  for(auto& treePair : rootMap)
3667  recursiveTreeToXML(
3668  treePair.second, depth - 1, xmlOut, parentEl, hideStatusFalse);
3669  }
3670  else //doDiff
3671  {
3672  __SUP_COUTT__ << "Diff Tree recursive handling." << __E__;
3673 
3674  //convert vector rootMap to set for searching
3675  std::set<std::string /* treeNodeName */> rootMapToSearch;
3676  for(const auto& rootMember : rootMap)
3677  rootMapToSearch.emplace(rootMember.first);
3678 
3679  std::stringstream rootSs;
3680  for(const auto& rootMember : rootMap)
3681  rootSs << ", " << rootMember.first;
3682 
3683  //add all tables in diff group that are missing to parentEl
3684  std::stringstream diffRootSs;
3685  for(const auto& diffMember : diffRootMap) //diffMemberMap)
3686  {
3687  diffRootSs << ", " << diffMember.first << ":"
3688  << diffMember.second.getNodeType();
3689  if(rootMapToSearch.find(diffMember.first) ==
3690  rootMapToSearch
3691  .end()) //memberMap.find(diffMember.first) == memberMap.end())
3692  {
3693  std::stringstream missingSs;
3694  missingSs << diffMember.first << //" <<< Not in " <<
3695  // groupName << "(" << groupKey << "), present in " <<
3696  " <<< Only in " << diffGroupName << "(" << diffGroupKey
3697  << ") >>>";
3698  xmlOut.addTextElementToParent(
3699  "diffNodeMissing", missingSs.str(), parentEl);
3700  }
3701 
3702  if(diffMember.second.getNodeType() == "UIDLinkNode")
3703  {
3704  __SUP_COUTT__
3705  << "diff active "
3706  << StringMacros::mapToString(diffCfgMgr->getActiveVersions())
3707  << __E__;
3708  __SUP_COUTT__
3709  << "root active "
3710  << StringMacros::mapToString(cfgMgr->getActiveVersions())
3711  << __E__;
3712 
3713  __SUP_COUTT__ << "diff map " << diffRootSs.str() << __E__;
3714  __SUP_COUTT__ << "root map " << rootSs.str() << __E__;
3715 
3716  __SUP_COUTT__ << "\t\t" << diffMember.second.getValueName() << ": "
3717  << diffMember.second.getValueAsString() << __E__;
3718 
3719  __SUP_COUTT__ << diffMember.second.nodeDump();
3720  }
3721  }
3722 
3723  __SUP_COUTT__ << "diff map " << diffRootSs.str() << __E__;
3724  __SUP_COUTT__ << "root map " << rootSs.str() << __E__;
3725 
3726  //recurse
3727  for(auto& treePair : rootMap)
3728  {
3729  if(diffRootMap.find(treePair.first) == diffRootMap.end())
3730  {
3731  __SUP_COUTT__ << "Diff Tree recursive handling... " << treePair.first
3732  << __E__;
3733  ConfigurationTree rootNode(diffCfgMgr, nullptr /* table */);
3734  recursiveTreeToXML(
3735  treePair.second,
3736  depth - 1,
3737  xmlOut,
3738  parentEl,
3739  hideStatusFalse,
3740  rootNode /* root node diffTree to indicate record not found in diff group */);
3741  }
3742  else
3743  {
3744  __SUP_COUTT__ << "Diff Tree recursive handling... " << treePair.first
3745  << __E__;
3746  recursiveTreeToXML(treePair.second,
3747  depth - 1,
3748  xmlOut,
3749  parentEl,
3750  hideStatusFalse,
3751  diffRootMap.at(treePair.first));
3752  }
3753  }
3754  }
3755  }
3756  catch(std::runtime_error& e)
3757  {
3758  __SUP_SS__ << "Error detected generating XML tree!\n\n " << e.what() << __E__;
3759  __SUP_COUT_ERR__ << "\n" << ss.str();
3760  xmlOut.addTextElementToData("Error", ss.str());
3761  }
3762  catch(...)
3763  {
3764  __SUP_SS__ << "Error detected generating XML tree!" << __E__;
3765  try
3766  {
3767  throw;
3768  } //one more try to printout extra info
3769  catch(const std::exception& e)
3770  {
3771  ss << "Exception message: " << e.what();
3772  }
3773  catch(...)
3774  {
3775  }
3776  __SUP_COUT_ERR__ << "\n" << ss.str();
3777  xmlOut.addTextElementToData("Error", ss.str());
3778  }
3779 } // end handleFillTreeViewXML()
3780 
3781 //==============================================================================
3787 void ConfigurationGUISupervisor::recursiveTreeToXML(
3788  const ConfigurationTree& t,
3789  unsigned int depth,
3790  HttpXmlDocument& xmlOut,
3791  xercesc::DOMElement* parentEl,
3792  bool hideStatusFalse,
3793  std::optional<std::reference_wrapper<const ConfigurationTree>> diffTree)
3794 {
3795  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << t.getValueAsString() << __E__;
3796 
3797  if(t.isValueNode())
3798  {
3799  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << "\t" << t.getValueName() << ": "
3800  << t.getValueAsString() << __E__;
3801 
3802  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
3803  if(diffTree.has_value() &&
3804  t.getValueName() != TableViewColumnInfo::COL_NAME_COMMENT &&
3805  t.getValueName() != TableViewColumnInfo::COL_NAME_AUTHOR &&
3806  t.getValueName() != TableViewColumnInfo::COL_NAME_CREATION)
3807  {
3808  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << "\t\t diff type "
3809  << diffTree->get().getNodeType() << __E__;
3810 
3811  if(diffTree->get().isValueNode())
3812  {
3813  __COUT_TYPE__(TLVL_DEBUG + 30)
3814  << __COUT_HDR__ << "\t" << diffTree->get().getValueAsString() << " ? "
3815  << t.getValueAsString() << __E__;
3816  __COUT_TYPE__(TLVL_DEBUG + 30)
3817  << __COUT_HDR__ << "\t" << diffTree->get().getTableName() << "-v"
3818  << diffTree->get().getTableVersion() << " ? " << t.getTableName()
3819  << "-v" << t.getTableVersion() << __E__;
3820 
3821  if(t.getValueAsString() != diffTree->get().getValueAsString())
3822  {
3823  std::stringstream missingSs; //assume only one group loaded for diff
3824  auto diffGroupPair =
3825  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3826  diffTree->get().getTableName());
3827  missingSs << "<<< '" << diffTree->get().getValueAsString() << "' in "
3828  << diffGroupPair.first << "(" << diffGroupPair.second
3829  << ") >>>";
3830  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3831  }
3832  }
3833  else
3834  {
3835  std::stringstream missingSs; //assume only one group loaded for diff
3836  //lookup group name in diffManager based on current node's table (best proxy info for missing diff node at this point)
3837  auto diffGroupPair =
3838  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3839  t.getTableName());
3840  missingSs << "<<< Path not found in " << diffGroupPair.first << "("
3841  << diffGroupPair.second << ") >>>";
3842  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3843  }
3844 
3845  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << "\t" << t.getValueName()
3846  << ": " << t.getValueAsString() << __E__;
3847 
3848  } //end diff tree handling
3849 
3850  xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
3851  parentEl = xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
3852 
3853  // fixed choice and bitmap both use fixed choices strings
3854  // so output them to xml
3855  if(t.getValueType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
3856  t.getValueType() == TableViewColumnInfo::TYPE_BITMAP_DATA)
3857  {
3858  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << t.getValueType() << __E__;
3859 
3860  std::vector<std::string> choices = t.getFixedChoices();
3861  for(const auto& choice : choices)
3862  xmlOut.addTextElementToParent("fixedChoice", choice, parentEl);
3863  }
3864  }
3865  else
3866  {
3867  __COUT_TYPE__(TLVL_DEBUG + 30)
3868  << __COUT_HDR__ << "\t" << t.getValueAsString() << __E__;
3869 
3870  if(t.isLinkNode())
3871  {
3872  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << "\t\t" << t.getValueName()
3873  << ": " << t.getValueAsString() << __E__;
3874 
3875  // Note: The order of xml fields is required by JavaScript, so do NOT change
3876  // order.
3877  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
3878 
3879  if(diffTree.has_value())
3880  {
3881  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << "\t\t diff type "
3882  << diffTree->get().getNodeType() << __E__;
3883 
3884  if(diffTree->get()
3885  .isRootNode()) //then diff group does not have this uid!
3886  {
3887  __COUT_TYPE__(TLVL_DEBUG + 30)
3888  << __COUT_HDR__ << "" << t.getValueAsString() << __E__;
3889  std::stringstream missingSs; //assume only one group loaded for diff
3890  //lookup group name in diffManager based on current node's parent's table (best proxy info for missing diff node at this point)
3891  auto diffGroupPair =
3892  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3893  t.getParentTableName());
3894  missingSs << "<<< Path not found in " << diffGroupPair.first << "("
3895  << diffGroupPair.second << ") >>>";
3896  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3897  }
3898  else if(t.isDisconnected() != diffTree->get().isDisconnected())
3899  {
3900  __COUT_TYPE__(TLVL_DEBUG + 30)
3901  << __COUT_HDR__ << "\t\t diff isDisconnected "
3902  << diffTree->get().isDisconnected() << __E__;
3903 
3904  std::stringstream missingSs; //assume only one group loaded for diff
3905  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
3906  auto diffGroupPair =
3907  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3908  t.getParentTableName());
3909  missingSs << "<<< Link is "
3910  << (diffTree->get().isDisconnected() ? "DISCONNECTED"
3911  : "connected")
3912  << " in " << diffGroupPair.first << "("
3913  << diffGroupPair.second << ") >>>";
3914  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3915  }
3916  else if(!t.isDisconnected() &&
3917  t.isUIDLinkNode() != diffTree->get().isUIDLinkNode())
3918  {
3919  __COUT_TYPE__(TLVL_DEBUG + 30)
3920  << __COUT_HDR__ << "" << t.getValueAsString() << __E__;
3921  std::stringstream missingSs; //assume only one group loaded for diff
3922  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
3923  auto diffGroupPair =
3924  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3925  t.getParentTableName());
3926  missingSs << "<<< Link is "
3927  << (diffTree->get().isUIDLinkNode() ? "a UID Link"
3928  : "a Group Link")
3929  << " in " << diffGroupPair.first << "("
3930  << diffGroupPair.second << ") >>>";
3931  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3932  }
3933  else if(!t.isDisconnected() && t.isUIDLinkNode() &&
3934  t.getValueAsString() !=
3935  diffTree->get().getValueAsString()) //both are UID link
3936  {
3937  __COUT_TYPE__(TLVL_DEBUG + 30)
3938  << __COUT_HDR__ << "" << t.getValueAsString() << __E__;
3939  std::stringstream missingSs; //assume only one group loaded for diff
3940  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
3941  auto diffGroupPair =
3942  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3943  t.getParentTableName());
3944  missingSs << "<<< Link to '" << diffTree->get().getValueAsString()
3945  << "' in " << diffGroupPair.first << "("
3946  << diffGroupPair.second << ") >>>";
3947  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3948  }
3949  else if(!t.isDisconnected() && !t.isUIDLinkNode()) //both are Group links
3950  {
3951  __COUT_TYPE__(TLVL_DEBUG + 30)
3952  << __COUT_HDR__ << "" << t.getValueAsString() << __E__;
3953  std::stringstream missingSs; //assume only one group loaded for diff
3954 
3955  auto tchildren = t.getChildrenMap();
3956  auto dtchildren = diffTree->get().getChildrenMap();
3957  missingSs << "<<< Group link";
3958  if(tchildren.size() != dtchildren.size())
3959  missingSs << " has " << tchildren.size() << " vs "
3960  << dtchildren.size() << " children..";
3961  for(auto& tchild : tchildren)
3962  if(dtchildren.find(tchild.first) == dtchildren.end())
3963  missingSs << " '" << tchild.first << "' missing..";
3964  for(auto& dtchild : dtchildren)
3965  if(tchildren.find(dtchild.first) == tchildren.end())
3966  missingSs << " '" << dtchild.first << "' present...";
3967 
3968  //only add nodeDiff if ss has been appended
3969  if(missingSs.str().length() > std::string("<<< Group link").length())
3970  {
3971  auto diffGroupPair =
3972  diffTree->get()
3973  .getConfigurationManager()
3974  ->getGroupOfLoadedTable(diffTree->get().getTableName());
3975  missingSs << " in " << diffGroupPair.first << "("
3976  << diffGroupPair.second << ") >>>";
3977  xmlOut.addTextElementToParent(
3978  "nodeDiff", missingSs.str(), parentEl);
3979  }
3980  }
3981  else
3982  __COUT_TYPE__(TLVL_DEBUG + 30)
3983  << __COUT_HDR__ << "" << t.getValueAsString() << __E__;
3984  } //end diff tree handling
3985 
3986  if(t.isDisconnected())
3987  {
3988  __COUT_TYPE__(TLVL_DEBUG + 30)
3989  << __COUT_HDR__ << t.getValueName() << __E__;
3990 
3991  // xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
3992  // xmlOut.addTextElementToParent("DisconnectedLink", t.getValueAsString(),
3993  // parentEl);
3994 
3995  xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
3996 
3997  // add extra fields for disconnected link
3998  xmlOut.addTextElementToParent(
3999  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
4000  t.getDisconnectedLinkID(),
4001  parentEl);
4002  xmlOut.addTextElementToParent(
4003  "LinkTableName", t.getDisconnectedTableName(), parentEl);
4004  xmlOut.addTextElementToParent(
4005  "LinkIndex", t.getChildLinkIndex(), parentEl);
4006 
4007  // add fixed choices (in case link has them)
4008  xercesc::DOMElement* choicesParentEl =
4009  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
4010  // try
4011  //{
4012 
4013  std::vector<std::string> choices = t.getFixedChoices();
4014  __COUT_TYPE__(TLVL_DEBUG + 30)
4015  << __COUT_HDR__ << "choices.size() " << choices.size() << __E__;
4016 
4017  for(const auto& choice : choices)
4018  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
4019  //}
4020  // catch(...)
4021  //{
4022  // __COUT__ << "Ignoring unknown fixed choice error"
4023  //} //ignore no fixed choices for disconnected
4024 
4025  return;
4026  }
4027  // else handle connected links
4028 
4029  xmlOut.addTextElementToParent(
4030  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
4031  t.getValueAsString(),
4032  parentEl);
4033 
4034  xmlOut.addTextElementToParent("LinkTableName", t.getTableName(), parentEl);
4035  xmlOut.addTextElementToParent("LinkIndex", t.getChildLinkIndex(), parentEl);
4036 
4037  // add fixed choices (in case link has them)
4038  {
4039  xercesc::DOMElement* choicesParentEl =
4040  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
4041  std::vector<std::string> choices = t.getFixedChoices();
4042 
4043  for(const auto& choice : choices)
4044  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
4045  }
4046  }
4047  else // uid node (or root node)
4048  {
4049  __COUT_TYPE__(TLVL_DEBUG + 30)
4050  << __COUT_HDR__ << "\t\t" << t.getValueAsString() << __E__;
4051  bool returnNode = true; // default to shown
4052 
4053  if(t.isUIDNode() && hideStatusFalse) // only show if status evaluates to true
4054  returnNode = t.isEnabled();
4055 
4056  if(returnNode)
4057  {
4058  parentEl =
4059  xmlOut.addTextElementToParent("node", t.getValueAsString(), parentEl);
4060  if(t.isUIDNode())
4061  xmlOut.addTextElementToParent("comment",
4062  t.getAuthor() + ": " + t.getComment(), parentEl);
4063 
4064  if(diffTree.has_value())
4065  {
4066  __COUT_TYPE__(TLVL_DEBUG + 30)
4067  << __COUT_HDR__ << "\t\t diff type "
4068  << diffTree->get().getNodeType() << __E__;
4069 
4070  if(diffTree->get()
4071  .isRootNode()) //then diff group does not have this uid!
4072  {
4073  __COUT_TYPE__(TLVL_DEBUG + 30)
4074  << __COUT_HDR__ << "" << t.getValueAsString() << __E__;
4075  std::stringstream
4076  missingSs; //assume only one group loaded for diff
4077  //lookup group name in diffManager based on current node's table (best proxy info for diff node at this point)
4078  auto diffGroupPair =
4079  diffTree->get()
4080  .getConfigurationManager()
4081  ->getGroupOfLoadedTable(t.getTableName());
4082  missingSs << "<<< Not in " << diffGroupPair.first << "("
4083  << diffGroupPair.second << ") >>>";
4084  xmlOut.addTextElementToParent(
4085  "nodeDiff", missingSs.str(), parentEl);
4086  }
4087  else
4088  __COUT_TYPE__(TLVL_DEBUG + 30)
4089  << __COUT_HDR__ << "" << t.getValueAsString() << __E__;
4090  } //end diff tree handling
4091  }
4092  else //hiding node
4093  return; // done.. no further depth needed for node that is not shown
4094  }
4095 
4096  // if depth>=1 toXml all children
4097  // child.toXml(depth-1)
4098  if(depth >= 1)
4099  {
4100  __COUT_TYPE__(TLVL_DEBUG + 30)
4101  << __COUT_HDR__ << "\t\t\t" << t.getValueAsString() << __E__;
4102  auto C = t.getChildren();
4103  for(auto& c : C)
4104  recursiveTreeToXML( //TODO -- implement diffTree for depth > 1 requests
4105  c.second,
4106  depth - 1,
4107  xmlOut,
4108  parentEl,
4109  hideStatusFalse);
4110  }
4111  }
4112 } // end recursiveTreeToXML()
4113 
4114 //==============================================================================
4121 void ConfigurationGUISupervisor::handleGetLinkToChoicesXML(
4122  HttpXmlDocument& xmlOut,
4123  ConfigurationManagerRW* cfgMgr,
4124  const std::string& linkToTableName,
4125  const TableVersion& linkToTableVersion,
4126  const std::string& linkIdType,
4127  const std::string& linkIndex,
4128  const std::string& linkInitId)
4129 try
4130 {
4131  // get table
4132  // if uid link
4133  // return all uids
4134  // if groupid link
4135  // find target column
4136  // create the set of values (unique values only)
4137  // note: insert group unions individually (i.e. groups | separated)
4138 
4139  // get table and activate target version
4140  // rename to re-use code template
4141  const std::string& tableName = linkToTableName;
4142  const TableVersion& version = linkToTableVersion;
4143  TableBase* table = cfgMgr->getTableByName(tableName);
4144  try
4145  {
4146  table->setActiveView(version);
4147  }
4148  catch(...)
4149  {
4150  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
4151  << version << __E__;
4152  cfgMgr->getVersionedTableByName(tableName, version);
4153  }
4154 
4155  if(version != table->getViewVersion())
4156  {
4157  __SUP_SS__ << "Target table version (" << version
4158  << ") is not the currently active version (" << table->getViewVersion()
4159  << ". Try refreshing the tree." << __E__;
4160  __SUP_COUT_WARN__ << ss.str();
4161  __SS_THROW__;
4162  }
4163 
4164  __SUP_COUT__ << "Active version is " << table->getViewVersion() << __E__;
4165 
4166  if(linkIdType == "UID")
4167  {
4168  // give all UIDs
4169  unsigned int col = table->getView().getColUID();
4170  for(unsigned int row = 0; row < table->getView().getNumberOfRows(); ++row)
4171  xmlOut.addTextElementToData("linkToChoice",
4172  table->getView().getDataView()[row][col]);
4173  }
4174  else if(linkIdType == "GroupID")
4175  {
4176  // find target column
4177  // create the set of values (unique values only)
4178  // note: insert group unions individually (i.e. groups | separated)
4179 
4180  __SUP_COUTV__(linkIndex);
4181  __SUP_COUTV__(linkInitId);
4182 
4183  std::set<std::string> setOfGroupIDs =
4184  table->getView().getSetOfGroupIDs(linkIndex);
4185 
4186  // build list of groupids
4187  // always include initial link group id in choices
4188  // (even if not in set of group ids)
4189  bool foundInitId = false;
4190  for(const auto& groupID : setOfGroupIDs)
4191  {
4192  if(!foundInitId && linkInitId == groupID)
4193  foundInitId = true; // mark init id found
4194 
4195  xmlOut.addTextElementToData("linkToChoice", groupID);
4196  }
4197  // if init id was not found, add to list
4198  if(!foundInitId)
4199  xmlOut.addTextElementToData("linkToChoice", linkInitId);
4200 
4201  // give all UIDs
4202  unsigned int col = table->getView().getColUID();
4203  for(unsigned int row = 0; row < table->getView().getNumberOfRows(); ++row)
4204  {
4205  xmlOut.addTextElementToData("groupChoice",
4206  table->getView().getDataView()[row][col]);
4207  if(table->getView().isEntryInGroup(row, linkIndex, linkInitId))
4208  xmlOut.addTextElementToData("groupMember",
4209  table->getView().getDataView()[row][col]);
4210  }
4211  }
4212  else
4213  {
4214  __SUP_SS__ << "Unrecognized linkIdType '" << linkIdType << ".'" << __E__;
4215  __SS_THROW__;
4216  }
4217 } //end handleGetLinkToChoicesXML()
4218 catch(std::runtime_error& e)
4219 {
4220  __SUP_SS__ << "Error detected saving tree node!\n\n " << e.what() << __E__;
4221  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4222  xmlOut.addTextElementToData("Error", ss.str());
4223 }
4224 catch(...)
4225 {
4226  __SUP_SS__ << "Error detected saving tree node!\n\n " << __E__;
4227  try
4228  {
4229  throw;
4230  } //one more try to printout extra info
4231  catch(const std::exception& e)
4232  {
4233  ss << "Exception message: " << e.what();
4234  }
4235  catch(...)
4236  {
4237  }
4238  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4239  xmlOut.addTextElementToData("Error", ss.str());
4240 } //end handleGetLinkToChoicesXML() catch
4241 
4242 //==============================================================================
4244 void ConfigurationGUISupervisor::handleMergeGroupsXML(
4245  HttpXmlDocument& xmlOut,
4246  ConfigurationManagerRW* cfgMgr,
4247  const std::string& groupANameContext,
4248  const TableGroupKey& groupAKeyContext,
4249  const std::string& groupBNameContext,
4250  const TableGroupKey& groupBKeyContext,
4251  const std::string& groupANameConfig,
4252  const TableGroupKey& groupAKeyConfig,
4253  const std::string& groupBNameConfig,
4254  const TableGroupKey& groupBKeyConfig,
4255  const std::string& author,
4256  const std::string& mergeApproach)
4257 try
4258 {
4259  __SUP_COUT__ << "Merging context group pair " << groupANameContext << " ("
4260  << groupAKeyContext << ") & " << groupBNameContext << " ("
4261  << groupBKeyContext << ") and table group pair " << groupANameConfig
4262  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4263  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
4264 
4265  // Merges group A and group B
4266  // with consideration for UID conflicts
4267  // Result is a new key of group A's name
4268  //
4269  // There 3 modes:
4270  // Rename -- All records from both groups are maintained, but conflicts from B
4271  // are renamed.
4272  // Must maintain a map of UIDs that are remapped to new name for
4273  // groupB, because linkUID fields must be preserved. Replace --
4274  // Any UID conflicts for a record are replaced by the record from group B.
4275  // Skip -- Any UID conflicts for a record are skipped so that group A record
4276  // remains
4277 
4278  // check valid mode
4279  if(!(mergeApproach == "Rename" || mergeApproach == "Replace" ||
4280  mergeApproach == "Skip"))
4281  {
4282  __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__;
4283  __SS_THROW__;
4284  }
4285 
4286  std::map<std::string /*name*/, TableVersion /*version*/> memberMapAContext,
4287  memberMapBContext, memberMapAConfig, memberMapBConfig;
4288 
4289  // check if skipping group pairs
4290  bool skippingContextPair = false;
4291  bool skippingConfigPair = false;
4292  if(groupANameContext.size() == 0 || groupANameContext[0] == ' ' ||
4293  groupBNameContext.size() == 0 || groupBNameContext[0] == ' ')
4294  {
4295  skippingContextPair = true;
4296  __SUP_COUTV__(skippingContextPair);
4297  }
4298  if(groupANameConfig.size() == 0 || groupANameConfig[0] == ' ' ||
4299  groupBNameConfig.size() == 0 || groupBNameConfig[0] == ' ')
4300  {
4301  skippingConfigPair = true;
4302  __SUP_COUTV__(skippingConfigPair);
4303  }
4304 
4305  // get context group member maps
4306  if(!skippingContextPair)
4307  {
4308  cfgMgr->loadTableGroup(groupANameContext,
4309  groupAKeyContext,
4310  false /*doActivate*/,
4311  &memberMapAContext,
4312  0 /*progressBar*/,
4313  0 /*accumulateErrors*/,
4314  0 /*groupComment*/,
4315  0 /*groupAuthor*/,
4316  0 /*groupCreationTime*/,
4317  false /*doNotLoadMember*/,
4318  0 /*groupTypeString*/
4319  );
4320  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
4321 
4322  cfgMgr->loadTableGroup(groupBNameContext,
4323  groupBKeyContext,
4324  false /*doActivate*/,
4325  &memberMapBContext,
4326  0 /*progressBar*/,
4327  0 /*accumulateErrors*/,
4328  0 /*groupComment*/,
4329  0 /*groupAuthor*/,
4330  0 /*groupCreationTime*/,
4331  false /*doNotLoadMember*/,
4332  0 /*groupTypeString*/
4333  );
4334 
4335  __SUP_COUTV__(StringMacros::mapToString(memberMapBContext));
4336  }
4337 
4338  // get table group member maps
4339  if(!skippingConfigPair)
4340  {
4341  cfgMgr->loadTableGroup(groupANameConfig,
4342  groupAKeyConfig,
4343  false /*doActivate*/,
4344  &memberMapAConfig,
4345  0 /*progressBar*/,
4346  0 /*accumulateErrors*/,
4347  0 /*groupComment*/,
4348  0 /*groupAuthor*/,
4349  0 /*groupCreationTime*/,
4350  false /*doNotLoadMember*/,
4351  0 /*groupTypeString*/
4352  );
4353  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
4354 
4355  cfgMgr->loadTableGroup(groupBNameConfig,
4356  groupBKeyConfig,
4357  false /*doActivate*/,
4358  &memberMapBConfig,
4359  0 /*progressBar*/,
4360  0 /*accumulateErrors*/,
4361  0 /*groupComment*/,
4362  0 /*groupAuthor*/,
4363  0 /*groupCreationTime*/,
4364  false /*doNotLoadMember*/,
4365  0 /*groupTypeString*/
4366  );
4367 
4368  __SUP_COUTV__(StringMacros::mapToString(memberMapBConfig));
4369  }
4370 
4371  // for each member of B
4372  // if not found in A member map, add it
4373  // if found in both member maps, and versions are different, load both tables and
4374  // merge
4375 
4376  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>,
4377  std::string /*converted uidB*/>
4378  uidConversionMap;
4379  std::map<
4380  std::pair<std::string /*original table*/,
4381  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>>,
4382  std::string /*converted gidB*/>
4383  groupidConversionMap;
4384 
4385  std::stringstream mergeReport;
4386  mergeReport << "======================================" << __E__;
4387  mergeReport << "Time of merge: " << StringMacros::getTimestampString() << __E__;
4388  mergeReport << "Merging context group pair " << groupANameContext << " ("
4389  << groupAKeyContext << ") & " << groupBNameContext << " ("
4390  << groupBKeyContext << ") and table group pair " << groupANameConfig
4391  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4392  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
4393  mergeReport << "======================================" << __E__;
4394 
4395  // first loop create record conversion map, second loop implement merge (using
4396  // conversion map if Rename)
4397  for(unsigned int i = 0; i < 2; ++i)
4398  {
4399  if(i == 0 && mergeApproach != "Rename")
4400  continue; // only need to construct uidConversionMap for rename approach
4401 
4402  // loop for context and table pair types
4403  for(unsigned int j = 0; j < 2; ++j)
4404  {
4405  if(j == 0 && skippingContextPair) // context
4406  {
4407  __COUT__ << "Skipping context pair..." << __E__;
4408  continue;
4409  }
4410  else if(j == 1 && skippingConfigPair)
4411  {
4412  __COUT__ << "Skipping table pair..." << __E__;
4413  continue;
4414  }
4415 
4416  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapAref =
4417  j == 0 ? memberMapAContext : memberMapAConfig;
4418 
4419  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapBref =
4420  j == 0 ? memberMapBContext : memberMapBConfig;
4421 
4422  if(j == 0) // context
4423  __COUT__ << "Context pair..." << __E__;
4424  else
4425  __COUT__ << "Table pair..." << __E__;
4426 
4427  __COUT__ << "Starting member map B scan." << __E__;
4428  for(const auto& bkey : memberMapBref)
4429  {
4430  __SUP_COUTV__(bkey.first);
4431 
4432  if(memberMapAref.find(bkey.first) == memberMapAref.end())
4433  {
4434  mergeReport << "\n'" << mergeApproach << "'-Missing table '"
4435  << bkey.first << "' A=v" << -1 << ", adding B=v"
4436  << bkey.second << __E__;
4437 
4438  // not found, so add to A member map
4439  memberMapAref[bkey.first] = bkey.second;
4440  }
4441  else if(memberMapAref[bkey.first] != bkey.second)
4442  {
4443  // found table version confict
4444  __SUP_COUTV__(memberMapAref[bkey.first]);
4445  __SUP_COUTV__(bkey.second);
4446 
4447  // load both tables, and merge
4448  TableBase* table = cfgMgr->getTableByName(bkey.first);
4449 
4450  __SUP_COUT__ << "Got table." << __E__;
4451 
4452  TableVersion newVersion = table->mergeViews(
4453  cfgMgr
4454  ->getVersionedTableByName(bkey.first,
4455  memberMapAref[bkey.first])
4456  ->getView(),
4457  cfgMgr->getVersionedTableByName(bkey.first, bkey.second)
4458  ->getView(),
4459  TableVersion() /* destinationVersion*/,
4460  author,
4461  mergeApproach /*Rename,Replace,Skip*/,
4462  uidConversionMap,
4463  groupidConversionMap,
4464  i == 0 /* fillRecordConversionMaps */,
4465  i == 1 /* applyRecordConversionMaps */,
4466  table->getTableName() ==
4467  ConfigurationManager::
4468  XDAQ_APPLICATION_TABLE_NAME
4469  /* generateUniqueDataColumns */,
4470  &mergeReport); // dont make destination version the first time
4471 
4472  if(i == 1)
4473  {
4474  __SUP_COUTV__(newVersion);
4475 
4476  try
4477  {
4478  // save all temporary tables to persistent tables
4479  // finish off the version creation
4480  newVersion =
4481  ConfigurationSupervisorBase::saveModifiedVersionXML(
4482  xmlOut,
4483  cfgMgr,
4484  bkey.first,
4485  TableVersion() /*original source version*/,
4486  false /* makeTemporary */,
4487  table,
4488  newVersion /*temporary modified version*/,
4489  false /*ignore duplicates*/,
4490  true /*look for equivalent*/);
4491  }
4492  catch(std::runtime_error& e)
4493  {
4494  __SUP_SS__
4495  << "There was an error saving the '"
4496  << table->getTableName()
4497  << "' merge result to a persistent table version. "
4498  << "Perhaps you can modify this table in one of the "
4499  "groups to resolve this issue, and then re-merge."
4500  << __E__ << e.what();
4501  __SS_THROW__;
4502  }
4503 
4504  __SUP_COUTV__(newVersion);
4505 
4506  memberMapAref[bkey.first] = newVersion;
4507  }
4508  } // end member version conflict handling
4509  } // end B member map loop
4510  } // end context and table loop
4511  } // end top level conversion map or not loop
4512 
4513  // Now save groups
4514 
4515  if(!skippingContextPair)
4516  {
4517  __SUP_COUT__ << "New context member map complete." << __E__;
4518  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
4519 
4520  // save the new table group
4521  TableGroupKey newKeyContext = cfgMgr->saveNewTableGroup(
4522  groupANameContext,
4523  memberMapAContext,
4524  "Merger of group " + groupANameContext + " (" + groupAKeyContext.toString() +
4525  ") and " + groupBNameContext + " (" + groupBKeyContext.toString() + ").");
4526 
4527  // return new resulting group
4528  xmlOut.addTextElementToData("ContextGroupName", groupANameContext);
4529  xmlOut.addTextElementToData("ContextGroupKey", newKeyContext.toString());
4530  }
4531  if(!skippingConfigPair)
4532  {
4533  __SUP_COUT__ << "New table member map complete." << __E__;
4534  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
4535 
4536  // save the new table group
4537  TableGroupKey newKeyConfig = cfgMgr->saveNewTableGroup(
4538  groupANameConfig,
4539  memberMapAConfig,
4540  "Merger of group " + groupANameConfig + " (" + groupAKeyConfig.toString() +
4541  ") and " + groupBNameConfig + " (" + groupBKeyConfig.toString() + ").");
4542 
4543  // return new resulting group
4544  xmlOut.addTextElementToData("ConfigGroupName", groupANameConfig);
4545  xmlOut.addTextElementToData("ConfigGroupKey", newKeyConfig.toString());
4546  }
4547 
4548  // output merge report
4549  {
4550  std::string mergeReportBasePath = std::string(__ENV__("USER_DATA"));
4551  std::string mergeReportPath = "/ServiceData/";
4552  // make merge report directories in case they don't exist
4553  mkdir((mergeReportBasePath + mergeReportPath).c_str(), 0755);
4554  mergeReportPath += "ConfigurationGUI_mergeReports/";
4555  // make merge report directories in case they don't exist
4556  mkdir((mergeReportBasePath + mergeReportPath).c_str(), 0755);
4557 
4558  mergeReportPath +=
4559  "merge_" + std::to_string(time(0)) + "_" + std::to_string(clock()) + ".txt";
4560  __SUP_COUTV__(mergeReportPath);
4561 
4562  FILE* fp = fopen((mergeReportBasePath + mergeReportPath).c_str(), "w");
4563  if(fp)
4564  {
4565  fprintf(fp, "%s", mergeReport.str().c_str());
4566  fclose(fp);
4567  xmlOut.addTextElementToData("MergeReportFile",
4568  "/$USER_DATA/" + mergeReportPath);
4569  }
4570  else
4571  xmlOut.addTextElementToData("MergeReportFile", "FILE FAILURE");
4572  } // end output merge report
4573 
4574 } // end handleMergeGroupsXML()
4575 catch(std::runtime_error& e)
4576 {
4577  __SUP_SS__ << "Error merging context group pair " << groupANameContext << " ("
4578  << groupAKeyContext << ") & " << groupBNameContext << " ("
4579  << groupBKeyContext << ") and table group pair " << groupANameConfig
4580  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4581  << groupBKeyConfig << ") with approach '" << mergeApproach << "': \n\n"
4582  << e.what() << __E__;
4583  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4584  xmlOut.addTextElementToData("Error", ss.str());
4585 }
4586 catch(...)
4587 {
4588  __SUP_SS__ << "Unknown error merging context group pair " << groupANameContext << " ("
4589  << groupAKeyContext << ") & " << groupBNameContext << " ("
4590  << groupBKeyContext << ") and table group pair " << groupANameConfig
4591  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4592  << groupBKeyConfig << ") with approach '" << mergeApproach << ".' \n\n";
4593  try
4594  {
4595  throw;
4596  } //one more try to printout extra info
4597  catch(const std::exception& e)
4598  {
4599  ss << "Exception message: " << e.what();
4600  }
4601  catch(...)
4602  {
4603  }
4604  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4605  xmlOut.addTextElementToData("Error", ss.str());
4606 } // end handleMergeGroupsXML() catch
4607 
4608 //==============================================================================
4610 void ConfigurationGUISupervisor::handleSavePlanCommandSequenceXML(
4611  HttpXmlDocument& xmlOut,
4612  ConfigurationManagerRW* cfgMgr,
4613  const std::string& groupName,
4614  const TableGroupKey& groupKey,
4615  const std::string& modifiedTables,
4616  const std::string& author,
4617  const std::string& planName,
4618  const std::string& commandString)
4619 try
4620 {
4621  __COUT__ << "handleSavePlanCommandSequenceXML " << planName << __E__;
4622 
4623  // setup active tables based on input group and modified tables
4624  setupActiveTablesXML(xmlOut,
4625  cfgMgr,
4626  groupName,
4627  groupKey,
4628  modifiedTables,
4629  true /* refresh all */,
4630  false /* getGroupInfo */,
4631  0 /* returnMemberMap */,
4632  false /* outputActiveTables */);
4633 
4634  TableEditStruct planTable(IterateTable::PLAN_TABLE,
4635  cfgMgr); // Table ready for editing!
4636  TableEditStruct targetTable(IterateTable::TARGET_TABLE,
4637  cfgMgr); // Table ready for editing!
4638 
4639  // create table-edit struct for each table that an iterate command type can use
4640  //if two command types have same table, TableEditStruct returns the same temporary version of the table, but then modified_
4641  // will be maintained separately and saving the table becomes a mess.
4642  std::map<std::string /* table name */, TableEditStruct> commandTableToEditMap;
4643  for(const auto& commandPair : IterateTable::commandToTableMap_)
4644  if(commandPair.second != "") // skip tables with no parameters
4645  commandTableToEditMap.emplace(std::pair<std::string, TableEditStruct>(
4646  commandPair.second, TableEditStruct(commandPair.second, cfgMgr)));
4647 
4648  // try to catch any errors while editing..
4649  // if errors delete temporary plan view (if created here)
4650  try
4651  {
4652  // Steps:
4653  // Reset plan commands
4654  // Remove all commands in group "<plan>-Plan"
4655  // Delete linked command parameters row (in separate table)
4656  // If no group remaining, then delete row.
4657  //
4658  // Save plan commands (if modified)
4659  // Create rows and add them to group "<plan>-Plan"
4660  // create row for command paramaters and add to proper table
4661 
4662  std::string groupName = planName + "-Plan";
4663  __SUP_COUT__ << "Handling commands for group " << groupName << __E__;
4664 
4665  unsigned int groupIdCol =
4666  planTable.tableView_->findCol(IterateTable::planTableCols_.GroupID_);
4667  unsigned int cmdTypeCol =
4668  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandType_);
4669 
4670  unsigned int targetGroupIdCol =
4671  targetTable.tableView_->findCol(IterateTable::targetCols_.GroupID_);
4672  unsigned int targetTableCol =
4673  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLink_);
4674  unsigned int targetUIDCol =
4675  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLinkUID_);
4676 
4677  std::string groupLinkIndex =
4678  planTable.tableView_->getColumnInfo(groupIdCol).getChildLinkIndex();
4679  __SUP_COUT__ << "groupLinkIndex: " << groupLinkIndex << __E__;
4680 
4681  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> commandUidLink;
4682  {
4683  bool isGroup; // local because we know is uid link
4684  planTable.tableView_->getChildLink(
4685  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandLink_),
4686  isGroup,
4687  commandUidLink);
4688  }
4689 
4690  unsigned int cmdRow, cmdCol;
4691  std::string targetGroupName;
4692 
4693  // Reset existing plan commands
4694  {
4695  std::string targetUID, cmdType;
4696 
4697  for(unsigned int row = 0; row < planTable.tableView_->getNumberOfRows();
4698  ++row)
4699  {
4700  targetUID = planTable.tableView_
4701  ->getDataView()[row][planTable.tableView_->getColUID()];
4702  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
4703 
4704  // remove command from plan group.. if no more groups, delete
4705  if(planTable.tableView_->isEntryInGroup(row, groupLinkIndex, groupName))
4706  {
4707  __SUP_COUT__ << "Removing." << __E__;
4708 
4709  // delete linked command
4710  // find linked UID in table (mapped by type)
4711  cmdType = planTable.tableView_->getDataView()[row][cmdTypeCol];
4712  auto cmdTypeTableIt = IterateTable::commandToTableMap_.find(cmdType);
4713  if(cmdTypeTableIt != IterateTable::commandToTableMap_.end() &&
4714  cmdTypeTableIt->second !=
4715  "") // skip if invalid command type or if no command parameter table
4716  {
4717  TableEditStruct& cmdTypeTableEdit =
4718  commandTableToEditMap.at(cmdTypeTableIt->second);
4719  cmdRow = cmdTypeTableEdit.tableView_->findRow(
4720  cmdTypeTableEdit.tableView_->getColUID(),
4721  planTable.tableView_
4722  ->getDataView()[row][commandUidLink.second]);
4723 
4724  // before deleting row...
4725  // look for target group
4726  // remove all targets in group
4727  try
4728  {
4729  cmdCol = cmdTypeTableEdit.tableView_->findCol(
4730  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
4731  targetGroupName = cmdTypeTableEdit.tableView_
4732  ->getDataView()[cmdRow][cmdCol];
4733 
4734  for(unsigned int trow = 0;
4735  trow < targetTable.tableView_->getNumberOfRows();
4736  ++trow)
4737  {
4738  // remove command from target group..
4739  if(targetTable.tableView_->isEntryInGroup(
4740  trow,
4741  cmdTypeTableEdit.tableView_->getColumnInfo(cmdCol)
4742  .getChildLinkIndex(),
4743  targetGroupName))
4744  {
4745  __SUP_COUT__ << "Removing target." << __E__;
4746  // remove command entry in plan table
4747  if(targetTable.tableView_->removeRowFromGroup(
4748  trow,
4749  targetGroupIdCol,
4750  targetGroupName,
4751  true /*deleteRowIfNoGroup*/))
4752  --trow; // since row was deleted, go back!
4753  }
4754  }
4755  }
4756  catch(...)
4757  {
4758  __SUP_COUT__ << "No targets." << __E__;
4759  }
4760 
4761  // now no more targets, delete row
4762 
4763  cmdTypeTableEdit.tableView_->deleteRow(cmdRow);
4764 
4765  cmdTypeTableEdit.modified_ = true;
4766  }
4767 
4768  // remove command entry in plan table
4769  if(planTable.tableView_->removeRowFromGroup(
4770  row, groupIdCol, groupName, true /*deleteRowIfNoGroup*/))
4771  --row; // since row was deleted, go back!
4772  }
4773  }
4774  }
4775 
4776  // Done resetting existing plan
4777  // Now save new commands
4778 
4779  std::vector<IterateTable::Command> commands;
4780 
4781  // extract command sequence and add to table
4782  // into vector with type, and params
4783  {
4784  std::istringstream f(commandString);
4785  std::string commandSubString, paramSubString, paramValue;
4786  int i;
4787  while(getline(f, commandSubString, ';'))
4788  {
4789  __SUP_COUTT__ << "commandSubString " << commandSubString << __E__;
4790  std::istringstream g(commandSubString);
4791 
4792  i = 0;
4793  while(getline(g, paramSubString, ','))
4794  {
4795  __SUP_COUTT__ << "paramSubString " << paramSubString << __E__;
4796  if(i == 0) // type
4797  {
4798  if(paramSubString != "type")
4799  {
4800  __SUP_SS__ << "Invalid command sequence" << __E__;
4801  __SS_THROW__;
4802  }
4803  // create command object
4804  commands.push_back(IterateTable::Command());
4805 
4806  getline(g, paramValue, ',');
4807  ++i;
4808  __SUP_COUTT__ << "paramValue " << paramValue << __E__;
4809  commands.back().type_ = paramValue;
4810  }
4811  else // params
4812  {
4813  getline(g, paramValue, ',');
4814  ++i;
4815  __SUP_COUTT__ << "paramValue " << paramValue << __E__;
4816 
4817  commands.back().params_.emplace(
4818  std::pair<std::string /*param name*/,
4819  std::string /*param value*/>(
4820  paramSubString,
4821  StringMacros::decodeURIComponent(paramValue)));
4822  }
4823 
4824  ++i;
4825  }
4826  }
4827 
4828  } // end extract command sequence
4829 
4830  __SUP_COUT__ << "commands size " << commands.size() << __E__;
4831 
4832  // at this point, have extracted commands
4833 
4834  // now save commands to plan group
4835  // group should be "<plan>-Plan"
4836 
4837  unsigned int row, tgtRow;
4838  unsigned int targetIndex;
4839  std::string targetStr, cmdUID;
4840 
4841  for(auto& command : commands)
4842  {
4843  __SUP_COUT__ << "command " << command.type_ << __E__;
4844  __SUP_COUT__ << "table " << IterateTable::commandToTableMap_.at(command.type_)
4845  << __E__;
4846 
4847  // create command entry at plan level
4848  row = planTable.tableView_->addRow(
4849  author, true /*incrementUniqueData*/, "planCommand");
4850  planTable.tableView_->addRowToGroup(row, groupIdCol, groupName);
4851 
4852  // set command type
4853  planTable.tableView_->setURIEncodedValue(command.type_, row, cmdTypeCol);
4854 
4855  // set command status true
4856  planTable.tableView_->setValueAsString(
4857  "1", row, planTable.tableView_->getColStatus());
4858 
4859  // create command specifics
4860  auto cmdTypeTableIt = IterateTable::commandToTableMap_.find(command.type_);
4861  if(cmdTypeTableIt != IterateTable::commandToTableMap_.end() &&
4862  cmdTypeTableIt->second !=
4863  "") // skip if invalid command type or if no command parameter table
4864  {
4865  TableEditStruct& cmdTypeTableEdit =
4866  commandTableToEditMap.at(cmdTypeTableIt->second);
4867  __SUP_COUT__ << "table " << cmdTypeTableEdit.tableName_ << __E__;
4868 
4869  // at this point have table, tempVersion, and createdFlag
4870 
4871  // create command parameter entry at command level
4872  cmdRow = cmdTypeTableEdit.tableView_->addRow(
4873  author, true /*incrementUniqueData*/, command.type_ + "_COMMAND_");
4874 
4875  // parameters are linked
4876  // now set value of all parameters
4877  // find parameter column, and set value
4878  // if special target parameter, extract targets
4879  for(auto& param : command.params_)
4880  {
4881  __SUP_COUT__ << "\t param " << param.first << " : " << param.second
4882  << __E__;
4883 
4884  if(param.first == IterateTable::targetParams_.Tables_)
4885  {
4886  __SUP_COUT__ << "\t\t found target tables" << __E__;
4887  std::istringstream f(param.second);
4888 
4889  targetIndex = 0;
4890  while(getline(f, targetStr, '='))
4891  {
4892  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
4893  if(!command.targets_.size() ||
4894  command.targets_.back().table_ != "")
4895  {
4896  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
4897  << __E__;
4898  // make new target
4899  command.addTarget();
4900  command.targets_.back().table_ = targetStr;
4901  }
4902  else // file existing target
4903  command.targets_[targetIndex++].table_ = targetStr;
4904  }
4905 
4906  continue; // go to next parameter
4907  }
4908 
4909  if(param.first == IterateTable::targetParams_.UIDs_)
4910  {
4911  __SUP_COUT__ << "\t\t found target UIDs" << __E__;
4912  std::istringstream f(param.second);
4913 
4914  targetIndex = 0;
4915  while(getline(f, targetStr, '='))
4916  {
4917  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
4918  if(!command.targets_.size() ||
4919  command.targets_.back().UID_ != "")
4920  {
4921  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
4922  << __E__;
4923  // make new target
4924  command.addTarget();
4925  command.targets_.back().UID_ = targetStr;
4926  }
4927  else // file existing target
4928  command.targets_[targetIndex++].UID_ = targetStr;
4929  }
4930  continue;
4931  }
4932 
4933  cmdCol = cmdTypeTableEdit.tableView_->findCol(param.first);
4934 
4935  __SUP_COUT__ << "param col " << cmdCol << __E__;
4936 
4937  cmdTypeTableEdit.tableView_->setURIEncodedValue(
4938  param.second, cmdRow, cmdCol);
4939  } // end parameter loop
4940 
4941  cmdUID =
4942  cmdTypeTableEdit.tableView_
4943  ->getDataView()[cmdRow][cmdTypeTableEdit.tableView_->getColUID()];
4944 
4945  if(command.targets_.size())
4946  {
4947  // if targets, create group in target table
4948 
4949  __SUP_COUT__ << "targets found for command UID=" << cmdUID << __E__;
4950 
4951  // create link from command table to target
4952  cmdCol = cmdTypeTableEdit.tableView_->findCol(
4953  IterateTable::commandTargetCols_.TargetsLink_);
4954  cmdTypeTableEdit.tableView_->setValueAsString(
4955  IterateTable::TARGET_TABLE, cmdRow, cmdCol);
4956 
4957  cmdCol = cmdTypeTableEdit.tableView_->findCol(
4958  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
4959  cmdTypeTableEdit.tableView_->setValueAsString(
4960  cmdUID + "_Targets", cmdRow, cmdCol);
4961 
4962  // create row(s) for each target in target table with correct groupID
4963 
4964  for(const auto& target : command.targets_)
4965  {
4966  __SUP_COUT__ << target.table_ << " " << target.UID_ << __E__;
4967 
4968  // create target entry in target table in group
4969  tgtRow = targetTable.tableView_->addRow(
4970  author, true /*incrementUniqueData*/, "commandTarget");
4971  targetTable.tableView_->addRowToGroup(
4972  tgtRow, targetGroupIdCol, cmdUID + "_Targets");
4973 
4974  // set target table
4975  targetTable.tableView_->setValueAsString(
4976  target.table_, tgtRow, targetTableCol);
4977 
4978  // set target UID
4979  targetTable.tableView_->setValueAsString(
4980  target.UID_, tgtRow, targetUIDCol);
4981  }
4982  } // end target handling
4983 
4984  // add link at plan level to created UID
4985  planTable.tableView_->setValueAsString(
4986  cmdTypeTableEdit.tableName_, row, commandUidLink.first);
4987  planTable.tableView_->setValueAsString(
4988  cmdUID, row, commandUidLink.second);
4989 
4990  __SUP_COUT__ << "linked to uid = " << cmdUID << __E__;
4991 
4992  cmdTypeTableEdit.modified_ = true;
4993  } // done with command specifics
4994 
4995  } // end command loop
4996 
4997  // commands are created in the temporary tables
4998  // validate with init
4999 
5000  planTable.tableView_->print();
5001  planTable.tableView_->init(); // verify new table (throws runtime_errors)
5002 
5003  __SUP_COUT__ << "requestType tables:" << __E__;
5004 
5005  for(auto& modifiedConfig : commandTableToEditMap)
5006  {
5007  __SUP_COUTV__(modifiedConfig.second.modified_);
5008  modifiedConfig.second.tableView_->print();
5009  modifiedConfig.second.tableView_->init();
5010  }
5011 
5012  targetTable.tableView_->print();
5013  targetTable.tableView_->init(); // verify new table (throws runtime_errors)
5014 
5015  } // end try for plan
5016  catch(...)
5017  {
5018  __SUP_COUT__ << "Handling command table errors while saving. Erasing all newly "
5019  "created versions."
5020  << __E__;
5021 
5022  // erase all temporary tables if created here
5023 
5024  if(planTable.createdTemporaryVersion_) // if temporary version created here
5025  {
5026  __SUP_COUT__ << "Erasing temporary version " << planTable.tableName_ << "-v"
5027  << planTable.temporaryVersion_ << __E__;
5028  // erase with proper version management
5029  cfgMgr->eraseTemporaryVersion(planTable.tableName_,
5030  planTable.temporaryVersion_);
5031  }
5032 
5033  if(targetTable.createdTemporaryVersion_) // if temporary version created here
5034  {
5035  __SUP_COUT__ << "Erasing temporary version " << targetTable.tableName_ << "-v"
5036  << targetTable.temporaryVersion_ << __E__;
5037  // erase with proper version management
5038  cfgMgr->eraseTemporaryVersion(targetTable.tableName_,
5039  targetTable.temporaryVersion_);
5040  }
5041 
5042  for(auto& modifiedConfig : commandTableToEditMap)
5043  {
5044  if(modifiedConfig.second
5045  .createdTemporaryVersion_) // if temporary version created here
5046  {
5047  __SUP_COUT__ << "Erasing temporary version "
5048  << modifiedConfig.second.tableName_ << "-v"
5049  << modifiedConfig.second.temporaryVersion_ << __E__;
5050  // erase with proper version management
5051  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
5052  modifiedConfig.second.temporaryVersion_);
5053  }
5054  }
5055 
5056  throw; // re-throw
5057  }
5058 
5059  // all edits are complete and tables verified
5060  // need to save all edits properly
5061  // if not modified, discard
5062 
5063  TableVersion finalVersion = ConfigurationSupervisorBase::saveModifiedVersionXML(
5064  xmlOut,
5065  cfgMgr,
5066  planTable.tableName_,
5067  planTable.originalVersion_,
5068  true /*make temporary*/,
5069  planTable.table_,
5070  planTable.temporaryVersion_,
5071  true /*ignoreDuplicates*/); // save temporary version properly
5072 
5073  __SUP_COUT__ << "Final plan version is " << planTable.tableName_ << "-v"
5074  << finalVersion << __E__;
5075 
5076  finalVersion = ConfigurationSupervisorBase::saveModifiedVersionXML(
5077  xmlOut,
5078  cfgMgr,
5079  targetTable.tableName_,
5080  targetTable.originalVersion_,
5081  true /*make temporary*/,
5082  targetTable.table_,
5083  targetTable.temporaryVersion_,
5084  true /*ignoreDuplicates*/); // save temporary version properly
5085 
5086  __SUP_COUT__ << "Final target version is " << targetTable.tableName_ << "-v"
5087  << finalVersion << __E__;
5088 
5089  for(auto& modifiedConfig : commandTableToEditMap)
5090  {
5091  if(!modifiedConfig.second.modified_)
5092  {
5093  if(modifiedConfig.second
5094  .createdTemporaryVersion_) // if temporary version created here
5095  {
5096  __SUP_COUT__ << "Erasing unmodified temporary version "
5097  << modifiedConfig.second.tableName_ << "-v"
5098  << modifiedConfig.second.temporaryVersion_ << __E__;
5099  // erase with proper version management
5100  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
5101  modifiedConfig.second.temporaryVersion_);
5102  }
5103  continue;
5104  }
5105 
5106  finalVersion = ConfigurationSupervisorBase::saveModifiedVersionXML(
5107  xmlOut,
5108  cfgMgr,
5109  modifiedConfig.second.tableName_,
5110  modifiedConfig.second.originalVersion_,
5111  true /*make temporary*/,
5112  modifiedConfig.second.table_,
5113  modifiedConfig.second.temporaryVersion_,
5114  true /*ignoreDuplicates*/); // save temporary version properly
5115 
5116  __SUP_COUT__ << "Final version is " << modifiedConfig.second.tableName_ << "-v"
5117  << finalVersion << __E__;
5118  }
5119 
5120  handleFillModifiedTablesXML(xmlOut, cfgMgr);
5121 } // end handleSavePlanCommandSequenceXML()
5122 catch(std::runtime_error& e)
5123 {
5124  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << e.what() << __E__;
5125  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5126  xmlOut.addTextElementToData("Error", ss.str());
5127 }
5128 catch(...)
5129 {
5130  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << __E__;
5131  try
5132  {
5133  throw;
5134  } //one more try to printout extra info
5135  catch(const std::exception& e)
5136  {
5137  ss << "Exception message: " << e.what();
5138  }
5139  catch(...)
5140  {
5141  }
5142  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5143  xmlOut.addTextElementToData("Error", ss.str());
5144 } // end handleSavePlanCommandSequenceXML() catch
5145 
5146 //==============================================================================
5156 void ConfigurationGUISupervisor::handleSaveTreeNodeEditXML(HttpXmlDocument& xmlOut,
5157  ConfigurationManagerRW* cfgMgr,
5158  const std::string& tableName,
5159  TableVersion version,
5160  const std::string& type,
5161  const std::string& uid,
5162  const std::string& colName,
5163  const std::string& newValue,
5164  const std::string& author)
5165 try
5166 {
5167  __SUP_COUT__ << "Editing table " << tableName << "(" << version << ") uid=" << uid
5168  << " type=" << type << __E__;
5169 
5170  // get the current table/version
5171  // check if the value is new
5172  // if new edit value (in a temporary version only)
5173 
5174  // get table and activate target version
5175  TableBase* table = cfgMgr->getTableByName(tableName);
5176  try
5177  {
5178  table->setActiveView(version);
5179  }
5180  catch(...)
5181  {
5182  if(version.isTemporaryVersion())
5183  throw; // if temporary, there is no hope to find lost version
5184 
5185  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
5186  << version << __E__;
5187  cfgMgr->getVersionedTableByName(tableName, version);
5188  }
5189 
5190  __SUP_COUT__ << "Active version is " << table->getViewVersion() << __E__;
5191  __SUP_COUTTV__(table->getView().getComment());
5192 
5193  if(version != table->getViewVersion())
5194  {
5195  __SUP_SS__ << "Target table version (" << version
5196  << ") is not the currently active version (" << table->getViewVersion()
5197  << "). Try refreshing the tree." << __E__;
5198  __SS_THROW__;
5199  }
5200 
5201  unsigned int col = -1;
5202  if(type == "uid" || type == "delete-uid" || type == "tree-copy")
5203  col = table->getView().getColUID();
5204  else if(type == "node-comment")
5205  col = table->getView().findCol(TableViewColumnInfo::COL_NAME_COMMENT);
5206  else if(type == "link-UID" || type == "link-GroupID" || type == "value" ||
5207  type == "value-groupid" || type == "value-bool" || type == "value-bitmap")
5208  col = table->getView().findCol(colName);
5209  else if(type == "table" || type == "link-comment" || type == "table-newGroupRow" ||
5210  type == "table-newUIDRow" || type == "table-newRow")
5211  ; // column N/A
5212  else
5213  {
5214  __SUP_SS__ << "Impossible! Unrecognized edit type: " << type << __E__;
5215  __SS_THROW__;
5216  }
5217 
5218  // check if the comment value is new before making temporary version
5219  if(type == "table" || type == "link-comment")
5220  {
5221  // editing comment, so check if comment is different
5222  if(table->getView().isURIEncodedCommentTheSame(newValue))
5223  {
5224  __SUP_SS__ << "Comment '" << newValue
5225  << "' is the same as the current comment. No need to save change."
5226  << __E__;
5227  __SS_THROW__;
5228  }
5229  }
5230 
5231  // version handling:
5232  // always make a new temporary-version from source-version
5233  // edit temporary-version
5234  // if edit fails
5235  // delete temporary-version
5236  // else
5237  // return new temporary-version
5238  // if source-version was temporary
5239  // then delete source-version
5240 
5241  TableVersion temporaryVersion = table->createTemporaryView(version);
5242 
5243  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
5244 
5245  TableView* cfgView = table->getTemporaryView(temporaryVersion);
5246  cfgView->init(); // prepare maps
5247 
5248  __SUP_COUTTV__(table->getView().getComment());
5249 
5250  // edit/verify new table (throws runtime_errors)
5251  try
5252  {
5253  // have view so edit it
5254  if(type == "table" || type == "link-comment")
5255  {
5256  // edit comment
5257  cfgView->setURIEncodedComment(newValue);
5258  }
5259  else if(type == "table-newRow" || type == "table-newUIDRow")
5260  {
5261  // add row
5262  unsigned int row = cfgView->addRow(
5263  author, true /*incrementUniqueData*/, newValue /*baseNameAutoUID*/);
5264 
5265  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
5266  try
5267  {
5268  col = cfgView->getColStatus();
5269  cfgView->setValueAsString("1", row, col);
5270  }
5271  catch(...)
5272  {
5273  } // if not, ignore
5274 
5275  // set UID value
5276  cfgView->setURIEncodedValue(newValue, row, cfgView->getColUID());
5277  }
5278  else if(type == "table-newGroupRow")
5279  {
5280  // get index value and group id value
5281  unsigned int csvIndex = newValue.find(',');
5282 
5283  std::string linkIndex = newValue.substr(0, csvIndex);
5284  std::string groupId = newValue.substr(csvIndex + 1);
5285 
5286  // get new row UID value from second part of string
5287  csvIndex = groupId.find(',');
5288  std::string newRowUID = groupId.substr(csvIndex + 1);
5289  groupId = groupId.substr(0, csvIndex);
5290 
5291  __SUP_COUT__ << "newValue " << linkIndex << "," << groupId << "," << newRowUID
5292  << __E__;
5293 
5294  // add row
5295  unsigned int row = cfgView->addRow(author,
5296  true /*incrementUniqueData*/,
5297  newRowUID /*baseNameAutoID*/,
5298  -1 /* rowToAdd */,
5299  linkIndex,
5300  groupId);
5301 
5302  // set UID value
5303  cfgView->setURIEncodedValue(newRowUID, row, cfgView->getColUID());
5304 
5305  // find groupId column from link index
5306  col = cfgView->getLinkGroupIDColumn(linkIndex);
5307 
5308  // set group id
5309  cfgView->setURIEncodedValue(groupId, row, col);
5310 
5311  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
5312  try
5313  {
5314  col = cfgView->getColStatus();
5315  cfgView->setValueAsString("1", row, col);
5316  }
5317  catch(...)
5318  {
5319  } // if not, ignore
5320  }
5321  else if(type == "delete-uid")
5322  {
5323  // delete row
5324  unsigned int row = cfgView->findRow(col, uid);
5325  cfgView->deleteRow(row);
5326  }
5327  else if(type == "tree-copy")
5328  {
5329  // recursively copy to depth
5330  __COUTV__(newValue);
5331  std::vector<std::string> paramArray =
5332  StringMacros::getVectorFromString(newValue);
5333  __COUTV__(StringMacros::vectorToString(paramArray));
5334 
5335  if(paramArray.size() != 2)
5336  {
5337  __SS__ << "Illegal parameters for tree copy request: must be number of "
5338  "copy instances & depth of copy."
5339  << __E__;
5340  __SS_THROW__;
5341  }
5342 
5343  unsigned int row = cfgView->findRow(col, uid);
5344  __COUTV__(uid);
5345  __COUTV__(row);
5346  unsigned int numberOfInstances = atoi(paramArray[0].c_str());
5347  unsigned int depth = atoi(paramArray[1].c_str());
5348  __COUTV__(depth);
5349  __COUTV__(numberOfInstances);
5350  if(numberOfInstances > 1000)
5351  {
5352  __SS__ << "Illegal parameters - the maximum number of copy instances is "
5353  "1000. Number of instances provided was "
5354  << numberOfInstances << __E__;
5355  __SS_THROW__;
5356  }
5357 
5358  std::map<std::string /*modified table*/, TableVersion /* modified version */>
5359  modifiedTablesMap = cfgMgr->getActiveVersions(); // handling copied from
5360  // ConfigurationGUISupervisor::handleFillModifiedTablesXML()
5361  ConfigurationSupervisorBase::recursiveCopyTreeUIDNode(xmlOut,
5362  cfgMgr,
5363  modifiedTablesMap,
5364  depth,
5365  depth,
5366  numberOfInstances,
5367  cfgView,
5368  uid);
5369  }
5370  else if(type == "uid" || type == "value" || type == "value-groupid" ||
5371  type == "value-bool" || type == "value-bitmap" || type == "node-comment")
5372  {
5373  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
5374  if(!cfgView->setURIEncodedValue(newValue, row, col, author))
5375  {
5376  // no change! so discard
5377  __SUP_SS__ << "Value '" << newValue
5378  << "' is the same as the current value. No need to save "
5379  "change to tree node."
5380  << __E__;
5381  __SS_THROW__;
5382  }
5383  }
5384  else if(type == "link-UID" || type == "link-GroupID")
5385  {
5386  bool isGroup;
5387  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
5388  if(!cfgView->getChildLink(col, isGroup, linkPair))
5389  {
5390  // not a link ?!
5391  __SUP_SS__ << "Col '" << colName << "' is not a link column." << __E__;
5392  __SS_THROW__;
5393  }
5394 
5395  __SUP_COUT__ << "linkPair " << linkPair.first << "," << linkPair.second
5396  << __E__;
5397 
5398  std::string linkIndex = cfgView->getColumnInfo(col).getChildLinkIndex();
5399 
5400  __SUP_COUT__ << "linkIndex " << linkIndex << __E__;
5401 
5402  // find table value and id value
5403  unsigned int csvIndexStart = 0, csvIndex = newValue.find(',');
5404 
5405  std::string newTable = newValue.substr(csvIndexStart, csvIndex);
5406  csvIndexStart = csvIndex + 1;
5407  csvIndex = newValue.find(',', csvIndexStart);
5408  std::string newLinkId = newValue.substr(
5409  csvIndexStart,
5410  csvIndex -
5411  csvIndexStart); // if no more commas will take the rest of string
5412 
5413  __SUP_COUT__ << "newValue " << newTable << "," << newLinkId << __E__;
5414 
5415  // change target table in two parts
5416  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
5417  bool changed = false;
5418  bool needSecondaryChange = (type == "link-GroupID");
5419 
5420  if(!cfgView->setURIEncodedValue(newTable, row, linkPair.first, author))
5421  {
5422  // no change
5423  __SUP_COUT__ << "Value '" << newTable
5424  << "' is the same as the current value." << __E__;
5425  }
5426  else
5427  {
5428  changed = true;
5429  // do NOT need secondary change for UID
5430  }
5431 
5432  std::string originalValue = cfgView->getValueAsString(row, linkPair.second);
5433  if(!cfgView->setURIEncodedValue(newLinkId, row, linkPair.second, author))
5434  {
5435  // no change
5436  __SUP_COUT__ << "Value '" << newLinkId
5437  << "' is the same as the current value." << __E__;
5438  }
5439  else
5440  {
5441  if(!changed)
5442  needSecondaryChange =
5443  true; // if table was unchanged, then need secondary change for
5444  // UID (groupID is already assumed needed)
5445  changed = true;
5446  }
5447 
5448  if(needSecondaryChange) // do secondary changes to child table target
5449  {
5450  bool secondaryChanged = false;
5451  bool defaultIsInGroup =
5452  false; // use to indicate if a recent new member was created
5453 
5454  // first close out main target table
5455  if(!changed) // if no changes throw out new version
5456  {
5457  __SUP_COUT__ << "No changes to primary view. Erasing temporary table."
5458  << __E__;
5459  table->eraseView(temporaryVersion);
5460  }
5461  else // if changes, save it
5462  {
5463  try
5464  {
5465  cfgView->init(); // verify new table (throws runtime_errors)
5466 
5467  ConfigurationSupervisorBase::saveModifiedVersionXML(
5468  xmlOut,
5469  cfgMgr,
5470  tableName,
5471  version,
5472  true /*make temporary*/,
5473  table,
5474  temporaryVersion,
5475  true /*ignoreDuplicates*/); // save
5476  // temporary
5477  // version
5478  // properly
5479  }
5480  catch(std::runtime_error&
5481  e) // erase temporary view before re-throwing error
5482  {
5483  __SUP_COUT__ << "Caught error while editing main table. Erasing "
5484  "temporary version."
5485  << __E__;
5486  table->eraseView(temporaryVersion);
5487  changed = false; // undo changed bool
5488 
5489  // send warning so that, secondary table can still be changed
5490  xmlOut.addTextElementToData(
5491  "Warning",
5492  "Error saving primary tree node! " + std::string(e.what()));
5493  }
5494  }
5495 
5496  // now, onto linked table
5497 
5498  // get the current linked table/version
5499  // check if the value is new
5500  // if new edit value (in a temporary version only)
5501 
5502  __SUP_COUTV__(newValue);
5503  csvIndexStart = csvIndex + 1;
5504  csvIndex = newValue.find(',', csvIndexStart);
5505  version = TableVersion(newValue.substr(
5506  csvIndexStart, csvIndex - csvIndexStart)); // if no more commas will
5507  // take the rest of string
5508 
5509  if(newTable == TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5510  {
5511  // done, since init was already tested
5512  // the result should be purposely DISCONNECTED link
5513  return;
5514  }
5515 
5516  // get table and activate target version
5517  table = cfgMgr->getTableByName(newTable);
5518  try
5519  {
5520  table->setActiveView(version);
5521  }
5522  catch(...)
5523  {
5524  if(version.isTemporaryVersion())
5525  throw; // if temporary, there is no hope to find lost version
5526 
5527  __SUP_COUT__ << "Failed to find stored version, so attempting to "
5528  "load version: "
5529  << newTable << " v" << version << __E__;
5530  cfgMgr->getVersionedTableByName(newTable, version);
5531  }
5532 
5533  __SUP_COUT__ << newTable << " active version is "
5534  << table->getViewVersion() << __E__;
5535 
5536  if(version != table->getViewVersion())
5537  {
5538  __SUP_SS__;
5539  if(version.isMockupVersion())
5540  ss << "Target table '" << newTable
5541  << "' is likely not a member of the current table group "
5542  << "since the mock-up version was not successfully loaded. "
5543  << "\n\n"
5544  <<
5545  // same as ConfigurationGUI.html L:9833
5546  (std::string("") +
5547  "To add a table to a group, click the group name to go to "
5548  "the " +
5549  "group view, then click 'Add/Remove/Modify Member Tables.' "
5550  "You " +
5551  "can then add or remove tables and save the new group." +
5552  "\n\n" +
5553  "OR!!! Click the following button to add the table '" +
5554  newTable +
5555  "' to the currently active Configuration Group: " +
5556  "<input type='button' style='color:black !important;' " +
5557  "title='Click to add table to the active Configuration "
5558  "Group' " +
5559  "onclick='addTableToConfigurationGroup(\"" + newTable +
5560  "\"); Debug.closeErrorPop();event.stopPropagation();' "
5561  "value='Add Table'>" +
5562  "</input>")
5563  << __E__;
5564  else
5565  ss << "Target table version (" << version
5566  << ") is not the currently active version ("
5567  << table->getViewVersion() << "). Try refreshing the tree."
5568  << __E__;
5569  __SS_THROW__;
5570  }
5571 
5572  // create temporary version for editing
5573  temporaryVersion = table->createTemporaryView(version);
5574 
5575  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
5576 
5577  cfgView = table->getTemporaryView(temporaryVersion);
5578 
5579  cfgView->init(); // prepare column lookup map
5580 
5581  if(type == "link-UID")
5582  {
5583  // handle UID links slightly differently
5584  // when editing link-UID,.. if specified name does not exist in child
5585  // table, then change the UID in the child table (rename target
5586  // record).
5587  // Otherwise, it is impossible to rename unique links targets in the
5588  // tree-view GUI.
5589 
5590  col = cfgView->getColUID();
5591  __SUP_COUT__ << "target col " << col << __E__;
5592 
5593  unsigned int row = -1;
5594  try
5595  {
5596  row = cfgView->findRow(col, newLinkId);
5597  }
5598  catch(...) // ignore not found error
5599  {
5600  }
5601  if(row == (unsigned int)-1) // if row not found then add a row
5602  {
5603  __SUP_COUT__ << "New link UID '" << newLinkId
5604  << "' was not found, so attempting to change UID of "
5605  "target record '"
5606  << originalValue << "'" << __E__;
5607  try
5608  {
5609  row = cfgView->findRow(col, originalValue);
5610  if(cfgView->setURIEncodedValue(newLinkId, row, col, author))
5611  {
5612  secondaryChanged = true;
5613  __SUP_COUT__ << "Original target record '"
5614  << originalValue << "' was changed to '"
5615  << newLinkId << "'" << __E__;
5616  }
5617  }
5618  catch(...) // ignore not found error
5619  {
5620  __SUP_COUT__ << "Original target record '" << originalValue
5621  << "' not found." << __E__;
5622  }
5623  }
5624  }
5625  else if(type == "link-GroupID")
5626  {
5627  // handle groupID links slightly differently
5628  // have to look at changing link table too!
5629  // if group ID, set all in member list to be members of group
5630 
5631  col = cfgView->getLinkGroupIDColumn(linkIndex);
5632 
5633  __SUP_COUT__ << "target col " << col << __E__;
5634 
5635  // extract vector of members to be
5636  std::vector<std::string> memberUIDs;
5637  do
5638  {
5639  csvIndexStart = csvIndex + 1;
5640  csvIndex = newValue.find(',', csvIndexStart);
5641  memberUIDs.push_back(
5642  newValue.substr(csvIndexStart, csvIndex - csvIndexStart));
5643  __SUP_COUT__ << "memberUIDs: " << memberUIDs.back() << __E__;
5644  } while(csvIndex !=
5645  (unsigned int)std::string::npos); // no more commas
5646 
5647  // for each row,
5648  // check if should be in group
5649  // if should be but is not
5650  // add to group, CHANGE
5651  // if should not be but is
5652  // remove from group, CHANGE
5653  //
5654 
5655  std::string targetUID;
5656  bool shouldBeInGroup;
5657  bool isInGroup;
5658 
5659  for(unsigned int row = 0; row < cfgView->getNumberOfRows(); ++row)
5660  {
5661  targetUID = cfgView->getDataView()[row][cfgView->getColUID()];
5662  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
5663 
5664  shouldBeInGroup = false;
5665  for(unsigned int i = 0; i < memberUIDs.size(); ++i)
5666  if(targetUID == memberUIDs[i])
5667  {
5668  // found in member uid list
5669  shouldBeInGroup = true;
5670  break;
5671  }
5672 
5673  isInGroup = cfgView->isEntryInGroup(row, linkIndex, newLinkId);
5674 
5675  // if should be but is not
5676  if(shouldBeInGroup && !isInGroup)
5677  {
5678  __SUP_COUT__ << "Changed to YES: " << row << __E__;
5679  secondaryChanged = true;
5680 
5681  cfgView->addRowToGroup(row, col, newLinkId);
5682 
5683  } // if should not be but is
5684  else if(!shouldBeInGroup && isInGroup)
5685  {
5686  __SUP_COUT__ << "Changed to NO: " << row << __E__;
5687  secondaryChanged = true;
5688 
5689  cfgView->removeRowFromGroup(row, col, newLinkId);
5690  }
5691  else if(targetUID ==
5692  cfgView
5693  ->getDefaultRowValues()[cfgView->getColUID()] &&
5694  isInGroup)
5695  {
5696  // use to indicate if a recent new member was created
5697  defaultIsInGroup = true;
5698  }
5699  }
5700  } // end (type == "link-GroupID")
5701 
5702  // first close out main target table
5703  if(!secondaryChanged) // if no changes throw out new version
5704  {
5705  __SUP_COUT__
5706  << "No changes to secondary view. Erasing temporary table."
5707  << __E__;
5708  table->eraseView(temporaryVersion);
5709  }
5710  else // if changes, save it
5711  {
5712  try
5713  {
5714  cfgView->init(); // verify new table (throws runtime_errors)
5715 
5716  ConfigurationSupervisorBase::saveModifiedVersionXML(
5717  xmlOut,
5718  cfgMgr,
5719  newTable,
5720  version,
5721  true /*make temporary*/,
5722  table,
5723  temporaryVersion,
5724  true /*ignoreDuplicates*/); // save
5725  // temporary
5726  // version
5727  // properly
5728  }
5729  catch(std::runtime_error&
5730  e) // erase temporary view before re-throwing error
5731  {
5732  __SUP_COUT__ << "Caught error while editing secondary table. "
5733  "Erasing temporary version."
5734  << __E__;
5735  table->eraseView(temporaryVersion);
5736  secondaryChanged = false; // undo changed bool
5737 
5738  // send warning so that, secondary table can still be changed
5739  xmlOut.addTextElementToData(
5740  "Warning",
5741  "Error saving secondary tree node! " + std::string(e.what()));
5742  }
5743  }
5744 
5745  // Block error message if default is in group, assume new member was just
5746  // created. Blocked because its hard to detect if changes were recently
5747  // made (one idea: to check if all other values are defaults, to assume it
5748  // was just created)
5749  if(0 && !changed && !secondaryChanged && !defaultIsInGroup)
5750  {
5751  __SUP_SS__ << "Link to table '" << newTable << "', linkID '"
5752  << newLinkId
5753  << "', and selected group members are the same as the "
5754  "current value. "
5755  << "No need to save changes to tree." << __E__;
5756  __SS_THROW__;
5757  }
5758 
5759  return; // exit since table inits were already tested
5760  }
5761  else if(0 && !changed) // '0 &&' to block error message because sometimes
5762  // things get setup twice depending on the path of the
5763  // user (e.g. when editing links in tree-view)
5764  { // '0 &&' to block error message also because versions are temporary at
5765  // this point anyway, might as well abuse temporary versions
5766  __SUP_SS__ << "Link to table '" << newTable << "' and linkID '"
5767  << newLinkId
5768  << "' are the same as the current values. No need to save "
5769  "change to tree node."
5770  << __E__;
5771  __SS_THROW__;
5772  }
5773  }
5774 
5775  cfgView->init(); // verify new table (throws runtime_errors)
5776  }
5777  catch(...) // erase temporary view before re-throwing error
5778  {
5779  __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
5780  table->eraseView(temporaryVersion);
5781  throw;
5782  }
5783 
5784  ConfigurationSupervisorBase::saveModifiedVersionXML(
5785  xmlOut,
5786  cfgMgr,
5787  tableName,
5788  version,
5789  true /*make temporary*/,
5790  table,
5791  temporaryVersion,
5792  true /*ignoreDuplicates*/); // save temporary version properly
5793 } //end handleSaveTreeNodeEditXML()
5794 catch(std::runtime_error& e)
5795 {
5796  __SUP_SS__ << "Error saving tree node! " << e.what() << __E__;
5797  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5798  xmlOut.addTextElementToData("Error", ss.str());
5799 }
5800 catch(...)
5801 {
5802  __SUP_SS__ << "Unknown Error saving tree node! " << __E__;
5803  try
5804  {
5805  throw;
5806  } //one more try to printout extra info
5807  catch(const std::exception& e)
5808  {
5809  ss << "Exception message: " << e.what();
5810  }
5811  catch(...)
5812  {
5813  }
5814  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5815  xmlOut.addTextElementToData("Error", ss.str());
5816 } //end handleSaveTreeNodeEditXML() catch
5817 
5818 //==============================================================================
5859 void ConfigurationGUISupervisor::handleGetTableXML(HttpXmlDocument& xmlOut,
5860  ConfigurationManagerRW* cfgMgr,
5861  const std::string& tableName,
5862  TableVersion version,
5863  bool allowIllegalColumns /* = false */,
5864  bool getRawData /* = false */)
5865 try
5866 {
5867  char tmpIntStr[100];
5868  xercesc::DOMElement *parentEl, *subparentEl;
5869 
5870  std::string accumulatedErrors = "";
5871 
5872  if(allowIllegalColumns)
5873  xmlOut.addTextElementToData("allowIllegalColumns", "1");
5874 
5875  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(
5876  allowIllegalColumns /* if allowIllegalColumns, then also refresh */,
5877  allowIllegalColumns ? &accumulatedErrors : 0,
5878  tableName); // filter errors by tableName
5879 
5880  TableBase* table = cfgMgr->getTableByName(tableName);
5881 
5882  if(!getRawData)
5883  {
5884  // send all table names along with
5885  // and check for specific version
5886  xmlOut.addTextElementToData("ExistingTableNames",
5887  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
5888  for(auto& configPair : allTableInfo)
5889  {
5890  xmlOut.addTextElementToData("ExistingTableNames", configPair.first);
5891  if(configPair.first == tableName && // check that version exists
5892  configPair.second.versions_.find(version) ==
5893  configPair.second.versions_.end())
5894  {
5895  __SUP_COUT__ << "Version not found, so using mockup." << __E__;
5896  version = TableVersion(); // use INVALID
5897  }
5898  }
5899  }
5900 
5901  xmlOut.addTextElementToData("TableName", tableName); // table name
5902  xmlOut.addTextElementToData("TableDescription",
5903  table->getTableDescription()); // table name
5904 
5905  // existing table versions
5906  if(!getRawData)
5907  {
5908  // get version aliases for translation
5909  std::map<
5910  std::string /*table name*/,
5911  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
5912  versionAliases;
5913  try
5914  {
5915  // use whatever backbone is currently active
5916  versionAliases = cfgMgr->getVersionAliases();
5917  for(const auto& aliases : versionAliases)
5918  for(const auto& alias : aliases.second)
5919  __SUP_COUTT__ << "ALIAS: " << aliases.first << " " << alias.first
5920  << " ==> " << alias.second << __E__;
5921  }
5922  catch(const std::runtime_error& e)
5923  {
5924  __SUP_COUT__ << "Could not get backbone information for version aliases: "
5925  << e.what() << __E__;
5926  }
5927 
5928  auto tableIterator = versionAliases.find(tableName);
5929 
5930  parentEl = xmlOut.addTextElementToData("TableVersions", "");
5931  for(const TableVersion& v : allTableInfo.at(tableName).versions_)
5932  {
5933  subparentEl =
5934  xmlOut.addTextElementToParent("Version", v.toString(), parentEl);
5935 
5936  if(tableIterator != versionAliases.end())
5937  {
5938  // check if this version has one or many aliases
5939  for(const auto& aliasPair : tableIterator->second)
5940  {
5941  if(v == aliasPair.second)
5942  {
5943  __SUP_COUT__ << "Found Alias " << aliasPair.second << " --> "
5944  << aliasPair.first << __E__;
5945  xmlOut.addTextElementToParent(
5946  "VersionAlias", aliasPair.first, subparentEl);
5947  }
5948  }
5949  }
5950  }
5951  }
5952 
5953  // table columns and then rows (from table view)
5954 
5955  // get view pointer
5956  TableView* tableViewPtr;
5957  if(version.isInvalid()) // use mock-up
5958  {
5959  tableViewPtr = table->getMockupViewP();
5960  }
5961  else // use view version
5962  {
5963  try
5964  {
5965  // locally accumulate 'manageable' errors getting the version to avoid
5966  // reverting to mockup
5967  std::string localAccumulatedErrors = "";
5968  tableViewPtr =
5969  cfgMgr
5970  ->getVersionedTableByName(tableName,
5971  version,
5972  allowIllegalColumns /*looseColumnMatching*/,
5973  &localAccumulatedErrors,
5974  getRawData)
5975  ->getViewP();
5976 
5977  if(getRawData)
5978  {
5979  xmlOut.addTextElementToData("TableRawData",
5980  tableViewPtr->getSourceRawData());
5981 
5982  const std::set<std::string>& srcColNames =
5983  tableViewPtr->getSourceColumnNames();
5984  for(auto& srcColName : srcColNames)
5985  xmlOut.addTextElementToData("ColumnHeader", srcColName);
5986 
5987  if(!version.isTemporaryVersion())
5988  {
5989  // if version is temporary, view is already ok
5990  table->eraseView(
5991  version); // clear so that the next get will fill the table
5992  tableViewPtr = cfgMgr
5993  ->getVersionedTableByName(
5994  tableName,
5995  version,
5996  allowIllegalColumns /*looseColumnMatching*/,
5997  &localAccumulatedErrors,
5998  false /* getRawData */)
5999  ->getViewP();
6000  }
6001  } // end rawData handling
6002 
6003  if(localAccumulatedErrors != "")
6004  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
6005  }
6006  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
6007  {
6008  __SUP_SS__ << "Failed to get table " << tableName << " version " << version
6009  << "... defaulting to mock-up! " << __E__;
6010  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
6011 
6012  __SUP_COUT_ERR__ << "\n" << ss.str();
6013  version = TableVersion();
6014  tableViewPtr = table->getMockupViewP();
6015 
6016  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6017  }
6018  catch(...) // default to mock-up for fail-safe in GUI editor
6019  {
6020  __SUP_SS__ << "Failed to get table " << tableName << " version: " << version
6021  << "... defaulting to mock-up! "
6022  << "(You may want to try again to see what was partially loaded "
6023  "into cache before failure. "
6024  << "If you think, the failure is due to a column name change, "
6025  << "you can also try to Copy the failing view to the new column "
6026  "names using "
6027  << "'Copy and Move' functionality.)" << __E__;
6028  try
6029  {
6030  throw;
6031  } //one more try to printout extra info
6032  catch(const std::exception& e)
6033  {
6034  ss << "Exception message: " << e.what();
6035  }
6036  catch(...)
6037  {
6038  }
6039 
6040  __SUP_COUT_ERR__ << "\n" << ss.str();
6041  version = TableVersion();
6042  tableViewPtr = table->getMockupViewP();
6043 
6044  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6045  }
6046  }
6047  xmlOut.addTextElementToData("TableVersion", version.toString()); // table version
6048 
6049  if(getRawData)
6050  return; // no need to go further for rawData handling
6051 
6052  // get 'columns' of view
6053  xercesc::DOMElement* choicesParentEl;
6054  parentEl = xmlOut.addTextElementToData("CurrentVersionColumnHeaders", "");
6055 
6056  std::vector<TableViewColumnInfo> colInfo = tableViewPtr->getColumnsInfo();
6057 
6058  for(int i = 0; i < (int)colInfo.size(); ++i) // column headers and types
6059  {
6060  xmlOut.addTextElementToParent("ColumnHeader", colInfo[i].getName(), parentEl);
6061  xmlOut.addTextElementToParent("ColumnType", colInfo[i].getType(), parentEl);
6062  xmlOut.addTextElementToParent(
6063  "ColumnDataType", colInfo[i].getDataType(), parentEl);
6064 
6065  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6066  // whereas DefaultRowValue are the defaults for the mockup
6067  xmlOut.addTextElementToParent(
6068  "ColumnDefaultValue", colInfo[i].getDefaultValue(), parentEl);
6069 
6070  choicesParentEl = xmlOut.addTextElementToParent("ColumnChoices", "", parentEl);
6071  // add data choices if necessary
6072  if(colInfo[i].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
6073  colInfo[i].getType() == TableViewColumnInfo::TYPE_BITMAP_DATA ||
6074  colInfo[i].isChildLink())
6075  {
6076  for(auto& choice : colInfo[i].getDataChoices())
6077  xmlOut.addTextElementToParent("ColumnChoice", choice, choicesParentEl);
6078  }
6079 
6080  xmlOut.addTextElementToParent(
6081  "ColumnMinValue", colInfo[i].getMinValue(), parentEl);
6082  xmlOut.addTextElementToParent(
6083  "ColumnMaxValue", colInfo[i].getMaxValue(), parentEl);
6084  }
6085 
6086  // verify mockup columns after columns are posted to xmlOut
6087  try
6088  {
6089  if(version.isInvalid())
6090  tableViewPtr->init();
6091  }
6092  catch(std::runtime_error& e)
6093  {
6094  // append accumulated errors, because they may be most useful
6095  __THROW__(e.what() + std::string("\n\n") + accumulatedErrors);
6096  }
6097  catch(...)
6098  {
6099  throw;
6100  }
6101 
6102  parentEl = xmlOut.addTextElementToData("CurrentVersionRows", "");
6103 
6104  for(int r = 0; r < (int)tableViewPtr->getNumberOfRows(); ++r)
6105  {
6106  sprintf(tmpIntStr, "%d", r);
6107  xercesc::DOMElement* tmpParentEl =
6108  xmlOut.addTextElementToParent("Row", tmpIntStr, parentEl);
6109 
6110  for(int c = 0; c < (int)tableViewPtr->getNumberOfColumns(); ++c)
6111  {
6112  if(colInfo[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
6113  {
6114  std::string timeAsString;
6115  tableViewPtr->getValue(timeAsString, r, c);
6116  xmlOut.addTextElementToParent("Entry", timeAsString, tmpParentEl);
6117  }
6118  else
6119  xmlOut.addTextElementToParent(
6120  "Entry", tableViewPtr->getDataView()[r][c], tmpParentEl);
6121  }
6122  }
6123 
6124  // add "other" fields associated with configView
6125  xmlOut.addTextElementToData("TableComment", tableViewPtr->getComment());
6126  xmlOut.addTextElementToData("TableAuthor", tableViewPtr->getAuthor());
6127  xmlOut.addTextElementToData("TableCreationTime",
6128  std::to_string(tableViewPtr->getCreationTime()));
6129  xmlOut.addTextElementToData("TableLastAccessTime",
6130  std::to_string(tableViewPtr->getLastAccessTime()));
6131 
6132  // add to xml the default row values
6133  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6134  // whereas DefaultRowValue are the defaults for the mockup
6135  std::vector<std::string> defaultRowValues =
6136  table->getMockupViewP()->getDefaultRowValues();
6137  // don't give author and time.. force default author, let JS fill time
6138  for(unsigned int c = 0; c < defaultRowValues.size() - 2; ++c)
6139  {
6140  xmlOut.addTextElementToData("DefaultRowValue", defaultRowValues[c]);
6141  }
6142 
6143  const std::set<std::string> srcColNames = tableViewPtr->getSourceColumnNames();
6144 
6145  if(accumulatedErrors != "") // add accumulated errors to xmlOut
6146  {
6147  __SUP_SS__ << (std::string("Column errors were allowed for this request, so "
6148  "perhaps you can ignore this, ") +
6149  "but please note the following warnings:\n" + accumulatedErrors)
6150  << __E__;
6151  __SUP_COUT_ERR__ << ss.str();
6152  xmlOut.addTextElementToData("TableWarnings", ss.str());
6153  }
6154  else if(!version.isTemporaryVersion() && // not temporary (these are not filled from
6155  // interface source)
6156  (srcColNames.size() != tableViewPtr->getNumberOfColumns() ||
6157  tableViewPtr->getSourceColumnMismatch() !=
6158  0)) // check for column size mismatch
6159  {
6160  __SUP_SS__ << "\n\nThere were warnings found when loading the table " << tableName
6161  << ":v" << version << ". Please see the details below:\n\n"
6162  << tableViewPtr->getMismatchColumnInfo();
6163 
6164  __SUP_COUT__ << "\n" << ss.str();
6165  xmlOut.addTextElementToData("TableWarnings", ss.str());
6166  }
6167 
6168 } // end handleGetTableXML()
6169 catch(std::runtime_error& e)
6170 {
6171  __SUP_SS__ << "Error getting table view!\n\n " << e.what() << __E__;
6172  __SUP_COUT_ERR__ << ss.str();
6173  xmlOut.addTextElementToData("Error", ss.str());
6174 }
6175 catch(...)
6176 {
6177  __SUP_SS__ << "Error getting table view!\n\n " << __E__;
6178  try
6179  {
6180  throw;
6181  } //one more try to printout extra info
6182  catch(const std::exception& e)
6183  {
6184  ss << "Exception message: " << e.what();
6185  }
6186  catch(...)
6187  {
6188  }
6189  __SUP_COUT_ERR__ << ss.str();
6190  xmlOut.addTextElementToData("Error", ss.str());
6191 } // end handleGetTableXML() catch
6192 
6193 //==============================================================================
6200 ConfigurationManagerRW* ConfigurationGUISupervisor::refreshUserSession(
6201  std::string username, bool refresh)
6202 {
6203  uint64_t sessionIndex =
6204  0; // make session by username for now! (may never want to change back)
6205 
6206  std::stringstream ssMapKey;
6207  ssMapKey << username << ":" << sessionIndex;
6208  std::string mapKey = ssMapKey.str();
6209  __SUP_COUTT__ << "Using Config Session " << mapKey
6210  << " ... Total Session Count: " << userConfigurationManagers_.size()
6211  << __E__;
6212 
6213  time_t now = time(0);
6214 
6215  // create new table mgr if not one for active session index
6216  if(userConfigurationManagers_.find(mapKey) == userConfigurationManagers_.end())
6217  {
6218  __SUP_COUT__ << "Creating new Configuration Manager. time=" << time(0) << " "
6219  << clock() << __E__;
6220  userConfigurationManagers_[mapKey] = new ConfigurationManagerRW(username);
6221 
6222  // update table info for each new configuration manager
6223  // IMPORTANTLY this also fills all configuration manager pointers with instances,
6224  // so we are not dealing with changing pointers later on
6225  userConfigurationManagers_[mapKey]->getAllTableInfo(
6226  true /* refresh */, // load empty instance of everything important
6227  0 /* accumulatedWarnings */,
6228  "" /* errorFilterName */,
6229  false /* getGroupKeys */,
6230  false /* getGroupInfo */,
6231  true /* initializeActiveGroups */);
6232  }
6233  else if(userLastUseTime_.find(mapKey) == userLastUseTime_.end())
6234  {
6235  __SUP_SS__ << "Fatal error managing userLastUseTime_! Check the logs for "
6236  "Configuration Interface failure."
6237  << __E__;
6238  __SUP_COUT_ERR__ << "\n" << ss.str();
6239  __SS_THROW__;
6240  }
6241  else if(refresh || (now - userLastUseTime_[mapKey]) >
6242  CONFIGURATION_MANAGER_REFRESH_THRESHOLD) // check if should refresh all table info
6243  {
6244  __SUP_COUT__ << "Refreshing all table info." << __E__;
6245  userConfigurationManagers_[mapKey]->getAllTableInfo(
6246  true /* refresh */,
6247  0 /* accumulatedWarnings */,
6248  "" /* errorFilterName */,
6249  false /* getGroupKeys */,
6250  false /* getGroupInfo */,
6251  true /* initializeActiveGroups */);
6252  }
6253  __SUP_COUTT__ << "Configuration Manager ready. time=" << time(0) << " " << clock()
6254  << " runTimeSeconds()="
6255  << userConfigurationManagers_[mapKey]->runTimeSeconds() << __E__;
6256 
6257  // update active sessionIndex last use time
6258  userLastUseTime_[mapKey] = now;
6259 
6260  // check for stale sessions and remove them (so table user maps do not grow forever)
6261  for(std::map<std::string, time_t>::iterator it = userLastUseTime_.begin();
6262  it != userLastUseTime_.end();
6263  ++it)
6264  if(now - it->second > CONFIGURATION_MANAGER_EXPIRATION_TIME) // expired!
6265  {
6266  __SUP_COUT__ << now << ":" << it->second << " = " << now - it->second
6267  << __E__;
6268  delete userConfigurationManagers_[it->first]; // call destructor
6269  if(!(userConfigurationManagers_.erase(it->first))) // erase by key
6270  {
6271  __SUP_SS__ << "Fatal error erasing configuration manager by key!"
6272  << __E__;
6273  __SUP_COUT_ERR__ << "\n" << ss.str();
6274  __SS_THROW__;
6275  }
6276  userLastUseTime_.erase(it); // erase by iterator
6277 
6278  it = userLastUseTime_.begin(); // fail safe.. reset it, to avoid trying to understand what happens with the next iterator
6279  }
6280 
6281  return userConfigurationManagers_[mapKey];
6282 } //end refreshUserSession()
6283 
6284 //==============================================================================
6289 void ConfigurationGUISupervisor::handleDeleteTableInfoXML(HttpXmlDocument& xmlOut,
6290  ConfigurationManagerRW* cfgMgr,
6291  std::string& tableName)
6292 {
6293  if(0 == rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
6294  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
6295  __SUP_COUT_INFO__ << ("Table Info File successfully renamed: " +
6296  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6297  << __E__;
6298  else
6299  {
6300  __SUP_COUT_ERR__ << ("Error renaming file to " +
6301  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6302  << __E__;
6303 
6304  xmlOut.addTextElementToData(
6305  "Error",
6306  ("Error renaming Table Info File to " +
6307  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")));
6308  return;
6309  }
6310 
6311  // reload all with refresh to remove new table
6312  cfgMgr->getAllTableInfo(true /* refresh */);
6313 } // end handleDeleteTableInfoXML()
6314 
6315 //==============================================================================
6322 void ConfigurationGUISupervisor::handleSaveTableInfoXML(
6323  HttpXmlDocument& xmlOut,
6324  ConfigurationManagerRW* cfgMgr,
6325  std::string& tableName,
6326  const std::string& data,
6327  const std::string& tableDescription,
6328  const std::string& columnChoicesCSV,
6329  bool allowOverwrite)
6330 {
6331  // create all caps name and validate
6332  // only allow alpha-numeric names with "Table" at end
6333  std::string capsName;
6334  try
6335  {
6336  capsName = TableBase::convertToCaps(tableName, true);
6337  }
6338  catch(std::runtime_error& e)
6339  { // error! non-alpha
6340  xmlOut.addTextElementToData("Error", e.what());
6341  return;
6342  }
6343 
6344  if(!allowOverwrite)
6345  {
6346  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "r");
6347  if(fp)
6348  {
6349  fclose(fp);
6350  xmlOut.addTextElementToData("TableName", tableName);
6351  xmlOut.addTextElementToData("OverwriteError", "1");
6352  xmlOut.addTextElementToData(
6353  "Error",
6354  "File already exists! ('" +
6355  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT) + "')");
6356  return;
6357  }
6358  }
6359 
6360  __SUP_COUT__ << "capsName=" << capsName << __E__;
6361  __SUP_COUT__ << "tableName=" << tableName << __E__;
6362  __SUP_COUT__ << "tableDescription=" << tableDescription << __E__;
6363  __SUP_COUT__ << "columnChoicesCSV=" << columnChoicesCSV << __E__;
6364 
6365  // create preview string to validate column info before write to file
6366  std::stringstream outss;
6367 
6368  outss << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n";
6369  outss << "\t<ROOT xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
6370  "xsi:noNamespaceSchemaLocation=\"TableInfo.xsd\">\n";
6371  outss << "\t\t<TABLE Name=\"" << tableName << "\">\n";
6372  outss << "\t\t\t<VIEW Name=\"" << capsName
6373  << "\" Type=\"File,Database,DatabaseTest\" Description=\"" << tableDescription
6374  << "\">\n";
6375 
6376  // each column is represented by 4 fields or 6
6377  // - type, name, dataType, defaultValue, minValue, maxValue
6378 
6379  std::istringstream columnChoicesISS(columnChoicesCSV);
6380  std::string columnChoicesString;
6381  std::string columnDefaultValue, columnMinValue, columnMaxValue;
6382  std::vector<std::string> columnParameters;
6383  std::vector<std::string> columnData =
6384  StringMacros::getVectorFromString(data, {';'} /*delimiter*/);
6385 
6386  for(unsigned int c = 0; c < columnData.size() - 1; ++c)
6387  {
6388  columnParameters =
6389  StringMacros::getVectorFromString(columnData[c], {','} /*delimiter*/);
6390  __COUT__ << "Column #" << c << ": "
6391  << StringMacros::vectorToString(columnParameters) << __E__;
6392  for(unsigned int p = 0; p < columnParameters.size(); ++p)
6393  {
6394  __COUT__ << "\t Parameter #" << p << ": " << columnParameters[p] << __E__;
6395  }
6396  __COUT__ << "\t creating the new xml" << __E__;
6397 
6398  std::string& columnType = columnParameters[0];
6399  std::string& columnDataType = columnParameters[2];
6400 
6401  outss << "\t\t\t\t<COLUMN Type=\"";
6402  outss << columnType;
6403  outss << "\" \t Name=\"";
6404  outss << columnParameters[1];
6405  outss << "\" \t StorageName=\"";
6406  try
6407  {
6408  outss << TableBase::convertToCaps(columnParameters[1]); // now caps
6409  }
6410  catch(std::runtime_error& e)
6411  { // error! non-alpha
6412  xmlOut.addTextElementToData("Error",
6413  std::string("For column name '") +
6414  columnParameters[1] + "' - " + e.what());
6415  return;
6416  }
6417  outss << "\" \t DataType=\"";
6418  outss << columnDataType;
6419 
6420  columnDefaultValue = StringMacros::decodeURIComponent(columnParameters[3]);
6421 
6422  if(columnDefaultValue !=
6423  TableViewColumnInfo::getDefaultDefaultValue(columnType, columnDataType))
6424  {
6425  __SUP_COUT__ << "FOUND user spec'd default value '" << columnDefaultValue
6426  << "'" << __E__;
6427  outss << "\" \t DefaultValue=\"";
6428  outss << columnParameters[3];
6429  }
6430  getline(columnChoicesISS, columnChoicesString, ';');
6431  outss << "\" \t DataChoices=\"";
6432  outss << columnChoicesString;
6433 
6434  if(columnParameters.size() > 4 &&
6435  columnDataType == TableViewColumnInfo::DATATYPE_NUMBER)
6436  {
6437  columnMinValue = StringMacros::decodeURIComponent(columnParameters[4]);
6438  if(columnMinValue != "")
6439  {
6440  if(columnMinValue !=
6441  TableViewColumnInfo::getMinDefaultValue(columnDataType))
6442  {
6443  __SUP_COUT__ << "FOUND user spec'd min value '" << columnParameters[4]
6444  << "'" << __E__;
6445  if(!StringMacros::isNumber(
6446  StringMacros::convertEnvironmentVariables(columnMinValue)))
6447  {
6448  __SS__ << "Inavlid user spec'd min value '" << columnParameters[4]
6449  << "' which evaluates to '" << columnMinValue
6450  << "' and is not a valid number. The minimum value must "
6451  "be a number (environment variables and math "
6452  "operations are allowed)."
6453  << __E__;
6454  __SS_THROW__;
6455  }
6456  outss << "\" \t MinValue=\"" << columnParameters[4];
6457  }
6458  }
6459 
6460  columnMaxValue = StringMacros::decodeURIComponent(columnParameters[5]);
6461  if(columnMaxValue != "")
6462  {
6463  if(columnMaxValue !=
6464  TableViewColumnInfo::getMaxDefaultValue(columnDataType))
6465  {
6466  __SUP_COUT__ << "FOUND user spec'd max value = " << columnMaxValue
6467  << __E__;
6468  if(!StringMacros::isNumber(
6469  StringMacros::convertEnvironmentVariables(columnMaxValue)))
6470  {
6471  __SS__ << "Inavlid user spec'd max value '" << columnParameters[5]
6472  << "' which evaluates to '" << columnMaxValue
6473  << "' and is not a valid number. The maximum value must "
6474  "be a number (environment variables and math "
6475  "operations are allowed)."
6476  << __E__;
6477  __SS_THROW__;
6478  }
6479  outss << "\" \t MaxValue=\"" << columnParameters[5];
6480  }
6481  }
6482  }
6483  outss << "\"/>\n";
6484  }
6485 
6486  outss << "\t\t\t</VIEW>\n";
6487  outss << "\t\t</TABLE>\n";
6488  outss << "\t</ROOT>\n";
6489 
6490  __SUP_COUT__ << outss.str() << __E__;
6491 
6492  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "w");
6493  if(!fp)
6494  {
6495  xmlOut.addTextElementToData("Error",
6496  "Failed to open destination Table Info file:" +
6497  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT));
6498  return;
6499  }
6500 
6501  fprintf(fp, "%s", outss.str().c_str());
6502  fclose(fp);
6503 
6504  // reload all table info with refresh AND reset to pick up possibly new table
6505  // check for errors related to this tableName
6506  std::string accumulatedErrors = "";
6507  cfgMgr->getAllTableInfo(true /* refresh */, &accumulatedErrors, tableName);
6508 
6509  // if errors associated with this table name stop and report
6510  if(accumulatedErrors != "")
6511  {
6512  __SUP_SS__ << ("The new version of the '" + tableName +
6513  "' table column info was saved, however errors were detected "
6514  "reading back the table '" +
6515  tableName + "' after the save attempt:\n\n" + accumulatedErrors)
6516  << __E__;
6517 
6518  __SUP_COUT_ERR__ << ss.str() << __E__;
6519  xmlOut.addTextElementToData("Error", ss.str());
6520 
6521  return;
6522  }
6523 
6524  // return the new table info
6525  handleGetTableXML(xmlOut, cfgMgr, tableName, TableVersion());
6526 
6527  // After save, debug all table column info
6528  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
6529 
6530  // give a print out of currently illegal table column info
6531  __SUP_COUT_INFO__ << "Looking for errors in all table column info..." << __E__;
6532  for(const auto& cfgInfo : allTableInfo)
6533  {
6534  try
6535  {
6536  cfgMgr->getTableByName(cfgInfo.first)->getMockupViewP()->init();
6537  }
6538  catch(std::runtime_error& e)
6539  {
6540  __SUP_COUT_WARN__ << "\n\n##############################################\n"
6541  << "Error identified in column info of table '"
6542  << cfgInfo.first << "':\n\n"
6543  << e.what() << "\n\n"
6544  << __E__;
6545  }
6546  }
6547 } // end handleSaveTableInfoXML()
6548 
6549 //==============================================================================
6557 void ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML(
6558  HttpXmlDocument& xmlOut,
6559  ConfigurationManagerRW* cfgMgr,
6560  const std::string& groupAliasCSV,
6561  const std::string& groupNameCSV,
6562  const std::string& groupKeyCSV,
6563  const std::string& author)
6564 try
6565 {
6566  cfgMgr->loadConfigurationBackbone();
6567  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6568 
6569  const std::string groupAliasesTableName =
6570  ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
6571  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
6572  {
6573  __SUP_SS__ << "Active version of " << groupAliasesTableName << " missing!"
6574  << __E__;
6575  xmlOut.addTextElementToData("Error", ss.str());
6576  return;
6577  }
6578 
6579  // put all old backbone versions in xmlOut
6580  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6581  for(auto& memberName : backboneMembers)
6582  {
6583  __SUP_COUT__ << "activeVersions[\"" << memberName
6584  << "\"]=" << activeVersions[memberName] << __E__;
6585 
6586  xmlOut.addTextElementToData("oldBackboneName", memberName);
6587  xmlOut.addTextElementToData("oldBackboneVersion",
6588  activeVersions[memberName].toString());
6589  }
6590 
6591  // make a temporary version from active view
6592  // modify the chosen groupAlias row
6593  // save as new version
6594 
6595  TableBase* table = cfgMgr->getTableByName(groupAliasesTableName);
6596  TableVersion originalVersion = activeVersions[groupAliasesTableName];
6597  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
6598 
6599  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6600  bool isDifferent = false;
6601 
6602  try
6603  {
6604  TableView* configView = table->getTemporaryView(temporaryVersion);
6605 
6606  unsigned int col = configView->findCol("GroupKeyAlias");
6607  unsigned int ccol = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
6608  unsigned int ncol = configView->findCol("GroupName");
6609  unsigned int kcol = configView->findCol("GroupKey");
6610 
6611  // only make a new version if we are changing compared to active backbone
6612  std::vector<std::string> groupAliases =
6613  StringMacros::getVectorFromString(groupAliasCSV);
6614  std::vector<std::string> groupNames =
6615  StringMacros::getVectorFromString(groupNameCSV);
6616  std::vector<std::string> groupKeys =
6617  StringMacros::getVectorFromString(groupKeyCSV);
6618  __SUP_COUTV__(StringMacros::vectorToString(groupAliases));
6619  __SUP_COUTV__(StringMacros::vectorToString(groupNames));
6620  __SUP_COUTV__(StringMacros::vectorToString(groupKeys));
6621 
6622  size_t i = 0;
6623  for(const auto& groupAlias : groupAliases)
6624  {
6625  if(groupAlias == "" || groupNames[i] == "" || groupKeys[i] == "")
6626  {
6627  //skip empty aliases
6628  __SUP_COUT_WARN__ << "Empty alias parameter found [" << i << "] = {"
6629  << groupAlias << ", " << groupNames[i] << "("
6630  << groupKeys[i] << ")}" << __E__;
6631  ++i;
6632  continue;
6633  }
6634 
6635  bool localIsDifferent = false;
6636  const std::string& groupName = groupNames[i];
6637  const TableGroupKey groupKey(groupKeys[i]);
6638  ++i;
6639 
6640  unsigned int row = -1;
6641  // find groupAlias row
6642  try
6643  {
6644  row = configView->findRow(col, groupAlias);
6645  }
6646  catch(...) // ignore not found error
6647  {
6648  }
6649 
6650  if(row == (unsigned int)-1) // if row not found then add a row
6651  {
6652  localIsDifferent = true;
6653  row = configView->addRow();
6654 
6655  // set all columns in new row
6656  configView->setValue(
6657  "This Group Alias was automatically setup by the server.", row, ccol);
6658  configView->setValue(groupAlias, row, col);
6659  }
6660 
6661  __SUP_COUT__ << "\t\t row: " << row << __E__;
6662 
6663  __SUP_COUT__ << "\t\t groupName: " << groupName << " vs "
6664  << configView->getDataView()[row][ncol] << __E__;
6665  if(groupName != configView->getDataView()[row][ncol])
6666  {
6667  configView->setValue(groupName, row, ncol);
6668  localIsDifferent = true;
6669  }
6670 
6671  __SUP_COUT__ << "\t\t groupKey: " << groupKey << " vs "
6672  << configView->getDataView()[row][kcol] << __E__;
6673  if(groupKey.toString() != configView->getDataView()[row][kcol])
6674  {
6675  configView->setValue(groupKey.toString(), row, kcol);
6676  localIsDifferent = true;
6677  }
6678 
6679  if(localIsDifferent) // set author/time of new record if different
6680  {
6681  configView->setValue(
6682  author,
6683  row,
6684  configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
6685  configView->setValue(
6686  time(0),
6687  row,
6688  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
6689  isDifferent = true;
6690  }
6691  } //end group alias modify loop
6692  }
6693  catch(...)
6694  {
6695  __SUP_COUT_ERR__ << "Error editing Group Alias view!" << __E__;
6696 
6697  // delete temporaryVersion
6698  table->eraseView(temporaryVersion);
6699  throw;
6700  }
6701 
6702  TableVersion newAssignedVersion;
6703  if(isDifferent) // make new version if different
6704  {
6705  __SUP_COUT__ << "\t\t**************************** Save as new table version"
6706  << __E__;
6707 
6708  // save or find equivalent
6709  newAssignedVersion = ConfigurationSupervisorBase::saveModifiedVersionXML(
6710  xmlOut,
6711  cfgMgr,
6712  table->getTableName(),
6713  originalVersion,
6714  false /*makeTemporary*/,
6715  table,
6716  temporaryVersion,
6717  false /*ignoreDuplicates*/,
6718  true /*lookForEquivalent*/);
6719  }
6720  else // use existing version
6721  {
6722  __SUP_COUT__
6723  << "\t\t**************************** Using the existing table version"
6724  << __E__;
6725 
6726  // delete temporaryVersion
6727  table->eraseView(temporaryVersion);
6728  newAssignedVersion = activeVersions[groupAliasesTableName];
6729 
6730  xmlOut.addTextElementToData("savedName", groupAliasesTableName);
6731  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6732  }
6733 
6734  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6735 } //end handleSetGroupAliasInBackboneXML()
6736 catch(std::runtime_error& e)
6737 {
6738  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << e.what() << __E__;
6739  __SUP_COUT_ERR__ << ss.str();
6740  xmlOut.addTextElementToData("Error", ss.str());
6741 }
6742 catch(...)
6743 {
6744  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << __E__;
6745  try
6746  {
6747  throw;
6748  } //one more try to printout extra info
6749  catch(const std::exception& e)
6750  {
6751  ss << "Exception message: " << e.what();
6752  }
6753  catch(...)
6754  {
6755  }
6756  __SUP_COUT_ERR__ << ss.str();
6757  xmlOut.addTextElementToData("Error", ss.str());
6758 } //end handleSetGroupAliasInBackboneXML() catch
6759 
6760 //==============================================================================
6768 void ConfigurationGUISupervisor::handleSetTableAliasInBackboneXML(
6769  HttpXmlDocument& xmlOut,
6770  ConfigurationManagerRW* cfgMgr,
6771  const std::string& tableAlias,
6772  const std::string& tableName,
6773  TableVersion version,
6774  const std::string& author)
6775 try
6776 {
6777  cfgMgr->loadConfigurationBackbone();
6778  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6779 
6780  const std::string versionAliasesTableName =
6781  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6782  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6783  {
6784  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
6785  << __E__;
6786  xmlOut.addTextElementToData("Error", ss.str());
6787  return;
6788  }
6789 
6790  // put all old backbone versions in xmlOut
6791  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6792  for(auto& memberName : backboneMembers)
6793  {
6794  __SUP_COUT__ << "activeVersions[\"" << memberName
6795  << "\"]=" << activeVersions[memberName] << __E__;
6796 
6797  xmlOut.addTextElementToData("oldBackboneName", memberName);
6798  xmlOut.addTextElementToData("oldBackboneVersion",
6799  activeVersions[memberName].toString());
6800  }
6801 
6802  // make a temporary version from active view
6803  // modify the chosen versionAlias row
6804  // save as new version
6805 
6806  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
6807  TableVersion originalVersion = activeVersions[versionAliasesTableName];
6808  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
6809 
6810  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6811 
6812  bool isDifferent = false;
6813 
6814  try
6815  {
6816  TableView* configView = table->getTemporaryView(temporaryVersion);
6817 
6818  unsigned int col;
6819  unsigned int col2 = configView->findCol("VersionAlias");
6820  unsigned int col3 = configView->findCol("TableName");
6821 
6822  // only make a new version if we are changing compared to active backbone
6823 
6824  unsigned int row = -1;
6825  // find tableName, versionAlias pair
6826  // NOTE: only accept the first pair, repeats are ignored.
6827  try
6828  {
6829  unsigned int tmpRow = -1;
6830  do
6831  { // start looking from beyond last find
6832  tmpRow = configView->findRow(col3, tableName, tmpRow + 1);
6833  } while(configView->getDataView()[tmpRow][col2] != tableAlias);
6834  // at this point the first pair was found! (else exception was thrown)
6835  row = tmpRow;
6836  }
6837  catch(...)
6838  {
6839  }
6840  if(row == (unsigned int)-1) // if row not found then add a row
6841  {
6842  isDifferent = true;
6843  row = configView->addRow();
6844 
6845  // set all columns in new row
6846  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
6847  configView->setValue(
6848  std::string("Entry was added by server in ") +
6849  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
6850  row,
6851  col);
6852 
6853  col = configView->findCol("VersionAliasUID");
6854  configView->setValue(
6855  tableName.substr(0, tableName.rfind("Table")) + tableAlias, row, col);
6856 
6857  configView->setValue(tableAlias, row, col2);
6858  configView->setValue(tableName, row, col3);
6859  }
6860 
6861  __SUP_COUT__ << "\t\t row: " << row << __E__;
6862 
6863  col = configView->findCol("Version");
6864  __SUP_COUT__ << "\t\t version: " << version << " vs "
6865  << configView->getDataView()[row][col] << __E__;
6866  if(version.toString() != configView->getDataView()[row][col])
6867  {
6868  configView->setValue(version.toString(), row, col);
6869  isDifferent = true;
6870  }
6871 
6872  if(isDifferent) // set author/time of new version if different
6873  {
6874  configView->setValue(
6875  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
6876  configView->setValue(
6877  time(0),
6878  row,
6879  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
6880  }
6881  }
6882  catch(...)
6883  {
6884  __SUP_COUT_ERR__ << "Error editing Version Alias view!" << __E__;
6885 
6886  // delete temporaryVersion
6887  table->eraseView(temporaryVersion);
6888  throw;
6889  }
6890 
6891  TableVersion newAssignedVersion;
6892  if(isDifferent) // make new version if different
6893  {
6894  __SUP_COUT__ << "\t\t**************************** Save as new table version"
6895  << __E__;
6896 
6897  newAssignedVersion = ConfigurationSupervisorBase::saveModifiedVersionXML(
6898  xmlOut,
6899  cfgMgr,
6900  table->getTableName(),
6901  originalVersion,
6902  false /*makeTemporary*/,
6903  table,
6904  temporaryVersion,
6905  false /*ignoreDuplicates*/,
6906  true /*lookForEquivalent*/);
6907  }
6908  else // use existing version
6909  {
6910  __SUP_COUT__ << "\t\t**************************** Using existing table version"
6911  << __E__;
6912 
6913  // delete temporaryVersion
6914  table->eraseView(temporaryVersion);
6915  newAssignedVersion = activeVersions[versionAliasesTableName];
6916 
6917  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
6918  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6919  }
6920 
6921  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6922 } // end handleSetVersionAliasInBackboneXML()
6923 catch(std::runtime_error& e)
6924 {
6925  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
6926  __SUP_COUT_ERR__ << ss.str();
6927  xmlOut.addTextElementToData("Error", ss.str());
6928 }
6929 catch(...)
6930 {
6931  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
6932  try
6933  {
6934  throw;
6935  } //one more try to printout extra info
6936  catch(const std::exception& e)
6937  {
6938  ss << "Exception message: " << e.what();
6939  }
6940  catch(...)
6941  {
6942  }
6943  __SUP_COUT_ERR__ << ss.str();
6944  xmlOut.addTextElementToData("Error", ss.str());
6945 } // end handleSetVersionAliasInBackboneXML() catch
6946 
6947 //==============================================================================
6953 void ConfigurationGUISupervisor::handleAliasGroupMembersInBackboneXML(
6954  HttpXmlDocument& xmlOut,
6955  ConfigurationManagerRW* cfgMgr,
6956  const std::string& versionAlias,
6957  const std::string& groupName,
6958  TableGroupKey groupKey,
6959  const std::string& author)
6960 try
6961 {
6962  cfgMgr->loadConfigurationBackbone();
6963  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6964 
6965  const std::string versionAliasesTableName =
6966  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6967  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6968  {
6969  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
6970  << __E__;
6971  xmlOut.addTextElementToData("Error", ss.str());
6972  return;
6973  }
6974 
6975  // put all old backbone versions in xmlOut
6976  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6977  for(auto& memberName : backboneMembers)
6978  {
6979  __SUP_COUT__ << "activeVersions[\"" << memberName
6980  << "\"]=" << activeVersions[memberName] << __E__;
6981 
6982  xmlOut.addTextElementToData("oldBackboneName", memberName);
6983  xmlOut.addTextElementToData("oldBackboneVersion",
6984  activeVersions[memberName].toString());
6985  }
6986 
6987  // make a temporary version from active view
6988  // modify the chosen versionAlias row
6989  // save as new version
6990 
6991  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
6992  TableVersion temporaryVersion =
6993  table->createTemporaryView(activeVersions[versionAliasesTableName]);
6994 
6995  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6996 
6997  TableView* configView = table->getTemporaryView(temporaryVersion);
6998 
6999  // only make a new version if we are changing compared to active backbone
7000  bool isDifferent = false;
7001 
7002  // get member names and versions
7003  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7004  try
7005  {
7006  cfgMgr->loadTableGroup(groupName,
7007  groupKey,
7008  false /*doActivate*/,
7009  &memberMap,
7010  0,
7011  0,
7012  0,
7013  0,
7014  0, // defaults
7015  true /*doNotLoadMember*/);
7016  }
7017  catch(...)
7018  {
7019  xmlOut.addTextElementToData(
7020  "Error",
7021  "Table group \"" + TableGroupKey::getFullGroupString(groupName, groupKey) +
7022  "\" can not be retrieved!");
7023  return;
7024  }
7025 
7026  unsigned int col;
7027  unsigned int col2 = configView->findCol("VersionAlias");
7028  unsigned int col3 = configView->findCol("TableName");
7029 
7030  for(auto& memberPair : memberMap)
7031  {
7032  bool thisMemberIsDifferent = false;
7033  unsigned int row = -1;
7034 
7035  __SUP_COUT__ << "Adding alias for " << memberPair.first << "_v"
7036  << memberPair.second << " to " << versionAlias << __E__;
7037 
7038  // find tableName, versionAlias pair
7039  // NOTE: only accept the first pair, repeats are ignored.
7040  try
7041  {
7042  unsigned int tmpRow = -1;
7043  do
7044  { // start looking from beyond last find
7045  tmpRow = configView->findRow(col3, memberPair.first, tmpRow + 1);
7046  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
7047  // at this point the first pair was found! (else exception was thrown)
7048  row = tmpRow;
7049  }
7050  catch(...)
7051  {
7052  }
7053  if(row == (unsigned int)-1) // if row not found then add a row
7054  {
7055  thisMemberIsDifferent = true;
7056  row = configView->addRow();
7057 
7058  // set all columns in new row
7059  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
7060  configView->setValue(
7061  std::string("Entry was added by server in ") +
7062  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
7063  row,
7064  col);
7065 
7066  col = configView->getColUID();
7067  configView->setValue(
7068  memberPair.first.substr(0, memberPair.first.rfind("Table")) +
7069  versionAlias,
7070  row,
7071  col);
7072 
7073  configView->setValue(versionAlias, row, col2);
7074  configView->setValue(memberPair.first, row, col3);
7075  }
7076 
7077  col = configView->findCol("Version");
7078 
7079  if(memberPair.second.toString() != configView->getDataView()[row][col])
7080  {
7081  configView->setValue(memberPair.second.toString(), row, col);
7082  thisMemberIsDifferent = true;
7083  }
7084 
7085  if(thisMemberIsDifferent) // change author and time if row is different
7086  {
7087  configView->setValue(
7088  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7089  configView->setValue(
7090  time(0),
7091  row,
7092  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7093  }
7094 
7095  if(thisMemberIsDifferent)
7096  isDifferent = true;
7097  }
7098 
7099  // configView->print();
7100 
7101  TableVersion newAssignedVersion;
7102  if(isDifferent) // make new version if different
7103  {
7104  __SUP_COUT__ << "\t\t**************************** Save v" << temporaryVersion
7105  << " as new table version" << __E__;
7106 
7107  newAssignedVersion =
7108  cfgMgr->saveNewTable(versionAliasesTableName, temporaryVersion);
7109  }
7110  else // use existing version
7111  {
7112  __SUP_COUT__ << "\t\t**************************** Using existing table version"
7113  << __E__;
7114 
7115  // delete temporaryVersion
7116  table->eraseView(temporaryVersion);
7117  newAssignedVersion = activeVersions[versionAliasesTableName];
7118  }
7119 
7120  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
7121  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7122  __SUP_COUT__ << "\t\t Resulting Version: " << newAssignedVersion << __E__;
7123 } // end handleAliasGroupMembersInBackboneXML()
7124 catch(std::runtime_error& e)
7125 {
7126  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
7127  __SUP_COUT_ERR__ << ss.str();
7128  xmlOut.addTextElementToData("Error", ss.str());
7129 }
7130 catch(...)
7131 {
7132  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
7133  try
7134  {
7135  throw;
7136  } //one more try to printout extra info
7137  catch(const std::exception& e)
7138  {
7139  ss << "Exception message: " << e.what();
7140  }
7141  catch(...)
7142  {
7143  }
7144  __SUP_COUT_ERR__ << ss.str();
7145  xmlOut.addTextElementToData("Error", ss.str());
7146 } // end handleAliasGroupMembersInBackboneXML() catch
7147 
7148 //==============================================================================
7159 void ConfigurationGUISupervisor::handleGroupAliasesXML(HttpXmlDocument& xmlOut,
7160  ConfigurationManagerRW* cfgMgr)
7161 {
7162  cfgMgr->loadConfigurationBackbone();
7163  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7164 
7165  std::string groupAliasesTableName = ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
7166  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
7167  {
7168  __SUP_SS__ << "\nActive version of " << groupAliasesTableName << " missing! "
7169  << groupAliasesTableName
7170  << " is a required member of the Backbone table group."
7171  << "\n\nLikely you need to activate a valid Backbone table group."
7172  << __E__;
7173  __SUP_COUT__ << ss.str(); // just output findings, and return empty xml to avoid
7174  // infinite error loops in GUI
7175  // xmlOut.addTextElementToData("Error", ss.str());
7176  return;
7177  }
7178  __SUP_COUT__ << "activeVersions[\"" << groupAliasesTableName
7179  << "\"]=" << activeVersions[groupAliasesTableName] << __E__;
7180  xmlOut.addTextElementToData("GroupAliasesTableName", groupAliasesTableName);
7181  xmlOut.addTextElementToData("GroupAliasesTableVersion",
7182  activeVersions[groupAliasesTableName].toString());
7183 
7184  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7185  cfgMgr->getNode(groupAliasesTableName).getChildren();
7186 
7187  const int numOfThreads = ConfigurationManager::PROCESSOR_COUNT / 2;
7188  __SUP_COUT__ << " PROCESSOR_COUNT " << ConfigurationManager::PROCESSOR_COUNT
7189  << " ==> " << numOfThreads << " threads for alias group loads." << __E__;
7190 
7191  if(numOfThreads < 2) // no multi-threading
7192  {
7193  std::string groupName, groupKey, groupComment, groupAuthor, groupCreateTime,
7194  groupType;
7195  for(auto& aliasNodePair : aliasNodePairs)
7196  {
7197  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7198  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7199 
7200  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7201  xmlOut.addTextElementToData("GroupName", groupName);
7202  xmlOut.addTextElementToData("GroupKey", groupKey);
7203  xmlOut.addTextElementToData(
7204  "AliasComment",
7205  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7206  .getValueAsString());
7207 
7208  // get group comment
7209  groupComment =
7210  ConfigurationManager::UNKNOWN_INFO; // clear just in case failure
7211  groupType = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7212  try
7213  {
7214  cfgMgr->loadTableGroup(groupName,
7215  TableGroupKey(groupKey),
7216  false /* doActivate */,
7217  0 /* groupMembers */,
7218  0 /* progressBar */,
7219  0 /* accumulatedWarnings */,
7220  &groupComment,
7221  &groupAuthor,
7222  &groupCreateTime,
7223  true /*doNotLoadMembers*/,
7224  &groupType);
7225  }
7226  catch(...)
7227  {
7228  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
7229  << groupKey << ")' to extract group comment and type."
7230  << __E__;
7231  }
7232  xmlOut.addTextElementToData("GroupComment", groupComment);
7233  xmlOut.addTextElementToData("GroupType", groupType);
7234  } // end alias pair loop
7235  }
7236  else //multi-threading
7237  {
7238  int threadsLaunched = 0;
7239  int foundThreadIndex = 0;
7240  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
7241  for(int i = 0; i < numOfThreads; ++i)
7242  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
7243 
7244  std::vector<std::shared_ptr<ots::GroupInfo>> sharedGroupInfoPtrs;
7245  std::string groupName, groupKey;
7246 
7247  for(auto& aliasNodePair : aliasNodePairs)
7248  {
7249  //make temporary group info for thread
7250  sharedGroupInfoPtrs.push_back(std::make_shared<ots::GroupInfo>());
7251 
7252  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7253  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7254 
7255  if(threadsLaunched >= numOfThreads)
7256  {
7257  //find availableThreadIndex
7258  foundThreadIndex = -1;
7259  while(foundThreadIndex == -1)
7260  {
7261  for(int i = 0; i < numOfThreads; ++i)
7262  if(*(threadDone[i]))
7263  {
7264  foundThreadIndex = i;
7265  break;
7266  }
7267  if(foundThreadIndex == -1)
7268  {
7269  __SUP_COUTT__ << "Waiting for available thread..." << __E__;
7270  usleep(10000);
7271  }
7272  } //end thread search loop
7273  threadsLaunched = numOfThreads - 1;
7274  }
7275  __SUP_COUTT__ << "Starting load group thread... " << groupName << "("
7276  << groupKey << ")" << __E__;
7277  *(threadDone[foundThreadIndex]) = false;
7278 
7279  std::thread(
7280  [](ConfigurationManagerRW* theCfgMgr,
7281  std::string theGroupName,
7282  ots::TableGroupKey theGroupKey,
7283  std::shared_ptr<ots::GroupInfo> theGroupInfo,
7284  std::shared_ptr<std::atomic<bool>> theThreadDone) {
7285  ConfigurationManagerRW::loadTableGroupThread(theCfgMgr,
7286  theGroupName,
7287  theGroupKey,
7288  theGroupInfo,
7289  theThreadDone);
7290  },
7291  cfgMgr,
7292  groupName,
7293  TableGroupKey(groupKey),
7294  sharedGroupInfoPtrs.back(),
7295  threadDone[foundThreadIndex])
7296  .detach();
7297 
7298  ++threadsLaunched;
7299  ++foundThreadIndex;
7300 
7301  } //end alias group thread loop
7302 
7303  //check for all threads done
7304  do
7305  {
7306  foundThreadIndex = -1;
7307  for(int i = 0; i < numOfThreads; ++i)
7308  if(!*(threadDone[i]))
7309  {
7310  foundThreadIndex = i;
7311  break;
7312  }
7313  if(foundThreadIndex != -1)
7314  {
7315  __SUP_COUTT__ << "Waiting for thread to finish... " << foundThreadIndex
7316  << __E__;
7317  usleep(10000);
7318  }
7319  } while(foundThreadIndex != -1); //end thread done search loop
7320 
7321  //threads done now, so copy group info
7322  size_t i = 0;
7323  for(auto& aliasNodePair : aliasNodePairs)
7324  {
7325  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7326  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7327  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7328  xmlOut.addTextElementToData("GroupName", groupName);
7329  xmlOut.addTextElementToData("GroupKey", groupKey);
7330  xmlOut.addTextElementToData(
7331  "AliasComment",
7332  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7333  .getValueAsString());
7334 
7335  xmlOut.addTextElementToData("GroupComment",
7336  sharedGroupInfoPtrs[i]->latestKeyGroupComment_);
7337  xmlOut.addTextElementToData("GroupAuthor",
7338  sharedGroupInfoPtrs[i]->latestKeyGroupAuthor_);
7339  xmlOut.addTextElementToData(
7340  "GroupCreationTime", sharedGroupInfoPtrs[i]->latestKeyGroupCreationTime_);
7341  xmlOut.addTextElementToData(
7342  "GroupType", sharedGroupInfoPtrs[i]->latestKeyGroupTypeString_);
7343  // xmlOut.addTextElementToData("GroupMemberMap", sharedGroupInfoPtrs[i]->latestKeyMemberMap_);
7344  ++i;
7345  } //end copy group info loop
7346 
7347  } //end multi-thread handling
7348 } // end handleGroupAliasesXML
7349 
7350 //==============================================================================
7361 void ConfigurationGUISupervisor::handleVersionAliasesXML(HttpXmlDocument& xmlOut,
7362  ConfigurationManagerRW* cfgMgr)
7363 {
7364  cfgMgr->loadConfigurationBackbone();
7365  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7366 
7367  std::string versionAliasesTableName =
7368  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7369  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7370  {
7371  __SUP_SS__ << "Active version of VersionAliases missing!"
7372  << "Make sure you have a valid active Backbone Group." << __E__;
7373  xmlOut.addTextElementToData("Error", ss.str());
7374  return;
7375  }
7376  __SUP_COUT__ << "activeVersions[\"" << versionAliasesTableName
7377  << "\"]=" << activeVersions[versionAliasesTableName] << __E__;
7378  xmlOut.addTextElementToData("VersionAliasesVersion",
7379  activeVersions[versionAliasesTableName].toString());
7380 
7381  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7382  cfgMgr->getNode(versionAliasesTableName).getChildren();
7383 
7384  for(auto& aliasNodePair : aliasNodePairs)
7385  {
7386  // note : these are column names in the versionAliasesTableName table
7387  // VersionAlias, TableName, Version, CommentDescription
7388  xmlOut.addTextElementToData(
7389  "VersionAlias",
7390  aliasNodePair.second.getNode("VersionAlias").getValueAsString());
7391  xmlOut.addTextElementToData(
7392  "TableName", aliasNodePair.second.getNode("TableName").getValueAsString());
7393  xmlOut.addTextElementToData(
7394  "Version", aliasNodePair.second.getNode("Version").getValueAsString());
7395  xmlOut.addTextElementToData(
7396  "Comment",
7397  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7398  .getValueAsString());
7399  }
7400 } // end handleVersionAliasesXML()
7401 
7402 //==============================================================================
7408 void ConfigurationGUISupervisor::handleGetTableGroupTypeXML(
7409  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr, const std::string& tableList)
7410 {
7411  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7412  std::string name, versionStr;
7413  auto c = tableList.find(',', 0);
7414  auto i = c;
7415  i = 0; // auto used to get proper index/length type
7416  while(c < tableList.length())
7417  {
7418  // add the table name and version pair to the map
7419  name = tableList.substr(i, c - i);
7420  i = c + 1;
7421  c = tableList.find(',', i);
7422  if(c == std::string::npos) // missing version list entry?!
7423  {
7424  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
7425  __SUP_COUT_ERR__ << "\n" << ss.str();
7426  xmlOut.addTextElementToData("Error", ss.str());
7427  return;
7428  }
7429 
7430  versionStr = tableList.substr(i, c - i);
7431  i = c + 1;
7432  c = tableList.find(',', i);
7433 
7434  memberMap[name] = TableVersion(versionStr);
7435  }
7436 
7437  std::string groupTypeString = "";
7438  // try to determine type, dont report errors, just mark ots::GroupType::UNKNOWN_TYPE
7439  try
7440  {
7441  // determine the type of the table group
7442  groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
7443  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7444  }
7445  catch(std::runtime_error& e)
7446  {
7447  __SUP_SS__ << "Table group has invalid type! " << e.what() << __E__;
7448  __SUP_COUT__ << "\n" << ss.str();
7449  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7450  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7451  }
7452  catch(...)
7453  {
7454  __SUP_SS__ << "Table group has invalid type! " << __E__;
7455  try
7456  {
7457  throw;
7458  } //one more try to printout extra info
7459  catch(const std::exception& e)
7460  {
7461  ss << "Exception message: " << e.what();
7462  }
7463  catch(...)
7464  {
7465  }
7466  __SUP_COUT__ << "\n" << ss.str();
7467  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7468  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7469  }
7470 } //end handleGetTableGroupTypeXML()
7471 
7472 //==============================================================================
7488 void ConfigurationGUISupervisor::handleTableGroupsXML(HttpXmlDocument& xmlOut,
7489  ConfigurationManagerRW* cfgMgr,
7490  bool returnMembers)
7491 {
7492  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7493  // use xmlOut.dataSs_ since there is no need for escape the string and it can be a huge data block to escape and recursively print
7494  // xercesc::DOMElement* parentEl;
7495 
7496  // get all group info from cache (if no cache, get from interface)
7497 
7498  if(!cfgMgr->getAllGroupInfo().size() ||
7499  cfgMgr->getAllGroupInfo().begin()->second.latestKeyGroupTypeString_ == "" ||
7500  cfgMgr->getAllGroupInfo().begin()->second.latestKeyGroupTypeString_ ==
7501  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN)
7502  {
7503  __SUP_COUT__ << "Group Info cache appears empty. Attempting to regenerate."
7504  << __E__;
7505  cfgMgr->getAllTableInfo(true /*refresh*/,
7506  0 /* accumulatedWarnings */,
7507  "" /* errorFilterName */,
7508  true /* getGroupKeys */,
7509  true /* getGroupInfo */,
7510  true /* initializeActiveGroups */);
7511  }
7512 
7513  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
7514 
7515  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7516 
7517  TableGroupKey groupKey;
7518  std::string groupName;
7519  std::string groupString, groupTypeString, groupComment, groupCreationTime,
7520  groupAuthor;
7521  for(auto& groupInfo : allGroupInfo)
7522  {
7523  groupName = groupInfo.first;
7524  if(groupInfo.second.keys_.size() == 0)
7525  {
7526  __SUP_COUT__ << "Group name '" << groupName
7527  << "' found, but no keys so ignoring." << __E__;
7528  continue;
7529  }
7530 
7531  groupKey = *(groupInfo.second.keys_.rbegin());
7532 
7533  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
7534  xmlOut.dataSs_ << "<TableGroupKey value='" << groupKey << "'/>" << __E__;
7535 
7536  // trusting the cache!
7537  xmlOut.dataSs_ << "<TableGroupType value='"
7538  << groupInfo.second.latestKeyGroupTypeString_ << "'/>" << __E__;
7539  xmlOut.dataSs_ << "<TableGroupComment value='"
7540  << StringMacros::escapeString(
7541  groupInfo.second.latestKeyGroupComment_,
7542  true /* allowWhiteSpace */)
7543  << "'/>" << __E__;
7544  xmlOut.dataSs_ << "<TableGroupAuthor value='"
7545  << groupInfo.second.latestKeyGroupAuthor_ << "'/>" << __E__;
7546  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
7547  << groupInfo.second.latestKeyGroupCreationTime_ << "'/>" << __E__;
7548 
7549  // xmlOut.addTextElementToData("TableGroupName", groupName);
7550  // xmlOut.addTextElementToData("TableGroupKey", groupKey.toString());
7551 
7552  // // trusting the cache!
7553  // xmlOut.addTextElementToData("TableGroupType",
7554  // groupInfo.second.latestKeyGroupTypeString_);
7555  // xmlOut.addTextElementToData("TableGroupComment",
7556  // groupInfo.second.latestKeyGroupComment_);
7557  // xmlOut.addTextElementToData("TableGroupAuthor",
7558  // groupInfo.second.latestKeyGroupAuthor_);
7559  // xmlOut.addTextElementToData("TableGroupCreationTime",
7560  // groupInfo.second.latestKeyGroupCreationTime_);
7561 
7562  if(returnMembers)
7563  {
7564  // parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
7565  xmlOut.dataSs_ << "<TableGroupMembers value=''>" << __E__;
7566 
7567  for(auto& memberPair : groupInfo.second.latestKeyMemberMap_)
7568  {
7569  xmlOut.dataSs_ << "\t<MemberName value='" << memberPair.first << "'/>"
7570  << __E__;
7571  xmlOut.dataSs_ << "\t<MemberVersion value='" << memberPair.second << "'/>"
7572  << __E__;
7573 
7574  // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
7575  // xmlOut.addTextElementToParent(
7576  // "MemberVersion", memberPair.second.toString(), parentEl);
7577  }
7578  xmlOut.dataSs_ << "</TableGroupMembers>" << __E__;
7579  } // end if returnMembers
7580 
7581  // add other group keys to xml for this group name
7582  // but just empty members (not displayed anyway)
7583  for(auto& keyInSet : groupInfo.second.keys_)
7584  {
7585  if(keyInSet == groupKey)
7586  continue; // skip the lastest
7587 
7588  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
7589  xmlOut.dataSs_ << "<TableGroupKey value='" << keyInSet << "'/>" << __E__;
7590  // xmlOut.addTextElementToData("TableGroupName", groupName);
7591  // xmlOut.addTextElementToData("TableGroupKey", keyInSet.toString());
7592 
7593  // TODO -- make loadingHistoricalInfo an input parameter
7594  bool loadingHistoricalInfo = false;
7595  if(loadingHistoricalInfo)
7596  {
7597  groupComment = ""; // clear just in case failure
7598  try
7599  {
7600  cfgMgr->loadTableGroup(groupName,
7601  keyInSet,
7602  0,
7603  0,
7604  0,
7605  0,
7606  &groupComment,
7607  0,
7608  0, // mostly defaults
7609  true /*doNotLoadMembers*/,
7610  &groupTypeString);
7611  }
7612  catch(...)
7613  {
7614  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7615  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
7616  << keyInSet
7617  << ")' to extract group comment and type." << __E__;
7618  }
7619 
7620  xmlOut.dataSs_ << "<TableGroupType value='" << groupTypeString << "'/>"
7621  << __E__;
7622  xmlOut.dataSs_ << "<TableGroupComment value='"
7623  << StringMacros::escapeString(groupComment,
7624  true /* allowWhiteSpace */)
7625  << "'/>" << __E__;
7626  xmlOut.dataSs_ << "<TableGroupAuthor value='" << groupAuthor << "'/>"
7627  << __E__;
7628  xmlOut.dataSs_ << "<TableGroupCreationTime value='" << groupCreationTime
7629  << "'/>" << __E__;
7630  // xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7631  // xmlOut.addTextElementToData("TableGroupComment", groupComment);
7632  // xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
7633  // xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime);
7634  }
7635  else
7636  {
7637  // just use guess that historical groups are of same type
7638  xmlOut.dataSs_ << "<TableGroupType value='"
7639  << groupInfo.second.latestKeyGroupTypeString_ << "'/>"
7640  << __E__;
7641  xmlOut.dataSs_ << "<TableGroupComment value='"
7642  << ""
7643  << "'/>" << __E__;
7644  xmlOut.dataSs_ << "<TableGroupAuthor value='"
7645  << ""
7646  << "'/>" << __E__;
7647  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
7648  << ""
7649  << "'/>" << __E__;
7650  // // assume latest in cache reflects others (for speed)
7651  // xmlOut.addTextElementToData("TableGroupType",
7652  // groupInfo.second.latestKeyGroupTypeString_);
7653  // xmlOut.addTextElementToData("TableGroupComment",
7654  // groupInfo.second.latestKeyGroupComment_);
7655  // xmlOut.addTextElementToData("TableGroupAuthor",
7656  // groupInfo.second.latestKeyGroupAuthor_);
7657  // xmlOut.addTextElementToData("TableGroupCreationTime",
7658  // groupInfo.second.latestKeyGroupCreationTime_);
7659  }
7660 
7661  if(returnMembers)
7662  {
7663  //need to add empty group members, event for historical groups, for easier Javascript extraction
7664  xmlOut.dataSs_ << "<TableGroupMembers/>" << __E__;
7665  // xmlOut.addTextElementToData("TableGroupMembers", "");
7666  }
7667 
7668  } // end other key loop
7669  __SUP_COUTT__ << groupName << " runtime=" << cfgMgr->runTimeSeconds() << __E__;
7670  } // end primary group loop
7671  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7672 } // end handleTableGroupsXML()
7673 
7674 //==============================================================================
7686 void ConfigurationGUISupervisor::handleTablesXML(HttpXmlDocument& xmlOut,
7687  ConfigurationManagerRW* cfgMgr)
7688 {
7689  if(cfgMgr->getAllGroupInfo().size() == 0 || cfgMgr->getActiveVersions().size() == 0)
7690  {
7691  __SUP_COUT__ << "Table Info cache appears empty. Attempting to regenerate."
7692  << __E__;
7693  cfgMgr->getAllTableInfo(true /*refresh*/,
7694  0 /* accumulatedWarnings */,
7695  "" /* errorFilterName */,
7696  false /* getGroupKeys */,
7697  false /* getGroupInfo */,
7698  true /* initializeActiveGroups */);
7699  }
7700 
7701  xercesc::DOMElement* parentEl;
7702  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
7703 
7704  // construct specially ordered table name set
7705  std::set<std::string, StringMacros::IgnoreCaseCompareStruct> orderedTableSet;
7706  for(const auto& tablePair : allTableInfo)
7707  orderedTableSet.emplace(tablePair.first);
7708 
7709  // std::map<std::string, TableInfo>::const_iterator it = allTableInfo.begin();
7710 
7711  __SUP_COUT__ << "# of tables found: " << allTableInfo.size() << __E__;
7712 
7713  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
7714  cfgMgr->getVersionAliases();
7715 
7716  __SUP_COUT__ << "# of tables w/aliases: " << versionAliases.size() << __E__;
7717 
7718  for(const auto& orderedTableName : orderedTableSet) // while(it !=
7719  // allTableInfo.end())
7720  {
7721  std::map<std::string, TableInfo>::const_iterator it =
7722  allTableInfo.find(orderedTableName);
7723  if(it == allTableInfo.end())
7724  {
7725  __SS__ << "Impossible missing table in map '" << orderedTableName << "'"
7726  << __E__;
7727  __SS_THROW__;
7728  }
7729 
7730  // for each table name
7731  // get existing version keys
7732 
7733  // add system table name
7734  xmlOut.addTextElementToData("TableName", it->first);
7735  parentEl = xmlOut.addTextElementToData("TableVersions", "");
7736 
7737  // include aliases for this table (if the versions exist)
7738  if(versionAliases.find(it->first) != versionAliases.end())
7739  for(auto& aliasVersion : versionAliases[it->first])
7740  if(it->second.versions_.find(aliasVersion.second) !=
7741  it->second.versions_.end())
7742  // if(aliasVersion.first !=
7743  // ConfigurationManager::SCRATCH_VERSION_ALIAS) //NOT NEEDED IF
7744  // SCRATCH IS ALWAYS ALIAS
7745  xmlOut.addTextElementToParent(
7746  "Version",
7747  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
7748  parentEl);
7749  // else //NOT NEEDED IF SCRATCH IS ALWAYS ALIAS
7750  // __SUP_COUT_ERR__ << "Alias for table " << it->first << " is a
7751  // reserved alias '" <<
7752  // ConfigurationManager::SCRATCH_VERSION_ALIAS << "' - this
7753  // is illegal." << __E__;
7754 
7755  // //if scratch version exists, add an alias for it /NOT NEEDED IF SCRATCH IS
7756  // ALWAYS ALIAS
7757  // if(it->second.versions_.find(TableVersion(TableVersion::SCRATCH)) !=
7758  // it->second.versions_.end())
7759  // xmlOut.addTextElementToParent("Version",
7760  // ConfigurationManager::ALIAS_VERSION_PREAMBLE +
7761  // ConfigurationManager::SCRATCH_VERSION_ALIAS, parentEl);
7762 
7763  // get all table versions for the current table
7764  // except skip scratch version
7765  for(auto& version : it->second.versions_)
7766  if(!version.isScratchVersion())
7767  xmlOut.addTextElementToParent("Version", version.toString(), parentEl);
7768 
7769  //++it;
7770  } // end table loop
7771 
7772 } // end handleTablesXML()
7773 
7774 //==============================================================================
7781 void ConfigurationGUISupervisor::handleGetArtdaqNodeRecordsXML(
7782  HttpXmlDocument& xmlOut,
7783  ConfigurationManagerRW* cfgMgr,
7784  const std::string& modifiedTables)
7785 {
7786  __COUT__ << "Retrieving artdaq nodes..." << __E__;
7787 
7788  // setup active tables based on active groups and modified tables
7789  setupActiveTablesXML(
7790  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
7791 
7792  std::map<std::string /*type*/,
7793  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
7794  nodeTypeToObjectMap;
7795  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
7796  subsystemObjectMap;
7797 
7798  std::vector<std::string /*property*/> artdaqSupervisorInfo;
7799 
7800  std::string artdaqSupervisorName;
7801  const ARTDAQTableBase::ARTDAQInfo& info = ARTDAQTableBase::getARTDAQSystem(
7802  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap, artdaqSupervisorInfo);
7803 
7804  if(artdaqSupervisorInfo.size() != 4 /*expecting 4 artdaq Supervisor parameters*/)
7805  {
7806  __SUP_COUT__ << "No artdaq supervisor found." << __E__;
7807  return;
7808  }
7809 
7810  __SUP_COUT__ << "========== "
7811  << "Found " << info.subsystems.size() << " subsystems." << __E__;
7812 
7813  unsigned int paramIndex = 0; // start at first artdaq Supervisor parameter
7814 
7815  auto parentEl = xmlOut.addTextElementToData("artdaqSupervisor",
7816  artdaqSupervisorInfo[paramIndex++]);
7817 
7818  std::string typeString = "artdaqSupervisor";
7819 
7820  xmlOut.addTextElementToParent(
7821  typeString + "-status", artdaqSupervisorInfo[paramIndex++], parentEl);
7822  xmlOut.addTextElementToParent(
7823  typeString + "-contextAddress", artdaqSupervisorInfo[paramIndex++], parentEl);
7824  xmlOut.addTextElementToParent(
7825  typeString + "-contextPort", artdaqSupervisorInfo[paramIndex++], parentEl);
7826 
7827  for(auto& subsystem : info.subsystems)
7828  {
7829  typeString = "subsystem";
7830 
7831  __SUP_COUT__ << "\t\t"
7832  << "Found " << typeString << " " << subsystem.first << " \t := '"
7833  << subsystem.second.label << "'" << __E__;
7834 
7835  xmlOut.addTextElementToParent(typeString, subsystem.second.label, parentEl);
7836  xmlOut.addTextElementToParent(
7837  typeString + "-id", std::to_string(subsystem.first), parentEl);
7838 
7839  xmlOut.addTextElementToParent(typeString + "-sourcesCount",
7840  std::to_string(subsystem.second.sources.size()),
7841  parentEl);
7842 
7843  // destination
7844  xmlOut.addTextElementToParent(typeString + "-destination",
7845  std::to_string(subsystem.second.destination),
7846  parentEl);
7847 
7848  } // end subsystem handling
7849 
7850  __SUP_COUT__ << "========== "
7851  << "Found " << nodeTypeToObjectMap.size() << " process types." << __E__;
7852 
7853  for(auto& nameTypePair : nodeTypeToObjectMap)
7854  {
7855  typeString = nameTypePair.first;
7856 
7857  __SUP_COUT__ << "\t"
7858  << "Found " << nameTypePair.second.size() << " " << typeString
7859  << "(s)" << __E__;
7860 
7861  for(auto& artdaqNode : nameTypePair.second)
7862  {
7863  __SUP_COUT__ << "\t\t"
7864  << "Found '" << artdaqNode.first << "' " << typeString << __E__;
7865  __SUP_COUTV__(StringMacros::vectorToString(artdaqNode.second));
7866 
7867  if(artdaqNode.second.size() < 2)
7868  {
7869  __SUP_SS__ << "Impossible parameter size for node '" << artdaqNode.first
7870  << "' " << typeString << " - please notify admins!" << __E__;
7871  __SUP_SS_THROW__;
7872  }
7873 
7874  auto nodeEl =
7875  xmlOut.addTextElementToParent(typeString, artdaqNode.first, parentEl);
7876 
7877  paramIndex = 3; // start at 3 after subsystem parameter
7878  if(artdaqNode.second.size() > paramIndex)
7879  xmlOut.addTextElementToParent(
7880  typeString + "-multinode", artdaqNode.second[paramIndex++], nodeEl);
7881  if(artdaqNode.second.size() > paramIndex)
7882  xmlOut.addTextElementToParent(typeString + "-nodefixedwidth",
7883  artdaqNode.second[paramIndex++],
7884  nodeEl);
7885  if(artdaqNode.second.size() > paramIndex)
7886  xmlOut.addTextElementToParent(
7887  typeString + "-hostarray", artdaqNode.second[paramIndex++], nodeEl);
7888  if(artdaqNode.second.size() > paramIndex)
7889  xmlOut.addTextElementToParent(typeString + "-hostfixedwidth",
7890  artdaqNode.second[paramIndex++],
7891  nodeEl);
7892 
7893  paramIndex = 0; // return to starting parameter
7894  xmlOut.addTextElementToParent(
7895  typeString + "-status", artdaqNode.second[paramIndex++], parentEl);
7896  xmlOut.addTextElementToParent(
7897  typeString + "-hostname", artdaqNode.second[paramIndex++], parentEl);
7898  xmlOut.addTextElementToParent(
7899  typeString + "-subsystem", artdaqNode.second[paramIndex], parentEl);
7900  }
7901  } // end processor type handling
7902 
7903  __SUP_COUT__ << "Done retrieving artdaq nodes." << __E__;
7904 
7905 } // end handleGetArtdaqNodeRecordsXML()
7906 
7907 //==============================================================================
7914 void ConfigurationGUISupervisor::handleSaveArtdaqNodeRecordsXML(
7915  const std::string& nodeString,
7916  const std::string& subsystemString,
7917  HttpXmlDocument& xmlOut,
7918  ConfigurationManagerRW* cfgMgr,
7919  const std::string& modifiedTables)
7920 {
7921  __SUP_COUT__ << "Saving artdaq nodes..." << __E__;
7922 
7923  // setup active tables based on active groups and modified tables
7924  setupActiveTablesXML(
7925  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
7926 
7927  // start node object extraction from nodeString
7928  std::map<std::string /*type*/,
7929  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
7930  nodeTypeToObjectMap;
7931  {
7932  // nodeString format:
7933  // <type>:<nodeName>=<originalName>,<hostname>,<subsystemName>;<nodeName>=<originalName>,<hostname>,<subsystemName>;
7934  // ... |<type>:...|
7935  // repeat | separated types
7936  std::map<std::string /*type*/, std::string /*typeRecordSetString*/>
7937  nodeTypeToStringMap;
7938  StringMacros::getMapFromString(nodeString, nodeTypeToStringMap, {'|'}, {':'});
7939 
7940  __SUP_COUTV__(StringMacros::mapToString(nodeTypeToStringMap));
7941 
7942  for(auto& typePair : nodeTypeToStringMap)
7943  {
7944  if(typePair.first == "")
7945  continue; // skip empty names
7946 
7947  __SUP_COUTV__(StringMacros::decodeURIComponent(typePair.first));
7948 
7949  nodeTypeToObjectMap.emplace(
7950  std::make_pair(StringMacros::decodeURIComponent(typePair.first),
7951  std::map<std::string /*record*/,
7952  std::vector<std::string /*property*/>>()));
7953 
7954  std::map<std::string /*node*/, std::string /*nodeRecordSetString*/>
7955  nodeRecordToStringMap;
7956 
7957  StringMacros::getMapFromString(
7958  typePair.second, nodeRecordToStringMap, {';'}, {'='});
7959 
7960  __SUP_COUTV__(StringMacros::mapToString(nodeRecordToStringMap));
7961 
7962  for(auto& nodePair : nodeRecordToStringMap)
7963  {
7964  if(nodePair.first == "")
7965  continue; // skip empty names
7966 
7967  __SUP_COUTV__(StringMacros::decodeURIComponent(nodePair.first));
7968 
7969  std::vector<std::string /*property*/> nodePropertyVector;
7970 
7971  StringMacros::getVectorFromString(
7972  nodePair.second, nodePropertyVector, {','});
7973 
7974  __SUP_COUTV__(StringMacros::vectorToString(nodePropertyVector));
7975 
7976  // decode all properties
7977  for(unsigned int i = 0; i < nodePropertyVector.size(); ++i)
7978  {
7979  __SUP_COUTV__(
7980  StringMacros::decodeURIComponent(nodePropertyVector[i]));
7981 
7982  nodePropertyVector[i] =
7983  StringMacros::decodeURIComponent(nodePropertyVector[i]);
7984  }
7985 
7986  nodeTypeToObjectMap[typePair.first].emplace(
7987  std::make_pair(StringMacros::decodeURIComponent(nodePair.first),
7988  nodePropertyVector));
7989  }
7990  }
7991  } // end node object extraction from nodeString
7992 
7993  // start subsystem object extraction from subsystemString
7994  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
7995  subsystemObjectMap;
7996  {
7997  // subsystemString format:
7998  // <name>:<destination>;<name>:<destination>; ...;
7999  // repeat ; separated subsystems
8000 
8001  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8002  tmpSubsystemObjectMap;
8003  StringMacros::getMapFromString(
8004  subsystemString, tmpSubsystemObjectMap, {';'}, {':'});
8005 
8006  __SUP_COUTV__(StringMacros::mapToString(tmpSubsystemObjectMap));
8007 
8008  // decode all values (probably unnecessary, but more future proof)
8009  for(auto& subsystemPair : tmpSubsystemObjectMap)
8010  {
8011  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.first));
8012  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.second));
8013 
8014  subsystemObjectMap.emplace(
8015  std::make_pair(StringMacros::decodeURIComponent(subsystemPair.first),
8016  StringMacros::decodeURIComponent(subsystemPair.second)));
8017  }
8018  } // end subsystem object extraction from subsystemString
8019 
8020  ARTDAQTableBase::setAndActivateARTDAQSystem(
8021  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap);
8022 
8023  __SUP_COUT__ << "Done saving artdaq nodes." << __E__;
8024 } // end handleSaveArtdaqNodeRecordsXML()
8025 
8026 //==============================================================================
8033 void ConfigurationGUISupervisor::handleLoadArtdaqNodeLayoutXML(
8034  HttpXmlDocument& xmlOut,
8035  ConfigurationManager*
8036  cfgMgr, //force read-only config manager to avoid requiring user-lock (i.e., not ConfigurationManagerRW)
8037  const std::string& contextGroupName /* = "" */,
8038  const TableGroupKey& contextGroupKey /* = INVALID */) const
8039 {
8040  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8041 
8042  const std::string& finalContextGroupName =
8043  usingActiveGroups
8044  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8045  : contextGroupName;
8046  const TableGroupKey& finalContextGroupKey =
8047  usingActiveGroups
8048  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8049  : contextGroupKey;
8050 
8051  std::stringstream layoutPath;
8052  layoutPath << ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName << "_"
8053  << finalContextGroupKey << ".dat";
8054  __SUP_COUTV__(layoutPath.str());
8055 
8056  FILE* fp = fopen(layoutPath.str().c_str(), "r");
8057  if(!fp)
8058  {
8059  __SUP_COUT__ << "Layout file not found for '" << finalContextGroupName << "("
8060  << finalContextGroupKey << ")'" << __E__;
8061  return;
8062  }
8063 
8064  // file format is line by line
8065  // line 0 -- grid: <rows> <cols>
8066  // line 1-N -- node: <type> <name> <x-grid> <y-grid>
8067 
8068  const size_t maxLineSz = 1000;
8069  char line[maxLineSz];
8070  if(!fgets(line, maxLineSz, fp))
8071  {
8072  fclose(fp);
8073  return;
8074  }
8075  else
8076  {
8077  // extract grid
8078 
8079  unsigned int rows, cols;
8080 
8081  sscanf(line, "%u %u", &rows, &cols);
8082 
8083  __COUT__ << "Grid rows,cols = " << rows << "," << cols << __E__;
8084 
8085  xmlOut.addTextElementToData("grid-rows", std::to_string(rows));
8086  xmlOut.addTextElementToData("grid-cols", std::to_string(cols));
8087  }
8088 
8089  char name[maxLineSz];
8090  char type[maxLineSz];
8091  unsigned int x, y;
8092  while(fgets(line, maxLineSz, fp))
8093  {
8094  // extract node
8095  sscanf(line, "%s %s %u %u", type, name, &x, &y);
8096 
8097  xmlOut.addTextElementToData("node-type", type);
8098  xmlOut.addTextElementToData("node-name", name);
8099  xmlOut.addTextElementToData("node-x", std::to_string(x));
8100  xmlOut.addTextElementToData("node-y", std::to_string(y));
8101  } // end node extraction loop
8102 
8103  fclose(fp);
8104 
8105 } // end handleLoadArtdaqNodeLayoutXML()
8106 
8107 //==============================================================================
8114 void ConfigurationGUISupervisor::handleSaveArtdaqNodeLayoutXML(
8115  HttpXmlDocument& /*xmlOut*/,
8116  ConfigurationManagerRW* cfgMgr,
8117  const std::string& layoutString,
8118  const std::string& contextGroupName,
8119  const TableGroupKey& contextGroupKey)
8120 {
8121  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8122 
8123  const std::string& finalContextGroupName =
8124  usingActiveGroups
8125  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8126  : contextGroupName;
8127  const TableGroupKey& finalContextGroupKey =
8128  usingActiveGroups
8129  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8130  : contextGroupKey;
8131 
8132  __SUP_COUTV__(layoutString);
8133 
8134  std::stringstream layoutPath;
8135  layoutPath << ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName << "_"
8136  << finalContextGroupKey << ".dat";
8137  __SUP_COUTV__(layoutPath.str());
8138 
8139  std::vector<std::string> fields = StringMacros::getVectorFromString(layoutString);
8140  __SUP_COUTV__(StringMacros::vectorToString(fields));
8141 
8142  if(fields.size() < 2 || (fields.size() - 2) % 4 != 0)
8143  {
8144  __SUP_SS__ << "Invalid layout string fields size of " << fields.size() << __E__;
8145  __SUP_SS_THROW__;
8146  }
8147 
8148  FILE* fp = fopen(layoutPath.str().c_str(), "w");
8149  if(!fp)
8150  {
8151  __SUP_SS__ << "Could not open layout file for writing for '"
8152  << finalContextGroupName << "(" << finalContextGroupKey << ")'"
8153  << __E__;
8154  __SUP_SS_THROW__;
8155  }
8156 
8157  // match load code at ::handleLoadArtdaqNodeLayoutXML()
8158 
8159  // write grid
8160  fprintf(fp, "%s %s\n", fields[0].c_str(), fields[1].c_str());
8161 
8162  // write nodes
8163  for(unsigned int i = 2; i < fields.size(); i += 4)
8164  fprintf(fp,
8165  "%s %s %s %s\n",
8166  fields[i + 0].c_str(),
8167  fields[i + 1].c_str(),
8168  fields[i + 2].c_str(),
8169  fields[i + 3].c_str());
8170 
8171  fclose(fp);
8172 
8173 } // end handleSaveArtdaqNodeLayoutXML()
8174 
8175 //==============================================================================
8177 void ConfigurationGUISupervisor::handleOtherSubsystemActiveGroups(
8178  HttpXmlDocument& xmlOut,
8179  ConfigurationManagerRW* cfgMgr,
8180  bool getFullList,
8181  std::string targetSubsystem /* = "" */)
8182 try
8183 {
8184  try
8185  {
8186  ConfigurationTree node =
8187  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8188  auto children = node.getChildren();
8189 
8190  for(auto subsystem : children)
8191  {
8192  __SUP_COUTV__(subsystem.first);
8193  __SUP_COUTV__(
8194  StringMacros::vectorToString(subsystem.second.getChildrenNames()));
8195 
8196  std::string userPath =
8197  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8198  __SUP_COUTV__(userPath);
8199  }
8200  }
8201  catch(const std::runtime_error& e)
8202  {
8203  __SUP_COUT__ << "Ignoring errors in handling other subsystem active groups "
8204  "(assuming the subsystem information map is not setup in "
8205  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
8206  << ") -- here is the error: \n"
8207  << e.what() << __E__;
8208  return; //ignore errors if subsystems not defined
8209  }
8210 
8211  //else subsystems are defined, so do not ignore errors!
8212 
8213  ConfigurationTree node =
8214  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8215  auto children = node.getChildren();
8216  for(auto subsystem : children)
8217  {
8218  if(targetSubsystem != "" && targetSubsystem != subsystem.first)
8219  continue; //skip non-target subsystem
8220 
8221  xercesc::DOMElement* parent =
8222  xmlOut.addTextElementToData("SubsystemName", subsystem.first);
8223 
8224  if(!getFullList)
8225  continue;
8226 
8227  std::string filename, userDataPath;
8228  std::string username, hostname;
8229 
8230  std::map<std::string /*groupType*/,
8231  std::pair<std::string /*groupName*/, TableGroupKey>>
8232  retMap = cfgMgr->getOtherSubsystemActiveTableGroups(
8233  subsystem.first, &userDataPath, &hostname, &username);
8234 
8235  for(const auto& retPair : retMap)
8236  {
8237  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupName",
8238  retPair.second.first,
8239  parent);
8240  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupKey",
8241  retPair.second.second.toString(),
8242  parent);
8243  }
8244 
8245  std::vector<std::string> filenameTypes = {"Configured",
8246  "Started",
8247  "ActivatedConfig",
8248  "ActivatedContext",
8249  "ActivatedBackbone",
8250  "ActivatedIterator"};
8251 
8252  std::vector<std::string> filenames = {
8253  FSM_LAST_CONFIGURED_GROUP_ALIAS_FILE,
8254  FSM_LAST_STARTED_GROUP_ALIAS_FILE,
8255  ConfigurationManager::LAST_ACTIVATED_CONFIG_GROUP_FILE,
8256  ConfigurationManager::LAST_ACTIVATED_CONTEXT_GROUP_FILE,
8257  ConfigurationManager::LAST_ACTIVATED_BACKBONE_GROUP_FILE,
8258  ConfigurationManager::LAST_ACTIVATED_ITERATOR_GROUP_FILE};
8259 
8260  std::string userPath =
8261  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8262  auto splitPath = StringMacros::getVectorFromString(userPath, {':'});
8263  std::string cmdResult;
8264  for(unsigned int i = 0; i < filenames.size(); ++i)
8265  {
8266  filename = userDataPath + "/ServiceData/RunControlData/" + filenames[i];
8267  __SUP_COUTV__(filename);
8268 
8269  std::string tmpSubsystemFilename =
8270  ConfigurationManager::LAST_TABLE_GROUP_SAVE_PATH + "/" + filenames[i] +
8271  "." + subsystem.first;
8272  __SUP_COUTV__(tmpSubsystemFilename);
8273 
8274  if(splitPath.size() == 2) //must scp
8275  {
8276  if(username.size()) //has username
8277  cmdResult = StringMacros::exec(
8278  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + username +
8279  "@" + hostname + ":" + filename + " " + tmpSubsystemFilename +
8280  " 2>&1; cat " + tmpSubsystemFilename + " 2>&1")
8281  .c_str());
8282  else
8283  cmdResult = StringMacros::exec(
8284  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + hostname +
8285  ":" + filename + " " + tmpSubsystemFilename + " 2>&1; cat " +
8286  tmpSubsystemFilename + " 2>&1")
8287  .c_str());
8288  }
8289  else if(splitPath.size() == 1) //then can just directly access the file
8290  {
8291  cmdResult = StringMacros::exec(("rm " + tmpSubsystemFilename +
8292  " 2>/dev/null; cp " + filename + " " +
8293  tmpSubsystemFilename + " 2>&1; cat " +
8294  tmpSubsystemFilename + " 2>&1")
8295  .c_str());
8296  }
8297 
8298  __SUP_COUTV__(cmdResult);
8299  std::string timeString;
8300  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
8301  ConfigurationManager::loadGroupNameAndKey(
8302  filenames[i] + "." + subsystem.first, timeString);
8303 
8304  // fill return parameters
8305  xmlOut.addTextElementToParent(
8306  "Last" + filenameTypes[i] + "GroupName", theGroup.first, parent);
8307  xmlOut.addTextElementToParent("Last" + filenameTypes[i] + "GroupKey",
8308  theGroup.second.toString(),
8309  parent);
8310  xmlOut.addTextElementToParent(
8311  "Last" + filenameTypes[i] + "GroupTime", timeString, parent);
8312  } // end active/recent filename handling
8313 
8314  } //end subsystem loop
8315 } // end getSubsytemTableGroups()
8316 catch(const std::runtime_error& e)
8317 {
8318  __SUP_SS__
8319  << "An error occurred handling subsystem active groups (Please check the "
8320  "subsystem user data path information map setup in the Context group table "
8321  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
8322  << ") -- here is the error: \n"
8323  << e.what() << __E__;
8324  __SUP_SS_THROW__;
8325 } // end getSubsytemTableGroups() catch
8326 
8327 //==============================================================================
8329 void ConfigurationGUISupervisor::handleGroupDiff(
8330  HttpXmlDocument& xmlOut,
8331  ConfigurationManagerRW* cfgMgr,
8332  const std::string& groupName,
8333  const TableGroupKey& groupKey,
8334  const TableGroupKey& diffKey /* = TableGroupKey() */,
8335  const std::string& diffGroupNameInput /* = "" */)
8336 {
8337  //Steps:
8338  // - Get group type and load table map
8339  // - Get match type active group table map
8340  // - For each table, compare
8341  std::string diffGroupName;
8342 
8343  if(diffKey.isInvalid())
8344  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
8345  << ") with the active group." << __E__;
8346  else
8347  {
8348  if(diffGroupNameInput == "")
8349  diffGroupName = groupName;
8350  else
8351  diffGroupName = diffGroupNameInput;
8352 
8353  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
8354  << ") with group " << diffGroupName << "(" << diffKey << ")"
8355  << __E__;
8356  }
8357 
8358  try
8359  {
8360  std::map<std::string /*name*/, TableVersion /*version*/> memberMap, diffMemberMap;
8361  std::string groupType, accumulateErrors;
8362  std::stringstream diffReport;
8363  bool noDifference = true;
8364 
8365  cfgMgr->loadTableGroup(
8366  groupName,
8367  groupKey,
8368  false /*doActivate*/,
8369  &memberMap /*groupMembers*/,
8370  0 /*progressBar*/,
8371  &accumulateErrors /*accumulateErrors*/,
8372  0 /*groupComment*/,
8373  0 /*groupAuthor*/,
8374  0 /*groupCreationTime*/,
8375  false /*doNotLoadMember*/,
8376  (diffKey.isInvalid()
8377  ? &groupType
8378  : 0)); //for specified diff group (not active), do not need groupType
8379 
8380  __SUP_COUTV__(StringMacros::mapToString(memberMap));
8381 
8382  std::map<std::string /* groupType */, std::pair<std::string, TableGroupKey>>
8383  activeGroups;
8384  if(diffKey.isInvalid())
8385  {
8386  activeGroups = cfgMgr->getActiveTableGroups();
8387 
8388  __SUP_COUTV__(StringMacros::mapToString(activeGroups));
8389  __SUP_COUTV__(groupType);
8390 
8391  if(activeGroups.find(groupType) == activeGroups.end() ||
8392  activeGroups.at(groupType).first == "" ||
8393  activeGroups.at(groupType).second.isInvalid())
8394  {
8395  __SUP_SS__ << "Could not find an active group of type '" << groupType
8396  << ".' Please check the expected active configuration groups "
8397  "for errors (going to 'System View' of the Config App may "
8398  "reveal errors)."
8399  << __E__;
8400  __SUP_SS_THROW__;
8401  }
8402 
8403  __SUP_COUT__ << "active " << groupType << " group is "
8404  << activeGroups.at(groupType).first << "("
8405  << activeGroups.at(groupType).second << ")" << __E__;
8406 
8407  diffReport << "This difference report is between " << groupType
8408  << " group <b>'" << groupName << "(" << groupKey << ")'</b>"
8409  << " and active group <b>'" << activeGroups.at(groupType).first
8410  << "(" << activeGroups.at(groupType).second << ")'</b>." << __E__;
8411 
8412  cfgMgr->loadTableGroup(activeGroups.at(groupType).first,
8413  activeGroups.at(groupType).second,
8414  false /*doActivate*/,
8415  &diffMemberMap /*groupMembers*/,
8416  0 /*progressBar*/,
8417  &accumulateErrors /*accumulateErrors*/,
8418  0 /*groupComment*/,
8419  0 /*groupAuthor*/,
8420  0 /*groupCreationTime*/,
8421  false /*doNotLoadMember*/);
8422 
8423  diffReport << "\n\n"
8424  << "'" << groupName << "(" << groupKey << ")' has <b>"
8425  << memberMap.size() << " member tables</b>, and "
8426  << "'" << activeGroups.at(groupType).first << "("
8427  << activeGroups.at(groupType).second << ")' has <b>"
8428  << diffMemberMap.size() << " member tables</b>." << __E__;
8429  }
8430  else //specified diff group (not active), so do not need groupType
8431  {
8432  diffReport << "This difference report is between group <b>'" << groupName
8433  << "(" << groupKey << ")'</b>"
8434  << " and group <b>'" << diffGroupName << "(" << diffKey
8435  << ")'</b>." << __E__;
8436 
8437  cfgMgr->loadTableGroup(diffGroupName,
8438  diffKey,
8439  false /*doActivate*/,
8440  &diffMemberMap /*groupMembers*/,
8441  0 /*progressBar*/,
8442  &accumulateErrors /*accumulateErrors*/,
8443  0 /*groupComment*/,
8444  0 /*groupAuthor*/,
8445  0 /*groupCreationTime*/,
8446  false /*doNotLoadMember*/);
8447 
8448  diffReport << "\n\n"
8449  << "'" << groupName << "(" << groupKey << ")' has <b>"
8450  << memberMap.size() << " member tables</b>, and "
8451  << "'" << diffGroupName << "(" << diffKey << ")' has <b>"
8452  << diffMemberMap.size() << " member tables</b>." << __E__;
8453  }
8454 
8455  __SUP_COUTV__(StringMacros::mapToString(diffMemberMap));
8456 
8457  diffReport << "<INDENT><ol>";
8458 
8459  unsigned int tableDifferences = 0;
8460 
8461  for(auto& member : memberMap)
8462  {
8463  if(diffMemberMap.find(member.first) == diffMemberMap.end())
8464  {
8465  diffReport << "\n\n<li>"
8466  << "Table <b>" << member.first << "-v" << member.second
8467  << "</b> not found in active group."
8468  << "</li>" << __E__;
8469  noDifference = false;
8470  ++tableDifferences;
8471  continue;
8472  }
8473 
8474  __SUP_COUTT__ << "Comparing " << member.first << "-v" << member.second
8475  << " ... " << member.first << "-v"
8476  << diffMemberMap.at(member.first) << __E__;
8477 
8478  if(member.second == diffMemberMap.at(member.first))
8479  continue;
8480 
8481  diffReport << "\n\n<li>"
8482  << "Table <b>" << member.first << " v" << member.second
8483  << "</b> in " << groupName << "(" << groupKey << ")' ...vs... "
8484  << " <b>v" << diffMemberMap.at(member.first) << "</b> in "
8485  << diffGroupName << "(" << diffKey << ")':" << __E__;
8486 
8487  TableBase* table = cfgMgr->getTableByName(member.first);
8488 
8489  diffReport << "<ul>";
8490  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
8491  modifiedRecords; //useful for tree diff view display
8492  if(!table->diffTwoVersions(member.second,
8493  diffMemberMap.at(member.first),
8494  &diffReport,
8495  &modifiedRecords))
8496  {
8497  //difference found!
8498  noDifference = false;
8499  ++tableDifferences;
8500  auto parentEl =
8501  xmlOut.addTextElementToData("TableWithDiff", member.first);
8502  for(auto& modifiedRecord : modifiedRecords)
8503  {
8504  auto recordParentEl = xmlOut.addTextElementToParent(
8505  "RecordWithDiff", modifiedRecord.first, parentEl);
8506  for(auto& modifiedColumn : modifiedRecord.second)
8507  xmlOut.addTextElementToParent(
8508  "ColNameWithDiff", modifiedColumn, recordParentEl);
8509  }
8510  }
8511  diffReport << "</ul></li>";
8512 
8513  } //end member table comparison loop
8514 
8515  for(auto& diffMember : diffMemberMap)
8516  {
8517  if(memberMap.find(diffMember.first) == memberMap.end())
8518  {
8519  if(diffKey.isInvalid())
8520  diffReport << "\n\n<li>"
8521  << "Active Group Table <b>" << diffMember.first << "-v"
8522  << diffMember.second << "</b> not found in '" << groupName
8523  << "(" << groupKey << ")'."
8524  << "</li>" << __E__;
8525  else
8526  diffReport << "\n\n<li>" << diffGroupName << "(" << diffKey
8527  << ") Table <b>" << diffMember.first << "-v"
8528  << diffMember.second << "</b> not found in '" << groupName
8529  << "(" << groupKey << ")'."
8530  << "</li>" << __E__;
8531 
8532  noDifference = false;
8533  ++tableDifferences;
8534  continue;
8535  }
8536  }
8537  diffReport << "\n</ol></INDENT>";
8538 
8539  if(diffKey.isInvalid())
8540  {
8541  if(noDifference)
8542  diffReport << "\n\nNo difference found between "
8543  << "<b>'" << groupName << "(" << groupKey
8544  << ")'</b> and active group "
8545  << "<b>'" << activeGroups.at(groupType).first << "("
8546  << activeGroups.at(groupType).second << ")'</b>." << __E__;
8547  else
8548  diffReport << "\n\n<b>" << tableDifferences
8549  << "</b> member table differences identified between "
8550  << "<b>'" << groupName << "(" << groupKey
8551  << ")'</b> and active group "
8552  << "<b>'" << activeGroups.at(groupType).first << "("
8553  << activeGroups.at(groupType).second << ")'</b>." << __E__;
8554  }
8555  else
8556  {
8557  if(noDifference)
8558  diffReport << "\n\nNo difference found between "
8559  << "<b>'" << groupName << "(" << groupKey
8560  << ")'</b> and group "
8561  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
8562  << __E__;
8563  else
8564  diffReport << "\n\n<b>" << tableDifferences
8565  << "</b> member table differences identified between "
8566  << "<b>'" << groupName << "(" << groupKey
8567  << ")'</b> and group "
8568  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
8569  << __E__;
8570  }
8571 
8572  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
8573  xmlOut.addTextElementToData("DiffReport", diffReport.str());
8574  }
8575  catch(const std::runtime_error& e)
8576  {
8577  __SUP_COUT_ERR__ << "Caught error while differencing group " << groupName << "("
8578  << groupKey << ") with group " << diffGroupName << "(" << diffKey
8579  << ")" << __E__ << e.what() << __E__;
8580  throw; //rethrow
8581  }
8582 } // end handleGroupDiff()
8583 
8584 //==============================================================================
8586 void ConfigurationGUISupervisor::handleTableDiff(HttpXmlDocument& xmlOut,
8587  ConfigurationManagerRW* cfgMgr,
8588  const std::string& tableName,
8589  const TableVersion& vA,
8590  const TableVersion& vB)
8591 {
8592  __SUP_COUT__ << "Differencing tableName " << tableName << " v" << vA << " with v"
8593  << vB << __E__;
8594 
8595  //first make sure tables are loaded
8596  TableBase* table = cfgMgr->getTableByName(tableName);
8597 
8598  try
8599  {
8600  // locally accumulate 'manageable' errors getting the version to avoid
8601  // reverting to mockup
8602  std::string localAccumulatedErrors = "";
8603  cfgMgr->getVersionedTableByName(tableName,
8604  vA,
8605  false /*looseColumnMatching*/,
8606  &localAccumulatedErrors,
8607  false /*getRawData*/);
8608 
8609  if(localAccumulatedErrors != "")
8610  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
8611  }
8612  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
8613  {
8614  __SUP_SS__ << "Failed to get table " << tableName << " version " << vA;
8615  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
8616  __SUP_COUT_ERR__ << "\n" << ss.str();
8617 
8618  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
8619  }
8620  catch(...) // default to mock-up for fail-safe in GUI editor
8621  {
8622  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vA << __E__;
8623  try
8624  {
8625  throw;
8626  } //one more try to printout extra info
8627  catch(const std::exception& e)
8628  {
8629  ss << "Exception message: " << e.what();
8630  }
8631  catch(...)
8632  {
8633  }
8634 
8635  __SUP_COUT_ERR__ << "\n" << ss.str();
8636  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
8637  }
8638  try
8639  {
8640  // locally accumulate 'manageable' errors getting the version to avoid
8641  // reverting to mockup
8642  std::string localAccumulatedErrors = "";
8643  cfgMgr->getVersionedTableByName(tableName,
8644  vB,
8645  false /*looseColumnMatching*/,
8646  &localAccumulatedErrors,
8647  false /*getRawData*/);
8648 
8649  if(localAccumulatedErrors != "")
8650  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
8651  }
8652  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
8653  {
8654  __SUP_SS__ << "Failed to get table " << tableName << " version " << vB;
8655  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
8656  __SUP_COUT_ERR__ << "\n" << ss.str();
8657 
8658  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
8659  }
8660  catch(...) // default to mock-up for fail-safe in GUI editor
8661  {
8662  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vB << __E__;
8663  try
8664  {
8665  throw;
8666  } //one more try to printout extra info
8667  catch(const std::exception& e)
8668  {
8669  ss << "Exception message: " << e.what();
8670  }
8671  catch(...)
8672  {
8673  }
8674 
8675  __SUP_COUT_ERR__ << "\n" << ss.str();
8676  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
8677  }
8678 
8679  bool noDifference = true;
8680  std::stringstream diffReport;
8681 
8682  diffReport << "This difference report is between table " << tableName << " v" << vA
8683  << " and v" << vB << "</b>." << __E__;
8684 
8685  diffReport << "<INDENT>";
8686  diffReport << "<ul>";
8687  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
8688  modifiedRecords; //useful for tree diff view display
8689  if(!table->diffTwoVersions(vA, vB, &diffReport))
8690  noDifference = false; //difference found!
8691  diffReport << "</ul></INDENT>";
8692 
8693  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
8694  xmlOut.addTextElementToData("DiffReport", diffReport.str());
8695 } // end handleTableDiff()
8696 
8697 //==============================================================================
8700 void ConfigurationGUISupervisor::testXDAQContext()
8701 {
8702  // ConfigurationManagerRW cfgMgrInst("ExampleUser");
8703  // ConfigurationManagerRW* cfgMgr =& cfgMgrInst;
8704  // cfgMgr->testXDAQContext();
8705  // return;
8706 
8707  try
8708  {
8709  __SUP_COUT__ << "Attempting test activation of the context group." << __E__;
8710  ConfigurationManager cfgMgr; // create instance to activate saved groups
8711  }
8712  catch(const std::runtime_error& e)
8713  {
8714  __SUP_COUT_WARN__
8715  << "The test activation of the context group failed. Ignoring error: \n"
8716  << e.what() << __E__;
8717  }
8718  catch(...)
8719  {
8720  __SUP_COUT_WARN__ << "The test activation of the context group failed. Ignoring."
8721  << __E__;
8722  }
8723 
8724  return;
8725 
8727  // below has been used for debugging.
8728 
8729  // behave like a user
8730  // start with top level xdaq context
8731  // then add and delete rows proof-of-concept
8732  // export xml xdaq table file
8733 
8736  // behave like a new user
8737  //
8738  // ConfigurationManagerRW cfgMgrInst("ExampleUser");
8739 
8740  // ConfigurationManagerRW* cfgMgr =& cfgMgrInst;
8741 
8742  // // std::map<std::string, TableVersion> groupMembers;
8743  // // groupMembers["DesktopIcon"] = TableVersion(2);
8744  // // cfgMgr->saveNewTableGroup("test",
8745  // // groupMembers, "test comment");
8746 
8747  // //
8748  // const std::map<std::string, TableInfo>& allTableInfo =
8749  // cfgMgr->getAllTableInfo(true /* refresh*/);
8750  // __SUP_COUT__ << "allTableInfo.size() = " << allTableInfo.size() << __E__;
8751  // for(auto& mapIt : allTableInfo)
8752  // {
8753  // __SUP_COUT__ << "Table Name: " << mapIt.first << __E__;
8754  // __SUP_COUT__ << "\t\tExisting Versions: " << mapIt.second.versions_.size()
8755  // <<
8756  // __E__;
8757 
8758  // //get version key for the current system table key
8759  // for (auto& v:mapIt.second.versions_)
8760  // {
8761  // __SUP_COUT__ << "\t\t" << v << __E__;
8762  // }
8763  // }
8764  // __SUP_COUTT__ << "Group Info end runtime=" << cfgMgr->runTimeSeconds() << __E__;
8765  // testXDAQContext just a test bed for navigating the new config tree
8766  // cfgMgr->testXDAQContext();
8767 
8770 } // end testXDAQContext()
virtual void forceSupervisorPropertyValues(void) override
override to force supervisor property values (and ignore user settings)
virtual void setSupervisorPropertyDefaults(void) override
ConfigurationGUISupervisor(xdaq::ApplicationStub *s)
static xdaq::Application * instantiate(xdaq::ApplicationStub *s)