comparison OrthancServer/Sources/OrthancWebDav.cpp @ 4242:5cfa6ba75dfc

deleting resources from Orthanc WebDAV
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 09 Oct 2020 17:51:06 +0200
parents 3510da0e260c
children 64f57c9d5f79
comparison
equal deleted inserted replaced
4241:3510da0e260c 4242:5cfa6ba75dfc
47 static const char* const BY_STUDIES = "by-studies"; 47 static const char* const BY_STUDIES = "by-studies";
48 static const char* const BY_DATES = "by-dates"; 48 static const char* const BY_DATES = "by-dates";
49 static const char* const BY_UIDS = "by-uids"; 49 static const char* const BY_UIDS = "by-uids";
50 static const char* const UPLOADS = "uploads"; 50 static const char* const UPLOADS = "uploads";
51 static const char* const MAIN_DICOM_TAGS = "MainDicomTags"; 51 static const char* const MAIN_DICOM_TAGS = "MainDicomTags";
52 static const char* const STUDY_INFO = "study.json";
53 static const char* const SERIES_INFO = "series.json";
52 54
53 55
54 namespace Orthanc 56 namespace Orthanc
55 { 57 {
56 static boost::posix_time::ptime GetNow() 58 static boost::posix_time::ptime GetNow()
455 { 457 {
456 private: 458 private:
457 ServerContext& context_; 459 ServerContext& context_;
458 std::string parentSeries_; 460 std::string parentSeries_;
459 461
462 static bool LookupInstanceId(std::string& instanceId,
463 const UriComponents& path)
464 {
465 if (path.size() == 1 &&
466 boost::ends_with(path[0], ".dcm"))
467 {
468 instanceId = path[0].substr(0, path[0].size() - 4);
469 return true;
470 }
471 else
472 {
473 return false;
474 }
475 }
476
460 public: 477 public:
461 InstancesOfSeries(ServerContext& context, 478 InstancesOfSeries(ServerContext& context,
462 const std::string& parentSeries) : 479 const std::string& parentSeries) :
463 context_(context), 480 context_(context),
464 parentSeries_(parentSeries) 481 parentSeries_(parentSeries)
509 virtual bool GetFileContent(MimeType& mime, 526 virtual bool GetFileContent(MimeType& mime,
510 std::string& content, 527 std::string& content,
511 boost::posix_time::ptime& time, 528 boost::posix_time::ptime& time,
512 const UriComponents& path) ORTHANC_OVERRIDE 529 const UriComponents& path) ORTHANC_OVERRIDE
513 { 530 {
514 if (path.size() == 1 && 531 std::string instanceId;
515 boost::ends_with(path[0], ".dcm")) 532 if (LookupInstanceId(instanceId, path))
516 { 533 {
517 std::string instanceId = path[0].substr(0, path[0].size() - 4);
518
519 try 534 try
520 { 535 {
521 mime = MimeType_Dicom; 536 mime = MimeType_Dicom;
522 context_.ReadDicom(content, instanceId); 537 context_.ReadDicom(content, instanceId);
523 LookupTime(time, context_, instanceId, MetadataType_Instance_ReceptionDate); 538 LookupTime(time, context_, instanceId, MetadataType_Instance_ReceptionDate);
526 catch (OrthancException&) 541 catch (OrthancException&)
527 { 542 {
528 // File was removed 543 // File was removed
529 return false; 544 return false;
530 } 545 }
546 }
547 else
548 {
549 return false;
550 }
551 }
552
553 virtual bool DeleteItem(const UriComponents& path) ORTHANC_OVERRIDE
554 {
555 std::string instanceId;
556 if (LookupInstanceId(instanceId, path))
557 {
558 Json::Value info;
559 return context_.DeleteResource(info, instanceId, ResourceType_Instance);
531 } 560 }
532 else 561 else
533 { 562 {
534 return false; 563 return false;
535 } 564 }
555 INode* GetChild(const std::string& path) // Don't delete the result pointer! 584 INode* GetChild(const std::string& path) // Don't delete the result pointer!
556 { 585 {
557 Children::const_iterator child = children_.find(path); 586 Children::const_iterator child = children_.find(path);
558 if (child == children_.end()) 587 if (child == children_.end())
559 { 588 {
560 INode* child = CreateChild(path); 589 INode* child = CreateSubfolder(path);
561 590
562 if (child == NULL) 591 if (child == NULL)
563 { 592 {
564 return NULL; 593 return NULL;
565 } 594 }
575 return child->second; 604 return child->second;
576 } 605 }
577 } 606 }
578 607
579 protected: 608 protected:
580 void RemoveSubfolder(const std::string& path) 609 void InvalidateSubfolder(const std::string& path)
581 { 610 {
582 Children::iterator child = children_.find(path); 611 Children::iterator child = children_.find(path);
583 if (child != children_.end()) 612 if (child != children_.end())
584 { 613 {
585 assert(child->second != NULL); 614 assert(child->second != NULL);
589 } 618 }
590 619
591 virtual void Refresh() = 0; 620 virtual void Refresh() = 0;
592 621
593 virtual bool ListSubfolders(IWebDavBucket::Collection& target) = 0; 622 virtual bool ListSubfolders(IWebDavBucket::Collection& target) = 0;
594 623
595 virtual INode* CreateChild(const std::string& path) = 0; 624 virtual INode* CreateSubfolder(const std::string& path) = 0;
596 625
597 public: 626 public:
598 virtual ~InternalNode() 627 virtual ~InternalNode()
599 { 628 {
600 for (Children::iterator it = children_.begin(); it != children_.end(); ++it) 629 for (Children::iterator it = children_.begin(); it != children_.end(); ++it)
618 { 647 {
619 // Recursivity 648 // Recursivity
620 INode* child = GetChild(path[0]); 649 INode* child = GetChild(path[0]);
621 if (child == NULL) 650 if (child == NULL)
622 { 651 {
623 return false; 652 // Must be "true" to allow DELETE on folders that are
653 // automatically removed through recursive deletion
654 return true;
624 } 655 }
625 else 656 else
626 { 657 {
627 UriComponents subpath(path.begin() + 1, path.end()); 658 UriComponents subpath(path.begin() + 1, path.end());
628 return child->ListCollection(target, subpath); 659 return child->ListCollection(target, subpath);
655 UriComponents subpath(path.begin() + 1, path.end()); 686 UriComponents subpath(path.begin() + 1, path.end());
656 return child->GetFileContent(mime, content, time, subpath); 687 return child->GetFileContent(mime, content, time, subpath);
657 } 688 }
658 } 689 }
659 } 690 }
691
692
693 virtual bool DeleteItem(const UriComponents& path) ORTHANC_OVERRIDE ORTHANC_FINAL
694 {
695 Refresh();
696
697 if (path.empty())
698 {
699 IWebDavBucket::Collection tmp;
700 if (ListSubfolders(tmp))
701 {
702 return (tmp.GetSize() == 0);
703 }
704 else
705 {
706 return false;
707 }
708 }
709 else
710 {
711 INode* child = GetChild(path[0]);
712 if (child == NULL)
713 {
714 return true;
715 }
716 else
717 {
718 // Recursivity
719 UriComponents subpath(path.begin() + 1, path.end());
720 return child->DeleteItem(subpath);
721 }
722 }
723 }
660 }; 724 };
661 725
662 726
663 class OrthancWebDav::ListOfResources : public InternalNode 727 class OrthancWebDav::ListOfResources : public InternalNode
664 { 728 {
679 743
680 // Remove the children whose associated resource doesn't exist anymore 744 // Remove the children whose associated resource doesn't exist anymore
681 for (std::set<std::string>::const_iterator 745 for (std::set<std::string>::const_iterator
682 it = removedPaths.begin(); it != removedPaths.end(); ++it) 746 it = removedPaths.begin(); it != removedPaths.end(); ++it)
683 { 747 {
684 RemoveSubfolder(*it); 748 InvalidateSubfolder(*it);
685 } 749 }
686 } 750 }
687 751
688 virtual bool ListSubfolders(IWebDavBucket::Collection& target) ORTHANC_OVERRIDE ORTHANC_FINAL 752 virtual bool ListSubfolders(IWebDavBucket::Collection& target) ORTHANC_OVERRIDE ORTHANC_FINAL
689 { 753 {
708 772
709 return true; 773 return true;
710 } 774 }
711 } 775 }
712 776
713 virtual INode* CreateChild(const std::string& path) ORTHANC_OVERRIDE ORTHANC_FINAL 777 virtual INode* CreateSubfolder(const std::string& path) ORTHANC_OVERRIDE ORTHANC_FINAL
714 { 778 {
715 ResourcesIndex::Map::const_iterator resource = index_->GetPathToResource().find(path); 779 ResourcesIndex::Map::const_iterator resource = index_->GetPathToResource().find(path);
716 if (resource == index_->GetPathToResource().end()) 780 if (resource == index_->GetPathToResource().end())
717 { 781 {
718 return NULL; 782 return NULL;
725 789
726 ServerContext& GetContext() const 790 ServerContext& GetContext() const
727 { 791 {
728 return context_; 792 return context_;
729 } 793 }
730 794
731 virtual void GetCurrentResources(std::list<std::string>& resources) = 0; 795 virtual void GetCurrentResources(std::list<std::string>& resources) = 0;
732 796
733 virtual INode* CreateResourceNode(const std::string& resource) = 0; 797 virtual INode* CreateResourceNode(const std::string& resource) = 0;
734 798
735 public: 799 public:
929 { 993 {
930 private: 994 private:
931 std::set<std::string> months_; 995 std::set<std::string> months_;
932 996
933 public: 997 public:
934 Visitor()
935 {
936 }
937
938 const std::set<std::string>& GetMonths() const 998 const std::set<std::string>& GetMonths() const
939 { 999 {
940 return months_; 1000 return months_;
941 } 1001 }
942 1002
984 } 1044 }
985 1045
986 return true; 1046 return true;
987 } 1047 }
988 1048
989 virtual INode* CreateChild(const std::string& path) ORTHANC_OVERRIDE 1049 virtual INode* CreateSubfolder(const std::string& path) ORTHANC_OVERRIDE
990 { 1050 {
991 if (path.size() != 7) // Format: "YYYY-MM" 1051 if (path.size() != 7) // Format: "YYYY-MM"
992 { 1052 {
993 throw OrthancException(ErrorCode_InternalError); 1053 throw OrthancException(ErrorCode_InternalError);
994 } 1054 }
1052 } 1112 }
1053 1113
1054 return true; 1114 return true;
1055 } 1115 }
1056 1116
1057 virtual INode* CreateChild(const std::string& path) ORTHANC_OVERRIDE 1117 virtual INode* CreateSubfolder(const std::string& path) ORTHANC_OVERRIDE
1058 { 1118 {
1059 return new ListOfStudiesByMonth(context_, path, templates_); 1119 return new ListOfStudiesByMonth(context_, path, templates_);
1060 } 1120 }
1061 1121
1062 public: 1122 public:
1067 { 1127 {
1068 } 1128 }
1069 }; 1129 };
1070 1130
1071 1131
1132 class OrthancWebDav::DicomDeleteVisitor : public ServerContext::ILookupVisitor
1133 {
1134 private:
1135 ServerContext& context_;
1136 ResourceType level_;
1137
1138 public:
1139 DicomDeleteVisitor(ServerContext& context,
1140 ResourceType level) :
1141 context_(context),
1142 level_(level)
1143 {
1144 }
1145
1146 virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
1147 {
1148 return false; // (*)
1149 }
1150
1151 virtual void MarkAsComplete() ORTHANC_OVERRIDE
1152 {
1153 }
1154
1155 virtual void Visit(const std::string& publicId,
1156 const std::string& instanceId /* unused */,
1157 const DicomMap& mainDicomTags /* unused */,
1158 const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE
1159 {
1160 Json::Value info;
1161 context_.DeleteResource(info, publicId, level_);
1162 }
1163 };
1164
1165
1072 void OrthancWebDav::AddVirtualFile(Collection& collection, 1166 void OrthancWebDav::AddVirtualFile(Collection& collection,
1073 const UriComponents& path, 1167 const UriComponents& path,
1074 const std::string& filename) 1168 const std::string& filename)
1075 { 1169 {
1076 MimeType mime; 1170 MimeType mime;
1146 } 1240 }
1147 } 1241 }
1148 } 1242 }
1149 1243
1150 1244
1151 OrthancWebDav::OrthancWebDav(ServerContext& context) : 1245 OrthancWebDav::INode& OrthancWebDav::GetRootNode(const std::string& rootPath)
1246 {
1247 if (rootPath == BY_PATIENTS)
1248 {
1249 return *patients_;
1250 }
1251 else if (rootPath == BY_STUDIES)
1252 {
1253 return *studies_;
1254 }
1255 else if (rootPath == BY_DATES)
1256 {
1257 return *dates_;
1258 }
1259 else
1260 {
1261 throw OrthancException(ErrorCode_InternalError);
1262 }
1263 }
1264
1265
1266 OrthancWebDav::OrthancWebDav(ServerContext& context,
1267 bool allowDicomDelete) :
1152 context_(context), 1268 context_(context),
1269 allowDicomDelete_(allowDicomDelete),
1153 uploads_(false /* store uploads as temporary files */), 1270 uploads_(false /* store uploads as temporary files */),
1154 running_(false) 1271 running_(false)
1155 { 1272 {
1156 patientsTemplates_[ResourceType_Patient] = "{{PatientID}} - {{PatientName}}"; 1273 patientsTemplates_[ResourceType_Patient] = "{{PatientID}} - {{PatientName}}";
1157 patientsTemplates_[ResourceType_Study] = "{{StudyDate}} - {{StudyDescription}}"; 1274 patientsTemplates_[ResourceType_Study] = "{{StudyDate}} - {{StudyDescription}}";
1173 return true; 1290 return true;
1174 } 1291 }
1175 else if (path[0] == BY_UIDS) 1292 else if (path[0] == BY_UIDS)
1176 { 1293 {
1177 return (path.size() <= 3 && 1294 return (path.size() <= 3 &&
1178 (path.size() != 3 || path[2] != "study.json")); 1295 (path.size() != 3 || path[2] != STUDY_INFO));
1179 } 1296 }
1180 else if (path[0] == BY_PATIENTS) 1297 else if (path[0] == BY_PATIENTS ||
1298 path[0] == BY_STUDIES ||
1299 path[0] == BY_DATES)
1181 { 1300 {
1182 IWebDavBucket::Collection tmp; 1301 IWebDavBucket::Collection tmp;
1183 return patients_->ListCollection(tmp, UriComponents(path.begin() + 1, path.end())); 1302 return GetRootNode(path[0]).ListCollection(tmp, UriComponents(path.begin() + 1, path.end()));
1184 }
1185 else if (path[0] == BY_STUDIES)
1186 {
1187 IWebDavBucket::Collection tmp;
1188 return studies_->ListCollection(tmp, UriComponents(path.begin() + 1, path.end()));
1189 }
1190 else if (path[0] == BY_DATES)
1191 {
1192 IWebDavBucket::Collection tmp;
1193 return dates_->ListCollection(tmp, UriComponents(path.begin() + 1, path.end()));
1194 } 1303 }
1195 else if (path[0] == UPLOADS) 1304 else if (path[0] == UPLOADS)
1196 { 1305 {
1197 return uploads_.IsExistingFolder(UriComponents(path.begin() + 1, path.end())); 1306 return uploads_.IsExistingFolder(UriComponents(path.begin() + 1, path.end()));
1198 } 1307 }
1226 level = ResourceType_Study; 1335 level = ResourceType_Study;
1227 limit = 100; // TODO 1336 limit = 100; // TODO
1228 } 1337 }
1229 else if (path.size() == 2) 1338 else if (path.size() == 2)
1230 { 1339 {
1231 AddVirtualFile(collection, path, "study.json"); 1340 AddVirtualFile(collection, path, STUDY_INFO);
1232 1341
1233 level = ResourceType_Series; 1342 level = ResourceType_Series;
1234 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1], 1343 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1],
1235 true /* case sensitive */, true /* mandatory tag */); 1344 true /* case sensitive */, true /* mandatory tag */);
1236 } 1345 }
1237 else if (path.size() == 3) 1346 else if (path.size() == 3)
1238 { 1347 {
1239 AddVirtualFile(collection, path, "series.json"); 1348 AddVirtualFile(collection, path, SERIES_INFO);
1240 1349
1241 level = ResourceType_Instance; 1350 level = ResourceType_Instance;
1242 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1], 1351 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1],
1243 true /* case sensitive */, true /* mandatory tag */); 1352 true /* case sensitive */, true /* mandatory tag */);
1244 query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2], 1353 query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2],
1252 DicomIdentifiersVisitor visitor(context_, collection, level); 1361 DicomIdentifiersVisitor visitor(context_, collection, level);
1253 context_.Apply(visitor, query, level, 0 /* since */, limit); 1362 context_.Apply(visitor, query, level, 0 /* since */, limit);
1254 1363
1255 return true; 1364 return true;
1256 } 1365 }
1257 else if (path[0] == BY_PATIENTS) 1366 else if (path[0] == BY_PATIENTS ||
1258 { 1367 path[0] == BY_STUDIES ||
1259 return patients_->ListCollection(collection, UriComponents(path.begin() + 1, path.end())); 1368 path[0] == BY_DATES)
1260 } 1369 {
1261 else if (path[0] == BY_STUDIES) 1370 return GetRootNode(path[0]).ListCollection(collection, UriComponents(path.begin() + 1, path.end()));
1262 {
1263 return studies_->ListCollection(collection, UriComponents(path.begin() + 1, path.end()));
1264 }
1265 else if (path[0] == BY_DATES)
1266 {
1267 return dates_->ListCollection(collection, UriComponents(path.begin() + 1, path.end()));
1268 } 1371 }
1269 else if (path[0] == UPLOADS) 1372 else if (path[0] == UPLOADS)
1270 { 1373 {
1271 return uploads_.ListCollection(collection, UriComponents(path.begin() + 1, path.end())); 1374 return uploads_.ListCollection(collection, UriComponents(path.begin() + 1, path.end()));
1272 } 1375 }
1287 return false; 1390 return false;
1288 } 1391 }
1289 else if (path[0] == BY_UIDS) 1392 else if (path[0] == BY_UIDS)
1290 { 1393 {
1291 if (path.size() == 3 && 1394 if (path.size() == 3 &&
1292 path[2] == "study.json") 1395 path[2] == STUDY_INFO)
1293 { 1396 {
1294 DatabaseLookup query; 1397 DatabaseLookup query;
1295 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1], 1398 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1],
1296 true /* case sensitive */, true /* mandatory tag */); 1399 true /* case sensitive */, true /* mandatory tag */);
1297 1400
1300 1403
1301 mime = MimeType_Json; 1404 mime = MimeType_Json;
1302 return visitor.IsSuccess(); 1405 return visitor.IsSuccess();
1303 } 1406 }
1304 else if (path.size() == 4 && 1407 else if (path.size() == 4 &&
1305 path[3] == "series.json") 1408 path[3] == SERIES_INFO)
1306 { 1409 {
1307 DatabaseLookup query; 1410 DatabaseLookup query;
1308 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1], 1411 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1],
1309 true /* case sensitive */, true /* mandatory tag */); 1412 true /* case sensitive */, true /* mandatory tag */);
1310 query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2], 1413 query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2],
1317 return visitor.IsSuccess(); 1420 return visitor.IsSuccess();
1318 } 1421 }
1319 else if (path.size() == 4 && 1422 else if (path.size() == 4 &&
1320 boost::ends_with(path[3], ".dcm")) 1423 boost::ends_with(path[3], ".dcm"))
1321 { 1424 {
1322 std::string sopInstanceUid = path[3]; 1425 const std::string sopInstanceUid = path[3].substr(0, path[3].size() - 4);
1323 sopInstanceUid.resize(sopInstanceUid.size() - 4);
1324 1426
1325 DatabaseLookup query; 1427 DatabaseLookup query;
1326 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1], 1428 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1],
1327 true /* case sensitive */, true /* mandatory tag */); 1429 true /* case sensitive */, true /* mandatory tag */);
1328 query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2], 1430 query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2],
1339 else 1441 else
1340 { 1442 {
1341 return false; 1443 return false;
1342 } 1444 }
1343 } 1445 }
1344 else if (path[0] == BY_PATIENTS) 1446 else if (path[0] == BY_PATIENTS ||
1345 { 1447 path[0] == BY_STUDIES ||
1346 return patients_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end())); 1448 path[0] == BY_DATES)
1347 } 1449 {
1348 else if (path[0] == BY_STUDIES) 1450 return GetRootNode(path[0]).GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end()));
1349 {
1350 return studies_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end()));
1351 }
1352 else if (path[0] == BY_DATES)
1353 {
1354 return dates_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end()));
1355 } 1451 }
1356 else if (path[0] == UPLOADS) 1452 else if (path[0] == UPLOADS)
1357 { 1453 {
1358 return uploads_.GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end())); 1454 return uploads_.GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end()));
1359 } 1455 }
1406 } 1502 }
1407 1503
1408 1504
1409 bool OrthancWebDav::DeleteItem(const std::vector<std::string>& path) 1505 bool OrthancWebDav::DeleteItem(const std::vector<std::string>& path)
1410 { 1506 {
1411 if (path.size() >= 1 && 1507 if (path.empty())
1412 path[0] == UPLOADS) 1508 {
1509 return false;
1510 }
1511 else if (path[0] == BY_UIDS &&
1512 path.size() >= 2 &&
1513 path.size() <= 4)
1514 {
1515 if (allowDicomDelete_)
1516 {
1517 ResourceType level;
1518 DatabaseLookup query;
1519
1520 query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1],
1521 true /* case sensitive */, true /* mandatory tag */);
1522 level = ResourceType_Study;
1523
1524 if (path.size() >= 3)
1525 {
1526 if (path[2] == STUDY_INFO)
1527 {
1528 return true; // Allow deletion of virtual files
1529 }
1530
1531 query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2],
1532 true /* case sensitive */, true /* mandatory tag */);
1533 level = ResourceType_Series;
1534 }
1535
1536 if (path.size() == 4)
1537 {
1538 if (path[3] == SERIES_INFO)
1539 {
1540 return true; // Allow deletion of virtual files
1541 }
1542 else if (boost::ends_with(path[3], ".dcm"))
1543 {
1544 const std::string sopInstanceUid = path[3].substr(0, path[3].size() - 4);
1545
1546 query.AddRestConstraint(DICOM_TAG_SOP_INSTANCE_UID, sopInstanceUid,
1547 true /* case sensitive */, true /* mandatory tag */);
1548 level = ResourceType_Instance;
1549 }
1550 else
1551 {
1552 return false;
1553 }
1554 }
1555
1556 std::cout << "\n\n" << query.Format() << "\n\n";
1557
1558 DicomDeleteVisitor visitor(context_, level);
1559 context_.Apply(visitor, query, ResourceType_Instance, 0 /* since */, 0 /* no limit */);
1560 return true;
1561 }
1562 else
1563 {
1564 return false; // read-only
1565 }
1566 }
1567 else if (path[0] == BY_PATIENTS ||
1568 path[0] == BY_STUDIES ||
1569 path[0] == BY_DATES)
1570 {
1571 if (allowDicomDelete_)
1572 {
1573 return GetRootNode(path[0]).DeleteItem(UriComponents(path.begin() + 1, path.end()));
1574 }
1575 else
1576 {
1577 return false; // read-only
1578 }
1579 }
1580 else if (path[0] == UPLOADS)
1413 { 1581 {
1414 return uploads_.DeleteItem(UriComponents(path.begin() + 1, path.end())); 1582 return uploads_.DeleteItem(UriComponents(path.begin() + 1, path.end()));
1415 } 1583 }
1416 else 1584 else
1417 { 1585 {
1418 return false; // read-only 1586 return false;
1419 } 1587 }
1420 } 1588 }
1421 1589
1422 1590
1423 void OrthancWebDav::Start() 1591 void OrthancWebDav::Start()