otsdaq_utilities  v2_04_00
ConfigurationGUISupervisor.cc
1 #include "otsdaq-utilities/ConfigurationGUI/ConfigurationGUISupervisor.h"
2 #include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h"
3 #include "otsdaq-core/Macros/CoutMacros.h"
4 #include "otsdaq-core/MessageFacility/MessageFacility.h"
5 #include "otsdaq-core/TablePluginDataFormats/IterateTable.h"
6 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
7 
8 #if MESSAGEFACILITY_HEX_VERSION > 0x20100
9 #include <boost/stacktrace.hpp>
10 #endif
11 
12 #include "otsdaq-core/TablePluginDataFormats/XDAQContextTable.h" //for context relaunch
13 
14 #include <xdaq/NamespaceURI.h>
15 
16 #include <iostream>
17 #include <map>
18 #include <utility>
19 
20 using namespace ots;
21 
22 #undef __MF_SUBJECT__
23 #define __MF_SUBJECT__ "CfgGUI"
24 
25 #define TABLE_INFO_PATH std::string(__ENV__("TABLE_INFO_PATH")) + "/"
26 #define TABLE_INFO_EXT std::string("Info.xml")
27 
30 xdaq::Application* ConfigurationGUISupervisor::instantiate(xdaq::ApplicationStub* stub)
31 {
32  return new ConfigurationGUISupervisor(stub);
33 }
34 
35 //========================================================================================================================
36 // new user gets a config mgr assigned
37 // user can fill any of the tables (fill from version or init empty), which becomes the
38 // active view for that table
39 ConfigurationGUISupervisor::ConfigurationGUISupervisor(xdaq::ApplicationStub* stub)
40  : CoreSupervisorBase(stub)
41 {
42  __SUP_COUT__ << "Constructor started." << __E__;
43 
44  INIT_MF("ConfigurationGUI");
45 
46  init();
47  __SUP_COUT__ << "Constructor complete." << __E__;
48 }
49 
50 //========================================================================================================================
51 ConfigurationGUISupervisor::~ConfigurationGUISupervisor(void) { destroy(); }
52 
53 //========================================================================================================================
54 void ConfigurationGUISupervisor::init(void)
55 {
56  __SUP_COUT__ << "Initializing..." << __E__;
57 
58  __SUP_COUT__ << "Activating saved context, which may prepare for normal mode..."
59  << __E__;
60  try
61  {
62  testXDAQContext(); // test context group activation
63  }
64  catch(...)
65  {
66  __COUT_WARN__ << "Failed test context group activation. otsdaq, in Normal mode, "
67  "will not launch when this test fails. "
68  << "Check the active context group from within Wizard Mode."
69  << __E__;
70  }
71 }
72 
73 //========================================================================================================================
74 void ConfigurationGUISupervisor::destroy(void)
75 {
76  // called by destructor
77  for(std::map<std::string, ConfigurationManagerRW*>::iterator it =
78  userConfigurationManagers_.begin();
79  it != userConfigurationManagers_.end();
80  ++it)
81  {
82  delete it->second;
83  it->second = 0;
84  }
85  userConfigurationManagers_.clear();
86 
87  // NOTE: Moved to ConfigurationGUISupervisor [FIXME is this correct?? should we use
88  // shared_ptr??]
89  if(ConfigurationInterface::getInstance(true) != 0)
90  delete ConfigurationInterface::getInstance(true);
91 }
92 
93 //========================================================================================================================
94 void ConfigurationGUISupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
95 {
96  cgicc::Cgicc cgiIn(in);
97  std::string configWindowName =
98  CgiDataUtilities::getData(cgiIn, "configWindowName"); // from GET
99  if(configWindowName == "tableEditor")
100  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
101  "src='/WebPath/html/ConfigurationTableEditor.html?urn="
102  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
103  if(configWindowName == "iterate")
104  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
105  "src='/WebPath/html/Iterate.html?urn="
106  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
107  else
108  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
109  "src='/WebPath/html/ConfigurationGUI.html?urn="
110  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
111 }
112 
113 //========================================================================================================================
114 // When overriding, setup default property values here
115 // called by CoreSupervisorBase constructor
116 void ConfigurationGUISupervisor::setSupervisorPropertyDefaults(void)
117 {
118  CorePropertySupervisorBase::setSupervisorProperty(
119  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
120  "*=10 | deleteTreeNodeRecords=255 | saveTableInfo=255 | "
121  "deleteTableInfo=255"); // experienced users to edit, admins to delete
122  CorePropertySupervisorBase::setSupervisorProperty(
123  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
124  "*"); // all
125 }
126 
127 //========================================================================================================================
128 // forceSupervisorPropertyValues
129 // override to force supervisor property values (and ignore user settings)
130 void ConfigurationGUISupervisor::forceSupervisorPropertyValues()
131 {
132  CorePropertySupervisorBase::setSupervisorProperty(
133  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
134  ""); // none
135  CorePropertySupervisorBase::setSupervisorProperty(
136  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes,
137  "*"); // all
138 }
139 
140 //========================================================================================================================
141 void ConfigurationGUISupervisor::request(const std::string& requestType,
142  cgicc::Cgicc& cgiIn,
143  HttpXmlDocument& xmlOut,
144  const WebUsers::RequestUserInfo& userInfo) try
145 {
146  // Commands
147 
148  // gatewayLaunchOTS -- and other StartOTS commands
149 
150  // saveTableInfo
151  // deleteTableInfo
152  // flattenToSystemAliases
153  // versionTracking
154  // getColumnTypes
155  // getGroupAliases
156  // setGroupAliasInActiveBackbone
157  // setVersionAliasInActiveBackbone
158  // setAliasOfGroupMembers
159  // getVersionAliases
160  // getTableGroups
161  // getTableGroupType
162  // getTables
163  // getContextMemberNames
164  // getBackboneMemberNames
165  // getIterateMemberNames
166  // getSpecificTableGroup
167  // saveNewTableGroup
168  // getSpecificTable
169  // saveSpecificTable
170  // clearTableTemporaryVersions
171  // clearTableCachedVersions
172  //
173  // ---- associated with JavaScript Config API
174  // getTreeView
175  // getTreeNodeCommonFields
176  // getUniqueFieldValuesForRecords
177  // getTreeNodeFieldValues
178  // setTreeNodeFieldValues
179  // addTreeNodeRecords
180  // deleteTreeNodeRecords
181  // ---- end associated with JavaScript Config API
182  //
183  // activateTableGroup
184  // getActiveTableGroups
185  // copyViewToCurrentColumns
186  // saveTreeNodeEdit
187  // getAffectedActiveGroups
188  // getLinkToChoices
189  // getLastTableGroups
190  // mergeGroups
191  //
192  // ---- associated with JavaScript Iterate App
193  // savePlanCommandSequence
194  // ---- end associated with JavaScript Iterate App
195 
196  // acquire user's configuration manager based on username& activeSessionIndex
197  std::string refresh = CgiDataUtilities::getData(cgiIn, "refresh"); // from GET
198  //__SUP_COUT__ << "refresh: " << refresh << __E__;
199 
200  // refresh to reload from info files and db (maintains temporary views!)
201  ConfigurationManagerRW* cfgMgr = refreshUserSession(
202  userInfo.username_, userInfo.activeUserSessionIndex_, (refresh == "1"));
203 
204  if(requestType == "saveTableInfo")
205  {
206  std::string tableName =
207  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
208  std::string columnCSV =
209  CgiDataUtilities::postData(cgiIn, "columnCSV"); // from POST
210  std::string allowOverwrite =
211  CgiDataUtilities::getData(cgiIn, "allowOverwrite"); // from GET
212  std::string tableDescription =
213  CgiDataUtilities::postData(cgiIn, "tableDescription"); // from POST
214  std::string columnChoicesCSV =
215  CgiDataUtilities::postData(cgiIn, "columnChoicesCSV"); // from POST
216 
217  // columnCSV = CgiDataUtilities::decodeURIComponent(columnCSV);
218  // tableDescription = CgiDataUtilities::decodeURIComponent(tableDescription);
219 
220  __SUP_COUT__ << "tableName: " << tableName << __E__;
221  __SUP_COUT__ << "columnCSV: " << columnCSV << __E__;
222  __SUP_COUT__ << "tableDescription: " << tableDescription << __E__;
223  __SUP_COUT__ << "columnChoicesCSV: " << columnChoicesCSV << __E__;
224  __SUP_COUT__ << "allowOverwrite: " << allowOverwrite << __E__;
225 
226  if(!allSupervisorInfo_.isWizardMode())
227  {
228  __SUP_SS__ << "Improper permissions for saving table info." << __E__;
229  xmlOut.addTextElementToData("Error", ss.str());
230  }
231  else
232  handleSaveTableInfoXML(xmlOut,
233  cfgMgr,
234  tableName,
235  columnCSV,
236  tableDescription,
237  columnChoicesCSV,
238  allowOverwrite == "1");
239  }
240  else if(requestType == "deleteTableInfo")
241  {
242  std::string tableName =
243  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
244  __SUP_COUT__ << "tableName: " << tableName << __E__;
245  handleDeleteTableInfoXML(xmlOut, cfgMgr, tableName);
246  }
247  else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz" ||
248  requestType == "flattenToSystemAliases")
249  {
250  // NOTE: similar to Supervisor version but does not keep active sessions
251  __SUP_COUT_WARN__ << requestType << " command received! " << __E__;
252  __MOUT_WARN__ << requestType << " command received! " << __E__;
253 
254  // now launch
255  __SUP_COUT_INFO__ << "Launching " << requestType << "... " << __E__;
256 
257  __SUP_COUT__ << "Extracting target context hostnames... " << __E__;
258  std::vector<std::string> hostnames;
259 
260  // flattenToSystemAliases should always work in wiz mode!
261  if(requestType == "flattenToSystemAliases" &&
262  CorePropertySupervisorBase::allSupervisorInfo_.isWizardMode())
263  {
264  hostnames.push_back(__ENV__("OTS_CONFIGURATION_WIZARD_SUPERVISOR_SERVER"));
265  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
266  }
267  else
268  {
269  try
270  {
271  cfgMgr->init(); // completely reset to re-align with any changes
272 
273  const XDAQContextTable* contextTable =
274  cfgMgr->__GET_CONFIG__(XDAQContextTable);
275 
276  auto contexts = contextTable->getContexts();
277  unsigned int i, j;
278  for(const auto& context : contexts)
279  {
280  if(!context.status_)
281  continue;
282 
283  // find last slash
284  j = 0; // default to whole string
285  for(i = 0; i < context.address_.size(); ++i)
286  if(context.address_[i] == '/')
287  j = i + 1;
288  hostnames.push_back(context.address_.substr(j));
289  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
290  }
291  }
292  catch(...)
293  {
294  __SUP_SS__ << "The Configuration Manager could not be initialized to "
295  "extract contexts."
296  << __E__;
297 
298  __SUP_COUT_ERR__ << "\n" << ss.str();
299  return;
300  }
301  }
302 
303  if(hostnames.size() == 0)
304  {
305  __SUP_SS__ << "No hostnames found to launch command '" + requestType +
306  "'... Is there a valid Context group activated?"
307  << __E__;
308  __SUP_COUT_ERR__ << "\n" << ss.str();
309 
310  xmlOut.addTextElementToData("Error", ss.str());
311  }
312 
313  for(const auto& hostname : hostnames)
314  {
315  std::string fn = (std::string(__ENV__("SERVICE_DATA_PATH")) +
316  "/StartOTS_action_" + hostname + ".cmd");
317  FILE* fp = fopen(fn.c_str(), "w");
318  if(fp)
319  {
320  if(requestType == "gatewayLaunchOTS")
321  fprintf(fp, "LAUNCH_OTS");
322  else if(requestType == "gatewayLaunchWiz")
323  fprintf(fp, "LAUNCH_WIZ");
324  else if(requestType == "flattenToSystemAliases")
325  {
326  fprintf(fp, "FLATTEN_TO_SYSTEM_ALIASES");
327  fclose(fp);
328  break; // only do at one host
329  }
330 
331  fclose(fp);
332  }
333  else
334  __SUP_COUT_ERR__ << "Unable to open command file: " << fn << __E__;
335  }
336  }
337  // else if(requestType == "launchOTS")
338  // {
339  // __SUP_COUT_WARN__ << "launchOTS command received! Launching... " << __E__;
340  //
341  // FILE* fp = fopen((std::string(__ENV__("SERVICE_DATA_PATH")) +
342  // "/StartOTS_action.cmd").c_str(),"w");
343  // if(fp)
344  // {
345  // fprintf(fp,"LAUNCH_OTS");
346  // fclose(fp);
347  // }
348  // else
349  // __SUP_COUT_ERR__ << "Unable to open command file: " <<
350  //(std::string(__ENV__("SERVICE_DATA_PATH")) +
351  // "/StartOTS_action.cmd") << __E__;
352  // }
353  // else if(requestType == "launchWiz")
354  // {
355  // __SUP_COUT_WARN__ << "launchWiz command received! Launching... " << __E__;
356  //
357  // FILE* fp = fopen((std::string(__ENV__("SERVICE_DATA_PATH")) +
358  // "/StartOTS_action.cmd").c_str(),"w");
359  // if(fp)
360  // {
361  // fprintf(fp,"LAUNCH_WIZ");
362  // fclose(fp);
363  // }
364  // else
365  // __SUP_COUT_ERR__ << "Unable to open command file: " <<
366  //(std::string(__ENV__("SERVICE_DATA_PATH")) +
367  // "/StartOTS_action.cmd") << __E__;
368  // }
369  // else if(requestType == "flattenToSystemAliases")
370  // {
371  // __SUP_COUT_WARN__ << "flattenToSystemAliases command received! Launching... "
372  //<<
373  //__E__;
374  //
375  // FILE* fp = fopen((std::string(__ENV__("SERVICE_DATA_PATH")) +
376  // "/StartOTS_action.cmd").c_str(),"w");
377  // if(fp)
378  // {
379  // fprintf(fp,"FLATTEN_TO_SYSTEM_ALIASES");
380  // fclose(fp);
381  // }
382  // else
383  // __SUP_COUT_ERR__ << "Unable to open command file: " <<
384  //(std::string(__ENV__("SERVICE_DATA_PATH")) +
385  // "/StartOTS_action.cmd") << __E__;
386  // }
387  else if(requestType == "versionTracking")
388  {
389  std::string type = CgiDataUtilities::getData(cgiIn, "Type"); // from GET
390  __SUP_COUT__ << "type: " << type << __E__;
391 
392  if(type == "Get")
393  xmlOut.addTextElementToData(
394  "versionTrackingStatus",
395  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
396  else if(type == "ON")
397  {
398  ConfigurationInterface::setVersionTrackingEnabled(true);
399  xmlOut.addTextElementToData(
400  "versionTrackingStatus",
401  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
402  }
403  else if(type == "OFF")
404  {
405  ConfigurationInterface::setVersionTrackingEnabled(false);
406  xmlOut.addTextElementToData(
407  "versionTrackingStatus",
408  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
409  }
410  }
411  else if(requestType == "getColumnTypes")
412  {
413  // return the possible column types and their defaults
414  std::vector<std::string> allTypes = TableViewColumnInfo::getAllTypesForGUI();
415  std::vector<std::string> allDataTypes =
416  TableViewColumnInfo::getAllDataTypesForGUI();
417  std::map<std::pair<std::string, std::string>, std::string> allDefaults =
418  TableViewColumnInfo::getAllDefaultsForGUI();
419 
420  for(const auto& type : allTypes)
421  xmlOut.addTextElementToData("columnTypeForGUI", type);
422  for(const auto& dataType : allDataTypes)
423  xmlOut.addTextElementToData("columnDataTypeForGUI", dataType);
424 
425  for(const auto& colDefault : allDefaults)
426  {
427  xmlOut.addTextElementToData("columnDefaultDataType", colDefault.first.first);
428  xmlOut.addTextElementToData("columnDefaultTypeFilter",
429  colDefault.first.second);
430  xmlOut.addTextElementToData("columnDefaultValue", colDefault.second);
431  }
432  }
433  else if(requestType == "getGroupAliases")
434  {
435  // Since this is called from setting up System View in the config GUI
436  // give option for reloading "persistent" active configurations
437  bool reloadActive =
438  1 == CgiDataUtilities::getDataAsInt(cgiIn, "reloadActiveGroups"); // from GET
439 
440  __SUP_COUT__ << "reloadActive: " << reloadActive << __E__;
441  bool wasError = false;
442  if(reloadActive)
443  {
444  try
445  {
446  cfgMgr->clearAllCachedVersions();
447  cfgMgr->restoreActiveTableGroups(true);
448  }
449  catch(std::runtime_error& e)
450  {
451  __SUP_SS__ << ("Error loading active groups!\n\n" + std::string(e.what()))
452  << __E__;
453  __SUP_COUT_ERR__ << "\n" << ss.str();
454  xmlOut.addTextElementToData("Error", ss.str());
455  wasError = true;
456  }
457  catch(...)
458  {
459  __SUP_SS__ << ("Error loading active groups!\n\n") << __E__;
460  __SUP_COUT_ERR__ << "\n" << ss.str();
461  xmlOut.addTextElementToData("Error", ss.str());
462  wasError = true;
463  }
464  }
465 
466  handleGroupAliasesXML(xmlOut, cfgMgr);
467  }
468  else if(requestType == "setGroupAliasInActiveBackbone")
469  {
470  std::string groupAlias =
471  CgiDataUtilities::getData(cgiIn, "groupAlias"); // from GET
472  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
473  // GET
474  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
475 
476  __SUP_COUT__ << "groupAlias: " << groupAlias << __E__;
477  __SUP_COUT__ << "groupName: " << groupName << __E__;
478  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
479 
480  handleSetGroupAliasInBackboneXML(xmlOut,
481  cfgMgr,
482  groupAlias,
483  groupName,
484  TableGroupKey(groupKey),
485  userInfo.username_);
486  }
487  else if(requestType == "setVersionAliasInActiveBackbone")
488  {
489  std::string versionAlias =
490  CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET
491  std::string tableName =
492  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
493  std::string version = CgiDataUtilities::getData(cgiIn, "version"); // from GET
494 
495  __SUP_COUT__ << "versionAlias: " << versionAlias << __E__;
496  __SUP_COUT__ << "tableName: " << tableName << __E__;
497  __SUP_COUT__ << "version: " << version << __E__;
498 
499  handleSetVersionAliasInBackboneXML(xmlOut,
500  cfgMgr,
501  versionAlias,
502  tableName,
503  TableVersion(version),
504  userInfo.username_);
505  }
506  else if(requestType == "setAliasOfGroupMembers")
507  {
508  std::string versionAlias =
509  CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET
510  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
511  // GET
512  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
513 
514  __SUP_COUT__ << "versionAlias: " << versionAlias << __E__;
515  __SUP_COUT__ << "groupName: " << groupName << __E__;
516  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
517 
518  handleAliasGroupMembersInBackboneXML(xmlOut,
519  cfgMgr,
520  versionAlias,
521  groupName,
522  TableGroupKey(groupKey),
523  userInfo.username_);
524  }
525  else if(requestType == "getVersionAliases")
526  {
527  handleVersionAliasesXML(xmlOut, cfgMgr);
528  }
529  else if(requestType == "getTableGroups")
530  {
531  bool doNotReturnMembers =
532  CgiDataUtilities::getDataAsInt(cgiIn, "doNotReturnMembers") == 1
533  ? true
534  : false; // from GET
535 
536  __SUP_COUT__ << "doNotReturnMembers: " << doNotReturnMembers << __E__;
537  handleTableGroupsXML(xmlOut, cfgMgr, !doNotReturnMembers);
538  }
539  else if(requestType == "getTableGroupType")
540  {
541  std::string tableList =
542  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
543  __SUP_COUT__ << "tableList: " << tableList << __E__;
544 
545  handleGetTableGroupTypeXML(xmlOut, cfgMgr, tableList);
546  }
547  else if(requestType == "getTables")
548  {
549  std::string allowIllegalColumns =
550  CgiDataUtilities::getData(cgiIn, "allowIllegalColumns"); // from GET
551 
552  __SUP_COUT__ << "allowIllegalColumns: " << allowIllegalColumns << __E__;
553 
554  handleTablesXML(xmlOut, cfgMgr, allowIllegalColumns == "1");
555  }
556  else if(requestType == "getContextMemberNames")
557  {
558  std::set<std::string> members = cfgMgr->getContextMemberNames();
559 
560  for(auto& member : members)
561  xmlOut.addTextElementToData("ContextMember", member);
562  }
563  else if(requestType == "getBackboneMemberNames")
564  {
565  std::set<std::string> members = cfgMgr->getBackboneMemberNames();
566 
567  for(auto& member : members)
568  xmlOut.addTextElementToData("BackboneMember", member);
569  }
570  else if(requestType == "getIterateMemberNames")
571  {
572  std::set<std::string> members = cfgMgr->getIterateMemberNames();
573 
574  for(auto& member : members)
575  xmlOut.addTextElementToData("IterateMember", member);
576  }
577  else if(requestType == "getSpecificTableGroup")
578  {
579  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
580  // GET
581  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
582 
583  __SUP_COUT__ << "groupName: " << groupName << __E__;
584  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
585 
586  handleGetTableGroupXML(xmlOut, cfgMgr, groupName, TableGroupKey(groupKey));
587  }
588  else if(requestType == "saveNewTableGroup")
589  {
590  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
591  // GET
592  bool ignoreWarnings =
593  CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings"); // from GET
594  bool allowDuplicates =
595  CgiDataUtilities::getDataAsInt(cgiIn, "allowDuplicates"); // from GET
596  bool lookForEquivalent =
597  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
598  std::string tableList =
599  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
600  std::string comment =
601  CgiDataUtilities::getData(cgiIn, "groupComment"); // from GET
602 
603  __SUP_COUT__ << "saveNewTableGroup: " << groupName << __E__;
604  __SUP_COUT__ << "tableList: " << tableList << __E__;
605  __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__;
606  __SUP_COUT__ << "allowDuplicates: " << allowDuplicates << __E__;
607  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
608  __SUP_COUT__ << "comment: " << comment << __E__;
609 
610  handleCreateTableGroupXML(xmlOut,
611  cfgMgr,
612  groupName,
613  tableList,
614  allowDuplicates,
615  ignoreWarnings,
616  comment,
617  lookForEquivalent);
618  }
619  else if(requestType == "getSpecificTable")
620  {
621  std::string tableName =
622  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
623  std::string versionStr = CgiDataUtilities::getData(cgiIn, "version"); // from GET
624  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
625  int chunkSize = CgiDataUtilities::getDataAsInt(cgiIn, "chunkSize"); // from GET
626 
627  std::string allowIllegalColumns =
628  CgiDataUtilities::getData(cgiIn, "allowIllegalColumns"); // from GET
629  __SUP_COUT__ << "allowIllegalColumns: " << (allowIllegalColumns == "1") << __E__;
630 
631  __SUP_COUT__ << "getSpecificTable: " << tableName << " versionStr: " << versionStr
632  << " chunkSize: " << chunkSize << " dataOffset: " << dataOffset
633  << __E__;
634 
635  TableVersion version;
636  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
637  std::string versionAlias;
638 
639  if(allTableInfo.find(tableName) != allTableInfo.end())
640  {
641  if(versionStr == "" && // take latest version if no version specified
642  allTableInfo.at(tableName).versions_.size())
643  version = *(allTableInfo.at(tableName).versions_.rbegin());
644  else if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
645  {
646  // convert alias to version
647  std::map<std::string /*table*/,
648  std::map<std::string /*alias*/, TableVersion>>
649  versionAliases = cfgMgr->getVersionAliases();
650 
651  versionAlias = versionStr.substr(
652  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
653  // if(versionAlias ==
654  // ConfigurationManager::SCRATCH_VERSION_ALIAS)
656  // {
657  // version = TableVersion::SCRATCH;
658  // __SUP_COUT__ << "version alias translated to: " << version
659  //<<
660  //__E__;
661  // }
662  // else
663  if(versionAliases.find(tableName) != versionAliases.end() &&
664  versionAliases[tableName].find(versionStr.substr(
665  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())) !=
666  versionAliases[tableName].end())
667  {
668  version = versionAliases[tableName][versionStr.substr(
669  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())];
670  __SUP_COUT__ << "version alias translated to: " << version << __E__;
671  }
672  else
673  __SUP_COUT_WARN__
674  << "version alias '"
675  << versionStr.substr(
676  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
677  << "'was not found in active version aliases!" << __E__;
678  }
679  else // else take specified version
680  version = atoi(versionStr.c_str());
681  }
682 
683  __SUP_COUT__ << "version: " << version << __E__;
684 
685  handleGetTableXML(xmlOut,
686  cfgMgr,
687  tableName,
688  TableVersion(version),
689  (allowIllegalColumns == "1"));
690  // append author column default value
691  xmlOut.addTextElementToData("DefaultRowValue", userInfo.username_);
692  }
693  else if(requestType == "saveSpecificTable")
694  {
695  std::string tableName =
696  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
697  int version = CgiDataUtilities::getDataAsInt(cgiIn, "version"); // from GET
698  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
699  bool sourceTableAsIs =
700  CgiDataUtilities::getDataAsInt(cgiIn, "sourceTableAsIs"); // from GET
701  bool lookForEquivalent =
702  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
703  int temporary = CgiDataUtilities::getDataAsInt(cgiIn, "temporary"); // from GET
704  std::string comment =
705  CgiDataUtilities::getData(cgiIn, "tableComment"); // from GET
706 
707  std::string data = CgiDataUtilities::postData(cgiIn, "data"); // from POST
708  // data format: commas and semi-colons indicate new row
709  // r0c0,r0c1,...,r0cN,;r1c0,...
710 
711  __SUP_COUT__ << "tableName: " << tableName << " version: " << version
712  << " temporary: " << temporary << " dataOffset: " << dataOffset
713  << __E__;
714  __SUP_COUT__ << "comment: " << comment << __E__;
715  __SUP_COUT__ << "data: " << data << __E__;
716  __SUP_COUT__ << "sourceTableAsIs: " << sourceTableAsIs << __E__;
717  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
718 
719  handleCreateTableXML(xmlOut,
720  cfgMgr,
721  tableName,
722  TableVersion(version),
723  temporary,
724  data,
725  dataOffset,
726  userInfo.username_,
727  comment,
728  sourceTableAsIs,
729  lookForEquivalent);
730  }
731  else if(requestType == "clearTableTemporaryVersions")
732  {
733  std::string tableName =
734  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
735  __SUP_COUT__ << "tableName: " << tableName << __E__;
736 
737  try
738  {
739  cfgMgr->eraseTemporaryVersion(tableName);
740  }
741  catch(std::runtime_error& e)
742  {
743  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
744  xmlOut.addTextElementToData(
745  "Error", "Error clearing temporary views!\n " + std::string(e.what()));
746  }
747  catch(...)
748  {
749  __SUP_COUT__ << "Error detected!\n\n " << __E__;
750  xmlOut.addTextElementToData("Error", "Error clearing temporary views! ");
751  }
752  }
753  else if(requestType == "clearTableCachedVersions")
754  {
755  std::string tableName =
756  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
757  __SUP_COUT__ << "tableName: " << tableName << __E__;
758 
759  try
760  {
761  cfgMgr->clearCachedVersions(tableName);
762  }
763  catch(std::runtime_error& e)
764  {
765  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
766  xmlOut.addTextElementToData(
767  "Error", "Error clearing cached views!\n " + std::string(e.what()));
768  }
769  catch(...)
770  {
771  __SUP_COUT__ << "Error detected!\n\n " << __E__;
772  xmlOut.addTextElementToData("Error", "Error clearing cached views! ");
773  }
774  }
775  else if(requestType == "getTreeView")
776  {
777  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
778  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
779  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
780  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
781  std::string filterList = CgiDataUtilities::postData(cgiIn, "filterList");
782  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
783  bool hideStatusFalse = CgiDataUtilities::getDataAsInt(cgiIn, "hideStatusFalse");
784 
785  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
786  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
787  __SUP_COUT__ << "startPath: " << startPath << __E__;
788  __SUP_COUT__ << "depth: " << depth << __E__;
789  __SUP_COUT__ << "hideStatusFalse: " << hideStatusFalse << __E__;
790  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
791  __SUP_COUT__ << "filterList: " << filterList << __E__;
792 
793  handleFillTreeViewXML(xmlOut,
794  cfgMgr,
795  configGroup,
796  TableGroupKey(configGroupKey),
797  startPath,
798  depth,
799  hideStatusFalse,
800  modifiedTables,
801  filterList);
802  }
803  else if(requestType == "getTreeNodeCommonFields")
804  {
805  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
806  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
807  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
808  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
809  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
810  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
811  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
812 
813  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
814  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
815  __SUP_COUT__ << "startPath: " << startPath << __E__;
816  __SUP_COUT__ << "depth: " << depth << __E__;
817  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
818  __SUP_COUT__ << "recordList: " << recordList << __E__;
819  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
820 
821  handleFillTreeNodeCommonFieldsXML(xmlOut,
822  cfgMgr,
823  configGroup,
824  TableGroupKey(configGroupKey),
825  startPath,
826  depth,
827  modifiedTables,
828  recordList,
829  fieldList);
830  }
831  else if(requestType == "getUniqueFieldValuesForRecords")
832  {
833  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
834  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
835  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
836  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
837  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
838  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
839 
840  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
841  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
842  __SUP_COUT__ << "startPath: " << startPath << __E__;
843  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
844  __SUP_COUT__ << "recordList: " << recordList << __E__;
845  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
846 
847  handleFillUniqueFieldValuesForRecordsXML(xmlOut,
848  cfgMgr,
849  configGroup,
850  TableGroupKey(configGroupKey),
851  startPath,
852  modifiedTables,
853  recordList,
854  fieldList);
855  }
856  else if(requestType == "getTreeNodeFieldValues")
857  {
858  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
859  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
860  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
861  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
862  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
863  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
864 
865  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
866  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
867  __SUP_COUT__ << "startPath: " << startPath << __E__;
868  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
869  __SUP_COUT__ << "recordList: " << recordList << __E__;
870  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
871 
872  handleFillGetTreeNodeFieldValuesXML(xmlOut,
873  cfgMgr,
874  configGroup,
875  TableGroupKey(configGroupKey),
876  startPath,
877  modifiedTables,
878  recordList,
879  fieldList);
880  }
881  else if(requestType == "setTreeNodeFieldValues")
882  {
883  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
884  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
885  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
886  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
887  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
888  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
889  std::string valueList = CgiDataUtilities::postData(cgiIn, "valueList");
890 
891  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
892  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
893  __SUP_COUT__ << "startPath: " << startPath << __E__;
894  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
895  __SUP_COUT__ << "valueList: " << valueList << __E__;
896  __SUP_COUT__ << "recordList: " << recordList << __E__;
897  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
898 
899  handleFillSetTreeNodeFieldValuesXML(xmlOut,
900  cfgMgr,
901  configGroup,
902  TableGroupKey(configGroupKey),
903  startPath,
904  modifiedTables,
905  recordList,
906  fieldList,
907  valueList,
908  userInfo.username_);
909  }
910  else if(requestType == "addTreeNodeRecords")
911  {
912  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
913  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
914  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
915  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
916  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
917 
918  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
919  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
920  __SUP_COUT__ << "startPath: " << startPath << __E__;
921  __SUP_COUT__ << "recordList: " << recordList << __E__;
922  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
923 
924  handleFillCreateTreeNodeRecordsXML(xmlOut,
925  cfgMgr,
926  configGroup,
927  TableGroupKey(configGroupKey),
928  startPath,
929  modifiedTables,
930  recordList,
931  userInfo.username_);
932  }
933  else if(requestType == "deleteTreeNodeRecords")
934  {
935  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
936  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
937  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
938  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
939  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
940 
941  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
942  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
943  __SUP_COUT__ << "startPath: " << startPath << __E__;
944  __SUP_COUT__ << "recordList: " << recordList << __E__;
945  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
946 
947  handleFillDeleteTreeNodeRecordsXML(xmlOut,
948  cfgMgr,
949  configGroup,
950  TableGroupKey(configGroupKey),
951  startPath,
952  modifiedTables,
953  recordList);
954  }
955  else if(requestType == "getAffectedActiveGroups")
956  {
957  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
958  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
959  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
960  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
961  __SUP_COUT__ << "groupName: " << groupName << __E__;
962  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
963 
964  handleGetAffectedGroupsXML(
965  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey), modifiedTables);
966  }
967  else if(requestType == "saveTreeNodeEdit")
968  {
969  std::string editNodeType = CgiDataUtilities::getData(cgiIn, "editNodeType");
970  std::string targetTable = CgiDataUtilities::getData(cgiIn, "targetTable");
971  std::string targetTableVersion =
972  CgiDataUtilities::getData(cgiIn, "targetTableVersion");
973  std::string targetUID = CgiDataUtilities::getData(cgiIn, "targetUID");
974  std::string targetColumn = CgiDataUtilities::getData(cgiIn, "targetColumn");
975  std::string newValue = CgiDataUtilities::postData(cgiIn, "newValue");
976 
977  __SUP_COUT__ << "editNodeType: " << editNodeType << __E__;
978  __SUP_COUT__ << "targetTable: " << targetTable << __E__;
979  __SUP_COUT__ << "targetTableVersion: " << targetTableVersion << __E__;
980  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
981  __SUP_COUT__ << "targetColumn: " << targetColumn << __E__;
982  __SUP_COUT__ << "newValue: " << newValue << __E__;
983 
984  handleSaveTreeNodeEditXML(xmlOut,
985  cfgMgr,
986  targetTable,
987  TableVersion(targetTableVersion),
988  editNodeType,
989  CgiDataUtilities::decodeURIComponent(targetUID),
990  CgiDataUtilities::decodeURIComponent(targetColumn),
991  newValue,
992  userInfo.username_);
993  }
994  else if(requestType == "getLinkToChoices")
995  {
996  std::string linkToTableName = CgiDataUtilities::getData(cgiIn, "linkToTableName");
997  std::string linkToTableVersion =
998  CgiDataUtilities::getData(cgiIn, "linkToTableVersion");
999  std::string linkIdType = CgiDataUtilities::getData(cgiIn, "linkIdType");
1000  std::string linkIndex = CgiDataUtilities::decodeURIComponent(
1001  CgiDataUtilities::getData(cgiIn, "linkIndex"));
1002  std::string linkInitId = CgiDataUtilities::getData(cgiIn, "linkInitId");
1003 
1004  __SUP_COUT__ << "linkToTableName: " << linkToTableName << __E__;
1005  __SUP_COUT__ << "linkToTableVersion: " << linkToTableVersion << __E__;
1006  __SUP_COUT__ << "linkIdType: " << linkIdType << __E__;
1007  __SUP_COUT__ << "linkIndex: " << linkIndex << __E__;
1008  __SUP_COUT__ << "linkInitId: " << linkInitId << __E__;
1009 
1010  handleGetLinkToChoicesXML(xmlOut,
1011  cfgMgr,
1012  linkToTableName,
1013  TableVersion(linkToTableVersion),
1014  linkIdType,
1015  linkIndex,
1016  linkInitId);
1017  }
1018  else if(requestType == "activateTableGroup")
1019  {
1020  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1021  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1022  bool ignoreWarnings = CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings");
1023 
1024  __SUP_COUT__ << "Activating config: " << groupName << "(" << groupKey << ")"
1025  << __E__;
1026  __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__;
1027 
1028  // add flag for GUI handling
1029  xmlOut.addTextElementToData("AttemptedGroupActivation", "1");
1030  xmlOut.addTextElementToData("AttemptedGroupActivationName", groupName);
1031  xmlOut.addTextElementToData("AttemptedGroupActivationKey", groupKey);
1032 
1033  std::string accumulatedTreeErrors;
1034  try
1035  {
1036  cfgMgr->activateTableGroup(
1037  groupName,
1038  TableGroupKey(groupKey),
1039  ignoreWarnings
1040  ? 0
1041  : &accumulatedTreeErrors); // if ignore warning then pass null
1042  }
1043  catch(std::runtime_error& e)
1044  {
1045  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1046  // single quotes around the groupName and groupKey and have them be
1047  // the first single quotes encountered in the error mesage!
1048  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1049  xmlOut.addTextElementToData(
1050  "Error",
1051  "Error activating config group '" + groupName + "(" + groupKey + ")" +
1052  ".' Please see details below:\n\n" + std::string(e.what()));
1053  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1054  << " (" << groupKey << ")" << __E__;
1055  try // just in case any lingering pieces, lets deactivate
1056  {
1057  cfgMgr->destroyTableGroup(groupName, true);
1058  }
1059  catch(...)
1060  {
1061  }
1062  }
1063  catch(cet::exception& e)
1064  {
1065  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1066  // single quotes around the groupName and groupKey and have them be
1067  // the first single quotes encountered in the error mesage!
1068 
1069  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1070  xmlOut.addTextElementToData("Error",
1071  "Error activating config group '" + groupName +
1072  "(" + groupKey + ")" + "!'\n\n" +
1073  std::string(e.what()));
1074  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1075  << " (" << groupKey << ")" << __E__;
1076  try // just in case any lingering pieces, lets deactivate
1077  {
1078  cfgMgr->destroyTableGroup(groupName, true);
1079  }
1080  catch(...)
1081  {
1082  }
1083  }
1084  catch(...)
1085  {
1086  __SUP_COUT__ << "Error detected!" << __E__;
1087  throw; // unexpected exception!
1088  }
1089 
1090  if(accumulatedTreeErrors != "")
1091  xmlOut.addTextElementToData("Error",
1092  "Warnings were found when activating group '" +
1093  groupName + "(" + groupKey + ")" +
1094  "'! Please see details below:\n\n" +
1095  accumulatedTreeErrors);
1096  }
1097  else if(requestType == "getActiveTableGroups")
1098  ; // do nothing, since they are always returned
1099  else if(requestType == "copyViewToCurrentColumns")
1100  {
1101  std::string tableName =
1102  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1103  std::string sourceVersion = CgiDataUtilities::getData(cgiIn, "sourceVersion");
1104 
1105  __SUP_COUT__ << "tableName: " << tableName << __E__;
1106  __SUP_COUT__ << "sourceVersion: " << sourceVersion << __E__;
1107  __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__;
1108 
1109  // copy source version to new temporary version
1110  TableVersion newTemporaryVersion;
1111  try
1112  {
1113  // force emptying of cache for this table
1114  newTemporaryVersion =
1115  cfgMgr->copyViewToCurrentColumns(tableName, TableVersion(sourceVersion));
1116  //
1117  // getTableByName(tableName)->reset();
1118  //
1119  // //make sure source version is loaded
1120  // //need to load with loose column rules!
1121  // config = cfgMgr->getVersionedTableByName(tableName,
1122  // TableVersion(sourceVersion), true);
1123  //
1124  // //copy from source version to a new temporary version
1125  // newTemporaryVersion = config->copyView(config->getView(),
1126  // TableVersion(),userName);
1127 
1128  __SUP_COUT__ << "New temporary version = " << newTemporaryVersion << __E__;
1129  }
1130  catch(std::runtime_error& e)
1131  {
1132  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1133  xmlOut.addTextElementToData("Error",
1134  "Error copying view from '" + tableName + "_v" +
1135  sourceVersion + "'! " +
1136  std::string(e.what()));
1137  }
1138  catch(...)
1139  {
1140  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1141  xmlOut.addTextElementToData(
1142  "Error",
1143  "Error copying view from '" + tableName + "_v" + sourceVersion + "'! ");
1144  }
1145 
1146  handleGetTableXML(xmlOut, cfgMgr, tableName, newTemporaryVersion);
1147  }
1148  else if(requestType == "getLastTableGroups")
1149  {
1150  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor =
1151  allSupervisorInfo_.isWizardMode() ? allSupervisorInfo_.getWizardDescriptor()
1152  : allSupervisorInfo_.getGatewayDescriptor();
1153 
1154  std::string timeString;
1155  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
1156  theRemoteWebUsers_.getLastConfigGroup(
1157  gatewaySupervisor, "Configured", timeString);
1158  xmlOut.addTextElementToData("LastConfiguredGroupName", theGroup.first);
1159  xmlOut.addTextElementToData("LastConfiguredGroupKey", theGroup.second.toString());
1160  xmlOut.addTextElementToData("LastConfiguredGroupTime", timeString);
1161  theGroup = theRemoteWebUsers_.getLastConfigGroup(
1162  gatewaySupervisor, "Started", timeString);
1163  xmlOut.addTextElementToData("LastStartedGroupName", theGroup.first);
1164  xmlOut.addTextElementToData("LastStartedGroupKey", theGroup.second.toString());
1165  xmlOut.addTextElementToData("LastStartedGroupTime", timeString);
1166  }
1167  else if(requestType == "savePlanCommandSequence")
1168  {
1169  std::string planName = CgiDataUtilities::getData(cgiIn, "planName"); // from GET
1170  std::string commands = CgiDataUtilities::postData(cgiIn, "commands"); // from
1171  // POST
1172  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1173  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1174  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1175 
1176  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1177  __SUP_COUT__ << "planName: " << planName << __E__;
1178  __SUP_COUT__ << "commands: " << commands << __E__;
1179  __SUP_COUT__ << "groupName: " << groupName << __E__;
1180  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
1181 
1182  handleSavePlanCommandSequenceXML(xmlOut,
1183  cfgMgr,
1184  groupName,
1185  TableGroupKey(groupKey),
1186  modifiedTables,
1187  userInfo.username_,
1188  planName,
1189  commands);
1190  }
1191  else if(requestType == "mergeGroups")
1192  {
1193  std::string groupANameContext =
1194  CgiDataUtilities::getData(cgiIn, "groupANameContext");
1195  std::string groupAKeyContext =
1196  CgiDataUtilities::getData(cgiIn, "groupAKeyContext");
1197  std::string groupBNameContext =
1198  CgiDataUtilities::getData(cgiIn, "groupBNameContext");
1199  std::string groupBKeyContext =
1200  CgiDataUtilities::getData(cgiIn, "groupBKeyContext");
1201  std::string groupANameConfig =
1202  CgiDataUtilities::getData(cgiIn, "groupANameConfig");
1203  std::string groupAKeyConfig = CgiDataUtilities::getData(cgiIn, "groupAKeyConfig");
1204  std::string groupBNameConfig =
1205  CgiDataUtilities::getData(cgiIn, "groupBNameConfig");
1206  std::string groupBKeyConfig = CgiDataUtilities::getData(cgiIn, "groupBKeyConfig");
1207  std::string mergeApproach = CgiDataUtilities::getData(cgiIn, "mergeApproach");
1208 
1209  __SUP_COUTV__(groupANameContext);
1210  __SUP_COUTV__(groupAKeyContext);
1211  __SUP_COUTV__(groupBNameContext);
1212  __SUP_COUTV__(groupBKeyContext);
1213  __SUP_COUTV__(groupANameConfig);
1214  __SUP_COUTV__(groupAKeyConfig);
1215  __SUP_COUTV__(groupBNameConfig);
1216  __SUP_COUTV__(groupBKeyConfig);
1217  __SUP_COUTV__(mergeApproach);
1218 
1219  handleMergeGroupsXML(xmlOut,
1220  cfgMgr,
1221  groupANameContext,
1222  TableGroupKey(groupAKeyContext),
1223  groupBNameContext,
1224  TableGroupKey(groupBKeyContext),
1225  groupANameConfig,
1226  TableGroupKey(groupAKeyConfig),
1227  groupBNameConfig,
1228  TableGroupKey(groupBKeyConfig),
1229  userInfo.username_,
1230  mergeApproach);
1231  }
1232  else
1233  {
1234  __SUP_SS__ << "requestType '" << requestType << "' request not recognized."
1235  << __E__;
1236  __SUP_COUT__ << "\n" << ss.str();
1237  xmlOut.addTextElementToData("Error", ss.str());
1238  }
1239 
1240  //__SUP_COUT__ << "Wrapping up..." << __E__;
1241 
1242  // always add active config groups to xml response
1243  std::map<std::string /*type*/, std::pair<std::string /*groupName*/, TableGroupKey>>
1244  activeGroupMap = cfgMgr->getActiveTableGroups();
1245 
1246  for(auto& type : activeGroupMap)
1247  {
1248  xmlOut.addTextElementToData(type.first + "-ActiveGroupName", type.second.first);
1249  xmlOut.addTextElementToData(type.first + "-ActiveGroupKey",
1250  type.second.second.toString());
1251  //__SUP_COUT__ << "ActiveGroup " << type.first << " " << type.second.first << "("
1252  //<< type.second.second << ")" << __E__;
1253  }
1254 
1255  // always add version tracking bool
1256  xmlOut.addTextElementToData(
1257  "versionTracking",
1258  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1259  //
1260  // __SUP_COUT__ << __E__;
1261  // xmlOut.outputXmlDocument(0,true,true);
1262  // __SUP_COUT__ << __E__;
1263 
1264 } // end ::request()
1265 catch(const std::runtime_error& e)
1266 {
1267  __SS__ << "A fatal error occurred while handling the request '" << requestType
1268  << ".' Error: " << e.what() << __E__;
1269  __COUT_ERR__ << "\n" << ss.str();
1270  xmlOut.addTextElementToData("Error", ss.str());
1271 
1272  try
1273  {
1274  // always add version tracking bool
1275  xmlOut.addTextElementToData(
1276  "versionTracking",
1277  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1278  }
1279  catch(...)
1280  {
1281  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1282  }
1283 }
1284 catch(...)
1285 {
1286  __SS__ << "An unknown fatal error occurred while handling the request '"
1287  << requestType << ".'" << __E__;
1288  __COUT_ERR__ << "\n" << ss.str();
1289  xmlOut.addTextElementToData("Error", ss.str());
1290 
1291  try
1292  {
1293  // always add version tracking bool
1294  xmlOut.addTextElementToData(
1295  "versionTracking",
1296  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1297  }
1298  catch(...)
1299  {
1300  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1301  }
1302 }
1303 
1304 //========================================================================================================================
1305 // handleGetAffectedGroupsXML
1306 // checks which of the active groups are affected
1307 // by the tables changing in the modified tables list.
1308 //
1309 // returns for each group affected:
1310 // the group name/key affected
1311 // and modified member map
1312 void ConfigurationGUISupervisor::handleGetAffectedGroupsXML(
1313  HttpXmlDocument& xmlOut,
1314  ConfigurationManagerRW* cfgMgr,
1315  const std::string& rootGroupName,
1316  const TableGroupKey& rootGroupKey,
1317  const std::string& modifiedTables) try
1318 {
1319  // determine type of rootGroup
1320  // replace the matching type in considered groups
1321  // for each considered table group
1322  //
1323  // check if there is a modified table that is also a member of that group
1324  // if so,
1325  // make xml entry pair
1326 
1327  std::map<std::string, std::pair<std::string, TableGroupKey>> consideredGroups =
1328  cfgMgr->getActiveTableGroups();
1329 
1330  // check that there is a context and config group to consider
1331  // if there is not, then pull from failed list
1332  if(consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT]
1333  .second.isInvalid())
1334  {
1335  __SUP_COUT__ << "Finding a context group to consider..." << __E__;
1336  if(cfgMgr->getFailedTableGroups().find(
1337  ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT) !=
1338  cfgMgr->getFailedTableGroups().end())
1339  {
1340  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT] =
1341  cfgMgr->getFailedTableGroups().at(
1342  ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT);
1343  }
1344  else if(cfgMgr->getFailedTableGroups().find(
1345  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN) !=
1346  cfgMgr->getFailedTableGroups().end())
1347  {
1348  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT] =
1349  cfgMgr->getFailedTableGroups().at(
1350  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN);
1351  }
1352  }
1353  if(consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION]
1354  .second.isInvalid())
1355  {
1356  __SUP_COUT__ << "Finding a table group to consider..." << __E__;
1357  if(cfgMgr->getFailedTableGroups().find(
1358  ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION) !=
1359  cfgMgr->getFailedTableGroups().end())
1360  {
1361  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION] =
1362  cfgMgr->getFailedTableGroups().at(
1363  ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION);
1364  }
1365  else if(cfgMgr->getFailedTableGroups().find(
1366  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN) !=
1367  cfgMgr->getFailedTableGroups().end())
1368  {
1369  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION] =
1370  cfgMgr->getFailedTableGroups().at(
1371  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN);
1372  }
1373  }
1374 
1375  __SUP_COUTV__(StringMacros::mapToString(consideredGroups));
1376 
1377  // determine the type of table group
1378  try
1379  {
1380  std::map<std::string /*name*/, TableVersion /*version*/> rootGroupMemberMap;
1381 
1382  cfgMgr->loadTableGroup(rootGroupName,
1383  rootGroupKey,
1384  0,
1385  &rootGroupMemberMap,
1386  0,
1387  0,
1388  0,
1389  0,
1390  0, // defaults
1391  true); // doNotLoadMember
1392 
1393  const std::string& groupType = cfgMgr->getTypeNameOfGroup(rootGroupMemberMap);
1394 
1395  consideredGroups[groupType] =
1396  std::pair<std::string, TableGroupKey>(rootGroupName, rootGroupKey);
1397  }
1398  catch(const std::runtime_error& e)
1399  {
1400  // if actual group name was attempted re-throw
1401  if(rootGroupName.size())
1402  {
1403  __SUP_SS__ << "Failed to determine type of table group for " << rootGroupName
1404  << "(" << rootGroupKey << ")! " << e.what() << __E__;
1405  __SUP_COUT_ERR__ << "\n" << ss.str();
1406  __SS_THROW__;
1407  }
1408 
1409  // else assume it was the intention to just consider the active groups
1410  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1411  "name - assuming this was intentional."
1412  << __E__;
1413  }
1414  catch(...)
1415  {
1416  // if actual group name was attempted re-throw
1417  if(rootGroupName.size())
1418  {
1419  __SUP_COUT_ERR__ << "Failed to determine type of table group for "
1420  << rootGroupName << "(" << rootGroupKey << ")!" << __E__;
1421  throw;
1422  }
1423 
1424  // else assume it was the intention to just consider the active groups
1425  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1426  "name - assuming this was intentional."
1427  << __E__;
1428  }
1429 
1430  std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap;
1431  std::map<std::string /*name*/, TableVersion /*version*/>::iterator
1432  modifiedTablesMapIt;
1433  {
1434  std::istringstream f(modifiedTables);
1435  std::string table, version;
1436  while(getline(f, table, ','))
1437  {
1438  getline(f, version, ',');
1439  modifiedTablesMap.insert(
1440  std::pair<std::string /*name*/, TableVersion /*version*/>(
1441  table, TableVersion(version)));
1442  }
1443  __SUP_COUT__ << modifiedTables << __E__;
1444  for(auto& pair : modifiedTablesMap)
1445  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second
1446  << __E__;
1447  }
1448 
1449  bool affected;
1450  DOMElement* parentEl;
1451  std::string groupComment;
1452  for(auto group : consideredGroups)
1453  {
1454  if(group.second.second.isInvalid())
1455  continue; // skip invalid
1456 
1457  __SUP_COUT__ << "Considering " << group.first << " group " << group.second.first
1458  << " (" << group.second.second << ")" << __E__;
1459 
1460  affected = false;
1461 
1462  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
1463  cfgMgr->loadTableGroup(group.second.first,
1464  group.second.second,
1465  0,
1466  &memberMap,
1467  0,
1468  0,
1469  &groupComment,
1470  0,
1471  0, // mostly defaults
1472  true /*doNotLoadMember*/);
1473 
1474  __SUP_COUT__ << "groupComment = " << groupComment << __E__;
1475 
1476  for(auto& table : memberMap)
1477  {
1478  if((modifiedTablesMapIt = modifiedTablesMap.find(table.first)) !=
1479  modifiedTablesMap
1480  .end() && // check if version is different for member table
1481  table.second != (*modifiedTablesMapIt).second)
1482  {
1483  __SUP_COUT__ << "Affected by " << (*modifiedTablesMapIt).first << ":"
1484  << (*modifiedTablesMapIt).second << __E__;
1485  affected = true;
1486  memberMap[table.first] = (*modifiedTablesMapIt).second;
1487  }
1488  }
1489 
1490  if(affected)
1491  {
1492  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1493  xmlOut.addTextElementToParent("GroupName", group.second.first, parentEl);
1494  xmlOut.addTextElementToParent(
1495  "GroupKey", group.second.second.toString(), parentEl);
1496  xmlOut.addTextElementToParent("GroupComment", groupComment, parentEl);
1497 
1498  for(auto& table : memberMap)
1499  {
1500  xmlOut.addTextElementToParent("MemberName", table.first, parentEl);
1501  xmlOut.addTextElementToParent(
1502  "MemberVersion", table.second.toString(), parentEl);
1503  }
1504  }
1505  }
1506 }
1507 catch(std::runtime_error& e)
1508 {
1509  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1510  xmlOut.addTextElementToData(
1511  "Error", "Error getting affected groups! " + std::string(e.what()));
1512 }
1513 catch(...)
1514 {
1515  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1516  xmlOut.addTextElementToData("Error", "Error getting affected groups! ");
1517 }
1518 
1519 //========================================================================================================================
1520 // setupActiveTables
1521 // setup active tables based on input group and modified tables
1522 //
1523 // if groupName == "" || groupKey is invalid
1524 // then do for active groups
1525 // if valid, then replace appropriate active group with specified group
1526 // Also replace active versions of modified tables with the specified version
1527 void ConfigurationGUISupervisor::setupActiveTablesXML(
1528  HttpXmlDocument& xmlOut,
1529  ConfigurationManagerRW* cfgMgr,
1530  const std::string& groupName,
1531  const TableGroupKey& groupKey,
1532  const std::string& modifiedTables,
1533  bool refreshAll,
1534  bool doGetGroupInfo,
1535  std::map<std::string /*name*/, TableVersion /*version*/>* returnMemberMap,
1536  bool outputActiveTables,
1537  std::string* accumulatedErrors) try
1538 {
1539  // if(accumulatedErrors)
1540  // *accumulatedErrors = "";
1541 
1542  xmlOut.addTextElementToData("configGroup", groupName);
1543  xmlOut.addTextElementToData("configGroupKey", groupKey.toString());
1544 
1545  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
1546 
1547  // reload all tables so that partially loaded tables are not allowed
1548  if(usingActiveGroups || refreshAll)
1549  {
1550  __SUP_COUT__ << "Refreshing all table info..." << __E__;
1551  cfgMgr->getAllTableInfo(true, accumulatedErrors); // do refresh
1552 
1553  if(accumulatedErrors && *accumulatedErrors != "")
1554  __SUP_COUTV__(*accumulatedErrors);
1555  }
1556 
1557  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(false);
1558 
1559  std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap;
1560  std::map<std::string /*name*/, TableVersion /*version*/>::iterator
1561  modifiedTablesMapIt;
1562 
1563  if(usingActiveGroups)
1564  {
1565  // no need to load a target group
1566  __SUP_COUT__ << "Using active groups." << __E__;
1567  }
1568  else
1569  {
1570  __SUP_COUT__ << "Loading group '" << groupName << "(" << groupKey << ")'"
1571  << __E__;
1572 
1573  std::string groupComment, groupAuthor, configGroupCreationTime;
1574 
1575  // only same member map if object pointer was passed
1576  cfgMgr->loadTableGroup(groupName,
1577  groupKey,
1578  false /*doActivate*/,
1579  returnMemberMap,
1580  0 /*progressBar*/,
1581  accumulatedErrors,
1582  doGetGroupInfo ? &groupComment : 0,
1583  doGetGroupInfo ? &groupAuthor : 0,
1584  doGetGroupInfo ? &configGroupCreationTime : 0);
1585 
1586  if(doGetGroupInfo)
1587  {
1588  xmlOut.addTextElementToData("configGroupComment", groupComment);
1589  xmlOut.addTextElementToData("configGroupAuthor", groupAuthor);
1590  xmlOut.addTextElementToData("configGroupCreationTime",
1591  configGroupCreationTime);
1592  }
1593 
1594  if(accumulatedErrors && *accumulatedErrors != "")
1595  __SUP_COUTV__(*accumulatedErrors);
1596  }
1597 
1598  // extract modified tables
1599  {
1600  std::istringstream f(modifiedTables);
1601  std::string table, version;
1602  while(getline(f, table, ','))
1603  {
1604  getline(f, version, ',');
1605  modifiedTablesMap.insert(
1606  std::pair<std::string /*name*/, TableVersion /*version*/>(
1607  table, TableVersion(version)));
1608  }
1609  //__SUP_COUT__ << modifiedTables << __E__;
1610  for(auto& pair : modifiedTablesMap)
1611  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second
1612  << __E__;
1613  }
1614 
1615  // add all active table pairs to xmlOut
1616  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
1617  xmlOut.addTextElementToData("DefaultNoLink",
1618  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
1619  for(auto& activePair : allActivePairs)
1620  {
1621  if(outputActiveTables)
1622  xmlOut.addTextElementToData("ActiveTableName", activePair.first);
1623 
1624  // check if name is in modifiedTables
1625  // if so, activate the temporary version
1626  if((modifiedTablesMapIt = modifiedTablesMap.find(activePair.first)) !=
1627  modifiedTablesMap.end())
1628  {
1629  __SUP_COUT__ << "Found modified table " << (*modifiedTablesMapIt).first
1630  << ": trying... " << (*modifiedTablesMapIt).second << __E__;
1631 
1632  try
1633  {
1634  allTableInfo.at(activePair.first)
1635  .tablePtr_->setActiveView((*modifiedTablesMapIt).second);
1636  }
1637  catch(...)
1638  {
1639  __SUP_SS__
1640  << "Modified table version v" << (*modifiedTablesMapIt).second
1641  << " failed. Reverting to v"
1642  << allTableInfo.at(activePair.first).tablePtr_->getView().getVersion()
1643  << "." << __E__;
1644  __SUP_COUT_WARN__ << "Warning detected!\n\n " << ss.str() << __E__;
1645  xmlOut.addTextElementToData(
1646  "Warning",
1647  "Error setting up active tables!\n\n" + std::string(ss.str()));
1648  }
1649  }
1650 
1651  if(outputActiveTables)
1652  {
1653  xmlOut.addTextElementToData("ActiveTableVersion",
1654  allTableInfo.at(activePair.first)
1655  .tablePtr_->getView()
1656  .getVersion()
1657  .toString());
1658  xmlOut.addTextElementToData(
1659  "ActiveTableComment",
1660  allTableInfo.at(activePair.first).tablePtr_->getView().getComment());
1661  }
1662 
1663  //__SUP_COUT__ << "Active table = " <<
1664  // activePair.first << "-v" <<
1665  // allTableInfo.at(activePair.first).tablePtr_->getView().getVersion() <<
1666  //__E__;
1667  }
1668 }
1669 catch(std::runtime_error& e)
1670 {
1671  __SUP_SS__ << ("Error setting up active tables!\n\n" + std::string(e.what()))
1672  << __E__;
1673  __SUP_COUT_ERR__ << "\n" << ss.str();
1674  xmlOut.addTextElementToData("Error", ss.str());
1675 }
1676 catch(...)
1677 {
1678  __SUP_SS__ << ("Error setting up active tables!\n\n") << __E__;
1679  __SUP_COUT_ERR__ << "\n" << ss.str();
1680  xmlOut.addTextElementToData("Error", ss.str());
1681 }
1682 
1683 //========================================================================================================================
1684 // handleFillCreateTreeNodeRecordsXML
1685 // Creates the records in the appropriate table
1686 // and creates a temporary version.
1687 // the modified-<modified tables> list is returned in xml
1688 //
1689 // if groupName == "" || groupKey is invalid
1690 // then do for active groups
1691 //
1692 // parameters
1693 // configGroupName (full name with key)
1694 // starting node path
1695 // modifiedTables := CSV of table/version pairs
1696 // recordList := CSV list of records to create
1697 //
1698 void ConfigurationGUISupervisor::handleFillCreateTreeNodeRecordsXML(
1699  HttpXmlDocument& xmlOut,
1700  ConfigurationManagerRW* cfgMgr,
1701  const std::string& groupName,
1702  const TableGroupKey& groupKey,
1703  const std::string& startPath,
1704  const std::string& modifiedTables,
1705  const std::string& recordList,
1706  const std::string& author)
1707 {
1708  // setup active tables based on input group and modified tables
1709  setupActiveTablesXML(xmlOut,
1710  cfgMgr,
1711  groupName,
1712  groupKey,
1713  modifiedTables,
1714  true /* refresh all */,
1715  false /* getGroupInfo */,
1716  0 /* returnMemberMap */,
1717  false /* outputActiveTables */);
1718 
1719  try
1720  {
1721  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
1722  TableBase* config = cfgMgr->getTableByName(targetNode.getTableName());
1723 
1724  __SUP_COUT__ << config->getTableName() << __E__;
1725  TableVersion temporaryVersion;
1726 
1727  // if current version is not temporary
1728  // create temporary
1729  // else re-modify temporary version
1730  // edit temporary version directly
1731  // then after all edits return active versions
1732  //
1733 
1734  bool firstSave = true;
1735 
1736  // save current version
1737  TableView backupView;
1738 
1739  // extract record list
1740  {
1741  std::istringstream f(recordList);
1742  std::string recordUID;
1743  unsigned int i;
1744 
1745  while(getline(f, recordUID, ',')) // for each record
1746  {
1747  recordUID = StringMacros::decodeURIComponent(recordUID);
1748 
1749  __SUP_COUT__ << "recordUID " << recordUID << __E__;
1750 
1751  if(firstSave) // handle version bookkeeping
1752  {
1753  if(!(temporaryVersion = targetNode.getTableVersion())
1754  .isTemporaryVersion())
1755  {
1756  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
1757  // create temporary version for editing
1758  temporaryVersion = config->createTemporaryView(temporaryVersion);
1759  cfgMgr->saveNewTable(targetNode.getTableName(),
1760  temporaryVersion,
1761  true); // proper bookkeeping for temporary
1762  // version with the new version
1763 
1764  __SUP_COUT__ << "Created temporary version " << temporaryVersion
1765  << __E__;
1766  }
1767  else // else table is already temporary version
1768  __SUP_COUT__ << "Using temporary version " << temporaryVersion
1769  << __E__;
1770 
1771  firstSave = false;
1772 
1773  // copy original to backup before modifying
1774  backupView.copy(config->getView(), temporaryVersion, author);
1775  }
1776 
1777  // at this point have valid temporary version to edit
1778 
1779  // copy "table-newRow" type edit from handleSaveTreeNodeEditXML()
1780  // functionality
1781 
1782  // add row
1783  unsigned int row = config->getViewP()->addRow(
1784  author, true /*incrementUniqueData*/); // increment all unique data
1785  // fields to void conflict
1786 
1787  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
1788  try
1789  {
1790  unsigned int col = config->getViewP()->getColStatus();
1791  config->getViewP()->setURIEncodedValue("1", row, col);
1792  }
1793  catch(...)
1794  {
1795  } // if not, ignore
1796 
1797  // set UID value
1798  config->getViewP()->setURIEncodedValue(
1799  recordUID, row, config->getViewP()->getColUID());
1800  }
1801  }
1802 
1803  if(!firstSave) // only test table if there was a change
1804  {
1805  try
1806  {
1807  config->getViewP()->init(); // verify new table (throws runtime_errors)
1808  }
1809  catch(...)
1810  {
1811  __SUP_COUT_INFO__ << "Reverting to original view." << __E__;
1812  __SUP_COUT__ << "Before:" << __E__;
1813  config->getViewP()->print();
1814  config->getViewP()->copy(backupView, temporaryVersion, author);
1815  __SUP_COUT__ << "After:" << __E__;
1816  config->getViewP()->print();
1817 
1818  throw; // rethrow
1819  }
1820  }
1821 
1822  handleFillModifiedTablesXML(xmlOut, cfgMgr);
1823  }
1824  catch(std::runtime_error& e)
1825  {
1826  __SUP_SS__ << ("Error creating new record(s)!\n\n" + std::string(e.what()))
1827  << __E__;
1828  __SUP_COUT_ERR__ << "\n" << ss.str();
1829  xmlOut.addTextElementToData("Error", ss.str());
1830  }
1831  catch(...)
1832  {
1833  __SUP_SS__ << ("Error creating new record(s)!\n\n") << __E__;
1834  __SUP_COUT_ERR__ << "\n" << ss.str();
1835  xmlOut.addTextElementToData("Error", ss.str());
1836  }
1837 }
1838 
1839 //========================================================================================================================
1840 // handleFillModifiedTablesXML
1841 // fills <modified tables> as used by ConfigurationAPI
1842 void ConfigurationGUISupervisor::handleFillModifiedTablesXML(
1843  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr) try
1844 {
1845  // return modified <modified tables>
1846  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
1847  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
1848  for(auto& activePair : allActivePairs)
1849  {
1850  xmlOut.addTextElementToData("NewActiveTableName", activePair.first);
1851  xmlOut.addTextElementToData("NewActiveTableVersion",
1852  allTableInfo.at(activePair.first)
1853  .tablePtr_->getView()
1854  .getVersion()
1855  .toString());
1856  xmlOut.addTextElementToData(
1857  "NewActiveTableComment",
1858  allTableInfo.at(activePair.first).tablePtr_->getView().getComment());
1859  }
1860 }
1861 catch(std::runtime_error& e)
1862 {
1863  __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
1864  __SUP_COUT_ERR__ << "\n" << ss.str();
1865  xmlOut.addTextElementToData("Error", ss.str());
1866 }
1867 catch(...)
1868 {
1869  __SUP_SS__ << ("Error!\n\n") << __E__;
1870  __SUP_COUT_ERR__ << "\n" << ss.str();
1871  xmlOut.addTextElementToData("Error", ss.str());
1872 }
1873 
1874 //========================================================================================================================
1875 // handleFillDeleteTreeNodeRecordsXML
1876 // Deletes the records in the appropriate table
1877 // and creates a temporary version.
1878 // the modified-<modified tables> list is returned in xml
1879 //
1880 // if groupName == "" || groupKey is invalid
1881 // then do for active groups
1882 //
1883 // parameters
1884 // configGroupName (full name with key)
1885 // starting node path
1886 // modifiedTables := CSV of table/version pairs
1887 // recordList := CSV list of records to create
1888 //
1889 void ConfigurationGUISupervisor::handleFillDeleteTreeNodeRecordsXML(
1890  HttpXmlDocument& xmlOut,
1891  ConfigurationManagerRW* cfgMgr,
1892  const std::string& groupName,
1893  const TableGroupKey& groupKey,
1894  const std::string& startPath,
1895  const std::string& modifiedTables,
1896  const std::string& recordList)
1897 {
1898  // setup active tables based on input group and modified tables
1899  setupActiveTablesXML(xmlOut,
1900  cfgMgr,
1901  groupName,
1902  groupKey,
1903  modifiedTables,
1904  true /* refresh all */,
1905  false /* getGroupInfo */,
1906  0 /* returnMemberMap */,
1907  false /* outputActiveTables */);
1908 
1909  try
1910  {
1911  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
1912  TableBase* config = cfgMgr->getTableByName(targetNode.getTableName());
1913 
1914  __SUP_COUT__ << config->getTableName() << __E__;
1915  TableVersion temporaryVersion;
1916 
1917  // if current version is not temporary
1918  // create temporary
1919  // else re-modify temporary version
1920  // edit temporary version directly
1921  // then after all edits return active versions
1922  //
1923 
1924  bool firstSave = true;
1925 
1926  // extract record list
1927  {
1928  std::istringstream f(recordList);
1929  std::string recordUID;
1930  unsigned int i;
1931 
1932  while(getline(f, recordUID, ',')) // for each record
1933  {
1934  recordUID = StringMacros::decodeURIComponent(recordUID);
1935 
1936  __SUP_COUT__ << "recordUID " << recordUID << __E__;
1937 
1938  if(firstSave) // handle version bookkeeping
1939  {
1940  if(!(temporaryVersion = targetNode.getTableVersion())
1941  .isTemporaryVersion())
1942  {
1943  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
1944  // create temporary version for editing
1945  temporaryVersion = config->createTemporaryView(temporaryVersion);
1946  cfgMgr->saveNewTable(targetNode.getTableName(),
1947  temporaryVersion,
1948  true); // proper bookkeeping for temporary
1949  // version with the new version
1950 
1951  __SUP_COUT__ << "Created temporary version " << temporaryVersion
1952  << __E__;
1953  }
1954  else // else table is already temporary version
1955  __SUP_COUT__ << "Using temporary version " << temporaryVersion
1956  << __E__;
1957 
1958  firstSave = false;
1959  }
1960 
1961  // at this point have valid temporary version to edit
1962 
1963  // copy "delete-uid" type edit from handleSaveTreeNodeEditXML()
1964  // functionality
1965  unsigned int row = config->getViewP()->findRow(
1966  config->getViewP()->getColUID(), recordUID);
1967  config->getViewP()->deleteRow(row);
1968  }
1969  }
1970 
1971  if(!firstSave) // only test table if there was a change
1972  config->getViewP()->init(); // verify new table (throws runtime_errors)
1973 
1974  handleFillModifiedTablesXML(xmlOut, cfgMgr);
1975  }
1976  catch(std::runtime_error& e)
1977  {
1978  __SUP_SS__ << ("Error removing record(s)!\n\n" + std::string(e.what())) << __E__;
1979  __SUP_COUT_ERR__ << "\n" << ss.str();
1980  xmlOut.addTextElementToData("Error", ss.str());
1981  }
1982  catch(...)
1983  {
1984  __SUP_SS__ << ("Error removing record(s)!\n\n") << __E__;
1985  __SUP_COUT_ERR__ << "\n" << ss.str();
1986  xmlOut.addTextElementToData("Error", ss.str());
1987  }
1988 }
1989 
1990 //========================================================================================================================
1991 // handleFillSetTreeNodeFieldValuesXML
1992 // writes for each record, the field/value pairs to the appropriate table
1993 // and creates a temporary version.
1994 // the modified-<modified tables> list is returned in xml
1995 //
1996 // if groupName == "" || groupKey is invalid
1997 // then do for active groups
1998 //
1999 // parameters
2000 // configGroupName (full name with key)
2001 // starting node path
2002 // modifiedTables := CSV of table/version pairs
2003 // recordList := CSV list of records for which to write values for fields
2004 // fieldList := CSV of relative-to-record-path to fields to write to each record
2005 // valueList := CSV of values corresponding to fields
2006 //
2007 void ConfigurationGUISupervisor::handleFillSetTreeNodeFieldValuesXML(
2008  HttpXmlDocument& xmlOut,
2009  ConfigurationManagerRW* cfgMgr,
2010  const std::string& groupName,
2011  const TableGroupKey& groupKey,
2012  const std::string& startPath,
2013  const std::string& modifiedTables,
2014  const std::string& recordList,
2015  const std::string& fieldList,
2016  const std::string& valueList,
2017  const std::string& author)
2018 {
2019  // setup active tables based on input group and modified tables
2020  setupActiveTablesXML(xmlOut,
2021  cfgMgr,
2022  groupName,
2023  groupKey,
2024  modifiedTables,
2025  true /* refresh all */,
2026  false /* getGroupInfo */,
2027  0 /* returnMemberMap */,
2028  false /* outputActiveTables */);
2029 
2030  // for each field
2031  // return field/value pair in xml
2032 
2033  try
2034  {
2035  std::vector<std::string /*relative-path*/> fieldPaths;
2036  // extract field list
2037  {
2038  std::istringstream f(fieldList);
2039  std::string fieldPath;
2040  while(getline(f, fieldPath, ','))
2041  {
2042  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2043  }
2044  __SUP_COUT__ << fieldList << __E__;
2045  for(const auto& field : fieldPaths)
2046  __SUP_COUT__ << "fieldPath " << field << __E__;
2047  }
2048 
2049  std::vector<std::string /*relative-path*/> fieldValues;
2050  // extract value list
2051  {
2052  std::istringstream f(valueList);
2053  std::string fieldValue;
2054  while(getline(f, fieldValue, ','))
2055  {
2056  fieldValues.push_back(
2057  fieldValue); // setURIEncodedValue is expected
2058  // StringMacros::decodeURIComponent(fieldValue));
2059  }
2060 
2061  // if last value is "" then push empty value
2062  if(valueList.size() && valueList[valueList.size() - 1] == ',')
2063  fieldValues.push_back("");
2064 
2065  __SUP_COUT__ << valueList << __E__;
2066  for(const auto& value : fieldValues)
2067  __SUP_COUT__ << "fieldValue " << value << __E__;
2068  }
2069 
2070  if(fieldPaths.size() != fieldValues.size())
2071  {
2072  __SUP_SS__;
2073  __THROW__(ss.str() + "Mismatch in fields and values array size!");
2074  }
2075 
2076  // extract record list
2077  {
2078  TableBase* config;
2079  TableVersion temporaryVersion;
2080  std::istringstream f(recordList);
2081  std::string recordUID;
2082  unsigned int i;
2083 
2084  while(getline(f, recordUID, ',')) // for each record
2085  {
2086  recordUID = StringMacros::decodeURIComponent(recordUID);
2087 
2088  //__SUP_COUT__ << "recordUID " << recordUID << __E__;
2089 
2090  DOMElement* parentEl =
2091  xmlOut.addTextElementToData("fieldValues", recordUID);
2092 
2093  // for each field, set value
2094  for(i = 0; i < fieldPaths.size(); ++i)
2095  {
2096  __SUP_COUT__ << "fieldPath " << fieldPaths[i] << __E__;
2097  __SUP_COUT__ << "fieldValue " << fieldValues[i] << __E__;
2098 
2099  // doNotThrowOnBrokenUIDLinks so that link UIDs can be edited like
2100  // other fields
2101  ConfigurationTree targetNode =
2102  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPaths[i],
2103  true /*doNotThrowOnBrokenUIDLinks*/);
2104 
2105  // need table, uid, columnName to set a value
2106 
2107  // assume correct table version is loaded by setupActiveTablesXML()
2108  // config = cfgMgr->getTableByName(
2109  // targetNode.getTableName());
2110  //
2111  //__SUP_COUT__ << "Active version is " << config->getViewVersion() <<
2112  //__E__;
2113 
2114  // mimic handleSaveTreeNodeEditXML L 1750
2115  // Actually call it! ..
2116  // with a modifier?
2117  // or
2118  // handleSaveTreeNodeEditXML(xmlOut,
2119  // cfgMgr,
2120  // targetNode.getTableName(),
2121  // targetNode.getTableVersion(),
2122  // "value",
2123  // targetNode.getUIDAsString(),
2124  // targetNode.getValueName(), //col name
2125  // fieldValues[i]
2126  // );
2127 
2128  // or
2129  // (because problem is this would create a new temporary version each
2130  // time) if current version is not temporary
2131  // create temporary
2132  // else re-modify temporary version
2133  // edit temporary version directly
2134  // then after all edits return active versions
2135  //
2136 
2137  __SUP_COUT__ << "Getting table " << targetNode.getFieldTableName()
2138  << __E__;
2139 
2140  // if link must get parent config name
2141  config = cfgMgr->getTableByName(
2142  targetNode.getFieldTableName()); // NOT getTableName!
2143  if(!(temporaryVersion = config->getViewP()->getVersion())
2144  .isTemporaryVersion())
2145  {
2146  // create temporary version for editing
2147  temporaryVersion =
2148  config->createTemporaryView(config->getViewP()->getVersion());
2149  cfgMgr->saveNewTable(config->getTableName(),
2150  temporaryVersion,
2151  true); // proper bookkeeping for temporary
2152  // version with the new version
2153 
2154  __SUP_COUT__ << "Created temporary version "
2155  << config->getTableName() << "-v" << temporaryVersion
2156  << __E__;
2157  }
2158  // else //else table is already temporary version
2159  __SUP_COUT__ << "Using temporary version " << config->getTableName()
2160  << "-v" << temporaryVersion << __E__;
2161 
2162  // copy "value" type edit from handleSaveTreeNodeEditXML()
2163  // functionality
2164  config->getViewP()->setURIEncodedValue(fieldValues[i],
2165  targetNode.getFieldRow(),
2166  targetNode.getFieldColumn(),
2167  author);
2168 
2169  config->getViewP()
2170  ->init(); // verify new table (throws runtime_errors)
2171  }
2172  }
2173  }
2174 
2175  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2176  }
2177  catch(std::runtime_error& e)
2178  {
2179  __SUP_SS__ << ("Error setting field values!\n\n" + std::string(e.what()))
2180  << __E__;
2181  __SUP_COUT_ERR__ << "\n" << ss.str();
2182  xmlOut.addTextElementToData("Error", ss.str());
2183  }
2184  catch(...)
2185  {
2186  __SUP_SS__ << ("Error setting field values!\n\n") << __E__;
2187  __SUP_COUT_ERR__ << "\n" << ss.str();
2188  xmlOut.addTextElementToData("Error", ss.str());
2189  }
2190 }
2191 
2192 //========================================================================================================================
2193 // handleFillGetTreeNodeFieldValuesXML
2194 // returns for each record, xml list of field/value pairs
2195 // field := relative-path
2196 //
2197 // if groupName == "" || groupKey is invalid
2198 // then do for active groups
2199 //
2200 // parameters
2201 // configGroupName (full name with key)
2202 // starting node path
2203 // modifiedTables := CSV of table/version pairs
2204 // recordStr := CSV list of records for which to lookup values for fields
2205 // fieldList := CSV of relative-to-record-path to filter common fields
2206 //
2207 void ConfigurationGUISupervisor::handleFillGetTreeNodeFieldValuesXML(
2208  HttpXmlDocument& xmlOut,
2209  ConfigurationManagerRW* cfgMgr,
2210  const std::string& groupName,
2211  const TableGroupKey& groupKey,
2212  const std::string& startPath,
2213  const std::string& modifiedTables,
2214  const std::string& recordList,
2215  const std::string& fieldList)
2216 {
2217  // setup active tables based on input group and modified tables
2218  setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables);
2219 
2220  // for each field
2221  // return field/value pair in xml
2222 
2223  try
2224  {
2225  std::vector<std::string /*relative-path*/> fieldPaths;
2226  // extract field list
2227  {
2228  std::istringstream f(fieldList);
2229  std::string fieldPath;
2230  while(getline(f, fieldPath, ','))
2231  {
2232  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2233  }
2234  __SUP_COUT__ << fieldList << __E__;
2235  for(auto& field : fieldPaths)
2236  __SUP_COUT__ << "fieldPath " << field << __E__;
2237  }
2238 
2239  // extract record list
2240  {
2241  std::istringstream f(recordList);
2242  std::string recordUID;
2243  while(getline(f, recordUID, ',')) // for each record
2244  {
2245  recordUID = StringMacros::decodeURIComponent(recordUID);
2246 
2247  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2248 
2249  DOMElement* parentEl =
2250  xmlOut.addTextElementToData("fieldValues", recordUID);
2251 
2252  // for each field, get value
2253  for(const auto& fieldPath : fieldPaths)
2254  {
2255  __SUP_COUT__ << "fieldPath " << fieldPath << __E__;
2256 
2257  ConfigurationTree node =
2258  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPath);
2259 
2260  xmlOut.addTextElementToParent("FieldPath", fieldPath, parentEl);
2261 
2262  xmlOut.addTextElementToParent(
2263  "FieldValue",
2264  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPath)
2265  .getValueAsString(true /*returnLinkTableValue*/),
2266  parentEl);
2267  }
2268  }
2269  }
2270  }
2271  catch(std::runtime_error& e)
2272  {
2273  __SUP_SS__ << ("Error getting field values!\n\n" + std::string(e.what()))
2274  << __E__;
2275  __SUP_COUT_ERR__ << "\n" << ss.str();
2276  xmlOut.addTextElementToData("Error", ss.str());
2277  }
2278  catch(...)
2279  {
2280  __SUP_SS__ << ("Error getting field values!\n\n") << __E__;
2281  __SUP_COUT_ERR__ << "\n" << ss.str();
2282  xmlOut.addTextElementToData("Error", ss.str());
2283  }
2284 }
2285 
2286 //========================================================================================================================
2287 // handleFillTreeNodeCommonFieldsXML
2288 // returns xml list of common fields among records
2289 // field := relative-path
2290 //
2291 // if groupName == "" || groupKey is invalid
2292 // then do for active groups
2293 //
2294 // parameters
2295 // configGroupName (full name with key)
2296 // starting node path
2297 // depth from starting node path
2298 // modifiedTables := CSV of table/version pairs
2299 // recordList := CSV of records to search for fields
2300 // fieldList := CSV of relative-to-record-path to filter common fields
2301 // (accept or reject [use ! as first character to reject])
2302 // [use leading* to ignore relative path - note that only leading and trailing
2303 // wildcards work]
2304 //
2305 void ConfigurationGUISupervisor::handleFillTreeNodeCommonFieldsXML(
2306  HttpXmlDocument& xmlOut,
2307  ConfigurationManagerRW* cfgMgr,
2308  const std::string& groupName,
2309  const TableGroupKey& groupKey,
2310  const std::string& startPath,
2311  unsigned int depth,
2312  const std::string& modifiedTables,
2313  const std::string& recordList,
2314  const std::string& fieldList)
2315 {
2316  // setup active tables based on input group and modified tables
2317  setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables);
2318 
2319  try
2320  {
2321  DOMElement* parentEl = xmlOut.addTextElementToData("fields", startPath);
2322 
2323  if(depth == 0)
2324  {
2325  __SUP_SS__ << "Depth of search must be greater than 0." << __E__;
2326  __SUP_COUT__ << ss.str();
2327  __SS_THROW__; // done if 0 depth, no fields
2328  }
2329 
2330  // do not allow traversing for common fields from root level
2331  // the tree view should be used for such a purpose
2332  // if(startPath == "/")
2333  // return;
2334 
2335  std::vector<ConfigurationTree::RecordField> retFieldList;
2336 
2337  {
2338  ConfigurationTree startNode = cfgMgr->getNode(startPath);
2339  if(startNode.isLinkNode() && startNode.isDisconnected())
2340  {
2341  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
2342  __SUP_COUT_ERR__ << "\n" << ss.str();
2343  __SS_THROW__;
2344  return; // quietly ignore disconnected links at depth
2345  // note: at the root level they will be flagged for the user
2346  }
2347 
2348  std::vector<std::string /*relative-path*/> fieldAcceptList, fieldRejectList;
2349  if(fieldList != "")
2350  {
2351  // extract field filter list
2352  {
2353  std::istringstream f(fieldList);
2354  std::string fieldPath, decodedFieldPath;
2355  while(getline(f, fieldPath, ','))
2356  {
2357  decodedFieldPath = StringMacros::decodeURIComponent(fieldPath);
2358 
2359  if(decodedFieldPath[0] == '!') // reject field
2360  fieldRejectList.push_back(decodedFieldPath.substr(1));
2361  else
2362  fieldAcceptList.push_back(decodedFieldPath);
2363  }
2364  __SUP_COUT__ << fieldList << __E__;
2365  for(auto& field : fieldAcceptList)
2366  __SUP_COUT__ << "fieldAcceptList " << field << __E__;
2367  for(auto& field : fieldRejectList)
2368  __SUP_COUT__ << "fieldRejectList " << field << __E__;
2369  }
2370  }
2371 
2372  std::vector<std::string /*relative-path*/> records;
2373  if(recordList == "*") // handle all records case
2374  {
2375  records.clear();
2376  records = startNode.getChildrenNames();
2377  __SUP_COUT__ << "Translating wildcard..." << __E__;
2378  for(auto& record : records)
2379  __SUP_COUT__ << "recordList " << record << __E__;
2380  }
2381  else if(recordList != "")
2382  {
2383  // extract record list
2384  {
2385  std::istringstream f(recordList);
2386  std::string recordStr;
2387  while(getline(f, recordStr, ','))
2388  {
2389  records.push_back(StringMacros::decodeURIComponent(recordStr));
2390  }
2391  __SUP_COUT__ << recordList << __E__;
2392  for(auto& record : records)
2393  __SUP_COUT__ << "recordList " << record << __E__;
2394  }
2395  }
2396 
2397  retFieldList = startNode.getCommonFields(
2398  records, fieldAcceptList, fieldRejectList, depth);
2399  }
2400 
2401  DOMElement* parentTypeEl;
2402  for(const auto& fieldInfo : retFieldList)
2403  {
2404  xmlOut.addTextElementToParent(
2405  "FieldTableName", fieldInfo.tableName_, parentEl);
2406  xmlOut.addTextElementToParent(
2407  "FieldColumnName", fieldInfo.columnName_, parentEl);
2408  xmlOut.addTextElementToParent(
2409  "FieldRelativePath", fieldInfo.relativePath_, parentEl);
2410  xmlOut.addTextElementToParent(
2411  "FieldColumnType", fieldInfo.columnInfo_->getType(), parentEl);
2412  xmlOut.addTextElementToParent(
2413  "FieldColumnDataType", fieldInfo.columnInfo_->getDataType(), parentEl);
2414  xmlOut.addTextElementToParent("FieldColumnDefaultValue",
2415  fieldInfo.columnInfo_->getDefaultValue(),
2416  parentEl);
2417 
2418  parentTypeEl =
2419  xmlOut.addTextElementToParent("FieldColumnDataChoices", "", parentEl);
2420 
2421  // if there are associated data choices, send info
2422  auto dataChoices = fieldInfo.columnInfo_->getDataChoices();
2423  xmlOut.addTextElementToParent(
2424  "FieldColumnDataChoice", // add default to list to mimic tree handling
2425  fieldInfo.columnInfo_->getDefaultValue(),
2426  parentTypeEl);
2427  for(const auto& dataChoice : dataChoices)
2428  xmlOut.addTextElementToParent(
2429  "FieldColumnDataChoice", dataChoice, parentTypeEl);
2430  }
2431  }
2432  catch(std::runtime_error& e)
2433  {
2434  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
2435  << __E__;
2436  __SUP_COUT_ERR__ << "\n" << ss.str();
2437  xmlOut.addTextElementToData("Error", ss.str());
2438  }
2439  catch(...)
2440  {
2441  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
2442  __SUP_COUT_ERR__ << "\n" << ss.str();
2443  xmlOut.addTextElementToData("Error", ss.str());
2444  }
2445 }
2446 
2447 //========================================================================================================================
2448 // handleFillUniqueFieldValuesForRecordsXML
2449 // returns xml list of unique values for each fields among records
2450 // field := relative-path
2451 //
2452 // return xml
2453 // <xml>
2454 // <field val=relative-path>
2455 // <unique_val val=uval0>
2456 // <unique_val val=uval1>
2457 // .. next unique value
2458 // </field>
2459 // ... next field
2460 // </xml>
2461 //
2462 // if groupName == "" || groupKey is invalid
2463 // then do for active groups
2464 //
2465 // parameters
2466 // configGroupName (full name with key)
2467 // starting node path
2468 // modifiedTables := CSV of table/version pairs
2469 // recordList := CSV of records to search for unique values
2470 // fieldList := CSV of fields relative-to-record-path for which to get list of unique
2471 //values fieldList = AUTO is a special keyword
2472 // if AUTO, then server picks filter fields (usually 3, with preference
2473 // for GroupID, On/Off, and FixedChoice fields.
2474 //
2475 void ConfigurationGUISupervisor::handleFillUniqueFieldValuesForRecordsXML(
2476  HttpXmlDocument& xmlOut,
2477  ConfigurationManagerRW* cfgMgr,
2478  const std::string& groupName,
2479  const TableGroupKey& groupKey,
2480  const std::string& startPath,
2481  const std::string& modifiedTables,
2482  const std::string& recordList,
2483  const std::string& fieldList)
2484 {
2485  // setup active tables based on input group and modified tables
2486  setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables);
2487 
2488  try
2489  {
2490  // do not allow traversing for common fields from root level
2491  // the tree view should be used for such a purpose
2492  if(startPath == "/")
2493  return;
2494 
2495  ConfigurationTree startNode = cfgMgr->getNode(startPath);
2496  if(startNode.isLinkNode() && startNode.isDisconnected())
2497  {
2498  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
2499  __SUP_COUT_ERR__ << "\n" << ss.str();
2500  __SS_THROW__;
2501  }
2502 
2503  // extract records list
2504  std::vector<std::string /*relative-path*/> records;
2505  if(recordList == "*") // handle all records case
2506  {
2507  records.clear();
2508  records = startNode.getChildrenNames();
2509  __SUP_COUT__ << "Translating wildcard..." << __E__;
2510  for(auto& record : records)
2511  __SUP_COUT__ << "recordList " << record << __E__;
2512  }
2513  else if(recordList != "")
2514  {
2515  // extract record list
2516  {
2517  std::istringstream f(recordList);
2518  std::string recordStr;
2519  while(getline(f, recordStr, ','))
2520  {
2521  records.push_back(StringMacros::decodeURIComponent(recordStr));
2522  }
2523  __SUP_COUT__ << recordList << __E__;
2524  for(auto& record : records)
2525  __SUP_COUT__ << "recordList " << record << __E__;
2526  }
2527  } // end records extraction
2528 
2529  // extract fields to get
2530  std::vector<std::string /*relative-path*/> fieldsToGet;
2531  if(fieldList != "")
2532  {
2533  // extract field filter list
2534 
2535  if(fieldList == "AUTO")
2536  {
2537  // automatically choose 3 fields, with preference
2538  // for GroupID, On/Off, and FixedChoice fields.
2539 
2540  __SUP_COUT__ << "Getting AUTO filter fields!" << __E__;
2541 
2542  std::vector<ConfigurationTree::RecordField> retFieldList;
2543  std::vector<std::string /*relative-path*/> fieldAcceptList,
2544  fieldRejectList;
2545  fieldRejectList.push_back("*CommentDescription");
2546  retFieldList = startNode.getCommonFields(
2547  records, fieldAcceptList, fieldRejectList, 5, true /*auto*/);
2548 
2549  for(const auto& retField : retFieldList)
2550  fieldsToGet.push_back(retField.columnName_);
2551  }
2552  else
2553  {
2554  std::istringstream f(fieldList);
2555  std::string fieldPath;
2556  while(getline(f, fieldPath, ','))
2557  {
2558  fieldsToGet.push_back(StringMacros::decodeURIComponent(fieldPath));
2559  }
2560  __SUP_COUTV__(fieldList);
2561  }
2562  } // end fields extraction
2563 
2564  __SUP_COUTV__(StringMacros::vectorToString(fieldsToGet));
2565 
2566  // loop through each field and get unique values among records
2567  for(auto& field : fieldsToGet)
2568  {
2569  __SUP_COUTV__(field);
2570 
2571  DOMElement* parentEl = xmlOut.addTextElementToData("field", field);
2572 
2573  // use set to force sorted unique values
2574  std::set<std::string /*unique-values*/> uniqueValues;
2575 
2576  uniqueValues =
2577  cfgMgr->getNode(startPath).getUniqueValuesForField(records, field);
2578 
2579  for(auto& uniqueValue : uniqueValues)
2580  {
2581  __SUP_COUT__ << "uniqueValue " << uniqueValue << __E__;
2582 
2583  xmlOut.addTextElementToParent("uniqueValue", uniqueValue, parentEl);
2584  }
2585  }
2586  }
2587  catch(std::runtime_error& e)
2588  {
2589  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
2590  << __E__;
2591  __SUP_COUT_ERR__ << "\n" << ss.str();
2592  xmlOut.addTextElementToData("Error", ss.str());
2593  }
2594  catch(...)
2595  {
2596  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
2597  __SUP_COUT_ERR__ << "\n" << ss.str();
2598  xmlOut.addTextElementToData("Error", ss.str());
2599  }
2600 }
2601 
2602 //========================================================================================================================
2603 // handleFillTreeViewXML
2604 // returns xml tree from path for given depth
2605 //
2606 // if groupName == "" || groupKey is invalid
2607 // then return tree for active groups
2608 //
2609 // parameters
2610 // configGroupName (full name with key)
2611 // starting node path
2612 // depth from starting node path
2613 // modifiedTables := CSV of table/version pairs
2614 // filterList := relative-to-record-path=value(,value,...);path=value... filtering
2615 // records with relative path not meeting all filter criteria
2616 // - can accept multiple values per field (values separated by commas) (i.e. OR)
2617 // - fields/value pairs separated by ; for AND
2618 // - Note: limitation here is there is no OR among fields/value pairs (in future,
2619 // could separate field/value pairs by : for OR) e.g.
2620 //"LinkToFETypeTable=NIMPlus,TemplateUDP;FEInterfacePluginName=NIMPlusPlugin"
2621 //
2622 void ConfigurationGUISupervisor::handleFillTreeViewXML(HttpXmlDocument& xmlOut,
2623  ConfigurationManagerRW* cfgMgr,
2624  const std::string& groupName,
2625  const TableGroupKey& groupKey,
2626  const std::string& startPath,
2627  unsigned int depth,
2628  bool hideStatusFalse,
2629  const std::string& modifiedTables,
2630  const std::string& filterList)
2631 {
2632  // return xml
2633  // <groupName="groupName"/>
2634  // <tree="path">
2635  // <node="...">
2636  // <node="...">
2637  // <node="...">
2638  // <value="...">
2639  // </node>
2640  // <node="...">
2641  // <value="...">
2642  // </node>
2643  // </node>
2644  // <node="...">
2645  // <value="..">
2646  // </node>
2647  // ...
2648  // </node>
2649  // </tree>
2650 
2651  // return the startPath as root "tree" element
2652  // and then display all children if depth > 0
2653 
2654  // Think about using this in the future to clean up the code
2655  // But may not work well since there is some special functionality used below
2656  // like getting group comments, and not reloading everything except for at root level
2658  // ....
2659  // // setup active tables based on input group and modified tables
2660 
2661  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
2662  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
2663 
2664  std::string accumulatedErrors = "";
2665  try
2666  {
2667  setupActiveTablesXML(
2668  xmlOut,
2669  cfgMgr,
2670  groupName,
2671  groupKey,
2672  modifiedTables,
2673  (startPath == "/"), // refreshAll, if at root node, reload all
2674  // tables so that partially loaded tables are
2675  // not allowed
2676  (startPath == "/"), // get group info
2677  &memberMap, // get group member map
2678  true, // output active tables (default)
2679  &accumulatedErrors // accumulate errors
2680  );
2681  }
2682  catch(const std::runtime_error& e)
2683  {
2684  __SS__ << "Error occured setting up active tables: " << e.what() << __E__;
2685  accumulatedErrors += ss.str();
2686  }
2687  catch(...)
2688  {
2689  __SS__ << "Unknown error occured setting up active tables." << __E__;
2690  accumulatedErrors += ss.str();
2691  }
2692 
2693  if(accumulatedErrors != "")
2694  {
2695  xmlOut.addTextElementToData("Warning", accumulatedErrors);
2696 
2697  __SUP_COUT__ << "Active tables are setup. Warning string: '" << accumulatedErrors
2698  << "'" << __E__;
2699  }
2700  else
2701  __SUP_COUT__ << "Active tables are setup. No issues found." << __E__;
2702 
2703  try
2704  {
2705  DOMElement* parentEl = xmlOut.addTextElementToData("tree", startPath);
2706 
2707  if(depth == 0)
2708  return; // already returned root node in itself
2709 
2710  std::vector<std::pair<std::string, ConfigurationTree>> rootMap;
2711 
2712  if(startPath == "/")
2713  {
2714  // then consider the configurationManager the root node
2715 
2716  std::string accumulateTreeErrs;
2717 
2718  if(usingActiveGroups)
2719  rootMap = cfgMgr->getChildren(0, &accumulateTreeErrs);
2720  else
2721  rootMap = cfgMgr->getChildren(&memberMap, &accumulateTreeErrs);
2722 
2723  __SUP_COUT__ << "accumulateTreeErrs = " << accumulateTreeErrs << __E__;
2724  if(accumulateTreeErrs != "")
2725  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
2726  }
2727  else
2728  {
2729  ConfigurationTree startNode =
2730  cfgMgr->getNode(startPath, true /*doNotThrowOnBrokenUIDLinks*/);
2731  if(startNode.isLinkNode() && startNode.isDisconnected())
2732  {
2733  xmlOut.addTextElementToData("DisconnectedStartNode", "1");
2734  return; // quietly ignore disconnected links at depth
2735  // note: at the root level they will be flagged for the user
2736  }
2737 
2738  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap;
2739  StringMacros::getMapFromString(
2740  filterList,
2741  filterMap,
2742  std::set<char>({';'}) /*pair delimiters*/,
2743  std::set<char>({'='}) /*name/value delimiters*/);
2744 
2745  __COUTV__(StringMacros::mapToString(filterMap));
2746 
2747  rootMap = cfgMgr->getNode(startPath).getChildren(filterMap);
2748  }
2749 
2750  for(auto& treePair : rootMap)
2751  recursiveTreeToXML(
2752  treePair.second, depth - 1, xmlOut, parentEl, hideStatusFalse);
2753  }
2754  catch(std::runtime_error& e)
2755  {
2756  __SUP_SS__ << "Error detected generating XML tree!\n\n " << e.what() << __E__;
2757  __SUP_COUT_ERR__ << "\n" << ss.str();
2758  xmlOut.addTextElementToData("Error", ss.str());
2759  }
2760  catch(...)
2761  {
2762  __SUP_SS__ << "Error detected generating XML tree!" << __E__;
2763  __SUP_COUT_ERR__ << "\n" << ss.str();
2764  xmlOut.addTextElementToData("Error", ss.str());
2765  }
2766 }
2767 
2768 //==============================================================================
2769 // recursiveToXml
2770 // output tree to XML from this node for desired depth
2771 // depth of 0 means output only this node's value
2772 // depth of 1 means include this node's children's values, etc..
2773 // depth of -1(unsigned int) effectively means output full tree
2774 void ConfigurationGUISupervisor::recursiveTreeToXML(const ConfigurationTree& t,
2775  unsigned int depth,
2776  HttpXmlDocument& xmlOut,
2777  DOMElement* parentEl,
2778  bool hideStatusFalse)
2779 {
2780  //__COUT__ << t.getValueAsString() << __E__;
2781 
2782  if(t.isValueNode())
2783  {
2784  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
2785  xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
2786  parentEl = xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
2787 
2788  // fixed choice and bitmap both use fixed choices strings
2789  // so output them to xml
2790  if(t.getValueType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
2791  t.getValueType() == TableViewColumnInfo::TYPE_BITMAP_DATA)
2792  {
2793  //__COUT__ << t.getValueType() << __E__;
2794 
2795  std::vector<std::string> choices = t.getFixedChoices();
2796  for(const auto& choice : choices)
2797  xmlOut.addTextElementToParent("fixedChoice", choice, parentEl);
2798  }
2799  }
2800  else
2801  {
2802  if(t.isLinkNode())
2803  {
2804  //__COUT__ << t.getValueName() << __E__;
2805 
2806  // Note: The order of xml fields is required by JavaScript, so do NOT change
2807  // order.
2808  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
2809 
2810  if(t.isDisconnected())
2811  {
2812  __COUT__ << t.getValueName() << __E__;
2813 
2814  // xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
2815  // xmlOut.addTextElementToParent("DisconnectedLink", t.getValueAsString(),
2816  // parentEl);
2817 
2818  xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
2819 
2820  // add extra fields for disconnected link
2821  xmlOut.addTextElementToParent(
2822  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
2823  t.getDisconnectedLinkID(),
2824  parentEl);
2825  xmlOut.addTextElementToParent(
2826  "LinkTableName", t.getDisconnectedTableName(), parentEl);
2827  xmlOut.addTextElementToParent(
2828  "LinkIndex", t.getChildLinkIndex(), parentEl);
2829 
2830  // add fixed choices (in case link has them)
2831  DOMElement* choicesParentEl =
2832  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
2833  // try
2834  //{
2835 
2836  std::vector<std::string> choices = t.getFixedChoices();
2837  __COUT__ << "choices.size() " << choices.size() << __E__;
2838 
2839  for(const auto& choice : choices)
2840  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
2841  //}
2842  // catch(...)
2843  //{
2844  // __COUT__ << "Ignoring unknown fixed choice error"
2845  //} //ignore no fixed choices for disconnected
2846 
2847  return;
2848  }
2849 
2850  // handle connected links
2851 
2852  xmlOut.addTextElementToParent(
2853  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
2854  t.getValueAsString(),
2855  parentEl);
2856 
2857  xmlOut.addTextElementToParent("LinkTableName", t.getTableName(), parentEl);
2858 
2859  xmlOut.addTextElementToParent("LinkIndex", t.getChildLinkIndex(), parentEl);
2860 
2861  // add fixed choices (in case link has them)
2862  {
2863  DOMElement* choicesParentEl =
2864  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
2865  std::vector<std::string> choices = t.getFixedChoices();
2866 
2867  //__COUT__ << "choices.size() " << choices.size() << __E__;
2868 
2869  for(const auto& choice : choices)
2870  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
2871  }
2872  }
2873  else // uid node
2874  {
2875  bool returnNode = true; // default to shown
2876 
2877  if(hideStatusFalse) // only show if status evaluates to true
2878  {
2879  try // try to get Status child as boolean..
2880  { // if Status bool doesn't exist exception will be thrown
2881  t.getNode(TableViewColumnInfo::COL_NAME_STATUS).getValue(returnNode);
2882  }
2883  catch(...)
2884  {
2885  }
2886  }
2887 
2888  if(returnNode)
2889  parentEl =
2890  xmlOut.addTextElementToParent("node", t.getValueAsString(), parentEl);
2891  else
2892  return; // done.. no further depth needed for node that is not shown
2893  }
2894 
2895  // if depth>=1 toXml all children
2896  // child.toXml(depth-1)
2897  if(depth >= 1)
2898  {
2899  auto C = t.getChildren();
2900  for(auto& c : C)
2901  recursiveTreeToXML(
2902  c.second, depth - 1, xmlOut, parentEl, hideStatusFalse);
2903  }
2904  }
2905 }
2906 
2907 //========================================================================================================================
2908 // handleGetLinkToChoicesXML
2909 // return all possible choices for link
2910 // linkIdType = "UID" or "GroupID"
2911 //
2912 // as xml:
2913 // <linkToChoice = xxx>
2914 void ConfigurationGUISupervisor::handleGetLinkToChoicesXML(
2915  HttpXmlDocument& xmlOut,
2916  ConfigurationManagerRW* cfgMgr,
2917  const std::string& linkToTableName,
2918  const TableVersion& linkToTableVersion,
2919  const std::string& linkIdType,
2920  const std::string& linkIndex,
2921  const std::string& linkInitId) try
2922 {
2923  // get table
2924  // if uid link
2925  // return all uids
2926  // if groupid link
2927  // find target column
2928  // create the set of values (unique values only)
2929  // note: insert group unions individually (i.e. groups | separated)
2930 
2931  // get table and activate target version
2932  // rename to re-use code template
2933  const std::string& tableName = linkToTableName;
2934  const TableVersion& version = linkToTableVersion;
2935  TableBase* config = cfgMgr->getTableByName(tableName);
2936  try
2937  {
2938  config->setActiveView(version);
2939  }
2940  catch(...)
2941  {
2942  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
2943  << version << __E__;
2944  cfgMgr->getVersionedTableByName(tableName, version);
2945  }
2946 
2947  if(version != config->getViewVersion())
2948  {
2949  __SUP_SS__ << "Target table version (" << version
2950  << ") is not the currently active version ("
2951  << config->getViewVersion() << ". Try refreshing the tree." << __E__;
2952  __SUP_COUT_WARN__ << ss.str();
2953  __SS_THROW__;
2954  }
2955 
2956  __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__;
2957 
2958  if(linkIdType == "UID")
2959  {
2960  // give all UIDs
2961  unsigned int col = config->getView().getColUID();
2962  for(unsigned int row = 0; row < config->getView().getNumberOfRows(); ++row)
2963  xmlOut.addTextElementToData("linkToChoice",
2964  config->getView().getDataView()[row][col]);
2965  }
2966  else if(linkIdType == "GroupID")
2967  {
2968  // find target column
2969  // create the set of values (unique values only)
2970  // note: insert group unions individually (i.e. groups | separated)
2971 
2972  __SUP_COUTV__(linkIndex);
2973  __SUP_COUTV__(linkInitId);
2974 
2975  std::set<std::string> setOfGroupIDs =
2976  config->getView().getSetOfGroupIDs(linkIndex);
2977 
2978  // build list of groupids
2979  // always include initial link group id in choices
2980  // (even if not in set of group ids)
2981  bool foundInitId = false;
2982  for(const auto& groupID : setOfGroupIDs)
2983  {
2984  if(!foundInitId && linkInitId == groupID)
2985  foundInitId = true; // mark init id found
2986 
2987  xmlOut.addTextElementToData("linkToChoice", groupID);
2988  }
2989  // if init id was not found, add to list
2990  if(!foundInitId)
2991  xmlOut.addTextElementToData("linkToChoice", linkInitId);
2992 
2993  // give all UIDs
2994  unsigned int col = config->getView().getColUID();
2995  for(unsigned int row = 0; row < config->getView().getNumberOfRows(); ++row)
2996  {
2997  xmlOut.addTextElementToData("groupChoice",
2998  config->getView().getDataView()[row][col]);
2999  if(config->getView().isEntryInGroup(row, linkIndex, linkInitId))
3000  xmlOut.addTextElementToData("groupMember",
3001  config->getView().getDataView()[row][col]);
3002  }
3003  }
3004  else
3005  {
3006  __SUP_SS__ << "Unrecognized linkIdType '" << linkIdType << ".'" << __E__;
3007  __SS_THROW__;
3008  }
3009 }
3010 catch(std::runtime_error& e)
3011 {
3012  __SUP_SS__ << "Error detected saving tree node!\n\n " << e.what() << __E__;
3013  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3014  xmlOut.addTextElementToData("Error", ss.str());
3015 }
3016 catch(...)
3017 {
3018  __SUP_SS__ << "Error detected saving tree node!\n\n " << __E__;
3019  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3020  xmlOut.addTextElementToData("Error", ss.str());
3021 }
3022 
3023 //========================================================================================================================
3024 // handleMergeGroupsXML
3025 void ConfigurationGUISupervisor::handleMergeGroupsXML(
3026  HttpXmlDocument& xmlOut,
3027  ConfigurationManagerRW* cfgMgr,
3028  const std::string& groupANameContext,
3029  const TableGroupKey& groupAKeyContext,
3030  const std::string& groupBNameContext,
3031  const TableGroupKey& groupBKeyContext,
3032  const std::string& groupANameConfig,
3033  const TableGroupKey& groupAKeyConfig,
3034  const std::string& groupBNameConfig,
3035  const TableGroupKey& groupBKeyConfig,
3036  const std::string& author,
3037  const std::string& mergeApproach) try
3038 {
3039  __SUP_COUT__ << "Merging context group pair " << groupANameContext << " ("
3040  << groupAKeyContext << ") & " << groupBNameContext << " ("
3041  << groupBKeyContext << ") and config group pair " << groupANameConfig
3042  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
3043  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
3044 
3045  // Merges group A and group B
3046  // with consideration for UID conflicts
3047  // Result is a new key of group A's name
3048  //
3049  // There 3 modes:
3050  // Rename -- All records from both groups are maintained, but conflicts from B
3051  // are renamed.
3052  // Must maintain a map of UIDs that are remapped to new name for
3053  // groupB, because linkUID fields must be preserved. Replace --
3054  // Any UID conflicts for a record are replaced by the record from group B.
3055  // Skip -- Any UID conflicts for a record are skipped so that group A record
3056  // remains
3057 
3058  // check valid mode
3059  if(!(mergeApproach == "Rename" || mergeApproach == "Replace" ||
3060  mergeApproach == "Skip"))
3061  {
3062  __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__;
3063  __SS_THROW__;
3064  }
3065 
3066  std::map<std::string /*name*/, TableVersion /*version*/> memberMapAContext,
3067  memberMapBContext, memberMapAConfig, memberMapBConfig;
3068 
3069  // check if skipping group pairs
3070  bool skippingContextPair = false;
3071  bool skippingConfigPair = false;
3072  if(groupANameContext.size() == 0 || groupANameContext[0] == ' ' ||
3073  groupBNameContext.size() == 0 || groupBNameContext[0] == ' ')
3074  {
3075  skippingContextPair = true;
3076  __SUP_COUTV__(skippingContextPair);
3077  }
3078  if(groupANameConfig.size() == 0 || groupANameConfig[0] == ' ' ||
3079  groupBNameConfig.size() == 0 || groupBNameConfig[0] == ' ')
3080  {
3081  skippingConfigPair = true;
3082  __SUP_COUTV__(skippingConfigPair);
3083  }
3084 
3085  // get context group member maps
3086  if(!skippingContextPair)
3087  {
3088  cfgMgr->loadTableGroup(groupANameContext,
3089  groupAKeyContext,
3090  false /*doActivate*/,
3091  &memberMapAContext,
3092  0 /*progressBar*/,
3093  0 /*accumulateErrors*/,
3094  0 /*groupComment*/,
3095  0 /*groupAuthor*/,
3096  0 /*groupCreationTime*/,
3097  false /*doNotLoadMember*/,
3098  0 /*groupTypeString*/
3099  );
3100  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
3101 
3102  cfgMgr->loadTableGroup(groupBNameContext,
3103  groupBKeyContext,
3104  false /*doActivate*/,
3105  &memberMapBContext,
3106  0 /*progressBar*/,
3107  0 /*accumulateErrors*/,
3108  0 /*groupComment*/,
3109  0 /*groupAuthor*/,
3110  0 /*groupCreationTime*/,
3111  false /*doNotLoadMember*/,
3112  0 /*groupTypeString*/
3113  );
3114 
3115  __SUP_COUTV__(StringMacros::mapToString(memberMapBContext));
3116  }
3117 
3118  // get config group member maps
3119  if(!skippingConfigPair)
3120  {
3121  cfgMgr->loadTableGroup(groupANameConfig,
3122  groupAKeyConfig,
3123  false /*doActivate*/,
3124  &memberMapAConfig,
3125  0 /*progressBar*/,
3126  0 /*accumulateErrors*/,
3127  0 /*groupComment*/,
3128  0 /*groupAuthor*/,
3129  0 /*groupCreationTime*/,
3130  false /*doNotLoadMember*/,
3131  0 /*groupTypeString*/
3132  );
3133  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
3134 
3135  cfgMgr->loadTableGroup(groupBNameConfig,
3136  groupBKeyConfig,
3137  false /*doActivate*/,
3138  &memberMapBConfig,
3139  0 /*progressBar*/,
3140  0 /*accumulateErrors*/,
3141  0 /*groupComment*/,
3142  0 /*groupAuthor*/,
3143  0 /*groupCreationTime*/,
3144  false /*doNotLoadMember*/,
3145  0 /*groupTypeString*/
3146  );
3147 
3148  __SUP_COUTV__(StringMacros::mapToString(memberMapBConfig));
3149  }
3150 
3151  // for each member of B
3152  // if not found in A member map, add it
3153  // if found in both member maps, and versions are different, load both tables and
3154  // merge
3155 
3156  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>,
3157  std::string /*converted uidB*/>
3158  uidConversionMap;
3159  std::map<
3160  std::pair<std::string /*original table*/,
3161  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>>,
3162  std::string /*converted gidB*/>
3163  groupidConversionMap;
3164 
3165  // first loop create record conversion map, second loop implement merge (using
3166  // conversion map if Rename)
3167  for(unsigned int i = 0; i < 2; ++i)
3168  {
3169  if(i == 0 && mergeApproach != "Rename")
3170  continue; // only need to construct uidConversionMap for rename approach
3171 
3172  // loop for context and config pair types
3173  for(unsigned int j = 0; j < 2; ++j)
3174  {
3175  if(j == 0 && skippingContextPair) // context
3176  {
3177  __COUT__ << "Skipping context pair..." << __E__;
3178  continue;
3179  }
3180  else if(j == 1 && skippingConfigPair)
3181  {
3182  __COUT__ << "Skipping config pair..." << __E__;
3183  continue;
3184  }
3185 
3186  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapAref =
3187  j == 0 ? memberMapAContext : memberMapAConfig;
3188 
3189  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapBref =
3190  j == 0 ? memberMapBContext : memberMapBConfig;
3191 
3192  if(j == 0) // context
3193  __COUT__ << "Context pair..." << __E__;
3194  else
3195  __COUT__ << "Config pair..." << __E__;
3196 
3197  __COUT__ << "Starting member map B scan." << __E__;
3198  for(const auto bkey : memberMapBref)
3199  {
3200  __SUP_COUTV__(bkey.first);
3201 
3202  if(memberMapAref.find(bkey.first) == memberMapAref.end())
3203  {
3204  // not found, so add to A member map
3205  memberMapAref[bkey.first] = bkey.second;
3206  }
3207  else if(memberMapAref[bkey.first] != bkey.second)
3208  {
3209  // found table version confict
3210  __SUP_COUTV__(memberMapAref[bkey.first]);
3211  __SUP_COUTV__(bkey.second);
3212 
3213  // load both tables, and merge
3214  TableBase* config = cfgMgr->getTableByName(bkey.first);
3215 
3216  __SUP_COUT__ << "Got table." << __E__;
3217 
3218  TableVersion newVersion = config->mergeViews(
3219  cfgMgr
3220  ->getVersionedTableByName(bkey.first,
3221  memberMapAref[bkey.first])
3222  ->getView(),
3223  cfgMgr->getVersionedTableByName(bkey.first, bkey.second)
3224  ->getView(),
3225  TableVersion() /* destinationVersion*/,
3226  author,
3227  mergeApproach /*Rename,Replace,Skip*/,
3228  uidConversionMap,
3229  groupidConversionMap,
3230  i == 0 /* fillRecordConversionMaps */,
3231  i == 1 /* applyRecordConversionMaps */,
3232  config->getTableName() ==
3233  ConfigurationManager::
3234  XDAQ_APPLICATION_TABLE_NAME /* generateUniqueDataColumns
3235  */
3236  ); // dont make destination version the first time
3237 
3238  if(i == 1)
3239  {
3240  __SUP_COUTV__(newVersion);
3241 
3242  try
3243  {
3244  // save all temporary tables to persistent tables
3245  // finish off the version creation
3246  newVersion = saveModifiedVersionXML(
3247  xmlOut,
3248  cfgMgr,
3249  bkey.first,
3250  TableVersion() /*original source version*/,
3251  false /* makeTemporary */,
3252  config,
3253  newVersion /*temporary modified version*/,
3254  false /*ignore duplicates*/,
3255  true /*look for equivalent*/);
3256  }
3257  catch(std::runtime_error& e)
3258  {
3259  __SUP_SS__
3260  << "There was an error saving the '"
3261  << config->getTableName()
3262  << "' merge result to a persistent table version. "
3263  << "Perhaps you can modify this table in one of the "
3264  "groups to resolve this issue, and then re-merge."
3265  << __E__ << e.what();
3266  __SS_THROW__;
3267  }
3268 
3269  __SUP_COUTV__(newVersion);
3270 
3271  memberMapAref[bkey.first] = newVersion;
3272  }
3273  } // end member version conflict handling
3274  } // end B member map loop
3275  } // end context and config loop
3276  } // end top level conversion map or not loop
3277 
3278  // Now save groups
3279 
3280  if(!skippingContextPair)
3281  {
3282  __SUP_COUT__ << "New context member map complete." << __E__;
3283  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
3284 
3285  // save the new table group
3286  TableGroupKey newKeyContext = cfgMgr->saveNewTableGroup(
3287  groupANameContext,
3288  memberMapAContext,
3289  "Merger of group " + groupANameContext + " (" + groupAKeyContext.toString() +
3290  ") and " + groupBNameContext + " (" + groupBKeyContext.toString() + ").");
3291 
3292  // return new resulting group
3293  xmlOut.addTextElementToData("ContextGroupName", groupANameContext);
3294  xmlOut.addTextElementToData("ContextGroupKey", newKeyContext.toString());
3295  }
3296  if(!skippingConfigPair)
3297  {
3298  __SUP_COUT__ << "New config member map complete." << __E__;
3299  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
3300 
3301  // save the new table group
3302  TableGroupKey newKeyConfig = cfgMgr->saveNewTableGroup(
3303  groupANameConfig,
3304  memberMapAConfig,
3305  "Merger of group " + groupANameConfig + " (" + groupAKeyConfig.toString() +
3306  ") and " + groupBNameConfig + " (" + groupBKeyConfig.toString() + ").");
3307 
3308  // return new resulting group
3309  xmlOut.addTextElementToData("ConfigGroupName", groupANameConfig);
3310  xmlOut.addTextElementToData("ConfigGroupKey", newKeyConfig.toString());
3311  }
3312 
3313 } // end handleMergeGroupsXML
3314 catch(std::runtime_error& e)
3315 {
3316  __SUP_SS__ << "Error merging context group pair " << groupANameContext << " ("
3317  << groupAKeyContext << ") & " << groupBNameContext << " ("
3318  << groupBKeyContext << ") and config group pair " << groupANameConfig
3319  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
3320  << groupBKeyConfig << ") with approach '" << mergeApproach << "': \n\n"
3321  << e.what() << __E__;
3322  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3323  xmlOut.addTextElementToData("Error", ss.str());
3324 }
3325 catch(...)
3326 {
3327  __SUP_SS__ << "Unknown error merging context group pair " << groupANameContext << " ("
3328  << groupAKeyContext << ") & " << groupBNameContext << " ("
3329  << groupBKeyContext << ") and config group pair " << groupANameConfig
3330  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
3331  << groupBKeyConfig << ") with approach '" << mergeApproach << ".' \n\n";
3332  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3333  xmlOut.addTextElementToData("Error", ss.str());
3334 }
3335 
3336 //========================================================================================================================
3337 // handleSavePlanCommandSequenceXML
3338 void ConfigurationGUISupervisor::handleSavePlanCommandSequenceXML(
3339  HttpXmlDocument& xmlOut,
3340  ConfigurationManagerRW* cfgMgr,
3341  const std::string& groupName,
3342  const TableGroupKey& groupKey,
3343  const std::string& modifiedTables,
3344  const std::string& author,
3345  const std::string& planName,
3346  const std::string& commandString) try
3347 {
3348  __MOUT__ << "handleSavePlanCommandSequenceXML" << __E__;
3349 
3350  // setup active tables based on input group and modified tables
3351  setupActiveTablesXML(xmlOut,
3352  cfgMgr,
3353  groupName,
3354  groupKey,
3355  modifiedTables,
3356  true /* refresh all */,
3357  false /* getGroupInfo */,
3358  0 /* returnMemberMap */,
3359  false /* outputActiveTables */);
3360 
3361  TableEditStruct planTable(IterateTable::PLAN_TABLE,
3362  cfgMgr); // Table ready for editing!
3363  TableEditStruct targetTable(IterateTable::TARGET_TABLE,
3364  cfgMgr); // Table ready for editing!
3365 
3366  // create table-edit struct for each iterate command type
3367  std::map<std::string, TableEditStruct> commandTypeToCommandTableMap;
3368  for(const auto& commandPair : IterateTable::commandToTableMap_)
3369  if(commandPair.second != "") // skip tables with no parameters
3370  commandTypeToCommandTableMap.emplace(std::pair<std::string, TableEditStruct>(
3371  commandPair.first, TableEditStruct(commandPair.second, cfgMgr)));
3372 
3373  // try to catch any errors while editing..
3374  // if errors delete temporary plan view (if created here)
3375  try
3376  {
3377  // Steps:
3378  // Reset plan commands
3379  // Remove all commands in group "<plan>-Plan"
3380  // Delete linked command parameters row (in separate table)
3381  // If no group remaining, then delete row.
3382  //
3383  // Save plan commands (if modified)
3384  // Create rows and add them to group "<plan>-Plan"
3385  // create row for command paramaters and add to proper table
3386 
3387  std::string groupName = planName + "-Plan";
3388  __SUP_COUT__ << "Handling commands for group " << groupName << __E__;
3389 
3390  unsigned int groupIdCol =
3391  planTable.tableView_->findCol(IterateTable::planTableCols_.GroupID_);
3392  unsigned int cmdTypeCol =
3393  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandType_);
3394 
3395  unsigned int targetGroupIdCol =
3396  targetTable.tableView_->findCol(IterateTable::targetCols_.GroupID_);
3397  unsigned int targetTableCol =
3398  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLink_);
3399  unsigned int targetUIDCol =
3400  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLinkUID_);
3401 
3402  std::string groupLinkIndex =
3403  planTable.tableView_->getColumnInfo(groupIdCol).getChildLinkIndex();
3404  __SUP_COUT__ << "groupLinkIndex: " << groupLinkIndex << __E__;
3405 
3406  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> commandUidLink;
3407  {
3408  bool isGroup; // local because we know is uid link
3409  planTable.tableView_->getChildLink(
3410  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandLink_),
3411  isGroup,
3412  commandUidLink);
3413  }
3414 
3415  unsigned int cmdRow, cmdCol;
3416  std::string targetGroupName;
3417 
3418  // Reset existing plan commands
3419  {
3420  std::string targetUID, cmdType;
3421 
3422  for(unsigned int row = 0; row < planTable.tableView_->getNumberOfRows();
3423  ++row)
3424  {
3425  targetUID = planTable.tableView_
3426  ->getDataView()[row][planTable.tableView_->getColUID()];
3427  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
3428 
3429  // remove command from plan group.. if no more groups, delete
3430  if(planTable.tableView_->isEntryInGroup(row, groupLinkIndex, groupName))
3431  {
3432  __SUP_COUT__ << "Removing." << __E__;
3433 
3434  // delete linked command
3435  // find linked UID in table (mapped by type)
3436  cmdType = planTable.tableView_->getDataView()[row][cmdTypeCol];
3437  if(commandTypeToCommandTableMap.find(cmdType) !=
3438  commandTypeToCommandTableMap
3439  .end()) // skip if invalid command type
3440  {
3441  cmdRow =
3442  commandTypeToCommandTableMap[cmdType].tableView_->findRow(
3443  commandTypeToCommandTableMap[cmdType]
3444  .tableView_->getColUID(),
3445  planTable.tableView_
3446  ->getDataView()[row][commandUidLink.second]);
3447 
3448  // before deleting row...
3449  // look for target group
3450  // remove all targets in group
3451  try
3452  {
3453  cmdCol =
3454  commandTypeToCommandTableMap[cmdType].tableView_->findCol(
3455  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
3456  targetGroupName =
3457  commandTypeToCommandTableMap[cmdType]
3458  .tableView_->getDataView()[cmdRow][cmdCol];
3459 
3460  for(unsigned int trow = 0;
3461  trow < targetTable.tableView_->getNumberOfRows();
3462  ++trow)
3463  {
3464  // remove command from target group..
3465  if(targetTable.tableView_->isEntryInGroup(
3466  trow,
3467  commandTypeToCommandTableMap[cmdType]
3468  .tableView_->getColumnInfo(cmdCol)
3469  .getChildLinkIndex(),
3470  targetGroupName))
3471  {
3472  __SUP_COUT__ << "Removing target." << __E__;
3473  // remove command entry in plan table
3474  if(targetTable.tableView_->removeRowFromGroup(
3475  trow,
3476  targetGroupIdCol,
3477  targetGroupName,
3478  true /*deleteRowIfNoGroup*/))
3479  --trow; // since row was deleted, go back!
3480  }
3481  }
3482  }
3483  catch(...)
3484  {
3485  __SUP_COUT__ << "No targets." << __E__;
3486  }
3487 
3488  // now no more targets, delete row
3489 
3490  commandTypeToCommandTableMap[cmdType].tableView_->deleteRow(
3491  cmdRow);
3492 
3493  commandTypeToCommandTableMap[cmdType].modified_ = true;
3494  }
3495 
3496  // remove command entry in plan table
3497  if(planTable.tableView_->removeRowFromGroup(
3498  row, groupIdCol, groupName, true /*deleteRowIfNoGroup*/))
3499  --row; // since row was deleted, go back!
3500  }
3501  }
3502  }
3503 
3504  // Done resetting existing plan
3505  // Now save new commands
3506 
3507  std::vector<IterateTable::Command> commands;
3508 
3509  // extract command sequence and add to table
3510  // into vector with type, and params
3511  {
3512  std::istringstream f(commandString);
3513  std::string commandSubString, paramSubString, paramValue;
3514  int i;
3515  while(getline(f, commandSubString, ';'))
3516  {
3517  //__SUP_COUT__ << "commandSubString " << commandSubString << __E__;
3518  std::istringstream g(commandSubString);
3519 
3520  i = 0;
3521  while(getline(g, paramSubString, ','))
3522  {
3523  //__SUP_COUT__ << "paramSubString " << paramSubString << __E__;
3524  if(i == 0) // type
3525  {
3526  if(paramSubString != "type")
3527  {
3528  __SUP_SS__ << "Invalid command sequence" << __E__;
3529  __SS_THROW__;
3530  }
3531  // create command object
3532  commands.push_back(IterateTable::Command());
3533 
3534  getline(g, paramValue, ',');
3535  ++i;
3536  //__SUP_COUT__ << "paramValue " << paramValue << __E__;
3537  commands.back().type_ = paramValue;
3538  }
3539  else // params
3540  {
3541  getline(g, paramValue, ',');
3542  ++i;
3543  //__SUP_COUT__ << "paramValue " << paramValue << __E__;
3544 
3545  commands.back().params_.emplace(
3546  std::pair<std::string /*param name*/,
3547  std::string /*param value*/>(
3548  paramSubString,
3549  StringMacros::decodeURIComponent(paramValue)));
3550  }
3551 
3552  ++i;
3553  }
3554  }
3555 
3556  } // end extract command sequence
3557 
3558  __SUP_COUT__ << "commands size " << commands.size() << __E__;
3559 
3560  // at this point, have extracted commands
3561 
3562  // now save commands to plan group
3563  // group should be "<plan>-Plan"
3564 
3565  unsigned int row, tgtRow;
3566  unsigned int targetIndex;
3567  std::string targetStr, cmdUID;
3568 
3569  for(auto& command : commands)
3570  {
3571  __SUP_COUT__ << "command " << command.type_ << __E__;
3572  __SUP_COUT__ << "table " << IterateTable::commandToTableMap_.at(command.type_)
3573  << __E__;
3574 
3575  // create command entry at plan level
3576  row = planTable.tableView_->addRow(author, "planCommand");
3577  planTable.tableView_->addRowToGroup(row, groupIdCol, groupName);
3578 
3579  // set command type
3580  planTable.tableView_->setURIEncodedValue(command.type_, row, cmdTypeCol);
3581 
3582  // set command status true
3583  planTable.tableView_->setValueAsString(
3584  "1", row, planTable.tableView_->getColStatus());
3585 
3586  // create command specifics
3587  if(commandTypeToCommandTableMap.find(command.type_) !=
3588  commandTypeToCommandTableMap.end()) // if table exists in map! (some
3589  // commands may have no parameters)
3590  {
3591  __SUP_COUT__ << "table "
3592  << commandTypeToCommandTableMap[command.type_].tableName_
3593  << __E__;
3594 
3595  // at this point have config, tempVersion, and createdFlag
3596 
3597  // create command parameter entry at command level
3598  cmdRow = commandTypeToCommandTableMap[command.type_].tableView_->addRow(
3599  author, true /*incrementUniqueData*/, command.type_ + "_COMMAND_");
3600 
3601  // parameters are linked
3602  // now set value of all parameters
3603  // find parameter column, and set value
3604  // if special target parameter, extract targets
3605  for(auto& param : command.params_)
3606  {
3607  __SUP_COUT__ << "\t param " << param.first << " : " << param.second
3608  << __E__;
3609 
3610  if(param.first == IterateTable::targetParams_.Tables_)
3611  {
3612  __SUP_COUT__ << "\t\t found target tables" << __E__;
3613  std::istringstream f(param.second);
3614 
3615  targetIndex = 0;
3616  while(getline(f, targetStr, '='))
3617  {
3618  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
3619  if(!command.targets_.size() ||
3620  command.targets_.back().table_ != "")
3621  {
3622  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
3623  << __E__;
3624  // make new target
3625  command.addTarget();
3626  command.targets_.back().table_ = targetStr;
3627  }
3628  else // file existing target
3629  command.targets_[targetIndex++].table_ = targetStr;
3630  }
3631 
3632  continue; // go to next parameter
3633  }
3634 
3635  if(param.first == IterateTable::targetParams_.UIDs_)
3636  {
3637  __SUP_COUT__ << "\t\t found target UIDs" << __E__;
3638  std::istringstream f(param.second);
3639 
3640  targetIndex = 0;
3641  while(getline(f, targetStr, '='))
3642  {
3643  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
3644  if(!command.targets_.size() ||
3645  command.targets_.back().UID_ != "")
3646  {
3647  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
3648  << __E__;
3649  // make new target
3650  command.addTarget();
3651  command.targets_.back().UID_ = targetStr;
3652  }
3653  else // file existing target
3654  command.targets_[targetIndex++].UID_ = targetStr;
3655  }
3656  continue;
3657  }
3658 
3659  cmdCol =
3660  commandTypeToCommandTableMap[command.type_].tableView_->findCol(
3661  param.first);
3662 
3663  __SUP_COUT__ << "param col " << cmdCol << __E__;
3664 
3665  commandTypeToCommandTableMap[command.type_]
3666  .tableView_->setURIEncodedValue(param.second, cmdRow, cmdCol);
3667  } // end parameter loop
3668 
3669  cmdUID =
3670  commandTypeToCommandTableMap[command.type_].tableView_->getDataView()
3671  [cmdRow][commandTypeToCommandTableMap[command.type_]
3672  .tableView_->getColUID()];
3673 
3674  if(command.targets_.size())
3675  {
3676  // if targets, create group in target table
3677 
3678  __SUP_COUT__ << "targets found for command UID=" << cmdUID << __E__;
3679 
3680  // create link from command table to target
3681  cmdCol =
3682  commandTypeToCommandTableMap[command.type_].tableView_->findCol(
3683  IterateTable::commandTargetCols_.TargetsLink_);
3684  commandTypeToCommandTableMap[command.type_]
3685  .tableView_->setValueAsString(
3686  IterateTable::TARGET_TABLE, cmdRow, cmdCol);
3687 
3688  cmdCol =
3689  commandTypeToCommandTableMap[command.type_].tableView_->findCol(
3690  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
3691  commandTypeToCommandTableMap[command.type_]
3692  .tableView_->setValueAsString(
3693  cmdUID + "_Targets", cmdRow, cmdCol);
3694 
3695  // create row(s) for each target in target table with correct groupID
3696 
3697  for(const auto& target : command.targets_)
3698  {
3699  __SUP_COUT__ << target.table_ << " " << target.UID_ << __E__;
3700 
3701  // create target entry in target table in group
3702  tgtRow = targetTable.tableView_->addRow(author, "commandTarget");
3703  targetTable.tableView_->addRowToGroup(
3704  tgtRow, targetGroupIdCol, cmdUID + "_Targets");
3705 
3706  // set target table
3707  targetTable.tableView_->setValueAsString(
3708  target.table_, tgtRow, targetTableCol);
3709 
3710  // set target UID
3711  targetTable.tableView_->setValueAsString(
3712  target.UID_, tgtRow, targetUIDCol);
3713  }
3714  } // end target handling
3715 
3716  // add link at plan level to created UID
3717  planTable.tableView_->setValueAsString(
3718  commandTypeToCommandTableMap[command.type_].tableName_,
3719  row,
3720  commandUidLink.first);
3721  planTable.tableView_->setValueAsString(
3722  cmdUID, row, commandUidLink.second);
3723 
3724  __SUP_COUT__ << "linked to uid = " << cmdUID << __E__;
3725 
3726  commandTypeToCommandTableMap[command.type_].modified_ = true;
3727  } // done with command specifics
3728 
3729  } // end command loop
3730 
3731  // commands are created in the temporary tables
3732  // validate with init
3733 
3734  planTable.tableView_->print();
3735  planTable.tableView_->init(); // verify new table (throws runtime_errors)
3736 
3737  __SUP_COUT__ << "requestType tables:" << __E__;
3738 
3739  for(auto& modifiedConfig : commandTypeToCommandTableMap)
3740  {
3741  modifiedConfig.second.tableView_->print();
3742  modifiedConfig.second.tableView_->init();
3743  }
3744 
3745  targetTable.tableView_->print();
3746  targetTable.tableView_->init(); // verify new table (throws runtime_errors)
3747 
3748  } // end try for plan
3749  catch(...)
3750  {
3751  __SUP_COUT__ << "Handling command table errors while saving. Erasing all newly "
3752  "created versions."
3753  << __E__;
3754 
3755  // erase all temporary tables if created here
3756 
3757  if(planTable.createdTemporaryVersion_) // if temporary version created here
3758  {
3759  __SUP_COUT__ << "Erasing temporary version " << planTable.tableName_ << "-v"
3760  << planTable.temporaryVersion_ << __E__;
3761  // erase with proper version management
3762  cfgMgr->eraseTemporaryVersion(planTable.tableName_,
3763  planTable.temporaryVersion_);
3764  }
3765 
3766  if(targetTable.createdTemporaryVersion_) // if temporary version created here
3767  {
3768  __SUP_COUT__ << "Erasing temporary version " << targetTable.tableName_ << "-v"
3769  << targetTable.temporaryVersion_ << __E__;
3770  // erase with proper version management
3771  cfgMgr->eraseTemporaryVersion(targetTable.tableName_,
3772  targetTable.temporaryVersion_);
3773  }
3774 
3775  for(auto& modifiedConfig : commandTypeToCommandTableMap)
3776  {
3777  if(modifiedConfig.second
3778  .createdTemporaryVersion_) // if temporary version created here
3779  {
3780  __SUP_COUT__ << "Erasing temporary version "
3781  << modifiedConfig.second.tableName_ << "-v"
3782  << modifiedConfig.second.temporaryVersion_ << __E__;
3783  // erase with proper version management
3784  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
3785  modifiedConfig.second.temporaryVersion_);
3786  }
3787  }
3788 
3789  throw; // re-throw
3790  }
3791 
3792  // all edits are complete and tables verified
3793  // need to save all edits properly
3794  // if not modified, discard
3795 
3796  TableVersion finalVersion = saveModifiedVersionXML(
3797  xmlOut,
3798  cfgMgr,
3799  planTable.tableName_,
3800  planTable.originalVersion_,
3801  true /*make temporary*/,
3802  planTable.table_,
3803  planTable.temporaryVersion_,
3804  true /*ignoreDuplicates*/); // save temporary version properly
3805 
3806  __SUP_COUT__ << "Final plan version is " << planTable.tableName_ << "-v"
3807  << finalVersion << __E__;
3808 
3809  finalVersion = saveModifiedVersionXML(
3810  xmlOut,
3811  cfgMgr,
3812  targetTable.tableName_,
3813  targetTable.originalVersion_,
3814  true /*make temporary*/,
3815  targetTable.table_,
3816  targetTable.temporaryVersion_,
3817  true /*ignoreDuplicates*/); // save temporary version properly
3818 
3819  __SUP_COUT__ << "Final target version is " << targetTable.tableName_ << "-v"
3820  << finalVersion << __E__;
3821 
3822  for(auto& modifiedConfig : commandTypeToCommandTableMap)
3823  {
3824  if(!modifiedConfig.second.modified_)
3825  {
3826  if(modifiedConfig.second
3827  .createdTemporaryVersion_) // if temporary version created here
3828  {
3829  __SUP_COUT__ << "Erasing unmodified temporary version "
3830  << modifiedConfig.second.tableName_ << "-v"
3831  << modifiedConfig.second.temporaryVersion_ << __E__;
3832  // erase with proper version management
3833  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
3834  modifiedConfig.second.temporaryVersion_);
3835  }
3836  continue;
3837  }
3838 
3839  finalVersion = saveModifiedVersionXML(
3840  xmlOut,
3841  cfgMgr,
3842  modifiedConfig.second.tableName_,
3843  modifiedConfig.second.originalVersion_,
3844  true /*make temporary*/,
3845  modifiedConfig.second.table_,
3846  modifiedConfig.second.temporaryVersion_,
3847  true /*ignoreDuplicates*/); // save temporary version properly
3848 
3849  __SUP_COUT__ << "Final version is " << modifiedConfig.second.tableName_ << "-v"
3850  << finalVersion << __E__;
3851  }
3852 
3853  handleFillModifiedTablesXML(xmlOut, cfgMgr);
3854 }
3855 catch(std::runtime_error& e)
3856 {
3857  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << e.what() << __E__;
3858  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3859  xmlOut.addTextElementToData("Error", ss.str());
3860 }
3861 catch(...)
3862 {
3863  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << __E__;
3864  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3865  xmlOut.addTextElementToData("Error", ss.str());
3866 } // end handleSavePlanCommandSequenceXML
3867 
3868 //========================================================================================================================
3869 // handleSaveTreeNodeEditXML
3870 // Changes the value specified by UID/Column
3871 // in the specified version of the table.
3872 //
3873 // Error, if the specified version is not the active one.
3874 // If the version is not temporary make a new temporary version
3875 //
3876 // return this information on success
3877 // <resultingTargetTableVersion = xxx>
3878 void ConfigurationGUISupervisor::handleSaveTreeNodeEditXML(HttpXmlDocument& xmlOut,
3879  ConfigurationManagerRW* cfgMgr,
3880  const std::string& tableName,
3881  TableVersion version,
3882  const std::string& type,
3883  const std::string& uid,
3884  const std::string& colName,
3885  const std::string& newValue,
3886  const std::string& author) try
3887 {
3888  __SUP_COUT__ << "table " << tableName << "(" << version << ")" << __E__;
3889 
3890  // get the current table/version
3891  // check if the value is new
3892  // if new edit value (in a temporary version only)
3893 
3894  // get table and activate target version
3895  TableBase* config = cfgMgr->getTableByName(tableName);
3896  try
3897  {
3898  config->setActiveView(version);
3899  }
3900  catch(...)
3901  {
3902  if(version.isTemporaryVersion())
3903  throw; // if temporary, there is no hope to find lost version
3904 
3905  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
3906  << version << __E__;
3907  cfgMgr->getVersionedTableByName(tableName, version);
3908  }
3909 
3910  __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__;
3911 
3912  if(version != config->getViewVersion())
3913  {
3914  __SUP_SS__ << "Target table version (" << version
3915  << ") is not the currently active version ("
3916  << config->getViewVersion() << ". Try refreshing the tree." << __E__;
3917  __SS_THROW__;
3918  }
3919 
3920  unsigned int col = -1;
3921  if(type == "uid" || type == "delete-uid")
3922  col = config->getView().getColUID();
3923  else if(type == "link-UID" || type == "link-GroupID" || type == "value" ||
3924  type == "value-groupid" || type == "value-bool" || type == "value-bitmap")
3925  col = config->getView().findCol(colName);
3926  else if(type == "table" || type == "link-comment" || type == "table-newGroupRow" ||
3927  type == "table-newUIDRow" || type == "table-newRow")
3928  ; // column N/A
3929  else
3930  {
3931  __SUP_SS__ << "Impossible! Unrecognized edit type: " << type << __E__;
3932  __SS_THROW__;
3933  }
3934 
3935  // check if the comment value is new before making temporary version
3936  if(type == "table" || type == "link-comment")
3937  {
3938  // editing comment, so check if comment is different
3939  if(config->getView().isURIEncodedCommentTheSame(newValue))
3940  {
3941  __SUP_SS__ << "Comment '" << newValue
3942  << "' is the same as the current comment. No need to save change."
3943  << __E__;
3944  __SS_THROW__;
3945  }
3946  }
3947 
3948  // version handling:
3949  // always make a new temporary-version from source-version
3950  // edit temporary-version
3951  // if edit fails
3952  // delete temporary-version
3953  // else
3954  // return new temporary-version
3955  // if source-version was temporary
3956  // then delete source-version
3957 
3958  TableVersion temporaryVersion = config->createTemporaryView(version);
3959 
3960  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
3961 
3962  TableView* cfgView = config->getTemporaryView(temporaryVersion);
3963 
3964  // edit/verify new table (throws runtime_errors)
3965  try
3966  {
3967  // have view so edit it
3968  if(type == "table" || type == "link-comment")
3969  {
3970  // edit comment
3971  cfgView->setURIEncodedComment(newValue);
3972  }
3973  else if(type == "table-newRow" || type == "table-newUIDRow")
3974  {
3975  // add row
3976  unsigned int row = cfgView->addRow(author, true /*incrementUniqueData*/);
3977 
3978  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
3979  try
3980  {
3981  col = cfgView->getColStatus();
3982  cfgView->setValueAsString("1", row, col);
3983  }
3984  catch(...)
3985  {
3986  } // if not, ignore
3987 
3988  // set UID value
3989  cfgView->setURIEncodedValue(newValue, row, cfgView->getColUID());
3990  }
3991  else if(type == "table-newGroupRow")
3992  {
3993  // add row
3994  unsigned int row = cfgView->addRow(author, true /*incrementUniqueData*/);
3995 
3996  // get index value and group id value
3997  unsigned int csvIndex = newValue.find(',');
3998 
3999  std::string linkIndex = newValue.substr(0, csvIndex);
4000  std::string groupId = newValue.substr(csvIndex + 1);
4001 
4002  // get new row UID value from second part of string
4003  csvIndex = groupId.find(',');
4004  std::string newRowUID = groupId.substr(csvIndex + 1);
4005  groupId = groupId.substr(0, csvIndex);
4006 
4007  __SUP_COUT__ << "newValue " << linkIndex << "," << groupId << "," << newRowUID
4008  << __E__;
4009 
4010  // set UID value
4011  cfgView->setURIEncodedValue(newRowUID, row, cfgView->getColUID());
4012 
4013  // find groupId column from link index
4014  col = cfgView->getColLinkGroupID(linkIndex);
4015 
4016  // set group id
4017  cfgView->setURIEncodedValue(groupId, row, col);
4018 
4019  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
4020  try
4021  {
4022  col = cfgView->getColStatus();
4023  cfgView->setValueAsString("1", row, col);
4024  }
4025  catch(...)
4026  {
4027  } // if not, ignore
4028  }
4029  else if(type == "delete-uid")
4030  {
4031  // delete row
4032  unsigned int row = cfgView->findRow(col, uid);
4033  cfgView->deleteRow(row);
4034  }
4035  else if(type == "uid" || type == "value" || type == "value-groupid" ||
4036  type == "value-bool" || type == "value-bitmap")
4037  {
4038  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
4039  if(!cfgView->setURIEncodedValue(newValue, row, col, author))
4040  {
4041  // no change! so discard
4042  __SUP_SS__ << "Value '" << newValue
4043  << "' is the same as the current value. No need to save "
4044  "change to tree node."
4045  << __E__;
4046  __SS_THROW__;
4047  }
4048  }
4049  else if(type == "link-UID" || type == "link-GroupID")
4050  {
4051  bool isGroup;
4052  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
4053  if(!cfgView->getChildLink(col, isGroup, linkPair))
4054  {
4055  // not a link ?!
4056  __SUP_SS__ << "Col '" << colName << "' is not a link column." << __E__;
4057  __SS_THROW__;
4058  }
4059 
4060  __SUP_COUT__ << "linkPair " << linkPair.first << "," << linkPair.second
4061  << __E__;
4062 
4063  std::string linkIndex = cfgView->getColumnInfo(col).getChildLinkIndex();
4064 
4065  __SUP_COUT__ << "linkIndex " << linkIndex << __E__;
4066 
4067  // find table value and id value
4068  unsigned int csvIndexStart = 0, csvIndex = newValue.find(',');
4069 
4070  std::string newTable = newValue.substr(csvIndexStart, csvIndex);
4071  csvIndexStart = csvIndex + 1;
4072  csvIndex = newValue.find(',', csvIndexStart);
4073  std::string newLinkId = newValue.substr(
4074  csvIndexStart,
4075  csvIndex -
4076  csvIndexStart); // if no more commas will take the rest of string
4077 
4078  __SUP_COUT__ << "newValue " << newTable << "," << newLinkId << __E__;
4079 
4080  // change target table in two parts
4081  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
4082  bool changed = false;
4083  if(!cfgView->setURIEncodedValue(newTable, row, linkPair.first, author))
4084  {
4085  // no change
4086  __SUP_COUT__ << "Value '" << newTable
4087  << "' is the same as the current value." << __E__;
4088  }
4089  else
4090  changed = true;
4091 
4092  if(!cfgView->setURIEncodedValue(newLinkId, row, linkPair.second, author))
4093  {
4094  // no change
4095  __SUP_COUT__ << "Value '" << newLinkId
4096  << "' is the same as the current value." << __E__;
4097  }
4098  else
4099  changed = true;
4100 
4101  // handle groupID links slightly differently
4102  // have to look at changing link table too!
4103  // if group ID set all in member list to be members of group
4104  if(type == "link-GroupID")
4105  {
4106  bool secondaryChanged = false;
4107 
4108  // first close out main target table
4109  if(!changed) // if no changes throw out new version
4110  {
4111  __SUP_COUT__ << "No changes to primary view. Erasing temporary table."
4112  << __E__;
4113  config->eraseView(temporaryVersion);
4114  }
4115  else // if changes, save it
4116  {
4117  try
4118  {
4119  cfgView->init(); // verify new table (throws runtime_errors)
4120 
4121  saveModifiedVersionXML(xmlOut,
4122  cfgMgr,
4123  tableName,
4124  version,
4125  true /*make temporary*/,
4126  config,
4127  temporaryVersion,
4128  true /*ignoreDuplicates*/); // save
4129  // temporary
4130  // version
4131  // properly
4132  }
4133  catch(std::runtime_error&
4134  e) // erase temporary view before re-throwing error
4135  {
4136  __SUP_COUT__ << "Caught error while editing main table. Erasing "
4137  "temporary version."
4138  << __E__;
4139  config->eraseView(temporaryVersion);
4140  changed = false; // undo changed bool
4141 
4142  // send warning so that, secondary table can still be changed
4143  xmlOut.addTextElementToData(
4144  "Warning",
4145  "Error saving primary tree node! " + std::string(e.what()));
4146  }
4147  }
4148 
4149  // now, onto linked table
4150 
4151  // get the current linked table/version
4152  // check if the value is new
4153  // if new edit value (in a temporary version only)
4154 
4155  csvIndexStart = csvIndex + 1;
4156  csvIndex = newValue.find(',', csvIndexStart);
4157  version = TableVersion(newValue.substr(
4158  csvIndexStart, csvIndex - csvIndexStart)); // if no more commas will
4159  // take the rest of string
4160 
4161  if(newTable == TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
4162  {
4163  // done, since init was already tested
4164  // the result should be purposely DISCONNECTED link
4165  return;
4166  }
4167 
4168  // get table and activate target version
4169  config = cfgMgr->getTableByName(newTable);
4170  try
4171  {
4172  config->setActiveView(version);
4173  }
4174  catch(...)
4175  {
4176  __SUP_COUT__ << "Failed to find stored version, so attempting to "
4177  "load version: "
4178  << version << __E__;
4179  cfgMgr->getVersionedTableByName(newTable, version);
4180  }
4181 
4182  __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__;
4183 
4184  if(version != config->getViewVersion())
4185  {
4186  __SUP_SS__ << "Target table version (" << version
4187  << ") is not the currently active version ("
4188  << config->getViewVersion() << ". Try refreshing the tree."
4189  << __E__;
4190  __SS_THROW__;
4191  }
4192 
4193  // create temporary version for editing
4194  temporaryVersion = config->createTemporaryView(version);
4195 
4196  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
4197 
4198  cfgView = config->getTemporaryView(temporaryVersion);
4199 
4200  col = cfgView->getColLinkGroupID(linkIndex);
4201 
4202  __SUP_COUT__ << "target col " << col << __E__;
4203 
4204  // extract vector of members to be
4205  std::vector<std::string> memberUIDs;
4206  do
4207  {
4208  csvIndexStart = csvIndex + 1;
4209  csvIndex = newValue.find(',', csvIndexStart);
4210  memberUIDs.push_back(
4211  newValue.substr(csvIndexStart, csvIndex - csvIndexStart));
4212  __SUP_COUT__ << "memberUIDs: " << memberUIDs.back() << __E__;
4213  } while(csvIndex != (unsigned int)std::string::npos); // no more commas
4214 
4215  // for each row,
4216  // check if should be in group
4217  // if should be but is not
4218  // add to group, CHANGE
4219  // if should not be but is
4220  // remove from group, CHANGE
4221  //
4222 
4223  std::string targetUID;
4224  bool shouldBeInGroup;
4225  bool defaultIsInGroup =
4226  false; // use to indicate if a recent new member was created
4227  bool isInGroup;
4228 
4229  for(unsigned int row = 0; row < cfgView->getNumberOfRows(); ++row)
4230  {
4231  targetUID = cfgView->getDataView()[row][cfgView->getColUID()];
4232  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
4233 
4234  shouldBeInGroup = false;
4235  for(unsigned int i = 0; i < memberUIDs.size(); ++i)
4236  if(targetUID == memberUIDs[i])
4237  {
4238  // found in member uid list
4239  shouldBeInGroup = true;
4240  break;
4241  }
4242 
4243  isInGroup = cfgView->isEntryInGroup(row, linkIndex, newLinkId);
4244 
4245  // if should be but is not
4246  if(shouldBeInGroup && !isInGroup)
4247  {
4248  __SUP_COUT__ << "Changed YES: " << row << __E__;
4249  secondaryChanged = true;
4250 
4251  cfgView->addRowToGroup(row, col, newLinkId);
4252 
4253  } // if should not be but is
4254  else if(!shouldBeInGroup && isInGroup)
4255  {
4256  __SUP_COUT__ << "Changed NO: " << row << __E__;
4257  secondaryChanged = true;
4258 
4259  cfgView->removeRowFromGroup(row, col, newLinkId);
4260  }
4261  else if(targetUID ==
4262  cfgView->getDefaultRowValues()[cfgView->getColUID()] &&
4263  isInGroup)
4264  {
4265  // use to indicate if a recent new member was created
4266  defaultIsInGroup = true;
4267  }
4268  }
4269 
4270  // first close out main target table
4271  if(!secondaryChanged) // if no changes throw out new version
4272  {
4273  __SUP_COUT__
4274  << "No changes to secondary view. Erasing temporary table."
4275  << __E__;
4276  config->eraseView(temporaryVersion);
4277  }
4278  else // if changes, save it
4279  {
4280  try
4281  {
4282  cfgView->init(); // verify new table (throws runtime_errors)
4283 
4284  saveModifiedVersionXML(xmlOut,
4285  cfgMgr,
4286  newTable,
4287  version,
4288  true /*make temporary*/,
4289  config,
4290  temporaryVersion,
4291  true /*ignoreDuplicates*/); // save
4292  // temporary
4293  // version
4294  // properly
4295  }
4296  catch(std::runtime_error&
4297  e) // erase temporary view before re-throwing error
4298  {
4299  __SUP_COUT__ << "Caught error while editing secondary table. "
4300  "Erasing temporary version."
4301  << __E__;
4302  config->eraseView(temporaryVersion);
4303  secondaryChanged = false; // undo changed bool
4304 
4305  // send warning so that, secondary table can still be changed
4306  xmlOut.addTextElementToData(
4307  "Warning",
4308  "Error saving secondary tree node! " + std::string(e.what()));
4309  }
4310  }
4311 
4312  // block error message if default is in group, assume new member was just
4313  // created RAR: block because its hard to detect if changes were recently
4314  // made (one idea: to check if all other values are defaults, to assume it
4315  // was just created)
4316  if(0 && !changed && !secondaryChanged && !defaultIsInGroup)
4317  {
4318  __SUP_SS__ << "Link to table '" << newTable << "', linkID '"
4319  << newLinkId
4320  << "', and selected group members are the same as the "
4321  "current value. "
4322  << "No need to save changes to tree." << __E__;
4323  __SS_THROW__;
4324  }
4325 
4326  return; // exit since table inits were already tested
4327  }
4328  else if(0 && !changed) // block error message because sometimes things get
4329  // setup twice depending on the path of the user (e.g.
4330  // when editing links in tree-view)
4331  { // RAR: block also becuase versions are temporary at this point anyway,
4332  // might as well abuse temporary versions
4333  __SUP_SS__ << "Link to table '" << newTable << "' and linkID '"
4334  << newLinkId
4335  << "' are the same as the current values. No need to save "
4336  "change to tree node."
4337  << __E__;
4338  __SS_THROW__;
4339  }
4340  }
4341 
4342  cfgView->init(); // verify new table (throws runtime_errors)
4343  }
4344  catch(...) // erase temporary view before re-throwing error
4345  {
4346  __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
4347  config->eraseView(temporaryVersion);
4348  throw;
4349  }
4350 
4351  saveModifiedVersionXML(xmlOut,
4352  cfgMgr,
4353  tableName,
4354  version,
4355  true /*make temporary*/,
4356  config,
4357  temporaryVersion,
4358  true /*ignoreDuplicates*/); // save temporary version properly
4359 }
4360 catch(std::runtime_error& e)
4361 {
4362  __SUP_SS__ << "Error saving tree node! " << e.what() << __E__;
4363  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4364  xmlOut.addTextElementToData("Error", ss.str());
4365 }
4366 catch(...)
4367 {
4368  __SUP_SS__ << "Unknown Error saving tree node! " << __E__;
4369  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4370  xmlOut.addTextElementToData("Error", ss.str());
4371 }
4372 
4373 //========================================================================================================================
4374 // handleGetTableGroupXML
4375 //
4376 // give the detail of specific table specified
4377 // groupKey=-1 returns latest
4378 //
4379 // Find historical group keys
4380 // and figure out all member configurations versions
4381 
4382 //
4383 // return this information
4384 // <group name=xxx key=xxx>
4385 // <historical key=xxx>
4386 // <historical key=xxx>
4387 // ....
4388 // <table name=xxx version=xxx />
4389 // <historical version=xxx>
4390 // <historical version=xxx>
4391 // ...
4392 // </table>
4393 // <table name=xxx version=xxx>
4394 // ...
4395 // </table>
4396 void ConfigurationGUISupervisor::handleGetTableGroupXML(HttpXmlDocument& xmlOut,
4397  ConfigurationManagerRW* cfgMgr,
4398  const std::string& groupName,
4399  TableGroupKey groupKey,
4400  bool ignoreWarnings) try
4401 {
4402  char tmpIntStr[100];
4403  DOMElement *parentEl, *configEl;
4404 
4405  // steps:
4406  // if invalid key, get latest key
4407  // get specific group with key
4408  // give member names and versions
4409  // get all table groups to locate historical keys
4410  // get all groups to find historical keys
4411 
4412  // std::set<std::string /*name+version*/> allGroups =
4413  // cfgMgr->getConfigurationInterface()->getAllTableGroupNames(groupName);
4414  // std::string name;
4415  // TableGroupKey key;
4416  // //put them in a set to sort them as TableGroupKey defines for operator<
4417  // std::set<TableGroupKey> sortedKeys;
4418  // for(auto& group: allGroups)
4419  // {
4420  // //now uses database filter
4421  // TableGroupKey::getGroupNameAndKey(group,name,key);
4422  // //if(name == groupName)
4423  // sortedKeys.emplace(key);
4424  // }
4425 
4426  const GroupInfo& groupInfo = cfgMgr->getGroupInfo(groupName);
4427  const std::set<TableGroupKey>& sortedKeys = groupInfo.keys_; // rename
4428 
4429  if(groupKey.isInvalid() || // if invalid or not found, get latest
4430  sortedKeys.find(groupKey) == sortedKeys.end())
4431  {
4432  if(sortedKeys.size())
4433  groupKey = *sortedKeys.rbegin();
4434  __SUP_COUT__ << "Group key requested was invalid or not found, going with latest "
4435  << groupKey << __E__;
4436  }
4437 
4438  xmlOut.addTextElementToData("TableGroupName", groupName);
4439  xmlOut.addTextElementToData("TableGroupKey", groupKey.toString());
4440 
4441  // add all other sorted keys for this groupName
4442  for(auto& keyInOrder : sortedKeys)
4443  xmlOut.addTextElementToData("HistoricalTableGroupKey", keyInOrder.toString());
4444 
4445  parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
4446 
4447  // get specific group with key
4448  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
4449  std::map<std::string /*name*/, std::string /*alias*/> groupMemberAliases;
4450 
4451  __SUP_COUT__ << "groupName=" << groupName << __E__;
4452  __SUP_COUT__ << "groupKey=" << groupKey << __E__;
4453 
4454  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
4455  std::map<std::string, TableInfo>::const_iterator it;
4456 
4457  // load group so comments can be had
4458  // and also group metadata (author, comment, createTime)
4459  try
4460  {
4461  std::string groupAuthor, groupComment, groupCreationTime, groupTypeString;
4462  std::string accumulateTreeErrors;
4463 
4464  __SUP_COUTV__(ignoreWarnings);
4465  cfgMgr->loadTableGroup(groupName,
4466  groupKey,
4467  false /*doActivate*/,
4468  &memberMap,
4469  0 /*progressBar*/,
4470  ignoreWarnings ? 0 : /*accumulateTreeErrors*/
4471  &accumulateTreeErrors,
4472  &groupComment,
4473  &groupAuthor,
4474  &groupCreationTime,
4475  false /*doNotLoadMember*/,
4476  &groupTypeString,
4477  &groupMemberAliases);
4478 
4479  if(accumulateTreeErrors != "")
4480  {
4481  __SUP_COUTV__(accumulateTreeErrors);
4482  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrors);
4483  }
4484 
4485  xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
4486  xmlOut.addTextElementToData("TableGroupComment", groupComment);
4487  xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime);
4488  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
4489  }
4490  catch(const std::runtime_error& e)
4491  {
4492  __SUP_SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" +
4493  "\" members can not be loaded!\n\n" + e.what()
4494  << __E__;
4495  __SUP_COUT_ERR__ << ss.str();
4496  xmlOut.addTextElementToData("Error", ss.str());
4497  // return;
4498  }
4499  catch(...)
4500  {
4501  __SUP_SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" +
4502  "\" members can not be loaded!"
4503  << __E__;
4504  __SUP_COUT_ERR__ << ss.str();
4505  xmlOut.addTextElementToData("Error", ss.str());
4506  // return;
4507  }
4508 
4509  __COUTV__(StringMacros::mapToString(groupMemberAliases));
4510 
4511  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
4512  cfgMgr->getVersionAliases();
4513 
4514  __SUP_COUT__ << "# of table version aliases: " << versionAliases.size() << __E__;
4515 
4516  // Seperate loop to get name and version
4517  for(auto& memberPair : memberMap)
4518  {
4519  xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
4520 
4521  // if member is in groupMemberAliases, then alias version
4522  if(groupMemberAliases.find(memberPair.first) != groupMemberAliases.end())
4523  configEl = xmlOut.addTextElementToParent(
4524  "MemberVersion",
4525  ConfigurationManager::ALIAS_VERSION_PREAMBLE +
4526  groupMemberAliases[memberPair.first], // return the ALIAS:<alias>
4527  parentEl);
4528  else
4529  configEl = xmlOut.addTextElementToParent(
4530  "MemberVersion", memberPair.second.toString(), parentEl);
4531 
4532  it = allTableInfo.find(memberPair.first);
4533  if(it == allTableInfo.end())
4534  {
4535  xmlOut.addTextElementToData(
4536  "Error", "Table \"" + memberPair.first + "\" can not be retrieved!");
4537  continue;
4538  }
4539 
4540  if(versionAliases.find(it->first) != versionAliases.end())
4541  for(auto& aliasVersion : versionAliases[it->first])
4542  xmlOut.addTextElementToParent(
4543  "TableExistingVersion",
4544  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
4545  configEl);
4546 
4547  for(auto& version : it->second.versions_)
4548  // if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016
4549  // (might as well show all versions in list to avoid user confusion) else
4550  xmlOut.addTextElementToParent(
4551  "TableExistingVersion", version.toString(), configEl);
4552  }
4553  // Seperate loop just for getting the Member Comment
4554  for(auto& memberPair : memberMap)
4555  {
4556  //__SUP_COUT__ << "\tMember config " << memberPair.first << ":" <<
4557  // memberPair.second << __E__;
4558 
4559  // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
4560  // if(commentsLoaded)
4561  xmlOut.addTextElementToParent(
4562  "MemberComment",
4563  allTableInfo.at(memberPair.first).tablePtr_->getView().getComment(),
4564  parentEl);
4565  // else
4566  // xmlOut.addTextElementToParent("MemberComment", "", parentEl);
4567 
4568  // __SUP_COUT__ << "\tMember config " << memberPair.first << ":" <<
4569  // memberPair.second << __E__;
4570 
4571  // configEl = xmlOut.addTextElementToParent("MemberVersion",
4572  // memberPair.second.toString(), parentEl);
4573 
4574  /* it = allTableInfo.find(memberPair.first);
4575  if(it == allTableInfo.end())
4576  {
4577  xmlOut.addTextElementToData("Error","Table \"" +
4578  memberPair.first +
4579  "\" can not be retrieved!");
4580  return;
4581  }
4582  */
4583  // include aliases for this table
4584  /*if(versionAliases.find(it->first) != versionAliases.end())
4585  for (auto& aliasVersion:versionAliases[it->first])
4586  xmlOut.addTextElementToParent("TableExistingVersion",
4587  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
4588  configEl);
4589 
4590  for (auto& version:it->second.versions_)
4591  //if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016
4592  (might as well show all versions in list to avoid user confusion)
4593  //else
4594  xmlOut.addTextElementToParent("TableExistingVersion",
4595  version.toString(), configEl);
4596  */
4597  }
4598 
4599 } // end handleGetTableGroupXML()
4600 catch(std::runtime_error& e)
4601 {
4602  __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
4603  __SUP_COUT_ERR__ << "\n" << ss.str();
4604  xmlOut.addTextElementToData("Error", ss.str());
4605 }
4606 catch(...)
4607 {
4608  __SUP_SS__ << ("Error!\n\n") << __E__;
4609  __SUP_COUT_ERR__ << "\n" << ss.str();
4610  xmlOut.addTextElementToData("Error", ss.str());
4611 } // end handleGetTableGroupXML() catch
4612 
4613 //========================================================================================================================
4614 // handleGetTableXML
4615 //
4616 // if INVALID or version does not exists, default to mock-up
4617 //
4618 // give the detail of specific table specified
4619 // by tableName and version
4620 //
4621 // if no version selected, default to latest version
4622 // if no versions exists, default to mock-up
4623 //
4624 // return existing versions
4625 // return column headers
4626 // return number of rows
4627 // from dataOffset
4628 // first CHUNK_SIZE rows
4629 //
4630 // return this information
4631 //<table name=xxx version=xxx rowCount=xxx chunkReq=xxx chunkSz=xxx>
4632 // <existing version=xxx>
4633 // <existing version=xxx>
4634 // ....
4635 // <colhdr name=xxx>
4636 // <colhdr name=xxx>
4637 // ....
4638 // <rowdata>
4639 // <cell value=xxx>
4640 // <cell value=xxx>
4641 // ....
4642 // </rowdata>
4643 // <rowdata>
4644 // ....
4645 // </rowdata>
4646 // ....
4647 //</table>
4648 //
4649 //
4650 // Note: options.. if allowIllegalColumns then attempts to load data to current mockup
4651 // column names
4652 // if not allowIllegalColumns, then it is still ok if the source has more or less
4653 // columns: the client is notified through "TableWarnings" field in this case.
4654 void ConfigurationGUISupervisor::handleGetTableXML(HttpXmlDocument& xmlOut,
4655  ConfigurationManagerRW* cfgMgr,
4656  const std::string& tableName,
4657  TableVersion version,
4658  bool allowIllegalColumns) try
4659 {
4660  char tmpIntStr[100];
4661  DOMElement *parentEl, *subparentEl;
4662 
4663  std::string accumulatedErrors = "";
4664 
4665  const std::map<std::string, TableInfo>&
4666  allTableInfo = // if allowIllegalColumns, then also refresh
4667  cfgMgr->getAllTableInfo(allowIllegalColumns,
4668  allowIllegalColumns ? &accumulatedErrors : 0,
4669  tableName); // filter errors by tableName
4670 
4671  TableBase* table = cfgMgr->getTableByName(tableName);
4672 
4673  // send all table names along with
4674  // and check for specific version
4675  xmlOut.addTextElementToData("ExistingTableNames",
4676  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
4677  for(auto& configPair : allTableInfo)
4678  {
4679  xmlOut.addTextElementToData("ExistingTableNames", configPair.first);
4680  if(configPair.first == tableName && // check that version exists
4681  configPair.second.versions_.find(version) == configPair.second.versions_.end())
4682  {
4683  __SUP_COUT__ << "Version not found, so using mockup." << __E__;
4684  version = TableVersion(); // use INVALID
4685  }
4686  }
4687 
4688  xmlOut.addTextElementToData("TableName", tableName); // table name
4689  xmlOut.addTextElementToData("TableDescription",
4690  table->getTableDescription()); // table name
4691 
4692  // existing table versions
4693  {
4694  // get version aliases for translation
4695  std::map<
4696  std::string /*table name*/,
4697  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
4698  versionAliases;
4699  try
4700  {
4701  // use whatever backbone is currently active
4702  versionAliases = cfgMgr->getVersionAliases();
4703  for(const auto& aliases : versionAliases)
4704  for(const auto& alias : aliases.second)
4705  __SUP_COUT__ << "ALIAS: " << aliases.first << " " << alias.first
4706  << " ==> " << alias.second << __E__;
4707  }
4708  catch(const std::runtime_error& e)
4709  {
4710  __SUP_COUT__ << "Could not get backbone information for version aliases: "
4711  << e.what() << __E__;
4712  }
4713 
4714  auto tableIterator = versionAliases.find(tableName);
4715 
4716  parentEl = xmlOut.addTextElementToData("TableVersions", "");
4717  for(const TableVersion& v : allTableInfo.at(tableName).versions_)
4718  {
4719  subparentEl =
4720  xmlOut.addTextElementToParent("Version", v.toString(), parentEl);
4721 
4722  if(tableIterator != versionAliases.end())
4723  {
4724  // check if this version has one or many aliases
4725  for(const auto& aliasPair : tableIterator->second)
4726  {
4727  // __SUP_COUT__ << "Checking " << aliasPair.second <<
4728  //"
4729  //-->
4730  //"
4731  //<< aliasPair.first << " for " << v <<
4732  //__E__;
4733  if(v == aliasPair.second)
4734  {
4735  __SUP_COUT__ << "Found Alias " << aliasPair.second << " --> "
4736  << aliasPair.first << __E__;
4737  xmlOut.addTextElementToParent(
4738  "VersionAlias", aliasPair.first, subparentEl);
4739  }
4740  }
4741  }
4742  }
4743  }
4744 
4745  // table columns and then rows (from table view)
4746 
4747  // get view pointer
4748  TableView* cfgViewPtr;
4749  if(version.isInvalid()) // use mock-up
4750  {
4751  cfgViewPtr = table->getMockupViewP();
4752  }
4753  else // use view version
4754  {
4755  try
4756  {
4757  cfgViewPtr = cfgMgr->getVersionedTableByName(tableName, version)->getViewP();
4758  }
4759  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
4760  {
4761  __SUP_SS__ << "Failed to get table " << tableName << " version " << version
4762  << "... defaulting to mock-up! " << __E__;
4763  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
4764 
4765  __SUP_COUT_ERR__ << "\n" << ss.str();
4766  version = TableVersion();
4767  cfgViewPtr = table->getMockupViewP();
4768 
4769  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
4770  }
4771  catch(...) // default to mock-up for fail-safe in GUI editor
4772  {
4773  __SUP_SS__ << "Failed to get table " << tableName << " version: " << version
4774  << "... defaulting to mock-up! "
4775  << "(You may want to try again to see what was partially loaded "
4776  "into cache before failure. "
4777  << "If you think, the failure is due to a column name change, "
4778  << "you can also try to Copy the failing view to the new column "
4779  "names using "
4780  << "'Copy and Move' functionality.)" << __E__;
4781 
4782  __SUP_COUT_ERR__ << "\n" << ss.str();
4783  version = TableVersion();
4784  cfgViewPtr = table->getMockupViewP();
4785 
4786  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
4787  }
4788  }
4789  xmlOut.addTextElementToData("TableVersion", version.toString()); // table version
4790 
4791  // get 'columns' of view
4792  DOMElement* choicesParentEl;
4793  parentEl = xmlOut.addTextElementToData("CurrentVersionColumnHeaders", "");
4794 
4795  std::vector<TableViewColumnInfo> colInfo = cfgViewPtr->getColumnsInfo();
4796 
4797  for(int i = 0; i < (int)colInfo.size(); ++i) // column headers and types
4798  {
4799  // __SUP_COUT__ << "\t\tCol " << i << ": " << colInfo[i].getType() << "() "
4800  //<< colInfo[i].getName() << " "
4801  // << colInfo[i].getStorageName() << " " << colInfo[i].getDataType()
4802  //<<
4803  //__E__;
4804 
4805  xmlOut.addTextElementToParent("ColumnHeader", colInfo[i].getName(), parentEl);
4806  xmlOut.addTextElementToParent("ColumnType", colInfo[i].getType(), parentEl);
4807  xmlOut.addTextElementToParent(
4808  "ColumnDataType", colInfo[i].getDataType(), parentEl);
4809 
4810  choicesParentEl = xmlOut.addTextElementToParent("ColumnChoices", "", parentEl);
4811  // add data choices if necessary
4812  if(colInfo[i].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
4813  colInfo[i].getType() == TableViewColumnInfo::TYPE_BITMAP_DATA ||
4814  colInfo[i].isChildLink())
4815  {
4816  for(auto& choice : colInfo[i].getDataChoices())
4817  xmlOut.addTextElementToParent("ColumnChoice", choice, choicesParentEl);
4818  }
4819  }
4820 
4821  // verify mockup columns after columns are posted to xmlOut
4822  try
4823  {
4824  if(version.isInvalid())
4825  cfgViewPtr->init();
4826  }
4827  catch(std::runtime_error& e)
4828  {
4829  // append accumulated errors, because they may be most useful
4830  __THROW__(e.what() + std::string("\n\n") + accumulatedErrors);
4831  }
4832  catch(...)
4833  {
4834  throw;
4835  }
4836 
4837  parentEl = xmlOut.addTextElementToData("CurrentVersionRows", "");
4838 
4839  for(int r = 0; r < (int)cfgViewPtr->getNumberOfRows(); ++r)
4840  {
4841  //__SUP_COUT__ << "\t\tRow " << r << ": " << __E__;
4842 
4843  sprintf(tmpIntStr, "%d", r);
4844  DOMElement* tmpParentEl =
4845  xmlOut.addTextElementToParent("Row", tmpIntStr, parentEl);
4846 
4847  for(int c = 0; c < (int)cfgViewPtr->getNumberOfColumns(); ++c)
4848  {
4849  if(colInfo[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
4850  {
4851  std::string timeAsString;
4852  cfgViewPtr->getValue(timeAsString, r, c);
4853  xmlOut.addTextElementToParent("Entry", timeAsString, tmpParentEl);
4854  }
4855  else
4856  xmlOut.addTextElementToParent(
4857  "Entry", cfgViewPtr->getDataView()[r][c], tmpParentEl);
4858  }
4859  }
4860 
4861  // add "other" fields associated with configView
4862  xmlOut.addTextElementToData("TableComment", cfgViewPtr->getComment());
4863  xmlOut.addTextElementToData("TableAuthor", cfgViewPtr->getAuthor());
4864  xmlOut.addTextElementToData("TableCreationTime",
4865  std::to_string(cfgViewPtr->getCreationTime()));
4866  xmlOut.addTextElementToData("TableLastAccessTime",
4867  std::to_string(cfgViewPtr->getLastAccessTime()));
4868 
4869  // add to xml the default row values
4870  std::vector<std::string> defaultRowValues = cfgViewPtr->getDefaultRowValues();
4871  // don't give author and time.. force default author, let JS fill time
4872  for(unsigned int c = 0; c < defaultRowValues.size() - 2; ++c)
4873  {
4874  // __SUP_COUT__ << "Default for c" << c << "=" <<
4875  // cfgViewPtr->getColumnInfo(c).getName() << " is " <<
4876  // defaultRowValues[c] << __E__;
4877  xmlOut.addTextElementToData("DefaultRowValue", defaultRowValues[c]);
4878  }
4879 
4880  if(accumulatedErrors != "") // add accumulated errors to xmlOut
4881  {
4882  __SUP_SS__ << (std::string("Column errors were allowed for this request, so "
4883  "maybe you can ignore this, ") +
4884  "but please note the following errors:\n" + accumulatedErrors)
4885  << __E__;
4886  __SUP_COUT_ERR__ << ss.str();
4887  xmlOut.addTextElementToData("Error", ss.str());
4888  }
4889  else if(!version.isTemporaryVersion() && // not temporary (these are not filled from
4890  // interface source)
4891  (cfgViewPtr->getDataColumnSize() != cfgViewPtr->getNumberOfColumns() ||
4892  cfgViewPtr->getSourceColumnMismatch() !=
4893  0)) // check for column size mismatch
4894  {
4895  __SUP_SS__ << "\n\nThere were warnings found when loading the table " << tableName
4896  << ":v" << version << ". Please see the details below:\n\n"
4897  << "The source column size was found to be "
4898  << cfgViewPtr->getDataColumnSize()
4899  << ", and the current number of columns for this table is "
4900  << cfgViewPtr->getNumberOfColumns() << ". This resulted in a count of "
4901  << cfgViewPtr->getSourceColumnMismatch()
4902  << " source column mismatches, and a count of "
4903  << cfgViewPtr->getSourceColumnMissing() << " table entries missing in "
4904  << cfgViewPtr->getNumberOfRows() << " row(s) of data." << __E__;
4905 
4906  const std::set<std::string> srcColNames = cfgViewPtr->getSourceColumnNames();
4907  ss << "\n\nSource column names in ALPHABETICAL order were as follows:\n";
4908  char index = 'a';
4909  std::string preIndexStr = "";
4910  for(auto& srcColName : srcColNames)
4911  {
4912  ss << "\n\t" << preIndexStr << index << ". " << srcColName;
4913  if(index == 'z') // wrap-around
4914  {
4915  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
4916  index = 'a';
4917  }
4918  else
4919  ++index;
4920  }
4921  ss << __E__;
4922 
4923  std::set<std::string> destColNames = cfgViewPtr->getColumnStorageNames();
4924  ss << "\n\nCurrent table column names in ALPHABETICAL order are as follows:\n";
4925  index = 'a';
4926  preIndexStr = "";
4927  for(auto& destColName : destColNames)
4928  {
4929  ss << "\n\t" << preIndexStr << index << ". " << destColName;
4930  if(index == 'z') // wrap-around
4931  {
4932  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
4933  index = 'a';
4934  }
4935  else
4936  ++index;
4937  }
4938  ss << __E__;
4939 
4940  __SUP_COUT__ << "\n" << ss.str();
4941  xmlOut.addTextElementToData("TableWarnings", ss.str());
4942  }
4943 } // end handleGetTableXML()
4944 catch(std::runtime_error& e)
4945 {
4946  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
4947  xmlOut.addTextElementToData("Error", "Error getting view! " + std::string(e.what()));
4948 }
4949 catch(...)
4950 {
4951  __SUP_COUT__ << "Error detected!\n\n " << __E__;
4952  xmlOut.addTextElementToData("Error", "Error getting view! ");
4953 }
4954 
4955 //========================================================================================================================
4956 // saveModifiedVersionXML
4957 //
4958 // once source version has been modified in temporary version
4959 // this function finishes it off.
4960 TableVersion ConfigurationGUISupervisor::saveModifiedVersionXML(
4961  HttpXmlDocument& xmlOut,
4962  ConfigurationManagerRW* cfgMgr,
4963  const std::string& tableName,
4964  TableVersion originalVersion,
4965  bool makeTemporary,
4966  TableBase* config,
4967  TableVersion temporaryModifiedVersion,
4968  bool ignoreDuplicates,
4969  bool lookForEquivalent)
4970 {
4971  bool needToEraseTemporarySource =
4972  (originalVersion.isTemporaryVersion() && !makeTemporary);
4973 
4974  // check for duplicate tables already in cache
4975  if(!ignoreDuplicates)
4976  {
4977  __SUP_COUT__ << "Checking for duplicate tables..." << __E__;
4978 
4979  TableVersion duplicateVersion;
4980 
4981  {
4982  //"DEEP" checking
4983  // load into cache 'recent' versions for this table
4984  // 'recent' := those already in cache, plus highest version numbers not
4985  // in cache
4986  const std::map<std::string, TableInfo>& allTableInfo =
4987  cfgMgr->getAllTableInfo(); // do not refresh
4988 
4989  auto versionReverseIterator =
4990  allTableInfo.at(tableName).versions_.rbegin(); // get reverse iterator
4991  __SUP_COUT__ << "Filling up cached from " << config->getNumberOfStoredViews()
4992  << " to max count of " << config->MAX_VIEWS_IN_CACHE << __E__;
4993  for(; config->getNumberOfStoredViews() < config->MAX_VIEWS_IN_CACHE &&
4994  versionReverseIterator != allTableInfo.at(tableName).versions_.rend();
4995  ++versionReverseIterator)
4996  {
4997  __SUP_COUT__ << "Versions in reverse order " << *versionReverseIterator
4998  << __E__;
4999  try
5000  {
5001  cfgMgr->getVersionedTableByName(
5002  tableName, *versionReverseIterator); // load to cache
5003  }
5004  catch(const std::runtime_error& e)
5005  {
5006  __SUP_COUT__
5007  << "Error loadiing historical version, but ignoring: " << e.what()
5008  << __E__;
5009  }
5010  }
5011  }
5012 
5013  __SUP_COUT__ << "Checking duplicate..." << __E__;
5014 
5015  duplicateVersion = config->checkForDuplicate(
5016  temporaryModifiedVersion,
5017  (!originalVersion.isTemporaryVersion() && !makeTemporary)
5018  ? TableVersion()
5019  : // if from persistent to persistent, then include original version in
5020  // search
5021  originalVersion);
5022 
5023  if(lookForEquivalent && !duplicateVersion.isInvalid())
5024  {
5025  // found an equivalent!
5026  __SUP_COUT__ << "Equivalent table found in version v" << duplicateVersion
5027  << __E__;
5028 
5029  // if duplicate version was temporary, do not use
5030  if(duplicateVersion.isTemporaryVersion() && !makeTemporary)
5031  {
5032  __SUP_COUT__ << "Need persistent. Duplicate version was temporary. "
5033  "Abandoning duplicate."
5034  << __E__;
5035  duplicateVersion = TableVersion(); // set invalid
5036  }
5037  else
5038  {
5039  // erase and return equivalent version
5040 
5041  // erase modified equivalent version
5042  cfgMgr->eraseTemporaryVersion(tableName, temporaryModifiedVersion);
5043 
5044  // erase original if needed
5045  if(needToEraseTemporarySource)
5046  cfgMgr->eraseTemporaryVersion(tableName, originalVersion);
5047 
5048  xmlOut.addTextElementToData("savedName", tableName);
5049  xmlOut.addTextElementToData("savedVersion", duplicateVersion.toString());
5050  xmlOut.addTextElementToData("foundEquivalentVersion", "1");
5051 
5052  __SUP_COUT__ << "\t\t equivalent AssignedVersion: " << duplicateVersion
5053  << __E__;
5054 
5055  return duplicateVersion;
5056  }
5057  }
5058 
5059  if(!duplicateVersion.isInvalid())
5060  {
5061  __SUP_SS__ << "This version of table '" << tableName
5062  << "' is identical to another version currently cached v"
5063  << duplicateVersion << ". No reason to save a duplicate." << __E__;
5064  __SUP_COUT_ERR__ << "\n" << ss.str();
5065 
5066  // delete temporaryModifiedVersion
5067  config->eraseView(temporaryModifiedVersion);
5068  __SS_THROW__;
5069  }
5070 
5071  __SUP_COUT__ << "Check for duplicate tables complete." << __E__;
5072  }
5073 
5074  if(makeTemporary)
5075  __SUP_COUT__ << "\t\t**************************** Save as temporary table version"
5076  << __E__;
5077  else
5078  __SUP_COUT__ << "\t\t**************************** Save as new table version"
5079  << __E__;
5080 
5081  TableVersion newAssignedVersion =
5082  cfgMgr->saveNewTable(tableName, temporaryModifiedVersion, makeTemporary);
5083 
5084  if(needToEraseTemporarySource)
5085  cfgMgr->eraseTemporaryVersion(tableName, originalVersion);
5086 
5087  xmlOut.addTextElementToData("savedName", tableName);
5088  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
5089 
5090  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
5091  return newAssignedVersion;
5092 }
5093 
5094 //========================================================================================================================
5095 // handleCreateTableXML
5096 //
5097 // Save the detail of specific table specified
5098 // by tableName and version
5099 // ...starting from dataOffset
5100 //
5101 // Note: if starting version is -1 start from mock-up
5102 void ConfigurationGUISupervisor::handleCreateTableXML(HttpXmlDocument& xmlOut,
5103  ConfigurationManagerRW* cfgMgr,
5104  const std::string& tableName,
5105  TableVersion version,
5106  bool makeTemporary,
5107  const std::string& data,
5108  const int& dataOffset,
5109  const std::string& author,
5110  const std::string& comment,
5111  bool sourceTableAsIs,
5112  bool lookForEquivalent) try
5113 {
5114  //__SUP_COUT__ << "handleCreateTableXML: " << tableName << " version: " <<
5115  // version
5116  // << " dataOffset: " << dataOffset << __E__;
5117 
5118  //__SUP_COUT__ << "data: " << data << __E__;
5119 
5120  // create temporary version from starting version
5121  if(!version.isInvalid()) // if not using mock-up, make sure starting version is
5122  // loaded
5123  {
5124  try
5125  {
5126  cfgMgr->getVersionedTableByName(tableName, version);
5127  }
5128  catch(...)
5129  {
5130  // force to mockup
5131  version = TableVersion();
5132  }
5133  }
5134 
5135  TableBase* config = cfgMgr->getTableByName(tableName);
5136 
5137  // check that the source version has the right number of columns
5138  // if there is a mismatch, start from mockup
5139  if(!version.isInvalid()) // if not using mock-up, then the starting version is the
5140  // active one
5141  {
5142  // compare active to mockup column counts
5143  if(config->getViewP()->getDataColumnSize() !=
5144  config->getMockupViewP()->getNumberOfColumns() ||
5145  config->getViewP()->getSourceColumnMismatch() != 0)
5146  {
5147  __SUP_COUT__ << "config->getViewP()->getNumberOfColumns() "
5148  << config->getViewP()->getNumberOfColumns() << __E__;
5149  __SUP_COUT__ << "config->getMockupViewP()->getNumberOfColumns() "
5150  << config->getMockupViewP()->getNumberOfColumns() << __E__;
5151  __SUP_COUT__ << "config->getViewP()->getSourceColumnMismatch() "
5152  << config->getViewP()->getSourceColumnMismatch() << __E__;
5153  __SUP_COUT_INFO__
5154  << "Source view v" << version
5155  << " has a mismatch in the number of columns, so using mockup as source."
5156  << __E__;
5157  version = TableVersion(); // invalid = mockup
5158  }
5159  }
5160 
5161  // create a temporary version from the source version
5162  TableVersion temporaryVersion = config->createTemporaryView(version);
5163 
5164  __SUP_COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__;
5165 
5166  TableView* cfgView = config->getTemporaryView(temporaryVersion);
5167 
5168  int retVal;
5169  try
5170  {
5171  // returns -1 on error that data was unchanged
5172  retVal = sourceTableAsIs ? 0 : cfgView->fillFromCSV(data, dataOffset, author);
5173 
5174  if(retVal == 1) // data was same but columns are different!
5175  {
5176  __SUP_COUT__ << "Data was the same, but columns have changed!" << __E__;
5177  __SUP_COUTV__(sourceTableAsIs);
5178  lookForEquivalent = false; // clear
5179  }
5180 
5181  cfgView->setURIEncodedComment(comment);
5182  __SUP_COUT__ << "Table comment was set to:\n\t" << cfgView->getComment() << __E__;
5183  }
5184  catch(...) // erase temporary view before re-throwing error
5185  {
5186  __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
5187  config->eraseView(temporaryVersion);
5188  throw;
5189  }
5190 
5191  // Note: be careful with any further table operations at this point..
5192  // must catch errors and erase temporary version on failure.
5193 
5194  // only consider it an error if source version was persistent version
5195  // allow it if source version is temporary and we are making a persistent version now
5196  // also, allow it if version tracking is off.
5197  if(retVal < 0 && (!version.isTemporaryVersion() || makeTemporary) &&
5198  ConfigurationInterface::isVersionTrackingEnabled())
5199  {
5200  if(!version.isInvalid() && // if source version was mockup, then consider it
5201  // attempt to create a blank table
5202  !version.isScratchVersion()) // if source version was scratch, then consider
5203  // it attempt to make it persistent
5204  {
5205  __SUP_SS__
5206  << "No rows were modified! No reason to fill a view with same content."
5207  << __E__;
5208  __SUP_COUT_ERR__ << "\n" << ss.str();
5209  // delete temporaryVersion
5210  config->eraseView(temporaryVersion);
5211  __SS_THROW__;
5212  }
5213  else if(version.isInvalid())
5214  __SUP_COUT__ << "This was interpreted as an attempt to create a blank table."
5215  << __E__;
5216  else if(version.isScratchVersion())
5217  __SUP_COUT__ << "This was interpreted as an attempt to make a persistent "
5218  "version of the scratch table."
5219  << __E__;
5220  else
5221  {
5222  __SUP_SS__;
5223  __THROW__(ss.str() + "impossible!");
5224  }
5225  }
5226  else if(retVal < 0 && (version.isTemporaryVersion() && !makeTemporary))
5227  {
5228  __SUP_COUT__ << "Allowing the static data because this is converting from "
5229  "temporary to persistent version."
5230  << __E__;
5231  }
5232  else if(retVal < 0 && !ConfigurationInterface::isVersionTrackingEnabled())
5233  {
5234  __SUP_COUT__ << "Allowing the static data because version tracking is OFF."
5235  << __E__;
5236  }
5237  else if(retVal < 0)
5238  {
5239  __SUP_SS__ << "This should not be possible! Fatal error." << __E__;
5240  // delete temporaryVersion
5241  config->eraseView(temporaryVersion);
5242  __SS_THROW__;
5243  }
5244 
5245  // note: if sourceTableAsIs, accept equivalent versions
5246  saveModifiedVersionXML(xmlOut,
5247  cfgMgr,
5248  tableName,
5249  version,
5250  makeTemporary,
5251  config,
5252  temporaryVersion,
5253  false /*ignoreDuplicates*/,
5254  lookForEquivalent || sourceTableAsIs /*lookForEquivalent*/);
5255 } // end handleCreateTableXML()
5256 catch(std::runtime_error& e)
5257 {
5258  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
5259  xmlOut.addTextElementToData("Error",
5260  "Error saving new view!\n " + std::string(e.what()));
5261 }
5262 catch(...)
5263 {
5264  __SUP_COUT__ << "Error detected!\n\n " << __E__;
5265  xmlOut.addTextElementToData("Error", "Error saving new view! ");
5266 }
5267 
5268 //========================================================================================================================
5269 // refreshUserSession
5270 // Finds/creates the active user session based on username& actionSessionIndex
5271 //
5272 // Returns a configurationMangager instance dedictated to the user.
5273 // This configurationManager will have at least empty instances of all base
5274 // configurations (no null pointers) and will load the backbone configurations to
5275 // specified backboneVersion
5276 //
5277 // If backboneVersion is -1, then latest, and backboneVersion passed by reference
5278 // will be updated
5279 ConfigurationManagerRW* ConfigurationGUISupervisor::refreshUserSession(
5280  std::string username, uint64_t activeSessionIndex, bool refresh)
5281 //, TableVersion& backboneVersion)
5282 {
5283  activeSessionIndex =
5284  0; // make session by username for now! (may never want to change back)
5285 
5286  std::stringstream ssMapKey;
5287  ssMapKey << username << ":" << activeSessionIndex;
5288  std::string mapKey = ssMapKey.str();
5289  __SUP_COUT__ << "Config Session: " << mapKey
5290  << " ... out of size: " << userConfigurationManagers_.size() << __E__;
5291 
5292  time_t now = time(0);
5293 
5294  // create new config mgr if not one for active session index
5295  if(userConfigurationManagers_.find(mapKey) == userConfigurationManagers_.end())
5296  {
5297  __SUP_COUT_INFO__ << "Creating new Configuration Manager." << __E__;
5298  userConfigurationManagers_[mapKey] = new ConfigurationManagerRW(username);
5299 
5300  // update table info for each new configuration manager
5301  // IMPORTANTLY this also fills all configuration manager pointers with instances,
5302  // so we are not dealing with changing pointers later on
5303  userConfigurationManagers_[mapKey]->getAllTableInfo(
5304  true); // load empty instance of everything important
5305  }
5306  else if(userLastUseTime_.find(mapKey) == userLastUseTime_.end())
5307  {
5308  __SUP_SS__ << "Fatal error managing userLastUseTime_!" << __E__;
5309  __SUP_COUT_ERR__ << "\n" << ss.str();
5310  __SS_THROW__;
5311  }
5312  else if(refresh || (now - userLastUseTime_[mapKey]) >
5313  CONFIGURATION_MANAGER_REFRESH_THRESHOLD) // check if should
5314  // refresh all config
5315  // info
5316  {
5317  __SUP_COUT_INFO__ << "Refreshing all table info." << __E__;
5318  userConfigurationManagers_[mapKey]->getAllTableInfo(true);
5319  }
5320 
5321  // load backbone configurations always based on backboneVersion
5322  // if backboneVersion is -1, then latest
5323  // backboneVersion = 0;//
5324  // userConfigurationManagers_[mapKey]->loadConfigurationBackbone(backboneVersion);
5325 
5326  // update active sessionIndex last use time
5327  userLastUseTime_[mapKey] = now;
5328 
5329  // check for stale sessions and remove them (so config user maps do not grow forever)
5330  for(std::map<std::string, time_t>::iterator it = userLastUseTime_.begin();
5331  it != userLastUseTime_.end();
5332  ++it)
5333  if(now - it->second > CONFIGURATION_MANAGER_EXPIRATION_TIME) // expired!
5334  {
5335  __SUP_COUT__ << now << ":" << it->second << " = " << now - it->second
5336  << __E__;
5337  delete userConfigurationManagers_[it->first]; // call destructor
5338  if(!(userConfigurationManagers_.erase(it->first))) // erase by key
5339  {
5340  __SUP_SS__ << "Fatal error erasing configuration manager by key!"
5341  << __E__;
5342  __SUP_COUT_ERR__ << "\n" << ss.str();
5343  __SS_THROW__;
5344  }
5345  userLastUseTime_.erase(it); // erase by iterator
5346 
5347  it = userLastUseTime_.begin(); // fail safe.. reset it, to avoid trying to
5348  // understand what happens with the next
5349  // iterator
5350  }
5351 
5352  return userConfigurationManagers_[mapKey];
5353 }
5354 
5355 //========================================================================================================================
5356 // handleCreateTableGroupXML
5357 //
5358 // Save a new TableGroup:
5359 // Search for existing TableGroupKeys for this TableGroup
5360 // Append a "bumped" system key to name
5361 // Save based on list of tableName/TableVersion
5362 //
5363 // tableList parameter is comma separated table name and version
5364 //
5365 // Note: if version of -1 (INVALID/MOCKUP) is given and there are no other existing
5366 // table versions... a new table version is generated using the mockup table.
5367 //
5368 // Table Version Alias Handling:
5369 // Allow table versions to be specified as an alias with ALIAS: preamble. Aliased
5370 // versions will be translated according to the active backbone at activation
5371 // time.
5372 //
5373 //
5374 void ConfigurationGUISupervisor::handleCreateTableGroupXML(
5375  HttpXmlDocument& xmlOut,
5376  ConfigurationManagerRW* cfgMgr,
5377  const std::string& groupName,
5378  const std::string& tableList,
5379  bool allowDuplicates,
5380  bool ignoreWarnings,
5381  const std::string& groupComment,
5382  bool lookForEquivalent) try
5383 {
5384  __SUP_COUT__ << "handleCreateTableGroupXML \n";
5385 
5386  xmlOut.addTextElementToData("AttemptedNewGroupName", groupName);
5387 
5388  // make sure not using partial tables or anything weird when creating the group
5389  // so start from scratch and load backbone
5390  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(true);
5391  cfgMgr->loadConfigurationBackbone();
5392 
5393  std::map<std::string /*tableName*/,
5394  std::map<std::string /*aliasName*/, TableVersion /*version*/>>
5395  versionAliases = cfgMgr->getVersionAliases();
5396  // for(const auto& aliases : versionAliases)
5397  // for(const auto& alias : aliases.second)
5398  // __SUP_COUT__ << aliases.first << " " << alias.first << " " << alias.second
5399  // << __E__;
5400 
5401  std::map<std::string /*name*/, TableVersion /*version*/> groupMembers;
5402  std::map<std::string /*name*/, std::string /*alias*/> groupAliases;
5403 
5404  std::string name, versionStr, alias;
5405  TableVersion version;
5406  auto c = tableList.find(',', 0);
5407  auto i = c;
5408  i = 0; // auto used to get proper index/length type
5409  while(c < tableList.length())
5410  {
5411  // add the table and version pair to the map
5412  name = tableList.substr(i, c - i);
5413  i = c + 1;
5414  c = tableList.find(',', i);
5415  if(c == std::string::npos) // missing version list entry?!
5416  {
5417  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
5418  __SUP_COUT_ERR__ << "\n" << ss.str();
5419  xmlOut.addTextElementToData("Error", ss.str());
5420  return;
5421  }
5422 
5423  versionStr = tableList.substr(i, c - i);
5424  i = c + 1;
5425  c = tableList.find(',', i);
5426 
5427  //__SUP_COUT__ << "name: " << name << __E__;
5428  //__SUP_COUT__ << "versionStr: " << versionStr << __E__;
5429 
5430  // check if version is an alias and convert
5431  if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
5432  {
5433  alias =
5434  versionStr.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
5435 
5436  __SUP_COUT__ << "Found alias " << name << " " << versionStr << __E__;
5437 
5438  // convert alias to version
5439  if(versionAliases.find(name) != versionAliases.end() &&
5440  versionAliases[name].find(alias) != versionAliases[name].end())
5441  {
5442  version = versionAliases[name][alias];
5443  __SUP_COUT__ << "version alias '" << alias
5444  << "'translated to: " << version << __E__;
5445 
5446  groupAliases[name] = alias;
5447  }
5448  else
5449  {
5450  __SUP_SS__ << "version alias '"
5451  << versionStr.substr(
5452  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
5453  << "' was not found in active version aliases!" << __E__;
5454  __SUP_COUT_ERR__ << "\n" << ss.str();
5455  xmlOut.addTextElementToData("Error", ss.str());
5456  return;
5457  }
5458  }
5459  else
5460  version = TableVersion(versionStr);
5461 
5462  if(version.isTemporaryVersion())
5463  {
5464  __SUP_SS__ << "Groups can not be created using temporary member tables. "
5465  << "Table member '" << name << "' with temporary version '"
5466  << version << "' is illegal." << __E__;
5467  xmlOut.addTextElementToData("Error", ss.str());
5468  return;
5469  }
5470 
5471  // enforce that table exists
5472  if(allTableInfo.find(name) == allTableInfo.end())
5473  {
5474  __SUP_SS__ << "Groups can not be created using mock-up member tables of "
5475  "undefined tables. "
5476  << "Table member '" << name << "' is not defined." << __E__;
5477  xmlOut.addTextElementToData("Error", ss.str());
5478  return;
5479  }
5480 
5481  if(version.isMockupVersion())
5482  {
5483  // if mockup, then generate a new persistent version to use based on mockup
5484  TableBase* config = cfgMgr->getTableByName(name);
5485  // create a temporary version from the mockup as source version
5486  TableVersion temporaryVersion = config->createTemporaryView();
5487  __SUP_COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__;
5488 
5489  // if other versions exist check for another mockup, and use that instead
5490  __SUP_COUT__ << "Creating version from mock-up for name: " << name
5491  << " inputVersionStr: " << versionStr << __E__;
5492 
5493  // set table comment
5494  config->getTemporaryView(temporaryVersion)
5495  ->setComment("Auto-generated from mock-up.");
5496 
5497  // finish off the version creation
5498  version =
5499  saveModifiedVersionXML(xmlOut,
5500  cfgMgr,
5501  name,
5502  TableVersion() /*original source is mockup*/,
5503  false /* makeTemporary */,
5504  config,
5505  temporaryVersion /*temporary modified version*/,
5506  false /*ignore duplicates*/,
5507  true /*look for equivalent*/);
5508 
5509  __SUP_COUT__ << "Using mockup version: " << version << __E__;
5510  }
5511 
5512  //__SUP_COUT__ << "version: " << version << __E__;
5513  groupMembers[name] = version;
5514  } // end member verification loop
5515 
5516  __COUTV__(StringMacros::mapToString(groupAliases));
5517 
5518  if(!allowDuplicates)
5519  {
5520  __SUP_COUT__ << "Checking for duplicate groups..." << __E__;
5521  TableGroupKey foundKey =
5522  cfgMgr->findTableGroup(groupName, groupMembers, groupAliases);
5523 
5524  if(!foundKey.isInvalid())
5525  {
5526  // return found equivalent key
5527  xmlOut.addTextElementToData("TableGroupName", groupName);
5528  xmlOut.addTextElementToData("TableGroupKey", foundKey.toString());
5529 
5530  if(lookForEquivalent)
5531  {
5532  __SUP_COUT__ << "Found equivalent group key (" << foundKey << ") for "
5533  << groupName << "." << __E__;
5534  // allow this equivalent group to be the response without an error
5535  xmlOut.addTextElementToData("foundEquivalentKey", "1"); // indicator
5536 
5537  // insert get table info
5538  handleGetTableGroupXML(
5539  xmlOut, cfgMgr, groupName, foundKey, ignoreWarnings);
5540  return;
5541  }
5542  else // treat as error, if not looking for equivalent
5543  {
5544  __SUP_COUT__ << "Treating duplicate group as error." << __E__;
5545  __SUP_SS__ << ("Failed to create table group: " + groupName +
5546  ". It is a duplicate of an existing group key (" +
5547  foundKey.toString() + ")");
5548  __SUP_COUT_ERR__ << ss.str() << __E__;
5549  xmlOut.addTextElementToData("Error", ss.str());
5550  return;
5551  }
5552  }
5553 
5554  __SUP_COUT__ << "Check for duplicate groups complete." << __E__;
5555  }
5556 
5557  // check the group for errors before creating group
5558  try
5559  {
5560  cfgMgr->loadMemberMap(groupMembers);
5561 
5562  std::string accumulateErrors = "";
5563  for(auto& groupMemberPair : groupMembers)
5564  {
5565  TableView* cfgViewPtr =
5566  cfgMgr->getTableByName(groupMemberPair.first)->getViewP();
5567  if(cfgViewPtr->getDataColumnSize() != cfgViewPtr->getNumberOfColumns() ||
5568  cfgViewPtr->getSourceColumnMismatch() !=
5569  0) // check for column size mismatch
5570  {
5571  __SUP_SS__ << "\n\nThere were errors found in loading a member table "
5572  << groupMemberPair.first << ":v" << cfgViewPtr->getVersion()
5573  << ". Please see the details below:\n\n"
5574  << "The source column size was found to be "
5575  << cfgViewPtr->getDataColumnSize()
5576  << ", and the current number of columns for this table is "
5577  << cfgViewPtr->getNumberOfColumns()
5578  << ". This resulted in a count of "
5579  << cfgViewPtr->getSourceColumnMismatch()
5580  << " source column mismatches, and a count of "
5581  << cfgViewPtr->getSourceColumnMissing()
5582  << " table entries missing in "
5583  << cfgViewPtr->getNumberOfRows() << " row(s) of data."
5584  << __E__;
5585 
5586  const std::set<std::string> srcColNames =
5587  cfgViewPtr->getSourceColumnNames();
5588  ss << "\n\nSource column names were as follows:\n";
5589  char index = 'a';
5590  for(auto& srcColName : srcColNames)
5591  ss << "\n\t" << index++ << ". " << srcColName;
5592  ss << __E__;
5593 
5594  std::set<std::string> destColNames = cfgViewPtr->getColumnStorageNames();
5595  ss << "\n\nCurrent table column names are as follows:\n";
5596  index = 'a';
5597  for(auto& destColName : destColNames)
5598  ss << "\n\t" << index++ << ". " << destColName;
5599  ss << __E__;
5600 
5601  __SUP_COUT_ERR__ << "\n" << ss.str();
5602  xmlOut.addTextElementToData("Error", ss.str());
5603  return;
5604  }
5605  }
5606  }
5607  catch(std::runtime_error& e)
5608  {
5609  __SUP_SS__ << "Failed to create config group: " << groupName
5610  << ".\nThere were problems loading the chosen members:\n\n"
5611  << e.what() << __E__;
5612  __SUP_COUT_ERR__ << "\n" << ss.str();
5613  xmlOut.addTextElementToData("Error", ss.str());
5614  return;
5615  }
5616  catch(...)
5617  {
5618  __SUP_SS__ << "Failed to create config group: " << groupName << __E__;
5619  __SUP_COUT_ERR__ << "\n" << ss.str();
5620  xmlOut.addTextElementToData("Error", ss.str());
5621  return;
5622  }
5623 
5624  // check the tree for warnings before creating group
5625  std::string accumulateTreeErrs;
5626  cfgMgr->getChildren(&groupMembers, &accumulateTreeErrs);
5627  if(accumulateTreeErrs != "")
5628  {
5629  __SUP_COUT_WARN__ << "\n" << accumulateTreeErrs << __E__;
5630  if(!ignoreWarnings)
5631  {
5632  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
5633  return;
5634  }
5635  }
5636 
5637  TableGroupKey newKey;
5638  try
5639  {
5640  __COUT__ << "Saving new group..." << __E__;
5641  newKey = cfgMgr->saveNewTableGroup(
5642  groupName, groupMembers, groupComment, &groupAliases);
5643  }
5644  catch(std::runtime_error& e)
5645  {
5646  __SUP_COUT_ERR__ << "Failed to create config group: " << groupName << __E__;
5647  __SUP_COUT_ERR__ << "\n\n" << e.what() << __E__;
5648  xmlOut.addTextElementToData(
5649  "Error", "Failed to create table group: " + groupName + ".\n\n" + e.what());
5650  return;
5651  }
5652  catch(...)
5653  {
5654  __SUP_COUT_ERR__ << "Failed to create table group: " << groupName << __E__;
5655  xmlOut.addTextElementToData("Error",
5656  "Failed to create table group: " + groupName);
5657  return;
5658  }
5659 
5660  // insert get table info
5661  __COUT__ << "Loading new table group..." << __E__;
5662  handleGetTableGroupXML(xmlOut, cfgMgr, groupName, newKey, ignoreWarnings);
5663 
5664 } // end handleCreateTableGroupXML()
5665 catch(std::runtime_error& e)
5666 {
5667  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
5668  xmlOut.addTextElementToData("Error",
5669  "Error saving table group! " + std::string(e.what()));
5670 }
5671 catch(...)
5672 {
5673  __SUP_COUT__ << "Unknown Error detected!\n\n " << __E__;
5674  xmlOut.addTextElementToData("Error", "Error saving table group! ");
5675 } // end handleCreateTableGroupXML() catch
5676 
5677 //========================================================================================================================
5678 // handleDeleteTableInfoXML
5679 //
5680 // return nothing except Error in xmlOut
5681 //
5682 void ConfigurationGUISupervisor::handleDeleteTableInfoXML(HttpXmlDocument& xmlOut,
5683  ConfigurationManagerRW* cfgMgr,
5684  std::string& tableName)
5685 {
5686  if(0 == rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
5687  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
5688  __SUP_COUT_INFO__ << ("Table Info File successfully renamed: " +
5689  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5690  << __E__;
5691  else
5692  {
5693  __SUP_COUT_ERR__ << ("Error renaming file to " +
5694  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5695  << __E__;
5696 
5697  xmlOut.addTextElementToData(
5698  "Error",
5699  ("Error renaming Table Info File to " +
5700  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")));
5701  return;
5702  }
5703 
5704  // reload all with refresh to remove new table
5705  cfgMgr->getAllTableInfo(true);
5706 } // end handleDeleteTableInfoXML()
5707 
5708 //========================================================================================================================
5709 // handleSaveTableInfoXML
5710 //
5711 // write new info file for tableName based CSV column info
5712 // data="type,name,dataType;type,name,dataType;..."
5713 // return resulting handleGetTableXML mock-up view
5714 //
5715 void ConfigurationGUISupervisor::handleSaveTableInfoXML(
5716  HttpXmlDocument& xmlOut,
5717  ConfigurationManagerRW* cfgMgr,
5718  std::string& tableName,
5719  const std::string& data,
5720  const std::string& tableDescription,
5721  const std::string& columnChoicesCSV,
5722  bool allowOverwrite)
5723 {
5724  // create all caps name and validate
5725  // only allow alpha-numeric names with "Table" at end
5726  std::string capsName;
5727  try
5728  {
5729  capsName = TableBase::convertToCaps(tableName, true);
5730  }
5731  catch(std::runtime_error& e)
5732  { // error! non-alpha
5733  xmlOut.addTextElementToData("Error", e.what());
5734  return;
5735  }
5736 
5737  if(!allowOverwrite)
5738  {
5739  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "r");
5740  if(fp)
5741  {
5742  fclose(fp);
5743  xmlOut.addTextElementToData("TableName", tableName);
5744  xmlOut.addTextElementToData("OverwriteError", "1");
5745  xmlOut.addTextElementToData(
5746  "Error",
5747  "File already exists! ('" +
5748  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT) + "')");
5749  return;
5750  }
5751  }
5752 
5753  __SUP_COUT__ << "capsName=" << capsName << __E__;
5754  __SUP_COUT__ << "tableName=" << tableName << __E__;
5755  __SUP_COUT__ << "tableDescription=" << tableDescription << __E__;
5756  __SUP_COUT__ << "columnChoicesCSV=" << columnChoicesCSV << __E__;
5757 
5758  // create preview string to validate column info before write to file
5759  std::stringstream outss;
5760 
5761  outss << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n";
5762  outss << "\t<ROOT xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
5763  "xsi:noNamespaceSchemaLocation=\"TableInfo.xsd\">\n";
5764  outss << "\t\t<TABLE Name=\"" << tableName << "\">\n";
5765  outss << "\t\t\t<VIEW Name=\"" << capsName
5766  << "\" Type=\"File,Database,DatabaseTest\" Description=\"" << tableDescription
5767  << "\">\n";
5768 
5769  // each column is represented by 3 fields
5770  // - type, name, dataType
5771  int i = 0; // use to parse data std::string
5772  int j = data.find(',', i); // find next field delimiter
5773  int k = data.find(';', i); // find next col delimiter
5774 
5775  std::istringstream columnChoicesISS(columnChoicesCSV);
5776  std::string columnChoicesString;
5777  std::string columnType;
5778 
5779  while(k != (int)(std::string::npos))
5780  {
5781  // type
5782  columnType = data.substr(i, j - i);
5783  outss << "\t\t\t\t<COLUMN Type=\"";
5784  outss << columnType;
5785 
5786  i = j + 1;
5787  j = data.find(',', i); // find next field delimiter
5788 
5789  // name and storage name
5790  outss << "\" \t Name=\"";
5791  capsName = data.substr(i, j - i); // not caps yet
5792  outss << capsName;
5793  outss << "\" \t StorageName=\"";
5794 
5795  try
5796  {
5797  outss << TableBase::convertToCaps(capsName); // now caps
5798  }
5799  catch(std::runtime_error& e)
5800  { // error! non-alpha
5801  xmlOut.addTextElementToData("Error",
5802  std::string("For column name '") +
5803  data.substr(i, j - i) + "' - " + e.what());
5804  return;
5805  }
5806 
5807  i = j + 1;
5808  j = data.find(',', i); // find next field delimiter
5809 
5810  // data type
5811  outss << "\" \t DataType=\"";
5812  outss << data.substr(i, k - i);
5813 
5814  // fixed data choices for TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA
5815  getline(columnChoicesISS, columnChoicesString, ';');
5816  //__SUP_COUT__ << "columnChoicesString = " << columnChoicesString << __E__;
5817  outss << "\" \t DataChoices=\"";
5818  outss << columnChoicesString;
5819 
5820  // end column info
5821  outss << "\"/>\n";
5822 
5823  i = k + 1;
5824  j = data.find(',', i); // find next field delimiter
5825  k = data.find(';', i); // find new col delimiter
5826  }
5827 
5828  outss << "\t\t\t</VIEW>\n";
5829  outss << "\t\t</TABLE>\n";
5830  outss << "\t</ROOT>\n";
5831 
5832  __SUP_COUT__ << outss.str() << __E__;
5833 
5834  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "w");
5835  if(!fp)
5836  {
5837  xmlOut.addTextElementToData("Error",
5838  "Failed to open destination Table Info file:" +
5839  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT));
5840  return;
5841  }
5842 
5843  fprintf(fp, "%s", outss.str().c_str());
5844  fclose(fp);
5845 
5846  // reload all config info with refresh AND reset to pick up possibly new config
5847  // check for errors related to this tableName
5848  std::string accumulatedErrors = "";
5849  cfgMgr->getAllTableInfo(true, &accumulatedErrors, tableName);
5850 
5851  // if errors associated with this config name stop and report
5852  if(accumulatedErrors != "")
5853  {
5854  __SUP_SS__ << ("The new version of the '" + tableName +
5855  "' table column info was saved, however errors were detected "
5856  "reading back the table '" +
5857  tableName + "' after the save attempt:\n\n" + accumulatedErrors)
5858  << __E__;
5859 
5860  __SUP_COUT_ERR__ << ss.str() << __E__;
5861  xmlOut.addTextElementToData("Error", ss.str());
5862 
5863  // if error detected reading back then move the saved table info to
5864  // .unused
5865  // // This was disabled by RAR on 11/4/2016.. (just keep broken info files)
5866  // // ... especially since now there is a Delete button
5867  if(0)
5868  {
5869  // table info is illegal so report error, and disable file
5870 
5871  // if error detected //move file to ".unused"
5872  if(0 ==
5873  rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
5874  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
5875  __SUP_COUT_INFO__
5876  << ("File successfully renamed: " +
5877  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5878  << __E__;
5879  else
5880 
5881  __SUP_COUT_ERR__
5882  << ("Error renaming file to " +
5883  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5884  << __E__;
5885 
5886  // reload all with refresh to remove new table
5887  cfgMgr->getAllTableInfo(true);
5888  }
5889  return;
5890  }
5891 
5892  // return the new table info
5893  handleGetTableXML(xmlOut, cfgMgr, tableName, TableVersion());
5894 
5895  // debug all table column info
5896  // FIXME -- possibly remove this debug feature in future
5897  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
5898 
5899  // give a print out of currently illegal table column info
5900  __SUP_COUT_INFO__ << "Looking for errors in all table column info..." << __E__;
5901  for(const auto& cfgInfo : allTableInfo)
5902  {
5903  try
5904  {
5905  cfgMgr->getTableByName(cfgInfo.first)->getMockupViewP()->init();
5906  }
5907  catch(std::runtime_error& e)
5908  {
5909  __SUP_COUT_WARN__ << "\n\n##############################################\n"
5910  << "Error identified in column info of table '"
5911  << cfgInfo.first << "':\n\n"
5912  << e.what() << "\n\n"
5913  << __E__;
5914  }
5915  }
5916 } // end handleSaveTableInfoXML()
5917 
5918 //========================================================================================================================
5919 // handleSetGroupAliasInBackboneXML
5920 // open current backbone
5921 // modify GroupAliases
5922 // save as new version of groupAliases
5923 // return new version of groupAliases
5924 //
5925 // Note: very similar to ConfigurationGUISupervisor::handleSetVersionAliasInBackboneXML
5926 void ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML(
5927  HttpXmlDocument& xmlOut,
5928  ConfigurationManagerRW* cfgMgr,
5929  const std::string& groupAlias,
5930  const std::string& groupName,
5931  TableGroupKey groupKey,
5932  const std::string& author) try
5933 {
5934  cfgMgr->loadConfigurationBackbone();
5935  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
5936 
5937  const std::string groupAliasesTableName =
5938  ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
5939  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
5940  {
5941  __SUP_SS__ << "Active version of " << groupAliasesTableName << " missing!"
5942  << __E__;
5943  xmlOut.addTextElementToData("Error", ss.str());
5944  return;
5945  }
5946 
5947  // put all old backbone versions in xmlOut
5948  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
5949  for(auto& memberName : backboneMembers)
5950  {
5951  __SUP_COUT__ << "activeVersions[\"" << memberName
5952  << "\"]=" << activeVersions[memberName] << __E__;
5953 
5954  xmlOut.addTextElementToData("oldBackboneName", memberName);
5955  xmlOut.addTextElementToData("oldBackboneVersion",
5956  activeVersions[memberName].toString());
5957  }
5958 
5959  // make a temporary version from active view
5960  // modify the chosen groupAlias row
5961  // save as new version
5962 
5963  TableBase* config = cfgMgr->getTableByName(groupAliasesTableName);
5964  TableVersion originalVersion = activeVersions[groupAliasesTableName];
5965  TableVersion temporaryVersion = config->createTemporaryView(originalVersion);
5966 
5967  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
5968  bool isDifferent = false;
5969 
5970  try
5971  {
5972  TableView* configView = config->getTemporaryView(temporaryVersion);
5973 
5974  unsigned int col = configView->findCol("GroupKeyAlias");
5975 
5976  // only make a new version if we are changing compared to active backbone
5977 
5978  unsigned int row = -1;
5979  // find groupAlias row
5980  try
5981  {
5982  row = configView->findRow(col, groupAlias);
5983  }
5984  catch(...)
5985  {
5986  }
5987  if(row == (unsigned int)-1) // if row not found then add a row
5988  {
5989  isDifferent = true;
5990  row = configView->addRow();
5991 
5992  // set all columns in new row
5993  col = configView->findCol("CommentDescription");
5994  configView->setValue(
5995  "This Group Alias was automatically setup by the server.", row, col);
5996  col = configView->findCol("GroupKeyAlias");
5997  configView->setValue(groupAlias, row, col);
5998  }
5999 
6000  __SUP_COUT__ << "\t\t row: " << row << __E__;
6001 
6002  col = configView->findCol("GroupName");
6003 
6004  __SUP_COUT__ << "\t\t groupName: " << groupName << " vs "
6005  << configView->getDataView()[row][col] << __E__;
6006  if(groupName != configView->getDataView()[row][col])
6007  {
6008  configView->setValue(groupName, row, col);
6009  isDifferent = true;
6010  }
6011 
6012  col = configView->findCol("GroupKey");
6013  __SUP_COUT__ << "\t\t groupKey: " << groupKey << " vs "
6014  << configView->getDataView()[row][col] << __E__;
6015  if(groupKey.toString() != configView->getDataView()[row][col])
6016  {
6017  configView->setValue(groupKey.toString(), row, col);
6018  isDifferent = true;
6019  }
6020 
6021  if(isDifferent) // set author/time of new version if different
6022  {
6023  configView->setValue(author, row, configView->findCol("Author"));
6024  configView->setValue(
6025  time(0), row, configView->findCol("RecordInsertionTime"));
6026  }
6027  }
6028  catch(...)
6029  {
6030  __SUP_COUT_ERR__ << "Error editing Group Alias view!" << __E__;
6031 
6032  // delete temporaryVersion
6033  config->eraseView(temporaryVersion);
6034  throw;
6035  }
6036 
6037  TableVersion newAssignedVersion;
6038  if(isDifferent) // make new version if different
6039  {
6040  __SUP_COUT__ << "\t\t**************************** Save as new table version"
6041  << __E__;
6042 
6043  // newAssignedVersion =
6044  // cfgMgr->saveNewTable(groupAliasesTableName,temporaryVersion);
6045 
6046  // save or find equivalent
6047 
6048  newAssignedVersion = saveModifiedVersionXML(xmlOut,
6049  cfgMgr,
6050  config->getTableName(),
6051  originalVersion,
6052  false /*makeTemporary*/,
6053  config,
6054  temporaryVersion,
6055  false /*ignoreDuplicates*/,
6056  true /*lookForEquivalent*/);
6057  }
6058  else // use existing version
6059  {
6060  __SUP_COUT__
6061  << "\t\t**************************** Using the existing table version"
6062  << __E__;
6063 
6064  // delete temporaryVersion
6065  config->eraseView(temporaryVersion);
6066  newAssignedVersion = activeVersions[groupAliasesTableName];
6067 
6068  xmlOut.addTextElementToData("savedName", groupAliasesTableName);
6069  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6070  }
6071 
6072  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6073 }
6074 catch(std::runtime_error& e)
6075 {
6076  __SUP_COUT_ERR__ << "Error detected!\n\n " << e.what() << __E__;
6077  xmlOut.addTextElementToData(
6078  "Error", "Error saving new Group Alias view!\n " + std::string(e.what()));
6079 }
6080 catch(...)
6081 {
6082  __SUP_COUT_ERR__ << "Error detected!\n\n " << __E__;
6083  xmlOut.addTextElementToData("Error", "Error saving new Group Alias view! ");
6084 }
6085 
6086 //========================================================================================================================
6087 // handleSetVersionAliasInBackboneXML
6088 // open current backbone
6089 // modify VersionAliases
6090 // save as new version of VersionAliases
6091 // return new version of VersionAliases
6092 //
6093 // Note: very similar to ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML
6094 void ConfigurationGUISupervisor::handleSetVersionAliasInBackboneXML(
6095  HttpXmlDocument& xmlOut,
6096  ConfigurationManagerRW* cfgMgr,
6097  const std::string& versionAlias,
6098  const std::string& tableName,
6099  TableVersion version,
6100  const std::string& author) try
6101 {
6102  cfgMgr->loadConfigurationBackbone();
6103  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6104 
6105  const std::string versionAliasesTableName =
6106  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6107  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6108  {
6109  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
6110  << __E__;
6111  xmlOut.addTextElementToData("Error", ss.str());
6112  return;
6113  }
6114 
6115  // put all old backbone versions in xmlOut
6116  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6117  for(auto& memberName : backboneMembers)
6118  {
6119  __SUP_COUT__ << "activeVersions[\"" << memberName
6120  << "\"]=" << activeVersions[memberName] << __E__;
6121 
6122  xmlOut.addTextElementToData("oldBackboneName", memberName);
6123  xmlOut.addTextElementToData("oldBackboneVersion",
6124  activeVersions[memberName].toString());
6125  }
6126 
6127  // make a temporary version from active view
6128  // modify the chosen versionAlias row
6129  // save as new version
6130 
6131  TableBase* config = cfgMgr->getTableByName(versionAliasesTableName);
6132  TableVersion originalVersion = activeVersions[versionAliasesTableName];
6133  TableVersion temporaryVersion = config->createTemporaryView(originalVersion);
6134 
6135  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6136 
6137  bool isDifferent = false;
6138 
6139  try
6140  {
6141  TableView* configView = config->getTemporaryView(temporaryVersion);
6142 
6143  unsigned int col;
6144  unsigned int col2 = configView->findCol("VersionAlias");
6145  unsigned int col3 = configView->findCol("TableName");
6146 
6147  // only make a new version if we are changing compared to active backbone
6148 
6149  unsigned int row = -1;
6150  // find tableName, versionAlias pair
6151  // NOTE: only accept the first pair, repeats are ignored.
6152  try
6153  {
6154  unsigned int tmpRow = -1;
6155  do
6156  { // start looking from beyond last find
6157  tmpRow = configView->findRow(col3, tableName, tmpRow + 1);
6158  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
6159  // at this point the first pair was found! (else exception was thrown)
6160  row = tmpRow;
6161  }
6162  catch(...)
6163  {
6164  }
6165  if(row == (unsigned int)-1) // if row not found then add a row
6166  {
6167  isDifferent = true;
6168  row = configView->addRow();
6169 
6170  // set all columns in new row
6171  col = configView->findCol("CommentDescription");
6172  configView->setValue(
6173  std::string("Entry was added by server in ") +
6174  "ConfigurationGUISupervisor::setVersionAliasInActiveBackbone().",
6175  row,
6176  col);
6177 
6178  col = configView->findCol("VersionAliasUID");
6179  configView->setValue(
6180  tableName.substr(0, tableName.rfind("Table")) + versionAlias, row, col);
6181 
6182  configView->setValue(versionAlias, row, col2);
6183  configView->setValue(tableName, row, col3);
6184  }
6185 
6186  __SUP_COUT__ << "\t\t row: " << row << __E__;
6187 
6188  col = configView->findCol("Version");
6189  __SUP_COUT__ << "\t\t version: " << version << " vs "
6190  << configView->getDataView()[row][col] << __E__;
6191  if(version.toString() != configView->getDataView()[row][col])
6192  {
6193  configView->setValue(version.toString(), row, col);
6194  isDifferent = true;
6195  }
6196 
6197  if(isDifferent) // set author/time of new version if different
6198  {
6199  configView->setValue(author, row, configView->findCol("Author"));
6200  configView->setValue(
6201  time(0), row, configView->findCol("RecordInsertionTime"));
6202  }
6203  }
6204  catch(...)
6205  {
6206  __SUP_COUT_ERR__ << "Error editing Version Alias view!" << __E__;
6207 
6208  // delete temporaryVersion
6209  config->eraseView(temporaryVersion);
6210  throw;
6211  }
6212 
6213  TableVersion newAssignedVersion;
6214  if(isDifferent) // make new version if different
6215  {
6216  __SUP_COUT__ << "\t\t**************************** Save as new table version"
6217  << __E__;
6218 
6219  // newAssignedVersion =
6220  // cfgMgr->saveNewTable(versionAliasesTableName,temporaryVersion);
6221 
6222  newAssignedVersion = saveModifiedVersionXML(xmlOut,
6223  cfgMgr,
6224  config->getTableName(),
6225  originalVersion,
6226  false /*makeTemporary*/,
6227  config,
6228  temporaryVersion,
6229  false /*ignoreDuplicates*/,
6230  true /*lookForEquivalent*/);
6231  }
6232  else // use existing version
6233  {
6234  __SUP_COUT__ << "\t\t**************************** Using existing table version"
6235  << __E__;
6236 
6237  // delete temporaryVersion
6238  config->eraseView(temporaryVersion);
6239  newAssignedVersion = activeVersions[versionAliasesTableName];
6240 
6241  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
6242  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6243  }
6244 
6245  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6246 } // end handleSetVersionAliasInBackboneXML()
6247 catch(std::runtime_error& e)
6248 {
6249  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
6250  xmlOut.addTextElementToData(
6251  "Error", "Error saving new Version Alias view!\n " + std::string(e.what()));
6252 }
6253 catch(...)
6254 {
6255  __SUP_COUT__ << "Error detected!\n\n " << __E__;
6256  xmlOut.addTextElementToData("Error", "Error saving new Version Alias view! ");
6257 } // end handleSetVersionAliasInBackboneXML() catch
6258 
6259 //========================================================================================================================
6260 // handleAliasGroupMembersInBackboneXML
6261 // open current backbone
6262 // modify VersionAliases
6263 // save as new version of VersionAliases
6264 // return new version of VersionAliases
6265 void ConfigurationGUISupervisor::handleAliasGroupMembersInBackboneXML(
6266  HttpXmlDocument& xmlOut,
6267  ConfigurationManagerRW* cfgMgr,
6268  const std::string& versionAlias,
6269  const std::string& groupName,
6270  TableGroupKey groupKey,
6271  const std::string& author) try
6272 {
6273  cfgMgr->loadConfigurationBackbone();
6274  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6275 
6276  const std::string versionAliasesTableName =
6277  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6278  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6279  {
6280  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
6281  << __E__;
6282  xmlOut.addTextElementToData("Error", ss.str());
6283  return;
6284  }
6285 
6286  // put all old backbone versions in xmlOut
6287  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6288  for(auto& memberName : backboneMembers)
6289  {
6290  __SUP_COUT__ << "activeVersions[\"" << memberName
6291  << "\"]=" << activeVersions[memberName] << __E__;
6292 
6293  xmlOut.addTextElementToData("oldBackboneName", memberName);
6294  xmlOut.addTextElementToData("oldBackboneVersion",
6295  activeVersions[memberName].toString());
6296  }
6297 
6298  // make a temporary version from active view
6299  // modify the chosen versionAlias row
6300  // save as new version
6301 
6302  TableBase* config = cfgMgr->getTableByName(versionAliasesTableName);
6303  TableVersion temporaryVersion =
6304  config->createTemporaryView(activeVersions[versionAliasesTableName]);
6305 
6306  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6307 
6308  TableView* configView = config->getTemporaryView(temporaryVersion);
6309 
6310  // only make a new version if we are changing compared to active backbone
6311  bool isDifferent = false;
6312 
6313  // get member names and versions
6314  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
6315  try
6316  {
6317  cfgMgr->loadTableGroup(groupName,
6318  groupKey,
6319  false /*doActivate*/,
6320  &memberMap,
6321  0,
6322  0,
6323  0,
6324  0,
6325  0, // defaults
6326  true /*doNotLoadMember*/);
6327  }
6328  catch(...)
6329  {
6330  xmlOut.addTextElementToData(
6331  "Error",
6332  "Table group \"" + TableGroupKey::getFullGroupString(groupName, groupKey) +
6333  "\" can not be retrieved!");
6334  return;
6335  }
6336 
6337  unsigned int col;
6338  unsigned int col2 = configView->findCol("VersionAlias");
6339  unsigned int col3 = configView->findCol("TableName");
6340 
6341  for(auto& memberPair : memberMap)
6342  {
6343  bool thisMemberIsDifferent = false;
6344  unsigned int row = -1;
6345 
6346  __SUP_COUT__ << "Adding alias for " << memberPair.first << "_v"
6347  << memberPair.second << " to " << versionAlias << __E__;
6348 
6349  // find tableName, versionAlias pair
6350  // NOTE: only accept the first pair, repeats are ignored.
6351  try
6352  {
6353  unsigned int tmpRow = -1;
6354  do
6355  { // start looking from beyond last find
6356  tmpRow = configView->findRow(col3, memberPair.first, tmpRow + 1);
6357 
6358  //__SUP_COUT__ << configView->getDataView()[tmpRow][col2] << __E__;
6359  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
6360  // at this point the first pair was found! (else exception was thrown)
6361  row = tmpRow;
6362  }
6363  catch(...)
6364  {
6365  }
6366  if(row == (unsigned int)-1) // if row not found then add a row
6367  {
6368  thisMemberIsDifferent = true;
6369  row = configView->addRow();
6370 
6371  // set all columns in new row
6372  col = configView->findCol("CommentDescription");
6373  configView->setValue(
6374  std::string("Entry was added by server in ") +
6375  "ConfigurationGUISupervisor::setVersionAliasInActiveBackbone().",
6376  row,
6377  col);
6378 
6379  col = configView->getColUID();
6380  configView->setValue(
6381  memberPair.first.substr(0, memberPair.first.rfind("Table")) +
6382  versionAlias,
6383  row,
6384  col);
6385 
6386  configView->setValue(versionAlias, row, col2);
6387  configView->setValue(memberPair.first, row, col3);
6388  }
6389 
6390  //__SUP_COUT__ << "\t\t row: " << row << __E__;
6391 
6392  col = configView->findCol("Version");
6393  //__SUP_COUT__ << "\t\t col: " << col << __E__;
6394  //__SUP_COUT__ << "\t\t version: " << memberPair.second << " vs "
6395  // << configView->getDataView()[row][col] << __E__;
6396  if(memberPair.second.toString() != configView->getDataView()[row][col])
6397  {
6398  configView->setValue(memberPair.second.toString(), row, col);
6399  thisMemberIsDifferent = true;
6400  }
6401 
6402  if(thisMemberIsDifferent) // change author and time if row is different
6403  {
6404  configView->setValue(author, row, configView->findCol("Author"));
6405  configView->setValue(
6406  time(0), row, configView->findCol("RecordInsertionTime"));
6407  }
6408 
6409  if(thisMemberIsDifferent)
6410  isDifferent = true;
6411  }
6412 
6413  // configView->print();
6414 
6415  TableVersion newAssignedVersion;
6416  if(isDifferent) // make new version if different
6417  {
6418  __SUP_COUT__ << "\t\t**************************** Save v" << temporaryVersion
6419  << " as new table version" << __E__;
6420 
6421  newAssignedVersion =
6422  cfgMgr->saveNewTable(versionAliasesTableName, temporaryVersion);
6423  }
6424  else // use existing version
6425  {
6426  __SUP_COUT__ << "\t\t**************************** Using existing table version"
6427  << __E__;
6428 
6429  // delete temporaryVersion
6430  config->eraseView(temporaryVersion);
6431  newAssignedVersion = activeVersions[versionAliasesTableName];
6432  }
6433 
6434  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
6435  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6436  __SUP_COUT__ << "\t\t Resulting Version: " << newAssignedVersion << __E__;
6437 } // end handleAliasGroupMembersInBackboneXML()
6438 catch(std::runtime_error& e)
6439 {
6440  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
6441  xmlOut.addTextElementToData(
6442  "Error", "Error saving new Version Alias view!\n " + std::string(e.what()));
6443 }
6444 catch(...)
6445 {
6446  __SUP_COUT__ << "Error detected!\n\n " << __E__;
6447  xmlOut.addTextElementToData("Error", "Error saving new Version Alias view! ");
6448 } // end handleAliasGroupMembersInBackboneXML() catch
6449 
6450 //========================================================================================================================
6451 // handleGroupAliasesXML
6452 //
6453 // return aliases and backbone groupAlias table version
6454 //
6455 // return this information:
6456 // <backbone groupTableName=xxx version=xxx>
6457 // <group alias=xxx name=xxx key=xxx comment=xxx>
6458 // <group alias=xxx name=xxx key=xxx comment=xxx>
6459 // ...
6460 //
6461 void ConfigurationGUISupervisor::handleGroupAliasesXML(HttpXmlDocument& xmlOut,
6462  ConfigurationManagerRW* cfgMgr)
6463 {
6464  cfgMgr->loadConfigurationBackbone();
6465  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6466 
6467  std::string groupAliasesTableName = ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
6468  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
6469  {
6470  __SUP_SS__ << "\nActive version of " << groupAliasesTableName << " missing! "
6471  << groupAliasesTableName
6472  << " is a required member of the Backbone table group."
6473  << "\n\nLikely you need to activate a valid Backbone table group."
6474  << __E__;
6475  xmlOut.addTextElementToData("Error", ss.str());
6476  return;
6477  }
6478  __SUP_COUT__ << "activeVersions[\"" << groupAliasesTableName
6479  << "\"]=" << activeVersions[groupAliasesTableName] << __E__;
6480  xmlOut.addTextElementToData("GroupAliasesTableName", groupAliasesTableName);
6481  xmlOut.addTextElementToData("GroupAliasesTableVersion",
6482  activeVersions[groupAliasesTableName].toString());
6483 
6484  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
6485  cfgMgr->getNode(groupAliasesTableName).getChildren();
6486 
6487  std::string groupName, groupKey, groupComment, groupType;
6488  for(auto& aliasNodePair : aliasNodePairs)
6489  {
6490  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
6491  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
6492 
6493  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
6494  xmlOut.addTextElementToData("GroupName", groupName);
6495  xmlOut.addTextElementToData("GroupKey", groupKey);
6496  xmlOut.addTextElementToData(
6497  "AliasComment",
6498  aliasNodePair.second.getNode("CommentDescription").getValueAsString());
6499 
6500  // get group comment
6501  groupComment = ""; // clear just in case failure
6502  groupType = "Invalid";
6503  try
6504  {
6505  cfgMgr->loadTableGroup(groupName,
6506  TableGroupKey(groupKey),
6507  0,
6508  0,
6509  0,
6510  0,
6511  &groupComment,
6512  0,
6513  0, // mostly defaults
6514  true /*doNotLoadMembers*/,
6515  &groupType);
6516  }
6517  catch(...)
6518  {
6519  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "(" << groupKey
6520  << ")' to extract group comment and type." << __E__;
6521  }
6522  xmlOut.addTextElementToData("GroupComment", groupComment);
6523  xmlOut.addTextElementToData("GroupType", groupType);
6524  }
6525 }
6526 
6527 //========================================================================================================================
6528 // handleTableVersionAliasesXML
6529 //
6530 // return version aliases and backbone versionAliases table version
6531 //
6532 // return this information:
6533 // <backbone aliasTableName=xxx version=xxx>
6534 // <version alias=xxx name=xxx version=xxx comment=xxx>
6535 // <version alias=xxx name=xxx version=xxx comment=xxx>
6536 // ...
6537 //
6538 void ConfigurationGUISupervisor::handleVersionAliasesXML(HttpXmlDocument& xmlOut,
6539  ConfigurationManagerRW* cfgMgr)
6540 {
6541  cfgMgr->loadConfigurationBackbone();
6542  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6543 
6544  std::string versionAliasesTableName =
6545  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6546  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6547  {
6548  __SUP_SS__ << "Active version of VersionAliases missing!"
6549  << "Make sure you have a valid active Backbone Group." << __E__;
6550  xmlOut.addTextElementToData("Error", ss.str());
6551  return;
6552  }
6553  __SUP_COUT__ << "activeVersions[\"" << versionAliasesTableName
6554  << "\"]=" << activeVersions[versionAliasesTableName] << __E__;
6555  xmlOut.addTextElementToData("VersionAliasesVersion",
6556  activeVersions[versionAliasesTableName].toString());
6557 
6558  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
6559  cfgMgr->getNode(versionAliasesTableName).getChildren();
6560 
6561  for(auto& aliasNodePair : aliasNodePairs)
6562  {
6563  // note : these are column names in the versionAliasesTableName table
6564  // VersionAlias, TableName, Version, CommentDescription
6565  xmlOut.addTextElementToData(
6566  "VersionAlias",
6567  aliasNodePair.second.getNode("VersionAlias").getValueAsString());
6568  xmlOut.addTextElementToData(
6569  "TableName", aliasNodePair.second.getNode("TableName").getValueAsString());
6570  xmlOut.addTextElementToData(
6571  "Version", aliasNodePair.second.getNode("Version").getValueAsString());
6572  xmlOut.addTextElementToData(
6573  "Comment",
6574  aliasNodePair.second.getNode("CommentDescription").getValueAsString());
6575  }
6576 }
6577 
6578 //========================================================================================================================
6579 // handleGetTableGroupTypeXML
6580 //
6581 // return this information based on member table list
6582 // <TableGroupType value=xxx>
6583 //
6584 void ConfigurationGUISupervisor::handleGetTableGroupTypeXML(
6585  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr, const std::string& tableList)
6586 {
6587  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
6588  std::string name, versionStr;
6589  auto c = tableList.find(',', 0);
6590  auto i = c;
6591  i = 0; // auto used to get proper index/length type
6592  while(c < tableList.length())
6593  {
6594  // add the table name and version pair to the map
6595  name = tableList.substr(i, c - i);
6596  i = c + 1;
6597  c = tableList.find(',', i);
6598  if(c == std::string::npos) // missing version list entry?!
6599  {
6600  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
6601  __SUP_COUT_ERR__ << "\n" << ss.str();
6602  xmlOut.addTextElementToData("Error", ss.str());
6603  return;
6604  }
6605 
6606  versionStr = tableList.substr(i, c - i);
6607  i = c + 1;
6608  c = tableList.find(',', i);
6609 
6610  memberMap[name] = TableVersion(versionStr);
6611  }
6612 
6613  std::string groupTypeString = "";
6614  // try to determine type, dont report errors, just mark "Invalid"
6615  try
6616  {
6617  // determine the type of the table group
6618  groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
6619  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6620  }
6621  catch(std::runtime_error& e)
6622  {
6623  __SUP_SS__ << "Table group has invalid type! " << e.what() << __E__;
6624  __SUP_COUT__ << "\n" << ss.str();
6625  groupTypeString = "Invalid";
6626  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6627  }
6628  catch(...)
6629  {
6630  __SUP_SS__ << "Table group has invalid type! " << __E__;
6631  __SUP_COUT__ << "\n" << ss.str();
6632  groupTypeString = "Invalid";
6633  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6634  }
6635 }
6636 
6637 //========================================================================================================================
6638 // handleTableGroupsXML
6639 //
6640 // if returnMembers then
6641 // return type, comment and members
6642 // else just name and key
6643 //
6644 // return this information
6645 // <group name=xxx key=xxx>
6646 // <config name=xxx version=xxx />
6647 // <config name=xxx version=xxx />
6648 // ...
6649 // </group>
6650 // <group name=xxx key=xxx>...</group>
6651 // ...
6652 //
6653 void ConfigurationGUISupervisor::handleTableGroupsXML(HttpXmlDocument& xmlOut,
6654  ConfigurationManagerRW* cfgMgr,
6655  bool returnMembers)
6656 {
6657  DOMElement* parentEl;
6658 
6659  // get all group info from cache (if no cache, get from interface)
6660 
6661  if(!cfgMgr->getAllGroupInfo()
6662  .size()) // empty cache is strange, attempt to get from interface
6663  {
6664  __SUP_COUT__ << "Cache is empty? Attempting to regenerate." << __E__;
6665  cfgMgr->getAllTableInfo(true /*refresh*/);
6666  }
6667 
6668  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
6669 
6670  // ConfigurationInterface* theInterface = cfgMgr->getConfigurationInterface();
6671  // std::set<std::string /*name*/> configGroups =
6672  // theInterface->getAllTableGroupNames();
6673  // __SUP_COUT__ << "Number of Config groups: " << configGroups.size() << __E__;
6674  //
6675  // TableGroupKey groupKey;
6676  // std::string groupName;
6677  //
6678  // std::map<std::string /*groupName*/,std::set<TableGroupKey> > allGroupsWithKeys;
6679  // for(auto& groupString:configGroups)
6680  // {
6681  // TableGroupKey::getGroupNameAndKey(groupString,groupName,groupKey);
6682  // allGroupsWithKeys[groupName].emplace(groupKey);
6683  //
6684  // //__SUP_COUT__ << "Config group " << groupString << " := " << groupName <<
6685  // //"(" << groupKey << ")" << __E__;
6686  // }
6687 
6688  TableGroupKey groupKey;
6689  std::string groupName;
6690  std::string groupString, groupTypeString, groupComment, groupCreationTime,
6691  groupAuthor;
6692  for(auto& groupInfo : allGroupInfo)
6693  {
6694  groupName = groupInfo.first;
6695  if(groupInfo.second.keys_.size() == 0)
6696  {
6697  __SUP_COUT__ << "Group name '" << groupName
6698  << "' found, but no keys so ignoring." << __E__;
6699  continue;
6700  }
6701 
6702  groupKey = *(groupInfo.second.keys_.rbegin());
6703 
6704  xmlOut.addTextElementToData("TableGroupName", groupName);
6705  xmlOut.addTextElementToData("TableGroupKey", groupKey.toString());
6706 
6707  // trusting the cache!
6708  xmlOut.addTextElementToData("TableGroupType",
6709  groupInfo.second.latestKeyGroupTypeString_);
6710  xmlOut.addTextElementToData("TableGroupComment",
6711  groupInfo.second.latestKeyGroupComment_);
6712  xmlOut.addTextElementToData("TableGroupAuthor",
6713  groupInfo.second.latestKeyGroupAuthor_);
6714  xmlOut.addTextElementToData("TableGroupCreationTime",
6715  groupInfo.second.latestKeyGroupCreationTime_);
6716 
6717  if(returnMembers)
6718  {
6719  // groupTypeString = "Invalid";
6720  // groupComment = ""; //clear just in case failure
6721 
6722  // groupString = TableGroupKey::getFullGroupString(groupName,groupKey);
6723 
6724  //__SUP_COUT__ << "Latest Config group " << groupString << " := " << groupName
6725  //<<
6726  // "(" << groupKey << ")" << __E__;
6727 
6728  parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
6729  //
6730  // std::map<std::string /*name*/, TableVersion /*version*/>
6731  // memberMap;
6732  // //try to determine type, dont report errors, just mark "Invalid"
6733  // try
6734  // {
6735  // //determine the type of the table group
6736  // memberMap = cfgMgr->loadTableGroup(groupName,groupKey,
6737  // 0,0,0,&groupComment,0,0, //mostly defaults
6738  // true /*doNotLoadMembers*/,&groupTypeString);
6739  // //groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
6740  // xmlOut.addTextElementToData("TableGroupType",
6741  // groupTypeString);
6742  // xmlOut.addTextElementToData("TableGroupComment",
6743  // groupComment);
6744  // }
6745  // catch(std::runtime_error& e)
6746  // {
6747  // __SUP_SS__ << "Table group \"" + groupString +
6748  // "\" has invalid type! " + e.what() << __E__;
6749  // __SUP_COUT__ << "\n" << ss.str();
6750  // groupTypeString = "Invalid";
6751  // xmlOut.addTextElementToData("TableGroupType",
6752  // groupTypeString);
6753  // xmlOut.addTextElementToData("TableGroupComment",
6754  // groupComment); continue;
6755  // }
6756  // catch(...)
6757  // {
6758  // __SUP_SS__ << "Table group \"" + groupString +
6759  // "\" has invalid type! " << __E__;
6760  // __SUP_COUT__ << "\n" << ss.str();
6761  // groupTypeString = "Invalid";
6762  // xmlOut.addTextElementToData("TableGroupType",
6763  // groupTypeString);
6764  // xmlOut.addTextElementToData("TableGroupComment",
6765  // groupComment); continue;
6766  // }
6767 
6768  for(auto& memberPair : groupInfo.second.latestKeyMemberMap_)
6769  {
6770  //__SUP_COUT__ << "\tMember config " << memberPair.first << ":" <<
6771  // memberPair.second << __E__;
6772  xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
6773  xmlOut.addTextElementToParent(
6774  "MemberVersion", memberPair.second.toString(), parentEl);
6775  }
6776  } // end if returnMembers
6777 
6778  // add other group keys to xml for this group name
6779  // but just empty members (not displayed anyway)
6780  for(auto& keyInSet : groupInfo.second.keys_)
6781  {
6782  if(keyInSet == groupKey)
6783  continue; // skip the lastest
6784  xmlOut.addTextElementToData("TableGroupName", groupName);
6785  xmlOut.addTextElementToData("TableGroupKey", keyInSet.toString());
6786 
6787  // assume latest in cache reflects others (for speed)
6788  xmlOut.addTextElementToData("TableGroupType",
6789  groupInfo.second.latestKeyGroupTypeString_);
6790  xmlOut.addTextElementToData("TableGroupComment",
6791  groupInfo.second.latestKeyGroupComment_);
6792  xmlOut.addTextElementToData("TableGroupAuthor",
6793  groupInfo.second.latestKeyGroupAuthor_);
6794  xmlOut.addTextElementToData("TableGroupCreationTime",
6795  groupInfo.second.latestKeyGroupCreationTime_);
6796 
6797  if(returnMembers)
6798  {
6799  xmlOut.addTextElementToData("TableGroupMembers", "");
6800 
6801  // TODO -- make loadingHistoricalInfo an input parameter
6802  bool loadingHistoricalInfo = false;
6803  if(loadingHistoricalInfo)
6804  {
6805  groupComment = ""; // clear just in case failure
6806  try
6807  {
6808  cfgMgr->loadTableGroup(groupName,
6809  keyInSet,
6810  0,
6811  0,
6812  0,
6813  0,
6814  &groupComment,
6815  0,
6816  0, // mostly defaults
6817  true /*doNotLoadMembers*/,
6818  &groupTypeString);
6819  }
6820  catch(...)
6821  {
6822  groupTypeString = "Invalid";
6823  __SUP_COUT_WARN__
6824  << "Failed to load group '" << groupName << "(" << keyInSet
6825  << ")' to extract group comment and type." << __E__;
6826  }
6827 
6828  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6829  xmlOut.addTextElementToData("TableGroupComment", groupComment);
6830  xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
6831  xmlOut.addTextElementToData("TableGroupCreationTime",
6832  groupCreationTime);
6833  }
6834  }
6835 
6836  } // end other key loop
6837  } // end primary group loop
6838 }
6839 
6840 //========================================================================================================================
6841 // handleTablesXML
6842 //
6843 // return this information
6844 // <table name=xxx>
6845 // <version key=xxx />
6846 // <version key=xxx />
6847 // ...
6848 // </table>
6849 // <table name=xxx>...</table>
6850 // ...
6851 //
6852 void ConfigurationGUISupervisor::handleTablesXML(HttpXmlDocument& xmlOut,
6853  ConfigurationManagerRW* cfgMgr,
6854  bool allowIllegalColumns)
6855 {
6856  DOMElement* parentEl;
6857 
6858  std::string accumulatedErrors = "";
6859  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(
6860  allowIllegalColumns,
6861  allowIllegalColumns ? &accumulatedErrors
6862  : 0); // if allowIllegalColumns, then also refresh
6863  std::map<std::string, TableInfo>::const_iterator it = allTableInfo.begin();
6864 
6865  __SUP_COUT__ << "# of tables found: " << allTableInfo.size() << __E__;
6866 
6867  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
6868  cfgMgr->getVersionAliases();
6869 
6870  __SUP_COUT__ << "# of tables w/aliases: " << versionAliases.size() << __E__;
6871 
6872  while(it != allTableInfo.end())
6873  {
6874  // for each table name
6875  // get existing version keys
6876 
6877  //__SUP_COUT__ << "Name: " << it->first << " - #ofVersions: " <<
6878  // it->second.versions_.size() << __E__;
6879 
6880  // add system table name
6881  xmlOut.addTextElementToData("TableName", it->first);
6882  parentEl = xmlOut.addTextElementToData("TableVersions", "");
6883 
6884  // include aliases for this table (if the versions exist)
6885  if(versionAliases.find(it->first) != versionAliases.end())
6886  for(auto& aliasVersion : versionAliases[it->first])
6887  if(it->second.versions_.find(aliasVersion.second) !=
6888  it->second.versions_.end())
6889  // if(aliasVersion.first !=
6890  // ConfigurationManager::SCRATCH_VERSION_ALIAS) //NOT NEEDED IF
6891  // SCRATCH IS ALWAYS ALIAS
6892  xmlOut.addTextElementToParent(
6893  "Version",
6894  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
6895  parentEl);
6896  // else //NOT NEEDED IF SCRATCH IS ALWAYS ALIAS
6897  // __SUP_COUT_ERR__ << "Alias for table " << it->first << " is a
6898  // reserved alias '" <<
6899  // ConfigurationManager::SCRATCH_VERSION_ALIAS << "' - this
6900  // is illegal." << __E__;
6901 
6902  // //if scratch version exists, add an alias for it /NOT NEEDED IF SCRATCH IS
6903  // ALWAYS ALIAS
6904  // if(it->second.versions_.find(TableVersion(TableVersion::SCRATCH)) !=
6905  // it->second.versions_.end())
6906  // xmlOut.addTextElementToParent("Version",
6907  // ConfigurationManager::ALIAS_VERSION_PREAMBLE +
6908  // ConfigurationManager::SCRATCH_VERSION_ALIAS, parentEl);
6909 
6910  // get all table versions for the current table
6911  // except skip scratch version
6912  for(auto& version : it->second.versions_)
6913  if(!version.isScratchVersion())
6914  xmlOut.addTextElementToParent("Version", version.toString(), parentEl);
6915 
6916  ++it;
6917  }
6918 
6919  if(accumulatedErrors != "")
6920  xmlOut.addTextElementToData(
6921  "Error",
6922  std::string("Column errors were allowed for this request, ") +
6923  "but please note the following errors:\n" + accumulatedErrors);
6924 }
6925 
6926 //========================================================================================================================
6927 // testXDAQContext
6928 // test activation of context group
6929 void ConfigurationGUISupervisor::testXDAQContext()
6930 {
6931  try
6932  {
6933  __SUP_COUT__ << "Attempting test activation of the context group." << __E__;
6934  ConfigurationManager cfgMgr; // create instance to activate saved groups
6935  }
6936  catch(const std::runtime_error& e)
6937  {
6938  __SUP_COUT_WARN__
6939  << "The test activation of the context group failed. Ignoring error: \n"
6940  << e.what() << __E__;
6941  }
6942  catch(...)
6943  {
6944  __SUP_COUT_WARN__ << "The test activation of the context group failed. Ignoring."
6945  << __E__;
6946  }
6947  return;
6948 
6950  // below has been used for debugging.
6951 
6952  // behave like a user
6953  // start with top level xdaq context
6954  // then add and delete rows proof-of-concept
6955  // export xml xdaq config file
6956 
6959  // behave like a new user
6960  //
6961  // ConfigurationManagerRW cfgMgrInst("ExampleUser");
6962  //
6963  // ConfigurationManagerRW* cfgMgr =& cfgMgrInst;
6964 
6965  // std::map<std::string, TableVersion> groupMembers;
6966  // groupMembers["DesktopIcon"] = TableVersion(2);
6967  // cfgMgr->saveNewTableGroup("test",
6968  // groupMembers, "test comment");
6969 
6970  // //
6971  // const std::map<std::string, TableInfo>& allTableInfo =
6972  // cfgMgr->getAllTableInfo(true);
6973  // __SUP_COUT__ << "allTableInfo.size() = " << allTableInfo.size() << __E__;
6974  // for(auto& mapIt : allTableInfo)
6975  // {
6976  // __SUP_COUT__ << "Config Name: " << mapIt.first << __E__;
6977  // __SUP_COUT__ << "\t\tExisting Versions: " << mapIt.second.versions_.size() <<
6978  //__E__;
6979  //
6980  // //get version key for the current system table key
6981  // for (auto& v:mapIt.second.versions_)
6982  // {
6983  // __SUP_COUT__ << "\t\t" << v << __E__;
6984  // }
6985  // }
6986 
6987  // testXDAQContext just a test bed for navigating the new config tree
6988  // cfgMgr->testXDAQContext();
6989 
6992 }
static xdaq::Application * instantiate(xdaq::ApplicationStub *s)