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