otsdaq_utilities  v2_05_02_indev
LogbookSupervisor.cc
1 #include "otsdaq-utilities/Logbook/LogbookSupervisor.h"
2 //#include "otsdaq/MessageFacility/MessageFacility.h"
3 //#include "otsdaq/Macros/CoutMacros.h"
4 //#include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
5 //#include "otsdaq/XmlUtilities/HttpXmlDocument.h"
6 //#include "otsdaq/SOAPUtilities/SOAPUtilities.h"
7 //#include "otsdaq/SOAPUtilities/SOAPParameters.h"
8 //
9 //#include <xdaq/NamespaceURI.h>
10 //
11 //#include <iostream>
12 //#include <fstream>
13 //#include <string>
14 #include <dirent.h> //for DIR
15 #include <sys/stat.h> //for mkdir
16 //#include <thread> // std::thread
17 
18 using namespace ots;
19 
20 const std::string LOGBOOK_PATH = __ENV__("LOGBOOK_DATA_PATH") + std::string("/");
21 #define LOGBOOK_EXPERIMENT_LIST_PATH LOGBOOK_PATH + "experiment_list.xml"
22 #define LOGBOOK_EXPERIMENT_DIR_PREFACE "log_"
23 #define LOGBOOK_UPLOADS_PATH "uploads/" // within experiment directory
24 #define LOGBOOK_LOGBOOKS_PATH "logbooks/"
25 #define LOGBOOK_PREVIEWS_PATH "previews/"
26 #define LOGBOOK_FILE_PREFACE "entries_"
27 #define LOGBOOK_FILE_EXTENSION ".xml"
28 
29 #define ACTIVE_EXPERIMENT_PATH LOGBOOK_PATH + "active_experiment.txt"
30 #define REMOVE_EXPERIMENT_LOG_PATH LOGBOOK_PATH + "removed_experiments.log"
31 
32 #define XML_ADMIN_STATUS "logbook_admin_status"
33 #define XML_STATUS "logbook_status"
34 #define XML_MOST_RECENT_DAY "most_recent_day"
35 #define XML_EXPERIMENTS_ROOT "experiments"
36 #define XML_EXPERIMENT "experiment"
37 #define XML_ACTIVE_EXPERIMENT "active_experiment"
38 #define XML_EXPERIMENT_CREATE "create_time"
39 #define XML_EXPERIMENT_CREATOR "creator"
40 
41 #define XML_LOGBOOK_ENTRY "logbook_entry"
42 #define XML_LOGBOOK_ENTRY_SUBJECT "logbook_entry_subject"
43 #define XML_LOGBOOK_ENTRY_TEXT "logbook_entry_text"
44 #define XML_LOGBOOK_ENTRY_FILE "logbook_entry_file"
45 #define XML_LOGBOOK_ENTRY_TIME "logbook_entry_time"
46 #define XML_LOGBOOK_ENTRY_CREATOR "logbook_entry_creator"
47 #define XML_LOGBOOK_ENTRY_HIDDEN "logbook_entry_hidden"
48 #define XML_LOGBOOK_ENTRY_HIDER "logbook_entry_hider"
49 #define XML_LOGBOOK_ENTRY_HIDDEN_TIME "logbook_entry_hidden_time"
50 
51 #define XML_PREVIEW_INDEX "preview_index"
52 #define LOGBOOK_PREVIEW_FILE "preview.xml"
53 #define LOGBOOK_PREVIEW_UPLOAD_PREFACE "upload_"
54 
55 XDAQ_INSTANTIATOR_IMPL(LogbookSupervisor)
56 
57 #undef __MF_SUBJECT__
58 #define __MF_SUBJECT__ "Logbook"
59 
60 //==============================================================================
61 // sendmail ~~
62 // Helper function to send emails to the subscriber list of the active experiment
63 int sendmail(const char* to, const char* from, const char* subject, const char* message)
64 {
65  int retval = -1;
66  FILE* mailpipe = popen("/usr/lib/sendmail -t", "w");
67  if(mailpipe != NULL)
68  {
69  fprintf(mailpipe, "To: %s\n", to);
70  fprintf(mailpipe, "From: %s\n", from);
71  fprintf(mailpipe, "Subject: %s\n\n", subject);
72  fwrite(message, 1, strlen(message), mailpipe);
73  fwrite(".\n", 1, 2, mailpipe);
74  pclose(mailpipe);
75  retval = 0;
76  }
77  else
78  {
79  perror("Failed to invoke sendmail");
80  }
81  return retval;
82 }
83 
84 //==============================================================================
85 LogbookSupervisor::LogbookSupervisor(xdaq::ApplicationStub* stub)
86  : CoreSupervisorBase(stub)
87  , allowedFileUploadTypes_({"image/png",
88  "image/jpeg",
89  "image/gif",
90  "image/bmp",
91  "application/pdf",
92  "application/zip",
93  "text/plain"}) // init allowed file upload types
94  , matchingFileUploadTypes_({"png",
95  "jpeg",
96  "gif",
97  "bmp",
98  "pdf",
99  "zip",
100  "txt"}) // init allowed file upload types
101 {
102  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
103 
104  // xgi::bind (this, &LogbookSupervisor::Default, "Default" );
105  // xgi::bind (this, &LogbookSupervisor::Log, "Log" );
106  // xgi::bind (this, &LogbookSupervisor::LogImage, "LogImage" );
107  // xgi::bind (this, &LogbookSupervisor::LogReport, "LogReport" );
108 
109  xoap::bind(this,
110  &LogbookSupervisor::MakeSystemLogbookEntry,
111  "MakeSystemLogbookEntry",
112  XDAQ_NS_URI);
113 
114  init();
115 
116  // TODO allow admins to subscribe to active experiment alerts using System messages (and email)
117 } //end constructor()
118 
119 //==============================================================================
120 LogbookSupervisor::~LogbookSupervisor(void) { destroy(); }
121 
122 //==============================================================================
123 void LogbookSupervisor::init(void)
124 {
125  // called by constructor
126  // allSupervisorInfo_.init(getApplicationContext());
127 
128  if(1) // check if LOGBOOK_PATH and subpaths event exist?! (if not, attempt to create)
129  {
130  std::string path = LOGBOOK_PATH;
131  DIR* dir = opendir(path.c_str());
132  if(dir)
133  closedir(dir);
134  else if(-1 == mkdir(path.c_str(), 0755))
135  {
136  // lets create the service folder (for first time)
137  std::stringstream ss;
138  ss << __COUT_HDR_FL__ << "Service directory creation failed: " << path
139  << std::endl;
140  __SS_THROW__;
141  }
142 
143  path = LOGBOOK_PATH + LOGBOOK_UPLOADS_PATH;
144  dir = opendir(path.c_str());
145  if(dir)
146  closedir(dir);
147  else if(-1 == mkdir((path).c_str(), 0755))
148  {
149  // lets create the service folder (for first time)
150  __SS__ << "Service directory creation failed: " << path << std::endl;
151  __SS_THROW__;
152  }
153 
154  path = LOGBOOK_PATH + LOGBOOK_LOGBOOKS_PATH;
155  dir = opendir(path.c_str());
156  if(dir)
157  closedir(dir);
158  else if(-1 == mkdir(path.c_str(), 0755))
159  {
160  // lets create the service folder (for first time)
161  __SS__ << "Service directory creation failed: " << path << std::endl;
162  __SS_THROW__;
163  }
164  }
165 
166  getActiveExperiment(); // init active experiment
167  __COUT__ << "Active Experiment is " << activeExperiment_ << std::endl;
168  mostRecentDayIndex_ = 0;
169 }
170 
171 //==============================================================================
172 void LogbookSupervisor::destroy(void)
173 {
174  // called by destructor
175 }
176 
177 //==============================================================================
178 void LogbookSupervisor::defaultPage(xgi::Input* /*in*/, xgi::Output* out)
179 {
180  __COUT__ << " active experiment " << activeExperiment_ << std::endl;
181  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
182  "src='/WebPath/html/Logbook.html?urn="
183  << this->getApplicationDescriptor()->getLocalId()
184  << "&active_experiment=" << activeExperiment_ << "'></frameset></html>";
185 }
186 
187 //==============================================================================
188 // setSupervisorPropertyDefaults
189 // override to set defaults for supervisor property values (before user settings
190 // override)
191 void LogbookSupervisor::setSupervisorPropertyDefaults()
192 {
193  CorePropertySupervisorBase::setSupervisorProperty(
194  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
195  std::string() +
196  "*=1 | CreateExperiment=-1 | RemoveExperiment=-1 | GetExperimentListAdmin=-1 "
197  "| SetActiveExperiment=-1" +
198  " | AdminRemoveRestoreEntry=-1");
199 }
200 
201 //==============================================================================
202 // forceSupervisorPropertyValues
203 // override to force supervisor property values (and ignore user settings)
204 void LogbookSupervisor::forceSupervisorPropertyValues()
205 {
206  CorePropertySupervisorBase::setSupervisorProperty(
207  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
208  "RefreshLogbook");
209  CorePropertySupervisorBase::setSupervisorProperty(
210  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NonXMLRequestTypes,
211  "LogImage | LogReport");
212  // CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NeedUsernameRequestTypes,
213  // "CreateExperiment | RemoveExperiment | PreviewEntry |
214  // AdminRemoveRestoreEntry");
215 }
216 
217 //==============================================================================
218 // request
219 // Handles Web Interface requests to Logbook supervisor.
220 // Does not refresh cookie for automatic update checks.
221 void LogbookSupervisor::request(const std::string& requestType,
222  cgicc::Cgicc& cgiIn,
223  HttpXmlDocument& xmlOut,
224  const WebUsers::RequestUserInfo& userInfo)
225 {
226  // Commands
227  // CreateExperiment
228  // RemoveExperiment
229  // GetExperimentList
230  // SetActiveExperiment
231  // RefreshLogbook
232  // PreviewEntry
233  // ApproveEntry
234  // AdminRemoveRestoreEntr
235 
236  //
237  // cgicc::Cgicc cgiIn(in);
238  // std::string requestType;
239  // if((requestType = CgiDataUtilities::postData(cgiIn,"RequestType")) == "")
240  // requestType = cgiIn("RequestType"); //get command from form, if PreviewEntry
241  //
242  // __COUT__ << "requestType " << requestType << " files: " << cgiIn.getFiles().size()
243  //<< std::endl;
244  //
245  //
246  // HttpXmlDocument xmlOut;
247  // uint64_t activeSessionIndex;
248  // std::string user;
249  // uint8_t userPermissions;
250  //
251  // //**** start LOGIN GATEWAY CODE ***//
252  // {
253  // bool automaticCommand = requestType == "RefreshLogbook"; //automatic commands
254  // should not refresh cookie code.. only user initiated commands should! bool
255  // checkLock = true; bool getUser = (requestType == "CreateExperiment") ||
256  //(requestType == "RemoveExperiment") ||
257  // (requestType == "PreviewEntry") || (requestType ==
258  //"AdminRemoveRestoreEntry"); bool requireLock = false;
259  //
260  // if(!theRemoteWebUsers_.xmlRequestToGateway(
261  // cgiIn,
262  // out,
263  // &xmlOut,
264  // allSupervisorInfo_,
265  // &userPermissions, //acquire user's access level (optionally null
266  // pointer) !automaticCommand, //true/false refresh cookie
267  // code 1, //set access level requirement to pass gateway
268  // checkLock, //true/false enable check that system is
269  // unlocked or this user has the lock requireLock,
270  // //true/false requires this user has the lock to proceed
271  // 0,//&userWithLock,
273  // (getUser?&user:0) //acquire username of this user
274  //(optionally
275  // null pointer) ,0//,&displayName //acquire user's Display
276  // Name
277  // ,&activeSessionIndex //acquire user's session index associated
278  // with the cookieCode
279  // ))
280  // { //failure
281  // __COUT__ << "Failed Login Gateway: " <<
282  // out->str() << std::endl; //print out return string on failure
283  // return;
284  // }
285  // }
286  // //**** end LOGIN GATEWAY CODE ***//
287 
288  // to report to logbook admin status use
289  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,tempStr);
290 
291  if(requestType == "CreateExperiment")
292  {
293  // check that experiment directory does not exist, and it is not in xml list
294  // create experiment
295  // create directory
296  // add to experiments list
297  //
298  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
299  // {
300  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
301  // permissions."); goto CLEANUP;
302  // }
303  //
304  // //user is admin
305  //
306  // __COUT__ << "Admin" << std::endl;
307 
308  // get creator name
309  std::string creator = userInfo.username_;
310 
311  createExperiment(
312  CgiDataUtilities::postData(cgiIn, "Experiment"), creator, &xmlOut);
313 
314  __COUT__ << "Created" << std::endl;
315  }
316  else if(requestType == "RemoveExperiment")
317  {
318  // remove from xml list, but do not remove directory (requires manual delete so
319  // mistakes aren't made)
320  //
321  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
322  // {
323  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
324  // permissions."); goto CLEANUP;
325  // }
326 
327  // get remover name
328  std::string remover = userInfo.username_;
329  removeExperiment(
330  CgiDataUtilities::postData(cgiIn, "Experiment"), remover, &xmlOut);
331  }
332  else if(requestType == "GetExperimentList")
333  {
334  // remove from xml list, but do not remove directory (requires manual delete so
335  // mistakes aren't made)
336  if(userInfo.permissionLevel_ >=
337  CoreSupervisorBase::getSupervisorPropertyUserPermissionsThreshold(
338  "GetExperimentListAdmin"))
339  {
340  xmlOut.addTextElementToData("is_admin", "0"); // indicate not an admin
341  return;
342  }
343  // else
344 
345  xmlOut.addTextElementToData("is_admin", "1"); // indicate not an admin
346  getExperiments(&xmlOut);
347  }
348  else if(requestType == "SetActiveExperiment")
349  {
350  // check that experiment exists
351  // set active experiment
352 
353  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
354  // {
355  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
356  // permissions."); goto CLEANUP;
357  // }
358 
359  webUserSetActiveExperiment(CgiDataUtilities::postData(cgiIn, "Experiment"),
360  &xmlOut);
361  }
362  else if(requestType == "RefreshLogbook")
363  {
364  // returns logbook for currently active experiment based on date and duration
365  // parameters
366 
367  std::string Date = CgiDataUtilities::postData(cgiIn, "Date");
368  std::string Duration = CgiDataUtilities::postData(cgiIn, "Duration");
369 
370  time_t date;
371  unsigned char duration;
372  sscanf(Date.c_str(), "%li", &date); // scan for unsigned long
373  sscanf(Duration.c_str(), "%hhu", &duration); // scan for unsigned char
374 
375  __COUT__ << "date " << date << " duration " << (int)duration << std::endl;
376  std::stringstream str;
377  refreshLogbook(date, duration, &xmlOut, (std::ostringstream*)&str);
378  __COUT__ << str.str() << std::endl;
379  }
380  else if(requestType == "PreviewEntry")
381  {
382  // cleanup temporary folder
383  // NOTE: all input parameters for PreviewEntry will be attached to form
384  // so use cgiIn(xxx) to get values.
385  // increment number for each temporary preview, previewPostTempIndex_
386  // save entry and uploads to previewPath / previewPostTempIndex_ /.
387 
388  cleanUpPreviews();
389  std::string EntryText = cgiIn("EntryText");
390  __COUT__ << "EntryText " << EntryText << std::endl << std::endl;
391  std::string EntrySubject = cgiIn("EntrySubject");
392  __COUT__ << "EntrySubject " << EntrySubject << std::endl << std::endl;
393 
394  // get creator name
395  std::string creator = userInfo.username_;
396 
397  savePostPreview(EntrySubject, EntryText, cgiIn.getFiles(), creator, &xmlOut);
398  // else xmlOut.addTextElementToData(XML_STATUS,"Failed - could not get username
399  // info.");
400  }
401  else if(requestType == "ApproveEntry")
402  {
403  // If Approve = "1", then previewed Log entry specified by PreviewNumber
404  // is moved to logbook
405  // Else the specified Log entry is deleted.
406  std::string PreviewNumber = CgiDataUtilities::postData(cgiIn, "PreviewNumber");
407  std::string Approve = CgiDataUtilities::postData(cgiIn, "Approve");
408 
409  movePreviewEntry(PreviewNumber, Approve == "1", &xmlOut);
410  }
411  else if(requestType == "AdminRemoveRestoreEntry")
412  {
413  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
414  // {
415  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
416  // permissions."); goto CLEANUP;
417  // }
418 
419  std::string EntryId = CgiDataUtilities::postData(cgiIn, "EntryId");
420  bool Hide = CgiDataUtilities::postData(cgiIn, "Hide") == "1" ? true : false;
421 
422  // get creator name
423  std::string hider = userInfo.username_;
424 
425  hideLogbookEntry(EntryId, Hide, hider);
426 
427  xmlOut.addTextElementToData(XML_ADMIN_STATUS, "1"); // success
428  }
429  else
430  __COUT__ << "requestType request not recognized." << std::endl;
431 }
432 
433 //==============================================================================
434 // request
435 // Handles Web Interface requests to Logbook supervisor.
436 // Does not refresh cookie for automatic update checks.
437 void LogbookSupervisor::nonXmlRequest(const std::string& requestType,
438  cgicc::Cgicc& cgiIn,
439  std::ostream& out,
440  const WebUsers::RequestUserInfo& /*userInfo*/)
441 {
442  // Commands
443  // LogImage
444  // LogReport
445 
446  if(requestType == "LogImage")
447  {
448  std::string src = CgiDataUtilities::getData(cgiIn, "src");
449  __COUT__ << " Get Log Image " << src << std::endl;
450 
451  out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
452  "src='/WebPath/html/LogbookImage.html?urn="
453  << this->getApplicationDescriptor()->getLocalId() << "&src=" << src
454  << "'></frameset></html>";
455  }
456  else if(requestType == "LogReport")
457  {
458  std::string activeExperiment =
459  CgiDataUtilities::getData(cgiIn, "activeExperiment");
460  __COUT__ << " Start Log Report for " << activeExperiment << std::endl;
461 
462  out << "<!DOCTYPE HTML><html lang='en'><header><title>ots Logbook "
463  "Reports</title></header><frameset col='100%' row='100%'><frame "
464  "src='/WebPath/html/LogbookReport.html?urn="
465  << this->getApplicationDescriptor()->getLocalId()
466  << "&activeExperiment=" << activeExperiment << "'></frameset></html>";
467  }
468  else
469  __COUT__ << "requestType request not recognized." << std::endl;
470 }
471 
472 //==============================================================================
473 // xoap::MakeSystemLogbookEntry
474 // make a system logbook entry into active experiment's logbook from Supervisor only
475 // TODO: (how to enforce?)
476 xoap::MessageReference LogbookSupervisor::MakeSystemLogbookEntry(
477  xoap::MessageReference msg)
478 {
479  SOAPParameters parameters("EntryText");
480  // SOAPParametersV parameters(1);
481  // parameters[0].setName("EntryText");
482  SOAPUtilities::receive(msg, parameters);
483  std::string EntryText = parameters.getValue("EntryText");
484 
485  __COUT__ << "Received External Supervisor System Entry " << EntryText << std::endl;
486  __COUT__ << "Active Experiment is " << activeExperiment_ << std::endl;
487 
488  std::string retStr = "Success";
489 
490  std::string logPath,
491  logDirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
492  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + activeExperiment_;
493 
494  char dayIndexStr[20];
495  HttpXmlDocument logXml;
496  char fileIndex[40];
497  xercesc::DOMElement* entryEl;
498  DIR* dir;
499 
500  if(activeExperiment_ == "")
501  {
502  retStr = "Warning - Currently, no Active Experiment.";
503  __COUT__ << retStr << std::endl;
504  goto XOAP_CLEANUP;
505  }
506 
507  // check that directory exists
508  dir = opendir(logDirPath.c_str());
509  if(!dir)
510  {
511  retStr = "Error - Active Experiment directory missing.";
512  __COUT__ << retStr << std::endl;
513  goto XOAP_CLEANUP;
514  }
515  closedir(dir);
516 
517  sprintf(dayIndexStr, "%6.6lu", time(0) / (60 * 60 * 24)); // get today's index
518 
519  logPath = logDirPath + "/" + LOGBOOK_FILE_PREFACE + activeExperiment_ + "_" +
520  (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
521  __COUT__ << "logPath " << logPath << std::endl;
522 
523  logXml.loadXmlDocument(logPath); // NOTE: on failure, no need to do anything
524  // because empty XML file is valid structure
525  // entry structure:
526  // <XML_LOGBOOK_ENTRY>
527  // <XML_LOGBOOK_ENTRY_TIME>
528  // <XML_LOGBOOK_ENTRY_CREATOR>
529  // <XML_LOGBOOK_ENTRY_TEXT>
530  // <XML_LOGBOOK_ENTRY_FILE value=fileType0>
531  // <XML_LOGBOOK_ENTRY_FILE value=fileType1> ...
532  // </XML_LOGBOOK_ENTRY>
533 
534  entryEl = logXml.addTextElementToData(XML_LOGBOOK_ENTRY);
535 
536  sprintf(fileIndex,
537  "%lu_%lu",
538  time(0),
539  clock()); // create unique time label for entry time(0)_clock()
540  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_TIME, fileIndex, entryEl);
541  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_CREATOR, "SYSTEM LOG", entryEl);
542  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_TEXT, EntryText, entryEl);
543  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_SUBJECT, "System Log", entryEl);
544 
545  logXml.saveXmlDocument(logPath);
546 
547 XOAP_CLEANUP:
548 
549  // fill return parameters
550  SOAPParameters retParameters("Status", retStr);
551  // SOAPParametersV retParameters(1);
552  // retParameters[0].setName("Status");
553  // retParameters[0].setValue(retStr);
554 
555  return SOAPUtilities::makeSOAPMessageReference("LogbookEntryStatusResponse",
556  retParameters);
557 }
558 
559 //
566 // void LogbookSupervisor::LogImage(xgi::Input * in, xgi::Output * out )
567 // throw (xgi::exception::Exception)
568 //{
569 // cgicc::Cgicc cgiIn(in);
570 // std::string src = CgiDataUtilities::getData(cgiIn,"src");
571 // __COUT__ << " Get Log Image " << src << std::endl;
572 // *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame
573 // src='/WebPath/html/LogbookImage.html?urn=" <<
574 // this->getApplicationDescriptor()->getLocalId() << "&src=" << src <<
575 //"'></frameset></html>";
576 //}
577 //
584 // void LogbookSupervisor::LogReport(xgi::Input * in, xgi::Output * out )
585 // throw (xgi::exception::Exception)
586 //{
587 // cgicc::Cgicc cgiIn(in);
588 // std::string activeExperiment = CgiDataUtilities::getData(cgiIn,"activeExperiment");
589 // __COUT__ << " Start Log Report for " << activeExperiment << std::endl;
590 // *out << "<!DOCTYPE HTML><html lang='en'><header><title>ots Logbook
591 // Reports</title></header><frameset col='100%' row='100%'><frame
592 // src='/WebPath/html/LogbookReport.html?urn=" <<
593 // this->getApplicationDescriptor()->getLocalId() << "&activeExperiment=" <<
594 // activeExperiment << "'></frameset></html>";
595 //}
596 
597 //==============================================================================
598 // getActiveExperiment
599 // load active experiment from txt file, must be first line in file
600 std::string LogbookSupervisor::getActiveExperiment()
601 {
602  FILE* fp = fopen(std::string((std::string)ACTIVE_EXPERIMENT_PATH).c_str(), "r");
603  if(!fp)
604  activeExperiment_ = "";
605  else
606  {
607  char line[100];
608  if(!fgets(line, 100, fp))
609  line[0] =
610  '\0'; // if null returned, file is empty and line is untouched, so touch.
611  fclose(fp);
612 
613  // remove \n \r
614  if(line[strlen(line) - 2] == '\r')
615  line[strlen(line) - 2] = '\0';
616  else if(line[strlen(line) - 1] == '\n')
617  line[strlen(line) - 1] = '\0';
618 
619  activeExperiment_ = line;
620  }
621 
622  return activeExperiment_;
623 }
624 
625 //==============================================================================
626 // setActiveExperiment
627 // "" means no experiment is active
628 void LogbookSupervisor::setActiveExperiment(std::string experiment)
629 {
630  FILE* fp = fopen(std::string((std::string)ACTIVE_EXPERIMENT_PATH).c_str(), "w");
631  if(!fp)
632  {
633  __COUT__ << "FATAL ERROR!!! - file write" << std::endl;
634  return;
635  }
636 
637  fprintf(fp, "%s", experiment.c_str());
638  fclose(fp);
639 
640  if(activeExperiment_ != "" &&
641  activeExperiment_ != experiment) // old active experiment is on its way out
642  theRemoteWebUsers_.makeSystemLogbookEntry(
643  "Experiment was made inactive."); // make system logbook entry
644 
645  bool entryNeeded = false;
646  if(experiment != "" &&
647  activeExperiment_ != experiment) // old active experiment is on its way out
648  entryNeeded = true;
649 
650  activeExperiment_ = experiment;
651  __COUT__ << "Active Experiment set to " << activeExperiment_ << std::endl;
652 
653  if(entryNeeded)
654  theRemoteWebUsers_.makeSystemLogbookEntry(
655  "Experiment was made active."); // make system logbook entry
656 }
657 
658 //==============================================================================
659 // validateExperimentName
660 // remove all chars that are not alphanumeric, dashes, or underscores
661 bool LogbookSupervisor::validateExperimentName(std::string& exp)
662 {
663  if(exp.length() < EXPERIMENT_NAME_MIN_LENTH ||
664  exp.length() > EXPERIMENT_NAME_MAX_LENTH)
665  return false;
666  for(int i = 0; i < (int)exp.length(); ++i)
667  if(!((exp[i] >= 'a' && exp[i] <= 'z') || (exp[i] >= 'A' && exp[i] <= 'Z') ||
668  (exp[i] >= '0' && exp[i] <= '9') || (exp[i] == '-' || exp[i] == '_')))
669  {
670  exp = exp.substr(0, i) + exp.substr(i + 1);
671  --i;
672  } // remove illegal chars and rewind i
673 
674  return true;
675 }
676 
677 //==============================================================================
678 // getExperiments
679 // if xmlOut, then output experiments to xml
680 // if out, then output to stream
681 void LogbookSupervisor::getExperiments(HttpXmlDocument* xmlOut, std::ostringstream* out)
682 {
683  // check that experiment listing doesn't already exist
684  HttpXmlDocument expXml;
685  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
686  {
687  __COUT__ << "Fatal Error - Experiment database." << std::endl;
688  __COUT__ << "Creating empty experiment database." << std::endl;
689 
690  expXml.addTextElementToData((std::string)XML_EXPERIMENTS_ROOT);
691  expXml.saveXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH);
692  return;
693  }
694 
695  std::vector<std::string> exps;
696  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
697 
698  if(xmlOut)
699  xmlOut->addTextElementToData(XML_ACTIVE_EXPERIMENT, activeExperiment_);
700 
701  for(unsigned int i = 0; i < exps.size(); ++i) // loop experiments
702  {
703  if(xmlOut)
704  xmlOut->addTextElementToData(XML_EXPERIMENT, exps[i]);
705  if(out)
706  *out << exps[i] << std::endl;
707  }
708 }
709 
710 //==============================================================================
711 // createExperiment
712 void LogbookSupervisor::createExperiment(std::string experiment,
713  std::string creator,
714  HttpXmlDocument* xmlOut)
715 {
716  if(!validateExperimentName(experiment))
717  {
718  if(xmlOut)
719  xmlOut->addTextElementToData(
720  XML_ADMIN_STATUS, "Error - Experiment name must be 3-25 characters.");
721  return;
722  }
723 
724  __COUT__ << "experiment " << experiment << std::endl;
725 
726  // check that directory doesn't already exist
727  std::string dirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
728  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + experiment;
729 
730  __COUT__ << "dirPath " << dirPath << std::endl;
731 
732  bool directoryExists = false;
733  DIR* dir = opendir(dirPath.c_str());
734  if(dir)
735  {
736  closedir(dir);
737  directoryExists = true;
738  }
739 
740  // check that experiment listing doesn't already exist
741  HttpXmlDocument expXml;
742  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
743  {
744  if(xmlOut)
745  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
746  "Fatal Error - Experiment database.");
747  return;
748  }
749 
750  std::vector<std::string> exps;
751  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
752 
753  for(unsigned int i = 0; i < exps.size(); ++i)
754  if(experiment == exps[i])
755  {
756  if(xmlOut)
757  xmlOut->addTextElementToData(
758  XML_ADMIN_STATUS,
759  "Failed - Experiment, " + experiment + ", already exists.");
760  return;
761  }
762  __COUT__ << "experiments count: " << exps.size() << std::endl;
763 
764  // everything checks out, add experiment!
765  // add to experiments xml doc and save
766  // <experiments>
767  // ...
768  // <experiment_name = "xx">
769  // <create_time = "##"> <who_created = "aa">
770  xercesc::DOMElement* expEl =
771  expXml.addTextElementToParent(XML_EXPERIMENT, experiment, XML_EXPERIMENTS_ROOT);
772  char createTime[20];
773  sprintf(createTime, "%lu", time(0));
774  expXml.addTextElementToParent(XML_EXPERIMENT_CREATE, createTime, expEl);
775  expXml.addTextElementToParent(XML_EXPERIMENT_CREATOR, creator, expEl);
776  expXml.saveXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH);
777 
778  // create directory only if doesn't already exist
779  if(directoryExists)
780  {
781  // check uploads folder
782  dirPath += "/" + (std::string)LOGBOOK_UPLOADS_PATH;
783  __COUT__ << "Checking uploads directory" << std::endl;
784 
785  directoryExists = false;
786  dir = opendir(dirPath.c_str());
787  if(!dir) // check if uploads directory exists within experiment directory
788  {
789  __COUT__ << "Creating uploads directory" << std::endl;
790  if(-1 == mkdir(dirPath.c_str(), 0755)) // make uploads directory
791  {
792  if(xmlOut)
793  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
794  "Failed - uploads directory for " +
795  experiment + " was not created.");
796  __COUT__ << "Uploads directory failure." << std::endl;
797  return;
798  }
799  }
800  else
801  closedir(dir);
802 
803  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
804  "Directory already exists for " + experiment +
805  ", re-added to list of experiments.");
806  return;
807  }
808  __COUT__ << "Creating experiment and uploads directory at: " << dirPath << std::endl;
809  if(-1 == mkdir(dirPath.c_str(), 0755) ||
810  -1 == mkdir((dirPath + "/" + (std::string)LOGBOOK_UPLOADS_PATH).c_str(), 0755))
811  {
812  if(xmlOut)
813  xmlOut->addTextElementToData(
814  XML_ADMIN_STATUS,
815  "Failed - directory, " + experiment + ", could not be created.");
816  return;
817  }
818 
819  if(xmlOut)
820  xmlOut->addTextElementToData(
821  XML_ADMIN_STATUS, "Experiment, " + experiment + ", successfully created.");
822 }
823 
824 //==============================================================================
825 // webUserSetActiveExperiment
826 // if experiment exists, set as active
827 // to clear active experiment set to ""
828 void LogbookSupervisor::webUserSetActiveExperiment(std::string experiment,
829  HttpXmlDocument* xmlOut)
830 {
831  if(experiment == "") // clear active experiment
832  {
833  setActiveExperiment(experiment);
834  if(xmlOut)
835  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
836  "Active experiment cleared successfully.");
837  }
838 
839  // check that experiment listing exists
840  HttpXmlDocument expXml;
841  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
842  {
843  if(xmlOut)
844  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
845  "Fatal Error - Experiment database.");
846  return;
847  }
848  std::vector<std::string> exps;
849  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
850 
851  unsigned int i;
852  for(i = 0; i < exps.size(); ++i)
853  if(experiment == exps[i])
854  break;
855 
856  if(i == exps.size()) // not found
857  {
858  if(xmlOut)
859  xmlOut->addTextElementToData(
860  XML_ADMIN_STATUS, "Failed - Experiment, " + experiment + ", not found.");
861  return;
862  }
863 
864  // found!
865  setActiveExperiment(experiment);
866  if(xmlOut)
867  xmlOut->addTextElementToData(
868  XML_ADMIN_STATUS,
869  "Active experiment set to " + experiment + " successfully.");
870 }
871 
872 //==============================================================================
873 // removeExperiment
874 // remove experiment from listing only (do NOT remove logbook data directory)
875 // record remover in log file REMOVE_EXPERIMENT_LOG_PATH
876 void LogbookSupervisor::removeExperiment(std::string experiment,
877  std::string remover,
878  HttpXmlDocument* xmlOut)
879 {
880  __COUT__ << "experiment " << experiment << std::endl;
881 
882  // check that experiment listing exists
883  HttpXmlDocument expXml;
884  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
885  {
886  if(xmlOut)
887  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
888  "Fatal Error - Experiment database.");
889  return;
890  }
891  std::vector<std::string> exps;
892  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
893 
894  unsigned int i;
895  for(i = 0; i < exps.size(); ++i)
896  if(experiment == exps[i])
897  break;
898 
899  if(i == exps.size()) // not found
900  {
901  if(xmlOut)
902  xmlOut->addTextElementToData(
903  XML_ADMIN_STATUS, "Failed - Experiment, " + experiment + ", not found.");
904  return;
905  }
906 
907  // found!
908 
909  // remove experiment from xml
910  xercesc::DOMElement* parent = expXml.getMatchingElement(XML_EXPERIMENTS_ROOT);
911  xercesc::DOMElement* child = expXml.getMatchingElement(XML_EXPERIMENT, i);
912  __COUT__ << "experiments original count: " << expXml.getChildrenCount(parent)
913  << std::endl;
914  expXml.recursiveRemoveChild(child, parent);
915  __COUT__ << "experiments new count: " << expXml.getChildrenCount(parent) << std::endl;
916 
917  // update removed experiments log
918  FILE* fp = fopen(((std::string)REMOVE_EXPERIMENT_LOG_PATH).c_str(), "a");
919  if(!fp)
920  {
921  if(xmlOut)
922  xmlOut->addTextElementToData(XML_ADMIN_STATUS, "Fatal Error - Remove log.");
923  return;
924  }
925  fprintf(fp,
926  "%s -- %s Experiment removed by %s.\n",
927  asctime(localtime(&((time_t const&)(time(0))))),
928  experiment.c_str(),
929  remover.c_str());
930  fclose(fp);
931 
932  expXml.saveXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH); // save database
933 
934  // unset from activeExperiment_ if is active experiment
935  if(activeExperiment_ == experiment)
936  setActiveExperiment(); // clear active experiment
937 
938  if(xmlOut)
939  xmlOut->addTextElementToData(
940  XML_ADMIN_STATUS, "Experiment, " + experiment + ", successfully removed.");
941 }
942 
943 //==============================================================================
944 // refreshLogbook
945 // returns all the logbook data for active experiment from starting date and back in
946 // time for duration total number of days.
947 // e.g. data = today, and duration = 1 returns logbook for today from active
948 // experiment The entries are returns from oldest to newest
949 void LogbookSupervisor::refreshLogbook(time_t date,
950  unsigned char duration,
951  HttpXmlDocument* xmlOut,
952  std::ostringstream* out,
953  std::string experiment)
954 {
955  if(experiment == "")
956  experiment = activeExperiment_; // default to active experiment
957  if(xmlOut)
958  xmlOut->addTextElementToData(XML_ACTIVE_EXPERIMENT, experiment); // for success
959 
960  // check that directory exists
961  std::string dirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
962  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + experiment;
963 
964  if(out)
965  *out << __COUT_HDR_FL__ << "dirPath " << dirPath << std::endl;
966 
967  DIR* dir = opendir(dirPath.c_str());
968  if(!dir)
969  {
970  if(xmlOut)
971  xmlOut->addTextElementToData(
972  XML_STATUS,
973  "Error - Directory for experiment, " + experiment + ", missing.");
974  if(out)
975  *out << __COUT_HDR_FL__ << "Error - Directory missing" << std::endl;
976  return;
977  }
978 
979  unsigned int baseDay;
980 
981  if(!date) // if date is 0 take most recent day and update it
982  {
983  struct dirent* drnt;
984  unsigned int extractedDay;
985  int start, finish; // always find number after last _ and before last .
986 
987  mostRecentDayIndex_ = 0;
988  while((drnt = readdir(dir)))
989  {
990  // if(out) *out << __COUT_HDR_FL__ << "dirContents " << drnt->d_name <<
991  // std::endl;
992 
993  if(strcmp(&(drnt->d_name[strlen(drnt->d_name) - 4]), ".xml"))
994  continue; // skip non logbook files
995 
996  for(finish = strlen(drnt->d_name) - 1; finish > 0; --finish)
997  if(drnt->d_name[finish] == '.')
998  break;
999  if(finish == 0)
1000  {
1001  if(out)
1002  *out << __COUT_HDR_FL__ << "failed to find day index finish "
1003  << std::endl;
1004  return;
1005  }
1006  for(start = finish - 1; start > 0; --start)
1007  if(drnt->d_name[start - 1] == '_')
1008  break;
1009  if(start == 0)
1010  {
1011  if(out)
1012  *out << __COUT_HDR_FL__ << "failed to find day index start "
1013  << std::endl;
1014  return;
1015  }
1016  drnt->d_name[finish] = '\0';
1017  extractedDay = atoi((char*)(&(drnt->d_name[start])));
1018  // if(out) *out << __COUT_HDR_FL__ << "dirContents " << (char
1019  // *)(&(drnt->d_name[start])) << " " << extractedDay << std::endl;
1020  if(!mostRecentDayIndex_ || mostRecentDayIndex_ < extractedDay)
1021  mostRecentDayIndex_ = extractedDay;
1022  }
1023  if(out)
1024  *out << __COUT_HDR_FL__
1025  << "dirContents done, found most recent day: " << mostRecentDayIndex_
1026  << std::endl;
1027 
1028  baseDay = mostRecentDayIndex_;
1029  }
1030  else
1031  baseDay = (date / (60 * 60 * 24));
1032  closedir(dir);
1033 
1034  std::string entryPath;
1035  char dayIndexStr[20];
1036  FILE* fp;
1037 
1038  // read all days selected out
1039  // entries are in file as oldest at top, newest at bottom
1040  // so read oldest files first to have global ordering of old to new
1041  for(unsigned char i = duration; i != 0; --i)
1042  {
1043  sprintf(dayIndexStr, "%6.6u", baseDay - i + 1); // get day index, back in time
1044  entryPath = dirPath + "/" + LOGBOOK_FILE_PREFACE + experiment + "_" +
1045  (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
1046 
1047  if(out)
1048  *out << __COUT_HDR_FL__ << "Directory Entry " << entryPath << std::endl;
1049 
1050  fp = fopen(entryPath.c_str(), "r");
1051  if(!fp)
1052  {
1053  if(out)
1054  *out << __COUT_HDR_FL__ << "File not found" << std::endl;
1055  continue;
1056  }
1057  fclose(fp);
1058 
1059  // file found! read file out
1060 
1061  HttpXmlDocument logXml;
1062  if(!logXml.loadXmlDocument(entryPath))
1063  {
1064  if(xmlOut)
1065  xmlOut->addTextElementToData(
1066  XML_STATUS, "Critical Failure - log did not load. Notify admins.");
1067  if(out)
1068  *out << __COUT_HDR_FL__ << "Failure - log XML did not load" << std::endl;
1069  return;
1070  }
1071 
1072  if(xmlOut)
1073  xmlOut->copyDataChildren(logXml); // copy file to output xml
1074  }
1075 
1076  if(xmlOut)
1077  xmlOut->addTextElementToData(XML_STATUS, "1"); // for success
1078  if(out)
1079  *out << __COUT_HDR_FL__ << "Today: " << time(0) / (60 * 60 * 24) << std::endl;
1080 
1081  sprintf(dayIndexStr, "%lu", time(0) / (60 * 60 * 24) - mostRecentDayIndex_);
1082  if(xmlOut)
1083  xmlOut->addTextElementToData(XML_MOST_RECENT_DAY,
1084  dayIndexStr); // send most recent day index
1085 }
1086 
1087 //==============================================================================
1088 // cleanUpPreviews
1089 // cleanup logbook preview directory
1090 // all names have time_t creation time + "_" + incremented index
1091 void LogbookSupervisor::cleanUpPreviews()
1092 {
1093  std::string previewPath =
1094  (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_PREVIEWS_PATH;
1095 
1096  DIR* dir = opendir(previewPath.c_str());
1097  if(!dir)
1098  {
1099  __COUT__ << "Error - Previews directory missing: " << previewPath << std::endl;
1100  return;
1101  }
1102 
1103  struct dirent* entry;
1104  time_t dirCreateTime;
1105  unsigned int i;
1106 
1107  while(
1108  (entry = readdir(
1109  dir))) // loop through all entries in directory and remove anything expired
1110  {
1111  if(strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0 &&
1112  strcmp(entry->d_name, ".svn") != 0)
1113  {
1114  // replace _ with space so sscanf works
1115  for(i = 0; i < strlen(entry->d_name); ++i)
1116  if(entry->d_name[i] == '_')
1117  {
1118  entry->d_name[i] = ' ';
1119  break;
1120  }
1121  sscanf(entry->d_name, "%li", &dirCreateTime);
1122 
1123  if((time(0) - dirCreateTime) > LOGBOOK_PREVIEW_EXPIRATION_TIME)
1124  {
1125  __COUT__ << "Expired" << std::endl;
1126 
1127  entry->d_name[i] = '_'; // put _ back
1128 
1129  __COUT__ << "rm -rf " << previewPath + (std::string)entry->d_name
1130  << std::endl
1131  << std::endl;
1132  system(
1133  ((std::string)("rm -rf " + previewPath + (std::string)entry->d_name))
1134  .c_str());
1135  }
1136  }
1137  }
1138 
1139  closedir(dir);
1140 }
1141 
1142 //==============================================================================
1143 // savePostPreview
1144 // save post to preview directory named with time and incremented index
1145 void LogbookSupervisor::savePostPreview(std::string& subject,
1146  std::string& text,
1147  const std::vector<cgicc::FormFile>& files,
1148  std::string creator,
1149  HttpXmlDocument* xmlOut)
1150 {
1151  if(activeExperiment_ == "") // no active experiment!
1152  {
1153  if(xmlOut)
1154  xmlOut->addTextElementToData(XML_STATUS,
1155  "Failed - no active experiment currently!");
1156  return;
1157  }
1158 
1159  char fileIndex[40];
1160  sprintf(fileIndex,
1161  "%lu_%lu",
1162  time(0),
1163  clock()); // create unique time label for entry time(0)_clock()
1164  std::string previewPath = (std::string)LOGBOOK_PATH +
1165  (std::string)LOGBOOK_PREVIEWS_PATH + (std::string)fileIndex;
1166 
1167  __COUT__ << "previewPath " << previewPath << std::endl;
1168  if(-1 == mkdir(previewPath.c_str(), 0755))
1169  {
1170  if(xmlOut)
1171  xmlOut->addTextElementToData(XML_STATUS,
1172  "Failed - preview could not be generated.");
1173  return;
1174  }
1175 
1176  // new directory created successfully, save text and files
1177  // entry structure:
1178  // <XML_LOGBOOK_ENTRY>
1179  // <XML_LOGBOOK_ENTRY_TIME>
1180  // <XML_LOGBOOK_ENTRY_CREATOR>
1181  // <XML_LOGBOOK_ENTRY_SUBJECT>
1182  // <XML_LOGBOOK_ENTRY_TEXT>
1183  // <XML_LOGBOOK_ENTRY_FILE value=fileType0>
1184  // <XML_LOGBOOK_ENTRY_FILE value=fileType1> ...
1185  // </XML_LOGBOOK_ENTRY>
1186 
1187  escapeLogbookEntry(text);
1188  escapeLogbookEntry(subject);
1189  __COUT__ << "~~subject " << subject << std::endl
1190  << "~~text " << text << std::endl
1191  << std::endl;
1192 
1193  HttpXmlDocument previewXml;
1194 
1195  previewXml.addTextElementToData(XML_LOGBOOK_ENTRY);
1196  previewXml.addTextElementToParent(
1197  XML_LOGBOOK_ENTRY_TIME, fileIndex, XML_LOGBOOK_ENTRY);
1198  if(xmlOut)
1199  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_TIME, fileIndex); // return time
1200  previewXml.addTextElementToParent(
1201  XML_LOGBOOK_ENTRY_CREATOR, creator, XML_LOGBOOK_ENTRY);
1202  if(xmlOut)
1203  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_CREATOR,
1204  creator); // return creator
1205  previewXml.addTextElementToParent(XML_LOGBOOK_ENTRY_TEXT, text, XML_LOGBOOK_ENTRY);
1206  if(xmlOut)
1207  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_TEXT, text); // return text
1208  previewXml.addTextElementToParent(
1209  XML_LOGBOOK_ENTRY_SUBJECT, subject, XML_LOGBOOK_ENTRY);
1210  if(xmlOut)
1211  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_SUBJECT,
1212  subject); // return subject
1213 
1214  __COUT__ << "file size " << files.size() << std::endl;
1215 
1216  std::string filename;
1217  std::ofstream myfile;
1218  for(unsigned int i = 0; i < files.size(); ++i)
1219  {
1220  previewXml.addTextElementToParent(
1221  XML_LOGBOOK_ENTRY_FILE, files[i].getDataType(), XML_LOGBOOK_ENTRY);
1222  if(xmlOut)
1223  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_FILE,
1224  files[i].getDataType()); // return file type
1225 
1226  if((filename = validateUploadFileType(files[i].getDataType())) ==
1227  "") // invalid file type
1228  {
1229  if(xmlOut)
1230  xmlOut->addTextElementToData(
1231  XML_STATUS,
1232  "Failed - invalid file type, " + files[i].getDataType() + ".");
1233  return;
1234  }
1235 
1236  // file validated, so save upload to temp directory
1237  sprintf(fileIndex, "%d", i);
1238  filename = previewPath + "/" + (std::string)LOGBOOK_PREVIEW_UPLOAD_PREFACE +
1239  (std::string)fileIndex + "." + filename;
1240 
1241  __COUT__ << "file " << i << " - " << filename << std::endl;
1242  myfile.open(filename.c_str());
1243  if(myfile.is_open())
1244  {
1245  files[i].writeToStream(myfile);
1246  myfile.close();
1247  }
1248  }
1249 
1250  // save xml doc for preview entry
1251  previewXml.saveXmlDocument(previewPath + "/" + (std::string)LOGBOOK_PREVIEW_FILE);
1252 
1253  if(xmlOut)
1254  xmlOut->addTextElementToData(XML_STATUS, "1"); // 1 indicates success!
1255  if(xmlOut)
1256  xmlOut->addTextElementToData(XML_PREVIEW_INDEX,
1257  "1"); // 1 indicates is a preview post
1258 }
1259 
1260 //==============================================================================
1261 // movePreviewEntry
1262 // if approve
1263 // move entry to current active logbook
1264 // if not approve
1265 // delete directory
1266 void LogbookSupervisor::movePreviewEntry(std::string previewNumber,
1267  bool approve,
1268  HttpXmlDocument* /*xmlOut*/)
1269 {
1270  __COUT__ << "previewNumber " << previewNumber
1271  << (approve ? " Accepted" : " Cancelled") << std::endl;
1272 
1273  std::string sysCmd, previewPath = (std::string)LOGBOOK_PATH +
1274  (std::string)LOGBOOK_PREVIEWS_PATH + previewNumber;
1275 
1276  if(approve)
1277  {
1278  // move from preview to logbook
1279 
1280  HttpXmlDocument previewXml;
1281  previewXml.loadXmlDocument(previewPath + "/" + (std::string)LOGBOOK_PREVIEW_FILE);
1282 
1283  std::string logPath,
1284  logDirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
1285  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + activeExperiment_;
1286 
1287  // check that directory exists
1288  DIR* dir = opendir(logDirPath.c_str());
1289  if(!dir)
1290  {
1291  __COUT__ << "Error - Active Experiment directory missing: " << logPath
1292  << std::endl;
1293  return;
1294  }
1295  closedir(dir);
1296 
1297  char dayIndexStr[20];
1298  sprintf(dayIndexStr, "%6.6lu", time(0) / (60 * 60 * 24)); // get today's index
1299 
1300  logPath = logDirPath + "/" + LOGBOOK_FILE_PREFACE + activeExperiment_ + "_" +
1301  (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
1302  __COUT__ << "logPath " << logPath << std::endl;
1303 
1304  HttpXmlDocument logXml;
1305  logXml.loadXmlDocument(logPath); // NOTE: on failure, no need to do anything
1306  // because empty XML file is valid structure
1307  // entry structure:
1308  // <XML_LOGBOOK_ENTRY>
1309  // <XML_LOGBOOK_ENTRY_TIME>
1310  // <XML_LOGBOOK_ENTRY_CREATOR>
1311  // <XML_LOGBOOK_ENTRY_TEXT>
1312  // <XML_LOGBOOK_ENTRY_FILE value=fileType0>
1313  // <XML_LOGBOOK_ENTRY_FILE value=fileType1> ...
1314  // </XML_LOGBOOK_ENTRY>
1315 
1316  logXml.copyDataChildren(previewXml); // Copy from previewXML to logXML
1317  logXml.saveXmlDocument(logPath);
1318 
1319  // Move upload files
1320  std::vector<std::string> fileTypes;
1321  previewXml.getAllMatchingValues(XML_LOGBOOK_ENTRY_FILE, fileTypes);
1322  std::string entryTimeLabel = previewXml.getMatchingValue(XML_LOGBOOK_ENTRY_TIME);
1323  std::string fileExtension, previewFilename, logFilename;
1324  char fileIndex[10];
1325  for(unsigned int i = 0; i < fileTypes.size(); ++i)
1326  {
1327  if((fileExtension = validateUploadFileType(fileTypes[i])) ==
1328  "") // invalid file type
1329  {
1330  __COUT__ << "Failed - invalid file type: " << fileTypes[i] << std::endl;
1331  continue;
1332  }
1333 
1334  // file validated, so save upload to temp directory
1335  sprintf(fileIndex, "%d", i);
1336  previewFilename = (std::string)LOGBOOK_PREVIEW_UPLOAD_PREFACE +
1337  (std::string)fileIndex + "." + fileExtension;
1338  logFilename = (std::string)LOGBOOK_PREVIEW_UPLOAD_PREFACE + entryTimeLabel +
1339  "_" + (std::string)fileIndex + "." + fileExtension;
1340 
1341  sysCmd = "mv " + (previewPath + "/" + previewFilename) + " " +
1342  (logDirPath + "/" + (std::string)LOGBOOK_UPLOADS_PATH + logFilename);
1343  __COUT__ << sysCmd << std::endl;
1344  system(sysCmd.c_str());
1345  }
1346  }
1347 
1348  // remove preview directory
1349  sysCmd = "rm -rf " + previewPath;
1350  __COUT__ << sysCmd << std::endl << std::endl;
1351  system(sysCmd.c_str());
1352 }
1353 
1354 //==============================================================================
1355 // validateUploadFileType
1356 // returns "" if file type is invalide, else returns file extension to use
1357 std::string LogbookSupervisor::validateUploadFileType(const std::string fileType)
1358 {
1359  for(unsigned int i = 0; i < allowedFileUploadTypes_.size(); ++i)
1360  if(allowedFileUploadTypes_[i] == fileType)
1361  return matchingFileUploadTypes_[i]; // found and done
1362 
1363  return ""; // not valid, return ""
1364 }
1365 
1366 //==============================================================================
1367 // escapeLogbookEntry
1368 // replace html/xhtml reserved characters with equivalent.
1369 // reserved: ", ', &, <, >, \n, double-space
1370 void LogbookSupervisor::escapeLogbookEntry(std::string& /*entry*/)
1371 {
1372  // NOTE: should already be taken care of by web gui javascript! do we care to check?
1373 }
1374 
1375 //==============================================================================
1376 // hideLogbookEntry
1377 // NOTE: does not actually delete entry, just marks as hidden
1378 // removes/restores logbook entry. Requires admin priveleges
1379 // Locates the entry within the active experiment and if hide
1380 // appends xml fields:
1381 // XML_LOGBOOK_ENTRY_HIDDEN
1382 // XML_LOGBOOK_ENTRY_HIDER
1383 // XML_LOGBOOK_ENTRY_HIDDEN_TIME
1384 void LogbookSupervisor::hideLogbookEntry(const std::string& entryId,
1385  bool hide,
1386  const std::string& hider)
1387 {
1388  __COUT__ << "Hide=" << hide << " for entryid " << entryId << std::endl;
1389 
1390  // get path to entries file for entry at entryId
1391  char dayIndexStr[20];
1392  unsigned int i;
1393  for(i = 0; i < entryId.length(); ++i)
1394  if(entryId[i] == '_')
1395  {
1396  dayIndexStr[i] = '\0';
1397  break;
1398  }
1399  else
1400  dayIndexStr[i] = entryId[i];
1401  time_t days;
1402  sscanf(dayIndexStr, "%li", &days); // get seconds
1403  days /= 60 * 60 * 24; // get days
1404  sprintf(dayIndexStr, "%6.6lu", days);
1405 
1406  std::string logDirPath =
1407  (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
1408  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + activeExperiment_;
1409  std::string logPath = logDirPath + "/" + LOGBOOK_FILE_PREFACE + activeExperiment_ +
1410  "_" + (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
1411 
1412  __COUT__ << "logPath=" << logPath << std::endl;
1413 
1414  // locate entry
1415  HttpXmlDocument logXml;
1416  if(!logXml.loadXmlDocument(logPath))
1417  {
1418  __COUT__ << "Failure - log XML did not load" << std::endl;
1419  return;
1420  }
1421 
1422  std::vector<std::string> allEntryIds;
1423  logXml.getAllMatchingValues(XML_LOGBOOK_ENTRY_TIME, allEntryIds);
1424  for(i = 0; i < allEntryIds.size(); ++i)
1425  if(allEntryIds[i] == entryId)
1426  break;
1427  if(i == allEntryIds.size())
1428  {
1429  __COUT__ << "Failure - entry not found" << std::endl;
1430  return;
1431  }
1432 
1433  __COUT__ << "found " << logXml.getMatchingValue(XML_LOGBOOK_ENTRY_TEXT, i)
1434  << std::endl;
1435 
1436  xercesc::DOMElement *hiddenParentEl, *entryParentEl = logXml.getMatchingElement(
1437  XML_LOGBOOK_ENTRY, i); // get entry element
1438 
1439  // check if already hidden
1440  hiddenParentEl =
1441  logXml.getMatchingElementInSubtree(entryParentEl, XML_LOGBOOK_ENTRY_HIDDEN);
1442 
1443  if(hide) // remove entry
1444  {
1445  if(hiddenParentEl)
1446  {
1447  __COUT__ << "Hidden tag already applied to entry." << std::endl;
1448  return;
1449  }
1450  hiddenParentEl = logXml.addTextElementToParent(
1451  XML_LOGBOOK_ENTRY_HIDDEN,
1452  "1",
1453  entryParentEl); // add hidden parent with value "1"
1454  logXml.addTextElementToParent(
1455  XML_LOGBOOK_ENTRY_HIDER, hider, hiddenParentEl); // hider
1456  sprintf(dayIndexStr, "%lu", time(0));
1457  logXml.addTextElementToParent(
1458  XML_LOGBOOK_ENTRY_HIDDEN_TIME, dayIndexStr, hiddenParentEl); // hide time
1459  }
1460  else // restore entry
1461  {
1462  if(!hiddenParentEl)
1463  {
1464  __COUT__ << "Entry already was not hidden." << std::endl;
1465  return;
1466  }
1467 
1468  logXml.recursiveRemoveChild(hiddenParentEl,
1469  entryParentEl); // remove hidden parent
1470  }
1471  logXml.saveXmlDocument(logPath);
1472  __COUT__ << "Success." << std::endl;
1473 }