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