Sharing files between pages & linking them via 'pages' field

Hello there,
I’m looking for a way to upload and select archives in different pages, but saving / uploading them to a central folder. I want to achieve this without depending on the dashboard / panel view.

Right now, I got this to select already uploaded files from certain other pages:

download:
  label: Archive
  type: files
  layout: list
  query: site.index.filterBy("intendedTemplate", "release").files.filterBy("type", "archive")

… but what I’d like is the following: when editing pages with “intendedTemplate”, “release”, one should be able to upload (section type: files) and select from those (field type: files), but they should be saved in a central folder (inside or outside content, doesn’t matter). I’d be okay if the ‘root’ files section lives in a site.yml tab, but it should be accessible through those ‘release’ pages.

For accessing those from a files field, I’d just alter above blueprint query, but what about the upload problem?

Thanks for your time and thoughts,
S1SYPHOS

// Edit: Among other stuff, my current solution eliminates uploading the same file twice, but it is generated two times inside the media folder (pages/page1/file.zip and pages/page2/file.zip), resulting in wasted webspace

It is not necessary to upload files to the current page. You can put a files section into your blueprint that uses a central media hub folder as the parent, well, using the parent option.

Oh my, totally didn’t see this:
https://getkirby.com/docs/reference/panel/sections/files#options

K3 rocks :metal:

The only thing I somehow can’t make out is how to set the site as files section parent.

using parent: site seemed to work just fine for me … ?

Well, that was the most obvious and I tried that and always got an error. Did you test with 3.0.3 or 3.0.2? Maybe I have to update first, I tested with 3.0.2.

Edit: no, I keep getting a “The JSON response from the API could not be parsed. Please check your API connection.” error even with a fresh 3.0.3 Starterkit. Maybe I’m just being dumb somehow.

        sections:
          files:
            headline: Files
            type: files
            parent: site
            template: archive
            info: "{{ file.niceSize }}"

… worked just fine with v3.0.2

Maybe you can help me once again.

I’m making a site for couple musicians, some of them released records together. However, I’m looking for a way to organize them - my first attempt being the obvious choice, subpages, but for shared releases, I don’t want to duplicate pages, because if something changes, you’d have to change two subpages: subpage below musician1 and again as subpage of musician 2, so maybe by using a related pages = musicians field?

I’d create records and musicians. Then use a pages field to assign musicians to records.

That way you can query the related musicians on every record page. And you can also fetch all records that belong to a musician on a musician’s page.

2 Likes

Ok, looks like something was really messed up, because I got it to work with a new new Starterkit. But still very strange.

I’m going with a pages field, but don’t seem to get the filtering t work like expected - on the musician page, I’m trying to get all records that contain this specific musician inside their pages field …

@S1SYPHOS Could you please post your code?

Each record / release has a pages field containing all musicians that worked on the piece:

# pages/release.yml

        sections:
          about:
            type: fields
            fields:
              artists:
                label: Musicians
                type: pages
                query: site.index.filterBy("intendedTemplate", "artist")
                required: true

In the artist template, I want to display all records / releases linked to this artist:

<div class="container">
  <div class="columns is-mobile is-centered is-multiline has-text-centered">
    <?php
      foreach ($releases as $release) :
      $slug = str::slug($release->title());
      $cover = $release->cover()->toFile();
      $image = $cover->thumb(option('thumbs.presets.cover'));
    ?>
      <a class="modal-toggle" data-toggle="<?= $slug ?>" href="#">
        <img src="<?= $image->url() ?>" title="<?= $release->title() ?>" alt="Cover von <?= $release->title() ?>" width="<?= $image->width(); ?>" height="<?= $image->height() ?>">
      </a>
    <?php endforeach ?>
  </div>
</div>

The important part is defining $releases in the controller:

<?php

$raps = page('releases')->children()->listed()->filterBy(???)

I tried different stuff but nothing seemed to work …

$raps = page('releases')->children()->listed()->filter(function($child) use($page) {
    $artists = $child->artists()->toPages();
    if($artists->has($page)) {
        return $child;
    }
});
1 Like

Last thing I want to know:
Is there a way to put this complex monster into a pages field for every musician to show the releases that are bound to him/her?

The notation translates like -> becomes . but what about the filter function? Given there are definitions as well?

That is very well possible:

Kirby::plugin('texnixe/customMethods', [
    'pagesMethods' => [
        'getRecords' => function ($musician) {

            $records = $this->filter(function($child) use($musician) {
                $artists = $child->artists()->toPages();
                if($artists->has($musician)) {
                    return $child;
                }
            });

            return $records;
        }
    ]
]);

In your controller/template:

$records = page('releases')->children()->listed()->getRecords($page); // where page is the musician page
dump($records);

This is also available inside a blueprint somehow, I presume?

// I meant to show associated records in the panel page of each musician, meaning I’d define them somehow inside blueprints/pages/artist.yml

That method should be available in the Panel as well. Maybe you can use @rasteiner’s Pagesdisplay plugin

I’ll try that and report back.

// Edit:
Works like a charm in the frontend, but not from within the blueprint:

        sections:
          releases:
            headline: Releases
            type: pagesdisplay
            query: site.find("releases").children.getRecords(page)

that won’t work because the query language doesn’t support evaluating function arguments.
Your query is automatically converted to the equivalent of:

site.find("releases").children.getRecords("page")

(page is simply seen as a string and not as a variable pointing to your artist)

There are many ways to work around this.
I guess the cleanest and most expressive one would be to create a page model for your “artist” pages.
That would go like this:

/site/models/artist.php:

<?php

use Kirby\Cms\Page;

class ArtistPage extends Page {
  function getRecords($recordsId) {
    return site()->find($recordsId)->children()->getRecords($this);
  }
}

you would use it in your artist.yml like this:

        sections:
          releases:
            headline: Releases
            type: pagesdisplay
            query: page.getRecords("releases")

If you only ever will have a single page that collects all records (and that one will never change uid), you might as well hardcode the path to it:

<?php

use Kirby\Cms\Page;

class ArtistPage extends Page {
  function getRecords() {
    return site()->find('releases')->children()->getRecords($this);
  }
}

and then simply use it as query like this:

query: page.getRecords

you can of course use that function also in your artist template ($page->getRecords())

PS: I haven’t tried any of that code.