Skip to content

Commit

Permalink
Merge pull request sakaiproject#2652 from clhedrick/LSNBLDR-636
Browse files Browse the repository at this point in the history
LSNBLDR-636; section heading, collapse / expand sections; UI changes
  • Loading branch information
clhedrick committed May 25, 2016
2 parents 57b6de1 + 8aef77c commit 3235e7f
Show file tree
Hide file tree
Showing 9 changed files with 414 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import org.sakaiproject.lessonbuildertool.model.SimplePageToolDao;
import org.sakaiproject.lessonbuildertool.SimplePage;
import org.sakaiproject.lessonbuildertool.SimplePageItem;
import org.sakaiproject.lessonbuildertool.service.LessonsAccess;

/**
* <p>
Expand All @@ -102,6 +103,7 @@ public class AjaxServer extends HttpServlet
private static SiteService siteService;
private static AuthzGroupService authzGroupService;
private static SimplePageToolDao simplePageToolDao;
private static LessonsAccess lessonsAccess;

public void setSimplePageToolDao(Object dao) {
log.info("setdao " + dao);
Expand Down Expand Up @@ -516,8 +518,7 @@ public static String insertBreakBefore(String itemId, String type, String cols,
return null;
}

String ref = "/site/" + siteId;
if (!SecurityService.unlock(SimplePage.PERMISSION_LESSONBUILDER_UPDATE, ref) || !checkCsrf(csrfToken)) {
if (!lessonsAccess.canEditPage(siteId, page) || !checkCsrf(csrfToken)) {
log.error("Ajax insertBreakBefore passed itemid " + itemId + " but user doesn't have permission");
return null;
}
Expand Down Expand Up @@ -638,32 +639,7 @@ else if (!color.matches("^[a-z]*$")) {
String siteId = null;
try {
Long itemNum = Long.parseLong(itemId);
item = simplePageToolDao.findItem(itemNum);
if (item.getType() != SimplePageItem.BREAK) {
// hopefully this is the first item, in an old page that doesnbt' begin with a page break
List<SimplePageItem>items = simplePageToolDao.findItemsOnPage(item.getPageId());
if (items.get(0).getId() == itemNum) {
// this is first item on page, add a section break before it
item = simplePageToolDao.makeItem(item.getPageId(), 1, SimplePageItem.BREAK, null, null);
item.setFormat("section");
simplePageToolDao.quickSaveItem(item);
int seq = 2;
// and bump sequence numbers
for (SimplePageItem i: items) {
i.setSequence(seq);
simplePageToolDao.quickUpdate(i);
seq++;
}
} else if (items.get(0).getType() == SimplePageItem.BREAK &&
items.get(1).getId() == itemNum) {
// maybe we just inserted a break before our item.
// If so, use the break;
item = items.get(0);
} else {
log.error("Ajax setcolumnproperties passed item not a break: " + itemId);
}

}
item = getCorrectBreakItem(itemNum);
page = simplePageToolDao.getPage(item.getPageId());
siteId = page.getSiteId();
} catch (Exception e) {
Expand All @@ -676,8 +652,7 @@ else if (!color.matches("^[a-z]*$")) {
return null;
}

String ref = "/site/" + siteId;
if (!SecurityService.unlock(SimplePage.PERMISSION_LESSONBUILDER_UPDATE, ref) || !checkCsrf(csrfToken)) {
if (!lessonsAccess.canEditPage(siteId, page) || !checkCsrf(csrfToken)) {
log.error("Ajax setcolumnproperties passed itemid " + itemId + " but user doesn't have permission");
return null;
}
Expand All @@ -703,6 +678,93 @@ else if (!color.matches("^[a-z]*$")) {

}

public static String setSectionCollapsible(String itemId, String collapsible, String sectionTitle, String defaultClosed, String csrfToken) {
if (itemId == null || collapsible == null || sectionTitle == null) {
log.error("Ajax setSectionCollapsible passed null argument");
return null;
}

itemId = itemId.trim();
// we don't actually use the integers. Just for syntax checking
int collapsiblei = 0;
int defaultClosedi = 0;
try {
collapsiblei = Integer.parseInt(collapsible);
defaultClosedi = Integer.parseInt(defaultClosed);
} catch (Exception e) {
log.error("Ajax setSectionCollapsible passed non-numeric collapsible or defaultClosed");
return null;
}

// currently this is only needed by the instructor

SimplePageItem item = null;
SimplePage page = null;
String siteId = null;
try {
Long itemNum = Long.parseLong(itemId);
item = getCorrectBreakItem(itemNum);
page = simplePageToolDao.getPage(item.getPageId());
siteId = page.getSiteId();
} catch (Exception e) {
e.printStackTrace();
log.error("Ajax setSectionCollapsible passed invalid data " + e);
return null;
}
if (siteId == null) {
log.error("Ajax setSectionCollapsible passed null site id");
return null;
}

if (!lessonsAccess.canEditPage(siteId, page) || !checkCsrf(csrfToken)) {
log.error("Ajax setSectionCollapsible passed itemid " + itemId + " but user doesn't have permission");
return null;
}

if (collapsible.trim().equals("1"))
item.setAttribute("collapsible", collapsible);
else
item.removeAttribute("collapsible");

if (defaultClosed.trim().equals("1"))
item.setAttribute("defaultClosed", defaultClosed);
else
item.removeAttribute("defaultClosed");

item.setName(sectionTitle);

simplePageToolDao.quickUpdate(item);
return "ok";
}

private static SimplePageItem getCorrectBreakItem(Long itemNum) {
SimplePageItem item = simplePageToolDao.findItem(itemNum);
if (item.getType() != SimplePageItem.BREAK) {
// hopefully this is the first item, in an old page that doesn't begin with a page break
List<SimplePageItem>items = simplePageToolDao.findItemsOnPage(item.getPageId());
if (items.get(0).getId() == itemNum) {
// this is first item on page, add a section break before it
item = simplePageToolDao.makeItem(item.getPageId(), 1, SimplePageItem.BREAK, null, null);
item.setFormat("section");
simplePageToolDao.quickSaveItem(item);
int seq = 2;
// and bump sequence numbers
for (SimplePageItem i: items) {
i.setSequence(seq);
simplePageToolDao.quickUpdate(i);
seq++;
}
} else if (items.get(0).getType() == SimplePageItem.BREAK &&
items.get(1).getId() == itemNum) {
// maybe we just inserted a break before our item.
// If so, use the break;
item = items.get(0);
} else {
log.error("Ajax setSectionCollapsible passed item not a break: " + itemNum);
}
}
return item;
}

public static String deleteItem(String itemId, String csrfToken) {
if (itemId == null) {
Expand Down Expand Up @@ -731,8 +793,7 @@ public static String deleteItem(String itemId, String csrfToken) {
return null;
}

String ref = "/site/" + siteId;
if (!SecurityService.unlock(SimplePage.PERMISSION_LESSONBUILDER_UPDATE, ref) || !checkCsrf(csrfToken)) {
if (!lessonsAccess.canEditPage(siteId, page) || !checkCsrf(csrfToken)) {
log.error("Ajax deleteBreak passed itemid " + itemId + " but user doesn't have permission");
return null;
}
Expand Down Expand Up @@ -819,6 +880,13 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws Ser
String color = req.getParameter("color");
String csrfToken = req.getParameter("csrf");
out.println(setColumnProperties(itemId, width, split, color, csrfToken));
} else if (op.equals("setsectioncollapsible")) {
String itemId = req.getParameter("itemid");
String collapsible = req.getParameter("collapsible");
String sectionTitle = req.getParameter("sectiontitle");
String defaultClosed = req.getParameter("defaultclosed");
String csrfToken = req.getParameter("csrf");
out.println(setSectionCollapsible(itemId, collapsible, sectionTitle, defaultClosed, csrfToken));
} else if (op.equals("deleteitem")) {
String itemId = req.getParameter("itemid");
String csrfToken = req.getParameter("csrf");
Expand All @@ -839,4 +907,8 @@ public void setAuthzGroupService(AuthzGroupService s) {
authzGroupService = s;
}

public void setLessonsAccess(LessonsAccess s) {
lessonsAccess = s;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,31 @@ public boolean isItemAccessible(long itemId, String siteId, String currentUserId
return true;
}

// shoud be the same as canEditPage in SimplePageBean, but without the caching from the bean.
public boolean canEditPage (String siteId, SimplePage page) {
String ref = "/site/" + siteId;
if (securityService.unlock(SimplePage.PERMISSION_LESSONBUILDER_UPDATE, ref))
return true;
if (page != null && isPageOwner(page))
return true;
return false;
}

// copied from SimplePageBean, for use at service level.
public boolean isPageOwner(SimplePage page) {
String owner = page.getOwner();
String group = page.getGroup();
if (group != null)
group = "/site/" + page.getSiteId() + "/group/" + group;
if (owner == null)
return false;
String currentUserId = UserDirectoryService.getCurrentUser().getId();
if (group == null)
return owner.equals(currentUserId);
else
return authzGroupService.getUserRole(currentUserId, group) != null;
}

public void setAuthzGroupService(AuthzGroupService authzGroupService) {
this.authzGroupService = authzGroupService;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,7 @@ private SimplePageItem appendItem(String id, String name, int type) {
*
**/

// There's a copy in LessonsAccess for use by services. Keep them in sync
public boolean isPageOwner(SimplePage page) {
String owner = page.getOwner();
String group = page.getGroup();
Expand Down Expand Up @@ -1636,6 +1637,7 @@ public boolean isPageOwner(SimpleStudentPage page) {
* Returns 2 otherwise
* @return
*/
// There's a copy of canEditPage in LessonsAccess for use by services. Keep them in sync
public int getEditPrivs() {
if(editPrivs != null) {
return editPrivs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,7 @@ public int compare(SimpleStudentPage o1, SimpleStudentPage o2) {
int cols = 0;
int colnum = 0;

UIBranchContainer sectionWrapper = null;
UIBranchContainer sectionContainer = null;
UIBranchContainer columnContainer = null;
UIBranchContainer tableContainer = null;
Expand All @@ -1124,7 +1125,23 @@ public int compare(SimpleStudentPage o1, SimpleStudentPage o2) {
if (first || i.getType() == SimplePageItem.BREAK) {
boolean sectionbreak = false;
if (first || "section".equals(i.getFormat())) {
sectionContainer = UIBranchContainer.make(container, "section:");
sectionWrapper = UIBranchContainer.make(container, "sectionWrapper:");
boolean collapsible = i.getAttribute("collapsible") != null && (!"0".equals(i.getAttribute("collapsible")));
boolean defaultClosed = i.getAttribute("defaultClosed") != null && (!"0".equals(i.getAttribute("defaultClosed")));
UIOutput sectionHeader = UIOutput.make(sectionWrapper, "sectionHeader");
UIOutput.make(sectionWrapper, "sectionHeaderText", i.getName() == null ? "" : i.getName());
sectionHeader.decorate(new UIStyleDecorator(i.getName() == null || i.getName().isEmpty() ? "skip" : ""));
sectionContainer = UIBranchContainer.make(sectionWrapper, "section:");
if (collapsible) {
sectionHeader.decorate(new UIStyleDecorator("collapsibleSectionHeader"));
sectionContainer.decorate(new UIStyleDecorator("collapsible"));
if (defaultClosed ) {
sectionHeader.decorate(new UIStyleDecorator("closedSectionHeader"));
sectionContainer.decorate(new UIStyleDecorator("defaultClosed"));
} else {
sectionHeader.decorate(new UIStyleDecorator("openSectionHeader"));
}
}
cols = colCount(itemList, i.getId());
sectionbreak = true;
colnum = 0;
Expand Down
8 changes: 8 additions & 0 deletions lessonbuilder/tool/src/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ simplepage.columnblue=Blue
simplepage.columngreen=Green
simplepage.columnyellow=Yellow

simplepage.collapsible=Collapsible
simplepage.sectionTitle=Section Title
simplepage.default.closed=Start closed
simplepage.expand.all=Expand All
simplepage.collapse.all=Collapse All
simplepage.clickToExpand=Click to expand
simplepage.clickToCollapse=Click to collapse

simplepage.textItemUnavailable=This text item will show when all pre-requisites, as marked by an asterisk (*), have been completed
simplepage.multimediaItemUnavailable=This multimedia item will show when all pre-requisites, as marked by an asterisk (*), have been completed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ simplePageBean.peerEvalAllowSelfGrade
<property name="siteService" ref="org.sakaiproject.site.api.SiteService" />
<property name="authzGroupService" ref="org.sakaiproject.authz.api.AuthzGroupService" />
<property name="simplePageToolDao" ref="org.sakaiproject.lessonbuildertool.model.SimplePageToolDao" />
<property name="lessonsAccess" ref="org.sakaiproject.lessonbuildertool.service.LessonsAccess" />
</bean>

</beans>
63 changes: 62 additions & 1 deletion lessonbuilder/tool/src/webapp/css/Simplepagetool.css
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,7 @@ div[role="dialog"] {
float:right !important;
}
.action {
margin-top:5px;
margin-top: 5px;
}
.Mrphs-siteHierarchy {
height: auto !important;
Expand Down Expand Up @@ -1304,6 +1304,67 @@ padding:1px;
color: #666;
}

.sectionHeader {
padding: .5em;
}

.sectionHeader .collapseIcon {
font-family: FontAwesome;
float:right;
}

.sectionCollapsedIcon {
font-family: FontAwesome;
margin-left: 1em;
}
#expandCollapseButtons {
width: 100%;
text-align: right;
}

.defaultClosed {
display:none;
}

.collapsibleSectionHeader {
position: relative;
}

.toggleCollapse, .collapseIcon {
display: none;
}
.collapsibleSectionHeader .collapseIcon {
display: block;
}
.collapsibleSectionHeader .toggleCollapse {
display: block;
position: absolute;
opacity: 0;
z-index: 1000000;
webkit-transition: 0.3s ease;
moz-transition: 0.3s ease;
pointer-events: none;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 8px 10px;
font-size: 12px;
white-space: nowrap;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3);
top: 100%;
left: 50%;
margin: -2px 0 0 -10px;
}

.collapsibleSectionHeader:hover {
cursor: pointer;
background-color: #eee;
}

.collapsibleSectionHeader:hover .toggleCollapse {
opacity: 1;
margin-top: 6px;
}

/* position relative with no args does nothing, but editsection's position absolute needs it as a refernce */
.column {
position:relative;
Expand Down
Loading

0 comments on commit 3235e7f

Please sign in to comment.