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