Mercurial > hg > orthanc-wsi
changeset 562:f88082aa5230 annotations
first end-to-end sharing of layer
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Tue, 01 Sep 2026 17:19:47 +0200 |
| parents | a802aa368b0a |
| children | 05c99738d28b |
| files | ViewerPlugin/Annotations/AnnotationsRestApi.cpp ViewerPlugin/WebApplication/viewer.html ViewerPlugin/WebApplication/viewer.js |
| diffstat | 3 files changed, 80 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- a/ViewerPlugin/Annotations/AnnotationsRestApi.cpp Tue Sep 01 16:46:07 2026 +0200 +++ b/ViewerPlugin/Annotations/AnnotationsRestApi.cpp Tue Sep 01 17:19:47 2026 +0200 @@ -134,9 +134,11 @@ static const char* const KEY_FEATURES = "features"; static const char* const KEY_ID = "id"; static const char* const KEY_LAYERS = "layers"; + static const char* const KEY_LAYER_ID = "layer-id"; static const char* const KEY_NAME = "name"; static const char* const KEY_PUBLIC = "public"; static const char* const KEY_SHARED_WITH = "shared_with"; + static const char* const KEY_TYPE = "type"; static const char* const KEY_VERSION = "version"; static const char* const KEY_VISIBLE = "visible"; @@ -754,32 +756,6 @@ }; - class SharedLayerId - { - private: - UserId author_; - std::string layerId_; - - public: - SharedLayerId(const UserId& author, - const std::string& layerId) : - author_(author), - layerId_(layerId) - { - } - - const UserId& GetAuthor() const - { - return author_; - } - - const std::string& GetLayerId() const - { - return layerId_; - } - }; - - class AnnotationsWorkspace : public Orthanc::IDynamicObject { private: @@ -1075,9 +1051,11 @@ } } - void ListSharedLayers(std::list<SharedLayerId>& target) const + void ListSharedLayers(std::set<UserId>& authors, + std::set<std::string>& layerIds) const { - target.clear(); + authors.clear(); + layerIds.clear(); if (IsValid()) { @@ -1085,7 +1063,12 @@ while (!iterator.IsDone()) { - target.push_back(SharedLayerId(userId_, iterator.GetLayer().GetId())); + const SharedLayer& layer = dynamic_cast<const SharedLayer&>(iterator.GetLayer()); + + // TODO - Ensure that layer is still shared with "userId_" + + authors.insert(layer.GetAuthor()); + layerIds.insert(layer.GetId()); iterator.Next(); } } @@ -1443,7 +1426,7 @@ { AnnotationsCommandContext context(request); - const std::string layerId = context.GetBodyString("layer-id"); + const std::string layerId = context.GetBodyString(KEY_LAYER_ID); { AnnotationsWorkspace::UserWriter writer(context.GetWorkspace(), context.GetUser().GetAnnotatingId()); @@ -1477,7 +1460,8 @@ if (!Orthanc::Toolbox::ReadJson(unserialized, uncompressed) || !unserialized.isObject() || - !unserialized.isMember(KEY_FEATURES)) + !unserialized.isMember(KEY_FEATURES) || + !unserialized[KEY_FEATURES].isArray()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } @@ -1504,6 +1488,8 @@ void Save() const { + assert(content_.isArray()); + Json::Value unserialized; unserialized[KEY_VERSION] = static_cast<unsigned int>(ORTHANC_WSI_ANNOTATIONS_VERSION); unserialized[KEY_FEATURES] = content_; @@ -1528,14 +1514,35 @@ void GetContent(Json::Value& target) { Orthanc::ReaderWriterLock::ReadLock lock(mutex_); + + assert(content_.isArray()); target = content_; } void SetContent(const Json::Value& content) { - Orthanc::ReaderWriterLock::WriteLock lock(mutex_); - content_ = content; - Save(); + if (!content.isArray()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + for (Json::Value::ArrayIndex i = 0; i < content.size(); i++) + { + if (!content[i].isObject() || + !content[i].isMember(KEY_LAYER_ID) || + !content[i].isMember(KEY_TYPE) || + !content[i][KEY_LAYER_ID].isString() || + !content[i][KEY_TYPE].isString()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + { + Orthanc::ReaderWriterLock::WriteLock lock(mutex_); + content_ = content; + Save(); + } } }; @@ -1563,10 +1570,10 @@ } public: - CachedUserFeatures(const AnnotationsWorkspaceId& annotations, + CachedUserFeatures(const AnnotationsWorkspaceId& id, const UserId& user) { - const std::string key = annotations.GetFeaturesKey(user); + const std::string key = id.GetFeaturesKey(user); cached_ = GetCache().GetCachedValue(key); @@ -1821,19 +1828,40 @@ { AnnotationsCommandContext context(request); - std::list<SharedLayerId> layers; + std::set<UserId> authors; + std::set<std::string> layerIds; { AnnotationsWorkspace::UserReader reader(context.GetWorkspace(), context.GetUser().GetAnnotatingId()); - reader.ListSharedLayers(layers); + reader.ListSharedLayers(authors, layerIds); } - for (std::list<SharedLayerId>::const_iterator it = layers.begin(); it != layers.end(); ++it) + Json::Value sharedFeatures = Json::arrayValue; + + // Loop over the imported authors + for (std::set<UserId>::const_iterator it = authors.begin(); it != authors.end(); ++it) { - printf("[%s] [%s]\n", it->GetAuthor().GetName().c_str(), it->GetLayerId().c_str()); + Json::Value authorFeatures; + + { + CachedUserFeatures author(context.GetWorkspaceId(), *it); + author.GetFeatures().GetContent(authorFeatures); + } + + assert(authorFeatures.isArray()); + + for (Json::Value::ArrayIndex i = 0; i < authorFeatures.size(); i++) + { + std::string layerId = Orthanc::SerializationToolbox::ReadString(authorFeatures[i], KEY_LAYER_ID); + if (layerIds.find(layerId) != layerIds.end()) + { + sharedFeatures.append(authorFeatures[i]); + } + } } Json::Value answer; + answer[KEY_FEATURES] = sharedFeatures; ViewerToolbox::AnswerJson(output, answer); } }
--- a/ViewerPlugin/WebApplication/viewer.html Tue Sep 01 16:46:07 2026 +0200 +++ b/ViewerPlugin/WebApplication/viewer.html Tue Sep 01 17:19:47 2026 +0200 @@ -396,13 +396,14 @@ <button type="button" class="btn btn-link p-1" style="color:inherit"> <i class="bi" :class="layer.visible ? 'bi-eye' : 'bi-eye-slash'" - v-on:click="layer.visible = !layer.visible; SaveSharedLayer(layer)" + v-on:click="layer.visible = !layer.visible; drawSharedLayer.changed(); SaveSharedLayer(layer)" ></i> </button> </td> <td class="text-center align-middle p-0"> <input type="color" style="width:22px;height:22px;padding:1px;border:none;cursor:pointer;border-radius:3px" v-model:value="layer.color" + v-on:input="drawSharedLayer.changed()" v-on:change="SaveSharedLayer(layer)"> </td> <td class="align-middle px-1 py-0">
--- a/ViewerPlugin/WebApplication/viewer.js Tue Sep 01 16:46:07 2026 +0200 +++ b/ViewerPlugin/WebApplication/viewer.js Tue Sep 01 17:19:47 2026 +0200 @@ -866,6 +866,15 @@ return null; } + function GetSharedLayerById(id) { + for (var i = 0; i < app.sharedLayers.length; i++) { + if (app.sharedLayers[i].id == id) { + return app.sharedLayers[i]; + } + } + return null; + } + function GetLayerOfFeature(feature) { var layerId = feature.get('layer-id'); console.assert(layerId !== null); @@ -900,7 +909,7 @@ this.drawSharedLayer = new ol.layer.Vector({ source: this.drawSharedSource, style: function(feature) { - var entry = GetSharedLayerById(feature.get('shared-layer-id')); + var entry = GetSharedLayerById(feature.get('layer-id')); if (!entry || !entry.visible) { return null; }
