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