Implement command "Search" in Connector.

Jun 27, 2013 at 5:18 AM
Hi, thanks for Connector.

I have a bit question here. When i set "search" to toolbar, it display but input something and click search, it show " Unknown command". I search on wiki and on github but may be no answer to how implement it : https://github.com/Studio-42/elFinder/issues/606.

I think i should config command search in Client configuration options and server will return a json result .
commandsOptions : {
    search: {
}
The problem is I don't know how to create correct json on server and property to config seach command.
Coordinator
Jun 27, 2013 at 8:47 AM
Hi. The 'search' is not implemented yet in connector. And there are several reasons. In original php connector this command returns all files and subdirs that satisfy the search query. But it could affect the performance. So in my opinion this command requered something more intellectial than enumerate throughout files and subdirs.

As for your question, I'm grabbed response and request from original demo and approximately decoded format of this comand
//Request

cmd: search //command
q:  some_query //query string


//Response

debug: [] //debug information (optional)
files: [] //array of mached files and directories. You can use DTOBase.Create to create every item in this array

I hope this helps.
Nov 19, 2014 at 1:33 PM
I have already implemented search command on C#, for example:

In Class SearchCommand.cs

using elFinder.Connector.Config;
using elFinder.Connector.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using elFinder.Connector.Service;

namespace elFinder.Connector.Command
{
public class SearchCommand : ICommand
{
    private class searchArgs
    {
        public string q { get; set; }
        public string target { get; set; }
        public int? tree { get; set; }
    }

    private readonly IVolumeManager _volumeManager;

    public SearchCommand(IVolumeManager volumeManager)
    {
        _volumeManager = volumeManager;
    }

    public string Name
    {
        get { return "search"; }
    }

    public Response.IResponse Execute(CommandArgs args)
    {
        searchArgs search = args.As<searchArgs>();

        // get volume for our target
        IVolume volume = _volumeManager.GetByHash(search.target);

        // try to get directory for our target
        DirectoryModel diretorioRoot = (volume != null ? volume.GetDirectoryByHash(search.target) : null);

        if ((diretorioRoot == null || diretorioRoot.IsReadable.IsFalse()))
        {
            // get default volume
            volume = _volumeManager.DefaultVolume;

            if (volume != null)
            {
                diretorioRoot = volume.GetRootDirectory();
            }
        }

        // if we still haven't got volume service, then something is wrong
        if (diretorioRoot == null || diretorioRoot.IsReadable.IsFalse())
        {
            return new Response.ErrorResponse("target dir not found or access denied");
        }

        List<ObjectModel> subItems = new List<ObjectModel>();

        List<ObjectModel> subItemsManipulado = new List<ObjectModel>();

        // get sub directories
        if (search.tree.IsTrue())
        {
            subItems.AddRange(volume.GetSubdirectoriesFlat(diretorioRoot));
        }
        else
        {
            subItems.AddRange(volume.GetSubdirectoriesFlat(diretorioRoot, 0));
        }

        // get files in our CWD
        subItems.AddRange(volume.GetFiles(diretorioRoot));

        foreach (ObjectModel objectModel in subItems)
        {
            if (objectModel.Mime.Equals("directory"))
            {
                if (objectModel.Name.ToLower().Contains(search.q.ToLower()))
                {
                    subItemsManipulado.Add(objectModel);
                }
            }
            else
            {
                //obtendo o indice
                int indice = objectModel.Name.LastIndexOf('.');

                string nomeArquivo = objectModel.Name.Substring(0, indice);

                if (nomeArquivo.ToLower().Contains(search.q.ToLower()))
                {
                    subItemsManipulado.Add(objectModel);
                }
            }
        }

        return new Response.SearchResponse(subItemsManipulado.ToArray());
    }
}
}

In Class SearchResponse.cs

using elFinder.Connector.Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace elFinder.Connector.Response
{
public class SearchResponse : JSONResponse
{
    [JsonProperty("files")]
    public ObjectModel[] SubItems { get; protected set; }

    public SearchResponse(ObjectModel[] subItems)
    {
        SubItems = subItems;
    }
}
}

I hope have help with solution. Thanks.
Jan 28, 2015 at 11:10 AM
Hi,
I coded this solution as I could not see how to use the earlier code.

In the Response folder create a new class called SearchResponse.cs:
using ElFinder.DTO;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace ElFinder.Response
{
    [DataContract]
    internal class SearchResponse
    {
        public SearchResponse()
        {
            Files = new List<DTOBase>();
        }

        [DataMember(Name = "files")]
        public List<DTOBase> Files { get; private set; }
    }
}
In the Facade folder update the IDriver.cs file to include a new method:
        JsonResult Search(string query); 
In the Facade folder, add a new method to FileSystemDriver.cs:
        JsonResult IDriver.Search(string query)
        {
            SearchResponse answer = new SearchResponse();

            foreach (Root root in _roots)
            {
                IEnumerable<FileInfo> fileList = new DirectoryInfo(root.Directory.FullName).EnumerateFiles("*" + query + "*", SearchOption.AllDirectories);
                foreach (FileInfo file in fileList)
                {
                     answer.Files.Add(DTOBase.Create(file, root));
                }
            }

            return Json(answer);
        }
And finally in the Facade folder, update Connector.cs changing the Process method to include a new case option to call the new command :
                case "search":
                    string query = parameters["q"];

                    if (string.IsNullOrEmpty(query))
                        return Error.MissedParameter("query");
                    
                    return _driver.Search(query);
Apr 8, 2016 at 5:21 PM
Edited Apr 8, 2016 at 5:21 PM
Thanks davejsmith, your solution is very usefully.

if someone is interesting in include on the search also directories names:


In the Facade folder, modify the method to FileSystemDriver.cs:
        JsonResult IDriver.Search(string query)
        {
            SearchResponse answer = new SearchResponse();

            foreach (Root root in _roots)
            {
                IEnumerable<FileInfo> fileList = new DirectoryInfo(root.Directory.FullName).EnumerateFiles("*" + query + "*", SearchOption.AllDirectories);
                foreach (FileInfo file in fileList)
                {
                     answer.Files.Add(DTOBase.Create(file, root));
                }
                //Agregando directorios a la búsqueda...
                 IEnumerable<DirectoryInfo> dirList = new DirectoryInfo(root.Directory.FullName).EnumerateDirectories("*" + query + "*", SearchOption.AllDirectories);
                foreach (DirectoryInfo dir in dirList)
                {
                    answer.Files.Add(DTOBase.Create(dir, root));
                }
            }

            return Json(answer);
        }