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