Problems, need help? Have a tip or advice? Post it here.
8 posts Page 1 of 1
Hello

I create an app that uses intercooler.js for making page loads and updates dynamically. All data processing on the server uses CouchCMS. It works like a charm, but when I try to add a securefile editable, problems occur. I have no idea how to get my app working with file uploads.

The following code is a excerpt of my approach which uses intercooler.js to load all form data synamically. Probably I have not adapted all elements 100% properly in the shown code since my application is more complex and I'm trying to show only the relevant parts to get an idea how it works (except securefiles):

The page (simple placeholder page) "kontaktdaten.php":
Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<cms:template title='Kontaktdaten' executable='1' clonable='0' hidden='0' ></cms:template>
<!DOCTYPE html>
<html>
<head>
   ...
</head>
<body>
   <div class="wrapper">
      <section class="content" id="section-content">
         <div class="row">
            <div class="col-xs-12 col-sm-12 col-md-12 col-lg-9">
               <div id="content-block"
                  ic-get-from="<cms:link masterpage='app.php' />kontaktdaten/"
                  ic-trigger-on="load"
                  >
                  <div class="box box-solid" id="box-kontaktdaten">
                     <div id="spinner-refresh-content-block" class="overlay" style="min-height: 528px;">
                        <i class="fa fa-spinner fa-pulse"></i>
                     </div>
                  </div>
               </div>
            </div>
         </div>
      </section>
   </div>
   <!-- REQUIRED JS SCRIPTS -->
   <!-- jQuery 2.2.3 -->
   <script src="<cms:show k_site_link />template/plugins/jQuery/jquery-2.2.3.min.js"></script>
   <!-- Bootstrap 3.3.6 -->
   <script src="<cms:show k_site_link />template/bootstrap/js/bootstrap.min.js"></script>
   <!-- intercooler.js -->
   <script src="<cms:show k_site_link />template/plugins/intercooler/intercooler-1.1.2.js"></script>
</body>
</html>
<?php COUCH::invoke(); ?>


The data template storing the data, "data.php":
Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<cms:template title='Contact data' executable='0' clonable='0' hidden='0' >

   <cms:editable order='1' type='text' name='name' label='Firmenname' required='0' />
   <cms:editable order='2' type='text' name='website' label='Webseite' required='0' />
   <cms:editable order='3' type='text' name='address' label='Adresse' required='0' />
   <cms:editable order='4' type='text' name='zip' label='PLZ' required='0' />
   <cms:editable order='5' type='text' name='city' label='Ort' required='0' />

   <cms:editable order='0' type='securefile' name='logo' label='Firmenlogo'
      allowed_ext='jpg, jpeg, png, gif'
      max_size='128000000'
      width='600'
      height='600'
      crop='0'
      enforce_max='1'
      quality='100'
      show_preview='1'
      preview_width='50'
      preview_height='50'
      thumb_width='300'
      thumb_height='300'
      thumb_enforce_max='1'
      thumb_quality='100'
      use_thumb_for_preview='1'
   />

</cms:template>
<?php COUCH::invoke(); ?>


The web app (routable template handling the data stuff together with intercooler.js) "app.php":
Code: Select all
<?require_once( 'couch/cms.php' ); ?>
<cms:template title='Web App' executable='1' clonable='0' hidden='0' routable='1' >

   <!-- Kontaktdaten -->
   <cms:route
      path='kontaktdaten/'
      name='kontaktdaten.html'
      filters='is_ajax'
      >
   </cms:route>

</cms:template>
<cms:match_route debug='0' is_404='1' />
<cms:embed "<cms:show k_matched_route />" />
<?php COUCH::invoke( K_IGNORE_CONTEXT ); ?>


The form snippet with the load/save logic (mainly data_bound_form) "kontaktdaten.html":
Code: Select all
<cms:pages masterpage='data.php'>
   <div class="box box-solid">

      <div class="box-header with-border">
         <h3 class="box-title">Kontaktdaten</h3>
      </div>
      <!-- /.box-header -->
      <div class="box-body">
         <cms:set submit_success="<cms:get_flash 'submit_success' />" />
         <cms:if submit_success >
            <cms:php>
               header("X-IC-Script:$.notify({icon: 'fa fa-check fa-lg',title: '<b>Gespeichert:</b>',message: 'Die Kontaktdaten wurden gespeichert.',},{type: 'success'});");
            </cms:php>
         </cms:if>
         <cms:form
            _invalidate_cache='0'
            masterpage='data.php'
            mode='edit'
            enctype='multipart/form-data'
            method='post'
            id='kontaktdatenForm'
            >
            <cms:if k_success>
               <cms:db_persist_form
                  _invalidate_cache='0'
               />
               <cms:if "<cms:not k_persist_error />">
                  <cms:set_flash name='submit_success' value='1' />
                  <cms:redirect "<cms:link masterpage='app.php' />kontaktdaten/" />
               <cms:else/>
                  <cms:php>
                     header("X-IC-Script:$.notify({icon: 'fa fa-ban fa-lg',title: '<b>Datenbank-Fehler:</b>',message: '<ul><cms:each k_persist_error><li><cms:show item /></li></cms:each></ul>',},{type: 'danger', delay: '10000'});");
                  </cms:php>
               </cms:if>
            </cms:if>

            <cms:if k_error>
               <cms:php>
                  header("X-IC-Script:$.notify({icon: 'fa fa-ban fa-lg',title: '<b>Fehler beim Speichern:</b>',message: '<ul><cms:each k_error><li><cms:show item /></li></cms:each></ul>',},{type: 'danger', delay: '10000'});");
               </cms:php>
            </cms:if>

            <div class="row">
               <div class="col-xs-12 col-md-6">
                  <h3>Firmenanschrift</h3>
                  <div class="row">
                     <div class="col-xs-12">
                        <div class="form-group form-group-sm<cms:if k_error_name> has-error</cms:if>">
                           <label class="control-label">Firmenname *</label>
                           <cms:input type="bound" name="name" class="form-control" />
                           <span class="help-block" id="nameMessage"><cms:if k_error_name><cms:show k_error_name /></cms:if></span>
                        </div>
                     </div>
                     <div class="col-xs-12">
                        <div class="form-group form-group-sm<cms:if k_error_website> has-error</cms:if>">
                           <label class="control-label">Webseite * <span style="font-weight: normal;" class="text-muted" >(vollständige URL inkl. http:// oder https://)</span></label>
                           <cms:input type="bound" name="website" class="form-control" />
                           <span class="help-block"><cms:if k_error_website><cms:show k_error_website /></cms:if></span>
                        </div>
                     </div>
                     <div class="col-xs-12 col-md-12">
                        <div class="form-group form-group-sm<cms:if k_error_address> has-error</cms:if>">
                           <label>Adresse *</label>
                           <cms:input type="bound" name="address" class="form-control" />
                           <span class="help-block"><cms:if k_error_address><cms:show k_error_address /></cms:if></span>
                        </div>
                     </div>
                     <div class="col-xs-4">
                        <div class="form-group form-group-sm<cms:if k_error_zip> has-error</cms:if>">
                           <label>PLZ *</label>
                           <cms:input type="bound" name="zip" class="form-control" />
                           <span class="help-block"><cms:if k_error_zip><cms:show k_error_zip /></cms:if></span>
                        </div>
                     </div>
                     <div class="col-xs-8">
                        <div class="form-group form-group-sm<cms:if k_error_city> has-error</cms:if>">
                           <label>Ort *</label>
                           <cms:input type="bound" name="city" class="form-control" />
                           <span class="help-block"><cms:if k_error_city><cms:show k_error_city /></cms:if></span>
                        </div>
                     </div>
                  </div>
               </div>
            </div>

            <div class="row">
               <div class="col-xs-12">
                  <hr/>
               </div>
            </div>

            <div class="row">
               <div class="col-xs-4 col-sm-3 col-md-3 col-lg-2 pull-right">
                  <button id="button-save-kontaktdaten"
                     ic-post-to="<cms:link masterpage='app.php' />kontaktdaten/"
                     ic-target="#content-block"
                     ic-replace-target="false"
                     ic-on-beforeTrigger="return $('#kontaktdatenForm').valid();"
                     ic-indicator="#spinner-refresh-content-block"
                     type="submit" class="btn btn-primary btn-block">
                     Speichern <i id="edit-spinner" class="fa fa-spinner fa-spin" style="display:none"></i>
                  </button>
               </div>
            </div>

         </cms:form>
      </div>
      <!-- /.box-body -->
      <div id="spinner-refresh-content-block" class="overlay" style="display:none;">
         <i class="fa fa-spinner fa-spin"></i>
      </div>

   </div>
   <!-- /.box -->
</cms:pages>


I would be happy if someone has a solution to add securefile upload while maintaining the intercooler.js ajax-approach.

When I am finished developing the application I will probably share the intercooler.js approach for others since it allows ajaxifying a CouchCMS frontend in an easy way making fully one page web apps.

Thanks,
Olliwalli
I wonder what is the problem with a single securefile-bound input? If your form correctly submits databound form and updates data in backend successfully, then file upload should be also fine, imo.

For multiple uploads I had to do this viewtopic.php?f=2&t=10354&start=30#p27790
I also believe in good working demo playgrounds to make things work. If you can provide one, I'd step in to investigate. So far I just very briefly looked at the code and have no answer.

but when I try to add a securefile editable, problems occur.

We can start with description of the problems. :D
Hi trendoman

Now I have stripped down everything to a minimal set of files. Find the file here: securefile.zip. In the real application I work with cloned pages ... but here it is only one flat data template. But the ajaxified approach with intercooler.js works fine in the example.

No we can just jump into the problem ... when trying to add the logo editable (securefile) to the data form snippet.
Hi olliwalli,

I have successfully installed your playdemo. So, I first checked what data is being sent by placing code to app.php and index.php.
Code: Select all
<?php
    error_log( print_r($_POST, true) );
    error_log( print_r($_FILES, true) );
    error_log( print_r($_GET, true) ); ?>
<?php require_once( 'admin/cms.php' ); ?>

When I temporarily disabled ajax filter in your route in app.php
Code: Select all
    <!-- Kontaktdaten -->
    <cms:route
        path='kontaktdaten/'
        name='box-kontaktdaten.html'
        filters=''
        >
    </cms:route>

I could test if the form works at all - it indeed worked with 'bound' input type='securefile'. In server's php error_log I could see that $_FILES variable consists of uploaded file's data.

Submitting the form from index.php doesn't fill in the $_FILES variable of php, so I went to add some debug tools from intercooler.js by placing this to your snippet page.html
Code: Select all

<script>


    $(function(){
      $(document).on('beforeAjaxSend.ic', function(event, ajaxSetup, elt){

        console.log( 'ajaxSetup:' );
        console.log( ajaxSetup );
        console.log( 'event:' );
        console.log( event );
        console.log( 'elt:' );
        console.log( elt );
       
        // Add a parameter
        ajaxSetup.data = ajaxSetup.data + "&timestamp_from_javascript=" + new Date().getTime().toString();

        // Add a custom HTTP header
        ajaxSetup.headers['X-My-Custom-Header'] = "A header value";

        // Add some custom data from the triggered element
        ajaxSetup.data = ajaxSetup.data + "&from_elt=" + elt.data('custom-data');

      });


      $(document).on('beforeSend.ic', function(evt, elt, data, settings, xhr, requestId){

        console.log( 'evt' );
        console.log( evt );
        console.log( 'elt' );
        console.log( elt );
        console.log( 'data' );
        console.log( data );
        console.log( 'settings' );
        console.log( settings );
        console.log( 'xhr' );
        console.log( xhr );
        console.log( 'requestId' );
        console.log( requestId );


      });
    });

  </script>



All in all, browser's console had submitted form data without any hints to 'multipart' thing, so I make a conclusion that IC.js doesn't send the form correctly.
After checking their github, there was some closed issue back ago when file uploading didn't work, but this had been resolved. I admit, this playdemo is way overcomplicated to test if the fileuploading ever works in IC.js.
Maybe you could create 2 simple php/html pages, without couchcms? Perhaps following their form samples from docs. Then we can see that a simple html form with a simple <input type="file" name="image"> works with intercooler. We don't need a form-receiving script, we just need to see if intercooler recognizes form's enctype correctly and submits FormData() correctly to php, which will be seen via checking $_FILES as in my first test.

I am not checking it myself, because I beleive this has nothing to do with CouchCMS :) It seems if you can make a regular html work with this 3d-party plugin - then it can be couchified without issues.

P.S. Regular ajax-code for submitting a form needs only several simple lines.
After some more hacking into intercooler-1.1.2.js file, I looked into its function
function getParametersForElement(verb, elt, triggerOrigin)
..
if (elt.is('form') && elt.attr('enctype') == 'multipart/form-data') {
data = new FormData(elt[0]);
..

This function shows that submitting files may work only if the element which has intercooler attributes is a form. So FormData is being set after js plugin recognized a form was submitted. In your sample, you have ic-attributes attached to a div, which had a form inside.
I think after implementing a form directly with ic-attributes, things should start rolling. Can you please test it?
Hi trendoman

I finally got time again to work on this problem. Some minor changes made 'uploading' work like a charm:

In file 'content-form-kontaktdaten.html' I removed the ic tags from the submit button:
Code: Select all
<div class="row">
   <div class="col-xs-4 col-sm-3 col-md-3 col-lg-2 pull-right">
      <button id="button-save-kontaktdaten" type="submit" class="btn btn-primary btn-block">Speichern</button>
   </div>
</div>


In 'box-kontaktdaten.html' I added the ic tags to the form:
Code: Select all
      <cms:form 
         _invalidate_cache='0'
         masterpage='kontaktdaten.php'
         mode='edit'
         enctype='multipart/form-data'
         method='post'
         id='kontaktdatenForm'
         ic-post-to="<cms:link masterpage='app.php' />kontaktdaten/"
         ic-target="#content-block"
         ic-replace-target="false"
         ic-on-beforeTrigger="return $('#kontaktdatenForm').valid();"
         ic-indicator="#spinner-refresh-content-block"
         >


Uploading works now as stated. But the "Delete file" button dies not work. Anyway I do not like the tag showing the filename and the unformatted 'Delete file' button.

Any Idea how to customize the delete button and make it work?

Probably by removing the html code from couch/addons/data-bound-form/securefile.php and adding a custom button with intercooler tags and remove the file server side with couch tags?

Any idea how this could be achieved? I would be happy to solve this problem finally.

Thanks in advance,
regards,
olliwalli
@KK

I do not understand how the button "Delete File" works. Is there a couch tag wheree I can remove the securefile programmatically?

I tried
Code: Select all
<cms:if k_success>
   <cms:db_persist_form
      _invalidate_cache='0'
      last_change="<cms:date format='Y-m-d H:i:s' />"
      logo=''
   />

where logo is an editable of type 'securefile'. But nothing happens. The "Delete File" button does not work as stated.

Any help?

Regards,
olliwalli
@olliwalli, I am not sure if it will help but I'll try to answer by explaining how the delete internally works with securefile.

All deletes happen automatically and in the background.
When a file is uploaded, an entry is made about it in the database (couch_attachments table) with its status set to 'orphan'.
When the page is finally successfully saved (cms:db_persist or cms:db_persist_form), the orphan status is removed.

The system, at regular intervals, searches for files with 'orphan' status and deletes them automatically.

So, in our example, if we were to upload one file, and then change our mind and upload another in its place (which is all that the 'delete' button you see does - it does not physically delete the file), both the uploaded files will have 'orphan' status. When the page is saved, the second file will have its orphan status removed while the first one will remain an orphan and will thus be garbage collected automatically anytime in the future.

You may try using your solution and see if this works for you. I suggest you keep an eye on the attachments table to see how the uploaded files get added to it and how their orphan status changes.

Hope it helps. Do keep me posted.
8 posts Page 1 of 1