James Moger
2013-07-03 d9d4677aa4f4774dddd57a6a9aef28931c8f3f5e
commit | author | age
f13c4c 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
a4d249 16 package com.gitblit.wicket.panels;
JM 17
8a2e9c 18 import java.text.MessageFormat;
a4d249 19 import java.util.ArrayList;
JM 20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27
eb96ea 28 import org.apache.wicket.Component;
a4d249 29 import org.apache.wicket.PageParameters;
JM 30 import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder;
31 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
32 import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
33 import org.apache.wicket.markup.html.basic.Label;
34 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
8c9a20 35 import org.apache.wicket.markup.html.link.ExternalLink;
8a2e9c 36 import org.apache.wicket.markup.html.link.Link;
a4d249 37 import org.apache.wicket.markup.html.panel.Fragment;
JM 38 import org.apache.wicket.markup.repeater.Item;
39 import org.apache.wicket.markup.repeater.data.DataView;
40 import org.apache.wicket.markup.repeater.data.IDataProvider;
41 import org.apache.wicket.markup.repeater.data.ListDataProvider;
42 import org.apache.wicket.model.IModel;
43 import org.apache.wicket.model.Model;
44
45 import com.gitblit.Constants.AccessRestrictionType;
46 import com.gitblit.GitBlit;
47 import com.gitblit.Keys;
8c9a20 48 import com.gitblit.SyndicationServlet;
13a3f5 49 import com.gitblit.models.ProjectModel;
1f9dae 50 import com.gitblit.models.RepositoryModel;
JM 51 import com.gitblit.models.UserModel;
661db6 52 import com.gitblit.utils.ArrayUtils;
a4d249 53 import com.gitblit.utils.StringUtils;
JM 54 import com.gitblit.wicket.GitBlitWebSession;
55 import com.gitblit.wicket.WicketUtils;
82df52 56 import com.gitblit.wicket.pages.BasePage;
a4d249 57 import com.gitblit.wicket.pages.EditRepositoryPage;
82df52 58 import com.gitblit.wicket.pages.EmptyRepositoryPage;
13a3f5 59 import com.gitblit.wicket.pages.ProjectPage;
fee060 60 import com.gitblit.wicket.pages.RepositoriesPage;
a4d249 61 import com.gitblit.wicket.pages.SummaryPage;
1e1b85 62 import com.gitblit.wicket.pages.UserPage;
a4d249 63
JM 64 public class RepositoriesPanel extends BasePanel {
65
66     private static final long serialVersionUID = 1L;
8a2e9c 67
6662e3 68     public RepositoriesPanel(String wicketId, final boolean showAdmin, final boolean showManagement,
cb57ec 69             List<RepositoryModel> models, boolean enableLinks,
2a7306 70             final Map<AccessRestrictionType, String> accessRestrictionTranslations) {
a4d249 71         super(wicketId);
8a2e9c 72
cb57ec 73         final boolean linksActive = enableLinks;
831469 74         final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);
JM 75
a4d249 76         final UserModel user = GitBlitWebSession.get().getUser();
831469 77
8a2e9c 78         final IDataProvider<RepositoryModel> dp;
JM 79
6662e3 80         Fragment managementLinks;
JM 81         if (showAdmin) {
82             // user is admin
83             managementLinks = new Fragment("managementPanel", "adminLinks", this);
84             managementLinks.add(new Link<Void>("clearCache") {
fee060 85
6662e3 86                 private static final long serialVersionUID = 1L;
fee060 87
6662e3 88                 @Override
JM 89                 public void onClick() {
90                     GitBlit.self().resetRepositoryListCache();
91                     setResponsePage(RepositoriesPage.class);
92                 }
93             }.setVisible(GitBlit.getBoolean(Keys.git.cacheRepositoryList, true)));
94             managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
95             add(managementLinks);
7f7051 96         } else if (showManagement && user != null && user.canCreate()) {
6662e3 97             // user can create personal repositories
JM 98             managementLinks = new Fragment("managementPanel", "personalLinks", this);
99             managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
100             add(managementLinks);
101         } else {
102             // user has no management permissions
103             add (new Label("managementPanel").setVisible(false));
104         }
8a2e9c 105
2a7306 106         if (GitBlit.getString(Keys.web.repositoryListType, "flat").equalsIgnoreCase("grouped")) {
5c2841 107             List<RepositoryModel> rootRepositories = new ArrayList<RepositoryModel>();
a4d249 108             Map<String, List<RepositoryModel>> groups = new HashMap<String, List<RepositoryModel>>();
JM 109             for (RepositoryModel model : models) {
110                 String rootPath = StringUtils.getRootPath(model.name);
88598b 111                 if (StringUtils.isEmpty(rootPath)) {
5c2841 112                     // root repository
88598b 113                     rootRepositories.add(model);
5c2841 114                 } else {
JM 115                     // non-root, grouped repository
116                     if (!groups.containsKey(rootPath)) {
117                         groups.put(rootPath, new ArrayList<RepositoryModel>());
118                     }
119                     groups.get(rootPath).add(model);
a4d249 120                 }
JM 121             }
122             List<String> roots = new ArrayList<String>(groups.keySet());
123             Collections.sort(roots);
88598b 124
5c2841 125             if (rootRepositories.size() > 0) {
JM 126                 // inject the root repositories at the top of the page
7ec9d3 127                 roots.add(0, "");
JM 128                 groups.put("", rootRepositories);
5c2841 129             }
13a3f5 130                         
a4d249 131             List<RepositoryModel> groupedModels = new ArrayList<RepositoryModel>();
JM 132             for (String root : roots) {
133                 List<RepositoryModel> subModels = groups.get(root);
7ec9d3 134                 ProjectModel project = GitBlit.self().getProjectModel(root);
d9d467 135                 GroupRepositoryModel group = new GroupRepositoryModel(project == null ? root : project.name, subModels.size());
7ec9d3 136                 if (project != null) {
JM 137                     group.title = project.title;
138                     group.description = project.description;
13a3f5 139                 }
JM 140                 groupedModels.add(group);
831469 141                 Collections.sort(subModels);
a4d249 142                 groupedModels.addAll(subModels);
JM 143             }
8a2e9c 144             dp = new RepositoriesProvider(groupedModels);
a4d249 145         } else {
8a2e9c 146             dp = new SortableRepositoriesProvider(models);
a4d249 147         }
72633d 148
2179fb 149         final String baseUrl = WicketUtils.getGitblitURL(getRequest());
309c55 150         final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);
1e1b85 151         
a4d249 152         DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) {
JM 153             private static final long serialVersionUID = 1L;
2a7306 154             int counter;
94750e 155             String currGroupName;
a4d249 156
8a2e9c 157             @Override
JM 158             protected void onBeforeRender() {
159                 super.onBeforeRender();
160                 counter = 0;
161             }
162
a4d249 163             public void populateItem(final Item<RepositoryModel> item) {
JM 164                 final RepositoryModel entry = item.getModelObject();
165                 if (entry instanceof GroupRepositoryModel) {
f5cbd0 166                     GroupRepositoryModel groupRow = (GroupRepositoryModel) entry;
94750e 167                     currGroupName = entry.name;
a4d249 168                     Fragment row = new Fragment("rowContent", "groupRepositoryRow", this);
JM 169                     item.add(row);
1e1b85 170                     
f5cbd0 171                     String name = groupRow.name;
1e1b85 172                     if (name.charAt(0) == '~') {
JM 173                         // user page
174                         String username = name.substring(1);
175                         UserModel user = GitBlit.self().getUserModel(username);
f5cbd0 176                         row.add(new LinkPanel("groupName", null, (user == null ? username : user.getDisplayName()) + " (" + groupRow.count + ")", UserPage.class, WicketUtils.newUsernameParameter(username)));
1e1b85 177                         row.add(new Label("groupDescription", getString("gb.personalRepositories")));
JM 178                     } else {
179                         // project page
f5cbd0 180                         row.add(new LinkPanel("groupName", null, groupRow.toString(), ProjectPage.class, WicketUtils.newProjectParameter(entry.name)));
1e1b85 181                         row.add(new Label("groupDescription", entry.description == null ? "":entry.description));
JM 182                     }
a4d249 183                     WicketUtils.setCssClass(item, "group");
94750e 184                     // reset counter so that first row is light background
JM 185                     counter = 0;
a4d249 186                     return;
JM 187                 }
188                 Fragment row = new Fragment("rowContent", "repositoryRow", this);
189                 item.add(row);
309c55 190
94750e 191                 // try to strip group name for less cluttered list
6c6fbf 192                 String repoName = entry.toString();
94750e 193                 if (!StringUtils.isEmpty(currGroupName) && (repoName.indexOf('/') > -1)) {
JM 194                     repoName = repoName.substring(currGroupName.length() + 1);
195                 }
1c2215 196                                 
309c55 197                 // repository swatch
1c2215 198                 Component swatch;
JM 199                 if (entry.isBare){
200                     swatch = new Label("repositorySwatch", "&nbsp;").setEscapeModelStrings(false);
201                 } else {
202                     swatch = new Label("repositorySwatch", "!");
ccab3a 203                     WicketUtils.setHtmlTooltip(swatch, getString("gb.workingCopyWarning"));
1c2215 204                 }
4ce036 205                 WicketUtils.setCssBackground(swatch, entry.toString());
309c55 206                 row.add(swatch);
JM 207                 swatch.setVisible(showSwatch);
208
82df52 209                 if (linksActive) {
JM 210                     Class<? extends BasePage> linkPage;
211                     if (entry.hasCommits) {
212                         // repository has content
213                         linkPage = SummaryPage.class;
214                     } else {
215                         // new/empty repository OR proposed repository
216                         linkPage = EmptyRepositoryPage.class;
217                     }
218
a4d249 219                     PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);
82df52 220                     row.add(new LinkPanel("repositoryName", "list", repoName, linkPage, pp));
2a7306 221                     row.add(new LinkPanel("repositoryDescription", "list", entry.description,
82df52 222                             linkPage, pp));
831469 223                 } else {
82df52 224                     // no links like on a federation page
94750e 225                     row.add(new Label("repositoryName", repoName));
831469 226                     row.add(new Label("repositoryDescription", entry.description));
JM 227                 }
228                 if (entry.hasCommits) {
229                     // Existing repository
72633d 230                     row.add(new Label("repositorySize", entry.size).setVisible(showSize));
a4d249 231                 } else {
JM 232                     // New repository
017749 233                     row.add(new Label("repositorySize", "<span class='empty'>(" + getString("gb.empty") + ")</span>")
88598b 234                             .setEscapeModelStrings(false));
a4d249 235                 }
JM 236
e4e682 237                 if (entry.isSparkleshared()) {
JM 238                     row.add(WicketUtils.newImage("sparkleshareIcon", "star_16x16.png",
239                             getString("gb.isSparkleshared")));
240                 } else {
241                     row.add(WicketUtils.newClearPixel("sparkleshareIcon").setVisible(false));
242                 }
243                 
cf265a 244                 if (entry.isFork()) {
JM 245                     row.add(WicketUtils.newImage("forkIcon", "commit_divide_16x16.png",
246                             getString("gb.isFork")));
247                 } else {
248                     row.add(WicketUtils.newClearPixel("forkIcon").setVisible(false));
249                 }
250
a4d249 251                 if (entry.useTickets) {
2a7306 252                     row.add(WicketUtils.newImage("ticketsIcon", "bug_16x16.png",
JM 253                             getString("gb.tickets")));
a4d249 254                 } else {
JM 255                     row.add(WicketUtils.newBlankImage("ticketsIcon"));
256                 }
257
258                 if (entry.useDocs) {
2a7306 259                     row.add(WicketUtils
JM 260                             .newImage("docsIcon", "book_16x16.png", getString("gb.docs")));
a4d249 261                 } else {
JM 262                     row.add(WicketUtils.newBlankImage("docsIcon"));
263                 }
264
265                 if (entry.isFrozen) {
2a7306 266                     row.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png",
JM 267                             getString("gb.isFrozen")));
a4d249 268                 } else {
JM 269                     row.add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));
831469 270                 }
JM 271
272                 if (entry.isFederated) {
273                     row.add(WicketUtils.newImage("federatedIcon", "federated_16x16.png",
274                             getString("gb.isFederated")));
275                 } else {
276                     row.add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));
a4d249 277                 }
JM 278                 switch (entry.accessRestriction) {
279                 case NONE:
280                     row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
281                     break;
282                 case PUSH:
2a7306 283                     row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",
JM 284                             accessRestrictionTranslations.get(entry.accessRestriction)));
a4d249 285                     break;
JM 286                 case CLONE:
2a7306 287                     row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",
JM 288                             accessRestrictionTranslations.get(entry.accessRestriction)));
a4d249 289                     break;
JM 290                 case VIEW:
2a7306 291                     row.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",
JM 292                             accessRestrictionTranslations.get(entry.accessRestriction)));
a4d249 293                     break;
JM 294                 default:
295                     row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
296                 }
297
661db6 298                 String owner = "";
JM 299                 if (!ArrayUtils.isEmpty(entry.owners)) {
300                     // display first owner
301                     for (String username : entry.owners) {
302                         UserModel ownerModel = GitBlit.self().getUserModel(username);
303                         if (ownerModel != null) {
304                             owner = ownerModel.getDisplayName();
305                             break;
306                         }
307                     }
308                     if (entry.owners.size() > 1) {
309                         owner += ", ...";
fb6bf3 310                     }
JM 311                 }
661db6 312                 Label ownerLabel = new Label("repositoryOwner", owner);
JM 313                 WicketUtils.setHtmlTooltip(ownerLabel, ArrayUtils.toString(entry.owners));
314                 row.add(ownerLabel);
a4d249 315
a2709d 316                 String lastChange;
JM 317                 if (entry.lastChange.getTime() == 0) {
318                     lastChange = "--";
319                 } else {
9adf62 320                     lastChange = getTimeUtils().timeAgo(entry.lastChange);
a2709d 321                 }
a4d249 322                 Label lastChangeLabel = new Label("repositoryLastChange", lastChange);
JM 323                 row.add(lastChangeLabel);
9adf62 324                 WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));
a4d249 325
6662e3 326                 boolean showOwner = user != null && entry.isOwner(user.username);
1e1b85 327                 boolean myPersonalRepository = showOwner && entry.isUsersPersonalRepository(user.username);
JM 328                 if (showAdmin || myPersonalRepository) {
2a7306 329                     Fragment repositoryLinks = new Fragment("repositoryLinks",
JM 330                             "repositoryAdminLinks", this);
331                     repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",
332                             EditRepositoryPage.class, WicketUtils
333                                     .newRepositoryParameter(entry.name)));
8a2e9c 334                     Link<Void> deleteLink = new Link<Void>("deleteRepository") {
JM 335
336                         private static final long serialVersionUID = 1L;
337
338                         @Override
339                         public void onClick() {
340                             if (GitBlit.self().deleteRepositoryModel(entry)) {
341                                 if (dp instanceof SortableRepositoriesProvider) {
1f52c8 342                                     info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));
8a2e9c 343                                     ((SortableRepositoriesProvider) dp).remove(entry);
JM 344                                 } else {
1f52c8 345                                     setResponsePage(getPage().getClass(), getPage().getPageParameters());
8a2e9c 346                                 }
JM 347                             } else {
017749 348                                 error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));
8a2e9c 349                             }
JM 350                         }
351                     };
2a7306 352                     deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
017749 353                             getString("gb.deleteRepository"), entry)));
8a2e9c 354                     repositoryLinks.add(deleteLink);
a4d249 355                     row.add(repositoryLinks);
JM 356                 } else if (showOwner) {
2a7306 357                     Fragment repositoryLinks = new Fragment("repositoryLinks",
JM 358                             "repositoryOwnerLinks", this);
359                     repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",
360                             EditRepositoryPage.class, WicketUtils
361                                     .newRepositoryParameter(entry.name)));
a4d249 362                     row.add(repositoryLinks);
JM 363                 } else {
364                     row.add(new Label("repositoryLinks"));
365                 }
2179fb 366                 row.add(new ExternalLink("syndication", SyndicationServlet.asLink(baseUrl,
JM 367                         entry.name, null, 0)).setVisible(linksActive));
a4d249 368                 WicketUtils.setAlternatingBackground(item, counter);
JM 369                 counter++;
370             }
371         };
372         add(dataView);
373
374         if (dp instanceof SortableDataProvider<?>) {
375             // add sortable header
376             SortableDataProvider<?> sdp = (SortableDataProvider<?>) dp;
377             Fragment fragment = new Fragment("headerContent", "flatRepositoryHeader", this);
378             fragment.add(newSort("orderByRepository", SortBy.repository, sdp, dataView));
379             fragment.add(newSort("orderByDescription", SortBy.description, sdp, dataView));
380             fragment.add(newSort("orderByOwner", SortBy.owner, sdp, dataView));
381             fragment.add(newSort("orderByDate", SortBy.date, sdp, dataView));
382             add(fragment);
383         } else {
384             // not sortable
385             Fragment fragment = new Fragment("headerContent", "groupRepositoryHeader", this);
386             add(fragment);
387         }
388     }
8a2e9c 389
2a7306 390     private static class GroupRepositoryModel extends RepositoryModel {
a4d249 391
JM 392         private static final long serialVersionUID = 1L;
393
2a7306 394         int count;
13a3f5 395         String title;
8a2e9c 396
JM 397         GroupRepositoryModel(String name, int count) {
a4d249 398             super(name, "", "", new Date(0));
8a2e9c 399             this.count = count;
JM 400         }
401
402         @Override
403         public String toString() {
b94059 404             return (StringUtils.isEmpty(title) ? name  : title) + " (" + count + ")";
a4d249 405         }
JM 406     }
8a2e9c 407
a4d249 408     protected enum SortBy {
JM 409         repository, description, owner, date;
410     }
411
2a7306 412     protected OrderByBorder newSort(String wicketId, SortBy field, SortableDataProvider<?> dp,
JM 413             final DataView<?> dataView) {
a4d249 414         return new OrderByBorder(wicketId, field.name(), dp) {
JM 415             private static final long serialVersionUID = 1L;
416
417             @Override
418             protected void onSortChanged() {
419                 dataView.setCurrentPage(0);
420             }
421         };
422     }
423
2a7306 424     private static class RepositoriesProvider extends ListDataProvider<RepositoryModel> {
8a2e9c 425
JM 426         private static final long serialVersionUID = 1L;
427
428         public RepositoriesProvider(List<RepositoryModel> list) {
429             super(list);
430         }
431
432         @Override
433         public List<RepositoryModel> getData() {
434             return super.getData();
435         }
436
437         public void remove(RepositoryModel model) {
438             int index = getData().indexOf(model);
439             RepositoryModel groupModel = null;
440             if (index == (getData().size() - 1)) {
441                 // last element
442                 if (index > 0) {
443                     // previous element is group header, then this is last
444                     // repository in group. remove group too.
445                     if (getData().get(index - 1) instanceof GroupRepositoryModel) {
446                         groupModel = getData().get(index - 1);
447                     }
448                 }
449             } else if (index < (getData().size() - 1)) {
450                 // not last element. check next element for group match.
2a7306 451                 if (getData().get(index - 1) instanceof GroupRepositoryModel
JM 452                         && getData().get(index + 1) instanceof GroupRepositoryModel) {
8a2e9c 453                     // repository is sandwiched by group headers so this
JM 454                     // repository is the only element in the group. remove
455                     // group.
456                     groupModel = getData().get(index - 1);
457                 }
458             }
459
460             if (groupModel == null) {
461                 // Find the group and decrement the count
462                 for (int i = index; i >= 0; i--) {
463                     if (getData().get(i) instanceof GroupRepositoryModel) {
464                         ((GroupRepositoryModel) getData().get(i)).count--;
465                         break;
466                     }
467                 }
468             } else {
469                 // Remove the group header
470                 getData().remove(groupModel);
471             }
472
473             getData().remove(model);
474         }
475     }
476
2a7306 477     private static class SortableRepositoriesProvider extends SortableDataProvider<RepositoryModel> {
JM 478
a4d249 479         private static final long serialVersionUID = 1L;
2a7306 480
JM 481         private List<RepositoryModel> list;
a4d249 482
8a2e9c 483         protected SortableRepositoriesProvider(List<RepositoryModel> list) {
a4d249 484             this.list = list;
JM 485             setSort(SortBy.date.name(), false);
486         }
487
8a2e9c 488         public void remove(RepositoryModel model) {
JM 489             list.remove(model);
490         }
491
a4d249 492         @Override
JM 493         public int size() {
2a7306 494             if (list == null) {
a4d249 495                 return 0;
2a7306 496             }
a4d249 497             return list.size();
JM 498         }
499
500         @Override
501         public IModel<RepositoryModel> model(RepositoryModel header) {
502             return new Model<RepositoryModel>(header);
503         }
504
505         @Override
506         public Iterator<RepositoryModel> iterator(int first, int count) {
507             SortParam sp = getSort();
508             String prop = sp.getProperty();
509             final boolean asc = sp.isAscending();
510
511             if (prop == null || prop.equals(SortBy.date.name())) {
512                 Collections.sort(list, new Comparator<RepositoryModel>() {
513                     @Override
514                     public int compare(RepositoryModel o1, RepositoryModel o2) {
2a7306 515                         if (asc) {
a4d249 516                             return o1.lastChange.compareTo(o2.lastChange);
2a7306 517                         }
a4d249 518                         return o2.lastChange.compareTo(o1.lastChange);
JM 519                     }
520                 });
521             } else if (prop.equals(SortBy.repository.name())) {
522                 Collections.sort(list, new Comparator<RepositoryModel>() {
523                     @Override
524                     public int compare(RepositoryModel o1, RepositoryModel o2) {
2a7306 525                         if (asc) {
a4d249 526                             return o1.name.compareTo(o2.name);
2a7306 527                         }
a4d249 528                         return o2.name.compareTo(o1.name);
JM 529                     }
530                 });
531             } else if (prop.equals(SortBy.owner.name())) {
532                 Collections.sort(list, new Comparator<RepositoryModel>() {
533                     @Override
534                     public int compare(RepositoryModel o1, RepositoryModel o2) {
661db6 535                         String own1 = ArrayUtils.toString(o1.owners);
JM 536                         String own2 = ArrayUtils.toString(o2.owners);
2a7306 537                         if (asc) {
661db6 538                             return own1.compareTo(own2);
2a7306 539                         }
661db6 540                         return own2.compareTo(own1);
a4d249 541                     }
JM 542                 });
543             } else if (prop.equals(SortBy.description.name())) {
544                 Collections.sort(list, new Comparator<RepositoryModel>() {
545                     @Override
546                     public int compare(RepositoryModel o1, RepositoryModel o2) {
2a7306 547                         if (asc) {
a4d249 548                             return o1.description.compareTo(o2.description);
2a7306 549                         }
a4d249 550                         return o2.description.compareTo(o1.description);
JM 551                     }
552                 });
553             }
554             return list.subList(first, first + count).iterator();
555         }
556     }
557 }