Merge PR#19 from 'sieverssj/RAVE-1275'
diff --git a/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PageImpl.java b/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PageImpl.java
index c14790e..9e0b524 100644
--- a/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PageImpl.java
+++ b/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PageImpl.java
@@ -18,6 +18,8 @@
  */

 package org.apache.rave.portal.model.impl;

 

+import com.fasterxml.jackson.annotation.JsonBackReference;

+import com.fasterxml.jackson.annotation.JsonManagedReference;

 import org.apache.rave.model.*;

 

 import java.util.List;

@@ -28,8 +30,8 @@
     private String name;

     private String ownerId;

     private String contextId;

-    private Page parentPage;

-    private List<Page> subPages;

+    @JsonBackReference private Page parentPage;

+    @JsonManagedReference private List<Page> subPages;

     private PageLayout pageLayout;

     private List<Region> regions;

     private String pageType;

diff --git a/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultPagePermissionEvaluator.java b/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultPagePermissionEvaluator.java
index e5c75db..81cf436 100644
--- a/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultPagePermissionEvaluator.java
+++ b/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultPagePermissionEvaluator.java
@@ -224,7 +224,8 @@
         List<PageUser> members = trustedPage.getMembers();
         if (members != null) {
             for (PageUser pageUser : members){
-                if (userRepository.get(pageUser.getUserId()).getUsername().equals(viewer)){
+                User user = userRepository.get(pageUser.getUserId());
+                if (user != null && user.getUsername().equals(viewer)){
                     log.info("User "+viewer+" is a member of page "+trustedPage.getId());
                     if(checkEditorStatus){
                         log.info("checking editor:"+trustedPage.getId()+"@"+viewer+"@"+pageUser.isEditor());
@@ -238,4 +239,4 @@
         return false;
     }
 
-}
\ No newline at end of file
+}
diff --git a/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionPermissionEvaluator.java b/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionPermissionEvaluator.java
index 0d1be72..fb6cc19 100644
--- a/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionPermissionEvaluator.java
+++ b/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionPermissionEvaluator.java
@@ -196,18 +196,22 @@
         }

         //

         // Check that the viewer is a member

+        // Make sure we check parent page permissions as well

         //

         String viewer = ((User)authentication.getPrincipal()).getUsername();

-        for (PageUser pageUser:containerPage.getMembers()){

-            if (userRepository.get(pageUser.getUserId()).getUsername().equals(viewer)){

-                log.info("User "+viewer+" is a member of page "+containerPage.getId());

-                if(checkEditorStatus){

-                    return pageUser.isEditor();

+        do {

+            for (PageUser pageUser:containerPage.getMembers()){

+                User user = userRepository.get(pageUser.getUserId());

+                if (user != null && user.getUsername().equals(viewer)){

+                    log.info("User "+viewer+" is a member of page "+containerPage.getId());

+                    if(checkEditorStatus){

+                        return pageUser.isEditor();

+                    }

+                    return true;

                 }

-                return true;

             }

-        }

+        } while((containerPage = containerPage.getParentPage()) != null);

         return false;

     }

 

-}
\ No newline at end of file
+}

diff --git a/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionWidgetPermissionEvaluator.java b/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionWidgetPermissionEvaluator.java
index 7ec533f..d521e7f 100644
--- a/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionWidgetPermissionEvaluator.java
+++ b/rave-components/rave-core/src/main/java/org/apache/rave/portal/security/impl/DefaultRegionWidgetPermissionEvaluator.java
@@ -201,17 +201,21 @@
         }

         //

         // Check that the viewer is a member

+        // Make sure we check parent page permissions as well

         //

         String viewer = ((User)authentication.getPrincipal()).getUsername();

-        for (PageUser pageUser:containerPage.getMembers()){

-            if (userRepository.get(pageUser.getUserId()).getUsername().equals(viewer)){

-                log.info("User "+viewer+" is a member of page "+containerPage.getId());

-                if(checkEditorStatus){

-                    return pageUser.isEditor();

+        do {

+            for (PageUser pageUser:containerPage.getMembers()){

+                User user = userRepository.get(pageUser.getUserId());

+                if (user != null && user.getUsername().equals(viewer)){

+                    log.info("User "+viewer+" is a member of page "+containerPage.getId());

+                    if(checkEditorStatus){

+                        return pageUser.isEditor();

+                    }

+                    return true;

                 }

-                return true;

             }

-        }

+        } while((containerPage = containerPage.getParentPage()) != null);

         return false;

     }

 }

diff --git a/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/PageService.java b/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/PageService.java
index fadb17b..b5fc108 100644
--- a/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/PageService.java
+++ b/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/PageService.java
@@ -297,10 +297,10 @@
      * @param pageId   - the id of the page in question

      * @param userId   - the userid to add

      * @param pageName = name of the page

-     * @return true or false whether the user was added

+     * @return the newly cloned page

      */

-    @PreAuthorize("hasPermission(#pageId, 'org.apache.rave.model.Page', 'update')")

-    Boolean clonePageForUser(String pageId, String userId, String pageName);

+    @PreAuthorize("hasPermission(#pageId, 'org.apache.rave.model.Page', 'read')")

+    Page clonePageForUser(String pageId, String userId, String pageName);

 

     /**

      * Add another user to share this page with

@@ -356,4 +356,4 @@
     @PreAuthorize("hasPermission(#pageId, 'org.apache.rave.model.Page', 'update')")

     Boolean updatePageEditingStatus(String pageId, String userId, boolean isEditor);

 

-}
\ No newline at end of file
+}

diff --git a/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultPageService.java b/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultPageService.java
index 9ee4f60..9d6dff5 100644
--- a/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultPageService.java
+++ b/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultPageService.java
@@ -328,8 +328,9 @@
         return pageRepository.save(page);

     }

 

+    @Override

     @Transactional

-    public Boolean clonePageForUser(String pageId, String userId, String pageName) {

+    public Page clonePageForUser(String pageId, String userId, String pageName) {

         Page page = getPage(pageId);

         if(pageName == null || pageName.equals("null")){

             // try to use the original page name if none supplied

@@ -363,16 +364,21 @@
             }

         }

         clonedPage = getFromRepository(clonedPage.getId(), pageRepository);

+        if (clonedPage.getSubPages() == null) {

+            // Subpages should always be a list (even an empty one)

+            clonedPage.setSubPages(new ArrayList<Page>());

+        }

         clonedPage.setProperties(page.getProperties());

         // newly created page - so only one pageUser

         PageUser pageUser = clonedPage.getMembers().get(0);

         // update status to pending

-        pageUser.setPageStatus(PageInvitationStatus.PENDING);

-        if(pageRepository.save(clonedPage) != null){

-            return Boolean.TRUE;

-        }else{

-            return Boolean.FALSE;

+        User currentUser = userService.getAuthenticatedUser();

+        if (currentUser.getId().equals(user.getId())) {

+            pageUser.setPageStatus(PageInvitationStatus.OWNER);

+        } else {

+            pageUser.setPageStatus(PageInvitationStatus.PENDING);

         }

+        return pageRepository.save(clonedPage);

     }

 

     @Transactional

diff --git a/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java b/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java
index fd25291..27686af 100644
--- a/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java
+++ b/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java
@@ -252,7 +252,10 @@
             person.setId(user.getId());

             people.add(person);

         }

-        return new SearchResult<Person>(people, count);

+        SearchResult<Person> searchResult = new SearchResult<Person>(people, count);

+        searchResult.setOffset(offset);

+        searchResult.setPageSize(pageSize);

+        return searchResult;

     }

 

     @Override

diff --git a/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultPageServiceTest.java b/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultPageServiceTest.java
index 1646fc6..66b180d 100644
--- a/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultPageServiceTest.java
+++ b/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultPageServiceTest.java
@@ -46,6 +46,7 @@
 import org.apache.rave.portal.repository.WidgetRepository;

 import org.apache.rave.portal.service.PageService;

 import org.apache.rave.portal.service.UserService;

+import org.easymock.Capture;

 import org.easymock.EasyMock;

 import org.easymock.IAnswer;

 import org.junit.Before;

@@ -1453,4 +1454,44 @@
 

         pageService.moveRegionWidgetToPage(VALID_REGION_WIDGET_ID, TO_PAGE_ID);

     }

+

+    @Test

+    public void clonePage_noRegions() {

+        PageLayout layout = new PageLayoutImpl("foobar");

+        layout.setNumberOfRegions(0L);

+        layout.setRenderSequence(1L);

+        page.setPageLayout(layout);

+        page.setRegions(new ArrayList<Region>());

+        page.setSubPages(new ArrayList<Page>());

+

+        expect(userService.getAuthenticatedUser()).andReturn(user);

+        expect(pageRepository.get(PAGE_ID)).andReturn(page);

+        List<Page> pages = Lists.newLinkedList();

+        pages.add(page);

+        expect(pageRepository.getAllPagesForUserType(user.getId(), "user")).andReturn(pages);

+        final Capture<Page> pageCapture = new Capture<Page>();

+        expect(pageRepository.save(capture(pageCapture))).andAnswer(new IAnswer<Page>() {

+            @Override

+            public Page answer() throws Throwable {

+                Page savedPage = (Page) EasyMock.getCurrentArguments()[0];

+                savedPage.setId("42");

+                return savedPage;

+            }

+        }).anyTimes();

+        expect(pageRepository.get("42")).andAnswer(new IAnswer<Page>() {

+            @Override

+            public Page answer() throws Throwable {

+                return pageCapture.getValue();

+            }

+        });

+

+        expect(pageLayoutRepository.getByPageLayoutCode("foobar")).andReturn(layout);

+        expect(userService.getUserById(user.getId())).andReturn(user);

+        replay(pageLayoutRepository, pageRepository, userService);

+

+        Page clonedPage = pageService.clonePageForUser(PAGE_ID, user.getId(), null);

+        assertEquals("ID matches", "42", clonedPage.getId());

+        assertEquals("Owner ID is set", user.getId(), clonedPage.getOwnerId());

+        assertEquals("Layout is set", "foobar", clonedPage.getPageLayout().getCode());

+    }

 }

diff --git a/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PageApi.java b/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PageApi.java
index 712456c..8fcd4fa 100644
--- a/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PageApi.java
+++ b/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PageApi.java
@@ -264,10 +264,10 @@
 

     @ResponseBody

     @RequestMapping(value = "{pageId}/clone", method = RequestMethod.POST)

-    public RpcResult<Boolean> clonePageForUser(@PathVariable final String pageId, @RequestParam final String userId, @RequestParam final String pageName) {

-        return new RpcOperation<Boolean>() {

+    public RpcResult<Page> clonePageForUser(@PathVariable final String pageId, @RequestParam final String userId, @RequestParam final String pageName) {

+        return new RpcOperation<Page>() {

              @Override

-             public Boolean execute() {

+             public Page execute() {

                return pageService.clonePageForUser(pageId, userId, pageName);

              }

         }.getResult();

diff --git a/rave-portal-resources/src/main/webapp/static/script/core/rave_opensocial.js b/rave-portal-resources/src/main/webapp/static/script/core/rave_opensocial.js
index 7dea976..5b3d036 100644
--- a/rave-portal-resources/src/main/webapp/static/script/core/rave_opensocial.js
+++ b/rave-portal-resources/src/main/webapp/static/script/core/rave_opensocial.js
@@ -148,6 +148,11 @@
             //If the element has no ID then it was launched in some secondary location.  Destroy the view.
             if(widget._el.id === "") viewManager.destroyView(widget._view);
             widget.render(renderInto, {view: viewName, view_params: opt_params, ownerId: opt_ownerId});
+
+            var view = viewManager.getView(viewName);
+            if (view && view.getWidgetSite && view._uid) {
+                view.getWidgetSite().setAttribute('data-rave-view', view._uid);
+            }
         }
 
         function setPref(args, editToken, prefName, prefValue) {
@@ -218,6 +223,7 @@
         exports.renderWidget = function (widget, el, opts) {
             if (widget.error) {
                 widget.renderError(el, widget.error.message);
+                opts && opts.callback && opts.callback({'error' : widget.error});
                 return;
             }
             opts = opts || {};
diff --git a/rave-portal-resources/src/main/webapp/static/script/portal/rave_ui.js b/rave-portal-resources/src/main/webapp/static/script/portal/rave_ui.js
index 61bb950..e5fa226 100644
--- a/rave-portal-resources/src/main/webapp/static/script/portal/rave_ui.js
+++ b/rave-portal-resources/src/main/webapp/static/script/portal/rave_ui.js
@@ -100,7 +100,7 @@
 
                 search: function (e) {
                     //allow search function to trigger from enter keypress or button click
-                    if (e.which == 13 || _.isUndefined(e.which)) {
+                    if ((e.type == 'keypress' && e.which == 13) || e.type == 'click') {
                         var term = $('#searchTerm', this.$el).val();
 
                         this.models.users.filter(term);
@@ -133,7 +133,7 @@
                         'editor:add': '',
                         'editor:remove': '',
                         'clone': 'success.clone.page'
-                    }
+                    };
 
                     var msg = eventsToMessages[event];
 
@@ -232,7 +232,7 @@
                 uiState.currentRegion = ravePortal.getObjectIdFromDomId(ui.item.parent().get(0).id);
 
                 // Workaround for SHINDIG-1965 to keep the iframe re-parenting from firing the load events again
-                $(widgetEl).find('iframe').removeAttr('onload');
+                $(widgetEl).find('iframe').get(0).onload = function() {};
 
                 //for every drag operation, create an overlay for each iframe
                 //to prevent the iframe from intercepting mouse events
@@ -972,7 +972,7 @@
             } else {
                 elem = "<a>" + label + "</a>";
             }
-            return $(elem).attr("tooltip", tooltip);
+            return $(elem).attr("title", tooltip);
         }
 
         function insertWidgetToolbarAction(widgetId, label, image, tooltip, id, onSelected) {