Open Journal Systems  3.3.0
bagit.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3 
32 require_once __DIR__ . "/../vendor/autoload.php";
33 require_once 'bagit_fetch.php';
34 require_once 'bagit_manifest.php';
35 require_once 'bagit_utils.php';
36 
37 
51 class BagItException extends Exception
52 {
53 
54 }
55 
56 
69 class BagIt
70 {
71 
72  //{{{ properties
73 
80  var $bag;
81 
87  var $bagDirectory;
88 
94  var $extended;
95 
101  var $bagVersion;
102 
108  var $tagFileEncoding;
109 
115  var $bagitFile;
116 
122  var $manifest;
123 
129  var $tagManifest;
130 
136  var $fetch;
137 
144 
150  var $bagInfoData;
151 
158  var $bagCompression;
159 
166  var $bagErrors;
167 
168  //}}}
169 
170  //{{{ Public Methods
171 
190  public function __construct(
191  $bag, $validate=false, $extended=true, $fetch=false, $bagInfoData=null
192  ) {
193  $this->bag = $bag;
194  $this->extended = $extended || (! is_null($bagInfoData));
195  $this->bagVersion = array('major' => 0, 'minor' => 96);
196  $this->tagFileEncoding = 'UTF-8';
197  $this->bagDirectory = null;
198  $this->bagitFile = null;
199  $this->manifest = null;
200  $this->tagManifest = null;
201  $this->fetch = null;
202  $this->bagInfoFile = null;
203  $this->bagInfoData = $bagInfoData;
204  $this->bagCompression = null;
205  $this->bagErrors = array();
206 
207  if (
208  file_exists($this->bag) &&
209  ($this->_isCompressed() || file_exists("{$this->bag}/bagit.txt"))
210  ) {
211  $this->_openBag();
212  } else {
213  $this->_createBag();
214  }
215 
216  if ($fetch) {
217  $this->fetch->download();
218  }
219 
220  if ($validate) {
221  $this->validate();
222  }
223  }
224 
230  public function isValid()
231  {
232  return (count($this->bagErrors) == 0);
233  }
234 
241  function isExtended()
242  {
243  return $this->extended;
244  }
245 
252  function getBagInfo()
253  {
254  $major = $this->bagVersion['major'];
255  $minor = $this->bagVersion['minor'];
256 
257  $info = array(
258  'version' => "$major.$minor",
259  'encoding' => $this->tagFileEncoding,
260  'hash' => $this->getHashEncoding()
261  );
262  return $info;
263  }
264 
270  function getDataDirectory()
271  {
272  return "{$this->bagDirectory}/data";
273  }
274 
280  function getHashEncoding()
281  {
282  return $this->manifest->getHashEncoding();
283  }
284 
293  function setHashEncoding($hashAlgorithm)
294  {
295  $hashAlgorithm = strtolower($hashAlgorithm);
296  if ($hashAlgorithm != 'md5' && $hashAlgorithm != 'sha1') {
297  throw new InvalidArgumentException("Invalid hash algorithim: '$hashAlgorithm'.");
298  }
299 
300  $this->manifest->setHashEncoding($hashAlgorithm);
301  if ($this->tagManifest !== null) {
302  $this->tagManifest->setHashEncoding($hashAlgorithm);
303  }
304  }
305 
312  function getBagContents()
313  {
314  return rls($this->getDataDirectory());
315  }
316 
325  function getBagErrors($validate=false)
326  {
327  if ($validate) {
328  $this->validate();
329  }
330  return $this->bagErrors;
331  }
332 
342  function validate()
343  {
344  $errors = array();
345 
346  BagIt_validateExists($this->bagitFile, $errors);
347  BagIt_validateExists($this->getDataDirectory(), $errors);
348  $this->manifest->validate($errors);
349 
350  $this->bagErrors = $errors;
352  }
353 
370  function update()
371  {
372  // Clear the manifests.
373  $this->manifest->clear();
374  if ($this->tagManifest !== null) {
375  $this->tagManifest->clear();
376  }
377 
378  // Clean up the file names in the data directory.
379  $dataFiles = rls($this->getDataDirectory());
380  foreach ($dataFiles as $dataFile) {
381  $baseName = basename($dataFile);
382  if ($baseName == '.' || $baseName == '..') {
383  continue;
384  }
385 
386  $cleanName = BagIt_sanitizeFileName($baseName);
387  if ($baseName != $cleanName) {
388  $dirName = dirname($dataFile);
389  rename($dataFile, "$dirName/$cleanName");
390  }
391  }
392 
393  if ($this->extended || count($this->bagInfoData) > 0) {
394  $this->_writeBagInfo();
395  }
396 
397  // Update the manifests.
398  $this->manifest->update(rls($this->getDataDirectory()));
399  if ($this->tagManifest !== null) {
400  $bagdir = $this->bagDirectory;
401  $tagFiles = array(
402  "$bagdir/bagit.txt",
403  "$bagdir/bag-info.txt",
404  $this->fetch->fileName,
405  $this->manifest->getFileName()
406  );
407  $this->tagManifest->update($tagFiles);
408  }
409  }
410 
422  function addFile($src, $dest)
423  {
424  $dataPref = 'data' . DIRECTORY_SEPARATOR;
425  $prefLen = strlen($dataPref);
426  if ((strncasecmp($dest, $dataPref, $prefLen) != 0)
427  && (strncasecmp($dest, $dataPref, $prefLen) != 0)
428  ) {
429  $dest = $dataPref . $dest;
430  }
431 
432  $fulldest = "{$this->bagDirectory}/$dest";
433  $dirname = dirname($fulldest);
434  if (! is_dir($dirname)) {
435  mkdir($dirname, 0777, true);
436  }
437 
438  copy($src, $fulldest);
439  }
440 
454  function createFile($content, $dest) {
455  $dataPref = 'data' . DIRECTORY_SEPARATOR;
456  $prefLen = strlen($dataPref);
457  if ((strncasecmp($dest, $dataPref, $prefLen) != 0)) {
458  $dest = $dataPref . $dest;
459  }
460 
461  $fulldest = "{$this->bagDirectory}/$dest";
462 
463  if(file_exists($fulldest)) {
464  throw new BagitException("File already exists: '$dest'");
465  }
466 
467  $dirname = dirname($fulldest);
468  if (! is_dir($dirname)) {
469  mkdir($dirname, 0777, true);
470  }
471  file_put_contents($fulldest, $content);
472  }
473 
474 
483  function package($destination, $method='tgz')
484  {
485  $method = strtolower($method);
486  if ($method != 'zip' && $method != 'tgz') {
487  throw new BagItException("Invalid compression method: '$method'.");
488  }
489 
490  if (substr_compare($destination, ".$method", -4, 4, true) != 0) {
491  $destination = "$destination.$method";
492  }
493 
494  BagIt_compressBag(
495  $this->bagDirectory,
496  $destination,
497  $method
498  );
499  }
500 
509  public function hasBagInfoData($key)
510  {
511  $this->_ensureBagInfoData();
512  return array_key_exists($key, $this->bagInfoData);
513  }
514 
524  public function setBagInfoData($key, $value)
525  {
526  $this->_ensureBagInfoData();
527  $this->bagInfoData[$key] = BagIt_getAccumulatedValue(
528  $this->bagInfoData, $key, $value
529  );
530  }
531 
540  public function clearBagInfoData($key)
541  {
542  if (array_key_exists($key, $this->bagInfoData)) {
543  unset($this->bagInfoData[$key]);
544  }
545  }
546 
555  public function getBagInfoData($key)
556  {
557  $this->_ensureBagInfoData();
558  return array_key_exists($key, $this->bagInfoData) ? $this->bagInfoData[$key] : null;
559  }
560 
561  //}}}
562 
563  //{{{ Private Methods
564 
570  private function _openBag()
571  {
572  if ($this->_isCompressed()) {
573  $this->bagDirectory = BagIt_uncompressBag($this->bag);
574  } else {
575  $this->bagDirectory = realpath($this->bag);
576  }
577 
578  $this->bagitFile = "{$this->bagDirectory}/bagit.txt";
579  list($version, $fileEncoding, $errors) = BagIt_readBagItFile(
580  $this->bagitFile
581  );
582  $this->bagVersion = $version;
583  $this->tagFileEncoding = $fileEncoding;
584  $this->bagErrors = array_merge($this->bagErrors, $errors);
585 
586  $files = scandir($this->bagDirectory);
587  if (count($files) > 0) {
588  $bagdir = $this->bagDirectory;
589  $manifestFile = findFirstExisting(
590  array("$bagdir/manifest-sha1.txt", "$bagdir/manifest-md5.txt"),
591  "$bagdir/manifest-sha1.txt"
592  );
593  try {
594  $this->manifest = new BagItManifest(
595  $manifestFile,
596  $this->bagDirectory . '/',
597  $this->tagFileEncoding
598  );
599  } catch (Exception $exc) {
600  array_push(
601  $this->bagErrors,
602  array('manifest', "Error reading $manifestFile.")
603  );
604  }
605 
606  if ($this->isExtended()) {
607  $manifestFile = findFirstExisting(
608  array("$bagdir/tagmanifest-sha1.txt",
609  "$bagdir/tagmanifest-md5.txt"),
610  "$bagdir/tagmanifest-sha1.txt"
611  );
612  $this->tagManifest = new BagItManifest(
613  $manifestFile,
614  $this->bagDirectory . '/',
615  $this->tagFileEncoding
616  );
617 
618  try {
619  $this->fetch = new BagItFetch(
620  "{$this->bagDirectory}/fetch.txt",
621  $this->tagFileEncoding
622  );
623  } catch (Exception $exc) {
624  array_push(
625  $this->bagErrors,
626  array('fetch', 'Error reading fetch file.')
627  );
628  }
629 
630  $this->bagInfoFile = "{$this->bagDirectory}/bag-info.txt";
631  $this->_readBagInfo();
632  }
633  }
634  }
635 
641  private function _createBag()
642  {
643  if (!is_dir($this->bag)) {
644  mkdir($this->bag);
645  }
646  $this->bagDirectory = realpath($this->bag);
647 
648  $dataDir = $this->getDataDirectory();
649  if (!is_dir($dataDir)) {
650  mkdir($dataDir);
651  }
652 
653  $this->bagitFile = $this->bagDirectory . '/bagit.txt';
654  $this->manifest = new BagItManifest(
655  "{$this->bagDirectory}/manifest-sha1.txt",
656  $this->bagDirectory . '/',
657  $this->tagFileEncoding
658  );
659 
660  $major = $this->bagVersion['major'];
661  $minor = $this->bagVersion['minor'];
662  $bagItData
663  = "BagIt-Version: $major.$minor\n" .
664  "Tag-File-Character-Encoding: {$this->tagFileEncoding}\n";
665  writeFileText($this->bagitFile, $this->tagFileEncoding, $bagItData);
666 
667  $this->_createExtendedBag();
668  }
669 
675  private function _createExtendedBag()
676  {
677  if ($this->extended) {
678  $hashEncoding = $this->getHashEncoding();
679  $this->tagManifest = new BagItManifest(
680  "{$this->bagDirectory}/tagmanifest-$hashEncoding.txt",
681  $this->bagDirectory . '/',
682  $this->tagFileEncoding
683  );
684 
685  $fetchFile = $this->bagDirectory . '/fetch.txt';
686  $this->fetch = new BagItFetch($fetchFile, $this->tagFileEncoding);
687 
688  $this->bagInfoFile = $this->bagDirectory . '/bag-info.txt';
689  touch($this->bagInfoFile);
690  if (is_null($this->bagInfoData)) {
691  $this->bagInfoData = array();
692  }
693  }
694  }
695 
701  private function _readBagInfo()
702  {
703  try {
704  $lines = readLines($this->bagInfoFile, $this->tagFileEncoding);
705  $this->bagInfoData = BagIt_parseBagInfo($lines);
706  } catch (Exception $exc) {
707  array_push(
708  $this->bagErrors,
709  array('baginfo', 'Error reading bag info file.')
710  );
711  }
712  }
713 
720  private function _writeBagInfo()
721  {
722  $lines = array();
723 
724  if (count($this->bagInfoData)) {
725  foreach ($this->bagInfoData as $label => $value) {
726  if (is_array($value)) {
727  foreach ($value as $v) {
728  $lines[] = "$label: $v\n";
729  }
730  } else {
731  $lines[] = "$label: $value\n";
732  }
733  }
734  }
735 
736  writeFileText($this->bagInfoFile, $this->tagFileEncoding, join('', $lines));
737  }
738 
744  private function _isCompressed()
745  {
746  if (is_dir($this->bag)) {
747  return false;
748  } else {
749  $bag = strtolower($this->bag);
750  if (endsWith($bag, '.zip')) {
751  $this->bagCompression = 'zip';
752  return true;
753  } else if (endsWith($bag, '.tar.gz') || endsWith($bag, '.tgz')) {
754  $this->bagCompression = 'tgz';
755  return true;
756  }
757  }
758  return false;
759  }
760 
767  private function _ensureBagInfoData()
768  {
769  if (is_null($this->bagInfoData)) {
770  $this->bagInfoData = array();
771  }
772  return $this->bagInfoData;
773  }
774 
775  //}}}
776 
777 }
778 
779 /* Functional wrappers/facades. */
780 
781 /*
782  * Local variables:
783  * tab-width: 4
784  * c-basic-offset: 4
785  * c-hanging-comment-ender-p: nil
786  * End:
787  */
788 
789 
790 ?>
BagIt\getHashEncoding
getHashEncoding()
Definition: bagit.php:319
BagIt\$tagManifest
$tagManifest
Definition: bagit.php:153
BagIt\$bagCompression
$bagCompression
Definition: bagit.php:194
BagIt\$bag
$bag
Definition: bagit.php:83
BagIt\getBagErrors
getBagErrors($validate=false)
Definition: bagit.php:364
BagItManifest
Definition: bagit_manifest.php:45
BagItFetch
Definition: bagit_fetch.php:45
BagIt\hasBagInfoData
hasBagInfoData($key)
Definition: bagit.php:548
BagIt\addFile
addFile($src, $dest)
Definition: bagit.php:461
BagIt\isValid
isValid()
Definition: bagit.php:269
BagIt\createFile
createFile($content, $dest)
Definition: bagit.php:493
BagIt\$bagVersion
$bagVersion
Definition: bagit.php:113
BagIt\$tagFileEncoding
$tagFileEncoding
Definition: bagit.php:123
BagIt\getBagInfo
getBagInfo()
Definition: bagit.php:291
BagIt\isExtended
isExtended()
Definition: bagit.php:280
BagIt\$bagInfoData
$bagInfoData
Definition: bagit.php:183
BagIt\$manifest
$manifest
Definition: bagit.php:143
BagIt\$bagDirectory
$bagDirectory
Definition: bagit.php:93
Seboettg\Collection\count
count()
Definition: ArrayListTrait.php:253
BagIt\__construct
__construct( $bag, $validate=false, $extended=true, $fetch=false, $bagInfoData=null)
Definition: bagit.php:229
BagIt\getDataDirectory
getDataDirectory()
Definition: bagit.php:309
BagIt\$bagErrors
$bagErrors
Definition: bagit.php:205
BagIt\setHashEncoding
setHashEncoding($hashAlgorithm)
Definition: bagit.php:332
BagIt\setBagInfoData
setBagInfoData($key, $value)
Definition: bagit.php:563
BagIt\package
package($destination, $method='tgz')
Definition: bagit.php:522
BagIt\getBagContents
getBagContents()
Definition: bagit.php:351
BagIt\update
update()
Definition: bagit.php:409
BagIt\getBagInfoData
getBagInfoData($key)
Definition: bagit.php:594
BagIt\$bagitFile
$bagitFile
Definition: bagit.php:133
BagIt\clearBagInfoData
clearBagInfoData($key)
Definition: bagit.php:579
BagItException
Definition: bagit.php:51
BagIt\$bagInfoFile
$bagInfoFile
Definition: bagit.php:173
BagIt\$extended
$extended
Definition: bagit.php:103
BagIt\validate
validate()
Definition: bagit.php:381
BagIt\$fetch
$fetch
Definition: bagit.php:163
BagIt
Definition: bagit.php:69