Jump to content
ridgedale

PW3 - Custom Upload Method for Data Archive

Recommended Posts

Reference: PW 3.0.62 and uikit3 based site using the Regular-Master profile.

I was wondering if it is possible to create an upload method that is not directly associated with any particular Processwire page so that a custom folder can be used from within the admin frontend to upload for example newsletters so they are stored in one hierarchical location to allow the latest edition to be automatically displayed using a single code call.

What I'd like to be able to do is provide an upload method that allows a user to create year folders and upload newsletters to the relevant folders, making sure the uploaded files are named as required.

Any input would be greatly appreciated.

Share this post


Link to post
Share on other sites

Hi,

As far as your webserver has write permission on a given folder outside the web directory, you can write your logic in a module which handle your upload thing - everything is possible inside ProcessWire.

1 hour ago, ridgedale said:

What I'd like to be able to do is provide an upload method that allows a user to create year folders and upload newsletters to the relevant folders, making sure the uploaded files are named as required.

I personnaly would go with pages. Make a parent page "newsletters-upload" in your page tree then create a child page for each "year", then create a child page inside "year" for each uploaded newsletter. Each newsletter page have a fieldtype-secure-file field which give you the possibility to store your uploaded files outside the webfolder. 

In the process module, the author could create/select a "year" page (folder) and upload file by drag&drop in the admin.  You can also pull files from an FTP server where authors create their folder tree, then do the logic in the module.

 

This is just an idea from what I understand, more details needed  😁

  • Like 2

Share this post


Link to post
Share on other sites
2 hours ago, ridgedale said:

What I'd like to be able to do is provide an upload method that allows a user to create year folders and upload newsletters to the relevant folders, making sure the uploaded files are named as required.

PW does not really use the concept of folders. I agree with @flydev - you should use pages in place of folders.

I've used a repeater for doing something quite similar to what I think you are describing. Create a page that will be the central place where all files are uploaded. Add a repeater to that page's template, containing an integer field for year and a files field to hold the uploads. Users will upload their files to the relevant year item. Using API code you can get the most recent year (i.e. the highest integer) and the most recently added file in that year (the last file in the field).

For automatic upload renaming there is @adrian's Custom Upload Names module.

  • Like 3

Share this post


Link to post
Share on other sites

I am doing something very similar to this and am just using pages.

I use two templates:  'newsletters_parent' and 'newsletters_child'.

The 'newsletters_parent' is the container and onyl has a title, but its settings on the "Family" tab are:

  • Can have children: YES
  • Can be used for new pages: ONE
  • Allowed template(s) for children: newsletter_child
  • Name format for children: Y/m/d H:i:s  (the page creation date/time -- this will get overwritten at a later stage)
  • Sort settings for children:  pub_date (a field that will be added to the newsletter_child page)
  • Reverse sort direction: CHECKED (yes)

The 'newsletters_child' template has the following fields:

  • title (default and required by all templates)
  • pub_date (a datepicker field  set to YYYY-MM-DD  -- the publication date might not necessarily be the same as the date the file was uploaded!)
  • pdf_file (file-upload field, single file only, and also set to only allow PDF files)

The settings ("Family" tab) for the child template are:

  • Can have children: NO
  • Can be used for new pages: YES
  • Allowed template(s) for parents: newsletter_parent
  • Show in the add-page shortcut menu: YES

To make the process of adding a new newsletter as easy as possible for users -- all they have to do is select the file to upload and the pick the pub date -- I use a couple of hooks hook in the site's ready.php file:

The first hook adds dummy text to the page title field on page creation so that the user doesn't have to enter anything (if it is left blank, it will throw an error) -- the proper title, along with the page name will be created from the pub_date field when the page is saved.

The second hook also creates the actual page title and the sets page name based on the supplied 'pub_date'. [NOTE: the newsletters only get published once a month, so we only use the Year/Month in page names/titles.]

/**
 * 1. Set a dummy title on page save at creation, to prevent the required field showing an error if it is blank.
 *    (Requires the parent template option 'Name format for children' to be set)
 * 2. Modify newsletter title and page name from pub_date before saving.
 */
$pages->addHookAfter('saveReady', function(HookEvent $event) {
	$page = $event->arguments(0);

	// Only on newsletter_child templates where the title is blank (on page creation)
	if ($page->template->name == 'newsletter_child' && $page->title == '') {
		$page->title = 'Newsletter'; // a dummy title, will be replaced at a later stage
	}

	// Only on newsletter_child templates with a pub_date
	if ($page->template->name == 'newsletter_child' && $page->pub_date) {
		$newName = wire('sanitizer')->pageName('newsletter-' . date('Y-M', $page->pub_date), true);
		$newTitle = date('F Y', $page->pub_date);
		if ($page->title != $newTitle) {
			$page->title = $newTitle;
		}
		if ($page->name != $newName) {
			$page->name = $newName;
		}
	}
});

 

Thiis then makes it easy to search the system for whatever you want:

// All newsletter files (no limit applied)
$allNews = $pages->find('template=newsletter_child, sort=pub_date'); // ascending order (earliest first)
$allNews = $pages->find('template=newsletter_child, sort=-pub_date'); // descending order (most recent first)

// All newsletters from a particular year (e.g., 2017):
$yearStart = strtotime('2017-01-01 00:00:00');
$yearEnd = strtotime('2018-01-01 00:00:00');
$yearNews = $pages->find("template=newsletter_child, pub_date>={$yearStart}, pub_date<{$yearEnd}, sort=-pub_date");

// The most recent newsletter:
$mostRecent = $pages->findOne('template=newsletter_child, sort=-pub_date');
 
// Accessing the file itself (using $mostRecent)
if ($mostRecent->pdf_file) {
	$file = $mostRecent->pdf_file;
	$downloadLink = "<a href='{$file->url}'>Download the newsletter: {$mostRecent->title}</a> ({$file->filesizeStr})";
}
  

 

That's all there is, really.  There is lots you can do with the system.

 

Edited by LMD
Realised I had two hooks, when I only needed one!
  • Like 6

Share this post


Link to post
Share on other sites

Thank you for all your feedback, flydev, Robin S and LMD.

I should have clarified that I have already setup a Parent page for the newsletters and the Children pages to each hold 10-years' worth of monthly newsletters - the newsletters go back to 1975 (the ones I've scanned and have yet to process) and possibly well beyond. See the screen grabs attached.

The thinking behind a single folder hierarchy for the newsletter store is that, firstly, it would allow straightforward portability should the archive ever need to be moved, and, secondly, to facilitate the automatic display of the current newsletter based upon today's yyyy-mm date, provided it has been uploaded, or otherwise display the previous month's newsletter.

The problem I foresee with storing the files as media attached to the Pages is that the paths to the files will not be predictable making the automatic displaying of the latest newsletter in the sidebar less than straightforward, if not impossible, and the newsletters will end up being scattered among many different folders meaning the portability of the archive would be less than ideal.

Any further thoughts or suggestions would be welcome. Thanks again.

ParentPage.jpg

ArchivePeriod-Child-Page.jpg

Share this post


Link to post
Share on other sites

@ridgedale, the essence of your scenario is that you have some newsletters, and for each newsletter you have:

1. A file
2. Some metadata about that file (its month and its year)

You want to associate the metadata with the file. Using a file naming scheme and a folder structure is one way to associate the metadata with the files, but it's not the only way, nor is it the "ProcessWire way".

The ProcessWire way is to use pages and/or the metadata fields that are built into File fields.

If you have a lot of metadata to associate with a file then the most powerful approach is the "one page per file" approach, which is what @LMD is suggesting. This way you can use all the available PW fields in your file page to store different kinds of metadata about each file.

But the downside to this approach is that the UI/workflow can be a bit less than ideal for clients, especially if they have a lot of files that they want to upload at once and populate metadata for. That's because, generally speaking, they will have to edit each file page individually (although there can be solutions to this such as Lister Pro inline editing, Batch Child Editor, and automatically creating a page for each file similar to what is done in AutoImagePages).

But you only have a little bit of metadata per file so I don't think you need to go this sophisticated. Instead I suggest you use a repeater for the years, and file tags for the months. See these screenshots...

2018-03-22_182925.png.e111ad104c27877a2d7609bd74ccaeaa.png2018-03-22_182955.png.fda7a4b1da7054834f1a22da6e61fdfc.png

2018-03-22_184541.thumb.png.9ff762f2829592d6a5bf2ca1df06d49f.png

Then you can get the latest newsletter like this...

$newsletters_page = $pages->get("template=newsletters");
$latest_year = $newsletters_page->newsletters->find("newsletter_files.count>0, sort=-year")->first();
$latest_newsletter = $latest_year->newsletter_files->sort("-tags")->first();
$month = substr($latest_newsletter->tags, 3);
echo "<a href='$latest_newsletter->url'>$month $latest_year->year</a>";

 

13 hours ago, ridgedale said:

I should have clarified that I have already setup a Parent page for the newsletters and the Children pages to each hold 10-years' worth of monthly newsletters

The pages that visitors browse the newsletters through can be completely decoupled from the storage of the newsletter files. You just get the relevant repeater items by year according to the page being viewed, e.g.

$years = $newsletters_page->newsletters->find("year>=1980, year<1990, sort=year");

You could even do it without actual pages for each decade, and use URL segments to get any range of years.

  • Like 6

Share this post


Link to post
Share on other sites

Hi Robin S,

Your suggestion looks like it should fit the bill. Is it possible to reverse sort the repeater fields so the most recent year appears at the top of the list?

Thanks again for all your input.

Share this post


Link to post
Share on other sites
5 hours ago, ridgedale said:

Is it possible to reverse sort the repeater fields

from @Robin S


$this->addHookAfter('FieldtypeRepeater::wakeupValue', function($event) {
    $field = $event->arguments('field');
    if($field->name !== 'myrepeater') return; // repeater field name
    $pa = $event->return;
    $pa->sort("-created"); // or whatever sort you want
    $event->return = $pa;
});

 

Will return :

  • 2018
  • 2017
  • 2016
  • ...
  • Like 3

Share this post


Link to post
Share on other sites
9 hours ago, ridgedale said:

Is it possible to reverse sort the repeater fields so the most recent year appears at the top of the list?

Another approach besides the one @flydev mentioned:

$pages->addHookAfter('saveReady', function(HookEvent $event) {
    $page = $event->arguments(0);
    if($page->template != 'newsletters') return; // Only for specific template
    $page->newsletters->sort('-year'); // Sort the repeater how you want
    // Sequentially set the sort property of the repeater items
    $i = 0;
    foreach($page->newsletters as $item) {
        $item->sort = $i;
        $i++;
    }
});

 

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By ridgedale
      Reference: PW 3.0.111 and uikit3 based site using the Regular-Master profile.
      Despite my searches of the forum I'm somewhat confused about how to create new child pages on the frontend when a user clicks on a button on the parent page. I also have an equivalent button that is intended for uploading a .csv file to automatically create multiple new pages. This basically relates to a club (parent) and members (child) template configuration. Hopefully this explanation makes sense.
      A button should be able to launch the code needed to initiate the script required to create a new page using something like:
      <a href="/path/page.php">New +</a> <a href="/path/page.php">New ++</a> Does the code to create the new page or new pages need to be run from the template file for the child or the parent?
      A new individual member page will need to be editable manually at the point of page creation as well as subsequently, whereas multiple new pages will need to be editable after they have created and populated with data, again, as well as subsequently.
      I would very grateful for any advice or pointers as to how to achieve this.
       
    • By ridgedale
      Reference: PW 3.0.111 and uikit3 based site using the Regular-Master profile.
      I was wondering if there is a way to restrict user navigation to specific pages.
      Login (home.php - not to be displayed)
          |__  About (not to be displayed)
          |__  Clubs (not to be displayed)
          |            |__ Club (to be displayed)
          |                       |__  Club Members (to be displayed)
          |__ League (not to be displayed)
          |            |__Season (not to be displayed)
          |                       |__  Match (not to be displayed)
          |__  News (blog.php -  to be displayed)
          |
      etc, etc
      Based on the above the navigation needs to appear simply as:
      ---------------------------------------------------------------------------
                           Club    Club Members    News    
      ---------------------------------------------------------------------------
      Any thoughts appreciated.
    • By ridgedale
      Reference: PW 3.0.111 and uikit3 based site using the Regular-Master profile.
      I am trying to create a process whereby when a user logs in to their profile page (the user is automatically redirected to their profile page on login) and they then go to their 'members' page and creates a child page ('club-member'), the data stored in the user template ($user->usercode) is automatically added to the equivalent field on the club-member page.  This will be applicable only to the template used for the 'club-member' pages. Once the new page has been created the equivalent 'usercode' field on the 'club-member' page should not be subsequently editable.
      Below is the page hierarchy for visual reference:
      Login (home)
          |__  profile
                      |__  members
                                     |__  club-members
      It seems like an AddHookBefore might be the solution here such as:
      wire()->addHookBefore("Pages::saveReady", function($event) { $page = $event->arguments(0); if($page->template == 'club-member') { $clubcode = $user->club_code; $field = $page->club_code; $field->set('value', $clubcode); } }); I am I on the right track or is there a better way to achieve this?
      Any thoughts appreciated.
    • By ridgedale
      Reference: PW 3.0.111 and uikit3 based site using the Regular-Master profile.
      I've setup a page where a member can edit contact details via the frontend displaying the field content using the <edit> ... </edit> tags.
      This works fine when the fields actually contain data. However fields that contain no data (i.e. empty) do not appear to be editable. No edit cursor appears (- possibly owing to the field width being 0px?)
      Is the only solution to recreate the page using a form, for example,  or is there a simple way to allow blank fields to be editable on the frontend?
      I wondered if anyone else has found a solution to this problem. Any assistance would be appreciated.
    • By ridgedale
      Reference: PW 3.0.111 and uikit3 based site using the Regular-Master profile.
      I am trying to add a field that provides a dropdown menu but there are no Options or Selector(s) type available - see attached image of field types available.
      The following reference under the docs does not appear to be applicable any more:
      https://processwire.com/api/modules/select-options-fieldtype/
      I can't see how to achieve this. Any assistance would be appreciated.

×
×
  • Create New...