diff --git a/app/Policies/PhotoQueryPolicy.php b/app/Policies/PhotoQueryPolicy.php
index ab37789882a..ec01cd27e1e 100644
--- a/app/Policies/PhotoQueryPolicy.php
+++ b/app/Policies/PhotoQueryPolicy.php
@@ -130,6 +130,34 @@ public function applySearchabilityFilter(FixedQueryBuilder $query, ?Album $origi
});
}
+ /**
+ * Restricts the photo query to only non sensitive photos.
+ *
+ * @param FixedQueryBuilder $query
+ * @param Album|null $origin
+ *
+ * @return FixedQueryBuilder
+ */
+ public function applySensitivityFilter(FixedQueryBuilder $query, ?Album $origin = null, bool $include_nsfw = true): FixedQueryBuilder
+ {
+ if ($include_nsfw) {
+ return $query;
+ }
+
+ $this->prepareModelQueryOrFail($query, true, false);
+
+ // If origin is set, also restrict the search result for admin
+ // to photos which are in albums below origin.
+ // This is not a security filter, but simply functional.
+ if ($origin !== null) {
+ $query
+ ->where('albums._lft', '>=', $origin->_lft)
+ ->where('albums._rgt', '<=', $origin->_rgt);
+ }
+
+ return $query->where(fn (Builder $query) => $this->appendSensitivityConditions($query->getQuery(), $origin?->_lft, $origin?->_rgt));
+ }
+
/**
* Adds the conditions of _searchable_ photos to the query.
*
diff --git a/app/Relations/HasManyPhotosByTag.php b/app/Relations/HasManyPhotosByTag.php
index 5b6c7e5b090..c006e80fe57 100644
--- a/app/Relations/HasManyPhotosByTag.php
+++ b/app/Relations/HasManyPhotosByTag.php
@@ -69,18 +69,33 @@ public function addEagerConstraints(array $albums): void
$album = $albums[0];
$tags = $album->show_tags;
- $this->photo_query_policy
- ->applySearchabilityFilter(
- $this->getRelationQuery(),
- origin: null,
- include_nsfw: !Configs::getValueAsBool('hide_nsfw_in_smart_albums')
- )
- ->where(function (Builder $q) use ($tags): void {
- // Filter for requested tags
- foreach ($tags as $tag) {
- $q->where('tags', 'like', '%' . trim($tag) . '%');
- }
- });
+ if (Configs::getValueAsBool('TA_override_visibility')) {
+ $this->photo_query_policy
+ ->applySensitivityFilter(
+ $this->getRelationQuery(),
+ origin: null,
+ include_nsfw: !Configs::getValueAsBool('hide_nsfw_in_smart_albums')
+ )
+ ->where(function (Builder $q) use ($tags): void {
+ // Filter for requested tags
+ foreach ($tags as $tag) {
+ $q->where('tags', 'like', '%' . trim($tag) . '%');
+ }
+ });
+ } else {
+ $this->photo_query_policy
+ ->applySearchabilityFilter(
+ $this->getRelationQuery(),
+ origin: null,
+ include_nsfw: !Configs::getValueAsBool('hide_nsfw_in_smart_albums')
+ )
+ ->where(function (Builder $q) use ($tags): void {
+ // Filter for requested tags
+ foreach ($tags as $tag) {
+ $q->where('tags', 'like', '%' . trim($tag) . '%');
+ }
+ });
+ }
}
/**
diff --git a/app/SmartAlbums/BaseSmartAlbum.php b/app/SmartAlbums/BaseSmartAlbum.php
index d4c9a5fa636..b5fd848713b 100644
--- a/app/SmartAlbums/BaseSmartAlbum.php
+++ b/app/SmartAlbums/BaseSmartAlbum.php
@@ -113,14 +113,18 @@ public function get_photos(): Collection
*/
public function photos(): Builder
{
- $query = $this->photo_query_policy
- ->applySearchabilityFilter(
- query: Photo::query()->with(['album', 'size_variants', 'statistics']),
- origin: null,
- include_nsfw: !Configs::getValueAsBool('hide_nsfw_in_smart_albums')
- )->where($this->smart_photo_condition);
-
- return $query;
+ $base_query = Photo::query()->with(['album', 'size_variants', 'statistics']);
+
+ if (!Configs::getValueAsBool('SA_override_visibility')) {
+ return $this->photo_query_policy
+ ->applySearchabilityFilter(query: $base_query, origin: null, include_nsfw: !Configs::getValueAsBool('hide_nsfw_in_smart_albums'))
+ ->where($this->smart_photo_condition);
+ }
+
+ // If the smart album visibility override is enabled, we do not need to apply any security filter, as all photos are visible
+ // in this smart album. We still need to apply the smart album condition, though.
+ return $this->photo_query_policy->applySensitivityFilter(query: $base_query, origin: null, include_nsfw: !Configs::getValueAsBool('hide_nsfw_in_smart_albums'))
+ ->where($this->smart_photo_condition);
}
/**
@@ -215,4 +219,4 @@ public function setPrivate(): void
$this->public_permissions = null;
$perm->delete();
}
-}
\ No newline at end of file
+}
diff --git a/database/migrations/2025_05_28_174009_add_visibility_override_smart_album.php b/database/migrations/2025_05_28_174009_add_visibility_override_smart_album.php
new file mode 100644
index 00000000000..9d3243260a5
--- /dev/null
+++ b/database/migrations/2025_05_28_174009_add_visibility_override_smart_album.php
@@ -0,0 +1,43 @@
+ 'SA_override_visibility',
+ 'value' => '0',
+ 'cat' => self::CAT,
+ 'type_range' => self::BOOL,
+ 'description' => 'Smart album visibility overrides the photo visibility.',
+ 'details' => ' This will make any photos matching the smart album condition visible.',
+ 'is_expert' => true,
+ 'is_secret' => false,
+ 'level' => 0,
+ 'order' => 10,
+ ],
+ [
+ 'key' => 'TA_override_visibility',
+ 'value' => '0',
+ 'cat' => self::CAT,
+ 'type_range' => self::BOOL,
+ 'description' => 'Tag album visibility overrides the photo visibility.',
+ 'details' => ' This will make any photos matching the tag album condition visible.',
+ 'is_expert' => true,
+ 'is_secret' => false,
+ 'level' => 0,
+ 'order' => 11,
+ ],
+ ];
+ }
+};
\ No newline at end of file