Merge branch 'origin/master'
Conflicts: lib/text_sentinel.rb
This commit is contained in:
commit
eb2a5e4654
|
@ -68,3 +68,6 @@ chef/tmp/*
|
||||||
|
|
||||||
# .procfile
|
# .procfile
|
||||||
.procfile
|
.procfile
|
||||||
|
|
||||||
|
# exclude our git version file for now
|
||||||
|
config/version.rb
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -47,7 +47,7 @@ gem 'slim' # required for sidekiq-web
|
||||||
gem 'therubyracer', require: 'v8'
|
gem 'therubyracer', require: 'v8'
|
||||||
gem 'thin'
|
gem 'thin'
|
||||||
|
|
||||||
# Gem that enables support for plugins. It is required
|
# Gem that enables support for plugins. It is required.
|
||||||
gem 'discourse_plugin', path: 'vendor/gems/discourse_plugin'
|
gem 'discourse_plugin', path: 'vendor/gems/discourse_plugin'
|
||||||
|
|
||||||
# Discourse Plugins (optional)
|
# Discourse Plugins (optional)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th class='user'></th>
|
<th class='user'></th>
|
||||||
<th class='excerpt'></th>
|
<th class='excerpt'></th>
|
||||||
<th class='flaggers'>Flag by</th>
|
<th class='flaggers'>{{i18n admin.flags.flagged_by}}</th>
|
||||||
<th class='last-flagged'></th>
|
<th class='last-flagged'></th>
|
||||||
<th class='action'></th>
|
<th class='action'></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -42,6 +42,7 @@ Discourse.PreferencesController = Ember.ObjectController.extend Discourse.Presen
|
||||||
|
|
||||||
save: ->
|
save: ->
|
||||||
@set('saving', true)
|
@set('saving', true)
|
||||||
|
@set('saved', false)
|
||||||
|
|
||||||
# Cook the bio for preview
|
# Cook the bio for preview
|
||||||
@get('content').save (result) =>
|
@get('content').save (result) =>
|
||||||
|
|
|
@ -72,8 +72,7 @@ Handlebars.registerHelper 'avatar', (user, options) ->
|
||||||
|
|
||||||
Handlebars.registerHelper 'unboundDate', (property, options) ->
|
Handlebars.registerHelper 'unboundDate', (property, options) ->
|
||||||
dt = new Date(Ember.Handlebars.get(this, property, options))
|
dt = new Date(Ember.Handlebars.get(this, property, options))
|
||||||
month = Date.SugarMethods.getLocale.method().months[12 + dt.getMonth()]
|
dt.format("{d} {Mon}, {yyyy} {hh}:{mm}")
|
||||||
"#{dt.getDate()} #{month}, #{dt.getFullYear()} #{dt.getHours()}:#{dt.getMinutes()}"
|
|
||||||
|
|
||||||
Handlebars.registerHelper 'editDate', (property, options) ->
|
Handlebars.registerHelper 'editDate', (property, options) ->
|
||||||
dt = Date.create(Ember.Handlebars.get(this, property, options))
|
dt = Date.create(Ember.Handlebars.get(this, property, options))
|
||||||
|
|
|
@ -7,23 +7,29 @@ Discourse.Onebox = (->
|
||||||
|
|
||||||
cache = (url, contents) ->
|
cache = (url, contents) ->
|
||||||
localCache[url] = contents
|
localCache[url] = contents
|
||||||
|
|
||||||
#if localStorage && localStorage.setItem
|
|
||||||
# localStorage.setItme
|
|
||||||
null
|
null
|
||||||
|
|
||||||
lookupCache = (url) ->
|
lookupCache = (url) ->
|
||||||
localCache[url]
|
cached = localCache[url]
|
||||||
|
if cached && cached.then # its a promise
|
||||||
|
null
|
||||||
|
else
|
||||||
|
cached
|
||||||
|
|
||||||
lookup = (url, refresh, callback) ->
|
lookup = (url, refresh, callback) ->
|
||||||
cached = lookupCache(url) unless refresh
|
cached = localCache[url]
|
||||||
|
cached = null if refresh && cached && !cached.then
|
||||||
if cached
|
if cached
|
||||||
callback(cached)
|
if cached.then
|
||||||
|
cached.then(callback(lookupCache(url)))
|
||||||
|
else
|
||||||
|
callback(cached)
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
$.get "/onebox", url: url, refresh: refresh, (html) ->
|
cache(url, $.get "/onebox", url: url, refresh: refresh, (html) ->
|
||||||
cache(url,html)
|
cache(url,html)
|
||||||
callback(html)
|
callback(html)
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
load = (e, refresh=false) ->
|
load = (e, refresh=false) ->
|
||||||
|
|
|
@ -9,6 +9,7 @@ window.Discourse.TopicList = Discourse.Model.extend
|
||||||
Em.String.i18n('topics.no_' + @get('filter'))
|
Em.String.i18n('topics.no_' + @get('filter'))
|
||||||
).property('topics', 'topics@each', 'filter', 'loaded')
|
).property('topics', 'topics@each', 'filter', 'loaded')
|
||||||
|
|
||||||
|
|
||||||
loadMoreTopics: ->
|
loadMoreTopics: ->
|
||||||
promise = new RSVP.Promise()
|
promise = new RSVP.Promise()
|
||||||
if moreUrl = @get('more_topics_url')
|
if moreUrl = @get('more_topics_url')
|
||||||
|
|
|
@ -2,10 +2,19 @@ window.Discourse.UserAction = Discourse.Model.extend
|
||||||
postUrl:(->
|
postUrl:(->
|
||||||
Discourse.Utilities.postUrl(@get('slug'), @get('topic_id'), @get('post_number'))
|
Discourse.Utilities.postUrl(@get('slug'), @get('topic_id'), @get('post_number'))
|
||||||
).property()
|
).property()
|
||||||
|
|
||||||
|
replyUrl: (->
|
||||||
|
Discourse.Utilities.postUrl(@get('slug'), @get('topic_id'), @get('reply_to_post_number'))
|
||||||
|
).property()
|
||||||
|
|
||||||
isPM: (->
|
isPM: (->
|
||||||
a = @get('action_type')
|
a = @get('action_type')
|
||||||
a == UserAction.NEW_PRIVATE_MESSAGE || UserAction.GOT_PRIVATE_MESSAGE
|
a == Discourse.UserAction.NEW_PRIVATE_MESSAGE || a == Discourse.UserAction.GOT_PRIVATE_MESSAGE
|
||||||
|
).property()
|
||||||
|
|
||||||
|
isPostAction: (->
|
||||||
|
a = @get('action_type')
|
||||||
|
a == Discourse.UserAction.RESPONSE || a == Discourse.UserAction.POST || a == Discourse.UserAction.NEW_TOPIC
|
||||||
).property()
|
).property()
|
||||||
|
|
||||||
addChild: (action)->
|
addChild: (action)->
|
||||||
|
|
|
@ -5,6 +5,7 @@ window.Discourse.FilteredListRoute = Discourse.Route.extend
|
||||||
listController = @controllerFor('list')
|
listController = @controllerFor('list')
|
||||||
listController.set('canCreateTopic', false)
|
listController.set('canCreateTopic', false)
|
||||||
listController.set('filterMode', '')
|
listController.set('filterMode', '')
|
||||||
|
|
||||||
renderTemplate: ->
|
renderTemplate: ->
|
||||||
@render 'listTopics', into: 'list', outlet: 'listView', controller: 'listTopics'
|
@render 'listTopics', into: 'list', outlet: 'listView', controller: 'listTopics'
|
||||||
setupController: ->
|
setupController: ->
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='control'>
|
<div class='control'>
|
||||||
<a href='#' class='toggler' {{action toggle bubbles=false}} title='toggle'></a>
|
<a href='#' class='toggler' {{action toggle bubbles=false}} title='toggle'></a>
|
||||||
|
|
||||||
{{#if content.viewOpen}}
|
{{#if content.viewOpen}}
|
||||||
<div class='control-row reply-area'>
|
<div class='control-row reply-area'>
|
||||||
<div class='reply-to'>{{{content.actionTitle}}}:</div>
|
<div class='reply-to'>{{{content.actionTitle}}}:</div>
|
||||||
|
|
||||||
{{#if content.editTitle}}
|
{{#if content.editTitle}}
|
||||||
<div class='form-element clearfix'>
|
<div class='form-element clearfix'>
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<button class='btn' {{action showOptions target="controller"}}>{{i18n topic.options}}</button>
|
<button class='btn' {{action showOptions target="controller"}}>{{i18n topic.options}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
<div id='wmd-preview' {{bindAttr class="controller.hidePreview:hidden"}}></div>
|
<div id='wmd-preview' {{bindAttr class="controller.hidePreview:hidden"}}></div>
|
||||||
</div>
|
</div>
|
||||||
{{#if Discourse.currentUser}}
|
{{#if Discourse.currentUser}}
|
||||||
<a href="#" {{action togglePreview target="controller"}} class='toggle-preview'>{{{content.toggleText}}}</a>
|
<a href="#" {{action togglePreview target="controller"}} class='toggle-preview'>{{{content.toggleText}}}</a>
|
||||||
<div class='saving-draft'></div>
|
<div class='saving-draft'></div>
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
|
|
||||||
{{#if view.loadingImage}}
|
{{#if view.loadingImage}}
|
||||||
<div id="image-uploading">
|
<div id="image-uploading">
|
||||||
Uploading image {{view.uploadProgress}}% <a {{action cancelUpload target="view"}}>cancel</a>
|
{{i18n image_selector.uploading_image}} {{view.uploadProgress}}% <a {{action cancelUpload target="view"}}>{{i18n cancel}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
{{i18n composer.saved}} <a class='permalink' href="{{unbound createdPost.url}}" {{action viewNewReply target="controller"}}>{{i18n composer.view_new_post}}</a>
|
{{i18n composer.saved}} <a class='permalink' href="{{unbound createdPost.url}}" {{action viewNewReply target="controller"}}>{{i18n composer.view_new_post}}</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{i18n composer.saving}}
|
{{i18n composer.saving}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class='draft-text'>
|
<div class='draft-text'>
|
||||||
{{i18n composer.saved_draft}}
|
{{i18n composer.saved_draft}}
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class='span11 topic-body'>
|
<div class='span11 topic-body'>
|
||||||
{{#if view.previousPost}}<a href='{{unbound url}}' class="arrow"><i class='icon icon-arrow-up'></i></a>{{/if}}
|
{{#if view.previousPost}}<a href='{{unbound url}}' class="arrow" title="jump to earlier reply"><i class='icon icon-arrow-up'></i></a>{{/if}}
|
||||||
{{{unbound cooked}}}
|
{{{unbound cooked}}}
|
||||||
{{#unless view.previousPost}}<a href='{{unbound url}}' class="arrow"><i class='icon icon-arrow-down'></i></a>{{/unless}}
|
{{#unless view.previousPost}}<a href='{{unbound url}}' class="arrow" title="jump to later reply"><i class='icon icon-arrow-down'></i></a>{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/with}}
|
{{/with}}
|
||||||
|
|
|
@ -55,5 +55,9 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
{{#if view.allLoaded}}
|
||||||
|
<h3>{{{i18n topics.footer}}}</h3>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
|
@ -5,9 +5,31 @@
|
||||||
<a href="/users/{{unbound username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="large" extraClasses="actor" avatarTemplatePath="avatar_template" ignoreTitle="true"}}</div></a>
|
<a href="/users/{{unbound username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="large" extraClasses="actor" avatarTemplatePath="avatar_template" ignoreTitle="true"}}</div></a>
|
||||||
<span class='time'>{{date path="created_at" leaveAgo="true"}}</span>
|
<span class='time'>{{date path="created_at" leaveAgo="true"}}</span>
|
||||||
<a class="title" href="{{unbound postUrl}}">{{unbound title}}</a><br>
|
<a class="title" href="{{unbound postUrl}}">{{unbound title}}</a><br>
|
||||||
|
{{#unless description}}
|
||||||
|
<span class="type">
|
||||||
|
{{#if isPM}}
|
||||||
|
<i class="icon icon-lock" title="{{i18n user.stream.private_message}}"></i>
|
||||||
|
{{i18n user.stream.sent_by}}
|
||||||
|
{{else}}
|
||||||
|
{{i18n user.stream.posted_by}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
{{/unless}}
|
||||||
<a class='name' href="/users/{{unbound username}}">{{personalizedName name usernamePath="username"}}</a>
|
<a class='name' href="/users/{{unbound username}}">{{personalizedName name usernamePath="username"}}</a>
|
||||||
<span class='type'>{{unbound description}}</span>
|
{{#if description}}
|
||||||
<a class="post-number" href="{{unbound postUrl}}">#{{unbound post_number}}</a>
|
<span class='type'>{{unbound description}}</span>
|
||||||
|
{{#if isPostAction}}
|
||||||
|
<a class="post-number" href="{{unbound replyUrl}}">
|
||||||
|
{{#if reply_to_post_number}}
|
||||||
|
#{{unbound reply_to_post_number}}
|
||||||
|
{{else}}
|
||||||
|
{{i18n user.stream.the_topic}}
|
||||||
|
{{/if}}
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<span class="name">{{personalizedName view.parentView.parentView.user.name usernamePath="view.parentView.parentView.user.username"}}</span>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<p class='excerpt'>
|
<p class='excerpt'>
|
||||||
{{{unbound excerpt}}}
|
{{{unbound excerpt}}}
|
||||||
|
|
|
@ -18,6 +18,10 @@ window.Discourse.ListTopicsView = Ember.View.extend Discourse.Scrolling, Discour
|
||||||
|
|
||||||
willDestroyElement: -> @unbindScrolling()
|
willDestroyElement: -> @unbindScrolling()
|
||||||
|
|
||||||
|
allLoaded: (->
|
||||||
|
!@get('loading') && !@get('controller.content.more_topics_url')
|
||||||
|
).property('loading', 'controller.content.more_topics_url')
|
||||||
|
|
||||||
didInsertElement: ->
|
didInsertElement: ->
|
||||||
@bindScrolling()
|
@bindScrolling()
|
||||||
eyeline = new Discourse.Eyeline('.topic-list-item')
|
eyeline = new Discourse.Eyeline('.topic-list-item')
|
||||||
|
|
|
@ -92,8 +92,8 @@ window.Discourse.SearchView = Ember.View.extend Discourse.Presence,
|
||||||
count
|
count
|
||||||
).property('content')
|
).property('content')
|
||||||
|
|
||||||
moreOfType: (e) ->
|
moreOfType: (type) ->
|
||||||
@set('typeFilter', e.context)
|
@set('typeFilter', type)
|
||||||
false
|
false
|
||||||
|
|
||||||
cancelType: ->
|
cancelType: ->
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lists
|
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
margin-left: 40px;
|
margin-left: 40px;
|
||||||
|
@ -44,9 +43,6 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content wrapper
|
|
||||||
// --------------------------------------------------
|
|
||||||
|
|
||||||
.body-page {
|
.body-page {
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
@ -54,44 +50,12 @@
|
||||||
width: 960px;
|
width: 960px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px 10px;
|
padding: 20px 10px;
|
||||||
|
margin-top: -60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
.nav-pills {
|
||||||
// --------------------------------------------------
|
margin-left:0px !important;
|
||||||
|
font: 13px/18px "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
.body-page {
|
|
||||||
|
|
||||||
nav {
|
|
||||||
width: 280px;
|
|
||||||
overflow: hidden;
|
|
||||||
position: fixed;
|
|
||||||
float: left;
|
|
||||||
border: 1px solid #b9b9b9;
|
|
||||||
background-color: #fafafa;
|
|
||||||
@include border-radius-all(4px);
|
|
||||||
@include box-shadow(0 1px 0 #fff);
|
|
||||||
> a {
|
|
||||||
display: block;
|
|
||||||
border-top: 1px solid #e6e6e6;
|
|
||||||
padding: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 20px;
|
|
||||||
text-shadow: 0 1px 0 rgba($white, 0.5);
|
|
||||||
&:first-child {
|
|
||||||
border-top: 0;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
&.active {
|
|
||||||
color: #f15b22;
|
|
||||||
background-color: #f9e7e0;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -431,6 +431,7 @@
|
||||||
a.arrow {
|
a.arrow {
|
||||||
float: right;
|
float: right;
|
||||||
margin: 3px 0 3px 0;
|
margin: 3px 0 3px 0;
|
||||||
|
color: grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,6 +258,9 @@
|
||||||
.type {
|
.type {
|
||||||
color: lighten($black, 40%);
|
color: lighten($black, 40%);
|
||||||
}
|
}
|
||||||
|
span.name {
|
||||||
|
color: lighten($black, 40%);
|
||||||
|
}
|
||||||
.time {
|
.time {
|
||||||
display: block;
|
display: block;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
class ExceptionsController < ApplicationController
|
class ExceptionsController < ApplicationController
|
||||||
skip_before_filter :check_xhr
|
skip_before_filter :check_xhr
|
||||||
skip_before_filter :check_restricted_access
|
|
||||||
layout 'no_js'
|
layout 'no_js'
|
||||||
|
|
||||||
def not_found
|
def not_found
|
||||||
|
|
|
@ -17,7 +17,6 @@ class Category < ActiveRecord::Base
|
||||||
after_save :invalidate_site_cache
|
after_save :invalidate_site_cache
|
||||||
after_destroy :invalidate_site_cache
|
after_destroy :invalidate_site_cache
|
||||||
|
|
||||||
|
|
||||||
def uncategorized_validator
|
def uncategorized_validator
|
||||||
return errors.add(:name, I18n.t(:is_reserved)) if name == SiteSetting.uncategorized_name
|
return errors.add(:name, I18n.t(:is_reserved)) if name == SiteSetting.uncategorized_name
|
||||||
return errors.add(:slug, I18n.t(:is_reserved)) if slug == SiteSetting.uncategorized_name
|
return errors.add(:slug, I18n.t(:is_reserved)) if slug == SiteSetting.uncategorized_name
|
||||||
|
@ -26,24 +25,22 @@ class Category < ActiveRecord::Base
|
||||||
def self.popular
|
def self.popular
|
||||||
order('topic_count desc')
|
order('topic_count desc')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Recalculates `topics_year`, `topics_month`, and `topics_week`
|
||||||
|
# for each Category.
|
||||||
def self.update_stats
|
def self.update_stats
|
||||||
exec_sql "UPDATE categories
|
topics = Topic
|
||||||
SET topics_week = (SELECT COUNT(*)
|
.select("COUNT(*)")
|
||||||
FROM topics as ft
|
.where("topics.category_id = categories.id")
|
||||||
WHERE ft.category_id = categories.id
|
.visible
|
||||||
AND ft.created_at > (CURRENT_TIMESTAMP - INTERVAL '1 WEEK')
|
|
||||||
AND ft.visible),
|
topics_year = topics.created_since(1.year.ago).to_sql
|
||||||
topics_month = (SELECT COUNT(*)
|
topics_month = topics.created_since(1.month.ago).to_sql
|
||||||
FROM topics as ft
|
topics_week = topics.created_since(1.week.ago).to_sql
|
||||||
WHERE ft.category_id = categories.id
|
|
||||||
AND ft.created_at > (CURRENT_TIMESTAMP - INTERVAL '1 MONTH')
|
Category.update_all("topics_year = (#{topics_year}),
|
||||||
AND ft.visible),
|
topics_month = (#{topics_month}),
|
||||||
topics_year = (SELECT COUNT(*)
|
topics_week = (#{topics_week})")
|
||||||
FROM topics as ft
|
|
||||||
WHERE ft.category_id = categories.id
|
|
||||||
AND ft.created_at > (CURRENT_TIMESTAMP - INTERVAL '1 YEAR')
|
|
||||||
AND ft.visible)"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use the first paragraph of the topic's first post as the excerpt
|
# Use the first paragraph of the topic's first post as the excerpt
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
class SiteCustomization < ActiveRecord::Base
|
class SiteCustomization < ActiveRecord::Base
|
||||||
|
|
||||||
ENABLED_KEY = '7e202ef2-56d7-47d5-98d8-a9c8d15e57dd'
|
ENABLED_KEY = '7e202ef2-56d7-47d5-98d8-a9c8d15e57dd'
|
||||||
CACHE_PATH = 'stylesheet-cache'
|
# placing this in uploads to ease deployment rules
|
||||||
|
CACHE_PATH = 'uploads/stylesheet-cache'
|
||||||
@lock = Mutex.new
|
@lock = Mutex.new
|
||||||
|
|
||||||
before_create do
|
before_create do
|
||||||
|
|
|
@ -167,7 +167,11 @@ class Topic < ActiveRecord::Base
|
||||||
def self.visible
|
def self.visible
|
||||||
where(visible: true)
|
where(visible: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.created_since(time_ago)
|
||||||
|
where("created_at > ?", time_ago)
|
||||||
|
end
|
||||||
|
|
||||||
def private_message?
|
def private_message?
|
||||||
self.archetype == Archetype.private_message
|
self.archetype == Archetype.private_message
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,12 +42,16 @@ class UserAction < ActiveRecord::Base
|
||||||
results = UserAction.select("action_type, COUNT(*) count, '' AS description")
|
results = UserAction.select("action_type, COUNT(*) count, '' AS description")
|
||||||
.joins(:target_topic)
|
.joins(:target_topic)
|
||||||
.where(user_id: user_id)
|
.where(user_id: user_id)
|
||||||
.group('action_type', 'topics.archetype')
|
.group('action_type')
|
||||||
|
|
||||||
# should push this into the sql at some point, but its simple enough for now
|
# We apply similar filters in stream, might consider trying to consolidate somehow
|
||||||
unless guardian.can_see_private_messages?(user_id)
|
unless guardian.can_see_private_messages?(user_id)
|
||||||
results = results.where('topics.archetype <> ?', Archetype::private_message)
|
results = results.where('topics.archetype <> ?', Archetype::private_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless guardian.user && guardian.user.id == user_id
|
||||||
|
results = results.where("action_type <> ?", BOOKMARK)
|
||||||
|
end
|
||||||
|
|
||||||
results = results.to_a
|
results = results.to_a
|
||||||
|
|
||||||
|
@ -79,6 +83,7 @@ class UserAction < ActiveRecord::Base
|
||||||
SELECT
|
SELECT
|
||||||
t.title, a.action_type, a.created_at, t.id topic_id,
|
t.title, a.action_type, a.created_at, t.id topic_id,
|
||||||
coalesce(p.post_number, 1) post_number,
|
coalesce(p.post_number, 1) post_number,
|
||||||
|
p.reply_to_post_number,
|
||||||
pu.email ,pu.username, pu.name, pu.id user_id,
|
pu.email ,pu.username, pu.name, pu.id user_id,
|
||||||
u.email acting_email, u.username acting_username, u.name acting_name, u.id acting_user_id,
|
u.email acting_email, u.username acting_username, u.name acting_name, u.id acting_user_id,
|
||||||
coalesce(p.cooked, p2.cooked) cooked
|
coalesce(p.cooked, p2.cooked) cooked
|
||||||
|
|
|
@ -58,5 +58,6 @@
|
||||||
<%= render :partial => "common/discourse_javascript" %>
|
<%= render :partial => "common/discourse_javascript" %>
|
||||||
<%= render_google_analytics_code %>
|
<%= render_google_analytics_code %>
|
||||||
|
|
||||||
|
<!-- Git Version: <%= Discourse.git_version %> -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,141 +1,133 @@
|
||||||
<div id="excerpt">
|
<ul class="nav-pills">
|
||||||
</div>
|
<li><a class="active" href="/faq">FAQ</a></li>
|
||||||
<!--<nav></nav> -->
|
<li><a href="/tos">Terms of Service</a></li>
|
||||||
<section id="questions">
|
<li><a href="/privacy">Privacy</a></li>
|
||||||
<article id="civilized">
|
</ul>
|
||||||
<h2>This is a Civilized Place for Public Discussion</h2>
|
|
||||||
<p>
|
|
||||||
Please treat this discussion forum with the same respect you would a public park. We, too, are a shared community resource — a place to share skills, knowledge and interests through ongoing conversation.
|
|
||||||
</p>
|
|
||||||
<div class="more">
|
|
||||||
<p>
|
|
||||||
Use these guidelines to keep this a clean, well-lighted place for civilized public discourse. These are not hard and fast rules, merely aids to the human judgment of moderators and the overall community.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<article id="improve">
|
|
||||||
<h2>Improve the Discussion</h2>
|
|
||||||
<p>
|
|
||||||
Help us make this a great place for discussion by always working to improve the discussion in some way, however small. If you are not sure your post adds to the discussion or might detract from its usefulness, think over what you want to say and try again later.
|
|
||||||
</p>
|
|
||||||
<div class="more">
|
|
||||||
<p>
|
|
||||||
The topics discussed here matter to us, and we want you to act as if they matter to you, too. Be respectful of the topics and the people discussing them, even if you disagree with some of what is being said.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
One way to improve the discussion is by searching for ones that are already happening. Please use the search function before starting a new discussion, and you will have a better chance of meeting others who share your interests.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
<article id="agreeable">
|
|
||||||
<h2>Be Agreeable, Even When You Disagree</h2>
|
|
||||||
<p>
|
|
||||||
You may wish to respond to something by disagreeing with it. That's fine. But, remember to <em>criticize ideas, not people</em>.
|
|
||||||
Please avoid:
|
|
||||||
<ul>
|
|
||||||
<li>Name-calling.</li>
|
|
||||||
<li>Ad hominem attacks.</li>
|
|
||||||
<li>Responding to a post's tone instead of its actual content.</li>
|
|
||||||
<li>Knee-jerk contradiction.</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
<div class="more">
|
|
||||||
<p>
|
|
||||||
Instead, provide reasoned counter-arguments that improve the conversation.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article id="participate">
|
|
||||||
<h2>Your Participation Counts</h2>
|
|
||||||
<p>
|
|
||||||
The conversations we have here set the tone for everyone. Help us influence the future of this community by choosing to engage in discussions that make this forum an interesting place to be — and avoiding those that do not.
|
|
||||||
</p>
|
|
||||||
<div class="more">
|
|
||||||
<p>
|
|
||||||
Discourse provides tools that enable the community to collectively identify the best (and worst) contributions: favorites, bookmarks, likes, flags, replies, edits, and so forth. Use these tools to improve your own experience, and everyone else’s, too.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Let’s try to leave our park better than we found it.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article id="flag-it">
|
<div id="civilized"></div>
|
||||||
<h2>If You See a Problem, Flag It</h2>
|
<h2><a href="#civilized">This is a Civilized Place for Public Discussion</a></h2>
|
||||||
<p>
|
<p>
|
||||||
Moderators have special responsibility and authority; they are technically responsible for this forum. But so are you. With your help, moderators should be community facilitators more than janitors or police.
|
Please treat this discussion forum with the same respect you would a public park. We, too, are a shared community resource — a place to share skills, knowledge and interests through ongoing conversation.
|
||||||
</p>
|
</p>
|
||||||
<div class="more">
|
<div class="more">
|
||||||
<p>
|
<p>
|
||||||
When you see bad behavior, don't reply. It encourages the bad behavior by acknowledging it, consumes your energy, and wastes everyone’s time. <i>Just flag it</i>. If enough flags accrue, action will be taken, either automatically or by moderator intervention.
|
Use these guidelines to keep this a clean, well-lighted place for civilized public discourse. These are not hard and fast rules, merely aids to the human judgment of moderators and the overall community.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
</div>
|
||||||
In order to maintain our community, moderators reserve the right to remove any content and any user account for any reason at any time. Moderators do not preview new posts or take any preemptive action, therefore, the moderators and site operators take no responsibility for any content posted by the community.
|
|
||||||
</p>
|
<div id="improve"></div>
|
||||||
</div>
|
<h2><a href="#improve">Improve the Discussion</a></h2>
|
||||||
</article>
|
<p>
|
||||||
|
Help us make this a great place for discussion by always working to improve the discussion in some way, however small. If you are not sure your post adds to the discussion or might detract from its usefulness, think over what you want to say and try again later.
|
||||||
<article id="be-civil">
|
</p>
|
||||||
<h2>Always Be Civil</h2>
|
<div class="more">
|
||||||
<p>
|
<p>
|
||||||
Nothing sabotages a healthy conversation like rudeness:
|
The topics discussed here matter to us, and we want you to act as if they matter to you, too. Be respectful of the topics and the people discussing them, even if you disagree with some of what is being said.
|
||||||
<ul>
|
</p>
|
||||||
<li>Be civil. Don’t post anything that a reasonable person would consider offensive, abusive, or hate speech.</li>
|
<p>
|
||||||
<li>Keep it clean. Don’t post anything obscene or sexually explicit.</li>
|
One way to improve the discussion is by searching for ones that are already happening. Please use the search function before starting a new discussion, and you will have a better chance of meeting others who share your interests.
|
||||||
<li>Respect each other. Don’t harass or grief anyone,
|
</p>
|
||||||
impersonate people, or expose their private information.</li>
|
</div>
|
||||||
<li>Respect our forum. Don’t post spam or otherwise vandalize the forum.</li>
|
|
||||||
</ul>
|
<div id="agreeable"></div>
|
||||||
</p>
|
<h2><a href="#agreeable">Be Agreeable, Even When You Disagree</a></h2>
|
||||||
<div class="more">
|
<p>
|
||||||
<p>
|
You may wish to respond to something by disagreeing with it. That's fine. But, remember to <em>criticize ideas, not people</em>.
|
||||||
These are not concrete terms with precise definitions — avoid
|
Please avoid:
|
||||||
even the <i>appearance</i> of any of these things. If you're unsure, ask yourself how you would feel if your post was featured on the front page of the New York Times.
|
<ul>
|
||||||
</p>
|
<li>Name-calling.</li>
|
||||||
<p>
|
<li>Ad hominem attacks.</li>
|
||||||
This is a public forum, and search engines index these discussions. Keep the language, links, and images safe for family and friends.
|
<li>Responding to a post's tone instead of its actual content.</li>
|
||||||
</p>
|
<li>Knee-jerk contradiction.</li>
|
||||||
</div>
|
</ul>
|
||||||
</article>
|
<div class="more">
|
||||||
|
<p>
|
||||||
<article id="keep-tidy">
|
Instead, provide reasoned counter-arguments that improve the conversation.
|
||||||
<h2>Keep It Tidy</h2>
|
</p>
|
||||||
<p>
|
</div>
|
||||||
Make the effort to put things in the right place, so that we can spend more time discussing and less cleaning up. So:
|
|
||||||
<ul>
|
<div id="participate"></div>
|
||||||
<li>Don't start a topic in the wrong category.</li>
|
<h2><a href="#participate">Your Participation Counts</a></h2>
|
||||||
<li>Don't cross-post the same thing in multiple topics.</li>
|
<p>
|
||||||
<li>Don't post no-content replies.</li>
|
The conversations we have here set the tone for everyone. Help us influence the future of this community by choosing to engage in discussions that make this forum an interesting place to be — and avoiding those that do not.
|
||||||
<li>Don't divert a topic by changing it midstream.</li>
|
</p>
|
||||||
</ul>
|
<div class="more">
|
||||||
</p>
|
<p>
|
||||||
<div class="more">
|
Discourse provides tools that enable the community to collectively identify the best (and worst) contributions: favorites, bookmarks, likes, flags, replies, edits, and so forth. Use these tools to improve your own experience, and everyone else’s, too.
|
||||||
<p>
|
</p>
|
||||||
Rather than posting "+1" or "Agreed," use the Like button. Rather than taking an existing topic in a radically different direction, use Reply as a New Topic.
|
<p>
|
||||||
</p>
|
Let’s try to leave our park better than we found it.
|
||||||
<p>
|
</p>
|
||||||
Also, don't sign your posts — every post has your profile information attached to it.
|
</div>
|
||||||
</p>
|
|
||||||
</div>
|
<div id="flag-problems"></div>
|
||||||
</article>
|
<h2><a href="#flag-problems">If You See a Problem, Flag It</a></h2>
|
||||||
|
<p>
|
||||||
<article id="stealing">
|
Moderators have special responsibility and authority; they are technically responsible for this forum. But so are you. With your help, moderators should be community facilitators more than janitors or police.
|
||||||
<h2>Post Only Your Own Stuff</h2>
|
</p>
|
||||||
<p>
|
<div class="more">
|
||||||
You may not post anything digital that belongs to someone else without permission. You may not post descriptions of, links to, or methods for stealing someone's intellectual property (software, video, audio, images), or for breaking any other law.
|
<p>
|
||||||
</p>
|
When you see bad behavior, don't reply. It encourages the bad behavior by acknowledging it, consumes your energy, and wastes everyone’s time. <i>Just flag it</i>. If enough flags accrue, action will be taken, either automatically or by moderator intervention.
|
||||||
<div class="more">
|
</p>
|
||||||
</div>
|
<p>
|
||||||
</article>
|
In order to maintain our community, moderators reserve the right to remove any content and any user account for any reason at any time. Moderators do not preview new posts or take any preemptive action, therefore, the moderators and site operators take no responsibility for any content posted by the community.
|
||||||
|
</p>
|
||||||
<article id="tos">
|
</div>
|
||||||
<h2>Terms of Service</h2>
|
|
||||||
<p>
|
<div id="be-civil"></div>
|
||||||
Yes, legalese is boring, but we must protect ourselves – and by extension, you and your data – against unfriendly folks. We have a <a href="/tos">Terms of Service</a> describing your (and our) behavior and rights related to content, privacy, and laws. To use this service, you must agree to abide by our <a href="/tos">TOS</a>.
|
<h2><a href="#be-civil">Always Be Civil</a></h2>
|
||||||
</p>
|
<p>
|
||||||
<div class="more">
|
Nothing sabotages a healthy conversation like rudeness:
|
||||||
</div>
|
<ul>
|
||||||
</article>
|
<li>Be civil. Don’t post anything that a reasonable person would consider offensive, abusive, or hate speech.</li>
|
||||||
</section>
|
<li>Keep it clean. Don’t post anything obscene or sexually explicit.</li>
|
||||||
|
<li>Respect each other. Don’t harass or grief anyone,
|
||||||
|
impersonate people, or expose their private information.</li>
|
||||||
|
<li>Respect our forum. Don’t post spam or otherwise vandalize the forum.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<div class="more">
|
||||||
|
<p>
|
||||||
|
These are not concrete terms with precise definitions — avoid
|
||||||
|
even the <i>appearance</i> of any of these things. If you're unsure, ask yourself how you would feel if your post was featured on the front page of the New York Times.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This is a public forum, and search engines index these discussions. Keep the language, links, and images safe for family and friends.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="keep-tidy"></div>
|
||||||
|
<h2><a href="#keep-tidy">Keep It Tidy</a></h2>
|
||||||
|
<p>
|
||||||
|
Make the effort to put things in the right place, so that we can spend more time discussing and less cleaning up. So:
|
||||||
|
<ul>
|
||||||
|
<li>Don't start a topic in the wrong category.</li>
|
||||||
|
<li>Don't cross-post the same thing in multiple topics.</li>
|
||||||
|
<li>Don't post no-content replies.</li>
|
||||||
|
<li>Don't divert a topic by changing it midstream.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<div class="more">
|
||||||
|
<p>
|
||||||
|
Rather than posting "+1" or "Agreed," use the Like button. Rather than taking an existing topic in a radically different direction, use Reply as a New Topic.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Also, don't sign your posts — every post has your profile information attached to it.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="stealing"></div>
|
||||||
|
<h2><a href="#stealing">Post Only Your Own Stuff</a></h2>
|
||||||
|
<p>
|
||||||
|
You may not post anything digital that belongs to someone else without permission. You may not post descriptions of, links to, or methods for stealing someone's intellectual property (software, video, audio, images), or for breaking any other law.
|
||||||
|
</p>
|
||||||
|
<div class="more">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tos"></div>
|
||||||
|
<h2><a href="tos">Terms of Service</a></h2>
|
||||||
|
<p>
|
||||||
|
Yes, legalese is boring, but we must protect ourselves – and by extension, you and your data – against unfriendly folks. We have a <a href="/tos">Terms of Service</a> describing your (and our) behavior and rights related to content, privacy, and laws. To use this service, you must agree to abide by our <a href="/tos">TOS</a>.
|
||||||
|
</p>
|
||||||
|
<div class="more">
|
||||||
|
</div>
|
|
@ -1,4 +1,11 @@
|
||||||
<h2>What information do we collect?</h2>
|
<ul class="nav-pills">
|
||||||
|
<li><a href="/faq">FAQ</a></li>
|
||||||
|
<li><a href="/tos">Terms of Service</a></li>
|
||||||
|
<li><a class="active" href="/privacy">Privacy</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div id="collect"></div>
|
||||||
|
<h2><a href="#collect">What information do we collect?</a></h2>
|
||||||
<p>
|
<p>
|
||||||
We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.
|
We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.
|
||||||
</p>
|
</p>
|
||||||
|
@ -7,22 +14,25 @@ We collect information from you when you register on our site and gather data wh
|
||||||
When participating on our site, you may be asked to enter your: name, age and or e-mail address. You may, however, visit our site anonymously.
|
When participating on our site, you may be asked to enter your: name, age and or e-mail address. You may, however, visit our site anonymously.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>What do we use your information for?</h2>
|
<div id="use"></div>
|
||||||
|
<h2><a href="#use">What do we use your information for?</a></h2>
|
||||||
<p>Any of the information we collect from you may be used in one of the following ways:</p>
|
<p>Any of the information we collect from you may be used in one of the following ways:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>To personalize your experience - your information helps us to better respond to your individual needs.</li>
|
<li>To personalize your experience — your information helps us to better respond to your individual needs.</li>
|
||||||
<li>To improve our site - we continually strive to improve our site offerings based on the information and feedback we receive from you.</li>
|
<li>To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.</li>
|
||||||
<li>To improve customer service - your information helps us to more effectively respond to your customer service requests and support needs.</li>
|
<li>To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.</li>
|
||||||
<li>To send periodic emails - The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.</li>
|
<li>To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>How do we protect your information?</h2>
|
<div id="protect"></div>
|
||||||
|
<h2><a href="#use">How do we protect your information?</a></h2>
|
||||||
<p>
|
<p>
|
||||||
We implement a variety of security measures to maintain the safety of your personal
|
We implement a variety of security measures to maintain the safety of your personal
|
||||||
information when you enter, submit, or access your personal information.
|
information when you enter, submit, or access your personal information.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Do we use cookies?</h2>
|
<div id="cookies"></div>
|
||||||
|
<h2><a href="#cookies">Do we use cookies?</a></h2>
|
||||||
<p>
|
<p>
|
||||||
Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow) that enables the sites or service providers systems to recognize your browser and capture and remember certain information.
|
Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow) that enables the sites or service providers systems to recognize your browser and capture and remember certain information.
|
||||||
</p>
|
</p>
|
||||||
|
@ -31,32 +41,38 @@ information when you enter, submit, or access your personal information.
|
||||||
We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.
|
We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Do we disclose any information to outside parties?</h2>
|
<div id="disclose"></div>
|
||||||
|
<h2><a href="#disclose">Do we disclose any information to outside parties?</a></h2>
|
||||||
<p>
|
<p>
|
||||||
We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.
|
We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Third party links</h2>
|
<div id="third-party"></div>
|
||||||
|
<h2><a href="#third-party">Third party links</a></h2>
|
||||||
<p>
|
<p>
|
||||||
Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.
|
Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Children's Online Privacy Protection Act Compliance</h2>
|
<div id="coppa"></div>
|
||||||
|
<h2><a href="#coppa">Children's Online Privacy Protection Act Compliance</a></h2>
|
||||||
<p>
|
<p>
|
||||||
We are in compliance with the requirements of COPPA (Childrens Online Privacy Protection Act), we do not collect any information from anyone under 13 years of age. Our site, products and services are all directed to people who are at least 13 years old or older.
|
We are in compliance with the requirements of COPPA (<a href="http://en.wikipedia.org/wiki/Children's_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>), we do not collect any information from anyone under 13 years of age. Our site, products and services are all directed to people who are at least 13 years old or older.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Online Privacy Policy Only</h2>
|
<div id="online"></div>
|
||||||
|
<h2><a href="#online">Online Privacy Policy Only</a></h2>
|
||||||
<p>
|
<p>
|
||||||
This online privacy policy applies only to information collected through our site and not to information collected offline.
|
This online privacy policy applies only to information collected through our site and not to information collected offline.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Your Consent</h2>
|
<div id="consent"></div>
|
||||||
|
<h2><a href="#consent">Your Consent</a></h2>
|
||||||
<p>
|
<p>
|
||||||
By using our site, you consent to our web site privacy policy.
|
By using our site, you consent to our web site privacy policy.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Changes to our Privacy Policy</h2>
|
<div id="changes"></div>
|
||||||
|
<h2><a href="#changes">Changes to our Privacy Policy</a></h2>
|
||||||
<p>
|
<p>
|
||||||
If we decide to change our privacy policy, we will post those changes on this page.
|
If we decide to change our privacy policy, we will post those changes on this page.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
<ul class="nav-pills">
|
||||||
|
<li><a href="/faq">FAQ</a></li>
|
||||||
|
<li><a class="active" href="/tos">Terms of Service</a></li>
|
||||||
|
<li><a href="/privacy">Privacy</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The following terms and conditions govern all use of the <%= @company_domain %> website and all content, services and products available at or through the website, including, but not limited to, <%= @company_domain %> Forum Software, <%= @company_domain %> Support Forums and the <%= @company_domain %> Hosting service (“Hosting”), (taken together, the Website). The Website is owned and operated by <%= @company_fullname %> (“<%= @company_shortname %>”). The Website is offered subject to your acceptance without modification of all of the terms and conditions contained herein and all other operating rules, policies (including, without limitation, <%= @company_domain %>’s <a href="/privacy">Privacy Policy</a> and <a href="/faq">Community Guidelines</a>) and procedures that may be published from time to time on this Site by <%= @company_shortname %> (collectively, the “Agreement”).
|
The following terms and conditions govern all use of the <%= @company_domain %> website and all content, services and products available at or through the website, including, but not limited to, <%= @company_domain %> Forum Software, <%= @company_domain %> Support Forums and the <%= @company_domain %> Hosting service (“Hosting”), (taken together, the Website). The Website is owned and operated by <%= @company_fullname %> (“<%= @company_shortname %>”). The Website is offered subject to your acceptance without modification of all of the terms and conditions contained herein and all other operating rules, policies (including, without limitation, <%= @company_domain %>’s <a href="/privacy">Privacy Policy</a> and <a href="/faq">Community Guidelines</a>) and procedures that may be published from time to time on this Site by <%= @company_shortname %> (collectively, the “Agreement”).
|
||||||
</p>
|
</p>
|
||||||
|
@ -6,36 +12,30 @@
|
||||||
Please read this Agreement carefully before accessing or using the Website. By accessing or using any part of the web site, you agree to become bound by the terms and conditions of this agreement. If you do not agree to all the terms and conditions of this agreement, then you may not access the Website or use any services. If these terms and conditions are considered an offer by <%= @company_shortname %>, acceptance is expressly limited to these terms. The Website is available only to individuals who are at least 13 years old.
|
Please read this Agreement carefully before accessing or using the Website. By accessing or using any part of the web site, you agree to become bound by the terms and conditions of this agreement. If you do not agree to all the terms and conditions of this agreement, then you may not access the Website or use any services. If these terms and conditions are considered an offer by <%= @company_shortname %>, acceptance is expressly limited to these terms. The Website is available only to individuals who are at least 13 years old.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>1. Your <%= @company_domain %> Account</h2>
|
<div id="1"></div>
|
||||||
|
<h2><a href="#1">1. Your <%= @company_domain %> Account</a></h2>
|
||||||
<p>
|
<p>
|
||||||
If you create an account on the Website, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the account. You must immediately notify <%= @company_shortname %> of any unauthorized uses of your account or any other breaches of security. <%= @company_shortname %> will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions.
|
If you create an account on the Website, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the account. You must immediately notify <%= @company_shortname %> of any unauthorized uses of your account or any other breaches of security. <%= @company_shortname %> will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>2. Responsibility of Contributors</h2>
|
<div id="2"></div>
|
||||||
|
<h2><a href="#2">2. Responsibility of Contributors</a></h2>
|
||||||
<p>If you post material to the Website, post links on the Website, or otherwise make (or allow any third party to make) material available by means of the Website (any such material, “Content”), You are entirely responsible for the content of, and any harm resulting from, that Content. That is the case regardless of whether the Content in question constitutes text, graphics, an audio file, or computer software. By making Content available, you represent and warrant that:
|
<p>If you post material to the Website, post links on the Website, or otherwise make (or allow any third party to make) material available by means of the Website (any such material, “Content”), You are entirely responsible for the content of, and any harm resulting from, that Content. That is the case regardless of whether the Content in question constitutes text, graphics, an audio file, or computer software. By making Content available, you represent and warrant that:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
<li>the downloading, copying and use of the Content will not infringe the proprietary rights, including but not limited to the copyright, patent, trademark or trade secret rights, of any third party;</li>
|
<li>the downloading, copying and use of the Content will not infringe the proprietary rights, including but not limited to the copyright, patent, trademark or trade secret rights, of any third party;</li>
|
||||||
|
|
||||||
<li>if your employer has rights to intellectual property you create, you have either (i) received permission from your employer to post or make available the Content, including but not limited to any software, or (ii) secured from your employer a waiver as to all rights in or to the Content;</li>
|
<li>if your employer has rights to intellectual property you create, you have either (i) received permission from your employer to post or make available the Content, including but not limited to any software, or (ii) secured from your employer a waiver as to all rights in or to the Content;</li>
|
||||||
|
|
||||||
<li>you have fully complied with any third-party licenses relating to the Content, and have done all things necessary to successfully pass through to end users any required terms;</li>
|
<li>you have fully complied with any third-party licenses relating to the Content, and have done all things necessary to successfully pass through to end users any required terms;</li>
|
||||||
|
|
||||||
<li>the Content does not contain or install any viruses, worms, malware, Trojan horses or other harmful or destructive content;</li>
|
<li>the Content does not contain or install any viruses, worms, malware, Trojan horses or other harmful or destructive content;</li>
|
||||||
|
|
||||||
<li>the Content is not spam, is not machine- or randomly-generated, and does not contain unethical or unwanted commercial content designed to drive traffic to third party sites or boost the search engine rankings of third party sites, or to further unlawful acts (such as phishing) or mislead recipients as to the source of the material (such as spoofing);</li>
|
<li>the Content is not spam, is not machine- or randomly-generated, and does not contain unethical or unwanted commercial content designed to drive traffic to third party sites or boost the search engine rankings of third party sites, or to further unlawful acts (such as phishing) or mislead recipients as to the source of the material (such as spoofing);</li>
|
||||||
|
|
||||||
<li>the Content is not pornographic, does not contain threats or incite violence, and does not violate the privacy or publicity rights of any third party;</li>
|
<li>the Content is not pornographic, does not contain threats or incite violence, and does not violate the privacy or publicity rights of any third party;</li>
|
||||||
|
|
||||||
<li>your content is not getting advertised via unwanted electronic messages such as spam links on newsgroups, email lists, blogs and web sites, and similar unsolicited promotional methods;</li>
|
<li>your content is not getting advertised via unwanted electronic messages such as spam links on newsgroups, email lists, blogs and web sites, and similar unsolicited promotional methods;</li>
|
||||||
|
|
||||||
<li>your content is not named in a manner that misleads your readers into thinking that you are another person or company; and</li>
|
<li>your content is not named in a manner that misleads your readers into thinking that you are another person or company; and</li>
|
||||||
|
|
||||||
<li>you have, in the case of Content that includes computer code, accurately categorized and/or described the type, nature, uses and effects of the materials, whether requested to do so by <%= @company_shortname %> or otherwise.</li>
|
<li>you have, in the case of Content that includes computer code, accurately categorized and/or described the type, nature, uses and effects of the materials, whether requested to do so by <%= @company_shortname %> or otherwise.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>3. User Content License</h2>
|
<div id="3"></div>
|
||||||
|
<h2><a href="#3">3. User Content License</a></h2>
|
||||||
<!--<p>
|
<!--<p>
|
||||||
By submitting Content to <%= @company_shortname %> for inclusion on your Website, you grant <%= @company_shortname %> a world-wide, royalty-free, and non-exclusive license to reproduce, modify, adapt and publish the Content solely for the purpose of displaying, distributing and promoting your content. If you delete Content, <%= @company_shortname %> will use reasonable efforts to remove it from the Website, but you acknowledge that caching or references to the Content may not be made immediately unavailable.
|
By submitting Content to <%= @company_shortname %> for inclusion on your Website, you grant <%= @company_shortname %> a world-wide, royalty-free, and non-exclusive license to reproduce, modify, adapt and publish the Content solely for the purpose of displaying, distributing and promoting your content. If you delete Content, <%= @company_shortname %> will use reasonable efforts to remove it from the Website, but you acknowledge that caching or references to the Content may not be made immediately unavailable.
|
||||||
</p>-->
|
</p>-->
|
||||||
|
@ -48,7 +48,8 @@
|
||||||
Without limiting any of those representations or warranties, <%= @company_shortname %> has the right (though not the obligation) to, in <%= @company_shortname %>’s sole discretion (i) refuse or remove any content that, in <%= @company_shortname %>’s reasonable opinion, violates any <%= @company_shortname %> policy or is in any way harmful or objectionable, or (ii) terminate or deny access to and use of the Website to any individual or entity for any reason, in <%= @company_shortname %>’s sole discretion. <%= @company_shortname %> will have no obligation to provide a refund of any amounts previously paid.
|
Without limiting any of those representations or warranties, <%= @company_shortname %> has the right (though not the obligation) to, in <%= @company_shortname %>’s sole discretion (i) refuse or remove any content that, in <%= @company_shortname %>’s reasonable opinion, violates any <%= @company_shortname %> policy or is in any way harmful or objectionable, or (ii) terminate or deny access to and use of the Website to any individual or entity for any reason, in <%= @company_shortname %>’s sole discretion. <%= @company_shortname %> will have no obligation to provide a refund of any amounts previously paid.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>4. Payment and Renewal</h2>
|
<div id="4"></div>
|
||||||
|
<h2><a href="#4">4. Payment and Renewal</a></h2>
|
||||||
<h3>General Terms</h3>
|
<h3>General Terms</h3>
|
||||||
<p>
|
<p>
|
||||||
Optional paid services such as extra storage, or domain purchases are available on the Website (any such services, an “Upgrade”). By selecting an Upgrade you agree to pay <%= @company_shortname %> the monthly or annual subscription fees indicated for that service. Payments will be charged on a pre-pay basis on the day you sign up for an Upgrade and will cover the use of that service for a monthly or annual subscription period as indicated. Upgrade fees are not refundable.
|
Optional paid services such as extra storage, or domain purchases are available on the Website (any such services, an “Upgrade”). By selecting an Upgrade you agree to pay <%= @company_shortname %> the monthly or annual subscription fees indicated for that service. Payments will be charged on a pre-pay basis on the day you sign up for an Upgrade and will cover the use of that service for a monthly or annual subscription period as indicated. Upgrade fees are not refundable.
|
||||||
|
@ -57,67 +58,81 @@
|
||||||
<p>Unless you notify <%= @company_shortname %> before the end of the applicable subscription period that you want to cancel an Upgrade, your Upgrade subscription will automatically renew and you authorize us to collect the then-applicable annual or monthly subscription fee for such Upgrade (as well as any taxes) using any credit card or other payment mechanism we have on record for you. Upgrades can be canceled at any time in the Upgrades section of your site’s dashboard.
|
<p>Unless you notify <%= @company_shortname %> before the end of the applicable subscription period that you want to cancel an Upgrade, your Upgrade subscription will automatically renew and you authorize us to collect the then-applicable annual or monthly subscription fee for such Upgrade (as well as any taxes) using any credit card or other payment mechanism we have on record for you. Upgrades can be canceled at any time in the Upgrades section of your site’s dashboard.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>5. Services</h2>
|
<div id="5"></div>
|
||||||
|
<h2><a href="#5">5. Services</a></h2>
|
||||||
<h3>Hosting, Support Services</h3>
|
<h3>Hosting, Support Services</h3>
|
||||||
<p>Hosting and Support services are provided by <%= @company_shortname %> under the terms and conditions for each such service, which are located at /hosting-tos and /support-tos, respectively. By signing up for a Hosting/Support or Support services account, you agree to abide by such terms and conditions.
|
<p>Hosting and Support services are provided by <%= @company_shortname %> under the terms and conditions for each such service, which are located at /hosting-tos and /support-tos, respectively. By signing up for a Hosting/Support or Support services account, you agree to abide by such terms and conditions.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>6. Responsibility of Website Visitors</h2>
|
<div id="6"></div>
|
||||||
|
<h2><a href="#6">6. Responsibility of Website Visitors</a></h2>
|
||||||
<p>
|
<p>
|
||||||
<%= @company_shortname %> has not reviewed, and cannot review, all of the material, including computer software, posted to the Website, and cannot therefore be responsible for that material’s content, use or effects. By operating the Website, <%= @company_shortname %> does not represent or imply that it endorses the material there posted, or that it believes such material to be accurate, useful or non-harmful. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. The Website may contain content that is offensive, indecent, or otherwise objectionable, as well as content containing technical inaccuracies, typographical mistakes, and other errors. The Website may also contain material that violates the privacy or publicity rights, or infringes the intellectual property and other proprietary rights, of third parties, or the downloading, copying or use of which is subject to additional terms and conditions, stated or unstated. <%= @company_shortname %> disclaims any responsibility for any harm resulting from the use by visitors of the Website, or from any downloading by those visitors of content there posted.
|
<%= @company_shortname %> has not reviewed, and cannot review, all of the material, including computer software, posted to the Website, and cannot therefore be responsible for that material’s content, use or effects. By operating the Website, <%= @company_shortname %> does not represent or imply that it endorses the material there posted, or that it believes such material to be accurate, useful or non-harmful. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. The Website may contain content that is offensive, indecent, or otherwise objectionable, as well as content containing technical inaccuracies, typographical mistakes, and other errors. The Website may also contain material that violates the privacy or publicity rights, or infringes the intellectual property and other proprietary rights, of third parties, or the downloading, copying or use of which is subject to additional terms and conditions, stated or unstated. <%= @company_shortname %> disclaims any responsibility for any harm resulting from the use by visitors of the Website, or from any downloading by those visitors of content there posted.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>7. Content Posted on Other Websites</h2>
|
<div id="7"></div>
|
||||||
|
<h2><a href="#7">7. Content Posted on Other Websites</a></h2>
|
||||||
<p>We have not reviewed, and cannot review, all of the material, including computer software, made available through the websites and webpages to which <%= @company_domain %> links, and that link to <%= @company_domain %>. <%= @company_shortname %> does not have any control over those non-<%= @company_domain %> websites and webpages, and is not responsible for their contents or their use. By linking to a non-<%= @company_domain %> website or webpage, <%= @company_shortname %> does not represent or imply that it endorses such website or webpage. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. <%= @company_shortname %> disclaims any responsibility for any harm resulting from your use of non-WordPress websites and webpages.
|
<p>We have not reviewed, and cannot review, all of the material, including computer software, made available through the websites and webpages to which <%= @company_domain %> links, and that link to <%= @company_domain %>. <%= @company_shortname %> does not have any control over those non-<%= @company_domain %> websites and webpages, and is not responsible for their contents or their use. By linking to a non-<%= @company_domain %> website or webpage, <%= @company_shortname %> does not represent or imply that it endorses such website or webpage. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. <%= @company_shortname %> disclaims any responsibility for any harm resulting from your use of non-WordPress websites and webpages.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>8. Copyright Infringement and DMCA Policy</h2>
|
<div id="8"></div>
|
||||||
|
<h2><a href="#8">8. Copyright Infringement and DMCA Policy</a></h2>
|
||||||
<p>
|
<p>
|
||||||
As <%= @company_shortname %> asks others to respect its intellectual property rights, it respects the intellectual property rights of others. If you believe that material located on or linked to by <%= @company_domain %> violates your copyright, you are encouraged to notify <%= @company_shortname %> in accordance with <%= @company_shortname %>’s <a href="http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act">Digital Millennium Copyright Act</a> (“DMCA”) Policy. <%= @company_shortname %> will respond to all such notices, including as required or appropriate by removing the infringing material or disabling all links to the infringing material. <%= @company_shortname %> will terminate a visitor’s access to and use of the Website if, under appropriate circumstances, the visitor is determined to be a repeat infringer of the copyrights or other intellectual property rights of <%= @company_shortname %> or others. In the case of such termination, <%= @company_shortname %> will have no obligation to provide a refund of any amounts previously paid to <%= @company_shortname %>.
|
As <%= @company_shortname %> asks others to respect its intellectual property rights, it respects the intellectual property rights of others. If you believe that material located on or linked to by <%= @company_domain %> violates your copyright, you are encouraged to notify <%= @company_shortname %> in accordance with <%= @company_shortname %>’s <a href="http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act">Digital Millennium Copyright Act</a> (“DMCA”) Policy. <%= @company_shortname %> will respond to all such notices, including as required or appropriate by removing the infringing material or disabling all links to the infringing material. <%= @company_shortname %> will terminate a visitor’s access to and use of the Website if, under appropriate circumstances, the visitor is determined to be a repeat infringer of the copyrights or other intellectual property rights of <%= @company_shortname %> or others. In the case of such termination, <%= @company_shortname %> will have no obligation to provide a refund of any amounts previously paid to <%= @company_shortname %>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>9. Intellectual Property</h2>
|
<div id="9"></div>
|
||||||
|
<h2><a href="#9">9. Intellectual Property</a></h2>
|
||||||
<p>
|
<p>
|
||||||
This Agreement does not transfer from <%= @company_shortname %> to you any <%= @company_shortname %> or third party intellectual property, and all right, title and interest in and to such property will remain (as between the parties) solely with <%= @company_shortname %>. <%= @company_shortname %>, <%= @company_domain %>, the <%= @company_domain %> logo, and all other trademarks, service marks, graphics and logos used in connection with <%= @company_domain %>, or the Website are trademarks or registered trademarks of <%= @company_shortname %> or <%= @company_shortname %>’s licensors. Other trademarks, service marks, graphics and logos used in connection with the Website may be the trademarks of other third parties. Your use of the Website grants you no right or license to reproduce or otherwise use any <%= @company_shortname %> or third-party trademarks.
|
This Agreement does not transfer from <%= @company_shortname %> to you any <%= @company_shortname %> or third party intellectual property, and all right, title and interest in and to such property will remain (as between the parties) solely with <%= @company_shortname %>. <%= @company_shortname %>, <%= @company_domain %>, the <%= @company_domain %> logo, and all other trademarks, service marks, graphics and logos used in connection with <%= @company_domain %>, or the Website are trademarks or registered trademarks of <%= @company_shortname %> or <%= @company_shortname %>’s licensors. Other trademarks, service marks, graphics and logos used in connection with the Website may be the trademarks of other third parties. Your use of the Website grants you no right or license to reproduce or otherwise use any <%= @company_shortname %> or third-party trademarks.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>10. Advertisements</h2>
|
<div id="10"></div>
|
||||||
|
<h2><a href="#10">10. Advertisements</a></h2>
|
||||||
<p><%= @company_shortname %> reserves the right to display advertisements on your content unless you have purchased an Ad-free Upgrade or a Services account.</p>
|
<p><%= @company_shortname %> reserves the right to display advertisements on your content unless you have purchased an Ad-free Upgrade or a Services account.</p>
|
||||||
|
|
||||||
<h2>11. Attribution</h2>
|
<div id="11"></div>
|
||||||
|
<h2><a href="#11">11. Attribution</a></h2>
|
||||||
<p><%= @company_shortname %> reserves the right to display attribution links such as ‘Powered by <%= @company_domain %>,’ theme author, and font attribution in your content footer or toolbar. Footer credits and the <%= @company_domain %> toolbar may not be removed regardless of upgrades purchased.</p>
|
<p><%= @company_shortname %> reserves the right to display attribution links such as ‘Powered by <%= @company_domain %>,’ theme author, and font attribution in your content footer or toolbar. Footer credits and the <%= @company_domain %> toolbar may not be removed regardless of upgrades purchased.</p>
|
||||||
|
|
||||||
<h2>12. Changes</h3>
|
<div id="12"></div>
|
||||||
<p>
|
<h2><a href="#12">12. Changes</a></h3>
|
||||||
<%= @company_shortname %> reserves the right, at its sole discretion, to modify or replace any part of this Agreement. It is your responsibility to check this Agreement periodically for changes. Your continued use of or access to the Website following the posting of any changes to this Agreement constitutes acceptance of those changes. <%= @company_shortname %> may also, in the future, offer new services and/or features through the Website (including, the release of new tools and resources). Such new features and/or services shall be subject to the terms and conditions of this Agreement.
|
<p>
|
||||||
</p>
|
<%= @company_shortname %> reserves the right, at its sole discretion, to modify or replace any part of this Agreement. It is your responsibility to check this Agreement periodically for changes. Your continued use of or access to the Website following the posting of any changes to this Agreement constitutes acceptance of those changes. <%= @company_shortname %> may also, in the future, offer new services and/or features through the Website (including, the release of new tools and resources). Such new features and/or services shall be subject to the terms and conditions of this Agreement.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>13. Termination</h2>
|
<div id="13"></div>
|
||||||
<p>
|
<h2><a href="#13">13. Termination</a></h2>
|
||||||
<%= @company_shortname %> may terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. If you wish to terminate this Agreement or your <%= @company_domain %> account (if you have one), you may simply discontinue using the Website. All provisions of this Agreement which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability.
|
<p>
|
||||||
</p>
|
<%= @company_shortname %> may terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. If you wish to terminate this Agreement or your <%= @company_domain %> account (if you have one), you may simply discontinue using the Website. All provisions of this Agreement which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>14.Disclaimer of Warranties.</h2>
|
<div id="14"></div>
|
||||||
<p>
|
<h2><a href="#14">14. Disclaimer of Warranties</a></h2>
|
||||||
The Website is provided “as is”. <%= @company_shortname %> and its suppliers and licensors hereby disclaim all warranties of any kind, express or implied, including, without limitation, the warranties of merchantability, fitness for a particular purpose and non-infringement. Neither <%= @company_shortname %> nor its suppliers and licensors, makes any warranty that the Website will be error free or that cess thereto will be continuous or uninterrupted. If you’re actually reading this, here’s <a href="http://www.newyorker.com/online/blogs/shouts/2012/12/the-hundred-best-lists-of-all-time.html">a treat</a>. You understand that you download from, or otherwise obtain content or services through, the Website at your own discretion and risk.
|
<p>
|
||||||
</p>
|
The Website is provided “as is”. <%= @company_shortname %> and its suppliers and licensors hereby disclaim all warranties of any kind, express or implied, including, without limitation, the warranties of merchantability, fitness for a particular purpose and non-infringement. Neither <%= @company_shortname %> nor its suppliers and licensors, makes any warranty that the Website will be error free or that cess thereto will be continuous or uninterrupted. If you’re actually reading this, here’s <a href="http://www.newyorker.com/online/blogs/shouts/2012/12/the-hundred-best-lists-of-all-time.html">a treat</a>. You understand that you download from, or otherwise obtain content or services through, the Website at your own discretion and risk.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>15. Limitation of Liability.</h2>
|
<div id="15"></div>
|
||||||
<p>
|
<h2><a href="#15">15. Limitation of Liability</a></h2>
|
||||||
In no event will <%= @company_shortname %>, or its suppliers or licensors, be liable with respect to any subject matter of this agreement under any contract, negligence, strict liability or other legal or equitable theory for: (i) any special, incidental or consequential damages; (ii) the cost of procurement for substitute products or services; (iii) for interruption of use or loss or corruption of data; or (iv) for any amounts that exceed the fees paid by you to <%= @company_shortname %> under this agreement during the twelve (12) month period prior to the cause of action. <%= @company_shortname %> shall have no liability for any failure or delay due to matters beyond their reasonable control. The foregoing shall not apply to the extent prohibited by applicable law.
|
<p>
|
||||||
</p>
|
In no event will <%= @company_shortname %>, or its suppliers or licensors, be liable with respect to any subject matter of this agreement under any contract, negligence, strict liability or other legal or equitable theory for: (i) any special, incidental or consequential damages; (ii) the cost of procurement for substitute products or services; (iii) for interruption of use or loss or corruption of data; or (iv) for any amounts that exceed the fees paid by you to <%= @company_shortname %> under this agreement during the twelve (12) month period prior to the cause of action. <%= @company_shortname %> shall have no liability for any failure or delay due to matters beyond their reasonable control. The foregoing shall not apply to the extent prohibited by applicable law.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>16. General Representation and Warranty.</h2>
|
<div id="16"></div>
|
||||||
<p>
|
<h2><a href="#16">16. General Representation and Warranty</a></h2>
|
||||||
You represent and warrant that (i) your use of the Website will be in strict accordance with the <%= @company_shortname %> <a href="/privacy">Privacy Policy<a>, <a href="/faq">Community Guidelines</a>, with this Agreement and with all applicable laws and regulations (including without limitation any local laws or regulations in your country, state, city, or other governmental area, regarding online conduct and acceptable content, and including all applicable laws regarding the transmission of technical data exported from the United States or the country in which you reside) and (ii) your use of the Website will not infringe or misappropriate the intellectual property rights of any third party.
|
<p>
|
||||||
</p>
|
You represent and warrant that (i) your use of the Website will be in strict accordance with the <%= @company_shortname %> <a href="/privacy">Privacy Policy<a>, <a href="/faq">Community Guidelines</a>, with this Agreement and with all applicable laws and regulations (including without limitation any local laws or regulations in your country, state, city, or other governmental area, regarding online conduct and acceptable content, and including all applicable laws regarding the transmission of technical data exported from the United States or the country in which you reside) and (ii) your use of the Website will not infringe or misappropriate the intellectual property rights of any third party.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>17.Indemnification</h2>
|
<div id="17"></div>
|
||||||
<p>
|
<h2><a href="#17">17.Indemnification</a></h2>
|
||||||
You agree to indemnify and hold harmless <%= @company_shortname %>, its contractors, and its licensors, and their respective directors, officers, employees and agents from and against any and all claims and expenses, including attorneys’ fees, arising out of your use of the Website, including but not limited to your violation of this Agreement.
|
<p>
|
||||||
</p>
|
You agree to indemnify and hold harmless <%= @company_shortname %>, its contractors, and its licensors, and their respective directors, officers, employees and agents from and against any and all claims and expenses, including attorneys’ fees, arising out of your use of the Website, including but not limited to your violation of this Agreement.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>18. Miscellaneous</h2>
|
<div id="18"></div>
|
||||||
<p>
|
<h2><a href="#18">18. Miscellaneous</a></h2>
|
||||||
This Agreement constitutes the entire agreement between <%= @company_shortname %> and you concerning the subject matter hereof, and they may only be modified by a written amendment signed by an authorized executive of <%= @company_shortname %>, or by the posting by <%= @company_shortname %> of a revised version. Except to the extent applicable law, if any, provides otherwise, this Agreement, any access to or use of the Website will be governed by the laws of the state of California, U.S.A., excluding its conflict of law provisions, and the proper venue for any disputes arising out of or relating to any of the same will be the state and federal courts located in San Francisco County, California. Except for claims for injunctive or equitable relief or claims regarding intellectual property rights (which may be brought in any competent court without the posting of a bond), any dispute arising under this Agreement shall be finally settled in accordance with the Comprehensive Arbitration Rules of the Judicial Arbitration and Mediation Service, Inc. (“JAMS”) by three arbitrators appointed in accordance with such Rules. The arbitration shall take place in San Francisco, California, in the English language and the arbitral decision may be enforced in any court. The prevailing party in any action or proceeding to enforce this Agreement shall be entitled to costs and attorneys’ fees. If any part of this Agreement is held invalid or unenforceable, that part will be construed to reflect the parties’ original intent, and the remaining portions will remain in full force and effect. A waiver by either party of any term or condition of this Agreement or any breach thereof, in any one instance, will not waive such term or condition or any subsequent breach thereof. You may assign your rights under this Agreement to any party that consents to, and agrees to be bound by, its terms and conditions; <%= @company_shortname %> may assign its rights under this Agreement without condition. This Agreement will be binding upon and will inure to the benefit of the parties, their successors and permitted assigns.
|
<p>
|
||||||
</p>
|
This Agreement constitutes the entire agreement between <%= @company_shortname %> and you concerning the subject matter hereof, and they may only be modified by a written amendment signed by an authorized executive of <%= @company_shortname %>, or by the posting by <%= @company_shortname %> of a revised version. Except to the extent applicable law, if any, provides otherwise, this Agreement, any access to or use of the Website will be governed by the laws of the state of California, U.S.A., excluding its conflict of law provisions, and the proper venue for any disputes arising out of or relating to any of the same will be the state and federal courts located in San Francisco County, California. Except for claims for injunctive or equitable relief or claims regarding intellectual property rights (which may be brought in any competent court without the posting of a bond), any dispute arising under this Agreement shall be finally settled in accordance with the Comprehensive Arbitration Rules of the Judicial Arbitration and Mediation Service, Inc. (“JAMS”) by three arbitrators appointed in accordance with such Rules. The arbitration shall take place in San Francisco, California, in the English language and the arbitral decision may be enforced in any court. The prevailing party in any action or proceeding to enforce this Agreement shall be entitled to costs and attorneys’ fees. If any part of this Agreement is held invalid or unenforceable, that part will be construed to reflect the parties’ original intent, and the remaining portions will remain in full force and effect. A waiver by either party of any term or condition of this Agreement or any breach thereof, in any one instance, will not waive such term or condition or any subsequent breach thereof. You may assign your rights under this Agreement to any party that consents to, and agrees to be bound by, its terms and conditions; <%= @company_shortname %> may assign its rights under this Agreement without condition. This Agreement will be binding upon and will inure to the benefit of the parties, their successors and permitted assigns.
|
||||||
|
</p>
|
|
@ -1,7 +1,6 @@
|
||||||
require File.expand_path('../boot', __FILE__)
|
require File.expand_path('../boot', __FILE__)
|
||||||
|
|
||||||
require 'rails/all'
|
require 'rails/all'
|
||||||
require "redis-store" # HACK
|
require 'redis-store' # HACK
|
||||||
|
|
||||||
# Plugin related stuff
|
# Plugin related stuff
|
||||||
require './lib/discourse_plugin_registry'
|
require './lib/discourse_plugin_registry'
|
||||||
|
@ -50,7 +49,7 @@ module Discourse
|
||||||
# config.i18n.default_locale = :de
|
# config.i18n.default_locale = :de
|
||||||
|
|
||||||
# Configure the default encoding used in templates for Ruby 1.9.
|
# Configure the default encoding used in templates for Ruby 1.9.
|
||||||
config.encoding = "utf-8"
|
config.encoding = 'utf-8'
|
||||||
|
|
||||||
# Configure sensitive parameters which will be filtered from the log file.
|
# Configure sensitive parameters which will be filtered from the log file.
|
||||||
config.filter_parameters += [:password]
|
config.filter_parameters += [:password]
|
||||||
|
|
|
@ -31,3 +31,4 @@ MessageBus.is_admin_lookup do |env|
|
||||||
end
|
end
|
||||||
|
|
||||||
MessageBus.cache_assets = !Rails.env.development?
|
MessageBus.cache_assets = !Rails.env.development?
|
||||||
|
MessageBus.enable_diagnostics
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
|
||||||
# Definitely change this when you deploy to production. Ours is replaced by jenkins.
|
# Definitely change this when you deploy to production. Ours is replaced by jenkins.
|
||||||
Discourse::Application.config.secret_token = "47f5390004bf6d25bb97083fb98e7cc133ab450ba814dd19638a78282b4ca291"
|
# This token is used to secure sessions, we don't mind shipping with one to ease test and debug,
|
||||||
|
# however, the stock one should never be used in production, people will be able to crack
|
||||||
|
# session cookies.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Discourse::Application.config.secret_token = "SET_SECRET_HERE"
|
||||||
|
|
||||||
|
# delete all lines below in production
|
||||||
|
if Rails.env.test? || Rails.env.development?
|
||||||
|
Discourse::Application.config.secret_token = "47f5390004bf6d25bb97083fb98e7cc133ab450ba814dd19638a78282b4ca291"
|
||||||
|
else
|
||||||
|
raise "You must set a secret token in config/initializers/secret_token.rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,61 +1,30 @@
|
||||||
|
# this is a trivial graceful restart on touch of tmp/restart.
|
||||||
|
#
|
||||||
|
# It simply drains all the requests (waits up to 4 seconds) and issues a HUP
|
||||||
|
# if you need a more sophisticated cycling restart for multiple thins it will need to be written
|
||||||
|
#
|
||||||
|
# This works fine for Discourse.org cause we host our app accross multiple machines, if you hosting
|
||||||
|
# on a single machine you have a trickier problem at hand as you need to cycle the processes in order
|
||||||
|
|
||||||
Thread.new do
|
Thread.new do
|
||||||
file = "#{Rails.root}/tmp/restart"
|
file = "#{Rails.root}/tmp/restart"
|
||||||
did_exist = nil
|
old_time = File.ctime(file).to_i if File.exists? file
|
||||||
old_time = nil
|
wait_seconds = 4
|
||||||
|
|
||||||
return if $PROGRAM_NAME !~ /thin/
|
return if $PROGRAM_NAME !~ /thin/
|
||||||
|
|
||||||
processes = {}
|
|
||||||
got_new = false
|
|
||||||
MessageBus.subscribe "/processes" do |msg|
|
|
||||||
filetime = msg.data["filetime"]
|
|
||||||
pid = msg.data["pid"]
|
|
||||||
got_new = processes[pid].nil? || (processes[pid][:filetime] != filetime)
|
|
||||||
# puts "#{got_new} #{pid}"
|
|
||||||
processes[pid] = {time: Time.now.to_i, filetime: filetime}
|
|
||||||
end
|
|
||||||
|
|
||||||
while true
|
while true
|
||||||
exists = File.exists? file
|
time = File.ctime(file).to_i if File.exists? file
|
||||||
time = File.ctime(file).to_i if exists
|
|
||||||
|
|
||||||
if (did_exist != nil && did_exist != exists) ||
|
|
||||||
(old_time != nil && time != nil && old_time != time)
|
|
||||||
|
|
||||||
got_new = false
|
if old_time != time
|
||||||
probably_restarted = []
|
Rails.logger.info "attempting to reload #{$$} #{$PROGRAM_NAME} in #{wait_seconds} seconds"
|
||||||
|
|
||||||
give_up_time = Time.now.to_i + 60
|
|
||||||
|
|
||||||
while Time.now.to_i < give_up_time
|
|
||||||
candidates = []
|
|
||||||
processes.each do |pid,data|
|
|
||||||
if data[:filetime] == old_time && data[:time] > Time.now.to_i - 40
|
|
||||||
candidates << pid
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
candidates = candidates - probably_restarted
|
|
||||||
|
|
||||||
break if (candidates.min || $$) >= $$
|
|
||||||
sleep 1
|
|
||||||
probably_restarted << candidates.min if got_new
|
|
||||||
got_new = false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
Rails.logger.info "attempting to reload #{$$} #{$PROGRAM_NAME} in 3 seconds restarted #{probably_restarted.inspect}"
|
|
||||||
$shutdown = true
|
$shutdown = true
|
||||||
sleep 4
|
sleep wait_seconds
|
||||||
Rails.logger.info "restarting #{$$}"
|
Rails.logger.info "restarting #{$$}"
|
||||||
Process.kill("HUP", $$)
|
Process.kill("HUP", $$)
|
||||||
|
return
|
||||||
break
|
|
||||||
end
|
end
|
||||||
|
|
||||||
MessageBus.publish "/processes", {pid: $$, filetime: time}
|
sleep 1
|
||||||
did_exist = exists
|
|
||||||
old_time = time
|
|
||||||
sleep 10
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
|
|
||||||
en:
|
en:
|
||||||
js:
|
js:
|
||||||
share:
|
share:
|
||||||
topic: 'share a link to this topic'
|
topic: 'share a link to this topic'
|
||||||
post: 'share a link to this post'
|
post: 'share a link to this post'
|
||||||
|
|
||||||
edit: 'edit the title and category of this topic'
|
edit: 'edit the title and category of this topic'
|
||||||
not_implemented: "That feature hasn't been implemented yet, sorry!"
|
not_implemented: "That feature hasn't been implemented yet, sorry!"
|
||||||
|
@ -61,7 +61,7 @@ en:
|
||||||
invited_by: "Invited By"
|
invited_by: "Invited By"
|
||||||
trust_level: "Trust Level"
|
trust_level: "Trust Level"
|
||||||
|
|
||||||
change_username:
|
change_username:
|
||||||
action: "change"
|
action: "change"
|
||||||
title: "Change Username"
|
title: "Change Username"
|
||||||
confirm: "There could be consequences to changing your username. Are you absolutely sure you want to?"
|
confirm: "There could be consequences to changing your username. Are you absolutely sure you want to?"
|
||||||
|
@ -75,7 +75,7 @@ en:
|
||||||
error: "There was an error changing your email. Perhaps that address is already in use?"
|
error: "There was an error changing your email. Perhaps that address is already in use?"
|
||||||
success: "We've sent an email to that address. Please follow the confirmation instructions."
|
success: "We've sent an email to that address. Please follow the confirmation instructions."
|
||||||
|
|
||||||
email:
|
email:
|
||||||
title: "Email"
|
title: "Email"
|
||||||
instructions: "Your email will never be shown to the public."
|
instructions: "Your email will never be shown to the public."
|
||||||
ok: "Looks good. We will email you to confirm."
|
ok: "Looks good. We will email you to confirm."
|
||||||
|
@ -83,12 +83,12 @@ en:
|
||||||
authenticated: "Your email has been authenticated by {{provider}}."
|
authenticated: "Your email has been authenticated by {{provider}}."
|
||||||
frequency: "We'll only email you if we haven't seen you recently and you haven't already seen the thing we're emailing you about."
|
frequency: "We'll only email you if we haven't seen you recently and you haven't already seen the thing we're emailing you about."
|
||||||
|
|
||||||
name:
|
name:
|
||||||
title: "Name"
|
title: "Name"
|
||||||
instructions: "The longer version of your name; does not need to be unique."
|
instructions: "The longer version of your name; does not need to be unique."
|
||||||
too_short: "Your name is too short."
|
too_short: "Your name is too short."
|
||||||
ok: "Your name looks good."
|
ok: "Your name looks good."
|
||||||
username:
|
username:
|
||||||
title: "Username"
|
title: "Username"
|
||||||
#instructions: "People can mention you as @{{username}}. This is an unregistered nickname. You can register it at <a href='http://discourse.org'>discourse.org</a>."
|
#instructions: "People can mention you as @{{username}}. This is an unregistered nickname. You can register it at <a href='http://discourse.org'>discourse.org</a>."
|
||||||
instructions: "People can mention you as @{{username}}."
|
instructions: "People can mention you as @{{username}}."
|
||||||
|
@ -108,7 +108,7 @@ en:
|
||||||
log_out: "Log Out"
|
log_out: "Log Out"
|
||||||
website: "Web Site"
|
website: "Web Site"
|
||||||
email_settings: "Email"
|
email_settings: "Email"
|
||||||
email_digests:
|
email_digests:
|
||||||
title: "When I don't visit the site, send me an email digest of what's new"
|
title: "When I don't visit the site, send me an email digest of what's new"
|
||||||
daily: "daily"
|
daily: "daily"
|
||||||
weekly: "weekly"
|
weekly: "weekly"
|
||||||
|
@ -124,11 +124,11 @@ en:
|
||||||
not_viewed: "I haven't viewed them yet"
|
not_viewed: "I haven't viewed them yet"
|
||||||
last_here: "they were posted since I was here last"
|
last_here: "they were posted since I was here last"
|
||||||
after_n_days:
|
after_n_days:
|
||||||
one: "they were posted in the last day"
|
one: "they were posted in the last day"
|
||||||
other: "they were posted in the last {{count}} days"
|
other: "they were posted in the last {{count}} days"
|
||||||
after_n_weeks:
|
after_n_weeks:
|
||||||
one: "they were posted in the last week"
|
one: "they were posted in the last week"
|
||||||
other: "they were posted in the last {{count}} week"
|
other: "they were posted in the last {{count}} week"
|
||||||
|
|
||||||
auto_track_topics: "Automatically track topics I enter"
|
auto_track_topics: "Automatically track topics I enter"
|
||||||
auto_track_options:
|
auto_track_options:
|
||||||
|
@ -141,7 +141,7 @@ en:
|
||||||
one: "after 1 minute"
|
one: "after 1 minute"
|
||||||
other: "after {{count}} minutes"
|
other: "after {{count}} minutes"
|
||||||
|
|
||||||
invited:
|
invited:
|
||||||
title: "Invites"
|
title: "Invites"
|
||||||
user: "Invited User"
|
user: "Invited User"
|
||||||
none: "{{username}} hasn't invited any users to the site."
|
none: "{{username}} hasn't invited any users to the site."
|
||||||
|
@ -156,19 +156,25 @@ en:
|
||||||
days_visited: "Days Visited"
|
days_visited: "Days Visited"
|
||||||
account_age_days: "Account age in days"
|
account_age_days: "Account age in days"
|
||||||
|
|
||||||
password:
|
password:
|
||||||
title: "Password"
|
title: "Password"
|
||||||
too_short: "Your password is too short."
|
too_short: "Your password is too short."
|
||||||
ok: "Your password looks good."
|
ok: "Your password looks good."
|
||||||
|
|
||||||
ip_address:
|
ip_address:
|
||||||
title: "Last IP Address"
|
title: "Last IP Address"
|
||||||
avatar:
|
avatar:
|
||||||
title: "Avatar"
|
title: "Avatar"
|
||||||
instructions: "We use <a href='https://gravatar.com' target='_blank'>Gravatar</a> for avatars based on your email"
|
instructions: "We use <a href='https://gravatar.com' target='_blank'>Gravatar</a> for avatars based on your email"
|
||||||
|
|
||||||
filters:
|
filters:
|
||||||
all: "All"
|
all: "All"
|
||||||
|
|
||||||
|
stream:
|
||||||
|
posted_by: "Posted by"
|
||||||
|
sent_by: "Sent by"
|
||||||
|
private_message: "private message"
|
||||||
|
the_topic: "the topic"
|
||||||
|
|
||||||
loading: "Loading..."
|
loading: "Loading..."
|
||||||
close: "Close"
|
close: "Close"
|
||||||
|
@ -207,7 +213,7 @@ en:
|
||||||
invite: "Don't have an account yet?"
|
invite: "Don't have an account yet?"
|
||||||
failed: "Something went wrong, perhaps this email is already registered, try the forgot password link"
|
failed: "Something went wrong, perhaps this email is already registered, try the forgot password link"
|
||||||
|
|
||||||
forgot_password:
|
forgot_password:
|
||||||
title: "Forgot Password"
|
title: "Forgot Password"
|
||||||
action: "I forgot my password"
|
action: "I forgot my password"
|
||||||
invite: "Enter your username or email address, and we'll send you a password reset email."
|
invite: "Enter your username or email address, and we'll send you a password reset email."
|
||||||
|
@ -240,15 +246,15 @@ en:
|
||||||
message: "Authenticating with Yahoo (make sure pop up blockers are not enabled)"
|
message: "Authenticating with Yahoo (make sure pop up blockers are not enabled)"
|
||||||
|
|
||||||
composer:
|
composer:
|
||||||
saving_draft_tip: "saving"
|
saving_draft_tip: "saving"
|
||||||
saved_draft_tip: "saved"
|
saved_draft_tip: "saved"
|
||||||
saved_local_draft_tip: "saved locally"
|
saved_local_draft_tip: "saved locally"
|
||||||
|
|
||||||
min_length:
|
min_length:
|
||||||
at_least: "enter at least {{n}} characters"
|
at_least: "enter at least {{n}} characters"
|
||||||
more: "{{n}} to go..."
|
more: "{{n}} to go..."
|
||||||
|
|
||||||
save_edit: "Save Edit"
|
save_edit: "Save Edit"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
create_topic: "Create Topic"
|
create_topic: "Create Topic"
|
||||||
create_pm: "Create Private Message"
|
create_pm: "Create Private Message"
|
||||||
|
@ -286,8 +292,9 @@ en:
|
||||||
remote_tip: "enter address of an image in the form http://example.com/image.jpg"
|
remote_tip: "enter address of an image in the form http://example.com/image.jpg"
|
||||||
local_tip: "click to select an image from your device."
|
local_tip: "click to select an image from your device."
|
||||||
upload: "Upload"
|
upload: "Upload"
|
||||||
|
uploading_image: "Uploading image"
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: "search for topics, posts, users, or categories"
|
title: "search for topics, posts, users, or categories"
|
||||||
placeholder: "type your search terms here"
|
placeholder: "type your search terms here"
|
||||||
no_results: "No results found."
|
no_results: "No results found."
|
||||||
|
@ -301,15 +308,16 @@ en:
|
||||||
title: 'Favorite'
|
title: 'Favorite'
|
||||||
help: 'add this topic to your favorites list'
|
help: 'add this topic to your favorites list'
|
||||||
|
|
||||||
topics:
|
topics:
|
||||||
no_favorited: "You haven't favorited any topics yet. To favorite a topic, click or tap the star next to the title."
|
no_favorited: "You haven't favorited any topics yet. To favorite a topic, click or tap the star next to the title."
|
||||||
no_unread: "You have no unread topics to read."
|
no_unread: "You have no unread topics to read."
|
||||||
no_new: "You have no new topics to read."
|
no_new: "You have no new topics to read."
|
||||||
no_read: "You haven't read any topics yet."
|
no_read: "You haven't read any topics yet."
|
||||||
no_posted: "You haven't posted in any topics yet."
|
no_posted: "You haven't posted in any topics yet."
|
||||||
no_popular: "There are no popular topics. That's sad."
|
no_popular: "There are no popular topics. That's sad."
|
||||||
|
footer: "No more topics in this category. <a href=\"/categories\">Browse all categories</a> or <a href=\"/\">view popular topics</a>"
|
||||||
|
|
||||||
topic:
|
topic:
|
||||||
create_in: 'Create {{categoryName}} Topic'
|
create_in: 'Create {{categoryName}} Topic'
|
||||||
create: 'Create Topic'
|
create: 'Create Topic'
|
||||||
create_long: 'Create a new Topic'
|
create_long: 'Create a new Topic'
|
||||||
|
@ -354,7 +362,7 @@ en:
|
||||||
"1_2": 'You will be notified only if someone mentions your @name or replies to your post.'
|
"1_2": 'You will be notified only if someone mentions your @name or replies to your post.'
|
||||||
"0": 'You are ignoring all notifications on this topic.'
|
"0": 'You are ignoring all notifications on this topic.'
|
||||||
"0_2": 'You are ignoring all notifications on this topic.'
|
"0_2": 'You are ignoring all notifications on this topic.'
|
||||||
watching:
|
watching:
|
||||||
title: "Watching"
|
title: "Watching"
|
||||||
description: "same as Tracking, plus you will be notified of all new posts."
|
description: "same as Tracking, plus you will be notified of all new posts."
|
||||||
tracking:
|
tracking:
|
||||||
|
@ -381,11 +389,11 @@ en:
|
||||||
multi_select: "Toggle Multi-Select"
|
multi_select: "Toggle Multi-Select"
|
||||||
convert_to_topic: "Convert to Regular Topic"
|
convert_to_topic: "Convert to Regular Topic"
|
||||||
|
|
||||||
reply:
|
reply:
|
||||||
title: 'Reply'
|
title: 'Reply'
|
||||||
help: 'begin composing a reply to this topic'
|
help: 'begin composing a reply to this topic'
|
||||||
|
|
||||||
share:
|
share:
|
||||||
title: 'Share'
|
title: 'Share'
|
||||||
help: 'share a link to this topic'
|
help: 'share a link to this topic'
|
||||||
|
|
||||||
|
@ -399,11 +407,11 @@ en:
|
||||||
success: "Thanks! We've invited that user to participate in this private conversation."
|
success: "Thanks! We've invited that user to participate in this private conversation."
|
||||||
error: "Sorry there was an error inviting that user."
|
error: "Sorry there was an error inviting that user."
|
||||||
|
|
||||||
invite_reply:
|
invite_reply:
|
||||||
title: 'Invite Friends to Reply'
|
title: 'Invite Friends to Reply'
|
||||||
help: 'send invitations to friends so they can reply to this topic with a single click'
|
help: 'send invitations to friends so they can reply to this topic with a single click'
|
||||||
email: "We'll send your friend a brief email allowing them to reply to this topic by clicking a link."
|
email: "We'll send your friend a brief email allowing them to reply to this topic by clicking a link."
|
||||||
email_placeholder: 'email address'
|
email_placeholder: 'email address'
|
||||||
success: "Thanks! We mailed out an invitation to <b>{{email}}</b>. We'll let you know when they redeem your invitation. Check the invitations tab on your user page to keep track of who you've invited."
|
success: "Thanks! We mailed out an invitation to <b>{{email}}</b>. We'll let you know when they redeem your invitation. Check the invitations tab on your user page to keep track of who you've invited."
|
||||||
error: "Sorry we couldn't invite that person. Perhaps they are already a user?"
|
error: "Sorry we couldn't invite that person. Perhaps they are already a user?"
|
||||||
|
|
||||||
|
@ -418,7 +426,7 @@ en:
|
||||||
title: "Move Selected Posts"
|
title: "Move Selected Posts"
|
||||||
topic_name: "New Topic Name:"
|
topic_name: "New Topic Name:"
|
||||||
error: "Sorry, there was an error moving those posts."
|
error: "Sorry, there was an error moving those posts."
|
||||||
instructions:
|
instructions:
|
||||||
one: "You are about to create a new topic and populate it with the post you've selected."
|
one: "You are about to create a new topic and populate it with the post you've selected."
|
||||||
other: "You are about to create a new topic and populate it with the <b>{{count}}</b> posts you've selected."
|
other: "You are about to create a new topic and populate it with the <b>{{count}}</b> posts you've selected."
|
||||||
|
|
||||||
|
@ -433,7 +441,7 @@ en:
|
||||||
other: "You have selected <b>{{count}}</b> posts."
|
other: "You have selected <b>{{count}}</b> posts."
|
||||||
|
|
||||||
post:
|
post:
|
||||||
reply: "Replying to {{link}} by {{replyAvatar}} {{username}}"
|
reply: "Replying to {{link}} by {{replyAvatar}} {{username}}"
|
||||||
reply_topic: "Reply to {{link}}"
|
reply_topic: "Reply to {{link}}"
|
||||||
edit: "Edit {{link}}"
|
edit: "Edit {{link}}"
|
||||||
in_reply_to: "in reply to"
|
in_reply_to: "in reply to"
|
||||||
|
@ -482,13 +490,13 @@ en:
|
||||||
one: "1 person {{long_form}}"
|
one: "1 person {{long_form}}"
|
||||||
other: "{{count}} people {{long_form}}"
|
other: "{{count}} people {{long_form}}"
|
||||||
|
|
||||||
edits:
|
edits:
|
||||||
one: 1 edit
|
one: 1 edit
|
||||||
other: "{{count}} edits"
|
other: "{{count}} edits"
|
||||||
zero: no edits
|
zero: no edits
|
||||||
|
|
||||||
delete:
|
delete:
|
||||||
confirm:
|
confirm:
|
||||||
one: "Are you sure you want to delete that post?"
|
one: "Are you sure you want to delete that post?"
|
||||||
other: "Are you sure you want to delete all those posts?"
|
other: "Are you sure you want to delete all those posts?"
|
||||||
|
|
||||||
|
@ -546,27 +554,27 @@ en:
|
||||||
categories_list: "Categories List"
|
categories_list: "Categories List"
|
||||||
|
|
||||||
filters:
|
filters:
|
||||||
popular:
|
popular:
|
||||||
title: "Popular"
|
title: "Popular"
|
||||||
help: "the most popular recent topics"
|
help: "the most popular recent topics"
|
||||||
favorited:
|
favorited:
|
||||||
title: "Favorited"
|
title: "Favorited"
|
||||||
help: "topics you marked as favorites"
|
help: "topics you marked as favorites"
|
||||||
read:
|
read:
|
||||||
title: "Read"
|
title: "Read"
|
||||||
help: "topics you've read"
|
help: "topics you've read"
|
||||||
categories:
|
categories:
|
||||||
title: "Categories"
|
title: "Categories"
|
||||||
title_in: "Category - {{categoryName}}"
|
title_in: "Category - {{categoryName}}"
|
||||||
help: "all topics grouped by category"
|
help: "all topics grouped by category"
|
||||||
unread:
|
unread:
|
||||||
title:
|
title:
|
||||||
zero: "Unread"
|
zero: "Unread"
|
||||||
one: "Unread (1)"
|
one: "Unread (1)"
|
||||||
other: "Unread ({{count}})"
|
other: "Unread ({{count}})"
|
||||||
help: "tracked topics with unread posts"
|
help: "tracked topics with unread posts"
|
||||||
new:
|
new:
|
||||||
title:
|
title:
|
||||||
zero: "New"
|
zero: "New"
|
||||||
one: "New (1)"
|
one: "New (1)"
|
||||||
other: "New ({{count}})"
|
other: "New ({{count}})"
|
||||||
|
@ -585,7 +593,7 @@ en:
|
||||||
admin_js:
|
admin_js:
|
||||||
type_to_filter: "type to filter..."
|
type_to_filter: "type to filter..."
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
title: 'Discourse Admin'
|
title: 'Discourse Admin'
|
||||||
dashboard: 'Admin Dashboard'
|
dashboard: 'Admin Dashboard'
|
||||||
|
|
||||||
|
@ -597,8 +605,9 @@ en:
|
||||||
clear_title: "dismiss all flags on this post (will unhide hidden posts)"
|
clear_title: "dismiss all flags on this post (will unhide hidden posts)"
|
||||||
delete: "Delete Post"
|
delete: "Delete Post"
|
||||||
delete_title: "delete post (if its the first post delete topic)"
|
delete_title: "delete post (if its the first post delete topic)"
|
||||||
|
flagged_by: "Flagged by"
|
||||||
|
|
||||||
customize:
|
customize:
|
||||||
title: "Customize"
|
title: "Customize"
|
||||||
header: "Header"
|
header: "Header"
|
||||||
css: "Stylesheet"
|
css: "Stylesheet"
|
||||||
|
|
|
@ -11,7 +11,7 @@ to rails, you are likely much better off with our **[Discourse Vagrant Developer
|
||||||
3. Clone the project.
|
3. Clone the project.
|
||||||
4. Create development and test databases in postgres.
|
4. Create development and test databases in postgres.
|
||||||
5. Copy `config/database.yml.sample` and `config/redis.yml.sample` to `config/database.yml` and `config/redis.yml` and input the correct values to point to your postgres and redis instances.
|
5. Copy `config/database.yml.sample` and `config/redis.yml.sample` to `config/database.yml` and `config/redis.yml` and input the correct values to point to your postgres and redis instances.
|
||||||
6. We recommend starting with seed data to play around in your development environment. [Download Seed SQL Data](http://discourse.org/vms/dev-discourse-seed.sql). Install it into postgres using a command like this: `psql -d discourse_development < dev-discourse-seed.sql`.
|
6. We recommend starting with seed data to play around in your development environment. [Download Seed SQL Data][seed_download]. Install it into postgres using a command like this: `psql -d discourse_development < dev-discourse-seed.sql`.
|
||||||
|
|
||||||
|
|
||||||
## Before you start Rails
|
## Before you start Rails
|
||||||
|
@ -107,7 +107,7 @@ Edit /etc/postgresql/9.1/main/pg_hba.conf to have this:
|
||||||
host all all ::1/128 trust
|
host all all ::1/128 trust
|
||||||
host all all 0.0.0.0/0 trust # wide-open
|
host all all 0.0.0.0/0 trust # wide-open
|
||||||
|
|
||||||
Download a database image from [http://discourse.org/vms/dev-discourse-seed.sql][1]
|
Download a database image from [http://discourse.org/vms/dev-discourse-seed.sql][seed_download].
|
||||||
|
|
||||||
Load it (as vagrant user):
|
Load it (as vagrant user):
|
||||||
|
|
||||||
|
@ -126,3 +126,5 @@ Load it (as vagrant user):
|
||||||
./install_server.sh
|
./install_server.sh
|
||||||
# Press enter to accept all the defaults
|
# Press enter to accept all the defaults
|
||||||
/etc/init.d/redis_6379 start
|
/etc/init.d/redis_6379 start
|
||||||
|
|
||||||
|
[seed_download]: (http://discourse.org/vms/dev-discourse-seed.sql)
|
|
@ -10,7 +10,7 @@ on Discourse with:
|
||||||
### Getting Started
|
### Getting Started
|
||||||
|
|
||||||
1. Install VirtualBox: https://www.virtualbox.org/wiki/Downloads
|
1. Install VirtualBox: https://www.virtualbox.org/wiki/Downloads
|
||||||
2. Install Vagrant: https://www.vagrantup.com/
|
2. Install Vagrant: http://www.vagrantup.com/
|
||||||
3. Open a terminal
|
3. Open a terminal
|
||||||
4. Clone the project: `git clone git@github.com:discourse/discourse.git`
|
4. Clone the project: `git clone git@github.com:discourse/discourse.git`
|
||||||
5. Enter the project directory: `cd discourse`
|
5. Enter the project directory: `cd discourse`
|
||||||
|
@ -32,6 +32,28 @@ Once the machine has booted up, you can shell into it by typing:
|
||||||
vagrant ssh
|
vagrant ssh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note to Windows users**: You cannot run ```vagrant ssh``` from a cmd prompt; you'll receive the error message:
|
||||||
|
|
||||||
|
```
|
||||||
|
`vagrant ssh` isn't available on the Windows platform. You are still able
|
||||||
|
to SSH into the virtual machine if you get a Windows SSH client (such as
|
||||||
|
PuTTY). The authentication information is shown below:
|
||||||
|
|
||||||
|
Host: 127.0.0.1
|
||||||
|
Port: 2222
|
||||||
|
Username: vagrant
|
||||||
|
Private key: C:/Users/Your Name/.vagrant.d/insecure_private_key
|
||||||
|
```
|
||||||
|
|
||||||
|
At this point, you will want to get an SSH client, and use it to connect to your Vagrant VM instead. We recommend
|
||||||
|
PuTTY:
|
||||||
|
|
||||||
|
**[PuTTY Download Link](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)**
|
||||||
|
|
||||||
|
You may use this client to connect to the VM by using ```vagrant/vagrant``` as your username/password, or by [using
|
||||||
|
PuTTYGen to import the insecure_private_key file](http://jason.sharonandjason.com/key_based_putty_logins_mini_how_to.htm)
|
||||||
|
(mentioned above) into a PuTTY profile to quickly access your VM.
|
||||||
|
|
||||||
### Keeping your VM up to date
|
### Keeping your VM up to date
|
||||||
|
|
||||||
Now you're in a virtual machine is almost ready to start developing. It's a good idea to perform the following instructions
|
Now you're in a virtual machine is almost ready to start developing. It's a good idea to perform the following instructions
|
||||||
|
@ -51,7 +73,7 @@ Once your VM is up to date, you can start a rails instance using the following c
|
||||||
bundle exec rails server
|
bundle exec rails server
|
||||||
```
|
```
|
||||||
|
|
||||||
In a few seconds, rails will start serving pages. To access them, open a web browser to http://localhost:4000 - if it all worked you should see discourse! Congratulations, you are ready to start working!
|
In a few seconds, rails will start serving pages. To access them, open a web browser to [http://localhost:4000](http://localhost:4000) - if it all worked you should see discourse! Congratulations, you are ready to start working!
|
||||||
|
|
||||||
You can now edit files on your local file system, using your favorite text editor or IDE. When you reload your web browser, it should have the latest changes.
|
You can now edit files on your local file system, using your favorite text editor or IDE. When you reload your web browser, it should have the latest changes.
|
||||||
|
|
||||||
|
@ -87,7 +109,7 @@ To start mailcatcher, run the following command in the vagrant image:
|
||||||
mailcatcher --http-ip 0.0.0.0
|
mailcatcher --http-ip 0.0.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Then in a browser, go to http://localhost:4080
|
Then in a browser, go to [http://localhost:4080](http://localhost:4080)
|
||||||
|
|
||||||
Sent emails will be received by mailcatcher and shown in its web ui.
|
Sent emails will be received by mailcatcher and shown in its web ui.
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,18 @@ module Discourse
|
||||||
!!$redis.get( maintenance_mode_key )
|
!!$redis.get( maintenance_mode_key )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.git_version
|
||||||
|
return $git_version if $git_version
|
||||||
|
f = Rails.root.to_s + "/config/version"
|
||||||
|
require f if File.exists?("#{f}.rb")
|
||||||
|
|
||||||
|
begin
|
||||||
|
$git_version ||= `git rev-parse HEAD`.strip
|
||||||
|
rescue
|
||||||
|
$git_version = "unknown"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ module Oneboxer
|
||||||
end
|
end
|
||||||
(doc/"link[@type='text/json+oembed']").each do |oembed|
|
(doc/"link[@type='text/json+oembed']").each do |oembed|
|
||||||
return OembedOnebox.new(oembed[:href]).onebox
|
return OembedOnebox.new(oembed[:href]).onebox
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for opengraph
|
# Check for opengraph
|
||||||
open_graph = Oneboxer.parse_open_graph(doc)
|
open_graph = Oneboxer.parse_open_graph(doc)
|
||||||
|
@ -50,7 +50,9 @@ module Oneboxer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
nil
|
nil
|
||||||
|
rescue OpenURI::HTTPError
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parse URLs out of HTML, returning the document when finished.
|
# Parse URLs out of HTML, returning the document when finished.
|
||||||
|
|
|
@ -8,6 +8,11 @@ module Oneboxer
|
||||||
node = doc.at("/html/head/meta[@property='og:#{prop}']")
|
node = doc.at("/html/head/meta[@property='og:#{prop}']")
|
||||||
result[prop] = (node['content'] || node['value']) if node
|
result[prop] = (node['content'] || node['value']) if node
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If there's no title, try using the page's title
|
||||||
|
if result['title'].blank?
|
||||||
|
result['title'] = doc.title
|
||||||
|
end
|
||||||
|
|
||||||
# If there's no description, try and get one from the meta tags
|
# If there's no description, try and get one from the meta tags
|
||||||
if result['description'].blank?
|
if result['description'].blank?
|
||||||
|
|
|
@ -100,7 +100,7 @@ module Search
|
||||||
|
|
||||||
db_result = []
|
db_result = []
|
||||||
[user_query_sql, category_query_sql, topic_query_sql].each do |sql|
|
[user_query_sql, category_query_sql, topic_query_sql].each do |sql|
|
||||||
sql << " LIMIT " << Search.per_facet.to_s
|
sql << " LIMIT " << (Search.per_facet + 1).to_s
|
||||||
db_result += ActiveRecord::Base.exec_sql(sql , query: terms.join(" & ")).to_a
|
db_result += ActiveRecord::Base.exec_sql(sql , query: terms.join(" & ")).to_a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -157,9 +157,12 @@ module Search
|
||||||
end
|
end
|
||||||
|
|
||||||
result = grouped.map do |type, results|
|
result = grouped.map do |type, results|
|
||||||
|
more = type_filter.blank? && (results.size > Search.per_facet)
|
||||||
|
results = results[0..([results.length, Search.per_facet].min - 1)] if type_filter.blank?
|
||||||
|
|
||||||
{type: type,
|
{type: type,
|
||||||
name: I18n.t("search.types.#{type}"),
|
name: I18n.t("search.types.#{type}"),
|
||||||
more: type_filter.blank? && (results.size == Search.per_facet),
|
more: more,
|
||||||
results: results}
|
results: results}
|
||||||
end
|
end
|
||||||
result
|
result
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
desc "stamp the current build with the git hash placed in version.rb"
|
||||||
|
task "build:stamp" => :environment do
|
||||||
|
git_version = `git rev-parse HEAD`.strip
|
||||||
|
File.open(Rails.root.to_s + '/config/version.rb', 'w') do |f|
|
||||||
|
f.write("$git_version = #{git_version.inspect}\n")
|
||||||
|
end
|
||||||
|
puts "Stamped current build with #{git_version}"
|
||||||
|
end
|
|
@ -1,5 +1,3 @@
|
||||||
require 'iconv'
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Given a string, tell us whether or not is acceptable. Also, remove stuff we don't like
|
# Given a string, tell us whether or not is acceptable. Also, remove stuff we don't like
|
||||||
# such as leading / trailing space.
|
# such as leading / trailing space.
|
||||||
|
@ -13,13 +11,10 @@ class TextSentinel
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(text, opts=nil)
|
def initialize(text, opts=nil)
|
||||||
if text.present?
|
|
||||||
@text = Iconv.new('UTF-8//IGNORE', 'UTF-8').iconv(text.dup)
|
|
||||||
end
|
|
||||||
|
|
||||||
@opts = opts || {}
|
@opts = opts || {}
|
||||||
|
|
||||||
if @text.present?
|
if text.present?
|
||||||
|
@text = text.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
||||||
@text.gsub!(/ +/m, ' ') if @opts[:remove_interior_spaces]
|
@text.gsub!(/ +/m, ' ') if @opts[:remove_interior_spaces]
|
||||||
@text.strip! if @opts[:strip]
|
@text.strip! if @opts[:strip]
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'text_sentinel'
|
require 'text_sentinel'
|
||||||
require 'iconv'
|
|
||||||
|
|
||||||
describe TextSentinel do
|
describe TextSentinel do
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,10 @@ Fabricator(:topic) do
|
||||||
title { sequence(:title) { |i| "Test topic #{i}" } }
|
title { sequence(:title) { |i| "Test topic #{i}" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Fabricator(:deleted_topic, from: :topic) do
|
||||||
|
deleted_at Time.now
|
||||||
|
end
|
||||||
|
|
||||||
Fabricator(:topic_allowed_user) do
|
Fabricator(:topic_allowed_user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
describe "Discourse.Onebox", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
spyOn($, 'ajax').andCallThrough()
|
||||||
|
|
||||||
|
it "Stops rapid calls with cache true", ->
|
||||||
|
Discourse.Onebox.lookup('http://bla.com', true, (c) -> c)
|
||||||
|
Discourse.Onebox.lookup('http://bla.com', true, (c) -> c)
|
||||||
|
expect($.ajax.calls.length).toBe(1)
|
||||||
|
|
||||||
|
it "Stops rapid calls with cache false", ->
|
||||||
|
Discourse.Onebox.lookup('http://bla.com/a', false, (c) -> c)
|
||||||
|
Discourse.Onebox.lookup('http://bla.com/a', false, (c) -> c)
|
||||||
|
expect($.ajax.calls.length).toBe(1)
|
|
@ -157,31 +157,57 @@ describe Category do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'update_stats' do
|
describe 'update_stats' do
|
||||||
|
|
||||||
# We're going to test with one topic. That's enough for stats!
|
|
||||||
before do
|
before do
|
||||||
@category = Fabricate(:category)
|
@category = Fabricate(:category)
|
||||||
|
|
||||||
# Create a non-invisible category to make sure count is 1
|
|
||||||
@topic = Fabricate(:topic, user: @category.user, category: @category)
|
|
||||||
|
|
||||||
Category.update_stats
|
|
||||||
@category.reload
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with regular topics' do
|
||||||
|
|
||||||
it 'updates topics_week' do
|
before do
|
||||||
@category.topics_week.should == 1
|
@category.topics << Fabricate(:topic,
|
||||||
|
user: @category.user)
|
||||||
|
Category.update_stats
|
||||||
|
@category.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates topics_week' do
|
||||||
|
@category.topics_week.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates topics_month' do
|
||||||
|
@category.topics_month.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates topics_year' do
|
||||||
|
@category.topics_year.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with deleted topics' do
|
||||||
|
|
||||||
it 'updates topics_month' do
|
before do
|
||||||
@category.topics_month.should == 1
|
@category.topics << Fabricate(:deleted_topic,
|
||||||
end
|
user: @category.user)
|
||||||
|
Category.update_stats
|
||||||
|
@category.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not count deleted topics for topics_week' do
|
||||||
|
@category.topics_week.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not count deleted topics for topics_month' do
|
||||||
|
@category.topics_month.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not count deleted topics for topics_year' do
|
||||||
|
@category.topics_year.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
it 'updates topics_year' do
|
|
||||||
@category.topics_year.should == 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ describe SiteCustomization do
|
||||||
|
|
||||||
it 'should allow me to lookup a filename containing my preview stylesheet' do
|
it 'should allow me to lookup a filename containing my preview stylesheet' do
|
||||||
SiteCustomization.custom_stylesheet(customization.key).should ==
|
SiteCustomization.custom_stylesheet(customization.key).should ==
|
||||||
"<link class=\"custom-css\" rel=\"stylesheet\" href=\"/stylesheet-cache/#{customization.key}.css?#{customization.stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
|
"<link class=\"custom-css\" rel=\"stylesheet\" href=\"/uploads/stylesheet-cache/#{customization.key}.css?#{customization.stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fix stylesheet files after changing the stylesheet' do
|
it 'should fix stylesheet files after changing the stylesheet' do
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
|
|
||||||
window.App = Ember.Application.createWithMixins({
|
window.App = Ember.Application.createWithMixins({
|
||||||
start: function(){
|
start: function(){
|
||||||
MessageBus.start();
|
MessageBus.start();
|
||||||
|
@ -10,12 +9,22 @@ window.App.start();
|
||||||
|
|
||||||
App.IndexRoute = Ember.Route.extend({
|
App.IndexRoute = Ember.Route.extend({
|
||||||
setupController: function(controller) {
|
setupController: function(controller) {
|
||||||
controller.set('content', App.IndexModel.create());
|
model = App.IndexModel.create();
|
||||||
|
model.ensureSubscribed();
|
||||||
|
controller.set('content', model);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App.IndexView = Ember.View.extend({
|
App.IndexView = Ember.View.extend({});
|
||||||
|
|
||||||
|
App.Process = Ember.View.extend({
|
||||||
|
uniqueId: function(){
|
||||||
|
return this.get('hostname') + this.get('pid');
|
||||||
|
}.property('hostname', 'pid'),
|
||||||
|
|
||||||
|
hup: function(){
|
||||||
|
$.post("/message-bus/_diagnostics/hup/" + this.get('hostname') + "/" + this.get('pid'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App.IndexModel = Ember.Object.extend({
|
App.IndexModel = Ember.Object.extend({
|
||||||
|
@ -23,20 +32,38 @@ App.IndexModel = Ember.Object.extend({
|
||||||
return this.get("discovering") ? "disabled" : null;
|
return this.get("discovering") ? "disabled" : null;
|
||||||
}.property("discovering"),
|
}.property("discovering"),
|
||||||
|
|
||||||
|
ensureSubscribed: function() {
|
||||||
|
var processes;
|
||||||
|
var _this = this;
|
||||||
|
if(this.get("subscribed")) { return; }
|
||||||
|
|
||||||
|
MessageBus.callbackInterval = 500;
|
||||||
|
MessageBus.subscribe("/_diagnostics/process-discovery", function(data){
|
||||||
|
processes = _this.get('processes');
|
||||||
|
processes.pushObject(App.Process.create(data));
|
||||||
|
processes = processes.sort(function(a,b){
|
||||||
|
return a.get('uniqueId') < b.get('uniqueId') ? -1 : 1;
|
||||||
|
});
|
||||||
|
// somewhat odd ...
|
||||||
|
_this.set('processes', null);
|
||||||
|
_this.set('processes', processes);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.set("subscribed", true);
|
||||||
|
},
|
||||||
|
|
||||||
discover: function(){
|
discover: function(){
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
this.set('processes', Em.A());
|
||||||
|
|
||||||
|
this.ensureSubscribed();
|
||||||
|
|
||||||
this.set("discovering", true);
|
this.set("discovering", true);
|
||||||
Ember.run.later(function(){
|
Ember.run.later(function(){
|
||||||
_this.set("discovering", false);
|
_this.set("discovering", false);
|
||||||
}, 20 * 1000);
|
}, 1 * 1000);
|
||||||
|
|
||||||
$.post("/message-bus/_diagnostics/discover");
|
$.post("/message-bus/_diagnostics/discover");
|
||||||
|
|
||||||
MessageBus.subscribe("/process-discovery", function(data){
|
|
||||||
console.log(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,5 +71,9 @@ App.IndexModel = Ember.Object.extend({
|
||||||
App.IndexController = Ember.ObjectController.extend({
|
App.IndexController = Ember.ObjectController.extend({
|
||||||
discover: function(){
|
discover: function(){
|
||||||
this.get("content").discover();
|
this.get("content").discover();
|
||||||
|
},
|
||||||
|
|
||||||
|
hup: function(process) {
|
||||||
|
process.hup();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,27 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (C) 2011 by Yehuda Katz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
// lib/handlebars/base.js
|
// lib/handlebars/base.js
|
||||||
|
|
||||||
/*jshint eqnull:true*/
|
/*jshint eqnull:true*/
|
||||||
|
@ -5,7 +29,13 @@ this.Handlebars = {};
|
||||||
|
|
||||||
(function(Handlebars) {
|
(function(Handlebars) {
|
||||||
|
|
||||||
Handlebars.VERSION = "1.0.rc.2";
|
Handlebars.VERSION = "1.0.0-rc.3";
|
||||||
|
Handlebars.COMPILER_REVISION = 2;
|
||||||
|
|
||||||
|
Handlebars.REVISION_CHANGES = {
|
||||||
|
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
||||||
|
2: '>= 1.0.0-rc.3'
|
||||||
|
};
|
||||||
|
|
||||||
Handlebars.helpers = {};
|
Handlebars.helpers = {};
|
||||||
Handlebars.partials = {};
|
Handlebars.partials = {};
|
||||||
|
@ -618,9 +648,13 @@ return new Parser;
|
||||||
// lib/handlebars/compiler/base.js
|
// lib/handlebars/compiler/base.js
|
||||||
Handlebars.Parser = handlebars;
|
Handlebars.Parser = handlebars;
|
||||||
|
|
||||||
Handlebars.parse = function(string) {
|
Handlebars.parse = function(input) {
|
||||||
|
|
||||||
|
// Just return if an already-compile AST was passed in.
|
||||||
|
if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
|
||||||
|
|
||||||
Handlebars.Parser.yy = Handlebars.AST;
|
Handlebars.Parser.yy = Handlebars.AST;
|
||||||
return Handlebars.Parser.parse(string);
|
return Handlebars.Parser.parse(input);
|
||||||
};
|
};
|
||||||
|
|
||||||
Handlebars.print = function(ast) {
|
Handlebars.print = function(ast) {
|
||||||
|
@ -702,8 +736,11 @@ Handlebars.print = function(ast) {
|
||||||
for(var i=0,l=parts.length; i<l; i++) {
|
for(var i=0,l=parts.length; i<l; i++) {
|
||||||
var part = parts[i];
|
var part = parts[i];
|
||||||
|
|
||||||
if(part === "..") { depth++; }
|
if (part === ".." || part === "." || part === "this") {
|
||||||
else if(part === "." || part === "this") { this.isScoped = true; }
|
if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
|
||||||
|
else if (part === "..") { depth++; }
|
||||||
|
else { this.isScoped = true; }
|
||||||
|
}
|
||||||
else { dig.push(part); }
|
else { dig.push(part); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -853,6 +890,26 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
|
|
||||||
return out.join("\n");
|
return out.join("\n");
|
||||||
},
|
},
|
||||||
|
equals: function(other) {
|
||||||
|
var len = this.opcodes.length;
|
||||||
|
if (other.opcodes.length !== len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
var opcode = this.opcodes[i],
|
||||||
|
otherOpcode = other.opcodes[i];
|
||||||
|
if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var j = 0; j < opcode.args.length; j++) {
|
||||||
|
if (opcode.args[j] !== otherOpcode.args[j]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
guid: 0,
|
guid: 0,
|
||||||
|
|
||||||
|
@ -944,7 +1001,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
// evaluate it by executing `blockHelperMissing`
|
// evaluate it by executing `blockHelperMissing`
|
||||||
this.opcode('pushProgram', program);
|
this.opcode('pushProgram', program);
|
||||||
this.opcode('pushProgram', inverse);
|
this.opcode('pushProgram', inverse);
|
||||||
this.opcode('pushHash');
|
this.opcode('emptyHash');
|
||||||
this.opcode('blockValue');
|
this.opcode('blockValue');
|
||||||
} else {
|
} else {
|
||||||
this.ambiguousMustache(mustache, program, inverse);
|
this.ambiguousMustache(mustache, program, inverse);
|
||||||
|
@ -953,7 +1010,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
// evaluate it by executing `blockHelperMissing`
|
// evaluate it by executing `blockHelperMissing`
|
||||||
this.opcode('pushProgram', program);
|
this.opcode('pushProgram', program);
|
||||||
this.opcode('pushProgram', inverse);
|
this.opcode('pushProgram', inverse);
|
||||||
this.opcode('pushHash');
|
this.opcode('emptyHash');
|
||||||
this.opcode('ambiguousBlockValue');
|
this.opcode('ambiguousBlockValue');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -977,6 +1034,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
|
|
||||||
this.opcode('assignToHash', pair[0]);
|
this.opcode('assignToHash', pair[0]);
|
||||||
}
|
}
|
||||||
|
this.opcode('popHash');
|
||||||
},
|
},
|
||||||
|
|
||||||
partial: function(partial) {
|
partial: function(partial) {
|
||||||
|
@ -1017,17 +1075,19 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
},
|
},
|
||||||
|
|
||||||
ambiguousMustache: function(mustache, program, inverse) {
|
ambiguousMustache: function(mustache, program, inverse) {
|
||||||
var id = mustache.id, name = id.parts[0];
|
var id = mustache.id,
|
||||||
|
name = id.parts[0],
|
||||||
|
isBlock = program != null || inverse != null;
|
||||||
|
|
||||||
this.opcode('getContext', id.depth);
|
this.opcode('getContext', id.depth);
|
||||||
|
|
||||||
this.opcode('pushProgram', program);
|
this.opcode('pushProgram', program);
|
||||||
this.opcode('pushProgram', inverse);
|
this.opcode('pushProgram', inverse);
|
||||||
|
|
||||||
this.opcode('invokeAmbiguous', name);
|
this.opcode('invokeAmbiguous', name, isBlock);
|
||||||
},
|
},
|
||||||
|
|
||||||
simpleMustache: function(mustache, program, inverse) {
|
simpleMustache: function(mustache) {
|
||||||
var id = mustache.id;
|
var id = mustache.id;
|
||||||
|
|
||||||
if (id.type === 'DATA') {
|
if (id.type === 'DATA') {
|
||||||
|
@ -1158,7 +1218,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
if(mustache.hash) {
|
if(mustache.hash) {
|
||||||
this.hash(mustache.hash);
|
this.hash(mustache.hash);
|
||||||
} else {
|
} else {
|
||||||
this.opcode('pushHash');
|
this.opcode('emptyHash');
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
|
@ -1175,7 +1235,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
if(mustache.hash) {
|
if(mustache.hash) {
|
||||||
this.hash(mustache.hash);
|
this.hash(mustache.hash);
|
||||||
} else {
|
} else {
|
||||||
this.opcode('pushHash');
|
this.opcode('emptyHash');
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
|
@ -1189,7 +1249,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
JavaScriptCompiler.prototype = {
|
JavaScriptCompiler.prototype = {
|
||||||
// PUBLIC API: You can override these methods in a subclass to provide
|
// PUBLIC API: You can override these methods in a subclass to provide
|
||||||
// alternative compiled forms for name lookup and buffering semantics
|
// alternative compiled forms for name lookup and buffering semantics
|
||||||
nameLookup: function(parent, name, type) {
|
nameLookup: function(parent, name /* , type*/) {
|
||||||
if (/^[0-9]+$/.test(name)) {
|
if (/^[0-9]+$/.test(name)) {
|
||||||
return parent + "[" + name + "]";
|
return parent + "[" + name + "]";
|
||||||
} else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
} else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
||||||
|
@ -1204,7 +1264,11 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
if (this.environment.isSimple) {
|
if (this.environment.isSimple) {
|
||||||
return "return " + string + ";";
|
return "return " + string + ";";
|
||||||
} else {
|
} else {
|
||||||
return "buffer += " + string + ";";
|
return {
|
||||||
|
appendToBuffer: true,
|
||||||
|
content: string,
|
||||||
|
toString: function() { return "buffer += " + string + ";"; }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1225,6 +1289,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
this.isChild = !!context;
|
this.isChild = !!context;
|
||||||
this.context = context || {
|
this.context = context || {
|
||||||
programs: [],
|
programs: [],
|
||||||
|
environments: [],
|
||||||
aliases: { }
|
aliases: { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1234,6 +1299,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
this.stackVars = [];
|
this.stackVars = [];
|
||||||
this.registers = { list: [] };
|
this.registers = { list: [] };
|
||||||
this.compileStack = [];
|
this.compileStack = [];
|
||||||
|
this.inlineStack = [];
|
||||||
|
|
||||||
this.compileChildren(environment, options);
|
this.compileChildren(environment, options);
|
||||||
|
|
||||||
|
@ -1255,11 +1321,11 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
},
|
},
|
||||||
|
|
||||||
nextOpcode: function() {
|
nextOpcode: function() {
|
||||||
var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
|
var opcodes = this.environment.opcodes;
|
||||||
return opcodes[this.i + 1];
|
return opcodes[this.i + 1];
|
||||||
},
|
},
|
||||||
|
|
||||||
eat: function(opcode) {
|
eat: function() {
|
||||||
this.i = this.i + 1;
|
this.i = this.i + 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1297,7 +1363,6 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
|
|
||||||
// Generate minimizer alias mappings
|
// Generate minimizer alias mappings
|
||||||
if (!this.isChild) {
|
if (!this.isChild) {
|
||||||
var aliases = [];
|
|
||||||
for (var alias in this.context.aliases) {
|
for (var alias in this.context.aliases) {
|
||||||
this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
|
this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
|
||||||
}
|
}
|
||||||
|
@ -1322,16 +1387,48 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
params.push("depth" + this.environment.depths.list[i]);
|
params.push("depth" + this.environment.depths.list[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Perform a second pass over the output to merge content when possible
|
||||||
|
var source = this.mergeSource();
|
||||||
|
|
||||||
|
if (!this.isChild) {
|
||||||
|
var revision = Handlebars.COMPILER_REVISION,
|
||||||
|
versions = Handlebars.REVISION_CHANGES[revision];
|
||||||
|
source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
|
||||||
|
}
|
||||||
|
|
||||||
if (asObject) {
|
if (asObject) {
|
||||||
params.push(this.source.join("\n "));
|
params.push(source);
|
||||||
|
|
||||||
return Function.apply(this, params);
|
return Function.apply(this, params);
|
||||||
} else {
|
} else {
|
||||||
var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
|
var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
|
||||||
Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
|
Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
|
||||||
return functionSource;
|
return functionSource;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mergeSource: function() {
|
||||||
|
// WARN: We are not handling the case where buffer is still populated as the source should
|
||||||
|
// not have buffer append operations as their final action.
|
||||||
|
var source = '',
|
||||||
|
buffer;
|
||||||
|
for (var i = 0, len = this.source.length; i < len; i++) {
|
||||||
|
var line = this.source[i];
|
||||||
|
if (line.appendToBuffer) {
|
||||||
|
if (buffer) {
|
||||||
|
buffer = buffer + '\n + ' + line.content;
|
||||||
|
} else {
|
||||||
|
buffer = line.content;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (buffer) {
|
||||||
|
source += 'buffer += ' + buffer + ';\n ';
|
||||||
|
buffer = undefined;
|
||||||
|
}
|
||||||
|
source += line + '\n ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
},
|
||||||
|
|
||||||
// [blockValue]
|
// [blockValue]
|
||||||
//
|
//
|
||||||
|
@ -1369,6 +1466,9 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
var current = this.topStack();
|
var current = this.topStack();
|
||||||
params.splice(1, 0, current);
|
params.splice(1, 0, current);
|
||||||
|
|
||||||
|
// Use the options value generated from the invocation
|
||||||
|
params[params.length-1] = 'options';
|
||||||
|
|
||||||
this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
|
this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1392,6 +1492,9 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
// If `value` is truthy, or 0, it is coerced into a string and appended
|
// If `value` is truthy, or 0, it is coerced into a string and appended
|
||||||
// Otherwise, the empty string is appended
|
// Otherwise, the empty string is appended
|
||||||
append: function() {
|
append: function() {
|
||||||
|
// Force anything that is inlined onto the stack so we don't have duplication
|
||||||
|
// when we examine local
|
||||||
|
this.flushInline();
|
||||||
var local = this.popStack();
|
var local = this.popStack();
|
||||||
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
|
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
|
||||||
if (this.environment.isSimple) {
|
if (this.environment.isSimple) {
|
||||||
|
@ -1406,15 +1509,9 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
//
|
//
|
||||||
// Escape `value` and append it to the buffer
|
// Escape `value` and append it to the buffer
|
||||||
appendEscaped: function() {
|
appendEscaped: function() {
|
||||||
var opcode = this.nextOpcode(), extra = "";
|
|
||||||
this.context.aliases.escapeExpression = 'this.escapeExpression';
|
this.context.aliases.escapeExpression = 'this.escapeExpression';
|
||||||
|
|
||||||
if(opcode && opcode.opcode === 'appendContent') {
|
this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
|
||||||
extra = " + " + this.quotedString(opcode.args[0]);
|
|
||||||
this.eat(opcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// [getContext]
|
// [getContext]
|
||||||
|
@ -1438,7 +1535,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
// Looks up the value of `name` on the current context and pushes
|
// Looks up the value of `name` on the current context and pushes
|
||||||
// it onto the stack.
|
// it onto the stack.
|
||||||
lookupOnContext: function(name) {
|
lookupOnContext: function(name) {
|
||||||
this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
|
this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
|
||||||
},
|
},
|
||||||
|
|
||||||
// [pushContext]
|
// [pushContext]
|
||||||
|
@ -1486,7 +1583,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
//
|
//
|
||||||
// Push the result of looking up `id` on the current data
|
// Push the result of looking up `id` on the current data
|
||||||
lookupData: function(id) {
|
lookupData: function(id) {
|
||||||
this.pushStack(this.nameLookup('data', id, 'data'));
|
this.push(this.nameLookup('data', id, 'data'));
|
||||||
},
|
},
|
||||||
|
|
||||||
// [pushStringParam]
|
// [pushStringParam]
|
||||||
|
@ -1509,13 +1606,25 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
pushHash: function() {
|
emptyHash: function() {
|
||||||
this.push('{}');
|
this.pushStackLiteral('{}');
|
||||||
|
|
||||||
if (this.options.stringParams) {
|
if (this.options.stringParams) {
|
||||||
this.register('hashTypes', '{}');
|
this.register('hashTypes', '{}');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
pushHash: function() {
|
||||||
|
this.hash = {values: [], types: []};
|
||||||
|
},
|
||||||
|
popHash: function() {
|
||||||
|
var hash = this.hash;
|
||||||
|
this.hash = undefined;
|
||||||
|
|
||||||
|
if (this.options.stringParams) {
|
||||||
|
this.register('hashTypes', '{' + hash.types.join(',') + '}');
|
||||||
|
}
|
||||||
|
this.push('{\n ' + hash.values.join(',\n ') + '\n }');
|
||||||
|
},
|
||||||
|
|
||||||
// [pushString]
|
// [pushString]
|
||||||
//
|
//
|
||||||
|
@ -1534,7 +1643,8 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
//
|
//
|
||||||
// Push an expression onto the stack
|
// Push an expression onto the stack
|
||||||
push: function(expr) {
|
push: function(expr) {
|
||||||
this.pushStack(expr);
|
this.inlineStack.push(expr);
|
||||||
|
return expr;
|
||||||
},
|
},
|
||||||
|
|
||||||
// [pushLiteral]
|
// [pushLiteral]
|
||||||
|
@ -1577,12 +1687,14 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
invokeHelper: function(paramSize, name) {
|
invokeHelper: function(paramSize, name) {
|
||||||
this.context.aliases.helperMissing = 'helpers.helperMissing';
|
this.context.aliases.helperMissing = 'helpers.helperMissing';
|
||||||
|
|
||||||
var helper = this.lastHelper = this.setupHelper(paramSize, name);
|
var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
|
||||||
this.register('foundHelper', helper.name);
|
|
||||||
|
|
||||||
this.pushStack("foundHelper ? foundHelper.call(" +
|
this.push(helper.name);
|
||||||
helper.callParams + ") " + ": helperMissing.call(" +
|
this.replaceStack(function(name) {
|
||||||
helper.helperMissingParams + ")");
|
return name + ' ? ' + name + '.call(' +
|
||||||
|
helper.callParams + ") " + ": helperMissing.call(" +
|
||||||
|
helper.helperMissingParams + ")";
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// [invokeKnownHelper]
|
// [invokeKnownHelper]
|
||||||
|
@ -1594,7 +1706,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
// so a `helperMissing` fallback is not required.
|
// so a `helperMissing` fallback is not required.
|
||||||
invokeKnownHelper: function(paramSize, name) {
|
invokeKnownHelper: function(paramSize, name) {
|
||||||
var helper = this.setupHelper(paramSize, name);
|
var helper = this.setupHelper(paramSize, name);
|
||||||
this.pushStack(helper.name + ".call(" + helper.callParams + ")");
|
this.push(helper.name + ".call(" + helper.callParams + ")");
|
||||||
},
|
},
|
||||||
|
|
||||||
// [invokeAmbiguous]
|
// [invokeAmbiguous]
|
||||||
|
@ -1609,19 +1721,18 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
// This operation emits more code than the other options,
|
// This operation emits more code than the other options,
|
||||||
// and can be avoided by passing the `knownHelpers` and
|
// and can be avoided by passing the `knownHelpers` and
|
||||||
// `knownHelpersOnly` flags at compile-time.
|
// `knownHelpersOnly` flags at compile-time.
|
||||||
invokeAmbiguous: function(name) {
|
invokeAmbiguous: function(name, helperCall) {
|
||||||
this.context.aliases.functionType = '"function"';
|
this.context.aliases.functionType = '"function"';
|
||||||
|
|
||||||
this.pushStackLiteral('{}');
|
this.pushStackLiteral('{}'); // Hash value
|
||||||
var helper = this.setupHelper(0, name);
|
var helper = this.setupHelper(0, name, helperCall);
|
||||||
|
|
||||||
var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
|
var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
|
||||||
this.register('foundHelper', helperName);
|
|
||||||
|
|
||||||
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
|
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
|
||||||
var nextStack = this.nextStack();
|
var nextStack = this.nextStack();
|
||||||
|
|
||||||
this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
|
this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
|
||||||
this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
|
this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1640,7 +1751,7 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.context.aliases.self = "this";
|
this.context.aliases.self = "this";
|
||||||
this.pushStack("self.invokePartial(" + params.join(", ") + ")");
|
this.push("self.invokePartial(" + params.join(", ") + ")");
|
||||||
},
|
},
|
||||||
|
|
||||||
// [assignToHash]
|
// [assignToHash]
|
||||||
|
@ -1651,17 +1762,19 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
// Pops a value and hash off the stack, assigns `hash[key] = value`
|
// Pops a value and hash off the stack, assigns `hash[key] = value`
|
||||||
// and pushes the hash back onto the stack.
|
// and pushes the hash back onto the stack.
|
||||||
assignToHash: function(key) {
|
assignToHash: function(key) {
|
||||||
var value = this.popStack();
|
var value = this.popStack(),
|
||||||
|
type;
|
||||||
|
|
||||||
if (this.options.stringParams) {
|
if (this.options.stringParams) {
|
||||||
var type = this.popStack();
|
type = this.popStack();
|
||||||
this.popStack();
|
this.popStack();
|
||||||
this.source.push("hashTypes['" + key + "'] = " + type + ";");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = this.topStack();
|
var hash = this.hash;
|
||||||
|
if (type) {
|
||||||
this.source.push(hash + "['" + key + "'] = " + value + ";");
|
hash.types.push("'" + key + "': " + type);
|
||||||
|
}
|
||||||
|
hash.values.push("'" + key + "': (" + value + ")");
|
||||||
},
|
},
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
@ -1675,11 +1788,27 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
child = children[i];
|
child = children[i];
|
||||||
compiler = new this.compiler();
|
compiler = new this.compiler();
|
||||||
|
|
||||||
this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
|
var index = this.matchExistingProgram(child);
|
||||||
var index = this.context.programs.length;
|
|
||||||
child.index = index;
|
if (index == null) {
|
||||||
child.name = 'program' + index;
|
this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
|
||||||
this.context.programs[index] = compiler.compile(child, options, this.context);
|
index = this.context.programs.length;
|
||||||
|
child.index = index;
|
||||||
|
child.name = 'program' + index;
|
||||||
|
this.context.programs[index] = compiler.compile(child, options, this.context);
|
||||||
|
this.context.environments[index] = child;
|
||||||
|
} else {
|
||||||
|
child.index = index;
|
||||||
|
child.name = 'program' + index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
matchExistingProgram: function(child) {
|
||||||
|
for (var i = 0, len = this.context.environments.length; i < len; i++) {
|
||||||
|
var environment = this.context.environments[i];
|
||||||
|
if (environment && environment.equals(child)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1723,57 +1852,111 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
},
|
},
|
||||||
|
|
||||||
pushStackLiteral: function(item) {
|
pushStackLiteral: function(item) {
|
||||||
this.compileStack.push(new Literal(item));
|
return this.push(new Literal(item));
|
||||||
return item;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
pushStack: function(item) {
|
pushStack: function(item) {
|
||||||
|
this.flushInline();
|
||||||
|
|
||||||
var stack = this.incrStack();
|
var stack = this.incrStack();
|
||||||
this.source.push(stack + " = " + item + ";");
|
if (item) {
|
||||||
|
this.source.push(stack + " = " + item + ";");
|
||||||
|
}
|
||||||
this.compileStack.push(stack);
|
this.compileStack.push(stack);
|
||||||
return stack;
|
return stack;
|
||||||
},
|
},
|
||||||
|
|
||||||
replaceStack: function(callback) {
|
replaceStack: function(callback) {
|
||||||
var stack = this.topStack(),
|
var prefix = '',
|
||||||
item = callback.call(this, stack);
|
inline = this.isInline(),
|
||||||
|
stack;
|
||||||
|
|
||||||
// Prevent modification of the context depth variable. Through replaceStack
|
// If we are currently inline then we want to merge the inline statement into the
|
||||||
if (/^depth/.test(stack)) {
|
// replacement statement via ','
|
||||||
stack = this.nextStack();
|
if (inline) {
|
||||||
|
var top = this.popStack(true);
|
||||||
|
|
||||||
|
if (top instanceof Literal) {
|
||||||
|
// Literals do not need to be inlined
|
||||||
|
stack = top.value;
|
||||||
|
} else {
|
||||||
|
// Get or create the current stack name for use by the inline
|
||||||
|
var name = this.stackSlot ? this.topStackName() : this.incrStack();
|
||||||
|
|
||||||
|
prefix = '(' + this.push(name) + ' = ' + top + '),';
|
||||||
|
stack = this.topStack();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stack = this.topStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.source.push(stack + " = " + item + ";");
|
var item = callback.call(this, stack);
|
||||||
|
|
||||||
|
if (inline) {
|
||||||
|
if (this.inlineStack.length || this.compileStack.length) {
|
||||||
|
this.popStack();
|
||||||
|
}
|
||||||
|
this.push('(' + prefix + item + ')');
|
||||||
|
} else {
|
||||||
|
// Prevent modification of the context depth variable. Through replaceStack
|
||||||
|
if (!/^stack/.test(stack)) {
|
||||||
|
stack = this.nextStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.source.push(stack + " = (" + prefix + item + ");");
|
||||||
|
}
|
||||||
return stack;
|
return stack;
|
||||||
},
|
},
|
||||||
|
|
||||||
nextStack: function(skipCompileStack) {
|
nextStack: function() {
|
||||||
var name = this.incrStack();
|
return this.pushStack();
|
||||||
this.compileStack.push(name);
|
|
||||||
return name;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
incrStack: function() {
|
incrStack: function() {
|
||||||
this.stackSlot++;
|
this.stackSlot++;
|
||||||
if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
|
if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
|
||||||
|
return this.topStackName();
|
||||||
|
},
|
||||||
|
topStackName: function() {
|
||||||
return "stack" + this.stackSlot;
|
return "stack" + this.stackSlot;
|
||||||
},
|
},
|
||||||
|
flushInline: function() {
|
||||||
|
var inlineStack = this.inlineStack;
|
||||||
|
if (inlineStack.length) {
|
||||||
|
this.inlineStack = [];
|
||||||
|
for (var i = 0, len = inlineStack.length; i < len; i++) {
|
||||||
|
var entry = inlineStack[i];
|
||||||
|
if (entry instanceof Literal) {
|
||||||
|
this.compileStack.push(entry);
|
||||||
|
} else {
|
||||||
|
this.pushStack(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isInline: function() {
|
||||||
|
return this.inlineStack.length;
|
||||||
|
},
|
||||||
|
|
||||||
popStack: function() {
|
popStack: function(wrapped) {
|
||||||
var item = this.compileStack.pop();
|
var inline = this.isInline(),
|
||||||
|
item = (inline ? this.inlineStack : this.compileStack).pop();
|
||||||
|
|
||||||
if (item instanceof Literal) {
|
if (!wrapped && (item instanceof Literal)) {
|
||||||
return item.value;
|
return item.value;
|
||||||
} else {
|
} else {
|
||||||
this.stackSlot--;
|
if (!inline) {
|
||||||
|
this.stackSlot--;
|
||||||
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
topStack: function() {
|
topStack: function(wrapped) {
|
||||||
var item = this.compileStack[this.compileStack.length - 1];
|
var stack = (this.isInline() ? this.inlineStack : this.compileStack),
|
||||||
|
item = stack[stack.length - 1];
|
||||||
|
|
||||||
if (item instanceof Literal) {
|
if (!wrapped && (item instanceof Literal)) {
|
||||||
return item.value;
|
return item.value;
|
||||||
} else {
|
} else {
|
||||||
return item;
|
return item;
|
||||||
|
@ -1788,22 +1971,22 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
.replace(/\r/g, '\\r') + '"';
|
.replace(/\r/g, '\\r') + '"';
|
||||||
},
|
},
|
||||||
|
|
||||||
setupHelper: function(paramSize, name) {
|
setupHelper: function(paramSize, name, missingParams) {
|
||||||
var params = [];
|
var params = [];
|
||||||
this.setupParams(paramSize, params);
|
this.setupParams(paramSize, params, missingParams);
|
||||||
var foundHelper = this.nameLookup('helpers', name, 'helper');
|
var foundHelper = this.nameLookup('helpers', name, 'helper');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
params: params,
|
params: params,
|
||||||
name: foundHelper,
|
name: foundHelper,
|
||||||
callParams: ["depth0"].concat(params).join(", "),
|
callParams: ["depth0"].concat(params).join(", "),
|
||||||
helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
|
helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
// the params and contexts arguments are passed in arrays
|
// the params and contexts arguments are passed in arrays
|
||||||
// to fill in
|
// to fill in
|
||||||
setupParams: function(paramSize, params) {
|
setupParams: function(paramSize, params, useRegister) {
|
||||||
var options = [], contexts = [], types = [], param, inverse, program;
|
var options = [], contexts = [], types = [], param, inverse, program;
|
||||||
|
|
||||||
options.push("hash:" + this.popStack());
|
options.push("hash:" + this.popStack());
|
||||||
|
@ -1848,7 +2031,13 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
options.push("data:data");
|
options.push("data:data");
|
||||||
}
|
}
|
||||||
|
|
||||||
params.push("{" + options.join(",") + "}");
|
options = "{" + options.join(",") + "}";
|
||||||
|
if (useRegister) {
|
||||||
|
this.register('options', options);
|
||||||
|
params.push('options');
|
||||||
|
} else {
|
||||||
|
params.push(options);
|
||||||
|
}
|
||||||
return params.join(", ");
|
return params.join(", ");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1886,23 +2075,23 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||||
|
|
||||||
})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
|
})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
|
||||||
|
|
||||||
Handlebars.precompile = function(string, options) {
|
Handlebars.precompile = function(input, options) {
|
||||||
if (typeof string !== 'string') {
|
if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
|
||||||
throw new Handlebars.Exception("You must pass a string to Handlebars.compile. You passed " + string);
|
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
|
||||||
}
|
}
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
if (!('data' in options)) {
|
if (!('data' in options)) {
|
||||||
options.data = true;
|
options.data = true;
|
||||||
}
|
}
|
||||||
var ast = Handlebars.parse(string);
|
var ast = Handlebars.parse(input);
|
||||||
var environment = new Handlebars.Compiler().compile(ast, options);
|
var environment = new Handlebars.Compiler().compile(ast, options);
|
||||||
return new Handlebars.JavaScriptCompiler().compile(environment, options);
|
return new Handlebars.JavaScriptCompiler().compile(environment, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Handlebars.compile = function(string, options) {
|
Handlebars.compile = function(input, options) {
|
||||||
if (typeof string !== 'string') {
|
if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
|
||||||
throw new Handlebars.Exception("You must pass a string to Handlebars.compile. You passed " + string);
|
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
|
||||||
}
|
}
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
@ -1911,7 +2100,7 @@ Handlebars.compile = function(string, options) {
|
||||||
}
|
}
|
||||||
var compiled;
|
var compiled;
|
||||||
function compile() {
|
function compile() {
|
||||||
var ast = Handlebars.parse(string);
|
var ast = Handlebars.parse(input);
|
||||||
var environment = new Handlebars.Compiler().compile(ast, options);
|
var environment = new Handlebars.Compiler().compile(ast, options);
|
||||||
var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
|
var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
|
||||||
return Handlebars.template(templateSpec);
|
return Handlebars.template(templateSpec);
|
||||||
|
@ -1946,12 +2135,32 @@ Handlebars.VM = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
programWithDepth: Handlebars.VM.programWithDepth,
|
programWithDepth: Handlebars.VM.programWithDepth,
|
||||||
noop: Handlebars.VM.noop
|
noop: Handlebars.VM.noop,
|
||||||
|
compilerInfo: null
|
||||||
};
|
};
|
||||||
|
|
||||||
return function(context, options) {
|
return function(context, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
|
var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
|
||||||
|
|
||||||
|
var compilerInfo = container.compilerInfo || [],
|
||||||
|
compilerRevision = compilerInfo[0] || 1,
|
||||||
|
currentRevision = Handlebars.COMPILER_REVISION;
|
||||||
|
|
||||||
|
if (compilerRevision !== currentRevision) {
|
||||||
|
if (compilerRevision < currentRevision) {
|
||||||
|
var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
|
||||||
|
compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
|
||||||
|
throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
|
||||||
|
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
|
||||||
|
} else {
|
||||||
|
// Use the embedded version info since the runtime doesn't know about this revision yet
|
||||||
|
throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
|
||||||
|
"Please update your runtime to a newer version ("+compilerInfo[1]+").";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1990,4 +2199,3 @@ Handlebars.VM = {
|
||||||
|
|
||||||
Handlebars.template = Handlebars.VM.template;
|
Handlebars.template = Handlebars.VM.template;
|
||||||
;
|
;
|
||||||
|
|
|
@ -1 +1,25 @@
|
||||||
<button {{action discover}} {{bindAttr disabled="disabled"}}>Discover Processes</button>
|
<button {{action discover}} {{bindAttr disabled="disabled"}}>Discover Processes</button>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>pid</td>
|
||||||
|
<td>full_path</td>
|
||||||
|
<td>hostname</td>
|
||||||
|
<td>uptime</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each processes}}
|
||||||
|
<tr>
|
||||||
|
<td>{{pid}}</td>
|
||||||
|
<td>{{full_path}}</td>
|
||||||
|
<td>{{hostname}}</td>
|
||||||
|
<td>{{uptime}} secs</td>
|
||||||
|
<td><button {{action hup this}}>HUP</button></td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
|
@ -30,6 +30,24 @@ window.MessageBus = (function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var processMessages = function(messages) {
|
||||||
|
failCount = 0;
|
||||||
|
$.each(messages,function(idx,message) {
|
||||||
|
gotData = true;
|
||||||
|
$.each(callbacks,function(idx,callback) {
|
||||||
|
if (callback.channel === message.channel) {
|
||||||
|
callback.last_id = message.message_id;
|
||||||
|
callback.func(message.data);
|
||||||
|
}
|
||||||
|
if (message.channel === "/__status") {
|
||||||
|
if (message.data[callback.channel] !== void 0) {
|
||||||
|
callback.last_id = message.data[callback.channel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
enableLongPolling: true,
|
enableLongPolling: true,
|
||||||
|
@ -42,7 +60,7 @@ window.MessageBus = (function() {
|
||||||
start: function(opts) {
|
start: function(opts) {
|
||||||
var poll,
|
var poll,
|
||||||
_this = this;
|
_this = this;
|
||||||
if (opts == null) {
|
if (opts === null) {
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
poll = function() {
|
poll = function() {
|
||||||
|
@ -52,7 +70,7 @@ window.MessageBus = (function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data = {};
|
data = {};
|
||||||
callbacks.each(function(c) {
|
$.each(callbacks, function(idx,c) {
|
||||||
return data[c.channel] = c.last_id === void 0 ? -1 : c.last_id;
|
return data[c.channel] = c.last_id === void 0 ? -1 : c.last_id;
|
||||||
});
|
});
|
||||||
gotData = false;
|
gotData = false;
|
||||||
|
@ -65,21 +83,7 @@ window.MessageBus = (function() {
|
||||||
'X-SILENCE-LOGGER': 'true'
|
'X-SILENCE-LOGGER': 'true'
|
||||||
},
|
},
|
||||||
success: function(messages) {
|
success: function(messages) {
|
||||||
failCount = 0;
|
processMessages(messages);
|
||||||
return messages.each(function(message) {
|
|
||||||
gotData = true;
|
|
||||||
return callbacks.each(function(callback) {
|
|
||||||
if (callback.channel === message.channel) {
|
|
||||||
callback.last_id = message.message_id;
|
|
||||||
callback.func(message.data);
|
|
||||||
}
|
|
||||||
if (message["channel"] === "/__status") {
|
|
||||||
if (message.data[callback.channel] !== void 0) {
|
|
||||||
callback.last_id = message.data[callback.channel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
error: failCount += 1,
|
error: failCount += 1,
|
||||||
complete: function() {
|
complete: function() {
|
||||||
|
|
|
@ -9,6 +9,7 @@ require "message_bus/reliable_pub_sub"
|
||||||
require "message_bus/client"
|
require "message_bus/client"
|
||||||
require "message_bus/connection_manager"
|
require "message_bus/connection_manager"
|
||||||
require "message_bus/message_handler"
|
require "message_bus/message_handler"
|
||||||
|
require "message_bus/diagnostics"
|
||||||
require "message_bus/rack/middleware"
|
require "message_bus/rack/middleware"
|
||||||
require "message_bus/rack/diagnostics"
|
require "message_bus/rack/diagnostics"
|
||||||
|
|
||||||
|
@ -125,6 +126,10 @@ module MessageBus::Implementation
|
||||||
@reliable_pub_sub ||= MessageBus::ReliablePubSub.new redis_config
|
@reliable_pub_sub ||= MessageBus::ReliablePubSub.new redis_config
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def enable_diagnostics
|
||||||
|
MessageBus::Diagnostics.enable
|
||||||
|
end
|
||||||
|
|
||||||
def publish(channel, data, opts = nil)
|
def publish(channel, data, opts = nil)
|
||||||
return if @off
|
return if @off
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
class MessageBus::Diagnostics
|
||||||
|
def self.full_process_path
|
||||||
|
begin
|
||||||
|
info = `ps -eo "%p|$|%a" | grep '^\\s*#{Process.pid}'`
|
||||||
|
info.strip.split('|$|')[1]
|
||||||
|
rescue
|
||||||
|
# skip it ... not linux or something weird
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.hostname
|
||||||
|
begin
|
||||||
|
`hostname`.strip
|
||||||
|
rescue
|
||||||
|
# skip it
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.enable
|
||||||
|
full_path = full_process_path
|
||||||
|
start_time = Time.now.to_f
|
||||||
|
hostname = self.hostname
|
||||||
|
|
||||||
|
# it may make sense to add a channel per machine/host to streamline
|
||||||
|
# process to process comms
|
||||||
|
MessageBus.subscribe('/_diagnostics/hup') do |msg|
|
||||||
|
if Process.pid == msg.data["pid"] && hostname == msg.data["hostname"]
|
||||||
|
$shutdown = true
|
||||||
|
sleep 4
|
||||||
|
Process.kill("HUP", $$)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MessageBus.subscribe('/_diagnostics/discover') do |msg|
|
||||||
|
MessageBus.on_connect.call msg.site_id if MessageBus.on_connect
|
||||||
|
MessageBus.publish '/_diagnostics/process-discovery', {
|
||||||
|
pid: Process.pid,
|
||||||
|
process_name: $0,
|
||||||
|
full_path: full_path,
|
||||||
|
uptime: (Time.now.to_f - start_time).to_i,
|
||||||
|
hostname: hostname
|
||||||
|
}, user_ids: [msg.data["user_id"]]
|
||||||
|
MessageBus.on_disconnect.call msg.site_id if MessageBus.on_disconnect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -38,7 +38,7 @@ class MessageBus::Rack::Diagnostics
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
#{js_asset "jquery-1.8.2.js"}
|
#{js_asset "jquery-1.8.2.js"}
|
||||||
#{js_asset "handlebars-1.0.rc.2.js"}
|
#{js_asset "handlebars.js"}
|
||||||
#{js_asset "ember.js"}
|
#{js_asset "ember.js"}
|
||||||
#{js_asset "message-bus.js"}
|
#{js_asset "message-bus.js"}
|
||||||
#{js_asset "application.handlebars"}
|
#{js_asset "application.handlebars"}
|
||||||
|
@ -71,8 +71,16 @@ HTML
|
||||||
|
|
||||||
return index unless route
|
return index unless route
|
||||||
|
|
||||||
if route == 'discover'
|
if route == '/discover'
|
||||||
MessageBus.publish('/discover', {user_id: MessageBus() })
|
user_id = MessageBus.user_id_lookup.call(env)
|
||||||
|
MessageBus.publish('/_diagnostics/discover', user_id: user_id)
|
||||||
|
return [200, {}, ['ok']]
|
||||||
|
end
|
||||||
|
|
||||||
|
if route =~ /^\/hup\//
|
||||||
|
hostname, pid = route.split('/hup/')[1].split('/')
|
||||||
|
MessageBus.publish('/_diagnostics/hup', {hostname: hostname, pid: pid.to_i})
|
||||||
|
return [200, {}, ['ok']]
|
||||||
end
|
end
|
||||||
|
|
||||||
asset = route.split('/assets/')[1]
|
asset = route.split('/assets/')[1]
|
||||||
|
|
|
@ -118,13 +118,13 @@ describe MessageBus::Rack::Middleware do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should get a 200 with html for an authorized user" do
|
it "should get a 200 with html for an authorized user" do
|
||||||
MessageBus.stub(:is_admin_lookup).and_return(lambda{ true })
|
MessageBus.stub(:is_admin_lookup).and_return(lambda{|env| true })
|
||||||
get "/message-bus/_diagnostics"
|
get "/message-bus/_diagnostics"
|
||||||
last_response.status.should == 200
|
last_response.status.should == 200
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should get the script it asks for" do
|
it "should get the script it asks for" do
|
||||||
MessageBus.stub(:is_admin_lookup).and_return(lambda{ true })
|
MessageBus.stub(:is_admin_lookup).and_return(lambda{|env| true })
|
||||||
get "/message-bus/_diagnostics/assets/message-bus.js"
|
get "/message-bus/_diagnostics/assets/message-bus.js"
|
||||||
last_response.status.should == 200
|
last_response.status.should == 200
|
||||||
last_response.content_type.should == "text/javascript;"
|
last_response.content_type.should == "text/javascript;"
|
||||||
|
|
Loading…
Reference in New Issue