Jump to content

Graham Quince

Administrators
  • Posts

    2,046
  • Joined

  • Last visited

Posts posted by Graham Quince

  1. It was very quiet in the last couple of days leading up to Christmas, so I finally got a chance to put together an advent calendar.  Obviously too late for 2023, so all the links are set up for December 2024.  There's 48 images on the front page, with Rules managing their visibility.  Each "door" links to a different hidden page.

    There is a fun bit of trivia on each page, but I'd strongly encourage you to edit this and make it your own - you could add:

    • prizes
    • quizzes
    • walls
    • pictures
    • videos
    • messages from teachers

    advent_calendar_screenshot.png

    You can find it on the FrogStore along with an example with a few open pages.

    https://www.frogeducation.com/frogstore/content/advent

  2. Using the Pencil icon on the Quick launch, you can remove all the links you've set Globally and for yourself, but you can't remove links individuals have made.  These links are by definition the links an individual wants quick access to.

  3. Hi everyone,

    It seems with the ongoing, ummm... issues with Twitter, that the latest feature to be removed is embedding tweets.  I've got this happening on one test platform, is it happening elsewhere?  We might need to pull the widget if it can no longer be used.  Such a shame

    Graham

  4. Just had a case where we needed to display the form data in a table:

    <div class="refresh">
        <span class="ff-refresh-mono"></span> Refresh
    </div>
    <div style="float: right;" class="btn download_doc">
        <span class="ff-download-mono"></span> Download for MS Word
    </div>
    <div class="docx">
        <div class="WordSection1">
          <table class="table table-bordered table-hover my_data"></table>
        </div>
    </div>
    <script>
    var refreshButtonOBJ = arguments[0].find('.refresh');
    var downloadDOC = arguments[0].find('.download_doc');
    var docx = arguments[0].find('.docx');
    var myDataDiv = arguments[0].find('.my_data');
    var user = FrogOS.getUser();
    var formUUID = 'FORM_UUID';
    
    function getObjSummary() {
        
        myDataDiv.empty();
        Frog.Model.api('dataviewer.gettable', {
            content_uuid: formUUID,
            current_user_only: 'false',
            form_uuid: formUUID,
            limit: 50,
            module: 'form',
            offset: '0',
            sort_dir: 'DESC',
            sort_field: 'date'
        }).done(function(response) {
            var submissions = response.data;
            
            myDataDiv.append('<tr class="headerrow"></tr>');
            var headers = submissions[0].fields;
            $.each(headers, function(ind,field) {
                if (field.type !== 'freetext') { // Freetext is the text widget field
                    myDataDiv.find('.headerrow').append(
                        '<th>'+field.label+'</th>'
                    );
                }
            });    
            $.each(submissions, function(index, submission) {
                myDataDiv.append('<tr class="sub_'+index+'""></tr>');
                var fields = submission.fields;
                $.each(fields, function(index1,field) {
                    if (field.type !== 'freetext') {
                        myDataDiv.find('.sub_'+index).append(
                            '<td>'+
                            field.responses[0].response+
                            '</td>'
                        );
                    }
                })       
            });
        }).fail(function(e) {
    
            console.log('Failed to load form');
        });
    }
    getObjSummary();
        
    refreshButtonOBJ.click(function(){
        getObjSummary();
    });
        
        
        
    downloadDOC.click(function(){
    
       var html, link, blob, url, css;
       
       // EU A4 use: size: 841.95pt 595.35pt;
    
       css = (
         '<style>' +
         '@page WordSection1{size: 841.95pt 595.35pt;mso-page-orientation: landscape;}' +
         'div.WordSection1 {page: WordSection1;}' +
         'table{border-collapse:collapse;}td{border:1px gray solid;width:5em;padding:2px;}'+
         '</style>'
       );
       console.log(docx);
       html = docx[0].innerHTML;
       blob = new Blob(['\ufeff', css + html], {
         type: 'application/msword'
       });
       url = URL.createObjectURL(blob);
       link = document.createElement('A');
       link.href = url;
       // Set default file name. 
       // Word will append file extension - do not add an extension here.
       link.download = user.displayname+' Objectives Summary';   
       document.body.appendChild(link);
       if (navigator.msSaveOrOpenBlob ) navigator.msSaveOrOpenBlob( blob, 'Document.doc'); // IE10-11
       		else link.click();  // other browsers
       document.body.removeChild(link);
    
    });
        
    </script>
    
    
    <style>
        .refresh  {
            float: left;
            color: #919191;
            cursor: pointer;
            margin-bottom: 15px;
            margin-right: 15px;
        }
    
    </style>

    With a handy option to export this as a word doc too.

  5. Here's a cute bit of code that shows that Javascript can read your laptop or Android's battery:

    <style>
        .battery_level {
            font-size: 30px;
            font-weight: bold;
            border: 1px solid #cccccc;
            border-radius: 6px;
            padding: 20px;
        }
    </style>
    
    <div class="battery_level"></div>
    
    
    <script>
        var batteryDiv = arguments[0].find('.battery_level');
        
        let isBatterySupported = 'getBattery' in navigator;
        if(!isBatterySupported) {  
            batteryDiv.html('Battery not supported');
        } else {
            let batteryPromise = navigator.getBattery();
            batteryPromise.then(batteryCallback);
            function batteryCallback(batteryObject) {
                var percent = batteryObject.level*100;
                batteryDiv.html('Your battery is at: '+percent+' %');
            }
        }
    
    </script>

    Probably not of tremendous use, I suppose you could use to remind students to plug in.

  6. A little while back I noticed a new feature in Chrome:install app.jpg

    Some websites, (including Frog) are setup as Progressive Web Apps (PWAs).  If you choose this function, Chrome will create and install on your computer a browser app called Frog OS, with no address bar, separate shortcut on the toolbar.

    It's searchable in Windows' Start Menu too:

    installed app.jpg

    Any external links will open in a regular browser and it behaves like a regular browser, but with most features missing.  In short, it's treating the platform like a standalone program.

    Has anyone else tried this out?  Can you see a use for this?

  7. I've been playing around with this today, it's more complicated now, but I think I have it:

    <style>
        select[id="9EC520B82003FFC1B8C84F291890790236728F8C3D26BB0A"],
        select[id="9EC520AF2003FBC0C1F0AF732938E9061A807D2CA7348E03"],
        select[id="9EC520B52003FEABEA582FCAEEEEE90DD087E7DCE9009A4A"]
        {
            display: none;
        }
    </style>
    
    <script>
    setTimeout(function(){  
        
        var form = $('div[data-content-uuid="9EC51EEE200287452C5BFFA9D479C00C908DF88CDDE54F77"]');
        
        var select1 = '9EC520B82003FFC1B8C84F291890790236728F8C3D26BB0A';
        var select2 = '9EC520AF2003FBC0C1F0AF732938E9061A807D2CA7348E03';
        var select3 = '9EC520B52003FEABEA582FCAEEEEE90DD087E7DCE9009A4A';
        form.find('select').show();
        
        var currentSelections = {};
        currentSelections[select1] = '';
        currentSelections[select2] = '';
        currentSelections[select3] = '';
    
        form.on('change', 'select', function() {
            
            var selectArray = [select1,select2,select3];
            var index = selectArray.indexOf(this.id);
            selectArray.splice(index, 1);
            
            for (var i = 0; i < selectArray.length; i++) {
                form.find('select[id="'+selectArray[i]+'"]').find('option[value="'+currentSelections[this.id]+'"]').show();
            }
            currentSelections[this.id] = this.value;
    
            for (var i = 0; i < selectArray.length; i++) {
                form.find('select[id="'+selectArray[i]+'"]').find('option[value="'+this.value+'"]').hide();
            }
    
        });
    
    }, 2000);
    
    </script>

    First off, I hide the select options, to give Frog a chance to load the form before the timeout function runs:

    <style>
        select[id="9EC520B82003FFC1B8C84F291890790236728F8C3D26BB0A"],
        select[id="9EC520AF2003FBC0C1F0AF732938E9061A807D2CA7348E03"],
        select[id="9EC520B52003FEABEA582FCAEEEEE90DD087E7DCE9009A4A"]
        {
            display: none;
        }
    </style>
    

    Then after two seconds (2000), the HTML widget finds the form and shows the select options.  

    I've also listed the three select dropdowns I care about:

    
    setTimeout(function(){  
        
        var form = $('div[data-content-uuid="9EC51EEE200287452C5BFFA9D479C00C908DF88CDDE54F77"]');
        
        var select1 = '9EC520B82003FFC1B8C84F291890790236728F8C3D26BB0A';
        var select2 = '9EC520AF2003FBC0C1F0AF732938E9061A807D2CA7348E03';
        var select3 = '9EC520B52003FEABEA582FCAEEEEE90DD087E7DCE9009A4A';
        form.find('select').show();
      
      
     ...
     
    }, 2000);

    Next, I've made an object to hold a history, so that a user can update their choices and not lose the original options from the other dropdowns:

        var currentSelections = {};
        currentSelections[select1] = '';
        currentSelections[select2] = '';
        currentSelections[select3] = '';

    Then the code waits for a change to any of the dropdowns. 

    @ADT - you have a couple of other dropdowns in your form which might cause an issue with the history.  I'd suggest swapping these for radio buttons.

    On selecting an option, the function creates an array of all three dropdown IDs, then identifies which dropdown was selected and removes that from the list.  (This prevents the selected option disappearing from it too).  

    The function then runs through all the dropdowns, turning back on any from the history, before updating the history with the new selection and hiding that selection from the other two dropdowns.

    
        form.on('change', 'select', function() {
            
            var selectArray = [select1,select2,select3];
            var index = selectArray.indexOf(this.id);
            selectArray.splice(index, 1);
            
            for (var i = 0; i < selectArray.length; i++) {
                form.find('select[id="'+selectArray[i]+'"]').find('option[value="'+currentSelections[this.id]+'"]').show();
            }
            currentSelections[this.id] = this.value;
    
            for (var i = 0; i < selectArray.length; i++) {
                form.find('select[id="'+selectArray[i]+'"]').find('option[value="'+this.value+'"]').hide();
            }
    
        });

    To use, you'll need the Form widget's UUID and the ID's of each select / dropdown:

    helped.jpg

     

    @ADT - I'm blocked from saving HTML on Gosforth, so you'll have to copy the code from the top box.  I had to make a copy of the site, so the UUID and IDs might be different.

     

    • Thanks 1
  8. I know we have Paul's excellent Exam Timetables widget, but I've just been helping a school who couldn't get it to work.  They're using SIMS Exam system for another purpose, and needed to display the PDFs to students.

    I don't know the Exam timetabler all that well, but the school in question had a single PDF produced with 500 students on it.  They'd already begun the process of saving the individual pages as separate documents, so I suggested we use the File Drop widget to hold them and show only the matching PDF to the student:

    <div class="File_PDF"></div>
    
    <script>
        var File_PDF = arguments[0].find('.File_PDF');
        var baseURL = Frog.Utilities.getBaseUrl();
        var fileDropUUID = 'FILE_DROP_UUID';
        
        
        var user = FrogOS.getUser();
        Frog.Model.api('users.getDataInCategory', {
            user_uuid: user.uuid,
            uuid: '60A77B2B2004CAF8CA73CFFA7491B80E180635BCDD1AEDB7' // Users app category for additional information, where the roll number is stored
        }).done(function(response) {
    
            var data = response.data.fields;
            var MISID = '';
            $.each(data, function(index, field) {
                if (field.label == 'roll_number') {
                    MISID = field.value;
                    Frog.Model.api('filedrop.get', {
                        filedrop: fileDropUUID
                    }).done(function(fileResponse) {
                        var files = fileResponse.data.resources;
                        $.each(files, function(ind,file) {
                            if (file.deleted == 0) {
                                if (file.file.name.indexOf('-'+MISID+'-') > -1) {
                                    File_PDF.append(
                                        '<embed src="'+baseURL+'/app/file/resource/'+file.file.uuid+'" width="100%"  height="1500px" />'
                                    );
                                }
                            }
                        })
                    }).fail(function(e) {
                        console.log("failed to get file drop");
                    });
                }
            });
        }).fail(function(e) {
            console.log("failed to get Roll Number");
        });
    
    </script>   
            
            
            
            

     

  9. Hi @Sue Busher

    I've made the change to your staff dashboard, this works:

    <style>
    div[data-content-uuid="WIDGET_UUID"] .countdown_section:nth-child(2),
    div[data-content-uuid="WIDGET_UUID"] .countdown_section:nth-child(3),
    div[data-content-uuid="WIDGET_UUID"] .countdown_section:nth-child(4)
        {
        display: none !important;
    }
    </style>

    Graham

     

  10. Here's a bit of code if you don't want free periods labeled as free

    <style>
        div[data-content-uuid="WIDGET_UUID"] .free .subject:before {
            content: 'Study Period \a';
            white-space: pre;
        }
        div[data-content-uuid="WIDGET_UUID"] .free .subject {
            height: 20px !important;
            overflow: hidden !important;
        }
    </style>

     

  11. Sorry, I've been away at a trade show.   

    Umm, I think we're getting into quite complicated territory here - you'd need code to listen to the form for a click response, then make changes to other dropdowns - it could easily get messed up. 

    It might be simpler to create a form in an HTML widget, include these features then on submission submit it using the Form API.  Simpler, but a bit time consuming.

  12. For those modal pop-ups, especially with forms, sometimes you just want a bit more width.  Dropping this code into an HTML widget will expand ALL modals, so I'm tempted to suggest you don't use it on staff dashboards, as things like the assignment pop-up and share wizard look a little weird:

    .modal-body {
        max-height: 560px !important;
    }
    
    .modal {
        width: 70% !important;
        left: 30% !important;
    }

     

     

    Fortunately the Image Carousel's gallery mode pop-up has an extra class:

    <style>
    .image_carousel_modal .modal-body {
        max-height: 600px !important;
        height: auto !important;
    }
    
    .image_carousel_modal {
        max-width: 80% !important;
    }
    </style>

     

  13. 3 hours ago, ADT said:

    That might hide the lines.... but not get rid of the gap!!

    How many hidden field!?!  🤯

     

    Here you go, it's a bit "belt-and-braces" but it works:

    <style>
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(14),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(15),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(16),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(17),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(18),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(19),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(20),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(21),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(22),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(23),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(24),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(25),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(26),
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid:nth-child(27)
        {
            height: 0px !important;
            max-height: 0px !important;
            overflow: hidden !important;
            border-top: 0px !important;
            margin: 0px !important;
            padding: 0px !important;
        }
    </style>

     

    • Thanks 1
  14. Lots of variations are possible here, just open an HTML widget and...

    This will hide all lines:

    <style>
        .form-body .row-fluid {
            border-top: 0px !important;
        }
    </style>

     

    Or this will hide specific lines:

    <style>
        .form-body .row-fluid:nth-child(3),
        .form-body .row-fluid:nth-child(4)
        {
            border-top: 0px !important;
        }
    </style>

     

    Or this will hide lines only on a specified form:

    <style>
        div[data-content-uuid="FORM_UUID"] .form-body .row-fluid {
            border-top: 0px !important;
        }
    </style>

     

  15. 35 minutes ago, ADT said:

    Yep so I want them to be able to add but not remove.... and I also what it as easy as possible without giving them access to the Groups and Policy App.  Good knows what they could break if they had access to that!!

    Obviously swapping from SIMs groups to Frog groups is dead easy.....and the Form Resister was something I looked at.  I know that will email but does it also store the data?

    I've used the join group before to add staff and students to a group... the bit I need to work out is how staff can search for a student like in the user picker and then hit add to join their group!!

    The Form Register stores the data in a form on the same site, so you'll be able to export attendance, correct entries, search for students etc... using a data viewer.

    For staff to add students, as above, we'd need to build a widget to allow that to happen.  So today.... you could do it with you being the middleman (adding students to groups), and in theory a FrogCode widget could sort it out what you need.  I could offer to estimate it for you, but I think we both know the answer.

  16. Now that's a tricky one.  You're wanting staff to be able to add users to a group, without being able to remove other users from a group?  Currently the Groups & Policies role to add users to a group is linked to being able to remove users.  They don't have access to add or remove features anyway.  And MIS groups are fixed, so they can't alter those users.

    The Join Club FrogCode widget does allow students to add themselves to a group, as it uses the groups.amendMembers API.  So it would be probably be possible for a teacher to search for a student and add them using similar code:

    Frog.Model.api('groups.amendMembers', {
      group_uuid: /*GROUP_UUID*/,
      users: JSON.stringify([{uuid: /*UUID_OF_USER_TO_BE_ADDED*/, leader: false}])
    },{type: "POST"}).done(function(response) {
      // Success  
    }).fail(function(e) {
      // Report Error
    });     

     

    Alternatively, the Form Register uses a form, so there is the ability to add an individual manually via that form.  This form's widget could be set to email the club coordinator (or you) and when you get that email, they (you) could add the new student to the group - so that next time, the register needs to be taken, they will be there - assuming someone has had time to update the club.

  17. I'm wondering about creating accounts for just the Frog Admins in each school,  (the likes of you) so that you get an interactive checklist of those Frog tasks you need to do and a record of having done them

    But, that being said, I wouldn't want people resenting it.

×
×
  • Create New...