packages feed

notmuch-web-0.1.0: src/Handler/Tags.hs

{-
Copyright (C) 2013 John Lenz <lenz@math.uic.edu>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
-}
module Handler.Tags(
    tagHeader
  , tagWidget
  , postRetagThreadR
  , postRetagMessageR
) where

import Import
import Settings
import qualified Data.Text as T

import NotmuchCmd

displayTag :: T.Text -> GWidget s App ()
displayTag "unread" = [whamlet|<span .label .label-important .label-tag>unread|]
displayTag t = [whamlet|<span .label .label-info .label-tag>#{t}|]

-- | A widget consisting of the retag buttons followed by the current tag list.
tagWidget :: Either SearchResult Message -> GWidget s App ()
tagWidget x = do
  let url = case x of
              Left s -> RetagThreadR (searchThread s)
              Right m -> RetagMessageR (messageId m)

  let tags = either searchTags messageTags x

  retags <- extraRetag <$> lift getExtra
  [whamlet|
<span .tags>
    <div .btn-group>
        $forall r <- retags
            <button .btn .retag-button .btn-link title="#{retagName r}" data-notmuch-url="@{url (retagName r)}">
                <i class="#{retagIcon r}">
    $forall tag <- tags
        ^{displayTag tag}
|]

-- | When posting to retag routes, the thread and retag information
-- is avaialble in the route but we also want a CSRF token to protect
-- against CSRF attacks.  Therefore the body of the post contains
-- an empty form (internally Yesod includes the CSRF token for us).
retagForm :: Form ()
retagForm = renderDivs $ pure ()

-- | A widget that contains the support elements for retagging, including the forms
-- and javascript.  This widget must be included only ONCE in the page.  A notmuch:retag
-- event is raised after a successful retag.
tagHeader :: Widget
tagHeader = do
    (widget,enctype) <- lift $ generateFormPost retagForm
    [whamlet|
<form #retag-csrf-form enctype=#{enctype} style="display:none">
    ^{widget}
|]

    toWidget [julius|
$(document).on("click", ".retag-button", function() {
  var tagspan = $(this).parents("span.tags");
  var retagform = $("#retag-csrf-form");

  $.ajax({
      type: 'POST',
      data: retagform.serialize(),
      dataType: 'json',
      url: $(this).data('notmuch-url')
  })
  .done(function(data) {
      tagspan.children("span").each(function(idx, elem) {
          if ($.inArray(elem.textContent, data.remove) >= 0) {
              $(elem).remove();
          }
          var i = $.inArray(elem.textContent, data.add);
          if (i >= 0) {
            data.add.splice(i, 1);
          }
      });
      $.each(data.add, function(idx, tag) {
          tagspan.append(" <span class='label label-info label-tag'>" + tag + "</span> ");
      });
      tagspan.trigger("notmuch:retag", data);
  })
  .fail(function(xhr, err, e) {
      alert("Error " + err + " " + e);
  });
});
|]

postRetagThreadR :: ThreadID -> T.Text -> Handler RepJson
postRetagThreadR t name = do
    ((result,_),_) <- runFormPost retagForm
    case result of
        FormMissing -> invalidArgs ["form is missing"]
        FormFailure msg -> invalidArgs msg
        FormSuccess _ -> return ()

    retags <- extraRetag <$> getExtra
    let retag = filter (\r -> retagName r == name) retags
    case retag of
        [] -> notFound
        (r:_) -> do notmuchTagThread (retagAdd r) (retagRemove r) t
                    jsonToRepJson r

postRetagMessageR :: MessageID -> T.Text -> Handler RepJson
postRetagMessageR m name = do
    ((result,_),_) <- runFormPost retagForm
    case result of
        FormMissing -> invalidArgs ["form is missing"]
        FormFailure msg -> invalidArgs msg
        FormSuccess _ -> return ()

    retags <- extraRetag <$> getExtra
    let retag = filter (\r -> retagName r == name) retags
    case retag of
        [] -> notFound
        (r:_) -> do notmuchTagMessage (retagAdd r) (retagRemove r) m
                    jsonToRepJson r