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