Skip to content

Commit

Permalink
XWIKI-17733: Use a LiveTable to display the page liked in user profile
Browse files Browse the repository at this point in the history
XWIKI-17724: Display Likers in a LiveTable

  * Use livetables in Likes UI
  * Add a new missing method to count user likes globally for allowing
    pagination
  • Loading branch information
surli committed Oct 13, 2020
1 parent e64e133 commit 0993a7a
Show file tree
Hide file tree
Showing 15 changed files with 298 additions and 42 deletions.
11 changes: 8 additions & 3 deletions xwiki-platform-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -494,24 +494,29 @@
<new>method java.util.Optional&lt;org.xwiki.ratings.Rating&gt; org.xwiki.ratings.script.AbstractScriptRatingsManager::setRating(org.xwiki.model.reference.EntityReference, int) @ org.xwiki.ratings.script.RatingsScriptService</new>
<justification>Redesign of Ratings API: use new best practices for script service return if type is nullable.</justification>
</item>
<item>
<item>
<code>java.method.parameterTypeChanged</code>
<old>parameter org.xwiki.ratings.script.RatingApi org.xwiki.ratings.script.RatingsScriptService::setRating(===org.xwiki.model.reference.DocumentReference===, org.xwiki.model.reference.DocumentReference, int)</old>
<new>parameter java.util.Optional&lt;org.xwiki.ratings.Rating&gt; org.xwiki.ratings.script.AbstractScriptRatingsManager::setRating(===org.xwiki.model.reference.EntityReference===, org.xwiki.user.UserReference, int) @ org.xwiki.ratings.script.RatingsScriptService</new>
<justification>Redesign of Ratings API: allow to set rating for any reference (not only DocumentReference)</justification>
</item>
<item>
<item>
<code>java.method.parameterTypeChanged</code>
<old>parameter org.xwiki.ratings.script.RatingApi org.xwiki.ratings.script.RatingsScriptService::setRating(org.xwiki.model.reference.DocumentReference, ===org.xwiki.model.reference.DocumentReference===, int)</old>
<new>parameter java.util.Optional&lt;org.xwiki.ratings.Rating&gt; org.xwiki.ratings.script.AbstractScriptRatingsManager::setRating(org.xwiki.model.reference.EntityReference, ===org.xwiki.user.UserReference===, int) @ org.xwiki.ratings.script.RatingsScriptService</new>
<justification>Redesign of Ratings API: use new best practices for specifying a user by using the UserReference.</justification>
</item>
<item>
<item>
<code>java.method.returnTypeChanged</code>
<old>method org.xwiki.ratings.script.RatingApi org.xwiki.ratings.script.RatingsScriptService::setRating(org.xwiki.model.reference.DocumentReference, org.xwiki.model.reference.DocumentReference, int)</old>
<new>method java.util.Optional&lt;org.xwiki.ratings.Rating&gt; org.xwiki.ratings.script.AbstractScriptRatingsManager::setRating(org.xwiki.model.reference.EntityReference, org.xwiki.user.UserReference, int) @ org.xwiki.ratings.script.RatingsScriptService</new>
<justification>Redesign of Ratings API: use new best practices for script service return if type is nullable.</justification>
</item>
<item>
<code>java.method.addedToInterface</code>
<new>method long org.xwiki.like.LikeManager::countUserLikes(org.xwiki.user.UserReference) throws org.xwiki.like.LikeException</new>
<justification>Unstable API.</justification>
</item>
</revapi.ignore>
</analysisConfiguration>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ public interface LikeManager
*/
List<EntityReference> getUserLikes(UserReference source, int offset, int limit) throws LikeException;

/**
* Retrieve the total number of likes performed by a user.
*
* @param source the user who performs the likes to count.
* @return the total number of likes performed.
* @throws LikeException in case of problem when getting the information.
* @since 12.9RC1
*/
@Unstable
long countUserLikes(UserReference source) throws LikeException;

/**
* Retrieve like information a specific entity.
*
Expand Down Expand Up @@ -95,7 +106,9 @@ public interface LikeManager
* @param limit the limit used for pagination.
* @return a list of user references of users who liked this page.
* @throws LikeException in case of problem for performing the query.
* @since 12.9RC1
*/
@Unstable
List<UserReference> getLikers(EntityReference target, int offset, int limit) throws LikeException;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import org.xwiki.cache.CacheManager;
import org.xwiki.cache.config.LRUCacheConfiguration;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.manager.ComponentLifecycleException;
import org.xwiki.component.phase.Disposable;
import org.xwiki.component.phase.Initializable;
import org.xwiki.component.phase.InitializationException;
import org.xwiki.like.LikeConfiguration;
Expand Down Expand Up @@ -65,7 +63,7 @@
*/
@Component
@Singleton
public class DefaultLikeManager implements LikeManager, Initializable, Disposable
public class DefaultLikeManager implements LikeManager, Initializable
{
private static final int DEFAULT_LIKE_VOTE = 1;

Expand Down Expand Up @@ -126,12 +124,6 @@ public void initialize() throws InitializationException
}
}

@Override
public void dispose() throws ComponentLifecycleException
{
//this.authorizationManager.
}

private String getExistCacheKey(UserReference source, EntityReference target)
{
return String.format("%s_%s",
Expand Down Expand Up @@ -176,6 +168,18 @@ public List<EntityReference> getUserLikes(UserReference source, int offset, int
}
}

@Override
public long countUserLikes(UserReference source) throws LikeException
{
try {
return this.ratingsManager.countRatings(
Collections.singletonMap(RatingsManager.RatingQueryField.USER_REFERENCE, source));
} catch (RatingsException e) {
throw new LikeException(
String.format("Error when trying to count user likes for user [%s]", source), e);
}
}

@Override
public long getEntityLikes(EntityReference target) throws LikeException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,26 @@ public List<EntityReference> getUserLikes(UserReference userReference, int offse
return Collections.emptyList();
}

/**
* Count the number of likes performed by the given user.
*
* @param userReference the user for whom to count likes.
* @return the number of likes performed.
* @since 12.9RC1
*/
@Unstable
public Optional<Long> countUserLikes(UserReference userReference)
{
Optional<Long> result = Optional.empty();
try {
result = Optional.of(this.likeManager.countUserLikes(userReference));
} catch (LikeException e) {
this.logger.warn("Error while counting likes for user [{}]", userReference,
ExceptionUtils.getRootCause(e));
}
return result;
}

/**
* Determine if the current user already liked the given reference.
* @param entityReference the reference for which to check if the current liked it or not already.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,96 @@
## Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
## 02110-1301 USA, or see the FSF site: http://www.fsf.org.
## ---------------------------------------------------------------------------
#set ($likers = $services.like.getLikers($doc, 0, 50))
<div id="document-title"><h1>$services.localization.render('like.likers.title', [$doc.title])</h1></div>
<div id="likers-content">
#if ($likers.isEmpty())
$services.localization.render('like.likers.empty')
#if ($request.livetable == "true")
#macro (displayUserAliasWithAvatar $userReference $disabled)
<div class="user#if ($disabled) disabled#end" data-reference="$escapetool.xml($userReference)">
<span class="user-avatar-wrapper">
#getUserAvatarURL($userReference $avatarURL 120)
<img class="user-avatar" src="$escapetool.xml($avatarURL.url)" />
</span>
<a href="$xwiki.getURL($userReference)">$escapetool.xml($userReference.name)</a>
</div>
#end
$response.setContentType('application/json')
#set ($documentReference = $doc.documentReference)
##==============================
## Offset = item # at which to start displaying data
##==============================
#set($offset = $numbertool.toNumber($request.get('offset')))
## offset starts from 0 in velocity and 1 in javascript
#set($offset = $offset - 1)
#if($offset < 0)
#set($offset = 0)
#end
##==================
## Limit = # of items to display
##==================
#set($limit = $numbertool.toNumber($request.get('limit')))
##==========
## Sort direction
##==========
#set($order = "$!request.sort")
#if($order != '')
#set($orderDirection = "$!{request.get('dir').toLowerCase()}")
#if("$!orderDirection" != '' && "$!orderDirection" != 'asc')
#set($orderDirection = 'desc')
#end
#end
#set ($likeRecords = $services.like.getLikers($documentReference, $offset, $limit))
#set ($userRows = [])
#foreach($userReference in $likeRecords)
#set ($grayed = $xcontext.userReference == $userReference.reference)
#set ($userDoc = $xwiki.getDocument($userReference.reference))
#set ($userProperties = $services.user.getProperties($userReference))
#set ($userObject = $user.getObject('XWiki.XWikiUsers'))
#set ($row = {
'grayed': $grayed,
'doc_fullName': $userDoc.fullName,
'doc_wiki': $userDoc.wiki,
'doc_url': $userDoc.getURL(),
'doc_viewable': $services.security.authorization.hasAccess('view', $userDoc.documentReference),
'name': "#displayUserAliasWithAvatar($userDoc.documentReference $disabled)",
'first_name': $userProperties.firstName,
'last_name': $userProperties.lastName
})
#set ($discard = $userRows.add($row))
#end
## ===
## JSON
## ===
#set ($newOffset = $offset + 1)
#set ($optLikesNumber = $services.like.getLikes($documentReference))
#if ($optLikesNumber.isPresent())
#set ($totalRows = $optLikesNumber.get())
#else
<p>
$services.localization.render('like.likers.number', [$likers.size()])
</p>
<ul>
#foreach($liker in $likers)
<li>#displayUserLink($liker)</li>
#end
</ul>
#set ($totalRows = $likeRecords.size())
#end
</div>
{
"totalrows": $totalRows,
"returnedrows": $likeRecords.size(),
"offset": $newOffset,
"reqNo": $numbertool.toNumber($request.reqNo),
"rows": $jsontool.serialize($userRows)
}
#else
<h1>$escapetool.xml($services.localization.render('like.likers.title', [$doc.plainTitle]))</h1>
#set($columns = ["name", "first_name", "last_name"])
#set($columnsProperties = {
"name" : { "type" : "text", "sortable": false, "filterable": false, "html": true },
"first_name" : { "type" : "text", "sortable": false, "filterable": false},
"last_name" : { "type" : "text", "sortable": false, "filterable": false}
})
#set ($queryParams = {
"livetable": "true",
"xpage": "likers",
"outputSyntax": "plain"
})
## We rely on the same column name than the Users administration, so we use same translation prefix for now.
#set($options = {
'url': $doc.getURL('get', $escapetool.url($queryParams)),
'outputOnlyHtml': true,
'translationPrefix' : "xe.admin.users."
})

#livetable("likers" $columns $columnsProperties $options)
#end
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,14 @@ void getLikers() throws Exception
assertEquals(Arrays.asList(userReference1, userReference2, userReference3),
this.defaultLikeManager.getLikers(this.target, 12, 4));
}

@Test
void countUserLikes() throws Exception
{
when(this.ratingsManager.countRatings(
Collections.singletonMap(RatingsManager.RatingQueryField.USER_REFERENCE, this.userReference)))
.thenReturn(42L);

assertEquals(42L, this.defaultLikeManager.countUserLikes(this.userReference));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.xwiki.like.script;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;

Expand Down Expand Up @@ -54,6 +55,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -284,4 +286,22 @@ void cleanCache()
Locale.ROOT);
verify(this.asyncRendererCache).cleanCache(uixReference);
}

@Test
void countUserLikes() throws LikeException
{
when(this.likeManager.countUserLikes(userReference)).thenReturn(43L);
assertEquals(Optional.of(43L), this.likeScriptService.countUserLikes(this.userReference));
}

@Test
void getUserLikes() throws LikeException
{
List<EntityReference> expectedList = Arrays.asList(
mock(EntityReference.class),
mock(EntityReference.class),
mock(EntityReference.class));
when(this.likeManager.getUserLikes(this.userReference, 2, 32)).thenReturn(expectedList);
assertEquals(expectedList, this.likeScriptService.getUserLikes(this.userReference, 2, 32));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,4 @@ XWiki.Like.LikeConfigurationClass_alwaysDisplayButton="Gefällt mir" Schaltfläc
XWiki.Like.LikeConfigurationClass_alwaysDisplayButton.hint=Die "Gefällt mir" Schaltfläche immer anzeigen - selbst wenn der Nutzer kein Recht hat diese zu benutzen. Anschalten, um die Anzahl der "Gefällt mir" immer anzuzeigen.
XWiki.Like.LikeConfigurationClass_cacheCapacity=Cache-Größe
XWiki.Like.LikeConfigurationClass_cacheCapacity.hint=Anzahl der "Gefällt mir"-Informationen im Cache. Sie müssen XWiki neustarten, damit veränderte Einstellungen angewendet werden.</content>

</xwikidoc>
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,4 @@ XWiki.Like.LikeConfigurationClass_alwaysDisplayButton=Mostrar siempre el botón
XWiki.Like.LikeConfigurationClass_alwaysDisplayButton.hint=Mostrar el botón incluso si el usuario no tiene derecho de interaccionar con él. Úsalo si deseas mostrar siempre el contador de "Me gusta".
XWiki.Like.LikeConfigurationClass_cacheCapacity=Capacidad de la caché
XWiki.Like.LikeConfigurationClass_cacheCapacity.hint=Número de información de "Me gusta" guardada en la caché. Ten en cuenta que tienes que reiniciar XWiki para que se activen los cambios de esta opción.</content>

</xwikidoc>
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,4 @@ XWiki.Like.LikeConfigurationClass_alwaysDisplayButton=Toujours afficher le bouto
XWiki.Like.LikeConfigurationClass_alwaysDisplayButton.hint=Afficher le bouton même si l'utilisateur n'a pas les droits pour intéragir avec. Utilisez cette option si vous souhaitez toujours afficher le compteur de J'aime.
XWiki.Like.LikeConfigurationClass_cacheCapacity=Capacité du Cache
XWiki.Like.LikeConfigurationClass_cacheCapacity.hint=Nombre d'information J'aime à conserver dans le cache. Notez que vous devrez redémarrer XWiki pour que cette option soit prise en compte.</content>

</xwikidoc>
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,4 @@ XWiki.Like.LikeConfigurationClass_alwaysDisplayButton=Vis alltid Liker-knappen
XWiki.Like.LikeConfigurationClass_alwaysDisplayButton.hint=Vis knappen selv om brukeren ikke har rettigheter til å bruke den. Bruk hvis du alltid vil vise Liker-telleren.
XWiki.Like.LikeConfigurationClass_cacheCapacity=Hurtigbufferkapasitet
XWiki.Like.LikeConfigurationClass_cacheCapacity.hint=Antall Liker-informasjon som er lagret i hurtigbufferen. Merk at du må starte XWiki på nytt for at dette valget skal aktiveres.</content>

</xwikidoc>
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,4 @@ like.newlike.error=Ошибка при попытке поставить "Нра
### Missing: XWiki.Like.LikeConfigurationClass_alwaysDisplayButton.hint=Display the button even if the user doesn't have the rights to interact with it. Use it if you wish to always display the Likes counter.
### Missing: XWiki.Like.LikeConfigurationClass_cacheCapacity=Cache capacity
### Missing: XWiki.Like.LikeConfigurationClass_cacheCapacity.hint=Number of Like information kept in cache. Note that you have to restart XWiki for this option change to be taken into account.</content>

</xwikidoc>
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ like.button.title.like=Click to like the current page. Number of likes on this p
like.button.title.unlike=Click to unlike the current page. Number of likes on this page: {0}.
like.likers.empty=No one likes this page yet.
like.likers.number={0} person(s) like this page:
like.likers.title=Likes on {0}
like.likers.title=Likers of {0}
like.livetable.column.title=Title
like.livetable.column.location=Location
like.livetable.column.likes=Likes
XWiki.Like.LikeConfigurationClass_alwaysDisplayButton=Always display Like button
XWiki.Like.LikeConfigurationClass_alwaysDisplayButton.hint=Display the button even if the user doesn't have the rights to interact with it. Use it if you wish to always display the Likes counter.
XWiki.Like.LikeConfigurationClass_cacheCapacity=Cache capacity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,4 @@
<scope>wiki</scope>
</property>
</object>
</xwikidoc>
</xwikidoc>
Loading

0 comments on commit 0993a7a

Please sign in to comment.