PowerSchool Customization
#3 - jQuery and Page Fragments
Working on the "client-side"
https://goo.gl/xSZBtK
https://goo.gl/xSZBtK
Link (or scan the code) to get a
Google copy of this presentation.
Also posted at
psugcal.org
(via the "Customization" link)
Reference/Credit
W3Schools
https://www.w3schools.com/
Adam Larsen at Aurora Educational Technology
https://auroraedtech.com/about
Eric Schaitel's Customization Documentation
https://support.powerschool.com/exchange/view.action?download.id=772
Marcia Brenner Associates
https://mba-link.com/
HTML - CSS - JS: The Client-Side Of The Web
https://html-css-js.com/
ID 772
The Languages of Customization
HTML CSS
JavaScript/JQuery
PSHTML
SQL Angular
...one of the 3 languages all web
developers must learn:
1. HTML to define the content of web pages
2. CSS to specify the layout of web pages
3. JavaScript to program the behavior of web
pages
Source: https://www.w3schools.com/js/default.asp
https://html-css-js.com/
Javascript Demo
https://www.w3schools.com/js/tryit.asp?filename=tryjs_myfirst
<!DOCTYPE html>
<html>
<body>
<h2>My First JavaScript</h2>
<button type="button"
onclick="document.getElementById('demo').innerHTML = Date()">
Click me to display Date and Time.</button>
<p id="demo"></p>
</body>
</html>
… a lightweight JavaScript library.
The purpose of jQuery is to make it much easier to use
JavaScript on your website.
jQuery takes a lot of common tasks that require many
lines of JavaScript code, and wraps them into methods
that you can call with a single line of code.
Deals with subtle differences between web browsers
Very popular, free, and included in PowerSchool.
Source: https://www.w3schools.com/jquery/jquery_intro.asp
http://www.lucemorker.com/blog/javascript-vs-jquery-quick-overview-and-comparison
Javascript
jQuery
https://learn.onemonth.com/jquery-vs-javascript/
Example: Making a line of text change color
Request
addresses.html?frn=0014321
addresses.html
Scripts
CSS
Wildcards
Images
Database
Table: 001
DCID: 4321
"Rendered"
addresses.html
Client
Server
Working with jQuery means working on
the "client-side"
Web Developer Tools
Learn how to "inspect" the client side code of PowerSchool
pages.
In most browsers it's as
simple as right-clicking
(ctrl-clicking) on an
element and choosing
"Inspect"
jQuery Basics - $j
The jQuery object on PowerSchool pages is
aliased as $j
jQuery documentation uses $()
PS pages require $j()
Tip: You'll find all sorts of example of jQuery
code on the Internet, just remember:
Change $ to $j
jQuery Basics - How to include in your pages
Include the
commonscripts
wildcard
The <script> Tag
In HTML, JavaScript code must be inserted between <script>
and </script> tags.
<script>
$j(function() {
/* Demographics View Link */
html = '';
html += '<li>';
html += '<a href="demo.html?frn=~(studentfrn)">';
html += 'Demographics View';
html += '</a></li>';
$j("[href^='morecustom.html']").closest("li").after(html);
});
</script>
JavaScript in <head> or <body>
You can place any number of scripts in an
HTML document.
Scripts can be placed in the <body>, or in
the <head> section of an HTML page, or in
both.
It's common to see scripts at the very
bottom of the page before the </body> tag
External Javascript
Scripts can also be placed in external files
JavaScript files have the file extension .js
To use an external script, put the name of the script
file in the src (source) attribute of a <script> tag:
Placing scripts in external files has some advantages:
It separates HTML and code
It makes HTML and JavaScript easier to read and maintain
Cached JavaScript files can speed up page loads
<script src="myScript.js"></script>
The Document Ready Event
You may notice that most jQuery is
written inside something like this
(called a "Document Ready Event"):
This is to make sure the document
is completely loaded(ready) before
running any jQuery.
It also isolates your code from other
code that might otherwise conflict (i.e.
- same variable names)
$j(document).ready(function(){
// jQuery methods go here…
});
$j(function(){
// jQuery methods go here…
});
Even shorter
jQuery Basics - Writing jQuery code
$j(selector).action();
SELECT something ...
... and then DO something with it
Examples:
$j(this).hide() - hides the current element.
$j("p").hide() - hides all <p> elements.
$j(".test").hide() - hides all elements with class="test".
$j("#test").hide() - hides the element with id="test".
Selectors
Common selectors are id and class
Select element by ID - $j('#elementID')
The id selector will operate on a single element. No
two elements in a document should be given the same
id.
Select element by class - $j('.className')
The class selector can operate on a single element or
multiple elements with the same class attribute.
Selectors
Element selectors
$j('p') - Select all the paragraph elements
$j('ul') - Select all unordered lists <ul>
Combine multiple selectors with commas
$j("h1,div,p") All <h1> OR <div> OR <p> elements
Double or Single quotes? Selectors are enclosed in
quotes. They can be either single quotes or double
quotes. If the inner text contains quotes, be sure the
inner quotes are the opposite of the outer.
Selectors
id and class are often combined with the
element type
$j('div.box-round')
Selects all <div> tags that have the box-round class
$j('p#student_detail_header')
Selects the <p> tag with id of student_detail_header
Selectors
Spaces are used to select "descendents", or
elements WITHIN another element
$j('ancestor descendent')
$j('ul#std_information li')
Selects <li> tags that
are descendants of a
<ul> tag with the id
of std_information
<ul id="std_information">
<li>...</li>
<li>...</li>
</u>
Example: Select by ID
Add a link to the "Information" section of the student left nav
Example: Select by ID
Add a link to the "Information" section of the student left nav
<!--AET Custom Alerts Begin-->
<script>
$j(document).ready(function()
{
$j("ul#std_information").append("<li><a
href='aet_customalerts.html?frn=~(frn)'>Custom
Alerts</a></li>");
...
Custom Alerts Example
Example: Select by class
Select all the rows with class="oddRow"
Example: Select by class
// Fix zebra striping - first change all oddRow classes to evenRow
$j('tr.oddRow').attr('class', 'evenRow');
//now reassign oddRow to the correct odd rows
$j('tr.evenRow:odd').attr('class', 'oddRow');
From "Add Reset Class Counts to Special Functions" at psugcal.org
https://www.psugcal.org/index.php?title=Customization
More selectors
The Documentation Reference Plugin has a more comprehensive
list of selectors in the jQuery section
Test your selector skills
The jQuery section of the Documentation Reference Plugin has
a great interactive tool to test your selector skills
https://yourservername/admin/district/customizationDocs/jsjq.html
Selector Test Exercises
Test these selector challenges (answers on next slide)
1 element with id="testDiv"
2 elements with class="specialSpan"
3 all the <span> elements
4 all the <span> elements with class="specialSpan"
5 all the <span> elements inside the div with id="testDiv"
6 the <span> element NOT inside the testDiv
7 the <table> element
8 the <tr> elements
Selector Test Exercises #1 (ANSWER KEY)
1 element with id="testDiv" #testDiv
2 elements with class="specialSpan" .specialSpan
3 all the <span> elements span
4 all the <span> elements with class="specialSpan" span.specialSpan
5 all the <span> elements inside the div with id="testDiv" #testDiv span
6 the <span> element NOT inside the testDiv span:not(#testDiv span)
7 the <table> element table
8 the <tr> elements tr
Selector Test Exercises #2
Test these selector challenges (answers on next slide)
9 table row 3 (which is the 4th row counting the header)
10 all the even rows / all the odd rows
11 cell 3:2 in the table
12 the span that contains the text "Span 3"
13 table row 2 and higher
14 all table rows except the last one
15 all the <span> elements AND all the <tr> elements
16 CHALLENGE: the 1st column in the table (the 2nd? 3rd?)
Selector Test Exercises #2 (ANSWER KEY)
9 table row 3 (which is the 4th row counting the header) table tr:eq(3)
10 all the even rows / all the odd rows tr:even / tr:odd
11 cell 3:2 in the table table tr:eq(3) td:eq(1)
12 the span that contains the text "Span 3" span:contains("Span 3")
13 table row 2 and higher table tr:gt(1)
14 all table rows except the last one table tr:lt(4)
15 all the <span> elements AND all the <tr> elements span,tr
16 CHALLENGE: the 1st column in the table (the 2nd?) table tr td:first-child
table tr td:nth-child(2)
Useful Selector in PowerSchool - Attribute
$j("[attribute='value']") - Attribute selector
Sometimes useful if all you have is the name
$j("input[name='userSentContact.email']")
Other operators include:
Contains: *=
Starts with: ^=
Ends with: $=
Useful Selector in PowerSchool - Attribute "contains"
Often PowerSchool does not have ids or classes in the code -
learning to select by partial name match is very useful
$j("a[href*='PSPRE_ADAADM_ByMinute']").remove();
Useful Selector in PowerSchool - Attribute "starts with"
$j("[href^='quicklookup.html']").closest("p").after(html);
jQuery Basics - action
$j(selector).action();
SELECT something ...
... and then DO something with it
Examples:
$j(this).hide() - hides the current element.
$j("p").hide() - hides all <p> elements.
$j(".test").hide() - hides all elements with class="test".
$j("#test").hide() - hides the element with id="test".
jQuery actions (or methods, or functions)
After selecting an element or a group of
elements, apply an action
Many uses
Select parent or descendant elements (traversing the tree)
Change CSS styling
Add content before or after
Remove
Hide or Show
Clone
Events
html(htmlValue); - Set the inner HTML of an element
click(function(){do something}); - Set an event
listener to run a function when an element is clicked.
val(); - Retrieve the value of an input
val(someValue); - Set the value of an input
change(function(){do something}); - Set an event listener
to run a function when an input's value is changed.
hide(); - Hide an element
show(); - Show an element
toggle(); - Similar to hide() and show(), it toggles the
visibility of an element
If the element is visible it will hide it
If the element is hidden it will display it
css("propertyname","value"); - Sets the CSS property of
an element
append() - Inserts content
inside
the selected element(s) at
the end
$j("ul#std_information").append("<li>
<a href='newPage.html?frn=~(frn)'>
New Page Link</a></li>");
New Page Link
Note: prepend() is similar, it
would just put the content at
the beginning of the element.
append() - Inserts content
inside
the selected element(s) at
the end
$j('p#student_detail_header').append('~([01]home_room)');
<script>
$j(function(){
$j('p#student_detail_header')
.append('~([01]home_room)');
});
</script>
before() and after() - Inserts content
outside
before or
after selected element
$j('#quickLookup table:eq(1) tr:last').after(linkrow);
before() and after() - Inserts content
outside
before or
after selected element
$j('#quickLookup table:eq(1) tr:last').after(linkrow);
<script>
var linkrow = '';
linkrow += '<tr><td align="center">';
linkrow += '<a href="mailto:~[tlist2;teachers]~(email_addr),[/tlist]"';
linkrow += ' target="_blank">Email these Teachers</a>';
linkrow += '</td></tr>';
$j(function() {
$j('#quickLookup table:eq(1) tr:last').after(linkrow);
});
</script>
Traversing methods
jQuery traversing methods
help you navigate up and
down the "tree" structure
of the web page and
select elements in
context
closest(ancestor selector) - Jump up to first ancestor that
matches given selector.
Very helpful in PowerSchool
Want to add new
link here
Add a new <li> element after the <li> that contains the
existing "Enter Attendance" link.
We can select the link itself with ^= "starts with":
$j("a[href^='/admin/attendance/record/week/meeting.html']")
We can then select it's ancestor <li> with closest("li")
closest() doesn't care how many levels
<script>
$j(function() {
~[if.~(studentscreenaccess;attendance.html)=1]
/* "Enter Attendance Prev5" Link */
var html = '';
html += '<li>';
html += '<a href="/admin/attendance/record/week/meeting.html?frn=~(studentfrn)';
html += '&startdate=~[date;-5]';
html += '&enddate=~[date;-1]';
html += '&ATT_RecordMode=ATT_ModeMeeting&setUserScreen=1">';
html += 'Enter Attendance Prev5';
html += '</a></li>';
$j("a[href^='/admin/attendance/record/week/meeting.html']").closest("li"
).after(html);
[/if]
});
</script>
closest(ancestor selector) - Jump up to first ancestor that
matches given selector.
Very helpful in PowerSchool
closest(ancestor selector) - Jump up to first ancestor that
matches given selector.
Very helpful in PowerSchool
Additional "Traversing" methods
parent()
Jump up to parent element of selected element
$j("td").parent();
children(
child selector
)
Selects child elements that match the selector
Use children() to select all direct descendants
ONLY the first generation of descendants
find(
descendant selector
)
Selects descendant elements of given selector
ANY generation of descendants
Even more...
HTML - CSS - JS: The Client-Side Of The Web
Nicely formatted "cheat sheets"
https://html-css-js.com/
W3Schools
https://www.w3schools.com/
Eric Schaitel's Customization Documentation
https://support.powerschool.com/exchange/view.action?download.id=772
Marcia Brenner Associates
https://mba-link.com/
Page Fragments
"The best way to
customize"
Core web_root
PowerSchool puts
updates here
Customization Problem
Custom
web_root
CPM
(Custom Page
Management)
You customize here
Core more2.html
Customized
Directly
CPM more2.html
PowerSchool
adds Contacts
Before
Contacts
came out you
added other
links
Core more2.html
Customized
Directly
CPM more2.html
No Contacts!
Core more2.html
Solution?
CPM more2.html
Copy the new
link to your
custom copy
Page Changes in
Release Notes
aka "The Romy Method"
Solution - Insertion Points and Page Fragments
Insertion points are special locations within the source
code of a page where customizers can more easily insert
dynamic content (page fragments).
With insertion points, the original source page does not
have to be customized in order to add new content to that
page. This can help dramatically cut down on the number of
custom pages that need to be created and subsequently
updated when a new version of PowerSchool is released.
Database Extensions Advanced User Guide for PowerSchool
https://support.powerschool.com/article/83171
content.header
content.footer
leftnav.footer
Standard
Insertion
Points
student.alert
When rendering the page, PowerSchool will gather together
all the page fragment insertions for that page and render
them as inline HTML with the page. Note that each
insertion point may have multiple inserted page fragments
for any given page and will all be rendered on the page.
Multiple page
fragments in harmony
more2.html
BrightArrow
Custom Alerts
District Created
Why insertion points are awesome
We use fragments to avoid customizing pages directly
Allows updates by PowerSchool to continue. Our
customizations work with stock pages instead of
overriding them.
Multiple fragments for different projects and from
different providers can all exist on the same page
Why insertion points are a little clumsy
The content.footer insertion point will put inserted
content at the bottom of the page
Not usually what we want
Must learn to use jQuery to move content into the proper
location
The naming of your fragment is the key
Save your page fragments to the same directory where the
source page or wildcard exists.
name_of_file (without the “.html”) +
.name_for_page_fragment (whatever you want to name it) +
.insertion.point (a common insertion point is “content.footer”) +
.txt
Hello World on Home Page
<p>Hello world! I'm an auto-inserted page fragment.</p>
Fragment=admin/home.hello.content.footer.txt
Stock Page=admin/home.html
Page Fragment
Examples
Add Homeroom to Student Detail Header
Add Homeroom to Student Detail Header
Goal: Add the student's homeroom information to the detail
line that appears on all student pages
Method: Append the homeroom field to the wildcard used for
the student detail line
Add Homeroom to Student Detail Header
Stock Page (wildcard):
wildcards/student_detail_header_line.txt
Fragment:
wildcards/student_detail_header_line.hr.content.footer.txt
Add Homeroom to Student Detail Header
Selector: <p> element with id="student_detail_header"
Action: append
jQuery: $j('p#student_detail_header').append(___);
<script>
$j(function(){
$j('p#student_detail_header').append('~([01]home_room)');
});
</script>
wildcards/student_detail_header_line.hr.content.footer.txt
Add Family_Ident to Modify Info Screen
Add Family_Ident field to Modify Info Screen
Goal: Use a Family ID field to help schools identify
families as entities.
Method: Re-introduce the existing family_ident field to the
"Modify Info" screen, where the family_rep field already is
displayed.
Data Dictionary Tables for PowerSchool 12.x
https://support.powerschool.com/article/80621
Address of stock page
Insert at top of table
Tip: Use "Open Link
in New Tab" to see
the actual address
Add Family_Ident field to Modify Info Screen
Stock Page:
admin/students/modifydata.html
Fragment:
admin/students/modifydata.familyid.content.footer.txt
Add Family_Ident field to Modify Info Screen
Selector: First row in the table Action: prepend
jQuery: $j("table:first").prepend(___);
Challenge: Adding content with validation
<tr>
<td class="bold">Family ID</td>
<td>
<input type="text" id="familyidfield" name="[Students]Family_Ident" value="">
</td>
</tr>
<script>
$j(function(){
$j("table:first").prepend('<tr><td class="bold">Family
ID</td><td><input type="text" id="familyidfield"
name="[Students]Family_Ident" value=""></td></tr>');
});
</script>
Challenge: Adding content with validation
PowerSchool can add additional code beyond your control that
can break javascript. One pattern as a workaround is:
Build the content you want to add in a hidden element
(such as a table).
Use jQuery to move the element to your desired location
Delete the hidden table
<input type="text" id="familyidfield" name="[Students]Family_Ident" value="">
<!-- create a hidden table with added rows -->
<table id="familyidhiddentable" style="display: none;">
<tr id="familyidrow">
<td class="bold">Family ID</td>
<td><input type="text" id="familyidfield" name="[Students]Family_Ident"
value=""></td>
</tr>
</table>
<!-- use jQuery to move the hidden row and remove the hidden table -->
<script>
$j(function() {
$j("table:first").prepend($j("#familyidrow"));
$j("#familyidhiddentable").remove();
});
</script>
admin/students/modifydata.familyid.content.footer.txt
Challenge: Inserting new rows and zebra striping
Problem: All the rows are now the same shade. Our new row shifted all the other
rows down by one, but every other row was hard-coded to have a bgcolor attribute.
This is an example of legacy styling that isn't completely removed from
PowerSchool. The class="linkDescList" on the table should style every other row
with the shading. So the solution is to remove the unnecessary bgcolor attributes
and let css style the table.
Solution: Remove bgcolor attribute with removeAttr()
$j("table:first tr").removeAttr("bgcolor");
<!-- create a hidden table with added rows -->
<table id="familyidhiddentable" style="display: none;">
<tr id="familyidrow">
<td class="bold">Family ID</td>
<td><input type="text" id="familyidfield" name="[Students]Family_Ident"
value=""></td>
</tr>
</table>
<!-- use jQuery to move the hidden row and remove the hidden table -->
<script>
$j(function() {
$j("table:first").prepend($j("#familyidrow"));
$j("#familyidhiddentable").remove();
$j("table:first tr").removeAttr("bgcolor");
});
</script>
admin/students/modifydata.familyid.content.footer.txt
More at psugcal.org on using family_ident
For a more complete example that adds the additional
features see the "Family Management" page at psugcal.org
Add validation to ensure Family_Ident is a number
Add a function to automatically select the next available
FamilyID to assign to a new family
Use sql reports to identify which siblings to mark as the
"family rep"
Consider adding family_ident to list of fields to copy
between siblings in "Family Management" so that the
field is kept in sync between siblings
Add "Reset Class Counts" to Special Functions
Resetting Class Counts in PowerSchool
Problem: Class counts often
need updating, requiring an
admin with access to Special
Operations to run "Reset
Class Counts"
Add Reset Class Counts to Special Functions
Goal: Add a "Reset Class Counts" to the Special Functions
screen to allow your users to fix class counts on their own
Method: Upload a special page limited to just the "Reset
Class Counts" special operation and then add a link to it in
Special Functions
Step 1: Upload the special page
https://support.powerschool.com/exchange/view.action?download.id=449
ID: 449
Upload via CPM: admin/resetclasscount.html
Add Reset Class Counts to Special Functions
Stock Page: admin/functions.html
Fragment: admin/functions.classcounts.content.footer.txt
Add Reset Class Counts to Special Functions
Selector: <tr> with link ".../studentattachments.html"
Action: before
jQuery:
$j("[href^='/admin/students/studentattachments.html']").closest("tr").before(___);
<script>
$j(function() {
// First build up the new html row into an 'html' variable
var html = '<tr class="evenRow">';
html += '<td><a href="/admin/resetclasscount.html">';
html += 'Reset Class Counts</a></td>';
html += '<td>Updates section student enrollment counts.</td>';
html += '</tr>';
// now insert the table row before the 'Search Attachments' row
$j("[href^='/admin/students/studentattachments.html']").closest("tr")
.before(html);
// Fix zebra striping - 1st change all oddRow classes to evenRow
// we do this make all non-header rows the same class
$j('tr.oddRow').attr('class', 'evenRow');
//now reassign oddRow to the correct odd rows
$j('tr.evenRow:odd').attr('class', 'oddRow');
});
</script>
admin/functions.classcounts.content.footer.txt
Adding links to Student Left Navigation
"Enter Attendance Prev 5"
more2.html
Enter Attendance always
displays the current week.
Enter Attendance Prev 5
It would sometimes be more
helpful to see the previous 5
days.
Add "Enter Attendance Prev 5" Link to Student Left Nav
Goal: Add a link like the "Enter Attendance" link, except
that it displays the last 5 days instead of the current
week. This is more useful especially at the beginning of the
week when the attendance secretary is still adjusting the
previous week.
Method: Add a specially formatted link just after the "Enter
Attendance" link.
Add "Enter Attendance Prev 5" Link to Student Left Nav
Stock Page: admin/students/more2.html
Fragment: admin/students/more2.attprev5.leftnav.footer.txt
https://yourserverURL/admin/attendance/rec
ord/week/meeting.html?frn=0012
&startdate=04/09/2018&enddate=04/13/2018
&ATT_RecordMode=ATT_ModeMeeting
Special Link for Previous 5 days
<a
href="/admin/attendance/record/week/meeting.html
?frn=~(studentfrn)&startdate=~[date;-5]
&enddate=~[date;-1]
&ATT_RecordMode=ATT_ModeMeeting
&setUserScreen=1">Enter Attendance Prev5</a>
Add "Enter Attendance Prev 5" Link to Student Left Nav
Selector: <li> containing the "Enter Attendance" link
Action: after
jQuery:
$j("a[href^='/admin/attendance/record/week/meeting.html']").closest("li").after(__);
<script>
$j(function() {
~[if.~(studentscreenaccess;attendance.html)=1]
/* "Enter Attendance Prev5" Link */
var html = '';
html += '<li>';
html += '<a href="/admin/attendance/record/week/meeting.html';
html += '?frn=~(studentfrn)';
html += '&startdate=~[date;-5]';
html += '&enddate=~[date;-1]';
html += '&ATT_RecordMode=ATT_ModeMeeting&setUserScreen=1">';
html += 'Enter Attendance Prev5';
html += '</a></li>';
$j("a[href^='/admin/attendance/record/week/meeting.html']").close
st("li").after(html);
[/if]
});
</script>
admin/students/more2.attprev5.leftnav.footer.txt
Tip: Experiment with different date
math options to meet the needs of
your attendance secretary.
&startdate=~[date;-5]
&enddate=~[date;-1]
Append and Sort
Credit: "Custom Alerts"
plugin by Adam Larsen
https://mba-link.com/customizations
Advanced method for inserting links alphabetically
Append and Sort
You can't always be certain exactly
which links will be available to a
given user, due to security
implementations.
A safer approach is to append your
link to the bottom of the section
and re-sort the list
alphabetically.
Append and Sort
Goal: Add a link to the Information
section of the student left
navigation.
Method: Append a new link to the
bottom of the section and re-sort
the section alphabetically
Append and Sort
Stock Page:
admin/students/more2.html
Fragment:
admin/students/more2.aet_customalerts.leftnav.footer.txt
Append and Sort
Selector: <ul> with the id="std_information"
Action: append
jQuery: $j("ul#std_information").append(___);
$j("ul#std_information").append("<li><a
href='aet_customalerts.html?frn=~(frn)'>
Custom Alerts</a></li>");
$j("ul#std_information li").sort(function(a, b)
{
if($j(a).text() < $j(b).text()) return -1;
if($j(a).text() > $j(b).text()) return 1;
return 0;
}
).appendTo("ul#std_information");
JavaScript sort function
https://www.w3schools.com/js/js_array_sort.asp
The sort functions evaluates all the
items in the array, sending two
items at a time to the compare
function.
- negative sorts
a
before
b
- positive sorts
b
before
a
- 0 means no change to the sort
The jQuery text()
method returns the
text of the
element without
any HTML markup
<script>
$j(document).ready(function() {
$j("ul#std_information").append("<li><a
href='aet_customalerts.html?frn=~(frn)'>Custom Alerts</a></li>");
$j("ul#std_information li").sort(function(a, b)
{
if($j(a).text() < $j(b).text()) return -1;
if($j(a).text() > $j(b).text()) return 1;
return 0;
}
).appendTo("ul#std_information");
});
</script>
admin/students/more2.aet_customalerts.leftnav.footer.txt