Jump to content

Mass create pages OR mass upload images and thus create pages


Recommended Posts

Hi folks,

This might be more a DB/mySQL question, perhaps, but thought I'd put it to the forum for discussion.

I have a lot of pages to add to a certain section of my PW site.


> Portrait 1

> Portrait 2


> Portrait 100

Each page has one image only.

I am wondering if it is possible, if the image filenames and the titles of each 'page' are the same, if I can somehow mass upload these images to create pages from? Either through the backend or via the DB? So, for example:

julian.jpg > Julian (page created with this image)

kate.jpg > Kate (page created)


mat.jpg > Mat (page created)

This, of course, is only to save time... but wondered what you all thought? Or, even still, is it possible to mass create pages much like you can do with templates?

Thanks for your time.

Link to comment
Share on other sites

class ImageBatcher extends WireData implements Module {

     * getModuleInfo is a module required by all modules to tell ProcessWire about them
     * @return array
    public static function getModuleInfo() {

        return array(

            'title' => 'Image Batcher', 
            'version' => 1, 
            'summary' => 'Do some additional actions after uploading an image',
            'singular' => true, 
            'autoload' => true,  

     * Initialize the module
    public function init() {
        // do additional action after upload
        $this->addHookAfter('InputfieldImage::fileAdded', $this, 'createPageFromImage');

     * create Page from uploaded Image
    public function createPageFromImage($event) {

        $file = $event->arguments(0);
        $parent = wire('pages')->get('/batcher'); // didn't know how to get the current page dynamically 

        // create new page
        $np = new Page();
        $np->parent = $parent;
        $np->template = 'portrait';
        $np->title = $file->name;
        $np->save(); // page has to be saved before adding images

        // add image


played a little around... something like this? :)

i didn't know how to get the current page dynamically... and i didn't manage to clear the added files from the "batcher" page because i got some errors i can't explore in detail right now.

this thread should also give you useful information: https://processwire.com/talk/topic/8698-creating-page-for-every-image-uploaded/

edit: i think hooking the method fileAdded should keep you save from timeouts though other things may get more complicated as it are all ajax requests (like the error i got when trying to remove all uploaded images from the batcher page)

edit2: of course you would have to check if the file was added to a "batcher" page. otherwise all your image uploads will create new pages. or you could bind the module to a specific imagefield only.

  • Like 1
Link to comment
Share on other sites

you would have to name this file ImageBatcher.module and place it in your modules folder /site/modules

then go to modules > install > new and maybe refresh, then install

but this is no working and bulletproof code! use a dev-installation to test everything!

here are lots of informations:



Link to comment
Share on other sites

Thanks, BernhardB. I'm thinking a PHP query that writes each image from a folder into the DB, as it is a one-off scenario, and uses a similar structure to what you suggested.

All the filenames have the year and title of the page, so for example:

2006_ALI.jpg should equal a page created called Ali, under the parent of 1026 and the date field should have the 2006

parent id in the DB for each page is 1026

title field in the DB is field_title (eg. Ali)
date field in DB is field_works_date (eg. 2007)
image field in the DB is field_works_image (eg. ocean_liner.jpg)

What do you think? Possible?

Link to comment
Share on other sites

sure possible

why do you want to write directly to the DB? if you create pages you have all the benefits of processwire like page editing, pagination, file handling and so on.

just use my example and replace:

$parent = wire('pages')->get(1026); // your given id of the new parent

$np->title = your part of the image filename that represents the title (see php string functions)

$np->date (you have to create this field) = part of the filename that represents the date (year)

Link to comment
Share on other sites

UNTESTED, probably have some typos + poorly formatted code (I gotta run...)

You want to add this to your template file....Wrap it around code that ensures only Superuser can load the code...

//absolute path to a directory called 'tmp' within the site folder where we have our images
$dir = $config->paths->site . 'tmp';

$addAndPublish = false;//true if you want to publish on save
//prepare some variables we'll need later
$a = 0;//for photo pages count
$failed = array();

$parent =$pages->get('/portraits/');
$t = wire('templates')->get('your-portrait-template-name');

//if we found the directory (here we use Standard PHP Library (SPL))
if (is_dir($dir)) {

    $directory = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS); 

    //iterate through each file in this directory
    foreach ($directory as $path ) {            

       set_time_limit(30);//we try to avoid timing out

       //Remove and delete invalid file types
       $validImagesExts = array('jpg', 'png', 'gif', 'jpeg');
       if($path->isFile() && !in_array($path->getExtension(), $validImagesExts)) {
        unlink($path);//we delete the invalid file

     //if valid image file we create a page named after it and later save it to the page
     if($path->isFile()) {

     //we are ready to start creating pages...
     $p = new Page();
    $p->parent =$parent;
    $p->template = $t;
    $title = $path->getBasename('.' . $path->getExtension());
    $p->title = $this->sanitizer->text($title);
    if (!$p->title) continue;//skip to next if no title found (just in case)
   $p->name = $this->sanitizer->pageName($p->title);//sanitize and convert to a URL friendly page name

    //check if name already taken
    if($p->parent->child("name={$p->name}, include=all")->id) {

        //if the same name already exists, add it to the $failed array [to display to user in error later] and skip to next title
        if ($path->isFile()) {
          $failed [] = $path->getFilename();


     //if add and publish is false, we save new page unpublished
     if (!$addAndPublish) $p->addStatus(Page::statusUnpublished);

      //add image to the page and save again
      $p->images_field->add($dir . '/' . $path->getFilename());

      unlink($path);//we delete the temp file


}//end foreach

     //delete the tmp directory
     wireRmdir($dir, $recursive = true);

    //create a string of "failed" category titles to add to error message
    $failedTitles = implode(', ', $failed);

    //give the user some feedback...
    if($a > 0) echo 'Added' . $a . 'Portraits<br>';
    if($failedTitles) echo 'Some Portraits not added because names already in use. These are:' . $failedTitles;

}//end if (is_dir($dir)))

else echo 'No Such Directory Found!';
Edited by kongondo
  • Like 7
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Similar Content

    • By David Karich
      The Page Hit Counter module for ProcessWire implements a simple page view counter in backend. Page views of visitors are automatically tracked on defined templates, with monitoring of multiple page views. This gives you a quick overview of how many visitors have read a news or a blog post, for example, without first having to open complex tools such as Google Analytics. This module quickly provides simple information, e.g. for editors. Or, for example, to sort certain news by most page views. For example for "Trending Topics".

      Works with ProCache and AdBlockers. With a lightweight tracking code of only ~320 bytes (gzipped). And no code changes necessary! In addition GDPR compliant, since no personal data or IP addresses are stored. Only session cookies are stored without information. 
      In addition, there are some options, for example filtering IP addresses (for CronJobs) and filtering bots, spiders and crawlers. You can also configure the lifetime of the session cookies. Repeated page views are not counted during this period. It is also possible to exclude certain roles from tracking. For example, logged in editors who work on a page are not counted as page views.

      Sort by hits and access page views (hit value)
      Each trackable template has an additional field called phits. For example, you want to output all news sorted by the number of page views.
      // It is assumed that the template, e.g. with the name "news", has been configured for tracking. $news = $pages->find("template=news, sort=-phits"); To output the page views of a tracked page, use:
      echo $page->phits; Example: Reset counter per API
      $modules->get("PageHitCounter")->resetPageViews("template=whatever", false); Example: Tracking a page hit via API and jQuery
      If you want to track a template that does not represent a full page to automatically inject a tracking script, you can define allowed API templates in the module that you can track. Below is an example of how you can track a click on news tag using jQuery. This will allow you to find out which keywords are clicked the most. For example, you can sort and display a tag cloud by the number of hits. Suppose your keywords have the template "news_tag". The template "news_tag" was also configured in the Page Hit Counter Module as a trackable API template.
      Example PHP output of keywords / tags:
      // Required: the data attribute "data-pid" with the ID of the template to be tracked. echo $pages->find("template=news_tag, sort=-phits")->each("<a href='{url}' class='news_tag' data-pid='{id}'>{title}</a>"); Example Tracking Script with jQuery:
      /** * Required: Data attribute "data-pid" with the ID of the news tag template * Required: Send the POST request to the URL "location.pathname.replace(/\/?$/, '/') + 'phcv1'" * Required: The POST parameter "pid" with the ID of the template */ $(function(){ if($('a.news_tag').length > 0) { $('a.news_tag').each(function(){ var tPID = $(this).data("pid"); if(tPID) { $(this).on("click", function(){ $.post(location.pathname.replace(/\/?$/, '/') + 'phcv1', {pid: tPID}); }); } }); } }); So simply every click on a tag is counted. Including all checks as for automatic tracking. Like Bot Filtering, Session Lifetime, etc.
      Notice: Tracking with URL segments
      If the option "Allow URL Segments" is activated on a template, the hits are only counted if the base URL of the page is called. If you want the hit to be counted even when a segment is requested, you MUST configure the segments in the template configuration. How to do this can be found here. If you use dynamic segments, configure them as RegEx. There is currently no other option. The problem is that the Page Hit Counter hooked into the PageNotFound process. If URL segments are allowed but not defined, a 404 is never triggered. This means that the Page Hit Counter cannot be called.
      New since 2.0.0: Ignore URL segments
      If a template has URL segments configured, each hit on a different segment is counted as a new hit. Enable "Ignore URL segments" so that dynamic segments are not counted individually on the base template / page.
      New since 2.0.0: Use cookieless tracking (Experimental)
      Enable this option to not use individual cookies for tracking or if you have many different pages you want to track. The limit for cookies is 50 per domain for all cookies on the page. If the option is enabled, PHP session storage is used. Downside: you can't set the lifetime higher than configured in your PHP.ini and the session will be terminated as soon as the browser is closed.
      Upgrade note for 2.0.0 from previous versions!
      Version 2.0.0 requires an update in the database schema, so that additionally the date of the last access / hit on the page can be displayed ($page->lastPageHit). To make this possible, you have to do the update via the upgrade module, upload the ZIP itself and do an update directly via the backend AND DO A MODULE REFRESH DIRECTLY AFTER UPLOAD/UPDATE. If you do not do this, you will get an error that a column is missing in the database table.
      Background: This module is the result of a customer requirement, where the editors are overwhelmed with analytics or no tracking tools were allowed to be used. However, a way had to be found to at least count page views in a simple form for evaluations. Furthermore, by using ProCache, a way had to be found to count views of a page without clearing the cache.
      Automatic Page View Tracking Lightweight tracking code, only ~320 bytes (gzipped) No code or frontend changes necessary Works with ProCache! Even if no PHP is executed on the cached page, the tracking works Works with browser AdBlockers No cache triggers (for example, ProCache) are triggered. The cache remains persistent GDPR compliant, session-based cookie only, no personal information Filtering of IPs and bots possible Exclude certain roles from tracking Ability to reset Page Views Works with all admin themes Counter database is created as write-optimized InnoDB API to track events for templates that are not viewable No dependencies on libraries, pure VanillaJS (Automatic tracking script) Works in all modern browsers Pages are sortable by hits Cons
      Only for ProcessWire version 3.0.80 or higher (Requires wireCount()) Only for PHP version 5.6.x or higher No support for Internet Explorer <= version 9 (Because of XMLHttpRequest()) No historical data, just simple summation (Because of GDPR) Segment URLs can only be counted if the segments are defined Planned Features / ToDos
      API access to hit values Since version 1.2.1 Possibility to sort the pages by hits (Request by @Zeka) Since version 1.2.0 Don't track logged in users with certain roles (Request by @wbmnfktr) Since version 1.1.0 Possibility to reset the counter for certain pages or templates (Request by @wbmnfktr) Since version 1.1.0 Better bot filter Since version 1.1.0 Disable session lifetime, don't store cookies to track every page view (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree on certain templates Since version 1.2.1 API to track events for templates that are not viewable Since version 1.2.2 Cookieless tracking Since version 2.0.0 Show last hit Since version 2.0.0 Ignore URL segments (Request by @bernhard) Since version 2.0.0 Add hookable method after pageview was tracked (Request by @bernhard) Since version 2.0.0 Changelog
      Feature request: Add hookable method after pageview was tracked (___pageViewTracked($pageID)) (Requested by @bernhard) Feature request: Ignore URL segments option (Requested by @bernhard) New: Cookieless tracking New: Show date of last hit Update: Botlist Enhancement: Documentation improvement 1.2.7
      Feature request: make buildPageListHitCounter-Function public (Requested by @bernhard) 1.2.6
      Bug-Fix: Set the counter of a cloned page to 0 Enhancement: The function for resetting counters is now available in the module as a public function to reset counters via own scripts on the API side (Request by @VeiJari) Enhancement: Documentation improvement API reset 1.2.5
      Bug-Fix: When counting 404 hits, cookies are no longer set. The session lifetime is deactivated for the 404 page Enhancement: Documentation improvement regarding URL segments 1.2.4
      Bug-Fix: Resetting the counters on system pages (e.g. 404) does not work (Reported by wbmnfktr) Bug-Fix: Tracking endpoint is logged as 404 if module "Jumplinks" is installed (Reported by wbmnfktr) Enhancement: Corrected few typos (Merged from Sergio #6 – THX!) 1.2.3
      Bug-Fix: Tracking script triggers 404 if pages are configured without slash (#3) Reported by @maxf5 Enhancement: Reduction of the tracking script size if it's gzipped (~320 bytes) Enhancement: Documentation improvement Enhancement: Corrected few typos 1.2.2
      New feature: API to track events for templates that are not viewable Enhancement: Documentation improvement 1.2.1
      API access to hit values Use $page->phits Bug-Fix: No tracking on welcomepage (Reported by wbmnfktr; Thx to matjazp) Bug-Fix: Tracking script path on subfolders (Reported by matjazp) Bug-Fix: Tracking on pages with status "hidden" Enhancement: Change database engine to InnoDB for phits field Enhancement: Option to disable session lifetime set session lifetime to 0, no cookies Enhancement: Better installation check Enhancement: AJAX Request asyncron Enhancement: Reduction of the tracking script size by ~20% Enhancement: Option to hide the counter in the page tree You can output the counter with the field name "phits" Enhancement: Option to hide the counter in the page tree on certain templates Enhancement: Option for activate general IP validation Enhancement: Reduction of tracking overhead up to ~30ms Enhancement: Better bot list for detection 1.2.0
      New feature: Sort pages by hits – New field phits Migrate old counter data to new field 1.1.0
      New feature: Exclude tracking of certain roles New feature: Reset Page Views Better bot filter and detection 1.0.0
      Initial release Notes
      By default, the page views are stored as INT in the database. This allows a maximum counter value of 4.2 billion views (4,294,967,295) per page. If you need more, change the type to BIGINT directly in the database. But I recommend to use Google Analytics or similar tools if you have such a large number of users.
      Download GitHub: ProcessWire Page Hit Counter (Version 2.0.0)
      PW Module Directory: ProcessWire Page Hit Counter (Version 2.0.0)
      Install via ProcessWire (Classname): PageHitCounter
      Update information
      If you have used version 1.2.1 from the DEV branch, please replace it completely with the new master version.
      Old stable version
      Download GitHub: ProcessWire Page Hit Counter (Version 1.2.7)
    • By benbyf
      Hi, Looking to create form elements on a page–some input with a colection of form inputs and the appropriate labels and variables for that input. I've used ProForms in the past and rolled out my own when creating simply one off forms, but I wonder if anyone has found a good way of allowing form creation on page editing so that clients can adhocly make and edit forms?
    • By dimitrios
      this module can publish content of a Processwire page on a Facebook page, triggered by saving the Processwire page.
      To set it up, configure the module with a Facebook app ID, secret and a Page ID. Following is additional configuration on Facebook for developers:
      Minimum Required Facebook App configuration:
      on Settings -> Basics, provide the App Domains, provide the Site URL, on Settings -> Advanced, set the API version (has been tested up to v3.3), add Product: Facebook Login, on Facebook Login -> Settings, set Client OAuth Login: Yes, set Web OAuth Login: Yes, set Enforce HTTPS: Yes, add "https://www.example.com/processwire/page/" to field Valid OAuth Redirect URIs. This module is configurable as follows:
      Templates: posts can take place only for pages with the defined templates. On/Off switch: specify a checkbox field that will not allow the post if checked. Specify a message and/or an image for the post.
      edit the desired PW page and save; it will post right after the initial Facebook log in and permission granting. After that, an access token is kept.
      PW module directory: http://modules.processwire.com/modules/auto-fb-post/ Github: https://github.com/kastrind/AutoFbPost   Note: Facebook SDK for PHP is utilized.

    • By Atlasfreeman
      I want to make a small site, a one page site.
      And i have this idea about doing 2 to 3 diffrent template that i can load into the index / home page.

      I want to do this with an array so that i can keep creating more topics (with the template) id needed.
      <?php include('./head.inc'); // include header markup ?> <?php $children = $page->get('template=onecolmn|twocolumn, sort=sort'); foreach($children as $child) { include($child); } ?> <?php include('./foot.inc'); // include footer markup ?>  
      As you can see i have to template wish i want to control from the backend.
      I know include does not work this way, put what is my other option to make an array that loads the whole page on an other pages.
    • By Liam88
      Hi All,
      New user over at Processwire and have been rebuilding my site based on this CMS.
      I have been able to find so many answers through Google but I'm a little stuck on this one.
      I have my services page -> services categories -> category children.
      An example of those would be - domain -> services -> ppc -> management
      I also have a set of tags which have different names - services-tag -> grow-your-traffic
      Under these tags I would have multiple links to pages such as ppc, seo, social media and so on.
      A second example would be - services-tag -> convert-your-traffic
      Under here i would have multiple links to pages such as CRO
      Now the set of tags are not visible on-site as they are only created to give overview content to the main services categories.
      Using the categories and the tags I am looking to produce a layout such as (i have also attached an image as an example:
      Tag_1 headline
      Pull all services categories linking to Tag_1
      Tag_1 snippet
      Tag_2 headline
      Pull all services categories linking to Tag_2
      Tag_2 snippet
      So far I have this snippet which is pulling in the tag content but unable to get the posts to show under each of the tags. 
      If i change the if and statement to "tags" instead of "tag" then all posts show under all tags. Where as i want it to show only the posts which are linked to that tag.
      <?php namespace ProcessWire; $tags = $pages->get("/categories-services/")->children(); // Gets the tags $posts = $pages->get("/services/")->children(); // Gets the services categories $link = $tags->ref_6; // Gets the tags and services categories link - under here you have pages_id (services cat id) and data (tags id) // Tag header and summary foreach($tags as $tag) { // This breaks down the tags into sections echo '<section id="services"> <div class="container"> <div class="row"> <h2 class="heading-1"><span>'. $tag->headline.'</span></h2> <p class="mb-5">'. $tag->summary.'</p> </div> <div class="row justify-content-around services">'; // Main services categories that link to the above tags if ($posts->id === $link->pages_id && $tag->id === $link->data){ foreach($posts as $service){ // This pulls in the services categories under the tag header. echo '<div class="card flex-card" id=""> <div class="card-img"> <a href="/'. $service->name.'" title="'. $service->name .'"> <img class="card-img-top" src="../assets/files/'. $service->id.'/'. $service->img_1.'" alt="'. $service->img_1.'" title="'. $service->img_1.'"></a> </div> <div class="card-body"> <h3 class="card-title">'. $service->headline.'</h3> <p class="card-text">'. $service->summary .'</p> <div class="card-action"> <a href="" title="'. $service->name .'" role="link" class="link">View service<span></span></a> </div> </div> </div> '; } } // Grey snippet text echo '</div> </div> </section> <div class="snip-2 light-grey"> <div class="container"> <div class="row text-center">'. $tag->get('grey') .'</div> </div> </div>'; } ?> I appreciate this is a long post but i'm trying to be clear as I appreciate everyone's time.
      Any insight into where I am going wrong is greatly appreciated.

  • Create New...