359 lines
23 KiB
Text
359 lines
23 KiB
Text
<% content_for :head do %>
|
|
<%= tag.meta property: "og:type", content: "website" %>
|
|
<%= tag.meta property: "og:image", content: model_model_file_url(@model, @model.preview_file, format: @model.preview_file.extension) if @model.preview_file&.is_image? && !@model.sensitive %>
|
|
<%= tag.meta name: "description", content: truncate(sanitize(@model.notes), length: 80) if @model.notes.present? %>
|
|
<%= tag.meta name: "fediverse:creator", content: @model.creator.federails_actor.at_address if @model.creator %>
|
|
<%= tag.link rel: "alternate", type: Mime[:oembed], href: model_url(@model, format: :oembed), title: @model.name %>
|
|
<% end %>
|
|
|
|
<div itemscope itemtype="https://schema.org/3DModel">
|
|
<%= turbo_stream_from @model %>
|
|
<h1>
|
|
<span itemprop="name">
|
|
<a contenteditable="plaintext-only"
|
|
data-controller="editable"
|
|
data-action="focus->editable#onFocus blur->editable#onBlur"
|
|
data-editable-field="model[name]"
|
|
data-editable-path="<%= model_path(@model) %>"><%= @model.name %></a>
|
|
</span>
|
|
<%= link_to icon("search", t(".search")),
|
|
"https://yeggi.com/q/#{ERB::Util.url_encode(@model.name)}/",
|
|
class: "btn btn-outline", target: "manyfold_search", rel: "noreferrer" %>
|
|
</h1>
|
|
|
|
<div class="row row-cols-md-2 mt-2">
|
|
<div class="col-md-9" id="item_list">
|
|
<% if @locked_files > 0 %>
|
|
<div class="alert alert-info"><%= t(".preview", count: @locked_files) %></div>
|
|
<% end %>
|
|
|
|
<%= render "image_carousel", images: @images %>
|
|
|
|
<%= card(:secondary) do %>
|
|
<p class="card-text" itemprop="description"><%= markdownify @model.notes %></p>
|
|
<% end if @model.notes.present? %>
|
|
|
|
<% if @num_files > 0 %>
|
|
<h2><a name="files"><%= t(".files") %></a></h2>
|
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 mb-4">
|
|
<%= render partial: "file", collection: (@groups.delete(nil)) + @images %>
|
|
</div>
|
|
<% @groups.each_pair do |group, files| %>
|
|
<h3><a name="<%= group.parameterize %>"><%= group.strip.careful_titleize %>*</a></h3>
|
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 mb-4">
|
|
<%= render partial: "file", collection: files %>
|
|
</div>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
<div class="col-md-3" id="sidebar">
|
|
<%= card :secondary, t(".model_details") do %>
|
|
<table class="table table-borderless table-sm">
|
|
<% if SiteSettings.federation_enabled? %>
|
|
<tr>
|
|
<td>⁂</td>
|
|
<td><% if @model.remote? %>
|
|
<small><%= link_to @model.federails_actor.at_address, @model.federails_actor.profile_url, target: "new" %></small>
|
|
<% else %>
|
|
<small>
|
|
<%= @model.federails_actor.short_at_address %>
|
|
<%= render Components::CopyButton.new(text: @model.federails_actor.at_address) %>
|
|
</small>
|
|
<% end %>
|
|
<label for="toggle_activitypub"><i class="bi bi-info-circle"></i></label>
|
|
<div class="toggle-box">
|
|
<input type="checkbox" id="toggle_activitypub" hidden>
|
|
<div class="hidden-tooltip">
|
|
<i class="bi bi-arrow-90deg-up"></i>
|
|
<small>
|
|
This is the <a href="https://en.wikipedia.org/wiki/Fediverse" target="_blank">fediverse</a> activitypub address of this experience.<br>
|
|
Follow updates by copy/pasting it into ActivityPub <a href="https://codeberg.org/fediverse/delightful-fediverse-clients" target="_blank">clients</a>.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<% end %>
|
|
<% if @model.creator %>
|
|
<tr>
|
|
<td><%= icon "person", Creator.model_name.human %></td>
|
|
<td><%= link_to @model.creator.name, @model.creator, itemprop: "author" %></td>
|
|
</tr>
|
|
<% end %>
|
|
<% if ENV['FEDERATE_DRIVE_HOST'].present? %>
|
|
|
|
<% if @model.tags.where(name: "janusxr" ).any? %>
|
|
<tr>
|
|
<td>
|
|
<img src="/assets/janusxr.svg" style="width: 16px; transform: translate(0px,-2px);">
|
|
</td>
|
|
<td>
|
|
<%
|
|
fediURL = ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/janusxr.html"
|
|
%>
|
|
<%= link_to "JanusXR address", fediURL, target:"_blank" %>
|
|
<a href="" target="_blank"><i class="bi bi-link-45deg"></i></a>
|
|
<label for="toggle_janusxr"><i class="bi bi-info-circle"></i></label>
|
|
<div class="toggle-box">
|
|
<input type="checkbox" id="toggle_janusxr" hidden>
|
|
<div class="hidden-tooltip">
|
|
<i class="bi bi-arrow-90deg-up"></i>
|
|
<small>
|
|
This is the JanusXR address.<br>
|
|
<a href="https://janusxr.org/" target="_blank">JanusXR</a> is an Open Immersive Web-layer since 2015, which allows existing webpages to become spatial.<br>
|
|
It is Free and Opensource, and allows you to meet others in this experience (avatars, chat and voice etc).<br>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
|
|
<%
|
|
# lets generate a JanusXR spatial layer for the current scene
|
|
jmlfile = @model.library.path+"/"+@model.path+"/.xrforge/scene.jml"
|
|
jml = ""
|
|
if File.exist?(jmlfile)
|
|
jml = File.read(jmlfile)
|
|
end
|
|
%>
|
|
|
|
<!-- JML (https://janusxr.org) spatial markup -->
|
|
|
|
<%== jml %>
|
|
|
|
<% end %>
|
|
|
|
<% if @model.tags.where(name: "mml" ).any? %>
|
|
<tr>
|
|
<td>
|
|
<i class="bi bi-eye-fill" role="img"></i>
|
|
</td>
|
|
<td>
|
|
<%= link_to "MML address", "https://viewer.mml.io/main/v1/?url="+URI.encode_www_form_component(ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/scene.mml"), target:"_blank" %>
|
|
<a href="<%= ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/scene.mml" %>" target="_blank"><i class="bi bi-link-45deg"></i></a>
|
|
<label for="toggle_mml"><i class="bi bi-info-circle"></i></label>
|
|
<div class="toggle-box">
|
|
<input type="checkbox" id="toggle_mml" hidden>
|
|
<div class="hidden-tooltip">
|
|
<i class="bi bi-arrow-90deg-up"></i>
|
|
<small>
|
|
This is the MML address.<br>
|
|
Metaverse Markup Language (<a href="https://mml.io" target="_blank">MML</a>) is an open markup language used to define experiences.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<% end %>
|
|
|
|
<tr>
|
|
<td>
|
|
<i class="bi bi-journal-check" role="img"></i>
|
|
</td>
|
|
<td>
|
|
<%= link_to "build log", ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/log.txt", target: "_blank" %>
|
|
<label for="toggle_log"><i class="bi bi-info-circle"></i></label>
|
|
<div class="toggle-box">
|
|
<input type="checkbox" id="toggle_log" hidden>
|
|
<div class="hidden-tooltip" style="max-height:400px">
|
|
<i class="bi bi-arrow-90deg-up"></i>
|
|
<small>
|
|
This is the build log of XR Forge.<br>
|
|
When you add files, they are processed, validated (for <a href="https://xrfragment.org" target="_blank">XR Fragment</a> compliance).<br>
|
|
<%= render 'models/tags_info' %>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<i class="bi bi-controller" role="img"></i>
|
|
</td>
|
|
<td>
|
|
<%= link_to "Godot project", ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/godot.zip" %>
|
|
<label for="toggle_godot"><i class="bi bi-info-circle"></i></label>
|
|
<div class="toggle-box">
|
|
<input type="checkbox" id="toggle_godot" hidden>
|
|
<div class="hidden-tooltip">
|
|
<i class="bi bi-arrow-90deg-up"></i>
|
|
<small>
|
|
This is a Godot project which wraps your (3D file) experience.<br>
|
|
<a href="https://godot.org" target="_blank">Godot</a> is a Free and Opensource Game engine.<br>
|
|
The Godot project is basically its own XR Fragment browser (which you can extend).<br><br>
|
|
<b>WARNING</b>: use <a href="https://en.wikipedia.org/wiki/Progressive_enhancement" target="_blank">progressive enhancement</a> so your 3D file experience will always run in other <a href="https://xrfragment.org" target="_blank">XR Fragment</a> viewers.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<i class="bi bi-filetype-json" role="img"></i>
|
|
</td>
|
|
<td>
|
|
<%= link_to "glTF JSON", ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/scene.gltf" %>
|
|
<label for="toggle_gltf"><i class="bi bi-info-circle"></i></label>
|
|
<div class="toggle-box">
|
|
<input type="checkbox" id="toggle_gltf" hidden>
|
|
<div class="hidden-tooltip">
|
|
<i class="bi bi-arrow-90deg-up"></i>
|
|
<small>
|
|
This is the glTF JSON URL.<br>
|
|
This is an opensource fallback-mechanism for developers/engine's which don't support <a href="https://xfragment.org" target="_blank">XR Fragments</a>.
|
|
deeplinks.<br><br>
|
|
Instead of <b>https://my.org/foo.glb#chair</b> they can do:
|
|
<code><pre>$ curl <gltfURL> | jq '.meshes[] | select(.name == "chair")'</pre></code>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<% end %>
|
|
|
|
<% if @model.collection %>
|
|
<tr>
|
|
<td><%= icon "collection", Collection.model_name.human(count: 100) %></td>
|
|
<td><%= link_to @model.collection.name, models_path({collection: @model.collection}.merge(@filter&.to_params)) %></td>
|
|
</tr>
|
|
<% end %>
|
|
<% if SiteSettings.show_libraries || current_user&.is_administrator? %>
|
|
<tr>
|
|
<td><%= icon "boxes", Library.model_name.human %></td>
|
|
<td><%= link_to @model.library.name, models_path({library: @model.library}.merge(@filter&.to_params)) %></td>
|
|
</tr>
|
|
<% end %>
|
|
<% if @model.license %>
|
|
<tr>
|
|
<td><%= icon "card-heading", t(".license") %></td>
|
|
<td>
|
|
<%= Spdx.licenses[@model.license]&.fetch("reference") ?
|
|
link_to(t_license(@model.license), Spdx.licenses[@model.license]["reference"], itemprop: "license") :
|
|
t_license(@model.license) %>
|
|
</td>
|
|
</tr>
|
|
<% end %>
|
|
<% if @model.sensitive %>
|
|
<tr>
|
|
<td><%= icon("explicit", Model.human_attribute_name(:sensitive)) %></td>
|
|
<td>
|
|
<%= Model.human_attribute_name(:sensitive) %>
|
|
</td>
|
|
</tr>
|
|
<% end %>
|
|
<tr>
|
|
<td><%= icon "folder", t(".path") %></td>
|
|
<td>
|
|
<%= content_tag(:code, class: "path") { safe_join @model.path.split("/"), safe_join([tag.wbr, "/"]) } %>
|
|
<% unless @model.contains_other_models? %>
|
|
<div><%= button_tag(t(".organize.button_text"), class: "btn btn-warning btn-sm", "data-bs-toggle": "modal", "data-bs-target": "#confirm-move") if @model.needs_organizing? && policy(@model).edit? %></div>
|
|
<% end %>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><%= icon "tag", t(".tags") %></td>
|
|
<td><%= render "tag_list", tags: @model.tags.order(taggings_count: :desc, name: :asc), filter: @filter %>
|
|
<label for="toggle_tags"><i class="bi bi-info-circle"></i></label>
|
|
<div class="toggle-box">
|
|
<input type="checkbox" id="toggle_tags" hidden>
|
|
<div class="hidden-tooltip" style="max-height:400px">
|
|
<i class="bi bi-arrow-90deg-up"></i>
|
|
<small>
|
|
<%= render 'models/tags_info' %>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<% if SiteSettings.social_enabled? %>
|
|
<tr>
|
|
<td><%= icon "people", t(".followers") %></td>
|
|
<td><%= t("general.followers", count: @model.followers.count) %></td>
|
|
</tr>
|
|
<% end %>
|
|
<tr>
|
|
<td><%= render Components::AccessIndicator.new(object: @model, text: false) %></td>
|
|
<td><%= render Components::AccessIndicator.new(object: @model, text: true, icon: false) %></td>
|
|
</tr>
|
|
</table>
|
|
<%= render Components::FollowButton.new(follower: current_user, target: @model) unless @model.remote? %>
|
|
<%= render Components::GoButton.new(icon: "pencil", label: t("general.edit"), href: edit_model_path(@model), variant: "primary") if policy(@model).edit? %>
|
|
<%= render Components::DoButton.new(icon: "trash", label: t("general.delete"), href: model_path(@model), method: :delete, variant: "outline-danger", confirm: translate("models.destroy.confirm")) if policy(@model).destroy? %>
|
|
<% end %>
|
|
|
|
<div class="mb-3 w-100 text-center">
|
|
<%= render Components::DownloadButton.new(model: @model) %>
|
|
</div>
|
|
|
|
<%= card :secondary, t("layouts.card_list_page.actions_heading") do %>
|
|
<%= render Components::ReportButton.new(object: @model, path: new_model_report_path(@model)) %>
|
|
<% end if SiteSettings.multiuser_enabled? %>
|
|
|
|
<% if !@model.parents.empty? && policy(@model).merge? %>
|
|
<%= card :danger, t(".merge.heading") do %>
|
|
<p>
|
|
<%= t(".merge.warning") %>
|
|
</p>
|
|
<strong><%= t(".merge.with") %>:</strong>
|
|
<% @model.parents.each do |parent| %>
|
|
<%= render Components::DoButton.new(icon: "union", label: parent.name, href: merge_models_path(target: parent.public_id, models: [@model.public_id]), method: :post, variant: "danger") %>
|
|
<% end %>
|
|
<% end %>
|
|
<% end %>
|
|
|
|
<%= render partial: "problem", collection: @model.problems.visible(problem_settings) %>
|
|
|
|
<%= card :secondary, t(".files_card.heading") do %>
|
|
<a href="#files">Top</a>
|
|
<ul>
|
|
<% @groups.each_pair do |group, files| %>
|
|
<li><a href="#<%= group.parameterize %>"><%= group.strip.careful_titleize %>*</a></li>
|
|
<% end %>
|
|
</ul>
|
|
<%= link_to t(".files_card.bulk_edit"), bulk_edit_model_model_files_path(@model), class: "btn btn-secondary" if policy(@model).edit? %>
|
|
<%= link_to t(".rescan"), scan_model_path(@model), class: "btn btn-warning", method: :post if policy(@model).scan? %>
|
|
<% end %>
|
|
|
|
<%= render "links_card", links: @model.links %>
|
|
|
|
<div class="modal fade" id="confirm-move" data-bs-backdrop='static' tabindex="-1" aria-labelledby="confirmMoveLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<div class="modal-title" id="confirmMoveLabel"><%= t(".organize.button_text") %></div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>
|
|
<%= t(".organize.confirm.summary_html", from: @model.path, to: @model.formatted_path) %>
|
|
</p>
|
|
<p>
|
|
<%= t(".organize.confirm.are_you_sure") %>
|
|
</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t(".organize.confirm.no") %></button>
|
|
<%= button_to t(".organize.confirm.yes"), model_path(@model, "model[organize]": true), method: :patch, class: "btn btn-warning" %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<% if policy(@model).upload? %>
|
|
<%= card :warning, t(".upload_card.heading") do %>
|
|
<%= form_with url: model_model_files_path(@model), id: "upload-form", class: "d-grid" do |form| %>
|
|
|
|
<%= content_tag :div, nil, class: "mb-3", data: {
|
|
controller: "upload",
|
|
action: "turbo:morph@window->upload#reconnect",
|
|
max_file_size: SiteSettings.max_file_upload_size,
|
|
allowed_file_types: input_accept_string,
|
|
upload_endpoint: upload_path
|
|
} %>
|
|
<%= submit_tag translate(".submit"), class: "btn btn-primary d-block" %>
|
|
<% end %>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</div>
|