Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Watch tree 842340 #3602

Merged
merged 19 commits into from
Nov 12, 2015
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion kuma/static/js/wiki.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@
var $link = $(this);
if($link.hasClass('disabled')) return;

mdn.analytics.trackEvent({
category: 'Page Watch',
action: $link.text().trim()
});

var $form = $link.closest('form');

var notification = mdn.Notifier.growl($link.data('subscribe-status'), { duration: 0 });
Expand All @@ -149,7 +154,6 @@
}).done(function(data) {

var message;
data = JSON.parse(data);
if(Number(data.status) === 1) {
$link.text($link.data('unsubscribe-text'));
message = $link.data('subscribe-message');
Expand Down
19 changes: 18 additions & 1 deletion kuma/wiki/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from kuma.core.email_utils import emails_with_users_and_watches
from kuma.core.helpers import add_utm
from kuma.core.urlresolvers import reverse
from tidings.events import InstanceEvent
from tidings.events import InstanceEvent, EventUnion

from .helpers import revisions_unified_diff, get_compare_url
from .models import Document
Expand Down Expand Up @@ -78,3 +78,20 @@ def _mails(self, users_and_watches):
context_vars=context,
users_and_watches=users_and_watches,
default_locale=document.locale)

def fire(self, **kwargs):
parent_events = [EditDocumentInTreeEvent(doc) for doc in
self.revision.document.get_topic_parents()]
return EventUnion(self,
EditDocumentInTreeEvent(self.revision.document),
*parent_events).fire(**kwargs)


class EditDocumentInTreeEvent(InstanceEvent):
"""
Event class for subscribing to all document edits to and under a document

Note: Do not call this class's .fire() method directly.
"""
event_type = 'wiki edit document in tree'
content_type = Document
12 changes: 12 additions & 0 deletions kuma/wiki/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,18 @@ def is_watched_by(self, user):
from .events import EditDocumentEvent
return EditDocumentEvent.is_notifying(user, self)

def tree_is_watched_by(self, user):
"""Return whether `user` is notified of edits to me AND sub-pages."""
from .events import EditDocumentInTreeEvent
return EditDocumentInTreeEvent.is_notifying(user, self)

def parent_trees_watched_by(self, user):
"""
Return any and all of this document's parents that are watched by the
given user.
"""
return [doc for doc in self.parents if doc.tree_is_watched_by(user)]

def get_document_type(self):
return WikiDocumentType

Expand Down
12 changes: 11 additions & 1 deletion kuma/wiki/templates/wiki/email/edited.ltxt
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@ Edit Article:
Article History:
{% endtrans %}
{{ history_url|absolutify }}
{% if watch %}{{ unsubscribe_text(watch) }}{% endif %}{% endautoescape %}
--
{% if watch %}
{% set title = watch.content_object.title %}
{% if watch.event_type == 'wiki edit document in tree' %}
{{ _('You are subscribed to edits on: {title} and all its sub-articles.')|f(title=title) }}
{% else %}
{{ _('You are subscribed to edits on: {title}.')|f(title=title) }}
{% endif %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an email, do you want to use jinja2 whitespace eating tags? Or do you want those strings indented?

{{ unsubscribe_text(watch) }}
{% endif %}
{% endautoescape %}
45 changes: 24 additions & 21 deletions kuma/wiki/templates/wiki/includes/buttons.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{% from "wiki/includes/document_macros.html" import watch_menu_items with context%}
{% macro get_document_buttons(document, edit_link) %}

{% if document.parent %}
Expand All @@ -11,8 +12,11 @@
{% set edit_link = document.get_edit_url() %}
{% endif %}

{% set watch_menu_items_html = watch_menu_items(document, user) %}

<ul class="page-buttons" data-sticky="false">{% if not document.is_template %}<li><button id="languages-menu" class="transparent" aria-haspopup="true" aria-owns="languages-menu-submenu" aria-expanded="false"><span>{{ _('Languages') }}</span><i aria-hidden="true" class="icon-globe"></i></button>
<ul class="page-buttons" data-sticky="false">
{% if not document.is_template %}
<li><button id="languages-menu" class="transparent" aria-haspopup="true" aria-owns="languages-menu-submenu" aria-expanded="false"><span>{{ _('Languages') }}</span><i aria-hidden="true" class="icon-globe"></i></button>

<div class="submenu js-submenu" id="languages-menu-submenu">
<div class="submenu-column">
Expand All @@ -30,34 +34,33 @@
{% endif %}
</ul>
</div>
</div></li>{% endif %}<li class="page-buttons-edit"><a href="{{ edit_link }}" class="button" data-optimizely-hook="button-edit-doc" id="edit-button">{{ _('Edit') }}<i aria-hidden="true" class="icon-pencil"></i></a></li><li><button id="advanced-menu" class="only-icon" aria-haspopup="true" aria-owns="advanced-menu-submenu" aria-expanded="false"><span>{{ _('Advanced') }}</span><i aria-hidden="true" class="icon-cog"></i></button>
</div>
</li>{% endif %}

<li class="page-buttons-edit"><a href="{{ edit_link }}" class="button" data-optimizely-hook="button-edit-doc" id="edit-button">{{ _('Edit') }}<i aria-hidden="true" class="icon-pencil"></i></a></li>

{% if user.is_authenticated() and document and waffle.flag('watch_menu')%}
<li><button id="watch-menu" class="only-icon" aria-haspopup="true" aria-owns="watch-menu-submenu" aria-expanded="false"><span>{{ _('Watch') }}</span><i aria-hidden="true" class="icon-eye"></i></button>
<div class="submenu js-submenu" id="watch-menu-submenu">
<div class="submenu-column">
<div class="title">{{ _('Watch') }}</div>
{{ watch_menu_items_html }}
</div>
</div>
</li>
{% endif %}

<li><button id="advanced-menu" class="only-icon" aria-haspopup="true" aria-owns="advanced-menu-submenu" aria-expanded="false"><span>{{ _('Advanced') }}</span><i aria-hidden="true" class="icon-cog"></i></button>
<div class="submenu js-submenu" id="advanced-menu-submenu">
<!-- this page -->
<div class="submenu-column">
<div class="title">{{ _('Advanced') }}</div>
{% if user.is_authenticated() and document and not waffle.flag('watch_menu')%}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space before %}

{{ watch_menu_items_html }}
{% endif %}
<ul>
<li><a href="{{ url('wiki.document_revisions', document.slug) }}" rel="nofollow, noindex">{{_('History')}}</a></li>
{% if user.is_authenticated() and document %}
<li class="page-watch">
{% set subscribe_status = _('Updating subscription status') %}
<form action="{{ url('wiki.subscribe', document.slug, locale=document.locale) }}" method="post">
{{ csrf() }}
<a href="#" data-subscribe-status="{{ subscribe_status }}" data-subscribe-text="{{ _('Subscribe to this article') }}" data-unsubscribe-text="{{ _('Unsubscribe from this article') }}" data-subscribe-message="{{ _('You are now subscribed to this article.') }}" data-unsubscribe-message="{{ _('You have been unsubscribed from this article.') }}">
{% if document.is_watched_by(user) %}{{ _('Unsubscribe from this article') }}{% else %}{{ _('Subscribe to this article') }}{% endif %}
</a>
</form>

{% if document.parent %}
{% set parent_language = document.parent.language %}
<form action="{{ url('wiki.subscribe', document.parent.slug, locale=document.parent.locale) }}" method="post">
{{ csrf() }}
<a href="#" data-subscribe-status="{{ subscribe_status }}" data-subscribe-text="{{ _('Subscribe to {language} version')|f(language=parent_language) }}" data-unsubscribe-text="{{ _('Unsubscribe from {language} version')|f(language=parent_language) }}" data-subscribe-message="{{ _('You are now subscribed to the {language} version of this article.')|f(language=parent_language) }}" data-unsubscribe-message="{{ _('You have been unsubscribed from the {language} version of this article.')|f(language=parent_language) }}">
{% if document.parent.is_watched_by(user) %}{{ _('Unsubscribe from {language} version')|f(language=parent_language) }}{% else %}{{ _('Subscribe to {language} version')|f(language=parent_language) }}{% endif %}
</a>
</form>
{% endif %}

</li>{% endif %}
{% if user.is_authenticated() and not document.is_template %}
<li><a href="{{ url('wiki.create') }}?parent={{ document.id }}" rel="nofollow, noindex">{{ _('New sub-article') }}</a></li>
Expand Down
41 changes: 41 additions & 0 deletions kuma/wiki/templates/wiki/includes/document_macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,44 @@ <h3>
<a href="{{ url('users.user_detail', username=contributor.username) }}">{{ contributor.username }}</a>{% if not loop.last %}, {% endif %}
{% endfor %}
{%- endmacro %}

{% macro watch_menu_items(document, user) -%}
<ul>
<li class="page-watch">
{% set subscribe_status = _('Updating subscription status') %}
{% set subscribe_text = _('Subscribe to this article') %}
{% set unsubscribe_text = _('Unsubscribe from this article') %}
<form action="{{ url('wiki.subscribe', document.slug, locale=document.locale) }}" method="post">
{{ csrf() }}
<a href="#" data-subscribe-status="{{ subscribe_status }}" data-subscribe-text="{{ subscribe_text }}" data-unsubscribe-text="{{ unsubscribe_text }}" data-subscribe-message="{{ _('You are now subscribed to this article.') }}" data-unsubscribe-message="{{ _('You have been unsubscribed from this article.') }}">
{% if document.is_watched_by(user) %}{{ unsubscribe_text }}{% else %}{{ subscribe_text }}{% endif %}
</a>
</form>
</li>

<li class="page-watch">
{% set subscribe_tree_text = _('Subscribe to this article and all its sub-articles') %}
{% set unsubscribe_tree_text = _('Unsubscribe from this article and all its sub-articles') %}
<form action="{{ url('wiki.subscribe_to_tree', document.slug, locale=document.locale) }}" method="post">
{{ csrf() }}
<a href="#" data-subscribe-status="{{ subscribe_status }}" data-subscribe-text="{{ subscribe_tree_text }}" data-unsubscribe-text="{{ unsubscribe_tree_text }}" data-subscribe-message="{{ _('You are now subscribed to this article and all its sub-articles.') }}" data-unsubscribe-message="{{ _('You have been unsubscribed from this article and all its sub-articles.') }}">
{% if document.tree_is_watched_by(user) %}{{ unsubscribe_tree_text }}{% else %}{{ subscribe_tree_text }}{% endif %}
</a>
</form>
</li>

{% if document.parent %}
<li class="page-watch">
{% set parent_language = document.parent.language %}
{% set subscribe_parent_text = _('Subscribe to <bdi>{language}</bdi> version')|f(language=parent_language) %}
{% set unsubscribe_parent_text = _('Unsubscribe from <bdi>{language}</bdi> version')|f(language=parent_language) %}
<form action="{{ url('wiki.subscribe', document.parent.slug, locale=document.parent.locale) }}" method="post">
{{ csrf() }}
<a href="#" data-subscribe-status="{{ subscribe_status }}" data-subscribe-text="{{ subscribe_parent_text }}" data-unsubscribe-text="{{ unsubscribe_parent_text }}" data-subscribe-message="{{ _('You are now subscribed to the {language} version of this article.')|f(language=parent_language) }}" data-unsubscribe-message="{{ _('You have been unsubscribed from the {language} version of this article.')|f(language=parent_language) }}">
{% if document.parent.is_watched_by(user) %}{{ unsubscribe_parent_text|safe }}{% else %}{{ subscribe_parent_text|safe }}{% endif %}
</a>
</form>
</li>
{% endif %}
</ul>
{%- endmacro %}
17 changes: 17 additions & 0 deletions kuma/wiki/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,20 @@ def create_topical_parents_docs():
d2.parent_topic = d1
d2.save()
return d1, d2


def create_test_document_tree():
root_doc = document(title="Root", slug="Root", save=True)
revision(document=root_doc, title="Root", slug="Root", save=True)
child_doc = document(title="Child", slug="Child", save=True)
child_doc.parent_topic = root_doc
child_doc.save()
revision(document=child_doc, title="Child", slug="Child", save=True)
grandchild_doc = document(title="Grandchild", slug="Grandchild",
save=True)
grandchild_doc.parent_topic = child_doc
grandchild_doc.save()
revision(document=grandchild_doc, title="Grandchild",
slug="Grandchild", save=True)

return root_doc, child_doc, grandchild_doc
14 changes: 13 additions & 1 deletion kuma/wiki/tests/test_events.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import mock
from nose.tools import eq_

from kuma.core.tests import get_user
from kuma.users.tests import UserTestCase
from . import WikiTestCase, revision
from ..events import context_dict
from ..events import context_dict, EditDocumentEvent


class NotificationEmailTests(UserTestCase, WikiTestCase):
Expand All @@ -14,3 +16,13 @@ def test_context_dict_no_previous_revision(self):
except AttributeError:
self.fail("Should not throw AttributeError")
eq_(cd, cd)

@mock.patch('tidings.events.EventUnion.fire')
def test_edit_document_event_fires_union(self, mock_union_fire):
rev = revision(save=True)
testuser2 = get_user(username='testuser2')
EditDocumentEvent.notify(testuser2, rev.document)

EditDocumentEvent(rev).fire()

assert mock_union_fire.called
24 changes: 21 additions & 3 deletions kuma/wiki/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
from django.test.utils import override_settings

from kuma.core.exceptions import ProgrammingError
from kuma.core.tests import KumaTestCase
from kuma.core.tests import KumaTestCase, get_user
from kuma.users.tests import UserTestCase

from . import (create_template_test_users, create_topical_parents_docs,
doc_rev, document, normalize_html, revision)
from . import (create_template_test_users, create_test_document_tree,
create_topical_parents_docs, doc_rev, document, normalize_html,
revision)
from .. import tasks
from ..constants import REDIRECT_CONTENT, TEMPLATE_TITLE_PREFIX
from ..events import EditDocumentInTreeEvent
from ..exceptions import (DocumentRenderedContentNotAvailable,
DocumentRenderingInProgress, PageMoveError)
from ..helpers import absolutify
Expand Down Expand Up @@ -532,6 +534,22 @@ def test_code_sample_extraction(self):
eq_(sample_css.strip(), result['css'].strip())
eq_(sample_js.strip(), result['js'].strip())

def test_tree_is_watched_by(self):
rev = revision()
testuser2 = get_user(username='testuser2')
EditDocumentInTreeEvent.notify(testuser2, rev.document)

assert rev.document.tree_is_watched_by(testuser2)

def test_parent_trees_watched_by(self):
root_doc, child_doc, grandchild_doc = create_test_document_tree()
testuser2 = get_user(username='testuser2')

EditDocumentInTreeEvent.notify(testuser2, root_doc)
EditDocumentInTreeEvent.notify(testuser2, child_doc)

assert 2 == len(grandchild_doc.parent_trees_watched_by(testuser2))


class TaggedDocumentTests(UserTestCase):
"""Tests for tags in Documents and Revisions"""
Expand Down
Loading