Jump to content

Chris.Smith

School User
  • Posts

    139
  • Joined

  • Last visited

Posts posted by Chris.Smith

  1. 36 minutes ago, Corinne said:

    @Chris.Smith That sounds amazing thank you. This something they want to test before September so sounds like this would definitely do the job for now - do I have to get Frog code turned on for our school, as I don't think we have it at the moment. 

     

    Hi @Corinne,

    You should be fine using just the Package Manager which is available for Admin users in the current release (Dickens). If you have not installed a FrogCode Widget or Application before, head on over to the Community Site and checkout @Graham Quince's video at https://www.frogcommunity.com/frogcode/tutorials and click on "Installing a downloaded widget".

    @Graham Quince will be sharing the widget imminently so stay tuned for updates :)

    ~ Chris

  2. 21 hours ago, Corinne said:

    @Chris.Smith did you happen to find any code for this?
    Thanks

    Hey @Corinne,

    Due to complications in the way that Sites manages widgets I have been unable to do this via the HTML widget. I could have shown a read-only view of the comments but that seemed a little like a cop out. So what I've done is create you a FrogCode widget which, when dropped on a page within a site, will detect the user info uuid and link that widget to the assignment wall.

    I'm going to spend sometime today cleaning it up and making sure that it works under all circumstances within the Assignment workflow. 

    It is worth saying however, that the Assignment wall is being built into a new Assignments based UI in Sites, meaning that soon, this will be some what pointless; but you've got this in the meantime!

     

    ~ Chris

    /cc @gbligh, @Graham Quince

  3. 1 minute ago, Corinne said:

    @Chris.Smith did you happen to find any code for this?
    Thanks

    Hi Corinne,

    Sorry, I got put on a high priority project and have been slammed up until now.

    I have a gap in my schedule this morning so I'll have a look now and see what I can come up with for you :)

    ~ Chris

  4. 5 hours ago, pconkie said:

    Amazing Chris, thanks. Makes sense.

    Couple of related questions....

    Does an app (not a widget) support preferences?

    On smaller screens i've noticed that apps are docked/maximised when they are initialized.  Is there a way to force an app to be maximised on initialization regardless of screen size?

    Thanks

    Paul

     

    Preferences aka Widget Preferences are only available for Widgets. If you let me know what you would like to do I can possibly provide you with some solutions.

    With regards to maximising applications; in your application manifest:

    {
      "application": {
        "args": {
          "mode": "fullscreen"
        }
      }
    }

    Hope that helps,

     

    ~ Chris

    • Like 1
  5. On 2017-6-6 at 09:52, pconkie said:

    @Chris.Smith @Graham Quince any advice please on the following?

     

    I am trying to combine information from two frog api calls.  

    Firstly calling getMembers to return basic information about every student in a particular group.  Secondly looping through each student and calling getDataInCategory to return extra information.  I am assuming there is no extra parameter I can add to the first call to get the extended information without making the second call! Regardless I quite like to know how you would go about beating the async issues anyway.

    The issue is that the first call does not wait for the second calls to complete.  

    
     Frog.Model.api('groups.getMembers',{ignore_profiles: true, uuid: my_group_uuid}).done(function(thisResponse) {
    	$.each(thisResponse.data, function(i, member) {
        	//loop through each member of the group
            //build object as we go
          	var thismember = {uuid: member.uuid, name: member.displayname, gender: member.gender, thumb: member.thumbnail, data:[]};
          	
        			Frog.Model.api('users.getDataInCategory',{user_uuid: member.uuid,uuid: student_flags_uuid}).done(function(thatResponse) {
                    		$.each(thatResponse.data.fields, function(x, data) {
        					//loop through each field of extra data and add the data to the data array in the thismember object
                              		var key = data.label;
                                    	MyContext[key] = data.value;
                            });
                      	thismember.data.push(MyContext);     //this happens after MyGroup.push(thismember)
                    });
        	//the code below doesnt wait for the inner api call
        	MyGroup.push(thismember);		//this line always has 'data' empty.
        });
     });

    I've read about closures, async=false and promises.  I don't understand the first and last, and async=false seems to be really bad practise,

    So what's the recommended approach here?  How can i get an object out of the end of this which contains basic and extended information for each of the students in the group?

    Thanks for your continued support

    P

     

    Hi @pconkie,

    Sorry for not getting to this sooner, I've been saddled with some emergency work which has taken over my life for the past two weeks...

    Handling async functionality is not for the faint of heart and it's here where you start to see the divide between 'scripters' and programmers. In this instance, Promises are your friend. There are different specifications for Promises, but the one most commonly used and widely available in all browsers is jQuery's implementation.

    As you may have worked out, Frog.Model.api returns a jQuery Promise object. When we have api's that we are racing (running at the same time) we need to collect all the promise objects and use jQuery's when function to wait for them all to complete. Here is your code, reworked using jQuery Promises.

    var MyGroup = [],
        my_group_uuid = 'my_group_uuid',
        student_flags_uuid = 'student_flags_uuid';
    
    Frog.Model.api('groups.getMembers', { ignore_profiles: true, uuid: my_group_uuid })
        .done(function(resp) {
            var members = res.data,
                requests = [];
    
            requests = members.map(function(member) {
                member = {
                    uuid: member.uuid,
                    name: member.displayname,
                    gender: member.gender,
                    thumb: member.thumbnail,
                    data: {}
                };
    
                MyGroup.push(member);
    
                return Frog.Model.api('users.getDataInCategory', { user_uuid: member.uuid, uuid: student_flags_uuid })
                    .done(function(resp) {
                        var fields = resp.data.fields || [];
    
                        fields.forEach(function(field) {
                            member.data[field.label] = field.value;
                        });
                    });
            });
    
            jQuery.when.apply(this, requests)
                .done(function() {
                    console.info('Yay! You now have all your data');
                });
        });

    I you would prefer, you can also store the requests array out side of the first API call and also move the jQuery.when to the same level. Any code that needs to execute after the APIs have completed will need to be in the callback of the associated done function.

    Here is a basic example:

    // inside my fictional app
    this.showLoader();
    Frog.Model.api('someapi.endpoint', params)
    	.always(this.hideLoader.bind(this)) // No matter the result, always remove the loading gliph
    	.fail(this.handleError.bind(this)) // Deal with error state gracefully
        .done(
            function(response) {
                var data = this.transformApiData(response.data);
              
                this.render(data);
            }.bind(this)
        );
    
    // after we have sent the request, lets set a timeout to fire in ~3 seconds if the request takes too long.
    setTimeout(
        function() {
            if (this.hasLoader()) {
            	this.updateLoader("This is taking longer than anticipated");
            }
        }.bind(this),
        3000
    );

    The setTimeout will execute after the request is sent but before the response has been captured, regardless of how quick the network response is.

    Async is something you will pickup over time; but it is definitely one of those "the more you do..." things.

     

    Let me know how you get on,

     

    ~ Chris

    /cc @Graham Quince

    • Like 1
  6. 31 minutes ago, pconkie said:

    @Chris.Smith

    Twice now I've been using FrogCode and FrogLearn has crashed. Both times the server has needed restarting by the network manager to get frog back up. 

    In both cases FrogCode froze up causing me to close and re-open chrome. Then immediately got a 500 error on desktop - frog redirects to app/redirect on mobile.

    If there are logs that can uncover what is actually going on here and if indeed linked to FrogCode then the last occurance was approx 02.00 Monday morning. Hopefully you can track the cause down!

    Thanks

    Paul

    Hi Paul,

    Can you please report the "outage" to the service desk as our systems engineers will need to be aware of any issues of this nature. I have had a brief chat with them and they tell me that at 02:00 your database gets backed up and this can cause performance issues / lock-outs during the backup process. It is likely that this is what happened. We also have a collection of maintenance tasks (cron jobs) which run during the small hours of the morning, and whilst these should run unnoticed, some may cause performance slow downs due to the complexity of the operation (hence why they are scheduled during the early morning).

    For the time-being we are confident that FrogCode is not linked to this issue, but I will continue to monitor it.

    ~ Chris

  7. 11 minutes ago, Corinne said:

    The teacher who requested this asked for teacher feedback - I would assume it was the general homework comment/wall type display from a specific assignment/s. 

    They also want a homework display of the results the students has received, like a mini mark book display I think to show progress through quizzes 

    Yeah, this should all be possible... I'll have a dig around this afternoon for you... I think I have some code lying around that should give you a starter-for-ten.

     

    ~ Chris

  8. On 2017-5-23 at 14:16, Corinne said:

    Potentially not possible but hopefully someone can help. 

    One of our departments are creating an e-portfolio site within Frog. They want it to display work for each half term, 1 beginning piece and 1 final piece to show progress. They also want to have the feedback for the first piece displayed directly in the page from the assignment manager/feedback so the student can then work on this feedback before adding the second/final file - is this at all possible? Can you embed feedback for a each student for a particular assignment into a page they all see their own copies of this. 

    Also is it possible to link to or display that file within the page, either directly from the assignment manager or can you use the file drop - student adds the file - this is then displayed on each students page below?

     

    @Corinne,

    When you say "feedback", do you mean Frog's Feedback application (Annotations and Stickers) or the "Assignment Wall"?

     

    ~ Chris

  9. 27 minutes ago, Chris.Smith said:

    @pconkie,

    I have just logged into your platform and it is telling me that there are no updates available to any of your packages?! Did you manage to update them after your previous post?

    ~ Chris

    Ignore all of that Paul,

    Good news, is that it is indeed actually, updating your source code. The error is occurring in the part of the system the updates the FrogOS Database records about your Widget. This is why it is allowing you to update source code within increasing your version number. The good news is that this is fixed already and should be with you soon!

    Whilst annoying, this issue should not affect you whilst you are developing Widgets.

     

    I have also looked at the styling problem you mentioned previously. I can confirm that your styles are being loaded correctly into the browser and I am struggling to see any conflicts with any other widget overriding your styles. Without messing around with your code I can't begin to tell you what is going wrong. Let me know if you would like me to have a tinker; otherwise, I would suggest copying across all the timetable styles that you are currently using. This will reduce the risk to your widget should we change something. Also it will mean you can remove the timetable widget classes you are using also meaning you won't be at risk of *something else* doing things to your widget!

    Finally, we do tell browsers not to cache anything in app/package and app/staging however they don't always listen. I you can, try clearing your browser data in Chrome's settings. You can tell it just to remove "Cached images and files" and this should do the job sufficiently.

    With all that said, there are a heap of updates and improvements coming to you soon that should help you out!

    ~ Chris

  10. 3 minutes ago, Chris.Smith said:

    Hi Paul,

    I think that should work... I've read the code that adds a Widget to a page and it includes origin_site_uuid in its options!

    I'm going to log into your platform and see if I can figure out whats going on with your update.

    Watch this Space,

    ~ Chris

    @pconkie,

    I have just logged into your platform and it is telling me that there are no updates available to any of your packages?! Did you manage to update them after your previous post?

    ~ Chris

  11. 1 hour ago, pconkie said:
    
    
     

    Hi @Chris.Smith

    Another possible bug? 

    Capture.thumb.PNG.dc966a8817383b4005fa45f5224ad86f.PNG

    This started all of a sudden. I have updated and re-deployed a FC Widget before without issue.  But yesterday the Notice Board widget stopped being update-able.  I tried uninstalling and reinstalling, but that didn't help.  The widget runs without error in preview, but fails silently when being updated.  Hopefully you can see from the screen grab the 500 error on packages.update method.

    Any ideas?

    Thanks for your help yesterday. I presume form what you said about site uuids that inside FrogCode something like this would check to see if the page is nested and return the origin site uuid if true, otherwise returning the site uuid?

    
    var site_uuid = this.options.origin_site_uuid;
      if (typeof site_uuid === 'undefined') {
      //not nested on another page
      site_uuid = this.options.site_uuid;
     }

    Paul

     

    Hi Paul,

    I think that should work... I've read the code that adds a Widget to a page and it includes origin_site_uuid in its options!

    I'm going to log into your platform and see if I can figure out whats going on with your update.

    Watch this Space,

    ~ Chris

  12. On 2017-5-22 at 23:05, pconkie said:

    We use the frog noticeboard widget on our dashboards (all-staff email groups don't exist!) and a filedrop for any attachments.  

    This doesn't work great - it's sometimes hard to find the right attachment and files remain long after notices have expired.

    So I thought i'd have a go at recreating the noticeboard widget with the option to attach files directly to the notice.

     

    I've managed to do this.  But I have a few things on the wishlist....... 

    1. With advice from @Chris.Smith i'm using the site assets to store the attachments. However, I've noticed that when the page with my noticeboard is nested on our dashboard the attachments get stored in the dashboards assets instead.  I'd rather not do this, so i'm wondering if there is a way to get the uuid of the site the widget is on when that site is nested on a dashboard? This: var site_uuid = this.elements.site.find('[data-site-uuid]:first').data('site-uuid'); doesn't do it.

    2. I currently have to drag a frog noticeboard widget onto a page and hunt down the uuid of the forum that got created to hijack. I then provide that forum uuid to the widget via a preference.  Is there a way in edit mode when my noticeboard is dragged onto a page to create a new forum and return the uuid (and i suppose to be tidy, delete a forum when the widget is deleted from a page).

    3. Frog seems to use CKeditor in various places in order to add custom mark-up to text.  Is it possible to turn my 'content' textarea into a WYSIWYG enabled element using CKeditor?

    4. I'm using this https://froglearn.backwellschool.net/app/public/sprite/os-icon-ext/sprite.png frog sprite map to get the correct file type images for each attachment. However is this reliable across different froglearn instances? Should i be uploading as a widget asset? I'm also getting a weird border around the images which I think is actually sue to not setting a src on the image placeholder i'm using.  Is there a frog transparent.png somewhere i can use or again, should i be uploading one as an asset.

    That's it for now!

    Thanks

    Paul

    Hi Paul,

    I can address some of your issues now but I'll have to get back to you on the rest.

    1. Try...

    this.elements.page.controller().options.origin_site_uuid

    2. I will have to get back to you...

    3. It is supposed to be simple... but for some reason our CKEditor component doesn't want to work for me today, I'll get back to you.

    4. For a reliable way to display file icons, use the extension as a css class on a div tag. For example:

    .os-icon-ext-jpeg, .os-icon-ext-jpg

     You can add a class of small to get a smaller icon. Here is a full example

    <div class="os-icon-ext os-icon-ext-jpeg">
      <span class="file-name">JPEG</span>
    </div>

     Hope that helps,

    ~ Chris

    • Like 2
  13. 10 hours ago, pconkie said:

    Possible bug -

    Just created first FrogCode Widget on own platform. Styles appeared correctly during testing, but once the widget was deployed and dragged onto a page in a site the widget didn't have the styles applied.

    Styles were written as suggested:

    
    [data-name="Widget.TimetableViewer"].widget-content {
      background: none;
      text-align: left;
      width: 100%;
      margin: 0;
    }

    And all looked good when previewing from within the FrogCode Editor.

    To get the styles to be applied correctly I had to remove the .widget-content part of the css above.

    Paul

    9 hours ago, pconkie said:

    Don't think this one is a 'bug', but....

    
    var site_uuid = this.elements.site.find('[data-site-uuid]:first').data('site-uuid');

    This line works in a html widget but not in FrogCode. 

    Hi Paul,

    You are right, that line won't work natively in FrogCode widgets as it is inserted by the engine running the HTML Widget. That being said however, FrogCode widgets are provided with the site_uuid on initialisation.

    this.options.site_uuid

    I will try and login to your platform today and try and diagnose your styling problem. Hopefully it is something simple and I'll be able to fix it for you today.

     

    ~ Chris

    • Like 1
  14. On 2017-5-15 at 19:59, pconkie said:

    If anyone is interested, I went with option 3 above.

    It displays slightly differently when staff or parents view a page with it on. This is what a student sees: 

    Capture.PNG.c5a0ba3e680a7d5f1759d60317eebf34.PNG

     

    That looks great Paul! How much was it in the end to reduce the data down?

    ~ Chris

  15. 17 hours ago, pconkie said:

    Yep.

    I would like to code a button like this:

    Capture.PNG.bed3dd4803b8eaf8d4d1eec3c151356c.PNG

    To open this:

    Capture2.PNG.44c3d61a53add8f4760d77511dfd9f48.PNG

     

    And return the uuid(s) of any file(s) uploaded.

     

    I assume the file up-loader needs somewhere to put the files?  But if this can all be done without using a filedrop at all, then even better.

    Thanks, I know your busy.  Any hint what the "top secret" stuff is your working on?

    Paul

    Hi Paul,

    Try this...

    <button class="btn btn-default" role="button" data-action="launch-uploader">
      Upload
    </button>
    
    <script type="text/javascript">
      var you_want_models = false,
          just_uuids = true;
      
      this.element.find('button[data-action=launch-uploader]')
        .on('click', function(ev) {
          var site_uuid = this.elements.site.find('[data-site-uuid]:first').data('site-uuid');
    
          ev.stopPropagation();
        
          $('.os_core:first').trigger('os.app.upload', {
            "data": {
              "upload_type": "asset",
              "site_uuid": site_uuid,
              "focusApp": this.element.closest('div.app_dialog'),
              "callback": function(result) {
                var files, uuids;
    
                if (result.files && result.files.length) {
                  if (you_want_models) {
                    files = Sites.Models.Assets.models(result.files);
                    uuids = Array.prototype.map.call(files, function(file) {
                      return file.attr('uuid');
                    });
                  } else if (just_uuids) {
                    uuids = result.files.map(function(file) {
                      return file.uuid;
                    });
                  }
    
                  // uuids = Array('uuid 1', 'uuid 2');            
                }
              }
            }
          });
        }.bind(this));
    </script>

    This will pop the uploaded files in the sites assets meaning you can manage them from the Site Assets Manager. You will notice that I have included two options for processing. It is preset just to return uuids, however if you change the boolean values at the top of the script tag then it can return a list of asset models which will give you more information about the file.

    Hope this helps,

     

    ~ Chris

    • Like 1
  16. 51 minutes ago, pconkie said:

    Yep.

    I would like to code a button like this:

    Capture.PNG.bed3dd4803b8eaf8d4d1eec3c151356c.PNG

    To open this:

    Capture2.PNG.44c3d61a53add8f4760d77511dfd9f48.PNG

     

    And return the uuid(s) of any file(s) uploaded.

     

    I assume the file up-loader needs somewhere to put the files?  But if this can all be done without using a filedrop at all, then even better.

    Thanks, I know your busy.  Any hint what the "top secret" stuff is your working on?

    Paul

    It's been one of our most requested features for a long time, you'll find out at Conference ;)

    ~ Chris

  17. 29 minutes ago, pconkie said:

    Hi @Chris.Smith

    Is it possible to open the fileupload dialogue and simultaneously pass the filedrop uuid using a similar approach to the code above?

    That would open up some interesting possibilities.

    To clarify, during Live or Edit mode?

     

    ~ Chris

  18. Hi @clangstaff,

    It would need to go inside the event callback:

    $(getHTMLElem).on('click', function(ev) {
      /* code goes here */
    });

    You would also need to make sure the assignments uuid is added to the html markup the same way you have added the "assignment-link"

    Hope that helps,

    ~ Chris

  19. @clangstaff,

    This is because you are telling the Sites application to open 'a site', not 'an assignment'. The difference is subtle, but important. This is from the Assignments Application:

    var self = this,
        params = {
            site: '',
            assignment_uuid: this.options.assignment.attr('assignment_uuid')
        };
    
    ev.stopPropagation();
    if (this.options.status === "closed") {
        params.mode = 'readonly'
    }
    
    Sites.Models.Assign.findOne({
        uuid: this.options.assignment.attr('assignment_uuid')
    }).done(function(site) {
        params.site = site.attr('link');
    
        self.trigger('os.app.siteviewer', params);
    });

    Now this will need some modification, as you don't have access to several elements of this code.

    Based on a previous example, lets trim it to something like:

    var $assignment = $(el.currentTarget);
    
    FrogOS.openSite({
      site: $assignment.data('assignment-link'),
      assignment_uuid: $assignment.data('assignment-uuid')
    });

    Super important that you have that assignment uuid. This is what will help link the data.

    ~ Chris

  20. On 2017-5-15 at 08:40, clangstaff said:

    @Chris.Smith @Graham Quince We have come across a bug or issue with the 'my assignments' code in this forum.

    Basically when a user opens an assignment using the 'assignments list' code and completes a Frog quiz on that assignment no other students marks will be received when they complete the assignment too. Any subsequent students can complete the quiz and also click 'Complete assignment' however their quiz results are not submitted to the teacher.

    Anyone have any idea why?

     

    @clangstaff can you provide your code for me to try and replicate the issue. Either:

    • Copy and paste into the reply
    • Copy into jsbin and add a link here
    • Message your code to me on here

    I'm busy working on a top-secret project at the moment, so I might be worth considering the first two options and hope that some one in the community has some ideas.

    ~ Chris

    • Like 1
  21. On 2017-5-12 at 14:16, Graham Quince said:

    Hi,

    Yes, there is a way of doing this.  You should think about replacing the current widget.edit section, with a widget.edit and widget.save

    This example is from the office365 widget, but there's a few others on the alpha build:

    
            'widget.edit': function(el, ev, data) {
                this.renderPlaceholder();
            },
            'widget.save': function(el, ev, data) {      
                this.renderPlaceholder();
            },
            renderPlaceholder : function() {
                this.element.html(
                    this.view('placeholder.ejs')
                );
                this.element.find('.placeholder').css("height",this.prefs.height.value+"px");
                this.element.find('.placeholder').css("line-height",this.prefs.height.value+"px");       
            }

    This runs a function called renderPlaceholder which uses .find to update elements of placeholder.ejs 

    You can also add the same function call in for the widget.live  function too.

    @sclough,

    Has this fixed your issue?

    I have checked your widget out on the beta build and I can't see any errors!?

     

    ~ Chris

  22. 58 minutes ago, mobrien said:

    @Chris.Smith Yes, that's right, a different colour for each House colour

    Michael,

    Assuming that it the colours are set (i.e. Bowman = red, Fulford = green etc) these could be set in styles as classes:

    td.bowman {
      background-color: red;
    }
    td.fulford {
      background-color: green;
    }

    or data attributes:

    td[data-house="bowman"] {
      background-color: 'red';
    }
    td[data-house="fulford"] {
      background-color: 'green';
    }

    Remember of course to set those attributes against the row when preparing the template either using $row.find('td[data-field=house]').addClass(datum.house) or using $row.find('td[data-field=house']).attr('data-house', datum.house).

    If this isn't the case you could dynamically generate the colours using Hexadecimal and cache the results to ensure the consistency.

    var house_colors = {}, getColorForHouse;
    
    getColorForHouse = function(house_name) {
      var data = {};
      
      if (house_name in house_colors) {
        return house_colors[house_name];
      }
      
      data.color = (Math.floor(Math.random() * 16777215)).toString(16);
      data.text = (parseInt(data.color, 16) > 0xffffff / 2) ? '000000' : 'FFFFFF';
      
      house_colors[house_name] = data;
      
      return data;
    };
    
    renderData = function(data) {
      data.forEach(function(datum) {
        var $row = $template.clone(),
            color_data = getColorForHouse(datum.house);
    
        for (var key in datum) {
          $row.find('[data-field='+key+']').text(datum[key]);
        }
    
        houses[datum.house] = null;
        tutor[datum.tutor] = null;
        applyTableFilter();
    
        $row.find('[data-field=house]')
          .css({
            'backgroundColor': '#' + color_data.color,
            'color': '#' + color_data.text
          });
        
        $tbody.append($row);
      }.bind(this));
    }.bind(this);

    Let me know how you get on :)

     

    ~ Chris

  23. 19 minutes ago, mobrien said:

    @Chris.Smith

    I had a good play with this yesterday evening and I'm not far from what appears in my mind's eye (see attachment). I removed some of the filter conditions so that Tutors have only those that they will need.

    The final part of the jigsaw is whether there is the possibility of conditionally formatting the 'House' cells/column. i.e. that the 'Bowman' cell appears one colour, 'Fulford' another colour etc. I can see that there is javascript if, then else possibility, but I have no clue how this might be written into the new json code that I am now using.

    Do you have any suggestions?

    Michael

     

    Capture.PNG

    Michael,

    Would you want the house cells to be specific colours? i.e. Bowman is always red because that is the house's colour

     

    ~ Chris

  24. 14 hours ago, mobrien said:

    @Chris.Smith Wow. This is superb - far more than I had expected.

    It will take me some time to process through, but this is exactly what I needed and more. I am very grateful. Glad it was a welcome distraction along the way too!

    @pconkie I hope one day to be at some sort of level where I can decode your comments! Aiming for the sky and hitting the ceiling at the moment.

     

    Thank you all for your help today.

    M.

    @mobrien,

    No problems, let me know if you need any of it explaining / if you have any questions.

    ~ Chris

×
×
  • Create New...