text: /*!
   * Copyright 2013 Apereo Foundation (AF) Licensed under the
   * Educational Community License, Version 2.0 (the "License"); you may
   * not use this file except in compliance with the License. You may
   * obtain a copy of the License at
   *
   *     http://opensource.org/licenses/ECL-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an "AS IS"
   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
   * or implied. See the License for the specific language governing
   * permissions and limitations under the License.
   */
  var _ = require('underscore');
  var AuthzAPI = require('oae-authz');
  var AuthzUtil = require('oae-authz/lib/util');
  var OaeUtil = require('oae-util/lib/util');
  var PrincipalsUtil = require('oae-principals/lib/util');
  var SearchConstants = require('oae-search/lib/constants').SearchConstants;
  var SearchAPI = require('oae-search');
  var SearchUtil = require('oae-search/lib/util');
  var TenantsUtil = require('oae-tenants/lib/util');
  var Validator = require('oae-util/lib/validator').Validator;
  
  /**
   * Register a search that searches through a user or group library.
   *
   * @param  {String} searchName   The name under which this search will be available.
   * @param  {String} resourceType The type of researches this search should return. ex: discussion or content.
   */
  module.exports.registerLibrarySearch = function(searchName, resourceType) {
  
      /*!
       * Search that searches a library.
       *
       * In addition to the specific `opts` parameters documented here, there are more generic options available that impact all
       * searches. @see SearchAPI#search for more information.
       *
       * @param  {Context}       ctx                 The context of the current request
       * @param  {Object}        opts                General search options
       * @param  {String[]}      opts.pathParams     An array of required parameters for the search
       * @param  {String}        opts.pathParams[0]  The principal whose library to search
       * @param  {Function}      callback            Invoked when the process completes
       * @param  {Object}        callback.err        An error that occurred, if any
       * @param  {SearchResult}  callback.results    An object that represents the results of the query
       */
      var searchCallback = function(ctx, opts, callback) {
          // Sanitize the custom search options
          opts = opts || {};
          opts.principalId = opts.pathParams[0];
          opts.limit = OaeUtil.getNumberParam(opts.limit, 12, 1, 25);
  
          var validator = new Validator();
          validator.check(opts.principalId, {'code': 400, 'msg': 'Must specificy an id of a user or group to search'}).isPrincipalId();
          if (validator.hasErrors()) {
              return callback(validator.getFirstError());
          }
  
          var authzPrincipal = AuthzUtil.getPrincipalFromId(opts.principalId);
          if (ctx.user() && (ctx.user().isAdmin(authzPrincipal.tenantAlias) || ctx.user().id === opts.principalId)) {
              // Perform the search with full access when the current user is an administrator of (or *is*) the target principal
              _search(ctx, resourceType, true, opts, callback);
          } else if (ctx.user() && PrincipalsUtil.isGroup(opts.principalId)) {
              // If we're searching a group library, assume full access to all resources in the group if the user is a member of the group
              AuthzAPI.hasAnyRole(ctx.user().id, opts.principalId, function(err, hasAnyRole) {
                  if (err) {
                      return callback(err);
                  }
                  _search(ctx, resourceType, hasAnyRole, opts, callback);
              });
          } else {
              // Either we're anonymous or we're searching some other user's library
              _search(ctx, resourceType, false, opts, callback);
          }
      };
  
      // Register our search
      SearchAPI.registerSearch(searchName, searchCallback);
  };
  
  /**
   * Perform the search that searches a user or group's library for content.
   *
   * @param  {Context}       ctx                 The context of the current request
   * @param  {Boolean}       fullAccess          Whether or not the user implicitly access to all content for the principal (e.g., is a member of the group, is global admin, is the current user)
   * @param  {Object}        opts                General search options
   * @param  {Function}      callback            Invoked when the process completes
   * @param  {Object}        callback.err        An error that occurred, if any
   * @param  {SearchResult}  callback.results    An object that represents the results of the query
   */
  var _search = function(ctx, resourceType, fullAccess, opts, callback) {
      var data = {};
  
      // Search trough all top level resource documents
      var query = SearchUtil.createQueryStringQuery(opts.q);
  
      // The filter object will limit it to a user or group library
      var filter = null;
  
      // Only look for discussion resources that are in the user's library
      var baseFilter = SearchUtil.filterAnd(
          SearchUtil.filterTerm('_type', SearchConstants.resourceMappingName),
          SearchUtil.filterTerm('resourceType', resourceType),
          SearchUtil.filterMembers([opts.principalId])
      );
  
      var authzPrincipal = AuthzUtil.getPrincipalFromId(opts.principalId);
      if (fullAccess) {
          // Being granted full access to this content, we are allowed to ignore visibility filters and query just the full 'private' library
          filter = baseFilter;
      } else if (TenantsUtil.isLoggedIn(ctx, authzPrincipal.tenantAlias)) {
          // Query the 'loggedin' library feed for the target principal, as we are logged in to its tenant
          filter = SearchUtil.filterAnd(
              baseFilter,
              SearchUtil.filterOr(
                  SearchUtil.filterTerm('visibility', 'public'),
                  SearchUtil.filterAnd(
                      SearchUtil.filterTerm('tenantAlias', ctx.tenant().alias),
                      SearchUtil.filterTerm('visibility', 'loggedin')
                  )
              )
          );
      } else {
          // For anonymous / cross-tenant users, only show the public library
          filter = SearchUtil.filterAnd(baseFilter, SearchUtil.filterTerm('visibility', 'public'));
      }
  
      // Wrap the query and filter into the top-level Query DSL "query" object and return it
      callback(null, SearchUtil.createQuery(query, filter, opts));
  };
  