In the second post in this series, we covered the basics of acquiring children and outputting them to PowerShell. Next we are going to add some nested navigation and only allow users to navigate into tags that we know exist.

First some administrative tasks. I decided to remove my custom C# Stackoverflow API that was very minimal and use something more robust. I went with StacMan and added it to the project using the nuget package. I then modified the existing code and views to work as before. I also changed the drive name to SO, to make it easier to type and to make it more relevant. With that out of the way, we can now forge ahead.

The final working code for this post can be found the NestedNavigation tag in the github repo.

To start, I modified the tag acquisition to keep the tags around, so I will only load them once and so I can reuse this code where necessary:

private static StacManResponse<Tag> tags;
private void LoadTags(bool forceReload=false)
{
    if (tags == null || forceReload)
    {
        var stackOverflow = new StacManClient();
        tags = stackOverflow.Tags.GetAll("stackoverflow.com").Result;
    }
}

Next, in my IsItemContainer, instead of blindly returning true, I now check to see if the path sent to us is blank (i.e. at the root) or if the path is a tag in our cache. This allows us to navigate into a valid state:

protected override bool IsItemContainer(string path)
{
    // The path is empty, so we are at the root.  The root is always valid for us.
    if (String.IsNullOrEmpty(path))
        return true;

    LoadTags();

    if (tags.Data == null)
        return false;

    if (tags.Data.Items == null)
        return false;

    var itemFromTag = from tag in tags.Data.Items
        where tag.Name.Equals(path, StringComparison.CurrentCultureIgnoreCase)
        select tag;

    return itemFromTag.Any();   
}

At this point, if you ran the project and started navigating around, you would be able to navigate into first level folders that were valid tags that came back from the API.

Now that I have that working, I am going to modify the GetChildItems call to return questions for the tag folder you are in. To do this, I am going to simply check the path that is sent to the call and if it is non-empty, then query the questions API:

protected override void GetChildItems(string path, bool recurse)
{
    if (string.IsNullOrEmpty(path))
    {
        // Write the tags
        LoadTags();

        foreach (var tag in tags.Data.Items)
        {
            WriteItemObject(tag, tag.Name, false);
        }
    }
    else
    {
        // Write the questions for this tag
        var stackOverflow = new StacManClient();
        var questions = stackOverflow.Questions.GetAll("stackoverflow.com", tagged:path);

        foreach (var q in questions.Result.Data.Items)
        {
            WriteItemObject(q, q.Title, false);
        }
    }
}

At this point questions are returned, but again the output is hideous. Similarly what we did for tags in the last post, we will do for questions. We are going to add a custom view:

<View>
  <Name>QuestionView</Name>
  <ViewSelectedBy>
    <TypeName>StackExchange.StacMan.Question</TypeName>
  </ViewSelectedBy>
  <TableControl>
    <TableHeaders>
      <TableColumnHeader>
        <Label>Title</Label>
        <Width>70</Width>
        <Alignment>left</Alignment>
      </TableColumnHeader>
      <TableColumnHeader>
        <Label>Creator</Label>
        <Width>20</Width>
        <Alignment>left</Alignment>
      </TableColumnHeader>
      <TableColumnHeader>
        <Label>Reputation</Label>
        <Width>10</Width>
        <Alignment>Right</Alignment>
      </TableColumnHeader>
    </TableHeaders>
    <TableRowEntries>
      <TableRowEntry>
        <TableColumnItems>
          <TableColumnItem>
            <PropertyName>Title</PropertyName>
          </TableColumnItem>
          <TableColumnItem>
            <ScriptBlock>$_.Owner.DisplayName</ScriptBlock>
          </TableColumnItem>
          <TableColumnItem>
            <ScriptBlock>$_.Owner.Reputation</ScriptBlock>
          </TableColumnItem>
        </TableColumnItems>
      </TableRowEntry>
    </TableRowEntries>
  </TableControl>
</View>

One interesting thing to call out in this view is that we are using the ScriptBlock element to access nested properties (Owner Name and Reputation) on the question object. So now, if you dir your directory, you will see this:

Title                                         Creator              Reputation
-----                                         -------              ----------
C# Clearing a Table                           user3594691                   1
To list and without tolist                    myfinite                     27
EntitySpaces/C#: How to use a subquery in ... brad                         11
SQL query search for all the data by month... user3337393                   1
Unity control object via socket Udp c#        iboy15                        6
multicolumn combo box in asp.net              nomi ikon                     6
C# How can I start PhantomJS + Selenium wi... Tiago Castro                  1
Override Json Serialization RavenDB           Shea                        101
How to get &quot;Windows Phone Pivot Page&... n179911                    3017
How to write binary data to serial port in C# Stefan Vogel                  1
Can&#39;t get the last row of excel when u... Wen Ling Lo                   1
Position Window before created using shell... Mark Robbins               1113

Since we are in PowerShell, we get a lot of querying functionality for free, so you can try things like:

#questions from people with rep over 100
dir so:\c# | where {$_.Owner.Reputation -gt 100}

#questions with an answer
dir so:\xml | where {$_.AnswerCount -gt 0}

#questions that have a positive score
dir | where {$_.Score -gt 0}    

As you can see combining PowerShell semantics with data from an API can be an interesting way for your users to access your data. In our next post, we will look at tab completion, so we can behave similarly to other drives within a PowerShell environment.