Jump to content

kongondo

PW-Moderators
  • Content Count

    6,054
  • Joined

  • Last visited

  • Days Won

    99

Posts posted by kongondo


  1. You go away for a minute and this is what happens...😀

    TL;DR

    From Device-centric to App-centric development: ambient computing

    1. Dart and Flutter ranked #1 and #2 for fastest-growing language and open source project respectively over the last twelve months - GitHub’s 2019 State of the Octoverse report.
    2. Flutter is now one of the ten most starred software repos on GitHub.
    3. Flutter described as “the fastest-growing skill among software engineers”. LinkedIn, 2019.
    4. Flutter for web is in beta.
    5. Flutter for desktop is in alpha for mac-OS.
    6. You can edit Flutter code, run it and view the rendered UI online in DartPad.
    7. Adobe XD, Supernova, etc: flutter plugins.

    Full announcement here. Videos here (Flutter Interact 2019).

    • Like 5
    • Thanks 1

  2. Update: Blog 2.4.5

    Changelog

    1. Added new option (see example use below) to MarkupBlog::getArchives() that allows to specify if archive months should be sorted descending (Dec - Jan). Default is ascending (Jan - Dec), thanks to question by @montero4
    2. Fixed bug in template blog-archives.php that caused illegal offset warnings.
    Module has been updated in the modules directory.
     
    Example usage of new getArchives() option
     
    getArchives() now accepts a third parameter as an $options array. If you want archive months to be sorted and rendered in descending order (December - January), you will need to pass this as an option in the $options array and set the value 'descending' to they key 'archives_month_sort_order'. Since this is the third parameter for the method, you will need to pass options for the first and second parameters as well. If you want the default ascending order of months, you don't have to change anything in your code. Just call getArchives() as usual without any parameters.  Otherwise, read on for descending order of months.
     
    <?php
    $blog = $modules->get("MarkupBlog");
    // options: if we want to sort archive months in descending order
    $archiveOptions = array('archives_month_sort_order'=>'descending');
    // get archives: order months descending
    $archives = $blog->getArchives(0, 1, $archiveOptions);
    // if you want to render the archives
    $content = $blog->renderArchives($archives);// order months descending

    Please test and let me know.

    Thanks.

     
     
    • Like 4
    • Thanks 1

  3. Hi @montero4,

    Welcome to the forums and ProcessWire. This seemed like a nice feature to add so I have gone ahead and added it as an option to getArchives(). I'll release over the weekend.

     

    PS: I have moved your thread to the blog support forum.

    • Like 1

  4. Downloading Media From Media Manager Library

    I was recently asked whether it was possible to download media already uploaded to Media Manager. Currently, there is no automated way to do this. However, using the ProcessWire API, one can achieve this. Below is a starter code showing how one can download image media from Media Manager. I'll consider adding this feature to Media Manager.

    Please note that I have deliberately left Tracy debugging statements in the code. You will want to wrap this around a check if user is Superuser.

    // get limited number of media manager images
    $mmPages = $pages->find('template=media-manager-image, limit=10');
    $tempName = 'media_manager_downloads';// temporary name for folder to place images in
    
    // 1. MAKE DIRECTORY
    // @note: could have used $files->tempDir('hello-world'); but not working for some reason
    // create a new directory in ProcessWire's cache dir
    $copyTo = $config->paths->cache . $tempName;
    if($files->mkdir($copyTo)) {
        // directory created: /site/assets/cache/media_manager_downloads/
    
        // 2. COPY IMAGES
        // get and copy image assets
        foreach($mmPages as $p) {
            $images = $p->media_manager_image;
            foreach($images as $image) {
                //bd($image->filename,'image path')// to debug image path
                $copyFrom = $image->filename;
                $bool = $files->copy($copyFrom, $copyTo);
                //bd($bool,'copied?');// just to test if files were copied
            }
        }
    
        // 3. ZIP IMAGES
        // if files were copied, zip them
        if(!empty($files->find($copyTo))){
            // create zip of all files in directory $copyTo to file $zip
            $zip = $config->paths->cache . "media-manager-image-files.zip";
            $result = $files->zip($zip, $copyTo);
    
            echo "<h3>These files were added to the ZIP:</h3>";
            foreach($result['files'] as $file) {
                echo "<li>" . $sanitizer->entities($file) . "</li>";
            }
    
            if(count($result['errors'])) {
                echo "<h3>There were errors:</h3>";
                foreach($result['errors'] as $error) {
                    echo "<li>" . $sanitizer->entities($error) . "</li>";
                }
            }
    
            // 4. SEND FILES TO BROWSER FOR DOWNLOADING
            // if files were added to zip file, send them
            if(!empty($result['files'])){
                bd($zip,'files were added');
                $files->send($zip);
            }
        }
    
        else {
            bd('NO FILES FOUND!');
        }
    
    
    }

    Hope this helps.

    • Like 2

  5. On 2/5/2020 at 8:30 PM, xportde said:

    Do you mean Image maximum width / height? So I misunderstood the notes Maximum width / height of resized images (pixels),

    Yes.

    On 2/5/2020 at 8:30 PM, xportde said:

    I thought, this settings are for resizing, not for uploading?

    They are for both. If using resizing feature, the images will be constrained to those limits. If they fall short of the minimum, the images are discarded. If not using resizing, original images should meet the minimum and maximum constraints. 

    On 2/5/2020 at 8:30 PM, xportde said:

    Maybe you could write the notes accordingly (Max width / height for uploaded images)?

    Good idea. I'll see how to better word this. I am coming up blank at the moment 😁


  6. 1 hour ago, xportde said:

    there's no option for limiting uploaded images to image width and/or height in MM settings (or is there?),

    Actually, there is 😁. See under Settings > Upload Settings > Image. There are settings for min/max height and min/max width. These are directly coupled to the the field's (media_manager_image) corresponding settings. If you change one it will change the other. However, I have just noticed a bug. The min values are decoupled! This shouldn't matter much though since Media Manager will still honour the min values. I will fix these later (i.e. the 'decoupling' issue).

    1 hour ago, xportde said:

    so that the user can see possible violations before start uploading (like the maximum file size rule from MM settings)?

    Excellent suggestion! I will add this to my TODO as well. 

    NO ETA for above, just yet, sorry. I hope it isn't urgent?

    Thanks!

     

     

     


  7. Welcome to the forums @Neeraz,

    5 hours ago, Neeraz said:

    because i needed css class in <a> tag,

    It was decided early on to add classes to the <li> instead of the <a>, as it was the usual practice.

    6 hours ago, Neeraz said:

    suggest me if there is any better way out!!

    Getting the <a> via CSS is quit easy, for instance...

    #menu li a {
    /* your styles here*/
    }

    You can be as specific as  you want with your CSS.

    6 hours ago, Neeraz said:

    added following line in MarkupMenuBuilder.module

    Not a good idea as this will get overwritten next time you upgrade Menu Builder :-).


  8. 21 hours ago, Nicolas said:

    @kongondo regarding the payment process, have you planned the ability to pay a fraction of the total cost (make a deposit) and schedule future payment ?

    Hmm. This is a tricky one as it is made up of two parts:

    1. Paying a fraction (instalment) of the total cost
    2. Future payment (aka recurring payment)

    Both are tricky, but especially #2. 

    #1 is partly achievable by raising an invoice using manual order creation. The order will be manually marked as complete once the total has been paid. Automating the process may not be an easy task. I'd have to think about it more.

     

    # 2 If this means storing user card details, then no; we are not going down that route. However, if it means using payment providers' subscription/recurring payments services, then it is probably doable. The payment providers API is there. Something else to think about, but for future versions.

    Currently none of these features are in the plan, but I'll have a think. I will probably need reminding though :-).

    • Like 2

  9. On 12/27/2019 at 8:35 AM, Ivo said:

    Would it be relatively easy to hook in a shipping provider (ie Australia Post)? Also I assume it would be easy to send data post transaction to Xero?

    The response I was going to give is aptly summarised by @gornycreative  (quoted below...)

    On 12/28/2019 at 10:46 PM, gornycreative said:

    I know this sounds super easy, and for one provider it sortof is. The issue might be that updating and keeping track of freight APIs for everyone would become a costly development project in itself. Might make more sense to provide API hooks that can be used to push to services like IFTTT or Zapier and see if they already have connectors to postal service APIs.

    It would be a nightmare to keep track of global freight. We will provide hooks on our side to facilitate this. However, whilst we will strive to cater for different scenarios, we will prioritise making sure the API works for the bigger providers first and/or the most popular (with respect to our client base). Please note that this (and other advanced features will be incrementally added to Padloper 2 but will certainly not make it in the first release).

    On 1/21/2020 at 12:44 AM, Mikie said:

    Does anyone know what is involved for connecting products to / selling through instagram?

    This is not currently planned. It might make it on my to do list for future releases, depending on demand.

    On 1/24/2020 at 6:41 PM, PWaddict said:

    @kongondo are you planning to add affiliate system?

    No. This is not planned. It might be considered depending on demand, though.

    On 1/10/2020 at 9:42 PM, J_Szwarga said:

    Looking forward to when the good news comes! 👍🤞

    As @Pete said earlier, if you need anyone to test it out, give us a shout.

    Thanks!

    On 1/13/2020 at 7:42 AM, alexmercenary said:

    The anticipation is bubbling 😁

    😁

    • Like 3

  10. Update: Menu Builder 0.2.7

    Changelog

    1. Added the property extra_fields for use with getMenuItems() ONLY. This enables returning the page field values for some selected Fieldtypes. For example, the description field in the page corresponding to the menu item. Thanks @e0f for the inspiration. Full documentation of this new feature is available here.
    2. Menus in multi-lingual sites can now be called using their multi-lingual titles or names irrespective of the current user's language. For example, a multi-lingual site with English, German and Finnish languages could have a menu with the respective titles My Awesome Menu / Mein Tolles Menü / Oma Mahtava Valikko. Any of these titles can be used in the code to call the menu (using render() or getMenuItems() methods). [PS: blame you-know-who for these language translations :-)]
    3. Changed the way new menus are added. Only one menu can be added at a time. The title field/input is multi-lingual ready. See screens below.
    4. Improved the backend GUI when viewing locked menus. Only the menu items and and exit button are shown in such cases.
    5. Fixed bug that allowed access to unpublished menus for frontend rendering.
    6. Updated the documentation
    7. Refactored code to improve efficiency.

    In dev branch only for testing.

    Screenshots

    a. Add Menu (Multi-lingual)

    mb-version-27-add-menu-multilingual-screen.thumb.png.1c5654f2eae1e95c5fcee2b4baf5f1d1.png

     

    b. Edit Menu (Multi-lingual)

    mb-version-27-edit-menu-multilingual-screen.thumb.png.2c5d93ef748b5b9298f51d4ed67ee366.png

    c. Locked Menu

    mb-version-27-locked-menu-screen.thumb.png.991346ad1977d5d2216c2c872ed96e95.png

    d. An example usage of extra_fields (I am no designer, so yeah, nothing much to see here...but you get the idea :-))

    mb-version-27-extra-fields-screen.thumb.png.4f981af877a394d4d2175f0294b1181d.png

     

    Thanks for testing!

    • Like 2

  11. On 1/8/2020 at 5:52 PM, e0f said:

    Is there a way to put a new field (ex: description) on each menu entries? I like to have something like:


    Title: Home
    Description: My beautiful home page
    URL: /my-beautiful-url/

    I wait your feedback.

    Thanks and have a nice day!

    Hi @e0f,

    Currently, this is not doable out of the box. However, using the method getMenuItems() (see a few posts up for examples), you can use the property pagesID (or index pages_id if getting back array of menu items) to get ($pages->get($m->pageID), for example) the respective ProcessWire pages of the menu items and return the custom fields you want, e.g. description. Having said that, I would not recommend this. It seems like an overkill and may not be very performant based on the number of menu items and other factors.

    Perhaps if you could explain your use case, I would see if I can add an option to retrieve the values of simple fields (text, integers, dates, etc) for use with getMenuItems(). MenuBuilder is primarily for building navigation menus. From your example, it seems you are using it for a different purpose? Maybe provide a fuller example so that I can get my head around this.

    • Like 1

  12. Anything that repeats in any programming language calls for, invariably, something you can iterate/traverse (e.g. an array) and the tool you use for the traversal/looping, (e.g. foreach). Let's go with these two.

    Looking at the UIkit markup you've pasted, I suppose some of the classes are automatically injected by UIkit JavaScript. However, the basic structure of the markup that repeats is the same. Hence, I'll ignore markup classes for now.

    The main differences in the images ratio markup are the following:

    The main containers for the images vary in the class they have. The first, the second and the fourth have the class:

    <div class="uk-width-expand@s">...</div>

    The 3rd image container has a different class:

    <div class="uk-width-2-3@s">...</div>

    Secondly, the <img> markup themselves have different values for their data-height attribute.

    Hence, we will need some variable to store these differences. Although some of the values are shared, for instance, the class for the main images container for the 1st, 2nd and 4th images (as stated above), I prefer to store each image's values separately. This can help down the line if the markup changes resulting in different values that were previously identical.

    Although it may be simple to assume that the images in your image field (which I will refer to as images) are already arranged in order and are in the correct size, from experience, we know that this will most likely NOT be the case. Clients can upload images that are not of the correct size and arrange them randomly. You may need to cater for this, e.g. have the clients tag the images with first, second, etc....but that may be too much work. 

    OK, for the fun part. 

    Please note that in the examples below, I only focus on the images' container part. I have also left in the CSS just for reference, although they may be auto-injected as stated above.

    We have at least two ways to loop through our images. Both ways involve having an array that will have 4 nested arrays, one each for our 4 images. The first way to loop through our images is to loop through the images themselves and access the array with images info inside this loop as shown below:

    Spoiler
    
    $imagesInfo = array(
        0 => array('height' => 604, 'container_class' => 'expand@s'),
        1 => array('height' => 604, 'container_class' => 'expand@s'),
        2 => array('height' => 400, 'container_class' => '2-3@s'),
        3 => array('height' => 820, 'container_class' => 'expand@s'),
    );
    $galleryPage = $pages->get("/gallery/");
    $counter = 0;
    $out = "";
    
    
    foreach($galleryPage->images as $image) {
    
        // grab the info for the current image
        $imageInfo = $imagesInfo[$counter];
        $containerClass = $imageInfo['container_class'];
        $height = $imageInfo['height'];
    
        $out .= "
            <div class='uk-width-{$containerClass}'>
                <div class='uk-margin-remove-vertical uk-text-center' uk-scrollspy-class>
                <a class='el-container uk-inline-clip uk-transition-toggle uk-link-reset' href='#'>
                    <img class='el-image' data-src='{$image->url}'
                        data-sizes='(min-width: 610px) 610px' data-width='610' data-height='{$height}'
                        alt='{$image->description}' uk-img>
                    <div class='uk-overlay-default uk-transition-fade uk-position-cover'></div>
                    <div class='uk-position-center uk-position-small'>
                        <div
                            class='uk-overlay uk-padding-large uk-transition-fade uk-margin-remove-first-child'>
                            <h3
                                class='el-title uk-h4 uk-heading-divider uk-transition-slide-top-small uk-margin-top uk-margin-remove-bottom'>
                                Lorem Ipsum</h3>
                            <div class='el-content uk-panel uk-transition-slide-bottom-small uk-margin-top'>
                                Comfort Food</div>
                        </div>
                    </div>
                </a>
            </div>
        </div>   
        
        ";
    
        // increment counter
        $counter++;
    
    }
    
    
    echo $out;

     

     

    The main downside to the above approach is we are assuming there will always be four images in the images field. What if a client adds more? What if there are less images? All these will require adding checks in the code (within the loop) to check how many images we have. To counter some of these challenges, we can instead loop through the array with the info for images as shown below:

     

    Spoiler
    
    $imagesInfo = array(
        0 => array('height' => 604, 'container_class' => 'expand@s'),
        1 => array('height' => 604, 'container_class' => 'expand@s'),
        2 => array('height' => 400, 'container_class' => '2-3@s'),
        3 => array('height' => 820, 'container_class' => 'expand@s'),
    );
    $galleryPage = $pages->get("/gallery/");
    $images = $galleryPage->images;
    $out = "";
    
    
    foreach($imagesInfo as $i => $value) {
    
        // get the image corresponding to the index
        $image = $images->eq($i);
    
        // grab the info for the current image
        $containerClass = $value['container_class'];
        $height = $value['height'];
    
        $out .= "
            <div class='uk-width-{$containerClass}'>
                <div class='uk-margin-remove-vertical uk-text-center' uk-scrollspy-class>
                <a class='el-container uk-inline-clip uk-transition-toggle uk-link-reset' href='#'>
                    <img class='el-image' data-src='{$image->url}'
                        data-sizes='(min-width: 610px) 610px' data-width='610' data-height='{$height}'
                        alt='{$image->description}' uk-img>
                    <div class='uk-overlay-default uk-transition-fade uk-position-cover'></div>
                    <div class='uk-position-center uk-position-small'>
                        <div
                            class='uk-overlay uk-padding-large uk-transition-fade uk-margin-remove-first-child'>
                            <h3
                                class='el-title uk-h4 uk-heading-divider uk-transition-slide-top-small uk-margin-top uk-margin-remove-bottom'>
                                Lorem Ipsum</h3>
                            <div class='el-content uk-panel uk-transition-slide-bottom-small uk-margin-top'>
                                Comfort Food</div>
                        </div>
                    </div>
                </a>
            </div>
        </div>   
        
        ";
    
    }
    
    echo $out;

     

    This is just basic code to get you started. There are a number of things that could be improved. For instance, you may want ProcessWire to resize the images for you (done once) rather than depending on your browser to contain larger images. See the Resize and crop section in the API documentation here. There are alternative approaches as well to the examples shown above. 

    PS: code untested

     

    • Like 3
    • Thanks 1

  13. 7 hours ago, spiroue said:

    with a status field set to booked (Multiple Select)

    Why does this have to be Multi Select? Shouldn't it be either checked or not (i.e a single select?) 

    4 hours ago, spiroue said:

    But when a page, a booking, for example changes status from 'offered' to 'booked'

    Aah, I see, it seems you have different status? 

    4 hours ago, spiroue said:

    With Pages::added only new pages will trigger the hook right?

    Correct.

    4 hours ago, spiroue said:

    But when a page, a booking, for example changes status from 'offered' to 'booked' then I need an email to be sent.

    If that's the case, it is better to use Pages:saved. However, please further explain your scenario, i.e.

    1. If a page is already set to booked, but it is edited, should an email be resent?
    2. What is booked? Is it the name of a page?
    3. What if a booking is cancelled, is an email to be sent?

  14. 8 minutes ago, gebeer said:

    This I already covered. Got it from the Map Marker example module 🙂

    EDIT: New v1.1.2 went out just before I read this. Will update soon.

    Great!

    Btw..

    You don't need to set inbuilt ProcessWire columns like 'pages_id in your getDatabaseSchema() :-). ProcessWire will do it automatically. Same with extra, unless you really are adding something extra.

    1 minute ago, gebeer said:

    @kongondo all is good! That  ___getSelectorInfo() method was exactly what I needed. It is already live on github

    Thanks for confirming! 😂👍

    • Like 2

  15. 1 hour ago, kongondo said:

    In __sleepValue() and __wakeupValue() convert it from/to your property and in getMatchQuery() make sure to pass the correct $subfield (as you are currently doing in both respects).

    Oops. I was looking at the wrong file, so not sure if you are currently doing this? If not, here are examples:

    public function ___wakeupValue(Page $page, Field $field, $value) {
    
        // some code...
    
        // start a blank value to be populated
        $myClassWireArray = $this->getBlankValue($page, $field); 
    
        // if we were given a blank value, then we've got nothing to do: just return a blank MyClassWireArray
        if(empty($value) || !is_array($value)) return $myClassWireArray; 
    
        // create new myClassObj objects from each item in the array
        foreach($value as $v) {
            $myClassObj = new MyClass();
          
            
            // @note we're converting 'data' to 'property1' (this is the filename)
            $myClassObj->property1 = $v['data']; 
            $myClassObj->property2 = $v['column_2']; 
            $myClassObj->property3 = $v['column_3'];
            $myClassObj->setTrackChanges(true); 
            $myClassWireArray->add($myClassObj); 
        }
    
        $myClassWireArray->resetTrackChanges(); 
    
        return $myClassWireArray;
    
    }
    
    public function ___sleepValue(Page $page, Field $field, $value) {
    
    		$sleepValue = array();
    
            // some code....
    
    		// convert each MyClassObj to an array within sleepValue
    		foreach($value as $myClassObj) {
    			$sleepValue[] = array(
    				'data' => $myClassObj->property1, // @note: property1 is becoming data (this is the filename)
    				'column_2' => (int) $myClassObj->property2,
    				'column_3' => (int) $myClassObj->property3
    				); 
    		}
    
    		return $sleepValue;
    
        }

     


  16. You might also need something like this in your getMatchQuery()

    // if normal sql characters, do 'normal' query, else do fulltext search
    if($this->wire('database')->isOperator($operator)) {
    	return parent::getMatchQuery($query, $table, $subfield, $operator, $value);
    }
    
    else {
      $ft = new DatabaseQuerySelectFulltext($query);
      $ft->match($table, $subfield, $operator, $value);
      return $query;
    }

     

    • Like 1

  17. 53 minutes ago, gebeer said:

    custom fieldtype and always wondered how to have the alias for data show up here.

    Try ___getSelectorInfo(). Grep wire folder for full examples. Here's some example code. @see the label index in the code.

    <?php
    
    	/**
    	 * Get information used for InputfieldSelector interactive selector builder
    	 *
    	 * This is for Lister purposes.
    	 * We want nice labels for our lister selects (i.e. not raw db ones, i.e. 'some_column').
    	 *
    	 * @param Field $field The field we are working with.
    	 * @param array $data Array of extra data, when/if needed.
    	 * @return array
    	 *
    	 */
    	public function ___getSelectorInfo(Field $field, array $data = array()) {
    		$info = parent::___getSelectorInfo($field, $data);
    		
    		## filterable subfields for this field ##
    
    		// we get rid of the subfield 'data' instead, we'll use 'custom one' => IF APPLICABLE TO YOU!
    		if(isset($info['subfields']['data'])) unset($info['subfields']['data']);
    		// unset misc_data (example column we don't need in filter)
    		if(isset($info['subfields']['misc_data'])) unset($info['subfields']['misc_data']);
    		$subfields = array(
    			// @note: @see  getMatchQuery! 
    			
    			// text column example
    			'some_text_column' => array(
    				'name' => 'some_text_column',
    				'input' => 'text',
    				'label' => $this->_('My Text Column'),// NICE LABEL FOR LISTER
    				// @note: comment out those that don't make sense!
    				'operators' => array('%=', '!%=', '*=', '!*=', '~=', '!~=', '^=', '!^=', '$=', '!$=', '=', '!=', '=""', '!=""'),
    				'options' => array(),
    			),
    			// number column example
    			'some_number_column' => array(
    				'name' => 'some_number_column',
    				'input' => 'number',
    				'label' => $this->_('My Number Column'),
    				// @note: commented out those that don't make sense
    				'operators' => array('=', '!=', /*'<', '>', '<=', '>=',*/ '=""', '!=""'),
    				'options' => array(),
    			),
    		);
    
    		$info['subfields'] = array_merge($info['subfields'], $subfields);
    
    		return $info;
    
    	}

    If it doesn't work, I'll need to dig a litter deeper...

    • Like 1
×
×
  • Create New...