diff --git a/doc/app/ActionView.html b/doc/app/ActionView.html new file mode 100644 index 0000000000..3926558742 --- /dev/null +++ b/doc/app/ActionView.html @@ -0,0 +1,95 @@ + + + +
+ + +The Active Record Helper makes it easier to create forms for records kept
+in instance variables. The most far-reaching is the form
+method that creates a complete form for all the basic content types of the
+record (not associations or aggregations, though). This is a great way of
+making the record quickly available for editing, but likely to prove
+lackluster for a complicated real-world form. In that case, it's better
+to use the input
method and the specialized form
+methods in classes/ActionView/Helpers/FormHelper.html
Returns a string containing the error message attached to the
+method
on the object
if one exists. This error
+message is wrapped in a DIV
tag by default or with
+:html_tag
if specified, which can be extended to include a
+:prepend_text
and/or :append_text
(to properly
+explain the error), and a :css_class
to style it accordingly.
+object
should either be the name of an instance variable or
+the actual object. The method can be passed in either as a string or a
+symbol. As an example, let's say you have a model @post
+that has an error message on the title
attribute:
<%= error_message_on "post", "title" %> +# => <div class="formError">can't be empty</div> + +<%= error_message_on @post, :title %> +# => <div class="formError">can't be empty</div> + +<%= error_message_on "post", "title", + :prepend_text => "Title simply ", + :append_text => " (or it won't work).", + :html_tag => "span", + :css_class => "inputError" %> +# => <span class="inputError">Title simply can't be empty (or it won't work).</span>+ + + + +
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 109 +def error_message_on(object, method, *args) + options = args.extract_options! + unless args.empty? + ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' + + 'prepend_text, append_text, html_tag, and css_class arguments', caller) + + options[:prepend_text] = args[0] || '' + options[:append_text] = args[1] || '' + options[:html_tag] = args[2] || 'div' + options[:css_class] = args[3] || 'formError' + end + options.reverse_merge!(:prepend_text => '', :append_text => '', :html_tag => 'div', :css_class => 'formError') + + object = convert_to_model(object) + + if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && + (errors = obj.errors[method]).presence + content_tag(options[:html_tag], + (options[:prepend_text].html_safe << errors.first).safe_concat(options[:append_text]), + :class => options[:css_class] + ) + else + '' + end +end+
Returns a string with a DIV
containing all of the error
+messages for the objects located as instance variables by the names given.
+If more than one object is specified, the errors for the objects are
+displayed in the order that the object names are provided.
This DIV
can be tailored by the following options:
:header_tag
- Used for the header of the error div (default:
+“h2”).
:id
- The id of the error div (default: “errorExplanation”).
:class
- The class of the error div (default:
+“errorExplanation”).
:object
- The object (or array of objects) for which to
+display errors, if you need to escape the instance variable convention.
:object_name
- The object name to use in the header, or any
+text that you prefer. If :object_name
is not set, the name of
+the first object will be used.
:header_message
- The message in the header of the error div.
+Pass nil
or an empty string to avoid the header message
+altogether. (Default: “X errors prohibited this object from being saved”).
:message
- The explanation message after the header message
+and before the error list. Pass nil
or an empty string to
+avoid the explanation message altogether. (Default: “There were problems
+with the following fields:”).
To specify the display for one object, you simply provide its name as a
+parameter. For example, for the @user
model:
error_messages_for 'user' ++ +
You can also supply an object:
+ +error_messages_for @user ++ +
This will use the last part of the model name in the presentation. For +instance, if this is a MyKlass::User object, this will use “user” as the +name in the String. This is taken from MyKlass::User.model_name.human, +which can be overridden.
+ +To specify more than one object, you simply list them; optionally, you can
+add an extra :object_name
parameter, which will be the name
+used in the header message:
error_messages_for 'user_common', 'user', :object_name => 'user' ++ +
You can also use a number of objects, which will have the same naming +semantics as a single object.
+ +error_messages_for @user, @post ++ +
If the objects cannot be located as instance variables, you can add an
+extra :object
parameter which gives the actual object (or
+array of objects to use):
error_messages_for 'user', :object => @question.user ++ +
NOTE: This is a pre-packaged presentation of the errors with embedded
+strings and a certain HTML structure. If what you need is significantly
+different from the default presentation, it makes plenty of sense to access
+the object.errors
instance yourself and set it up. View the
+source of this method to see how easy it is.
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 186 +def error_messages_for(*params) + options = params.extract_options!.symbolize_keys + + objects = Array.wrap(options.delete(:object) || params).map do |object| + object = instance_variable_get("@#{object}") unless object.respond_to?(:to_model) + object = convert_to_model(object) + + if object.class.respond_to?(:model_name) + options[:object_name] ||= object.class.model_name.human.downcase + end + + object + end + + objects.compact! + count = objects.inject(0) {|sum, object| sum + object.errors.count } + + unless count.zero? + html = {} + [:id, :class].each do |key| + if options.include?(key) + value = options[key] + html[key] = value unless value.blank? + else + html[key] = 'errorExplanation' + end + end + options[:object_name] ||= params.first + + I18n.with_options :locale => options[:locale], :scope => [:errors, :template] do |locale| + header_message = if options.include?(:header_message) + options[:header_message] + else + locale.t :header, :count => count, :model => options[:object_name].to_s.gsub('_', ' ') + end + + message = options.include?(:message) ? options[:message] : locale.t(:body) + + error_messages = objects.sum do |object| + object.errors.full_messages.map do |msg| + content_tag(:li, msg) + end + end.join.html_safe + + contents = '' + contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank? + contents << content_tag(:p, message) unless message.blank? + contents << content_tag(:ul, error_messages) + + content_tag(:div, contents.html_safe, html) + end + else + '' + end +end+
Returns an entire form with all needed input tags for a specified Active
+Record object. For example, if @post
has attributes named
+title
of type VARCHAR
and body
of
+type TEXT
then
form("post") ++ +
would yield a form like the following (modulus formatting):
+ +<form action='/posts/create' method='post'> + <p> + <label for="post_title">Title</label><br /> + <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /> + </p> + <p> + <label for="post_body">Body</label><br /> + <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea> + </p> + <input name="commit" type="submit" value="Create" /> +</form>+ +
It's possible to specialize the form builder by using a different
+action name and by supplying another block renderer. For example, if
+@entry
has an attribute message
of type
+VARCHAR
then
form("entry", + :action => "sign", + :input_block => Proc.new { |record, column| + "#{column.human_name}: #{input(record, column.name)}<br />" +}) ++ +
would yield a form like the following (modulus formatting):
+ +<form action="/entries/sign" method="post"> + Message: + <input id="entry_message" name="entry[message]" size="30" type="text" /><br /> + <input name="commit" type="submit" value="Sign" /> +</form>+ +
It's also possible to add additional content to the form by giving it a +block, such as:
+ +form("entry", :action => "sign") do |form| + form << content_tag("b", "Department") + form << collection_select("department", "id", @departments, "id", "name") +end ++ +
The following options are available:
+:action
- The action used when submitting the form (default:
+create
if a new record, otherwise update
).
:input_block
- Specialize the output using a different block,
+see above.
:method
- The method used when submitting the form (default:
+post
).
:multipart
- Whether to change the enctype of the form to
+“multipart/form-data”, used when uploading a file (default:
+false
).
:submit_value
- The text of the submit button (default:
+“Create” if a new record, otherwise “Update”).
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 72 +def form(record_name, options = {}) + record = instance_variable_get("@#{record_name}") + record = convert_to_model(record) + + options = options.symbolize_keys + options[:action] ||= record.persisted? ? "update" : "create" + action = url_for(:action => options[:action], :id => record) + + submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize + + contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil) + contents.safe_concat hidden_field(record_name, :id) if record.persisted? + contents.safe_concat all_input_tags(record, record_name, options) + yield contents if block_given? + contents.safe_concat submit_tag(submit_value) + contents.safe_concat('</form>') +end+
Returns a default input tag for the type of object returned by the method.
+For example, if @post
has an attribute title
+mapped to a VARCHAR
column that holds “Hello World”:
input("post", "title") +# => <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /> ++ + + + +
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 18 +def input(record_name, method, options = {}) + InstanceTag.new(record_name, method, self).to_tag(options) +end+
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 280 +def error_message_on(method, *args) + @template.error_message_on(@object || @object_name, method, *args) +end+
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 284 +def error_messages(options = {}) + @template.error_messages_for(@object_name, objectify_options(options)) +end+
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 274 +def column_type + object.send(:column_for_attribute, @method_name).type +end+
# File lib/dynamic_form/action_view/helpers/dynamic_form.rb, line 254 +def to_tag(options = {}) + case column_type + when :string + field_type = @method_name.include?("password") ? "password" : "text" + to_input_field_tag(field_type, options) + when :text + to_text_area_tag(options) + when :integer, :float, :decimal + to_input_field_tag("text", options) + when :date + to_date_select_tag(options) + when :datetime, :timestamp + to_datetime_select_tag(options) + when :time + to_time_select_tag(options) + when :boolean + to_boolean_select_tag(options) + end +end+
# File app/controllers/admin_controller.rb, line 220 +def ban + user = DrupalUser.find params[:id] + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + user.ban + flash[:notice] = 'The user has been banned.' + else + flash[:error] = 'Only moderators can ban other users.' + end + redirect_to '/profile/' + user.name + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/admin_controller.rb, line 251 +def batch + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + nodes = 0 + users = [] + params[:ids].split(',').uniq.each do |nid| + node = Node.find nid + node.spam + nodes += 1 + user = node.author + user.ban + users << user.id + end + flash[:notice] = nodes.to_s + ' nodes spammed and ' + users.length.to_s + ' users banned.' + redirect_to '/spam/wiki' + else + flash[:error] = 'Only admins can batch moderate.' + redirect_to '/dashboard' + end +end+
# File app/controllers/admin_controller.rb, line 32 +def demote_basic + @user = User.find params[:id] + unless @user.nil? + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @user.role = 'basic' + @user.save({}) + flash[:notice] = "User '<a href='/profile/" + @user.username + "'>" + @user.username + "</a>' is no longer a moderator." + else + flash[:error] = 'Only admins and moderators can demote other users.' + end + end + redirect_to '/profile/' + @user.username + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/admin_controller.rb, line 103 +def mark_spam + @node = Node.find params[:id] + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + if @node.status == 1 || @node.status == 4 + @node.spam + @node.author.ban + AdminMailer.notify_moderators_of_spam(@node, current_user) + flash[:notice] = "Item marked as spam and author banned. You can undo this on the <a href='/spam'>spam moderation page</a>." + redirect_to '/dashboard' + else + flash[:notice] = "Item already marked as spam and author banned. You can undo this on the <a href='/spam'>spam moderation page</a>." + redirect_to '/dashboard' + end + else + flash[:error] = 'Only moderators can moderate posts.' + if @node.has_power_tag('question') + redirect_to @node.path(:question) + else + redirect_to @node.path + end + end +end+
# File app/controllers/admin_controller.rb, line 159 +def mark_spam_revision + @revision = Revision.find_by(vid: params[:vid]) + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + if @revision.status == 1 + @revision.spam + @revision.author.ban + flash[:notice] = "Item marked as spam and author banned. You can undo this on the <a href='/spam/revisions'>spam moderation page</a>." + redirect_to '/wiki/revisions/' + @revision.node.slug_from_path + '?_=' + Time.now.to_i.to_s + else + flash[:notice] = "Item already marked as spam and author banned. You can undo this on the <a href='/spam/revisions'>spam moderation page</a>." + redirect_to '/dashboard' + end + else + flash[:error] = 'Only moderators can moderate posts.' + if @node.has_power_tag('question') + redirect_to @node.path(:question) + else + redirect_to @node.path + end + end +end+
# File app/controllers/admin_controller.rb, line 271 +def migrate + if current_user && current_user.role == 'admin' + du = DrupalUser.find params[:id] + if du.user + flash[:error] = 'The user has already been migrated.' + else + if du.migrate + flash[:notice] = 'The user was migrated! Enthusiasm!' + else + flash[:error] = 'The user could not be migrated.' + end + end + else + flash[:error] = 'Only admins can migrate users.' + end + redirect_to '/profile/' + du.name +end+
# File app/controllers/admin_controller.rb, line 198 +def moderate + user = DrupalUser.find params[:id] + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + user.moderate + flash[:notice] = 'The user has been moderated.' + else + flash[:error] = 'Only moderators can moderate other users.' + end + redirect_to '/profile/' + user.name + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/admin_controller.rb, line 4 +def promote_admin + @user = User.find params[:id] + unless @user.nil? + if current_user && current_user.role == 'admin' + @user.role = 'admin' + @user.save({}) + flash[:notice] = "User '<a href='/profile/" + @user.username + "'>" + @user.username + "</a>' is now an admin." + else + flash[:error] = 'Only admins can promote other users to admins.' + end + end + redirect_to '/profile/' + @user.username + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/admin_controller.rb, line 18 +def promote_moderator + @user = User.find params[:id] + unless @user.nil? + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @user.role = 'moderator' + @user.save({}) + flash[:notice] = "User '<a href='/profile/" + @user.username + "'>" + @user.username + "</a>' is now a moderator." + else + flash[:error] = 'Only moderators can promote other users.' + end + end + redirect_to '/profile/' + @user.username + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/admin_controller.rb, line 126 +def publish + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @node = Node.find params[:id] + if @node.status == 1 + flash[:notice] = 'Item already published.' + else + first_timer_post = (@node.status == 4) + @node.publish + @node.author.unban + if first_timer_post + AdminMailer.notify_author_of_approval(@node, current_user) + AdminMailer.notify_moderators_of_approval(@node, current_user) + SubscriptionMailer.notify_node_creation(@node) + if @node.has_power_tag('question') + flash[:notice] = "Question approved and published after #{time_ago_in_words(@node.created_at)} in moderation. Now reach out to the new community member; thank them, just say hello, or help them revise/format their post in the comments." + else + flash[:notice] = "Post approved and published after #{time_ago_in_words(@node.created_at)} in moderation. Now reach out to the new community member; thank them, just say hello, or help them revise/format their post in the comments." + end + else + flash[:notice] = 'Item published.' + end + end + if @node.has_power_tag('question') + redirect_to @node.path(:question) + else + redirect_to @node.path + end + else + flash[:error] = 'Only moderators can publish posts.' + redirect_to '/dashboard' + end +end+
# File app/controllers/admin_controller.rb, line 181 +def publish_revision + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @revision = Revision.find params[:vid] + @revision.publish + @revision.author.unban + flash[:notice] = 'Item published.' + if @revision.parent.has_power_tag('question') + redirect_to @revision.parent.path(:question) + else + redirect_to @revision.parent.path + end + else + flash[:error] = 'Only moderators can publish posts.' + redirect_to '/dashboard' + end +end+
# File app/controllers/admin_controller.rb, line 289 +def queue + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @notes = Node.where(status: 4) + .paginate(page: params[:page]) + flash[:warning] = "These are notes requiring moderation. <a href='/wiki/moderation'>Community moderators</a> may approve or reject them." + render template: 'notes/index' + else + flash[:error] = 'Only moderators and admins can see the moderation queue.' + redirect_to '/dashboard' + end +end+
# File app/controllers/admin_controller.rb, line 46 +def reset_user_password + if current_user && current_user.role == 'admin' + user = User.find(params[:id]) + if user + key = user.generate_reset_key + user.save({}) + # send key to user email + PasswordResetMailer.reset_notify(user, key) unless user.nil? # respond the same to both successes and failures; security + end + + flash[:notice] = "#{user.name} should receive an email with instructions on how to reset their password. If they do not, please double check that they are using the email they registered with." + redirect_to "/profile/" + user.name + end +end+
# File app/controllers/admin_controller.rb, line 76 +def spam + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @nodes = Node.paginate(page: params[:page]) + .order('nid DESC') + @nodes = if params[:type] == 'wiki' + @nodes.where(type: 'page', status: 1) + else + @nodes.where(status: 0) + end + else + flash[:error] = 'Only moderators can moderate posts.' + redirect_to '/dashboard' + end +end+
# File app/controllers/admin_controller.rb, line 91 +def spam_revisions + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @revisions = Revision.paginate(page: params[:page]) + .order('timestamp DESC') + .where(status: 0) + render template: 'admin/spam' + else + flash[:error] = 'Only moderators can moderate revisions.' + redirect_to '/dashboard' + end +end+
# File app/controllers/admin_controller.rb, line 231 +def unban + user = DrupalUser.find params[:id] + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + user.unban + flash[:notice] = 'The user has been unbanned.' + else + flash[:error] = 'Only moderators can unban other users.' + end + redirect_to '/profile/' + user.name + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/admin_controller.rb, line 209 +def unmoderate + user = DrupalUser.find params[:id] + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + user.unmoderate + flash[:notice] = 'The user has been unmoderated.' + else + flash[:error] = 'Only moderators can unmoderate other users.' + end + redirect_to '/profile/' + user.name + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/admin_controller.rb, line 62 +def useremail + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + if params[:address] + # address was submitted. find the username(s) and return. + @address = params[:address] + @users = User.where(email: params[:address]) + .where(status: [1,4]) + end + else + # unauthorized. instead of return ugly 403, just send somewhere else + redirect_to '/dashboard' + end +end+
# File app/controllers/admin_controller.rb, line 242 +def users + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @users = DrupalUser.order('uid DESC').limit(200) + else + flash[:error] = 'Only moderators can moderate other users.' + redirect_to '/dashboard' + end +end+
Will this further bait spammers? If we don't, will non-spammers whose +posts were moderated get confused? Should: show explanation/appeal process +to authors who visit again Should: prompt moderators to reach out if +it's not spam, but a guidelines violation def +notify_author_of_spam(node) end
+ + + + +# File app/mailers/admin_mailer.rb, line 35 +def notify_moderators_of_approval(node, moderator) + subject = '[New Public Lab poster needs moderation] ' + node.title + @author = node.author + @moderator = moderator + @node = node + @footer = feature('email-footer') + moderators = User.where(role: %w[moderator admin]).collect(&:email) + mail( + to: "moderators@#{ActionMailer::Base.default_url_options[:host]}", + bcc: moderators, + subject: subject + ).deliver +end+
# File app/mailers/admin_mailer.rb, line 49 +def notify_moderators_of_spam(node, moderator) + subject = '[New Public Lab poster needs moderation] ' + node.title + @author = node.author + @moderator = moderator + @node = node + @footer = feature('email-footer') + moderators = User.where(role: %w[moderator admin]).collect(&:email) + mail( + to: "moderators@#{ActionMailer::Base.default_url_options[:host]}", + bcc: moderators, + subject: subject + ).deliver +end+
# File app/mailers/admin_mailer.rb, line 6 +def notify_node_moderators(node) + subject = '[New Public Lab poster needs moderation] ' + node.title + @node = node + @user = node.author.user + @footer = feature('email-footer') + moderators = User.where(role: %w[moderator admin]).collect(&:email) + mail( + to: "moderators@#{ActionMailer::Base.default_url_options[:host]}", + bcc: moderators, + subject: subject + ).deliver +end+
# File app/models/answer.rb, line 29 +def answer_notify(current_user) + # notify question author + if current_user.uid != node.author.uid + AnswerMailer.notify_question_author(node.author, self).deliver + end + users_with_everything_tag = Tag.followers('everything') + uids = (node.answers.collect(&:uid) + node.likers.collect(&:uid) + users_with_everything_tag.collect(&:uid)).uniq + # notify other answer authors and users who liked the question + DrupalUser.where('uid IN (?)', uids).each do |user| + if (user.uid != current_user.uid) && (user.uid != node.author.uid) + AnswerMailer.notify_answer_likers_author(user.user, self).deliver + end + end +end+
# File app/models/answer.rb, line 13 +def body + finder = content.gsub(Callouts.const_get(:FINDER), Callouts.const_get(:PRETTYLINKMD)) + finder = finder.gsub(Callouts.const_get(:HASHTAGNUMBER), Callouts.const_get(:NODELINKMD)) + finder = finder.gsub(Callouts.const_get(:HASHTAG), Callouts.const_get(:HASHLINKMD)) +end+
users who like this answer
+ + + + +# File app/models/answer.rb, line 20 +def likers + answer_selections + .joins(:drupal_user) + .references(:users) + .where(liking: true) + .where('users.status = ?', 1) + .collect(&:user) +end+
# File app/controllers/answer_like_controller.rb, line 8 +def likes + @answer = Answer.find(params[:aid]) + if @answer.liked_by(current_user.uid) + AnswerSelection.set_likes(current_user.uid, @answer.id, false) + else + AnswerSelection.set_likes(current_user.uid, @answer.id, true) + user = User.find(current_user.uid) + AnswerMailer.notify_answer_like(user, @answer).deliver + end + @answer.reload + respond_to do |format| + format.js {} + end +end+
# File app/controllers/answer_like_controller.rb, line 4 +def show + render json: Answer.find(params[:id]).cached_likes +end+
# File app/mailers/answer_mailer.rb, line 22 +def notify_answer_accept(user, answer) + @user = user + @answer = answer + @footer = feature('email-footer') + mail(to: @user.email, subject: '[PublicLab] Your answer has been accepted') +end+
# File app/mailers/answer_mailer.rb, line 29 +def notify_answer_like(user, answer) + subject = "[PublicLab] #{user.username} liked your answer to: " + answer.node.title + @user = user + @answer = answer + @footer = feature('email-footer') + mail(to: @answer.author.email, subject: subject) +end+
# File app/models/answer_selection.rb, line 11 +def self.set_likes(uid, aid, value) + like = where(user_id: uid, aid: aid).first_or_create + like.liking = value + if like.liking_changed? + answer = Answer.find(aid) + if like.liking + answer.cached_likes += 1 + else + answer.cached_likes -= 1 + end + like.save + answer.save + end + like.liking +end+
# File app/models/answer_selection.rb, line 7 +def user + User.find_by(username: DrupalUser.find_by(uid: user_id).name) +end+
# File app/controllers/answers_controller.rb, line 57 +def accept + @answer = Answer.find(params[:id]) + + if (current_user.role == "admin" || current_user.role == "moderator" || current_user.uid == @answer.node.uid) + respond_to do |format| + if @answer.accepted + @answer.accepted = false + @answer.save + else + @answer.accepted = true + @answer.save + AnswerMailer.notify_answer_accept(@answer.author, @answer).deliver + end + @answer.reload + format.js + end + else + render text: "Answer couldn't be accepted" + end +end+
# File app/controllers/answers_controller.rb, line 4 +def create + @node = Node.find(params[:nid]) + @answer = Answer.new( + nid: @node.id, + uid: current_user.uid, + content: params[:body] + ) + respond_to do |format| + if current_user && @answer.save + @answer.answer_notify(current_user) + format.html { redirect_to @node.path(:question), notice: 'Answer successfully posted' } + format.js {} + end + end +end+
# File app/controllers/answers_controller.rb, line 37 +def delete + @answer = Answer.find(params[:id]) + if current_user.uid == @answer.node.uid || + @answer.uid == current_user.uid || + current_user.role == 'admin' || + current_user.role == 'moderator' + respond_to do |format| + if @answer.delete + format.html { redirect_to @answer.node.path(:question), notice: 'Answer deleted' } + format.js + else + flash[:error] = "The answer couldn't be deleted" + redirect_to @answer.node.path(:question) + end + end + else + prompt_login 'Only the answer or question author can delete this answer' + end +end+
# File app/controllers/answers_controller.rb, line 20 +def update + @answer = Answer.find(params[:id]) + if @answer.uid == current_user.uid + @answer.content = params[:body] + if @answer.save + flash[:notice] = 'Answer updated' + redirect_to @answer.node.path(:question) + else + flash[:error] = "Answer couldn't be updated" + redirect_to @answer.node.path(:question) + end + else + flash[:error] = 'Only the author of the answer can edit it.' + redirect_to @answer.node.path(:question) + end +end+
# File app/helpers/application_helper.rb, line 15 +def feature(title) + features = Node.where(type: 'feature', title: title) + if !features.empty? + return features.last.body.to_s.html_safe + else + '' + end +end+
# File app/helpers/application_helper.rb, line 30 +def insert_extras(body) + body = NodeShared.notes_grid(body) + body = NodeShared.questions_grid(body) + body = NodeShared.activities_grid(body) + body = NodeShared.upgrades_grid(body) + body = NodeShared.notes_map(body) + body = NodeShared.notes_map_by_tag(body) + body = NodeShared.people_grid(body) + body = NodeShared.people_map(body) + body = NodeShared.graph_grid(body) + body = NodeShared.wikis_grid(body) + body +end+
# File app/helpers/application_helper.rb, line 24 +def locale_name_pairs + I18n.available_locales.map do |locale| + [I18n.t('language', locale: locale), locale] + end +end+
returns true if user is logged in and has any of the roles given, as +['admin','moderator']
+ + + + +# File app/helpers/application_helper.rb, line 3 +def logged_in_as(roles) + if current_user + has_valid_role = false + roles.each do |role| + has_valid_role = true if current_user.role == role + end + has_valid_role + else + false + end +end+
# File app/helpers/application_helper.rb, line 44 +def render_map(lat, lon, items) + render partial: 'map/leaflet', locals: { lat: lat, lon: lon, items: items } +end+
This module is responsible for adding OpenID functionality to Authlogic. Checkout the README for more info and please see the sub +modules for detailed documentation.
+ +This module is responsible for adding in the OpenID functionality to your +models. It hooks itself into the acts_as_authentic method provided by Authlogic.
+ +Adds in the neccesary modules for acts_as_authentic to include and also +disabled password validation if OpenID is being used.
+ + + + +# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 9 +def self.included(klass) + klass.class_eval do + extend Config + add_acts_as_authentic_module(Methods, :prepend) + end +end+
Same as required_fields, but optional instead.
+Default:
[]
Accepts:
Array of symbols
# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 34 +def openid_optional_fields(value = nil) + rw_config(:openid_optional_fields, value, []) +end+
Some OpenID providers support a lightweight profile exchange protocol, for +those that do, you can require certain fields. This is convenient for new +registrations, as it will basically fill out the fields in the form for +them, so they don't have to re-type information already stored with +their OpenID account.
+ +For more info and what fields you can use see: openid.net/specs/openid-simple-registration-extension-1_0.html
+Default:
[]
Accepts:
Array of symbols
# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 25 +def openid_required_fields(value = nil) + rw_config(:openid_required_fields, value, []) +end+
Set up some simple validations
+ + + + +# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 42 +def self.included(klass) + return if !klass.column_names.include?("openid_identifier") + + klass.class_eval do + validates_uniqueness_of :openid_identifier, :scope => validations_scope, :if => :using_openid? + validate :validate_openid + validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_openid?) + validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_openid?) + validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_openid?) + end +end+
Set the openid_identifier field and also resets the persistence_token if +this value changes.
+ + + + +# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 55 +def openid_identifier=(value) + write_attribute(:openid_identifier, value.blank? ? nil : OpenID.normalize_url(value)) + reset_persistence_token if openid_identifier_changed? +rescue OpenID::DiscoveryFailure => e + @openid_error = e.message +end+
This is where all of the magic happens. This is where we hook in and add +all of the OpenID sweetness.
+ +I had to take this approach because when authenticating with OpenID nonces +and what not are stored in database tables. That being said, the whole save +process for ActiveRecord is wrapped +in a transaction. Trying to authenticate with OpenID in a transaction is +not good because that transaction be get rolled back, thus reversing all of +the OpenID inserts and making OpenID authentication fail every time. So We +need to step outside of the transaction and do our OpenID madness.
+ +Another advantage of taking this approach is that we can set fields from +their OpenID profile before we save the record, if their OpenID provider +supports it.
+ + +# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 72 +def save(perform_validation = true, &block) + return false if perform_validation && block_given? && authenticate_with_openid? && !authenticate_with_openid + result = super + yield(result) if block_given? + result +end+
This method works in conjunction with map_saved_attributes.
+ +Let's say a user fills out a registration form, provides an OpenID and +submits the form. They are then redirected to their OpenID provider. All is +good and they are redirected back. All of those fields they spent time +filling out are forgetten and they have to retype them all. To avoid this, +AuthlogicOpenid saves all of these +attributes in the session and then attempts to restore them. See the source +for what attributes it saves. If you need to block more attributes, or save +more just override this method and do whatever you want.
+ + + + +# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 133 +def attributes_to_save # :doc: + attrs_to_save = attributes.clone.delete_if do |k, v| + [:id, :password, crypted_password_field, password_salt_field, :persistence_token, :perishable_token, :single_access_token, :login_count, + :failed_login_count, :last_request_at, :current_login_at, :last_login_at, :current_login_ip, :last_login_ip, :created_at, + :updated_at, :lock_version].include?(k.to_sym) + end + attrs_to_save.merge!(:password => password, :password_confirmation => password_confirmation) +end+
Override this method to map the OpenID registration fields with fields in +your model. See the required_fields and optional_fields configuration +options to enable this feature.
+ +Basically you will get a hash of values passed as a single argument. Then +just map them as you see fit. Check out the source of this method for an +example.
+ + + + +# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 120 +def map_openid_registration(registration) # :doc: + self.name ||= registration[:fullname] if respond_to?(:name) && !registration[:fullname].blank? + self.first_name ||= registration[:fullname].split(" ").first if respond_to?(:first_name) && !registration[:fullname].blank? + self.last_name ||= registration[:fullname].split(" ").last if respond_to?(:last_name) && !registration[:last_name].blank? +end+
This method works in conjunction with attributes_to_save. See that method +for a description of the why these methods exist.
+ +If the default behavior of this method is not sufficient for you because +you have attr_protected or attr_accessible then override this method and +set them individually. Maybe something like this would be good:
+ +attrs.each do |key, value| + send("#{key}=", value) +end ++ + + + +
# File lib/authlogic_openid/authlogic_openid/acts_as_authentic.rb, line 150 +def map_saved_attributes(attrs) # :doc: + self.attributes = attrs +end+
This module is responsible for adding all of the OpenID goodness to the +Authlogic::Session::Base class.
+ +Add a simple openid_identifier attribute and some validations for the +field.
+ + + + +# File lib/authlogic_openid/authlogic_openid/session.rb, line 5 +def self.included(klass) + klass.class_eval do + extend Config + include Methods + end +end+
Add this in your Session object to Auto +Register a new user using openid via sreg
+ + + + +# File lib/authlogic_openid/authlogic_openid/session.rb, line 33 +def auto_register(value=true) + auto_register_value(value) +end+
# File lib/authlogic_openid/authlogic_openid/session.rb, line 37 +def auto_register_value(value=nil) + rw_config(:auto_register,value,false) +end+
What method should we call to find a record by the openid_identifier? This +is useful if you want to store multiple openid_identifiers for a single +record. You could do something like:
+ +class User < ActiveRecord::Base + def self.find_by_openid_identifier(identifier) + user.first(:conditions => {:openid_identifiers => {:identifier => identifier}}) + end +end ++ +
Obviously the above depends on what you are calling your assocition, etc. +But you get the point.
+Default:
:find_by_openid_identifier
Accepts:
Symbol
# File lib/authlogic_openid/authlogic_openid/session.rb, line 27 +def find_by_openid_identifier_method(value = nil) + rw_config(:find_by_openid_identifier_method, value, :find_by_openid_identifier) +end+
# File lib/authlogic_openid/authlogic_openid/session.rb, line 45 +def self.included(klass) + klass.class_eval do + attr_reader :openid_identifier + validate :validate_openid_error + validate :validate_by_openid, :if => :authenticating_with_openid? + end +end+
Hooks into credentials so that you can pass an :openid_identifier key.
+ + +# File lib/authlogic_openid/authlogic_openid/session.rb, line 54 +def credentials=(value) + super + values = value.is_a?(Array) ? value : [value] + hash = values.first.is_a?(Hash) ? values.first.with_indifferent_access : nil + self.openid_identifier = hash[:openid_identifier] if !hash.nil? && hash.key?(:openid_identifier) +end+
# File lib/authlogic_openid/authlogic_openid/session.rb, line 61 +def openid_identifier=(value) + @openid_identifier = value.blank? ? nil : OpenID.normalize_url(value) + @openid_error = nil +rescue OpenID::DiscoveryFailure => e + @openid_identifier = nil + @openid_error = e.message +end+
Cleaers out the block if we are authenticating with OpenID, so that we can +redirect without a DoubleRender error.
+ + +# File lib/authlogic_openid/authlogic_openid/session.rb, line 71 +def save(&block) + block = nil if !openid_identifier.blank? && controller.request.env[Rack::OpenID::RESPONSE].blank? + super(&block) +end+
A class for describing the current version of a library. The version
+consists of three parts: the major
number, the
+minor
number, and the tiny
(or
+patch
) number.
The current version as a Version instance
+ + +The current version as a String
+ + +A convenience method for instantiating a new Version instance with the given major
,
+minor
, and tiny
components.
# File lib/authlogic_openid/authlogic_openid/version.rb, line 10 +def self.[](major, minor, tiny) + new(major, minor, tiny) +end+
Create a new Version object with the given +components.
+ + + + +# File lib/authlogic_openid/authlogic_openid/version.rb, line 17 +def initialize(major, minor, tiny) + @major, @minor, @tiny = major, minor, tiny +end+
Compare this version to the given version
object.
# File lib/authlogic_openid/authlogic_openid/version.rb, line 22 +def <=>(version) + to_i <=> version.to_i +end+
# File lib/authlogic_openid/authlogic_openid/version.rb, line 38 +def to_a + [@major, @minor, @tiny] +end+
Converts this version to a canonical integer that may be compared against +other version objects.
+ + + + +# File lib/authlogic_openid/authlogic_openid/version.rb, line 34 +def to_i + @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny +end+
Converts this version object to a string, where each of the three version +components are joined by the '.' character. E.g., 2.0.0.
+ + + + +# File lib/authlogic_openid/authlogic_openid/version.rb, line 28 +def to_s + @to_s ||= [@major, @minor, @tiny].join(".") +end+
# File app/models/comment.rb, line 26 +def self.comment_weekly_tallies(span = 52, time = Time.now) + weeks = {} + (0..span).each do |week| + weeks[span - week] = Comment.select(:timestamp) + .where(timestamp: time.to_i - week.weeks.to_i..time.to_i - (week - 1).weeks.to_i) + .count + end + weeks +end+
# File app/models/comment.rb, line 18 +def self.inheritance_column + 'rails_type' +end+
# File app/models/comment.rb, line 22 +def self.search(query) + Comment.where('MATCH(comment) AGAINST(?)', query) +end+
# File app/models/comment.rb, line 126 +def answer_comment_notify(current_user) + # notify answer author + if answer.uid != current_user.uid + CommentMailer.notify_answer_author(answer.author, self).deliver + end + + notify_callout_users + + already = mentioned_users.collect(&:uid) + [answer.uid] + uids = [] + # notify other answer commenter and users who liked the answer + # except mentioned users and answer author + (answer.comments.collect(&:uid) + answer.likers.collect(&:uid)).uniq.each do |u| + uids << u unless already.include?(u) + end + + notify_users(uids, current_user) + notify_tag_followers(already + uids) +end+
# File app/models/comment.rb, line 44 +def body + finder = comment.gsub(Callouts.const_get(:FINDER), Callouts.const_get(:PRETTYLINKMD)) + finder = finder.gsub(Callouts.const_get(:HASHTAGNUMBER), Callouts.const_get(:NODELINKMD)) + finder = finder.gsub(Callouts.const_get(:HASHTAG), Callouts.const_get(:HASHLINKMD)) +end+
# File app/models/comment.rb, line 40 +def created_at + Time.at(timestamp) +end+
# File app/models/comment.rb, line 50 +def icon + "<i class='icon-comment'></i>" +end+
# File app/models/comment.rb, line 36 +def id + cid +end+
# File app/models/comment.rb, line 77 +def mentioned_users + usernames = comment.scan(Callouts.const_get(:FINDER)) + User.where(username: usernames.map { |m| m[1] }).uniq +end+
# File app/models/comment.rb, line 62 +def next_thread + (thread.split('/').first.to_i(16) + 1).to_s(16).rjust(2, '0') + '/' +end+
email all users in this thread plus all who've starred it
+ + + + +# File app/models/comment.rb, line 111 +def notify(current_user) + if parent.uid != current_user.uid + CommentMailer.notify_note_author(parent.author, self).deliver + end + + notify_callout_users + + # notify other commenters, revisers, and likers, but not those already @called out + already = mentioned_users.collect(&:uid) + [parent.uid] + uids = uids_to_notify - already + + notify_users(uids, current_user) + notify_tag_followers(already + uids) +end+
# File app/models/comment.rb, line 87 +def notify_callout_users + # notify mentioned users + mentioned_users.each do |user| + CommentMailer.notify_callout(self, user) if user.username != author.username + end +end+
# File app/models/comment.rb, line 94 +def notify_tag_followers(already_mailed_uids = []) + # notify users who follow the tags mentioned in the comment + followers_of_mentioned_tags.each do |user| + CommentMailer.notify_tag_followers(self, user) unless already_mailed_uids.include?(user.uid) + end +end+
# File app/models/comment.rb, line 101 +def notify_users(uids, current_user) + DrupalUser.where('uid IN (?)', uids).each do |user| + if user.uid != current_user.uid + CommentMailer.notify(user.user, self).deliver + end + end +end+
# File app/models/comment.rb, line 66 +def parent + if aid == 0 + node + else + answer.node + end +end+
users who are involved in this comment thread
+ + + + +# File app/models/comment.rb, line 75 +def thread_participants; end+
# File app/models/comment.rb, line 54 +def type + 'comment' +end+
create answer comments
+ + + + +# File app/controllers/comment_controller.rb, line 71 +def answer_create + @answer_id = params[:aid] + @comment = Comment.new( + uid: current_user.uid, + aid: params[:aid], + comment: params[:body], + timestamp: Time.now.to_i + ) + if @comment.save + @comment.answer_comment_notify(current_user) + respond_to do |format| + format.js { render template: 'comment/create' } + end + else + flash[:error] = 'The comment could not be saved.' + render text: 'failure' + end +end+
handle some errors!!!!!! create node comments
+ + + + +# File app/controllers/comment_controller.rb, line 15 +def create + @node = Node.find params[:id] + @body = params[:body] + @user = current_user + + begin + @comment = create_comment(@node, @user, @body) + respond_with do |format| + if params[:type] && params[:type] == 'question' + @answer_id = 0 + format.js + else + format.html do + if request.xhr? + render partial: 'notes/comment', locals: { comment: @comment } + else + flash[:notice] = 'Comment posted.' + redirect_to @node.path + '#last' # to last comment + end + end + end + end + rescue CommentError + flash[:error] = 'The comment could not be saved.' + render text: 'failure' + end +end+
# File app/controllers/comment_controller.rb, line 43 +def create_by_token + @node = Node.find params[:id] + @user = User.find_by(username: params[:username]) + @body = params[:body] + @token = request.headers["HTTP_TOKEN"] + + if @user && @user.token == @token + begin + # The create_comment is a function that has been defined inside the + # CommentHelper module inside app/helpers/comment_helper.rb and can be + # used in here because the module was `include`d right at the beginning + @comment = create_comment(@node, @user, @body) + respond_to do |format| + format.all { render :nothing => true, :status => :created } + end + rescue CommentError + respond_to do |format| + format.all { render :nothing => true, :status => :bad_request } + end + end + else + respond_to do |format| + format.all { render :nothing => true, :status => :unauthorized } + end + end +end+
# File app/controllers/comment_controller.rb, line 111 +def delete + @comment = Comment.find params[:id] + + comments_node_and_path + + if current_user.uid == @node.uid || + @comment.uid == current_user.uid || + current_user.role == 'admin' || + current_user.role == 'moderator' + + if @comment.delete + respond_with do |format| + if params[:type] && params[:type] == 'question' + @answer_id = @comment.aid + format.js + else + format.html do + if request.xhr? + render text: 'success' + else + flash[:notice] = 'Comment deleted.' + redirect_to '/' + @node.path + end + end + end + end + else + flash[:error] = 'The comment could not be deleted.' + render text: 'failure' + end + else + prompt_login 'Only the comment or post author can delete this comment' + end +end+
# File app/controllers/comment_controller.rb, line 7 +def index + @comments = Comment.paginate(page: params[:page], per_page: 30) + .order('timestamp DESC') + render template: 'comments/index' +end+
# File app/controllers/comment_controller.rb, line 146 +def make_answer + @comment = Comment.find params[:id] + comments_node_and_path + + if @comment.uid == current_user.uid + @answer = Answer.new( + nid: @comment.nid, + uid: @comment.uid, + content: @comment.comment + ) + + if @answer.save && @comment.delete + @answer.answer_notify(current_user) + @answer_id = @comment.aid + respond_with do |format| + format.js { render template: 'comment/make_answer' } + end + else + flash[:error] = 'The comment could not be promoted to answer.' + render text: 'failure' + end + else + prompt_login 'Only the comment author can promote this comment to answer' + end +end+
# File app/controllers/comment_controller.rb, line 90 +def update + @comment = Comment.find params[:id] + + comments_node_and_path + + if @comment.uid == current_user.uid + # should abstract ".comment" to ".body" for future migration to native db + @comment.comment = params[:body] + if @comment.save + flash[:notice] = 'Comment updated.' + redirect_to @path + '?_=' + Time.now.to_i.to_s + else + flash[:error] = 'The comment could not be updated.' + redirect_to @path + end + else + flash[:error] = 'Only the author of the comment can edit it.' + redirect_to @path + end +end+
# File app/helpers/comment_helper.rb, line 5 +def create_comment(node, user, body) + @comment = node.add_comment(uid: user.uid, body: body) + if user && @comment.save + @comment.notify user + return @comment + else + raise CommentError.new() + end +end+
CommentMailer.notify_of_comment(user,self).deliver
+ + + + +# File app/mailers/comment_mailer.rb, line 7 +def notify(user, comment) + @user = user + @comment = comment + @footer = feature('email-footer') + mail(to: user.email, subject: "New comment on '" + comment.parent.title + "'") +end+
user is awarder, not awardee
+ + + + +# File app/mailers/comment_mailer.rb, line 22 +def notify_barnstar(user, note) + @giver = user.drupal_user + @note = note + @footer = feature('email-footer') + mail(to: note.author.email, subject: 'You were awarded a Barnstar!').deliver +end+
# File app/mailers/comment_mailer.rb, line 29 +def notify_callout(comment, user) + @user = user + @comment = comment + @footer = feature('email-footer') + mail(to: user.email, subject: 'You were mentioned in a comment.').deliver +end+
# File app/mailers/comment_mailer.rb, line 36 +def notify_tag_followers(comment, user) + @user = user + @comment = comment + @footer = feature('email-footer') + mail(to: user.email, subject: 'A tag you follow was mentioned in a comment.').deliver +end+
Active Support concerns are a good way to use modules that can used across +different models Refer to this link: stackoverflow.com/questions/14541823/how-to-use-concerns-in-rails-4
+ +filtered version additionally appending http/https
+ +protocol to protocol-relative URLslike "/foo"+ + + + +
# File app/models/concerns/comments_shared.rb, line 8 +def body_email(host = 'publiclab.org') + body.gsub(/([\s|"|'|\[|\(])(\/\/)([\w]?\.?#{host})/, '\1https://\3') +end+
# File app/models/concerns/comments_shared.rb, line 16 +def parent_commenter_uids + parent.comments.collect(&:uid) +end+
# File app/models/concerns/comments_shared.rb, line 20 +def parent_liker_uids + parent.likers.collect(&:uid) +end+
# File app/models/concerns/comments_shared.rb, line 24 +def parent_reviser_uids + (parent.revision.collect(&:uid).uniq - [parent.author.uid]) +end+
# File app/models/concerns/comments_shared.rb, line 28 +def uids_to_notify + (parent_commenter_uids + parent_liker_uids + parent_reviser_uids).uniq +end+
List of documents returned from a search
+ +# File app/models/doc_list.rb, line 5 +def initialize; end+
# File app/models/doc_list.rb, line 12 +def addAll(dlist) + @items ||= [] + dlist.each { |docItem| @items << docItem } unless dlist.nil? +end+
# File app/models/doc_list.rb, line 7 +def addDoc(ndoc) + @items ||= [] + @items << ndoc +end+
# File app/models/doc_list.rb, line 17 +def getDocs + @items +end+
This subclass is used to auto-generate the RESTful data structure. It is +generally not useful for internal Ruby usage
+ +but must be included for full RESTful functionality.+ +
A DocResult is an individual return item for a +document (web page) search
+ +# File app/models/doc_result.rb, line 7 +def self.fromSearch(idval, typeval, urlval, titleval, sumval, scoreval) + obj = new + obj.docId = idval + obj.docType = typeval + obj.docUrl = urlval + obj.docTitle = titleval + obj.docSummary = sumval + obj.docScore = scoreval + obj +end+
# File app/models/doc_result.rb, line 5 +def initialize; end+
This subclass is used to auto-generate the RESTful data structure. It is +generally not useful for internal Ruby usage
+ +but must be included for full RESTful functionality.+ +
# File app/models/drupal_content_field_image_gallery.rb, line 22 +def description + PHP.unserialize(field_image_gallery_data)['description'] +rescue + '' +end+
# File app/models/drupal_content_field_image_gallery.rb, line 14 +def fid + field_image_gallery_fid +end+
# File app/models/drupal_content_field_image_gallery.rb, line 10 +def file + drupal_file +end+
# File app/models/drupal_content_field_image_gallery.rb, line 18 +def image + DrupalFile.find field_image_gallery_fid +end+
# File app/models/drupal_content_type_map.rb, line 46 +def captured_on + field_capture_date_value +end+
# File app/models/drupal_content_type_map.rb, line 27 +def cartographer_notes + field_cartographer_notes_value +end+
# File app/models/drupal_content_type_map.rb, line 31 +def license + l = "<a href='http://creativecommons.org/publicdomain/zero/1.0/'>Public Domain</a>" if field_license_value == 'publicdomain' + l = "<a href='http://creativecommons.org/licenses/by/3.0/'>CC-BY</a>" if field_license_value == 'cc-by' + l = "<a href='http://creativecommons.org/licenses/by-sa/3.0/'>CC-BY-SA</a>" if field_license_value == 'cc-by-sa' + l +end+
# File app/models/drupal_content_type_map.rb, line 23 +def max_zoom + field_zoom_max_value +end+
# File app/models/drupal_content_type_map.rb, line 19 +def min_zoom + field_zoom_min_value +end+
# File app/models/drupal_content_type_map.rb, line 38 +def notes + field_notes_value +end+
# File app/models/drupal_content_type_map.rb, line 42 +def published_on + field_publication_date_value +end+
# File app/models/drupal_content_type_map.rb, line 15 +def tms + field_tms_url_value +end+
# File app/models/drupal_file.rb, line 7 +def filetype + filename[-3..filename.length].downcase +end+
# File app/models/drupal_file.rb, line 11 +def is_image? + (filetype == 'jpg' || filetype == 'jpeg' || filetype == 'gif' || filetype == 'png') +end+
swap legacy Drupal static routes
+ + + + +# File app/models/drupal_file.rb, line 16 +def path(size = :default) + if is_image? + if size == :thumb + "/#{filepath.gsub('sites/default/files/', 'sites/default/files/imagecache/thumb/')}" + elsif size == :default + "/#{filepath.gsub('sites/default/files/', 'sites/default/files/imagecache/default/')}" + elsif size == :large + "/#{filepath.gsub('sites/default/files/', 'sites/default/files/imagecache/default/')}" + elsif size == :original + "/#{filepath}" + end + else + "/#{filepath}" + end +end+
# File app/models/drupal_profile_field.rb, line 6 +def self.inheritance_column + 'rails_type' +end+
# File app/models/drupal_upload.rb, line 11 +def file + drupal_file +end+
# File app/models/drupal_upload.rb, line 7 +def node + node +end+
# File app/models/drupal_user.rb, line 60 +def ban + self.status = 0 + decrease_likes_banned + save + # user is logged out next time they access current_user in a controller; see application controller + self +end+
# File app/models/drupal_user.rb, line 23 +def bio + user.bio +end+
Rails-style adaptors:
+ + + + +# File app/models/drupal_user.rb, line 37 +def created_at + Time.at(created) +end+
# File app/models/drupal_user.rb, line 75 +def email + mail +end+
# File app/models/drupal_user.rb, line 79 +def first_time_poster + user.first_time_poster +end+
last node
+ + + + +# File app/models/drupal_user.rb, line 107 +def last + Node.limit(1) + .where(uid: uid) + .order('changed DESC') + .first +end+
# File app/models/drupal_user.rb, line 87 +def like_count + NodeSelection.where(user_id: uid, liking: true).count +end+
# File app/models/drupal_user.rb, line 91 +def liked_notes + Node.includes(:node_selections) + .references(:node_selections) + .where("type = 'note' AND node_selections.liking = ? AND node_selections.user_id = ? AND node.status = 1", true, uid) + .order('node_selections.nid DESC') +end+
# File app/models/drupal_user.rb, line 98 +def liked_pages + NodeSelection.where("status = 1 AND user_id = ? AND liking = ? AND (node.type = 'page' OR node.type = 'tool' OR node.type = 'place')", uid, true) + .includes(:node) + .references(:node) + .collect(&:node) + .reverse +end+
# File app/models/drupal_user.rb, line 83 +def likes + NodeSelection.where(user_id: uid, liking: true) +end+
# File app/models/drupal_user.rb, line 170 +def migrate + u = User.new(username: name, + id: uid, + email: mail, + openid_identifier: '//old.publiclab.org/user/' + uid.to_s + '/identity') + u.persistence_token = rand(100_000_000) + if u.save(validate: false) # <= because validations checks for conflict with existing drupal_user.name + key = u.generate_reset_key + PasswordResetMailer.reset_notify(u, key) + return true + else + return false + end +end+
# File app/models/drupal_user.rb, line 47 +def moderate + self.status = 5 + save + # user is logged out next time they access current_user in a controller; see application controller + self +end+
# File app/models/drupal_user.rb, line 126 +def node_count + Node.where(status: 1, uid: uid).count + Revision.where(uid: uid).count +end+
# File app/models/drupal_user.rb, line 122 +def note_count + Node.where(status: 1, uid: uid, type: 'note').count +end+
# File app/models/drupal_user.rb, line 118 +def notes + user.notes +end+
# File app/models/drupal_user.rb, line 114 +def profile_values + drupal_profile_values +end+
End rails-style adaptors
+ + + + +# File app/models/drupal_user.rb, line 43 +def role + user.role if user +end+
# File app/models/drupal_user.rb, line 156 +def tag_counts + tags = {} + Node.order('nid DESC').where(type: 'note', status: 1, uid: uid).limit(20).each do |node| + node.tags.each do |tag| + if tags[tag.name] + tags[tag.name] += 1 + else + tags[tag.name] = 1 + end + end + end + tags +end+
# File app/models/drupal_user.rb, line 152 +def tagnames(limit = 20, defaults = true) + self.user.tagnames(limit, defaults) +end+
# File app/models/drupal_user.rb, line 68 +def unban + self.status = 1 + increase_likes_unbanned + save + self +end+
# File app/models/drupal_user.rb, line 54 +def unmoderate + self.status = 1 + save + self +end+
# File app/models/drupal_user.rb, line 19 +def user + User.where(username: name).first +end+
# File app/models/drupal_user.rb, line 27 +def username + name +end+
# File app/models/drupal_user.rb, line 31 +def using_new_site? + !User.find_by(username: name).nil? +end+
# File app/controllers/editor_controller.rb, line 24 +def editor + redirect_to "/post?#{request.env['QUERY_STRING']}" +end+
main image via URL passed as GET param
+ + + + +# File app/controllers/editor_controller.rb, line 5 +def legacy + # /post/?i=http://myurl.com/image.jpg + flash.now[:notice] = "This is the legacy editor. For the new rich editor, <a href='/editor'>click here</a>." + if params[:i] + @image = Image.new(remote_url: params[:i], + uid: current_user.uid) + flash[:error] = 'The image could not be saved.' unless @image.save! + end + if params[:n] && !params[:body] # use another node body as a template + node = Node.find(params[:n]) + params[:body] = node.body if node + end + if params[:tags] && params[:tags].include?('question:') + redirect_to "/questions/new?#{request.env['QUERY_STRING']}" + else + render template: 'editor/post' + end +end+
# File app/controllers/editor_controller.rb, line 28 +def post + if params[:tags] && params[:tags].include?('question:') + redirect_to "/questions/new?#{request.env['QUERY_STRING']}" + elsif params[:legacy] || params[:template] == 'event' + legacy + else + rich + render '/editor/rich' + end +end+
# File app/controllers/editor_controller.rb, line 39 +def rich + flash.now[:notice] = "This is the new rich editor. For the legacy editor, <a href='/post?#{request.env['QUERY_STRING']}&legacy=true' class='legacy-button'>click here</a>." + if params[:main_image] && Image.find_by(id: params[:main_image]) + @main_image = Image.find_by(id: params[:main_image]).path + end + if params[:n] && !params[:body] # use another node body as a template + node = Node.find(params[:n]) + params[:body] = node.body if node + end +end+
# File app/controllers/features_controller.rb, line 30 +def create + if current_user.role != 'admin' + flash[:warning] = 'Only admins may edit features.' + redirect_to '/features?_=' + Time.now.to_i.to_s + else + @node = Node.new(uid: current_user.id, + title: params[:title], + type: 'feature') + if @node.valid? + saved = true + @revision = false + ActiveRecord::Base.transaction do + @node.save! + @revision = @node.new_revision(uid: current_user.id, + title: params[:title], + body: params[:body]) + if @revision.valid? + @revision.save! + @node.vid = @revision.vid + @node.save! + else + saved = false + @node.destroy + end + end + end + if saved + flash[:notice] = 'Feature saved.' + redirect_to '/features?_=' + Time.now.to_i.to_s + else + render template: 'features/new' + end + end +end+
# File app/controllers/features_controller.rb, line 21 +def edit + if current_user.role != 'admin' + flash[:warning] = 'Only admins may edit features.' + redirect_to '/features' + else + @node = Node.find params[:id] + end +end+
# File app/controllers/features_controller.rb, line 9 +def embed + @node = Node.find_by(title: params[:id]) + render layout: false +end+
# File app/controllers/features_controller.rb, line 4 +def index + @features = Node.where(type: 'feature') + .paginate(page: params[:page]) +end+
# File app/controllers/features_controller.rb, line 14 +def new + if current_user.role != 'admin' + flash[:warning] = 'Only admins may edit features.' + redirect_to '/features' + end +end+
# File app/controllers/features_controller.rb, line 65 +def update + if current_user.role != 'admin' + flash[:warning] = 'Only admins may edit features.' + redirect_to '/features?_=' + Time.now.to_i.to_s + else + @node = Node.find(params[:id]) + @revision = @node.new_revision(uid: current_user.uid) + @revision.title = params[:title] || @node.latest.title + @revision.body = params[:body] if params[:body] + if @revision.valid? + ActiveRecord::Base.transaction do + @revision.save + @node.vid = @revision.vid + @node.title = @revision.title + @node.save + end + ActionController::Base.new.expire_fragment("feature_#{params[:title]}") + flash[:notice] = 'Edits saved and cache cleared.' + redirect_to '/features?_=' + Time.now.to_i.to_s + else + flash[:error] = 'Your edit could not be saved.' + render action: 'edit' + end + end +end+
# File app/controllers/home_controller.rb, line 76 +def activity + blog = Tag.find_nodes_by_type('blog', 'note', 1).first + # remove "classroom" postings; also switch to an EXCEPT operator in sql, see https://github.com/publiclab/plots2/issues/375 + hidden_nids = Node.joins(:node_tag) + .joins('LEFT OUTER JOIN term_data ON term_data.tid = community_tags.tid') + .select('node.*, term_data.*, community_tags.*') + .where(type: 'note', status: 1) + .where('term_data.name = (?)', 'hidden:response') + .collect(&:nid) + notes = Node.where(type: 'note') + .where('node.nid NOT IN (?)', hidden_nids + [0]) # in case hidden_nids is empty + .order('nid DESC') + .page(params[:page]) + notes = notes.where('nid != (?)', blog.nid) if blog + + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + notes = notes.where('(node.status = 1 OR node.status = 4)') + elsif current_user + notes = notes.where('(node.status = 1 OR (node.status = 4 AND node.uid = ?))', current_user.uid) + else + notes = notes.where('node.status = 1') + end + notes = notes.to_a # ensure it can be serialized for caching + + # include revisions, then mix with new pages: + wikis = Node.where(type: 'page', status: 1) + .order('nid DESC') + .limit(10) + revisions = Revision.joins(:node) + .order('timestamp DESC') + .where('type = (?)', 'page') + .where('node.status = 1') + .where('node_revisions.status = 1') + .where('timestamp - node.created > ?', 300) # don't report edits within 5 mins of page creation + .limit(10) + .group('node.title') + # group by day: http://stackoverflow.com/questions/5970938/group-by-day-from-timestamp + revisions = revisions.group('DATE(FROM_UNIXTIME(timestamp))') if Rails.env == 'production' + revisions = revisions.to_a # ensure it can be serialized for caching + wikis += revisions + wikis = wikis.sort_by(&:created_at).reverse + comments = Comment.joins(:node, :drupal_user) + .order('timestamp DESC') + .where('timestamp - node.created > ?', 86_400) # don't report edits within 1 day of page creation + .page(params[:page]) + .group('title') # group by day: http://stackoverflow.com/questions/5970938/group-by-day-from-timestamp + # group by day: http://stackoverflow.com/questions/5970938/group-by-day-from-timestamp + comments = comments.group('DATE(FROM_UNIXTIME(timestamp))') if Rails.env == 'production' + comments = comments.to_a # ensure it can be serialized for caching + answer_comments = Comment.joins(:answer, :drupal_user) + .order('timestamp DESC') + .where('timestamp - answers.created_at > ?', 86_400) + .limit(20) + .group('answers.id') + answer_comments = answer_comments.group('DATE(FROM_UNIXTIME(timestamp))') if Rails.env == 'production' + answer_comments = answer_comments.to_a # ensure it can be serialized for caching + activity = (notes + wikis + comments + answer_comments).sort_by(&:created_at).reverse + response = [ + activity, + blog, + notes, + wikis, + revisions, + comments, + answer_comments + ] + response +end+
# File app/controllers/home_controller.rb, line 50 +def dashboard + @note_count = Node.select(%[created type status]) + .where(type: 'note', status: 1, created: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) + .count(:all) + @wiki_count = Revision.select(:timestamp) + .where(timestamp: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) + .count + @user_note_count = Node.where(type: 'note', status: 1, uid: current_user.uid).count if current_user + @activity, @blog, @notes, @wikis, @revisions, @comments, @answer_comments = self.activity + render template: 'dashboard/dashboard' + @title = I18n.t('home_controller.community_research') unless current_user +end+
# File app/controllers/home_controller.rb, line 46 +def dashboard2 + redirect_to '/dashboard' +end+
proxy to enable AJAX loading of RSS feeds, which requires same-origin
+ + + + +# File app/controllers/home_controller.rb, line 25 +def fetch + if true # Rails.env.production? + if params[:url][0..24] == 'https://groups.google.com' || params[:url] == 'https://feeds.feedburner.com/rssmixer/ZvcX' + url = URI.parse(params[:url]) + result = Net::HTTP.get_response(url) + send_data result.body, type: result.content_type, disposition: 'inline' + end + else + redirect_to params[:url] + end +end+
route for seeing the front page even if you are logged in
+ + + + +# File app/controllers/home_controller.rb, line 38 +def front + @title = I18n.t('home_controller.environmental_investigation') + @activity, @blog, @notes, @wikis, @revisions, @comments, @answer_comments = Rails.cache.fetch("front-activity", expires_in: 30.minutes) do + self.activity + end + render template: 'home/home' +end+
caches_action :index, :cache_path => { :last => +Node.find(:last).updated_at.to_i }
+ + + + +# File app/controllers/home_controller.rb, line 11 +def home + @activity, @blog, @notes, @wikis, @revisions, @comments, @answer_comments = Rails.cache.fetch("front-activity", expires_in: 30.minutes) do + self.activity + end + + @title = I18n.t('home_controller.science_community') + if current_user + redirect_to '/dashboard' + else + render template: 'home/home' + end +end+
trashy… clean this up! this will eventually be based on the profile_tags +data where people can mark their location with “location:lat,lon”
+ + + + +# File app/controllers/home_controller.rb, line 65 +def nearby + if current_user.lat + dist = 1.5 + minlat = current_user.lat - dist + maxlat = current_user.lat + dist + minlon = current_user.lon - dist + maxlon = current_user.lon + dist + @users = DrupalUser.where('lat != 0.0 AND lon != 0.0 AND lat > ? AND lat < ? AND lon > ? AND lon < ?', minlat, maxlat, minlon, maxlon) + end +end+
# File app/models/image.rb, line 45 +def filename + photo_file_name +end+
# File app/models/image.rb, line 32 +def filetype + filename[-3..filename.length].downcase +end+
# File app/models/image.rb, line 28 +def is_image? + (filetype == 'jpg' || filetype == 'jpeg' || filetype == 'gif' || filetype == 'png') +end+
# File app/models/image.rb, line 36 +def path(size = :medium) + if is_image? + size = :medium if size == :default + else + size = :original + end + absolute_uri + photo.url(size) +end+
# File app/controllers/images_controller.rb, line 7 +def create + if params[:i] + @image = Image.new(remote_url: params[:i], + uid: current_user.uid) + flash[:error] = 'The image could not be saved.' unless @image.save! + else + @image = Image.new(uid: current_user.uid, + photo: params[:image][:photo], + title: params[:image][:title], + notes: params[:image][:notes]) + end + @image.nid = Node.find(params[:nid].to_i).nid unless params[:nid].nil? || params[:nid] == 'undefined' + if @image.save! + render json: { + id: @image.id, + url: @image.path(:large), + filename: @image.photo_file_name, + href: @image.path(:large), # Woofmark/PublicLab.Editor + title: @image.photo_file_name, + results: [{ # Woofmark/PublicLab.Editor + href: @image.path(:large), + title: @image.photo_file_name + }] + } + else + render text: 'The image could not be saved.' + end +end+
# File app/controllers/images_controller.rb, line 40 +def delete + @image = Image.find params[:id] + if @image.uid == current_user.uid # or current_user.role == "admin" + if @image.delete + flash[:notice] = 'Image deleted.' + else + flash[:error] = 'The image could not be deleted.' + end + redirect_to '/post' + else + prompt_login 'Only the owner can delete this image.' + end +end+
# File app/controllers/images_controller.rb, line 36 +def new + @image = Image.new +end+
# File app/controllers/legacy_controller.rb, line 39 +def file + redirect_to "//#{request.host}/sites/default/files/" + params[:filename] + '.' + params[:format], status: 301 +end+
publiclaboratory.org/node/5853
+ + + + +# File app/controllers/legacy_controller.rb, line 53 +def node + node = Node.find params[:id] + redirect_to node.path, status: 301 +end+
# File app/controllers/legacy_controller.rb, line 10 +def note_add + redirect_to '/post', status: 301 +end+
# File app/controllers/legacy_controller.rb, line 2 +def notes + if params[:id] + redirect_to '/tag/' + params[:id], status: 301 + else + redirect_to '/research', status: 301 + end +end+
# File app/controllers/legacy_controller.rb, line 30 +def openid + user = User.find params[:id] + redirect_to '/openid/' + user.username, status: 301 +end+
# File app/controllers/legacy_controller.rb, line 35 +def openid_username + redirect_to '/openid/' + params[:username], status: 301 +end+
# File app/controllers/legacy_controller.rb, line 14 +def page_add + redirect_to '/wiki/new', status: 301 +end+
# File app/controllers/legacy_controller.rb, line 18 +def people + redirect_to '/profile/' + params[:id], status: 301 +end+
# File app/controllers/legacy_controller.rb, line 22 +def place + redirect_to '/wiki/' + params[:id], status: 301 +end+
def image
+ +# sites/default/files/imagecache/thumb/san-martin-spectro.jpg +redirect_to "//i.publiclab.org/sites/default/files/imagecache/"+params[:size]+"/"+params[:filename]+"."+params[:format], :status => 301 ++ +
end
+ + + + +# File app/controllers/legacy_controller.rb, line 48 +def register + redirect_to '/signup', status: 301 +end+
# File app/controllers/legacy_controller.rb, line 58 +def report + @node = DrupalUrlAlias.find_by(dst: 'report/' + params[:id]).node + redirect_to '/notes/' + @node.author.name.downcase + '/' + Time.at(@node.created_at).strftime('%m-%d-%Y') + '/' + params[:id], status: 301 +end+
# File app/controllers/legacy_controller.rb, line 63 +def rss + redirect_to '/feed.rss', status: 301 +end+
# File app/controllers/legacy_controller.rb, line 26 +def tool + redirect_to '/wiki/' + params[:id], status: 301 +end+
for the current user, register as liking the given node
+ + + + +# File app/controllers/like_controller.rb, line 29 +def create + render json: Node.like(params[:id] , current_user) +end+
for the current user, remove the like from the given node
+ + + + +# File app/controllers/like_controller.rb, line 34 +def delete + render json: Node.unlike(params[:id] , current_user) +end+
list all recent likes
+ + + + +# File app/controllers/like_controller.rb, line 6 +def index + @paginated = true + @likes = NodeSelection.all.reverse.paginate(page: params[:page]) +end+
for the current user, return whether is presently liked or not
+ + + + +# File app/controllers/like_controller.rb, line 18 +def liked? + result = NodeSelection.find_by_user_id_and_nid(current_user.uid, params[:id]) + result = if result.nil? + false + else + result.liking + end + render json: result +end+
return a count of likes for a given node This does not support non-nodes +very well
+ + + + +# File app/controllers/like_controller.rb, line 13 +def show + render json: Node.find(params[:id]).cached_likes +end+
must require min_zoom and lat/lon location, and TMS URL solving this by +min_zoom default here, but need better solution
+ + + + +# File app/controllers/map_controller.rb, line 127 +def create + if current_user && current_user.role == 'admin' + saved, @node, @revision = Node.new_node(uid: current_user.uid, + title: params[:title], + body: params[:body], + type: 'map', + main_image: params[:main_image]) + + if saved + if params[:tags] + params[:tags].split(',').each do |tagname| + @node.add_tag(tagname, current_user) + end + end + + # save main image + if params[:main_image] && params[:main_image] != '' + img = Image.find params[:main_image] + unless img.nil? + img.nid = @node.id + img.save + end + end + + @node.add_tag('lat:' + params[:lat], current_user) + @node.add_tag('lon:' + params[:lon], current_user) + + map = DrupalContentTypeMap.new + map.nid = @node.nid + map.vid = @node.nid + + map.field_publication_date_value = params[:map][:field_publication_date_value] + map.field_capture_date_value = params[:map][:field_capture_date_value] + map.field_geotiff_url_value = params[:map][:field_geotiff_url_value] + map.field_google_maps_url_value = params[:map][:field_google_maps_url_value] + map.field_openlayers_url_value = params[:map][:field_openlayers_url_value] + map.field_tms_url_value = params[:map][:field_tms_url_value] + map.field_jpg_url_value = params[:map][:field_jpg_url_value] + map.field_license_value = params[:map][:field_license_value] + map.field_raw_images_value = params[:map][:field_raw_images_value] + map.field_cartographer_notes_value = params[:map][:field_cartographer_notes_value] + map.field_notes_value = params[:map][:field_notes_value] + map.field_mbtiles_url_value = params[:map][:field_mbtiles_url_value] + map.field_zoom_min_value = params[:map][:field_zoom_min_value] + map.field_zoom_min_value ||= 17 + map.field_ground_resolution_value = params[:map][:field_ground_resolution_value] + map.field_geotiff_filesize_value = params[:map][:field_geotiff_filesize_value] + map.field_jpg_filesize_value = params[:map][:field_jpg_filesize_value] + map.field_raw_images_filesize_value = params[:map][:field_raw_images_filesize_value] + map.field_tms_tile_type_value = params[:map][:field_tms_tile_type_value] + map.field_zoom_max_value = params[:map][:field_zoom_max_value] + + # need to create/delete these. Maybe best just make a new field, no need to store individual records + # @node.drupal_content_field_map_editor + # @node.drupal_content_field_mappers.collect(&:field_mappers_value).uniq.join(', ') + map.authorship = params[:map][:authorship] + + ActiveRecord::Base.transaction do # in case only part of this completes + if @node.save && @revision.save && map.save + flash[:notice] = 'Edits saved.' + redirect_to @node.path + else + flash[:error] = 'Your edit could not be saved.' + render action: :edit + end + end + else + flash[:error] = 'Your edit could not be saved.' + render template: 'map/edit' + end + else + prompt_login 'Only admins can publish maps at this time.' + end +end+
# File app/controllers/map_controller.rb, line 39 +def delete + @node = Node.find_by(id: params[:id]) + if current_user.uid == @node.uid || current_user.role == 'admin' + @node.delete + flash[:notice] = 'Content deleted.' + redirect_to '/archive' + else + prompt_login 'Only admins can edit maps at this time.' + end +end+
# File app/controllers/map_controller.rb, line 30 +def edit + @node = Node.find_by(id: params[:id]) + if current_user.uid == @node.uid || current_user.role == 'admin' + render template: 'map/edit' + else + prompt_login 'Only admins can edit maps at this time.' + end +end+
# File app/controllers/map_controller.rb, line 2 +def index + @title = 'Maps' + @nodes = Node.paginate(page: params[:page], per_page: 32) + .order('nid DESC') + .where(type: 'map', status: 1) + + # I'm not sure if this is actually eager loading the tags... + @maps = Node.joins(:tag) + .where('type = "map" AND status = 1 AND (term_data.name LIKE ? OR term_data.name LIKE ?)', 'lat:%', 'lon:%') + .uniq + + # This is supposed to eager load the url_aliases, and seems to run, but doesn't actually eager load them...? + # @maps = Node.select("node.*,url_alias.dst AS dst").joins(:tag).where('type = "map" AND status = 1 AND (term_data.name LIKE ? OR term_data.name LIKE ?)', 'lat:%', 'lon:%').joins("INNER JOIN url_alias ON url_alias.src = CONCAT('node/',node.nid)") +end+
# File app/controllers/map_controller.rb, line 116 +def new + if current_user && current_user.role == 'admin' + @node = Node.new(type: 'map') + render template: 'map/edit' + else + prompt_login 'Only admins can publish maps at this time.' + end +end+
# File app/controllers/map_controller.rb, line 17 +def show + @node = Node.find_map(params[:name], params[:date]) + + # redirect_old_urls + + impressionist(@node) + @title = @node.title + @tags = @node.tags + @tagnames = @tags.collect(&:name) + + set_sidebar :tags, @tagnames +end+
# File app/controllers/map_controller.rb, line 202 +def tag + set_sidebar :tags, [params[:id]], note_count: 20 + + @tagnames = params[:id].split(',') + nids = Tag.find_nodes_by_type(params[:id], 'map', 20).collect(&:nid) + @notes = Node.paginate(page: params[:page]) + .where('nid in (?)', nids) + .order('nid DESC') + + @title = @tagnames.join(', ') if @tagnames + @unpaginated = true + render template: 'tag/show' +end+
# File app/controllers/map_controller.rb, line 50 +def update + @node = Node.find_by(id: params[:id]) + if current_user.uid == @node.uid || current_user.role == 'admin' + + @node.title = params[:title] + @revision = @node.latest + @revision.title = params[:title] + @revision.body = params[:body] + + if params[:tags] + params[:tags].split(',').each do |tagname| + @node.add_tag(tagname, current_user) + end + end + + # save main image + if params[:main_image] && params[:main_image] != '' + img = Image.find params[:main_image] + unless img.nil? + img.nid = @node.id + img.save + end + end + + @node.add_tag('lat:' + params[:lat], current_user) + @node.add_tag('lon:' + params[:lon], current_user) + + map = @node.map + map.field_publication_date_value = params[:map][:field_publication_date_value] + map.field_capture_date_value = params[:map][:field_capture_date_value] + map.field_geotiff_url_value = params[:map][:field_geotiff_url_value] + map.field_google_maps_url_value = params[:map][:field_google_maps_url_value] + map.field_openlayers_url_value = params[:map][:field_openlayers_url_value] + map.field_tms_url_value = params[:map][:field_tms_url_value] + map.field_jpg_url_value = params[:map][:field_jpg_url_value] + map.field_license_value = params[:map][:field_license_value] + map.field_raw_images_value = params[:map][:field_raw_images_value] + map.field_cartographer_notes_value = params[:map][:field_cartographer_notes_value] + map.field_notes_value = params[:map][:field_notes_value] + map.field_mbtiles_url_value = params[:map][:field_mbtiles_url_value] + map.field_zoom_min_value = params[:map][:field_zoom_min_value] + map.field_ground_resolution_value = params[:map][:field_ground_resolution_value] + map.field_geotiff_filesize_value = params[:map][:field_geotiff_filesize_value] + map.field_jpg_filesize_value = params[:map][:field_jpg_filesize_value] + map.field_raw_images_filesize_value = params[:map][:field_raw_images_filesize_value] + map.field_tms_tile_type_value = params[:map][:field_tms_tile_type_value] + map.field_zoom_max_value = params[:map][:field_zoom_max_value] + + # need to create/delete these. Maybe best just make a new field, no need to store individual records + # @node.drupal_content_field_map_editor + # @node.drupal_content_field_mappers.collect(&:field_mappers_value).uniq.join(', ') + # combined record as string: + map.authorship = params[:map][:authorship] + + if @node.save && @revision.save && map.save + flash[:notice] = 'Edits saved.' + redirect_to @node.path + else + flash[:error] = 'Your edit could not be saved.' + render action: :edit + end + else + prompt_login 'Only admins can edit maps at this time.' + end +end+
so we can call #activities('balloon-mapping')
+ + + + +# File app/models/node.rb, line 752 +def self.activities(tagname) + Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:term_data) + .where('term_data.name LIKE ?', "activity:#{tagname}") +end+
# File app/models/node.rb, line 462 +def self.find_by_path(title) + Node.where(path: ["/#{title}"]).first +end+
# File app/models/node.rb, line 694 +def self.find_map(name, date) + Node.where(path: "/map/#{name}/#{date}").first +end+
# File app/models/node.rb, line 690 +def self.find_notes(author, date, title) + Node.where(path: "/notes/#{author}/#{date}/#{title}").first +end+
# File app/models/node.rb, line 698 +def self.find_wiki(title) + Node.where(path: ["/#{title}", "/tool/#{title}", "/wiki/#{title}", "/place/#{title}"]).first +end+
making drupal and rails database conventions play nice; 'type' is a +reserved word in rails
+ + + + +# File app/models/node.rb, line 67 +def self.inheritance_column + 'rails_type' +end+
# File app/models/node.rb, line 58 +def instance_method_already_implemented?(method_name) + return true if method_name == 'changed' + return true if method_name == 'changed?' + super +end+
# File app/models/node.rb, line 825 +def self.like(nid , user) + # scope like variable outside the transaction + like = nil + count = nil + + ActiveRecord::Base.transaction do + # Create the entry if it isn't already created. + like = NodeSelection.where(user_id: user.uid, + nid: nid).first_or_create + like.liking = true + node = Node.find(nid) + if node.type == 'note' + SubscriptionMailer.notify_note_liked(node, like.user) + end + count = 1 + node.toggle_like(like.user) + # Save the changes. + node.save! + like.save! + end + count +end+
same as ::new_note or ::new_wiki but with arbitrary type – +use for maps, DRY out ::new_note +and ::new_wiki
+ + + + +# File app/models/node.rb, line 609 +def self.new_node(params) + saved = false + node = Node.new(uid: params[:uid], + title: params[:title], + type: params[:type]) + if node.valid? + revision = false + saved = true + ActiveRecord::Base.transaction do + node.save! + revision = node.new_revision(nid: node.id, + uid: params[:uid], + title: params[:title], + body: params[:body]) + if revision.valid? + revision.save! + node.vid = revision.vid + node.save! + else + saved = false + node.destroy # clean up + end + end + end + [saved, node, revision] +end+
handle creating a new note with attached revision and main image this is +kind of egregiously bad… must revise after researching simultaneous +creation of associated records
+ + + + +# File app/models/node.rb, line 544 +def self.new_note(params) + saved = false + author = DrupalUser.find(params[:uid]) + node = Node.new(uid: author.uid, + title: params[:title], + comment: 2, + type: 'note') + node.status = 4 if author.first_time_poster + if node.valid? # is this not triggering title uniqueness validation? + saved = true + revision = false + ActiveRecord::Base.transaction do + node.save! + revision = node.new_revision(uid: author.uid, + title: params[:title], + body: params[:body]) + if revision.valid? + revision.save! + node.vid = revision.vid + # save main image + if params[:main_image] && (params[:main_image] != '') + img = Image.find params[:main_image] + img.nid = node.id + img.save + end + node.save! + node.notify + else + saved = false + node.destroy + end + end + end + [saved, node, revision] +end+
# File app/models/node.rb, line 580 +def self.new_wiki(params) + saved = false + node = Node.new(uid: params[:uid], + title: params[:title], + type: 'page') + if node.valid? + revision = false + saved = true + ActiveRecord::Base.transaction do + node.save! + revision = node.new_revision(nid: node.id, + uid: params[:uid], + title: params[:title], + body: params[:body]) + if revision.valid? + revision.save! + node.vid = revision.vid + node.save! + # node.notify # we don't yet notify of wiki page creations + else + saved = false + node.destroy # clean up + end + end + end + [saved, node, revision] +end+
all questions
+ + + + +# File app/models/node.rb, line 732 +def self.questions + questions = Node.where(type: 'note') + .joins(:tag) + .where('term_data.name LIKE ?', 'question:%') + .group('node.nid') +end+
# File app/models/node.rb, line 702 +def self.research_notes + nids = Node.where(type: 'note') + .joins(:tag) + .where('term_data.name LIKE ?', 'question:%') + .group('node.nid') + .collect(&:nid) + notes = Node.where(type: 'note') + .where('node.nid NOT IN (?)', nids) +end+
# File app/models/node.rb, line 23 +def self.search(query) + Revision.where('MATCH(node_revisions.body, node_revisions.title) AGAINST(?)', query) +end+
# File app/models/node.rb, line 848 +def self.unlike(nid , user) + like = nil + count = nil + + ActiveRecord::Base.transaction do + like = NodeSelection.where(user_id: user.uid, + nid: nid).first_or_create + like.liking = false + count = -1 + node = Node.find(nid) + node.toggle_like(like.user) + node.save! + like.save! + end + count +end+
so we can call #upgrades('balloon-mapping')
+ + + + +# File app/models/node.rb, line 772 +def self.upgrades(tagname) + Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:term_data) + .where('term_data.name LIKE ?', "upgrade:#{tagname}") +end+
# File app/models/node.rb, line 132 +def self.weekly_tallies(type = 'note', span = 52, time = Time.now) + weeks = {} + (0..span).each do |week| + weeks[span - week] = Node.select(:created) + .where(type: type, + status: 1, + created: time.to_i - week.weeks.to_i..time.to_i - (week - 1).weeks.to_i) + .count + end + weeks +end+
so we can quickly fetch activities corresponding to this node with +node.activities
+ + + + +# File app/models/node.rb, line 741 +def activities + # override with a tag like `activities:h2s` + if self.has_power_tag('activities') + tagname = node.power_tag('activities') + else + tagname = self.slug_from_path + end + Node.activities(tagname) +end+
# File app/models/node.rb, line 644 +def add_barnstar(tagname, giver) + add_tag(tagname, giver.drupal_user) + CommentMailer.notify_barnstar(giver, self) +end+
Automated constructors for associated models
+ + + + +# File app/models/node.rb, line 513 +def add_comment(params = {}) + thread = if !comments.empty? && !comments.last.nil? + comments.last.next_thread + else + '01/' + end + c = Comment.new(pid: 0, + nid: nid, + uid: params[:uid], + subject: '', + hostname: '', + comment: params[:body], + status: 0, + format: 1, + thread: thread, + timestamp: DateTime.now.to_i) + c.save + c +end+
# File app/models/node.rb, line 649 +def add_tag(tagname, user) + tagname = tagname.downcase + unless has_tag_without_aliasing(tagname) + saved = false + tag = Tag.find_by(name: tagname) || Tag.new(vid: 3, # vocabulary id; 1 + name: tagname, + description: '', + weight: 0) + + ActiveRecord::Base.transaction do + if tag.valid? + if tag.name.split(':')[0] == 'date' + begin + DateTime.strptime(tag.name.split(':')[1], '%m-%d-%Y').to_date.to_s(:long) + rescue + return [false, tag.destroy] + end + end + tag.save! + node_tag = NodeTag.new(tid: tag.id, + uid: user.uid, + date: DateTime.now.to_i, + nid: id) + if node_tag.save + saved = true + SubscriptionMailer.notify_tag_added(self, tag, user) unless tag.subscriptions.empty? + else + saved = false + tag.destroy + end + end + end + return [saved, tag] + end +end+
# File app/models/node.rb, line 168 +def answered + self.answers && self.answers.length > 0 +end+
# File app/models/node.rb, line 636 +def barnstar + power_tag_objects('barnstar').first +end+
# File app/models/node.rb, line 640 +def barnstars + power_tag_objects('barnstar') +end+
# File app/models/node.rb, line 244 +def body + latest.body if latest +end+
# File app/models/node.rb, line 712 +def body_preview(length = 100) + try(:latest).body_preview(length) +end+
# File app/models/node.rb, line 779 +def can_tag(tagname, user, errors = false) + if tagname[0..4] == 'with:' + if User.find_by_username_case_insensitive(tagname.split(':')[1]).nil? + errors ? I18n.t('node.cannot_find_username') : false + elsif author.uid != user.uid + errors ? I18n.t('node.only_author_use_powertag') : false + elsif tagname.split(':')[1] == user.username + errors ? I18n.t('node.cannot_add_yourself_coauthor') : false + else + true + end + elsif tagname[0..4] == 'rsvp:' && user.username != tagname.split(':')[1] + errors ? I18n.t('node.only_RSVP_for_yourself') : false + elsif tagname == 'locked' && user.role != 'admin' + errors ? I18n.t('node.only_admins_can_lock') : false + elsif tagname.split(':')[0] == 'redirect' && Node.where(slug: tagname.split(':')[1]).length <= 0 + errors ? I18n.t('node.page_does_not_exist') : false + else + true + end +end+
# File app/models/node.rb, line 202 +def comment_count + comments + .count +end+
# File app/models/node.rb, line 236 +def created_at + Time.at(created) +end+
was unable to set up this relationship properly with ActiveRecord associations
+ + + + +# File app/models/node.rb, line 269 +def drupal_content_field_image_gallery + DrupalContentFieldImageGallery.where(nid: nid) + .order('field_image_gallery_fid') +end+
was unable to set up this relationship properly with ActiveRecord associations
+ + + + +# File app/models/node.rb, line 249 +def drupal_main_image + DrupalMainImage.order('vid') + .where('nid = ? AND field_main_image_fid IS NOT NULL', nid) + .last +end+
# File app/models/node.rb, line 453 +def edit_path + path = if type == 'page' || type == 'tool' || type == 'place' + '/wiki/edit/' + self.path.split('/').last + else + '/notes/edit/' + id.to_s + end + path +end+
# File app/models/node.rb, line 164 +def files + drupal_files +end+
# File app/models/node.rb, line 274 +def gallery + if !drupal_content_field_image_gallery.empty? && drupal_content_field_image_gallery.first.field_image_gallery_fid + drupal_content_field_image_gallery + else + [] + end +end+
should only be run at actual creation time – or, we should refactor to us +node.created instead of Time.now
+ + + + +# File app/models/node.rb, line 91 +def generate_path + if type == 'note' + username = DrupalUser.find_by(uid: uid).name + "/notes/#{username}/#{Time.now.strftime('%m-%d-%Y')}/#{title.parameterize}" + elsif type == 'page' + '/wiki/' + title.parameterize + elsif type == 'map' + "/map/#{title.parameterize}/#{Time.now.strftime('%m-%d-%Y')}" + elsif type == 'feature' + "/feature/#{title.parameterize}" + end +end+
# File app/models/node.rb, line 172 +def has_accepted_answers + self.answers.where(accepted: true).count > 0 +end+
power tags have “key:value” format, and should be searched with a “key:*” +wildcard
+ + + + +# File app/models/node.rb, line 312 +def has_power_tag(key) + tids = Tag.includes(:node_tag) + .references(:community_tags) + .where('community_tags.nid = ? AND name LIKE ?', id, key + ':%') + .collect(&:tid) + !NodeTag.where('nid = ? AND tid IN (?)', id, tids).empty? +end+
accests a tagname /or/ tagname ending in wildcard such as “tagnam*” also +searches for other tags whose parent field matches given tagname, but not +tags matching given tag's parent field
+ + + + +# File app/models/node.rb, line 370 +def has_tag(tagname) + tags = get_matching_tags_without_aliasing(tagname) + # search for tags with parent matching this + tags += Tag.includes(:node_tag) + .references(:community_tags) + .where('community_tags.nid = ? AND parent LIKE ?', id, tagname) + # search for parent tag of this, if exists + # tag = Tag.where(name: tagname).try(:first) + # if tag && tag.parent + # tags += Tag.includes(:node_tag) + # .references(:community_tags) + # .where("community_tags.nid = ? AND name LIKE ?", self.id, tag.parent) + # end + tids = tags.collect(&:tid).uniq + !NodeTag.where('nid IN (?) AND tid IN (?)', id, tids).empty? +end+
# File app/models/node.rb, line 402 +def has_tag_without_aliasing(tagname) + tags = get_matching_tags_without_aliasing(tagname) + tids = tags.collect(&:tid).uniq + !NodeTag.where('nid IN (?) AND tid IN (?)', id, tids).empty? +end+
used in typeahead autocomplete search results
+ + + + +# File app/models/node.rb, line 420 +def icon + icon = 'file' if type == 'note' + icon = 'book' if type == 'page' + icon = 'map-marker' if type == 'map' + icon = 'flag' if has_tag('chapter') + icon = 'wrench' if type == 'tool' + icon = 'question-circle' if has_power_tag('question') + icon +end+
view adaptors for typical rails db conventions so we can migrate someday
+ + + + +# File app/models/node.rb, line 232 +def id + nid +end+
# File app/models/node.rb, line 812 +def is_liked_by(user) + !NodeSelection.where(user_id: user.uid, nid: self.id , liking: true).empty? +end+
# File app/models/node.rb, line 472 +def lat + if has_power_tag('lat') + power_tag('lat').to_f + else + false + end +end+
# File app/models/node.rb, line 186 +def latest + revisions + .where(status: 1) + .first +end+
users who like this node
+ + + + +# File app/models/node.rb, line 177 +def likers + node_selections + .joins(:drupal_user) + .references(:users) + .where(liking: true) + .where('users.status = ?', 1) + .collect(&:user) +end+
# File app/models/node.rb, line 480 +def lon + if has_power_tag('lon') + power_tag('lon').to_f + else + false + end +end+
has it been tagged with “list:foo” where “foo” is the name of a Google +Group?
+ + + + +# File app/models/node.rb, line 409 +def mailing_list + Rails.cache.fetch('feed-' + id.to_s + '-' + (updated_at.to_i / 300).to_i.to_s) do + RSS::Parser.parse(open('https://groups.google.com/group/' + power_tag('list') + '/feed/rss_v2_0_topics.xml').read, false).items + end +rescue + return [] +end+
provide either a Drupally #main_image or a Railsy one
+ + + + +# File app/models/node.rb, line 256 +def main_image(node_type = :all) + if !images.empty? && node_type != :drupal + if main_image_id.blank? + images.order('vid').last + else + images.where(id: main_image_id).first + end + elsif drupal_main_image && node_type != :rails + drupal_main_image.drupal_file + end +end+
# File app/models/node.rb, line 466 +def map + # This fires off a query that orders by vid DESC + # and is quicker than doing .order(vid: :DESC) for some reason. + drupal_content_type_map.last +end+
# File app/models/node.rb, line 685 +def mentioned_users + usernames = body.scan(Callouts.const_get(:FINDER)) + User.where(username: usernames.map { |m| m[1] }).uniq +end+
# File app/models/node.rb, line 533 +def new_revision(params) + title = params[:title] || self.title + Revision.new(nid: id, + uid: params[:uid], + title: title, + body: params[:body]) +end+
# File app/models/node.rb, line 144 +def notify + if status == 4 + AdminMailer.notify_node_moderators(self) + else + SubscriptionMailer.notify_node_creation(self) + end +end+
can switch to a “question-style” path if specified
+ + + + +# File app/models/node.rb, line 80 +def path(type = :default) + if type == :question + self[:path].gsub('/notes/', '/questions/') + else + # default path + self[:path] + end +end+
returns the value for the most recent power tag of form key:value
+ + + + +# File app/models/node.rb, line 321 +def power_tag(tag) + tids = Tag.includes(:node_tag) + .references(:community_tags) + .where('community_tags.nid = ? AND name LIKE ?', id, tag + ':%') + .collect(&:tid) + node_tag = NodeTag.where('nid = ? AND tid IN (?)', id, tids) + .order('nid DESC') + if node_tag && node_tag.first + node_tag.first.tag.name.gsub(tag + ':', '') + else + '' + end +end+
returns all power tag results as whole community_tag objects
+ + + + +# File app/models/node.rb, line 350 +def power_tag_objects(tag) + tids = Tag.includes(:node_tag) + .references(:community_tags) + .where('community_tags.nid = ? AND name LIKE ?', id, tag + ':%') + .collect(&:tid) + NodeTag.where('nid = ? AND tid IN (?)', id, tids) +end+
# File app/models/node.rb, line 152 +def publish + self.status = 1 + save + self +end+
so we can quickly fetch questions corresponding to this node with +node.questions
+ + + + +# File app/models/node.rb, line 718 +def questions + # override with a tag like `questions:h2s` + if self.has_power_tag('questions') + tagname = node.power_tag('questions') + else + tagname = self.slug_from_path + end + Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:term_data) + .where('term_data.name LIKE ?', "question:#{tagname}") +end+
# File app/models/node.rb, line 801 +def replace(before, after, user) + matches = latest.body.scan(before) + if matches.length == 1 + revision = new_revision(uid: user.id, + body: latest.body.gsub(before, after)) + revision.save + else + false + end +end+
Nodes this node is responding to with a `response:<nid>` power tag; +The key word “response” can be customized, i.e. `replication:<nid>` +for other uses.
+ + + + +# File app/models/node.rb, line 291 +def responded_to(key = 'response') + Node.where(nid: power_tags(key)) || [] +end+
Nodes that respond to this node with a `response:<nid>` power tag; +The key word “response” can be customized, i.e. `replication:<nid>` +for other uses.
+ + + + +# File app/models/node.rb, line 303 +def response_count(key = 'response') + Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:term_data) + .where('term_data.name = ?', "#{key}:#{id}") + .count +end+
Nodes that respond to this node with a `response:<nid>` power tag; +The key word “response” can be customized, i.e. `replication:<nid>` +for other uses.
+ + + + +# File app/models/node.rb, line 297 +def responses(key = 'response') + Tag.find_nodes_by_type([key + ':' + id.to_s]) +end+
# File app/models/node.rb, line 197 +def revision_count + revision + .count +end+
# File app/models/node.rb, line 192 +def revisions + revision + .order('timestamp DESC') +end+
# File app/models/node.rb, line 71 +def slug_from_path + path.split('/').last +end+
# File app/models/node.rb, line 158 +def spam + self.status = 0 + save + self +end+
tag- and node-based followers
+ + + + +# File app/models/node.rb, line 221 +def subscribers(conditions = false) + users = TagSelection.where(tid: tags.collect(&:tid)) + .collect(&:user) + users += NodeSelection.where(nid: nid) + .collect(&:user) + + users = users.where(conditions) if conditions + users.uniq +end+
these should eventually displace the above means of finding locations …they +may already be redundant after tagged_map_coord migration
+ + + + +# File app/models/node.rb, line 490 +def tagged_lat + power_tags('lat')[0] +end+
# File app/models/node.rb, line 494 +def tagged_lon + power_tags('lon')[0] +end+
# File app/models/node.rb, line 438 +def tagnames + tags.collect(&:name) +end+
Here we re-query to fetch /all/ tagnames; this is used in +/views/notes/_notes.html.erb in a way that would otherwise only return a +single tag due to a join, yet select() keeps this efficient
+ + + + +# File app/models/node.rb, line 445 +def tagnames_as_classes + Node.select([:nid]) + .find(id) + .tagnames + .map { |t| 'tag-' + t.tr(':', '-') } + .join(' ') +end+
# File app/models/node.rb, line 816 +def toggle_like(user) + nodes = NodeSelection.where(nid: self.id , liking: true).count + if is_liked_by(user) + self.cached_likes = nodes-1 + else + self.cached_likes = nodes+1 + end +end+
# File app/models/node.rb, line 126 +def totalviews + # disabled as impressionist is not currently updating counter_cache; see above + # self.views + self.legacy_views + impressionist_count(filter: :ip_address) + legacy_views +end+
# File app/models/node.rb, line 240 +def updated_at + Time.at(self['changed']) +end+
# File app/models/node.rb, line 27 +def updated_month + updated_at.strftime('%B %Y') +end+
so we can quickly fetch upgrades corresponding to this node with +node.upgrades
+ + + + +# File app/models/node.rb, line 761 +def upgrades + # override with a tag like `upgrades:h2s` + if self.has_power_tag('upgrades') + tagname = node.power_tag('upgrades') + else + tagname = self.slug_from_path + end + Node.upgrades(tagname) +end+
# File app/models/node_selection.rb, line 8 +def user + User.find_by(username: DrupalUser.find_by(uid: user_id).name) +end+
# File app/models/concerns/node_shared.rb, line 80 +def self.activities_grid(body) + body.gsub(/[^\>`](\<p\>)?\[activities\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + nodes = Node.activities(tagname) + .order('node.cached_likes DESC') + output = '' + output += '<p>' if Regexp.last_match(1) == '<p>' + a = ActionController::Base.new() + output += a.render_to_string(template: "grids/_notes", + layout: false, + locals: { + tagname: tagname, + randomSeed: rand(1000).to_s, + className: 'activity-grid-' + tagname.parameterize, + nodes: nodes, + type: "activity" + }) + output + end +end+
rubular regex: rubular.com/r/hBEThNL4qd
+ + + + +# File app/models/concerns/node_shared.rb, line 13 +def self.graph_grid(body, _page = 1) + body.gsub(/[^\>`](\<p\>)?\[graph\:(\S+)\]/) do |_tagname| + url = Regexp.last_match(2) + a = ActionController::Base.new() + randomSeed = rand(1000).to_s + output = a.render_to_string(template: "grids/_graph", + layout: false, + locals: { + url: url, + randomSeed: randomSeed, + idName: 'graph-grid-' + randomSeed, + type: "graph" + }) + output + end +end+
rubular regex: rubular.com/r/hBEThNL4qd
+ + + + +# File app/models/concerns/node_shared.rb, line 31 +def self.notes_grid(body, _page = 1) + body.gsub(/[^\>`](\<p\>)?\[notes\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + nodes = Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name = ?', tagname) + .order('node_revisions.timestamp DESC') + output = '' + output += '<p>' if Regexp.last_match(1) == '<p>' + a = ActionController::Base.new() + output += a.render_to_string(template: "grids/_notes", + layout: false, + locals: { + tagname: tagname, + randomSeed: rand(1000).to_s, + className: 'notes-grid-' + tagname.parameterize, + nodes: nodes, + type: "notes" + }) + output + end +end+
# File app/models/concerns/node_shared.rb, line 122 +def self.notes_map(body) + body.gsub(/[^\>`](\<p\>)?\[map\:content\:(\S+)\:(\S+)\]/) do |_tagname| + lat = Regexp.last_match(2) + lon = Regexp.last_match(3) + nids = NodeTag.joins(:tag) + .where('name LIKE ?', 'lat:' + lat[0..lat.length - 2] + '%') + .collect(&:nid) + nids = nids || [] + items = Node.includes(:tag) + .references(:node, :term_data) + .where('node.nid IN (?) AND term_data.name LIKE ?', nids, 'lon:' + lon[0..lon.length - 2] + '%') + .limit(200) + .order('node.nid DESC') + a = ActionController::Base.new() + output = a.render_to_string(template: "map/_leaflet", + layout: false, + locals: { + lat: lat, + lon: lon, + items: items + } + ) + output + end +end+
# File app/models/concerns/node_shared.rb, line 148 +def self.notes_map_by_tag(body) + body.gsub(/[^\>`](\<p\>)?\[map\:tag\:(\S+)\:(\S+)\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + lat = Regexp.last_match(3) + lon = Regexp.last_match(4) + nids = NodeTag.joins(:tag) + .where('term_data.name = ?', tagname) + .collect(&:nid) + nids = NodeTag.joins(:tag) + .where(nid: nids) + .where('name LIKE ?', 'lat:' + lat[0..lat.length - 2] + '%') + .collect(&:nid) + nids = nids || [] + items = Node.includes(:tag) + .references(:node, :term_data) + .where('node.nid IN (?) AND term_data.name LIKE ?', nids, 'lon:' + lon[0..lon.length - 2] + '%') + .limit(200) + .order('node.nid DESC') + a = ActionController::Base.new() + output = a.render_to_string(template: "map/_leaflet", + layout: false, + locals: { + lat: lat, + lon: lon, + items: items + } + ) + output + end +end+
in our interface, “users” are known as “people” because it's more human
+ + + + +# File app/models/concerns/node_shared.rb, line 206 +def self.people_grid(body, _page = 1) + body.gsub(/[^\>`](\<p\>)?\[people\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + users = User.where(status: 1) + .includes(:user_tags) + .references(:user_tags) + .where('user_tags.value = ?', tagname) + output = '' + output += '<p>' if Regexp.last_match(1) == '<p>' + a = ActionController::Base.new() + output += a.render_to_string(template: "grids/_people", + layout: false, + locals: { + tagname: tagname, + randomSeed: rand(1000).to_s, + className: 'people-grid-' + tagname.parameterize, + users: users + }) + output + end +end+
in our interface, “users” are known as “people” because it's more human
+ + + + +# File app/models/concerns/node_shared.rb, line 180 +def self.people_map(body, _page = 1) + body.gsub(/[^\>`](\<p\>)?\[map\:people\:(\S+)\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + lat = Regexp.last_match(2) + lon = Regexp.last_match(3) + nids = nids || [] + users = User.where(status: 1) + .includes(:user_tags) + .references(:user_tags) + .where('user_tags.value LIKE ?', 'lat:' + lat[0..lat.length - 2] + '%') + a = ActionController::Base.new() + output = a.render_to_string(template: "map/_leaflet", + layout: false, + locals: { + lat: lat, + lon: lon, + items: users, + people: true + } + ) + output + end +end+
rubular regex: rubular.com/r/hBEThNL4qd
+ + + + +# File app/models/concerns/node_shared.rb, line 56 +def self.questions_grid(body, _page = 1) + body.gsub(/[^\>`](\<p\>)?\[questions\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + nodes = Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:node_revisions, :term_data) + .where('term_data.name = ?', "question:#{tagname}") + .order('node_revisions.timestamp DESC') + output = '' + output += '<p>' if Regexp.last_match(1) == '<p>' + a = ActionController::Base.new() + output += a.render_to_string(template: "grids/_notes", + layout: false, + locals: { + tagname: tagname, + randomSeed: rand(1000).to_s, + className: 'questions-grid-' + tagname.parameterize, + nodes: nodes, + type: "questions" + }) + output + end +end+
# File app/models/concerns/node_shared.rb, line 101 +def self.upgrades_grid(body) + body.gsub(/[^\>`](\<p\>)?\[upgrades\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + nodes = Node.upgrades(tagname) + .order('node.cached_likes DESC') + output = '' + output += '<p>' if Regexp.last_match(1) == '<p>' + a = ActionController::Base.new() + output += a.render_to_string(template: "grids/_notes", + layout: false, + locals: { + tagname: tagname, + randomSeed: rand(1000).to_s, + className: 'upgrades-grid-' + tagname.parameterize, + nodes: nodes, + type: "upgrades" + }) + output + end +end+
# File app/models/concerns/node_shared.rb, line 228 +def self.wikis_grid(body, _page = 1) + body.gsub(/[^\>`](\<p\>)?\[wikis\:(\S+)\]/) do |_tagname| + tagname = Regexp.last_match(2) + nodes = Node.where(status: 1, type: 'page') + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name = ?', tagname) + .order('node_revisions.timestamp DESC') + output = '' + output += '<p>' if Regexp.last_match(1) == '<p>' + a = ActionController::Base.new() + output += a.render_to_string(template: "grids/_wikis", + layout: false, + locals: { + tagname: tagname, + randomSeed: rand(1000).to_s, + className: 'wikis-grid-' + tagname.parameterize, + nodes: nodes, + type: "wikis" + }) + output + end + end+
# File app/models/concerns/node_shared.rb, line 8 +def liked_by(uid) + likers.collect(&:uid).include?(uid) +end+
# File app/models/concerns/node_shared.rb, line 4 +def likes + cached_likes +end+
# File app/models/node_tag.rb, line 35 +def description + self.tag.description if self.tag && self.tag.description && !self.tag.description.empty? +end+
# File app/models/node_tag.rb, line 27 +def drupal_user + DrupalUser.find uid +end+
# File app/models/node_tag.rb, line 12 +def increment_count + tag = self.tag + tag.count = 0 if tag.count.nil? + tag.count += 1 + tag.save +end+
# File app/models/node_tag.rb, line 31 +def name + tag.name +end+
# File app/models/node_tag.rb, line 23 +def user + DrupalUser.find_by(uid: uid).try(:user) +end+
# File app/controllers/notes_controller.rb, line 92 +def create + if current_user.drupal_user.status == 1 + saved, @node, @revision = Node.new_note(uid: current_user.uid, + title: params[:title], + body: params[:body], + main_image: params[:main_image]) + + if saved + if params[:tags] + params[:tags].tr(' ', ',').split(',').each do |tagname| + @node.add_tag(tagname.strip, current_user) + end + end + if params[:event] == 'on' + @node.add_tag('event', current_user) + @node.add_tag('event:rsvp', current_user) + @node.add_tag('date:' + params[:date], current_user) if params[:date] + end + if current_user.first_time_poster + AdminMailer.notify_node_moderators(@node) + flash[:first_time_post] = true + if @node.has_power_tag('question') + flash[:notice] = I18n.t('notes_controller.thank_you_for_question').html_safe + else + flash[:notice] = I18n.t('notes_controller.thank_you_for_contribution').html_safe + end + else + if @node.has_power_tag('question') + flash[:notice] = I18n.t('notes_controller.question_note_published').html_safe + else + flash[:notice] = I18n.t('notes_controller.research_note_published').html_safe + end + end + # Notice: Temporary redirect.Remove this condition after questions show page is complete. + # Just keep @node.path(:question) + if params[:redirect] && params[:redirect] == 'question' + redirect_to @node.path(:question) + else + if request.xhr? # rich editor! + render text: @node.path + else + redirect_to @node.path + end + end + else + if request.xhr? # rich editor! + errors = @node.errors + errors = errors.to_hash.merge(@revision.errors.to_hash) if @revision && @revision.errors + render json: errors + else + render template: 'editor/post' + end + end + else + flash.keep[:error] = I18n.t('notes_controller.you_have_been_banned').html_safe + redirect_to '/logout' + end +end+
at /notes/delete/:id only for notes
+ + + + +# File app/controllers/notes_controller.rb, line 235 +def delete + @node = Node.find(params[:id]) + if current_user.uid == @node.uid && @node.type == 'note' || current_user.role == 'admin' || current_user.role == 'moderator' + @node.delete + respond_with do |format| + format.html do + if request.xhr? + render text: I18n.t('notes_controller.content_deleted') + else + flash[:notice] = I18n.t('notes_controller.content_deleted') + redirect_to '/dashboard' + '?_=' + Time.now.to_i.to_s + end + end + end + else + prompt_login + end +end+
# File app/controllers/notes_controller.rb, line 151 +def edit + @node = Node.find_by(nid: params[:id], type: 'note') + if current_user.uid == @node.uid || current_user.role == 'admin' || @node.has_tag("with:#{current_user.username}") + if params[:legacy] + render template: 'editor/post' + else + if @node.main_image + @main_image = @node.main_image.path(:default) + elsif params[:main_image] && Image.find_by(id: params[:main_image]) + @main_image = Image.find_by(id: params[:main_image]).path + elsif @image + @main_image = @image.path(:default) + end + flash.now[:notice] = "This is the new rich editor. For the legacy editor, <a href='/notes/edit/#{@node.id}?#{request.env['QUERY_STRING']}&legacy=true'>click here</a>." + render template: 'editor/rich' + end + else + if @node.has_power_tag('question') + prompt_login I18n.t('notes_controller.author_can_edit_question') + else + prompt_login I18n.t('notes_controller.author_can_edit_note') + end + end +end+
# File app/controllers/notes_controller.rb, line 82 +def image + params[:size] = params[:size] || :large + node = Node.find(params[:id]) + if node.main_image + redirect_to node.main_image.path(params[:size]) + else + redirect_to 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' + end +end+
# File app/controllers/notes_controller.rb, line 5 +def index + @title = I18n.t('notes_controller.research_notes') + set_sidebar +end+
notes with high # of likes
+ + + + +# File app/controllers/notes_controller.rb, line 275 +def liked + @title = I18n.t('notes_controller.highly_liked_research_notes') + @wikis = Node.limit(10) + .where(type: 'page', status: 1) + .order('nid DESC') + + @notes = Node.research_notes + .where(status: 1) + .limit(20) + .order('nid DESC') + @unpaginated = true + render template: 'notes/index' +end+
# File app/controllers/notes_controller.rb, line 316 +def liked_rss + @notes = Node.limit(20) + .order('created DESC') + .where('type = ? AND status = 1 AND cached_likes > 0', 'note') + respond_to do |format| + format.rss do + render layout: false, template: 'notes/rss' + response.headers['Content-Type'] = 'application/xml; charset=utf-8' + end + end +end+
# File app/controllers/notes_controller.rb, line 14 +def places + @title = 'Places' + @notes = Node.joins('LEFT OUTER JOIN node_revisions ON node_revisions.nid = node.nid + LEFT OUTER JOIN community_tags ON community_tags.nid = node.nid + LEFT OUTER JOIN term_data ON term_data.tid = community_tags.tid') + .select('*, max(node_revisions.timestamp)') + .where(status: 1, type:%w[page place]) + .includes(:revision, :tag) + .references(:term_data) + .where('term_data.name = ?', 'chapter') + .group('node.nid') + .order('max(node_revisions.timestamp) DESC, node.nid') + .page(params[:page]) + + render template: 'notes/tools_places' +end+
notes with high # of views
+ + + + +# File app/controllers/notes_controller.rb, line 290 +def popular + @title = I18n.t('notes_controller.popular_research_notes') + @wikis = Node.limit(10) + .where(type: 'page', status: 1) + .order('nid DESC') + @notes = Node.research_notes + .limit(20) + .where(status: 1) + .order('views DESC') + @unpaginated = true + render template: 'notes/index' +end+
display a revision, raw
+ + + + +# File app/controllers/notes_controller.rb, line 41 +def raw + response.headers['Content-Type'] = 'text/plain; charset=utf-8' + render text: Node.find(params[:id]).latest.body +end+
# File app/controllers/notes_controller.rb, line 303 +def rss + @notes = Node.limit(20) + .order('nid DESC') + .where('type = ? AND status = 1 AND created < ?', 'note', (Time.now.to_i - 30.minutes.to_i)) + respond_to do |format| + format.rss do + render layout: false + response.headers['Content-Type'] = 'application/xml; charset=utf-8' + response.headers['Access-Control-Allow-Origin'] = '*' + end + end +end+
# File app/controllers/notes_controller.rb, line 328 +def rsvp + @node = Node.find params[:id] + # leave a comment + @comment = @node.add_comment(subject: 'rsvp', uid: current_user.uid, body: 'I will be attending!') + # make a tag + @node.add_tag('rsvp:' + current_user.username, current_user) + redirect_to @node.path + '#comments' +end+
# File app/controllers/notes_controller.rb, line 31 +def shortlink + @node = Node.find params[:id] + if @node.has_power_tag('question') + redirect_to @node.path(:question) + else + redirect_to @node.path + end +end+
# File app/controllers/notes_controller.rb, line 46 +def show + if params[:author] && params[:date] + @node = Node.find_notes(params[:author], params[:date], params[:id]) + @node ||= Node.where(path: "/report/#{params[:id]}").first + # redirect_old_urls + else + @node = Node.find params[:id] + end + + if @node.has_power_tag('question') + redirect_to @node.path(:question) + return + end + + if @node.has_power_tag('redirect') + if current_user.nil? || (current_user.role != 'admin' && current_user.role != 'moderator') + redirect_to Node.find(@node.power_tag('redirect')).path + return + elsif current_user.role == 'admin' || current_user.role == 'moderator' + flash.now[:warning] = "Only moderators and admins see this page, as it is redirected to #{Node.find(@node.power_tag('redirect')).title}. + To remove the redirect, delete the tag beginning with 'redirect:'" + end + end + + return if check_and_redirect_node(@node) + + alert_and_redirect_moderated + + impressionist(@node, 'show', unique: [:ip_address]) + @title = @node.latest.title + @tags = @node.tags + @tagnames = @tags.collect(&:name) + + set_sidebar :tags, @tagnames +end+
# File app/controllers/notes_controller.rb, line 10 +def tools + redirect_to '/methods', status: 302 +end+
at /notes/update/:id
+ + + + +# File app/controllers/notes_controller.rb, line 177 +def update + @node = Node.find(params[:id]) + if current_user.uid == @node.uid || current_user.role == 'admin' || @node.has_tag("with:#{current_user.username}") + @revision = @node.latest + @revision.title = params[:title] + @revision.body = params[:body] + if params[:tags] + params[:tags].tr(' ', ',').split(',').each do |tagname| + @node.add_tag(tagname, current_user) + end + end + if @revision.valid? + @revision.save + @node.vid = @revision.vid + # update vid (version id) of main image + if @node.drupal_main_image + i = @node.drupal_main_image + i.vid = @revision.vid + i.save + end + @node.drupal_content_field_image_gallery.each do |img| + img.vid = @revision.vid + img.save + end + @node.title = @revision.title + # save main image + if params[:main_image] && params[:main_image] != '' + img = Image.find params[:main_image] + unless img.nil? + img.nid = @node.id + @node.main_image_id = img.id + img.save + end + end + @node.save! + flash[:notice] = I18n.t('notes_controller.edits_saved') + format = false + format = :question if params[:redirect] && params[:redirect] == 'question' + if request.xhr? + render text: @node.path(format) + '?_=' + Time.now.to_i.to_s + else + redirect_to @node.path(format) + '?_=' + Time.now.to_i.to_s + end + else + flash[:error] = I18n.t('notes_controller.edit_not_saved') + if request.xhr? || params[:rich] + errors = @node.errors + errors = errors.to_hash.merge(@revision.errors.to_hash) if @revision && @revision.errors + render json: errors + else + render 'editor/post' + end + end + end +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 6 +def self.new(app) + store = OpenIdAuthentication.store + if store.nil? + Rails.logger.warn "OpenIdAuthentication.store is nil. Using in-memory store." + end + + ::Rack::OpenID.new(app, OpenIdAuthentication.store) +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 15 +def self.store + @@store +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 19 +def self.store=(*store_option) + store, *parameters = *([ store_option ].flatten) + + @@store = case store + when :memory + require 'openid/store/memory' + OpenID::Store::Memory.new + when :file + require 'openid/store/filesystem' + OpenID::Store::Filesystem.new(Rails.root.join('tmp/openids')) + when :memcache + require 'memcache' + require 'openid/store/memcache' + OpenID::Store::Memcache.new(MemCache.new(parameters)) + else + store + end +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 86 +def authenticate_with_open_id(identifier = nil, options = {}, &block) #:doc: + identifier ||= open_id_identifier + + if request.env[Rack::OpenID::RESPONSE] + complete_open_id_authentication(&block) + else + begin_open_id_authentication(identifier, options, &block) + end +end+
The parameter name of “openid_identifier” is used rather than the Rails +convention “open_id_identifier” because that's what the specification +dictates in order to get browser auto-complete working across sites
+ + + + +# File lib/open_id_authentication/open_id_authentication.rb, line 81 +def using_open_id?(identifier = nil) #:doc: + identifier ||= open_id_identifier + !identifier.blank? || request.env[Rack::OpenID::RESPONSE] +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 49 +def self.[](code) + new(code) +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 53 +def initialize(code) + @code = code +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 71 +def message + ERROR_MESSAGES[@code] +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 57 +def status + @code +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 63 +def successful? + @code == :successful +end+
# File lib/open_id_authentication/open_id_authentication.rb, line 67 +def unsuccessful? + ERROR_MESSAGES.keys.include?(@code) +end+
# File app/controllers/openid_controller.rb, line 172 +def decision + oidreq = session[:last_oidreq] + session[:last_oidreq] = nil + + if params[:yes].nil? + redirect_to oidreq.cancel_url + return + else + id_to_send = params[:id_to_send] + + identity = oidreq.identity + if oidreq.id_select + if id_to_send && (id_to_send != '') + session[:username] = id_to_send + session[:approvals] = [] + identity = url_for_user + else + msg = 'You must enter a username to in order to send ' 'an identifier to the Relying Party.' + show_decision_page(oidreq, msg) + return + end + else + session[:username] = current_user.username + end + + if session[:approvals] + session[:approvals] << oidreq.trust_root + else + session[:approvals] = [oidreq.trust_root] + end + oidresp = oidreq.answer(true, nil, identity) + add_sreg(oidreq, oidresp) + add_pape(oidreq, oidresp) + return render_response(oidresp) + end +end+
# File app/controllers/openid_controller.rb, line 164 +def idp_xrds + types = [ + OpenID::OPENID_IDP_2_0_TYPE + ] + + render_xrds(types) +end+
# File app/controllers/openid_controller.rb, line 16 +def index + begin + if params['openid.mode'] + oidreq = server.decode_request(params) + else + oidreq = server.decode_request(Rack::Utils.parse_query(request.env['ORIGINAL_FULLPATH'].split('?')[1])) + end + rescue ProtocolError => e + # invalid openid request, so just display a page with an error message + render text: e.to_s, status: 500 + return + end + + # no openid.mode was given + unless oidreq + render text: 'This is an OpenID server endpoint.' + return + end + + if current_user.nil? && !params['openid.mode'] + session[:openid_return_to] = request.env['ORIGINAL_FULLPATH'] + flash[:warning] = 'Please log in first.' + redirect_to '/login' + return + else + + if oidreq + + requested_username = '' + if request.env['ORIGINAL_FULLPATH'] && request.env['ORIGINAL_FULLPATH'].split('?')[1] + request.env['ORIGINAL_FULLPATH'].split('?')[1].split('&').each do |param| + requested_username = param.split('=')[1].split('%2F').last if param.split('=')[0] == 'openid.claimed_id' + end + end + + if current_user && !requested_username.casecmp(current_user.username.downcase).zero? + flash[:error] = "You are requesting access to an account that's not yours. Please <a href='/logout'>log out</a> and use the correct account, or <a href='" + oidreq.trust_root + "'>try to login with the correct username</a>" + redirect_to '/dashboard' + else + oidresp = nil + + if oidreq.is_a?(CheckIDRequest) + + identity = oidreq.identity + + if oidreq.id_select + if oidreq.immediate + oidresp = oidreq.answer(false) + elsif session[:username] + # The user hasn't logged in. + # show_decision_page(oidreq) # this doesnt make sense... it was in the example though + session[:openid_return_to] = request.env['ORIGINAL_FULLPATH'] + redirect_to '/login' + else + # Else, set the identity to the one the user is using. + identity = url_for_user + end + + end + + if oidresp + nil + elsif is_authorized(identity, oidreq.trust_root) + oidresp = oidreq.answer(true, nil, identity) + + # add the sreg response if requested + add_sreg(oidreq, oidresp) + # ditto pape + add_pape(oidreq, oidresp) + + elsif oidreq.immediate + server_url = url_for action: 'index' + oidresp = oidreq.answer(false, server_url) + + else + show_decision_page(oidreq) + return + end + + else + oidresp = server.handle_request(oidreq) + end + + render_response(oidresp) + end + else + session[:openid_return_to] = request.env['ORIGINAL_FULLPATH'] + redirect_to '/login' + end + end +end+
# File app/controllers/openid_controller.rb, line 108 +def resume + if session[:openid_return_to] # for openid login, redirects back to openid auth process + return_to = session[:openid_return_to] + session[:openid_return_to] = nil + session[:openid_requester] = nil + redirect_to return_to + end +end+
# File app/controllers/openid_controller.rb, line 117 +def show_decision_page(oidreq, message = 'Do you trust this site with your identity?') + session[:last_oidreq] = oidreq + @oidreq = oidreq + + flash[:notice] = message if message + + render template: 'openid/decide' +end+
# File app/controllers/openid_controller.rb, line 126 + def user_page + # Yadis content-negotiation: we want to return the xrds if asked for. + accept = request.env['HTTP_ACCEPT'] + + # This is not technically correct, and should eventually be updated + # to do real Accept header parsing and logic. Though I expect it will work + # 99% of the time. + if accept && accept.include?('application/xrds+xml') + user_xrds + return + end + + # content negotiation failed, so just render the user page + xrds_url = url_for(controller: 'user', action: params[:username]) + '/xrds' + identity_page = <<EOS +<html><head> +<meta http-equiv="X-XRDS-Location" content="#{xrds_url}" /> +<link rel="openid.server" href="#{url_for action: 'index'}" /> +</head><body><p>OpenID identity page for #{params[:username]}</p> +</body></html> +EOS + + # Also add the Yadis location header, so that they don't have + # to parse the html unless absolutely necessary. + response.headers['X-XRDS-Location'] = xrds_url + render text: identity_page + end+
# File app/controllers/openid_controller.rb, line 154 +def user_xrds + types = [ + OpenID::OPENID_2_0_TYPE, + OpenID::OPENID_1_0_TYPE, + OpenID::SREG_URI + ] + + render_xrds(types) +end+
# File app/controllers/openid_controller.rb, line 273 +def add_pape(oidreq, oidresp) + papereq = OpenID::PAPE::Request.from_openid_request(oidreq) + return if papereq.nil? + paperesp = OpenID::PAPE::Response.new + paperesp.nist_auth_level = 0 # we don't even do auth at all! + oidresp.add_extension(paperesp) +end+
# File app/controllers/openid_controller.rb, line 256 +def add_sreg(oidreq, oidresp) + # check for Simple Registration arguments and respond + sregreq = OpenID::SReg::Request.from_openid_request(oidreq) + + return if sregreq.nil? + # In a real application, this data would be user-specific, + # and the user should be asked for permission to release + # it. + sreg_data = { + 'nickname' => current_user.username, # session[:username], + 'email' => current_user.email + } + + sregresp = OpenID::SReg::Response.extract_response(sregreq, sreg_data) + oidresp.add_extension(sregresp) +end+
# File app/controllers/openid_controller.rb, line 222 +def approved(trust_root) + return false if session[:approvals].nil? + session[:approvals].member?(trust_root) +end+
# File app/controllers/openid_controller.rb, line 281 +def render_response(oidresp) + signed_response = server.signatory.sign(oidresp) if oidresp.needs_signing + web_response = server.encode_response(oidresp) + case web_response.code + when HTTP_OK + render text: web_response.body, status: 200 + + when HTTP_REDIRECT + redirect_to web_response.headers['location'] + + else + render text: web_response.body, status: 400 + end +end+
# File app/controllers/openid_controller.rb, line 231 + def render_xrds(types) + type_str = '' + + types.each do |uri| + type_str += "<Type>#{uri}</Type>\n " + end + + yadis = <<EOS +<?xml version="1.0" encoding="UTF-8"?> +<xrds:XRDS + xmlns:xrds="xri://$xrds" + xmlns="xri://$xrd*($v*2.0)"> + <XRD> + <Service priority="0"> + #{type_str} + <URI>#{url_for(controller: 'openid', only_path: false)}</URI> + </Service> + </XRD> +</xrds:XRDS> +EOS + + response.headers['content-type'] = 'application/xrds+xml' + render text: yadis + end+
# File app/controllers/openid_controller.rb, line 212 +def server + if @server.nil? + server_url = url_for action: 'index', only_path: false + dir = Pathname.new(request.host).join('db').join('openid-store') + store = OpenID::Store::Filesystem.new(dir) + @server = Server.new(store, server_url) + end + @server +end+
# File app/helpers/openid_helper.rb, line 3 +def url_for_user + '/profile/' + current_user.username +end+
#reset_notify.deliver
+ + + + +# File app/mailers/password_reset_mailer.rb, line 7 +def reset_notify(user, key) + subject = '[Public Lab] Reset your password' + @user = user + @key = key + @footer = feature('email-footer') + mail(to: user.email, subject: subject).deliver +end+
# File app/controllers/questions_controller.rb, line 66 +def answered + @title = 'Recently answered' + @questions = Node.questions + .where(status: 1) + @questions = filter_questions_by_tag(@questions, params[:tagnames]) + .joins(:answers) + .order('answers.created_at DESC') + .group('node.nid') + .paginate(page: params[:page], per_page: 24) + @wikis = Node.limit(10) + .where(type: 'page', status: 1) + .order('nid DESC') + render template: 'questions/index' +end+
# File app/controllers/questions_controller.rb, line 21 +def index + @title = 'Questions and Answers' + set_sidebar + @questions = Node.questions + .where(status: 1) + .order('node.nid DESC') + .paginate(page: params[:page], per_page: 24) +end+
# File app/controllers/questions_controller.rb, line 118 +def liked + @title = 'Highly liked Questions' + @questions = Node.questions.where(status: 1) + @questions = filter_questions_by_tag(@questions, params[:tagnames]) + .order('cached_likes DESC') + .limit(20) + + @wikis = Node.limit(10) + .where(type: 'page', status: 1) + .order('nid DESC') + @unpaginated = true + render template: 'questions/index' +end+
# File app/controllers/questions_controller.rb, line 30 +def new + if current_user == nil + redirect_to new_user_session_path( return_to: request.path ) + flash[:notice] = "Your question is important and we want to hear from you! Please log in or sign up to post a question" + else + if params[:legacy] + render 'editor/question' + else + render 'editor/questionRich' + end + end +end+
# File app/controllers/questions_controller.rb, line 103 +def popular + @title = 'Popular Questions' + @questions = Node.questions + .where(status: 1) + @questions = filter_questions_by_tag(@questions, params[:tagnames]) + .order('views DESC') + .limit(20) + + @wikis = Node.limit(10) + .where(type: 'page', status: 1) + .order('nid DESC') + @unpaginated = true + render template: 'questions/index' +end+
# File app/controllers/questions_controller.rb, line 94 +def shortlink + @node = Node.find params[:id] + if @node.has_power_tag('question') + redirect_to @node.path(:question) + else + redirect_to @node.path + end +end+
# File app/controllers/questions_controller.rb, line 43 +def show + if params[:author] && params[:date] + @node = Node.find_notes(params[:author], params[:date], params[:id]) + @node ||= Node.where(path: "/report/#{params[:id]}").first + else + @node = Node.find params[:id] + end + + redirect_to @node.path unless @node.has_power_tag('question') + + alert_and_redirect_moderated + + impressionist(@node) + @title = @node.latest.title + @tags = @node.power_tag_objects('question') + @tagnames = @tags.collect(&:name) + @users = @node.answers.group(:uid) + .order('count(*) DESC') + .collect(&:author) + + set_sidebar :tags, @tagnames +end+
# File app/controllers/questions_controller.rb, line 81 +def unanswered + @title = 'Unanswered questions' + @questions = Node.questions + .where(status: 1) + .includes(:answers) + .references(:answers) + .where(answers: { id: nil }) + .order('answers.created_at DESC') + .group('node.nid') + .paginate(page: params[:page], per_page: 24) + render template: 'questions/index' +end+
Rails is a web-application framework that includes everything needed to +create database-backed web applications according to the Model-View-Control +pattern.
+ +This pattern splits the view (also called the presentation) into “dumb” +templates that are primarily responsible for inserting pre-built data in +between HTML tags. The model contains the “smart” domain objects (such as +Account, Product, Person, Post) that holds all the business logic and knows +how to persist themselves to a database. The controller handles the +incoming requests (such as Save New Account, Update Product, Show Post) by +manipulating the model and directing data to the view.
+ +In Rails, the model is handled by what's called an object-relational +mapping layer entitled Active Record. This layer allows you to present the +data from database rows as objects and embellish these data objects with +business logic methods. You can read more about Active Record in files/vendor/rails/activerecord/README.html.
+ +The controller and view are handled by the Action Pack, which handles both +layers by its two parts: Action View and Action Controller. These two +layers are bundled in a single package due to their heavy interdependence. +This is unlike the relationship between the Active Record and Action Pack +that is much more separate. Each of these packages can be used +independently outside of Rails. You can read more about Action Pack in files/vendor/rails/actionpack/README.html.
+ +At the command prompt, create a new Rails application:
+ +<tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)+
Change directory to myapp
and start the web server:
<tt>cd myapp; rails server</tt> (run with --help for options)+
Go to localhost:3000/ and you'll +see:
+ +"Welcome aboard: You're riding Ruby on Rails!"
+
+Follow the guidelines to start developing your application. You can find
+the following resources handy:
+The Getting Started Guide: guides.rubyonrails.org/getting_started.html
+Ruby on Rails Tutorial Book: www.railstutorial.org/
+Sometimes your application goes wrong. Fortunately there are a lot of tools +that will help you debug it and get it back on the rails.
+ +First area to check is the application log files. Have “tail -f” commands +running on the server.log and development.log. Rails will automatically +display debugging and runtime information to these files. Debugging info +will also be shown in the browser on requests from 127.0.0.1.
+ +You can also log your own messages directly into the log file from your +code using the Ruby logger class from inside your controllers. Example:
+ +class WeblogController < ActionController::Base + def destroy + @weblog = Weblog.find(params[:id]) + @weblog.destroy + logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") + end +end ++ +
The result will be a message in your log file along the lines of:
+ +Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!+ +
More information on how to use the logger is at www.ruby-doc.org/core/
+ +Also, Ruby documentation can be found at www.ruby-lang.org/. There are several +books available online as well:
+Programming Ruby: www.ruby-doc.org/docs/ProgrammingRuby/ +(Pickaxe)
+Learn to Program: pine.fm/LearnToProgram/ (a +beginners guide)
+These two books will bring you up to speed on the Ruby language and also on +programming in general.
+ +Debugger support is available through the debugger command when you start
+your Mongrel or WEBrick server with –debugger. This means that you can
+break out of execution at any point in the code, investigate and change the
+model, and then, resume execution! You need to install ruby-debug to run
+the server in debugging mode. With gems, use sudo gem install
+ruby-debug
. Example:
class WeblogController < ActionController::Base + def index + @posts = Post.all + debugger + end +end ++ +
So the controller will accept the action, run the first line, then present +you with a IRB prompt in the server window. Here you can do things like:
+ +>> @posts.inspect +=> "[#<Post:0x14a6be8 + @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>, + #<Post:0x14a6620 + @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]" +>> @posts.first.title = "hello from a debugger" +=> "hello from a debugger"+ +
…and even better, you can examine how your runtime objects actually work:
+ +>> f = @posts.first +=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}> +>> f. +Display all 152 possibilities? (y or n)+ +
Finally, when you're ready to resume execution, you can enter “cont”.
+ +The console is a Ruby shell, which allows you to interact with your +application's domain model. Here you'll have all parts of the +application configured, just like it is when the application is running. +You can inspect domain models, change values, and save to the database. +Starting the script without arguments will launch it in the development +environment.
+ +To start the console, run rails console
from the application
+directory.
Options:
+Passing the -s, --sandbox
argument will rollback any
+modifications made to the database.
Passing an environment name as an argument will load the corresponding
+environment. Example: rails console production
.
To reload your controllers and models after launching the console run
+reload!
More information about irb can be found at: http://www.rubycentral.org/pickaxe/irb.html
+ +You can go to the command line of your database directly through
+rails dbconsole
. You would be connected to the database with
+the credentials defined in database.yml. Starting the script without
+arguments will connect you to the development database. Passing an argument
+will connect you to a different database, like rails dbconsole
+production
. Currently works for MySQL, PostgreSQL and SQLite 3.
The default directory structure of a generated Ruby on Rails application:
+ +|-- app +| |-- assets +| |-- images +| |-- javascripts +| `-- stylesheets +| |-- controllers +| |-- helpers +| |-- mailers +| |-- models +| `-- views +| `-- layouts +|-- config +| |-- environments +| |-- initializers +| `-- locales +|-- db +|-- doc +|-- lib +| `-- tasks +|-- log +|-- public +|-- script +|-- test +| |-- fixtures +| |-- functional +| |-- integration +| |-- performance +| `-- unit +|-- tmp +| |-- cache +| |-- pids +| |-- sessions +| `-- sockets +`-- vendor + |-- assets + `-- stylesheets + `-- plugins+ +
app
+ +Holds all the code that's specific to this particular application.+ +
app/assets
+ +Contains subdirectories for images, stylesheets, and JavaScript files.+ +
app/controllers
+ +Holds controllers that should be named like weblogs_controller.rb for +automated URL mapping. All controllers should descend from +ApplicationController which itself descends from ActionController::Base.+ +
app/models
+ +Holds models that should be named like post.rb. Models descend from +ActiveRecord::Base by default.+ +
app/views
+ +Holds the template files for the view that should be named like +weblogs/index.html.erb for the WeblogsController#index action. All views use +eRuby syntax by default.+ +
app/views/layouts
+ +Holds the template files for layouts to be used with views. This models the +common header/footer method of wrapping views. In your views, define a layout +using the <tt>layout :default</tt> and create a file named default.html.erb. +Inside default.html.erb, call <% yield %> to render the view using this +layout.+ +
app/helpers
+ +Holds view helpers that should be named like weblogs_helper.rb. These are +generated for you automatically when using generators for controllers. +Helpers can be used to wrap functionality for your views into methods.+ +
config
+ +Configuration files for the Rails environment, the routing map, the database, +and other dependencies.+ +
db
+ +Contains the database schema in schema.rb. db/migrate contains all the +sequence of Migrations for your schema.+ +
doc
+ +This directory is where your application documentation will be stored when +generated using <tt>rake doc:app</tt>+ +
lib
+ +Application specific libraries. Basically, any kind of custom code that +doesn't belong under controllers, models, or helpers. This directory is in +the load path.+ +
public
+ +The directory available for the web server. Also contains the dispatchers and the +default HTML files. This should be set as the DOCUMENT_ROOT of your web +server.+ +
script
+ +Helper scripts for automation and generation.+ +
test
+ +Unit and functional tests along with fixtures. When using the rails generate +command, template test files will be generated for you and placed in this +directory.+ +
vendor
+ +External libraries that the application depends on. Also includes the plugins +subdirectory. If the app has frozen rails, those gems also go here, under +vendor/rails/. This directory is in the load path.+
# File app/controllers/relationships_controller.rb, line 4 +def create + user = User.find(params[:followed_id]) + current_user.follow(user) + redirect_to "/profile/#{user.username}" +end+
# File app/controllers/relationships_controller.rb, line 10 +def destroy + user = Relationship.find(params[:id]).followed + current_user.unfollow(user) + redirect_to "/profile/#{user.username}" +end+
# File app/models/revision.rb, line 126 +def body_extras(content) + # inline edit button + content = content.gsub('[edit]', '<p class="well" style="padding:6px;"><a class="btn btn-primary" href="' + parent.edit_path + '"><i class="fa fa-white icon-pencil"></i> Edit this page</a> to help complete it!</p>') + # inline question button + content = content.gsub(/\[question:([\w-]+)\]/, '<p class="well" style="padding:6px;"><a style="margin-bottom:6px;" class="btn btn-primary" href="/post?tags=question:\1&template=question"><i class="fa fa-white icon-question-sign"></i> Ask a question about <b>\1</b></a> or <a style="margin-bottom:6px;" class="btn" target="_blank" href="/subscribe/tag/question:\1">Sign up to answer questions on this topic</a></p>') + # inline foldaway + content = content.gsub(/\[fold\:(.+)\]/, '<p class="foldaway-link" data-title="\1"><i style="color:#666;padding-right:3px;" class="fa fa-expand-alt"></i> <a>\1 »</a></p><div class="foldaway" data-title="\1">') + content = content.gsub('[unfold]', '</div>') + content +end+
# File app/models/revision.rb, line 111 +def body_preview(length = 100) + newBody = body.gsub(/^#+.+/, '') + newBody.truncate(length) +end+
# File app/models/revision.rb, line 122 +def body_raw + body_extras(body) +end+
some adaptations for the new rich editor
+ + + + +# File app/models/revision.rb, line 117 +def body_rich + # turn ##Headers into ## Headers + body.gsub(/(^|\n)(#+)([A-z]+)/, '\1\2 \3') +end+
# File app/models/revision.rb, line 55 +def created_at + Time.at(timestamp) +end+
# File app/models/revision.rb, line 75 +def is_initial? + parent.revision.count == 1 +end+
# File app/models/revision.rb, line 85 +def next + parent.revision.order('timestamp DESC') + .where('timestamp > ?', timestamp) + .last +end+
# File app/models/revision.rb, line 71 +def parent + node +end+
# File app/models/revision.rb, line 63 +def path + node.path +end+
# File app/models/revision.rb, line 79 +def previous + parent.revision.order('timestamp DESC') + .where('timestamp < ?', timestamp) + .first +end+
# File app/models/revision.rb, line 29 +def publish + self.status = 1 + save + self +end+
filtered version of node content
+ + + + +# File app/models/revision.rb, line 92 +def render_body + body = self.body || '' + body = RDiscount.new(body) + body = body.to_html + body = body.gsub(Callouts.const_get(:FINDER), Callouts.const_get(:PRETTYLINKHTML)) + body = body.gsub(Callouts.const_get(:HASHTAGNUMBER), Callouts.const_get(:NODELINKHTML)) + body = body.gsub(Callouts.const_get(:HASHTAG), Callouts.const_get(:HASHLINKHTML)) + body_extras(body) +end+
filtered version additionally appending http/https protocol to +protocol-relative URLs like “/foo” #render_body plus making all +relative links absolute
+ + + + +# File app/models/revision.rb, line 104 +def render_body_email(host = 'publiclab.org') + body = render_body.gsub(/([\s|"|'|\[|\(])(\/\/)([\w]?\.?#{host})/, '\1https://\3') + body = body.gsub("href='/", "href='https://#{host}/") + body = body.gsub('href="/', 'href="https://' + host.to_s + '/') + body +end+
# File app/models/revision.rb, line 22 +def setup + self.teaser = '' + self.log = '' + self.timestamp = DateTime.now.to_i + self.format = 1 +end+
# File app/models/revision.rb, line 35 +def spam + self.status = 0 + save + self +end+
# File app/models/revision.rb, line 59 +def updated_at + Time.at(timestamp) +end+
Class encapsulating search requests.
+ +Minimum query length, or we return an empty result
+ + +# File app/models/search_request.rb, line 10 +def self.fromRequest(rparams) + obj = new + obj.srchString = rparams[:srchString] + obj.seq = rparams[:seq] + obj.showCount = rparams[:showCount] + obj.pageNum = rparams[:pageNum] + obj +end+
# File app/models/search_request.rb, line 8 +def initialize; end+
Check the query string to make sure it is not blank (null, empty string, or +' ') and make sure it is at least 3 characters
+ + + + +# File app/models/search_request.rb, line 21 +def valid? + isValid = true + isValid &&= !srchString.blank? + isValid &&= srchString.length >= MIN_QUERY_LENGTH + isValid +end+
This subclass is used to auto-generate the RESTful data structure. It is +generally not useful for internal Ruby usage
+ +but must be included for full RESTful functionality.+ +
The SearchService class is a utility class +whose purpose is to provide detailed responses to queries within different +categories (record types, functionality, subsystems, etc). Though similar +in operation to the TypeaheadService, +the implementation is separate, in that the goal of the response is to +provide detailed results at a deep level. In effect, TypeaheadService provides pointers to +better searches, while SearchService +provides deep and detailed information. TODO: Refactor TypeaheadService and SearchService so that common functions come +from a higher level class?
+ +# File app/services/search_service.rb, line 9 +def initialize; end+
# File app/services/search_service.rb, line 31 +def comments + @comments ||= find_comments(params) +end+
# File app/services/search_service.rb, line 50 +def find_comments(input, limit = 5) + Comment.limit(limit) + .order('nid DESC') + .where('status = 1 AND comment LIKE ?', '%' + input + '%') +end+
# File app/services/search_service.rb, line 70 +def find_maps(input, limit = 5) + Node.limit(limit) + .order('nid DESC') + .where('type = "map" AND node.status = 1 AND title LIKE ?', '%' + input + '%') +end+
# File app/services/search_service.rb, line 56 +def find_nodes(input, limit = 5) + Node.limit(limit) + .order('nid DESC') + .where('node.status = 1 AND title LIKE ?', '%' + input + '%') +end+
search for node title only FIXme with solr
+ + + + +# File app/services/search_service.rb, line 64 +def find_notes(input, limit = 5) + Node.limit(limit) + .order('nid DESC') + .where('type = "note" AND node.status = 1 AND title LIKE ?', '%' + input + '%') +end+
# File app/services/search_service.rb, line 35 +def find_users(input, limit = 10) + User.limit(limit) + .order('id DESC') + .where(status: 1) + .where('username LIKE ?', '%' + input + '%') +end+
# File app/services/search_service.rb, line 27 +def maps(params) + @maps ||= find_maps(params) +end+
# File app/services/search_service.rb, line 19 +def nodes(params) + @nodes ||= find_nodes(params) +end+
# File app/services/search_service.rb, line 23 +def notes(params) + @notes ||= find_notes(params) +end+
Run a search in any of the associated systems for references that contain +the search string
+ + + + +# File app/services/search_service.rb, line 77 +def textSearch_all(srchString) + sresult = DocList.new + unless srchString.nil? || srchString == 0 + # notes + noteList = textSearch_notes(srchString) + sresult.addAll(noteList.items) + + # Node search + Node.limit(5) + .order('nid DESC') + .where('(type = "page" OR type = "place" OR type = "tool") AND node.status = 1 AND title LIKE ?', '%' + srchString + '%') + .select('title,type,nid,path').each do |match| + doc = DocResult.fromSearch(match.nid, match.icon, match.path, match.title, '', 0) + sresult.addDoc(doc) + end + # User profiles + userList = textSearch_profiles(srchString) + sresult.addAll(userList.items) + + # Tags + tagList = textSearch_tags(srchString) + sresult.addAll(tagList.items) + # maps + mapList = textSearch_maps(srchString) + sresult.addAll(mapList.items) + # questions + qList = textSearch_questions(srchString) + sresult.addAll(qList.items) + end + sresult +end+
Search maps for matching text
+ + + + +# File app/services/search_service.rb, line 136 +def textSearch_maps(srchString) + sresult = DocList.new + unless srchString.nil? || srchString == 0 + # maps + maps(srchString).select('title,type,nid,path').each do |match| + doc = DocResult.fromSearch(match.nid, match.icon, match.path, match.title, '', 0) + sresult.addDoc(doc) + end + end + sresult +end+
Search notes for matching strings
+ + + + +# File app/services/search_service.rb, line 123 +def textSearch_notes(srchString) + sresult = DocList.new + unless srchString.nil? || srchString == 0 + # notes + find_notes(srchString, 25).each do |match| + doc = DocResult.fromSearch(match.nid, 'file', match.path, match.title, match.body.split(/#+.+\n+/, 5)[1], 0) + sresult.addDoc(doc) + end + end + sresult +end+
Search profiles for matching text
+ + + + +# File app/services/search_service.rb, line 110 +def textSearch_profiles(srchString) + sresult = DocList.new + unless srchString.nil? || srchString == 0 + # User profiles + users(srchString).each do |match| + doc = DocResult.fromSearch(0, 'user', '/profile/' + match.name, match.name, '', 0) + sresult.addDoc(doc) + end + end + sresult +end+
Search question entries for matching text
+ + + + +# File app/services/search_service.rb, line 170 +def textSearch_questions(srchString) + sresult = DocList.new + questions = Node.where( + 'type = "note" AND node.status = 1 AND title LIKE ?', + '%' + srchString + '%' + ) + .joins(:tag) + .where('term_data.name LIKE ?', 'question:%') + .order('node.nid DESC') + .limit(25) + questions.each do |match| + doc = DocResult.fromSearch(match.nid, 'question-circle', match.path(:question), match.title, 0, match.answers.length.to_i) + sresult.addDoc(doc) + end + sresult +end+
# File app/services/search_service.rb, line 11 +def users(params) + @users ||= find_users(params) +end+
Dynamic Search Page using pure JavaScript JSON RESTful API
+ + + + +# File app/controllers/searches_controller.rb, line 6 +def dynamic + render :dynamic +end+
/search/
+ + + + +# File app/controllers/searches_controller.rb, line 11 +def new +end+
results: /search/foo
+ + + + +# File app/controllers/searches_controller.rb, line 15 +def results + @title = 'Search' + @tagnames = params[:id].split(',') + @users = SearchService.new.users(params[:id]) + @nodes = SearchService.new.nodes(params[:id]) + .paginate(page: params[:page], per_page: 24) +end+
Check the locale was passed and if it is a valid one, set the locale in +cookies
+ + + + +# File app/controllers/settings_controller.rb, line 3 +def change_locale + lang = params[:locale].to_s.strip.to_sym + lang = I18n.default_locale unless I18n.available_locales.include?(lang) + cookies.permanent[:plots2_locale] = lang + I18n.locale = lang + if request.referer + redirect_to request.referer + '?_=' + Time.now.to_i.to_s + else + redirect_to root_url + end +end+
# File lib/solr_toggle/solr_toggle.rb, line 3 +def shouldIndexSolr + if ENV["DISABLE_SOLR_CHECK"] + true + else + solrAvailable + end +end+
# File lib/solr_toggle/solr_toggle.rb, line 11 +def solrAvailable + begin + if !Sunspot::Rails.configuration.disabled? + Node.search do + fulltext 'test' # just see if we break things + end + true + else + false + end + rescue + false + end +end+
# File app/models/spamaway.rb, line 20 +def self.get_pairs(how_many) + @human = [I18n.t('spamaway.human.statement1'), + I18n.t('spamaway.human.statement2'), + I18n.t('spamaway.human.statement3'), + I18n.t('spamaway.human.statement4'), + I18n.t('spamaway.human.statement5'), + I18n.t('spamaway.human.statement6')] + + @robot = [I18n.t('spamaway.robot.statement1'), + I18n.t('spamaway.robot.statement2'), + I18n.t('spamaway.robot.statement3'), + I18n.t('spamaway.robot.statement4'), + I18n.t('spamaway.robot.statement5'), + I18n.t('spamaway.robot.statement6')] + + # static method to return how_many pairs of human/robot statements. + if (how_many <= 0) || (how_many > @human.length) || (how_many > @robot.length) + raise ArgumentError, 'Cannot return ' + how_many + ' statements.' + end + + # randomly select how_many statements from each list + human_perms = @human.permutation(how_many).to_a + robot_perms = @robot.permutation(how_many).to_a + human_index = rand(human_perms.length) + robot_index = rand(robot_perms.length) + + # slap pairs together + pairs = human_perms[human_index].zip(robot_perms[robot_index]) + # randomly flip human/robot order for each statement pair + pairs.each_index do |i| + pairs[i] = pairs[i].permutation.to_a[rand * 2] + end + pairs +end+
# File app/models/spamaway.rb, line 55 +def human_response?(response) + @human = [I18n.t('spamaway.human.statement1'), + I18n.t('spamaway.human.statement2'), + I18n.t('spamaway.human.statement3'), + I18n.t('spamaway.human.statement4'), + I18n.t('spamaway.human.statement5'), + I18n.t('spamaway.human.statement6')] + + @robot = [I18n.t('spamaway.robot.statement1'), + I18n.t('spamaway.robot.statement2'), + I18n.t('spamaway.robot.statement3'), + I18n.t('spamaway.robot.statement4'), + I18n.t('spamaway.robot.statement5'), + I18n.t('spamaway.robot.statement6')] + + # return True if response is a human response, False otherwise. + @human.member? response +end+
Number of top values of each type to return
+ + +# File app/controllers/stats_controller.rb, line 34 +def index + @time = if params[:time] + Time.parse(params[:time]) + else + Time.now + end + + @weekly_notes = Node.select(%[created type status]) + .where(type: 'note', status: 1, created: @time.to_i - 1.weeks.to_i..@time.to_i) + .count(:all) + @weekly_wikis = Revision.select(:timestamp) + .where(timestamp: @time.to_i - 1.weeks.to_i..@time.to_i) + .count + @weekly_members = User.where(created_at: @time - 1.weeks..@time) + .joins('INNER JOIN users ON users.uid = rusers.id') + .where('users.status = 1') + .count + @monthly_notes = Node.select(%[created type status]) + .where(type: 'note', status: 1, created: @time.to_i - 1.months.to_i..@time.to_i) + .count(:all) + @monthly_wikis = Revision.select(:timestamp) + .where(timestamp: @time.to_i - 1.months.to_i..@time.to_i) + .count + @monthly_members = User.where(created_at: @time - 1.months..@time) + .joins('INNER JOIN users ON users.uid = rusers.id') + .where('users.status = 1') + .count + + @notes_per_week_past_year = Node.select(%[created type status]) + .where(type: 'note', status: 1, created: @time.to_i - 1.years.to_i..@time.to_i) + .count(:all) / 52.0 + @edits_per_week_past_year = Revision.select(:timestamp) + .where(timestamp: @time.to_i - 1.years.to_i..@time.to_i) + .count / 52.0 + + @graph_notes = Node.weekly_tallies('note', 52, @time).to_a.sort.to_json + @graph_wikis = Node.weekly_tallies('page', 52, @time).to_a.sort.to_json + @graph_comments = Comment.comment_weekly_tallies(52, @time).to_a.sort.to_json + + users = [] + nids = [] + Node.where(type: 'note', status: 1).each do |note| + unless note.uid == 674 || note.uid == 671 + users << note.uid + nids << note.nid + end + end + + @all_notes = nids.uniq.length + @all_contributors = users.uniq.length +end+
# File app/controllers/stats_controller.rb, line 11 +def range + @start = params[:start] ? Time.parse(params[:start]) : Time.now - 1.month + @end = params[:end] ? Time.parse(params[:end]) : Time.now + @notes = Node.select(%[created type status]) + .where(type: 'note', status: 1, created: @start.to_i..@end.to_i) + .count(:all) + @wikis = Revision.select(:timestamp) + .where(timestamp: @start.to_i..@end.to_i) + .count - @notes # because notes each have one revision + @people = User.where(created_at: @start..@end) + .joins('INNER JOIN users ON users.uid = rusers.id') + .where('users.status = 1') + .count + @answers = Answer.where(created_at: @start..@end) + .count + @comments = Comment.select(:timestamp) + .where(timestamp: @start.to_i..@end.to_i) + .count + @questions = Node.questions.where(status: 1, created: @start.to_i..@end.to_i) + .count + @contributors = User.contributor_count_for(@start,@end) +end+
# File app/controllers/stats_controller.rb, line 2 +def subscriptions + @tags = {} + TagSelection.where(following: true).each do |tag| + @tags[tag.tagname] = @tags[tag.tagname] || 0 + @tags[tag.tagname] += 1 + end + render text: @tags.inspect, status: 200 +end+
for now, adapting like_controller for just tag following. We can create +switches for different kinds of likes. No route or view code as of yet.
+ +for the current user, register as liking the given tag
+ + + + +# File app/controllers/subscription_controller.rb, line 33 +def add + if current_user + # assume tag, for now + if params[:type] == "tag" + tag = Tag.find_by(name: params[:name]) + if tag.nil? + # if the tag doesn't exist, we should create it! + # this could fail validations; error out if so... + tag = Tag.new({ + :vid => 3, # vocabulary id + :name => params[:name], + :description => "", + :weight => 0}) + begin + tag.save! + rescue ActiveRecord::RecordInvalid + flash[:error] = tag.errors.full_messages + redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s + return false + end + end + + # test for uniqueness, handle it as a validation error if you like + if TagSelection.where(following: true, user_id: current_user.uid, tid: tag.tid).length > 0 + flash[:error] = "You are already subscribed to '#{params[:name]}'" + redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s + else + if set_following(true,params[:type],tag.tid) + respond_with do |format| + format.html do + if request.xhr? + render :json => true + else + flash[:notice] = "You are now following '#{params[:name]}'." + redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s + end + end + end + else + flash[:error] = "Something went wrong!" # silly + end + end + else + # user or node subscription + + end + else + flash[:warning] = "You must be logged in to subscribe for email updates; please <a href='javascript:void()' onClick='login()'>log in</a> or <a href='/signup'>create an account</a>." + redirect_to "/tag/"+params[:name] + end +end+
for the current user, remove the like from the given tag
+ + + + +# File app/controllers/subscription_controller.rb, line 86 +def delete + # assume tag, for now + if params[:type] == "tag" + id = Tag.find_by(name: params[:name]).tid + end + if id.nil? + flash[:error] = "You are not subscribed to '#{params[:name]}'" + redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s + else + if !set_following(false,params[:type],id) #should return false if result is that following == false + respond_with do |format| + format.html do + if request.xhr? + render :json => true + else + flash[:notice] = "You have stopped following '#{params[:name]}'." + redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s + end + end + end + else + flash[:error] = "Something went wrong!" # silly + redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s + end + end +end+
for the current user, return whether is presently liked or not
+ + + + +# File app/controllers/subscription_controller.rb, line 21 +def followed + # may be trouble: there can be multiple tags with the same name, no? We can eliminate that possibility in a migration if so. + result = TagSelection.find_by_user_id_and_tid(current_user.uid, params[:id]) if params[:type] == "tag" + if result.nil? + result = false + else + result = result.following + end + render :json => result +end+
# File app/controllers/subscription_controller.rb, line 10 +def index + @title = "Subscriptions" + render :template => "home/subscriptions" +end+
return a count of subscriptions for a given tag
+ + + + +# File app/controllers/subscription_controller.rb, line 16 +def tag_count + render json: TagSelection.where(tid: params[:tid], following: true) +end+
# File app/mailers/subscription_mailer.rb, line 6 +def notify_node_creation(node) + subject = '[PublicLab] ' + (node.has_power_tag('question') ? 'Question: ' : '') + + node.title + Tag.subscribers(node.tags).each do |_key, val| + @user = val[:user] + @node = node + @tags = val[:tags] + @footer = feature('email-footer') + mail(to: val[:user].email, subject: subject).deliver + end +end+
# File app/mailers/subscription_mailer.rb, line 18 +def notify_note_liked(node, user) + subject = "[PublicLab] #{user.username} liked your " + + (node.has_power_tag('question') ? 'question' : 'research note') + @user = user + @node = node + @footer = feature('email-footer') + mail(to: node.author.email, subject: subject).deliver +end+
# File app/mailers/subscription_mailer.rb, line 27 +def notify_tag_added(node, tag, current_user) + @tag = tag + @node = node + @current_user = current_user + given_tags = node.tags.reject { |t| t == tag} + users_to_email = tag.followers_who_dont_follow_tags(given_tags) + users_with_everything_tag = Tag.followers('everything') + final_users_ids = nil + if(!users_to_email.nil? && !users_with_everything_tag.nil?) + final_users_ids = users_to_email.collect(&:id) - users_with_everything_tag.collect(&:uid) + elsif(!users_to_email.nil?) + final_users_ids = users_to_email.collect(&:id) + end + final_users_to_email = User.find(final_users_ids) + final_users_to_email.each do |user| + @user = user + unless user.id == current_user.id + mail(to: user.email, subject: "New tag added on #{node.title}").deliver + end + end + @footer = feature('email-footer') + end+
stackoverflow.com/questions/937429/activerecordbase-without-table +Models without Tables
+ +# File app/models/tableless.rb, line 9 +def self.column(name, sql_type = nil, default = nil, null = true) + columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, + sql_type.to_s, null) +end+
# File app/models/tableless.rb, line 5 +def self.columns + @columns ||= [] +end+
Override the save method to prevent exceptions.
+ + + + +# File app/models/tableless.rb, line 15 +def save(validate = true) + validate ? valid? : true +end+
# File app/models/tag.rb, line 55 +def self.contributor_count(tagname) + tag = Tag.includes(:node).where(name: tagname).first + nodes = tag.node.includes(:revision, :comments,:answers).where(status: 1) + uids = nodes.collect(&:uid) + nodes.each do |n| + uids+=n.comments.collect(&:uid) + uids+=n.answers.collect(&:uid) + uids+=n.revision.collect(&:uid) + end + uids = uids.uniq + uids.length +end+
# File app/models/tag.rb, line 135 +def self.exists?(tagname, nid) + !NodeTag.where('nid = ? AND term_data.name = ?', nid, tagname) + .joins(:tag).empty? +end+
finds recent nodes - should drop “limit” and allow use of chainable +.limit()
+ + + + +# File app/models/tag.rb, line 79 +def self.find_nodes_by_type(tagnames, type = 'note', limit = 10) + nodes = Node.where(status: 1, type: type) + .includes(:tag) + .references(:term_data) + .where('term_data.name IN (?)', tagnames) + #.select(%i[node.nid node.status node.type community_tags.nid community_tags.tid term_data.name term_data.tid]) + # above select could be added later for further optimization + # .where('term_data.name IN (?) OR term_data.parent in (?)', tagnames, tagnames) # greedily fetch children + tags = Tag.where('term_data.name IN (?)', tagnames) + parents = Node.where(status: 1, type: type) + .includes(:tag) + .references(:term_data) + .where('term_data.name IN (?)', tags.collect(&:parent)) + Node.where('node.nid IN (?)', (nodes + parents).collect(&:nid)) + .includes(:revision, :tag) + .references(:node_revisions) + .where(status: 1) + .order('node_revisions.timestamp DESC') + .limit(limit) +end+
just like ::find_nodes_by_type, but +searches wiki pages, places, and tools
+ + + + +# File app/models/tag.rb, line 101 +def self.find_pages(tagnames, limit = 10) + find_nodes_by_type(tagnames, %w[page place tool], limit) +end+
# File app/models/tag.rb, line 126 +def self.find_popular_notes(tagname, views = 20, limit = 10) + Node.where(type: 'note') + .where('term_data.name = ? AND node.views > (?)', tagname, views) + .order('node.nid DESC') + .limit(limit) + .includes(:node_tag, :tag) + .references(:community_tags) +end+
# File app/models/tag.rb, line 212 +def self.find_research_notes(tagnames, limit = 10) + Node.research_notes.where(status: 1) + .includes(:revision, :tag) + .references(:node_revisions) + .where('term_data.name IN (?)', tagnames) + .order('node_revisions.timestamp DESC') + .limit(limit) +end+
finds highest viewcount nodes
+ + + + +# File app/models/tag.rb, line 69 +def self.find_top_nodes_by_type(tagname, type = 'wiki', limit = 10) + Node.where(type: type) + .where('term_data.name = ?', tagname) + .order('node.views DESC') + .limit(limit) + .includes(:node_tag, :tag) + .references(:term_data) +end+
# File app/models/tag.rb, line 144 +def self.follower_count(tagname) + TagSelection.joins(:tag) + .where('term_data.name = ? AND following = ?', tagname, true) + .count +end+
# File app/models/tag.rb, line 150 +def self.followers(tagname) + uids = TagSelection.joins(:tag) + .where('term_data.name = ? AND following = ?', tagname, true) + .collect(&:user_id) + User.where(id: uids) + .where(status: [1, 4]) +end+
# File app/models/tag.rb, line 140 +def self.is_powertag?(tagname) + !tagname.match(':').nil? +end+
# File app/models/tag.rb, line 176 +def self.nodes_for_period(type, nids, start, finish) + Node.select(%[created status type nid]) + .where( + 'type = ? AND status = 1 AND nid IN (?) AND created > ? AND created <= ?', + type, + nids.uniq, + start, + finish + ) +end+
Given a set of tags, return all users following those tags. Return a +dictionary of tags indexed by user. Accepts array of Tags, outputs array of +users as: {user: <user>, tags: [<tags>]} Used in +subscription_mailer
+ + + + +# File app/models/tag.rb, line 192 +def self.subscribers(tags) + tids = tags.collect(&:tid) + # include special tid for indiscriminant subscribers who want it all! + all_tag = Tag.find_by(name: 'everything') + tids += [all_tag.tid] if all_tag + usertags = TagSelection.where('tid IN (?) AND following = ?', tids, true) + d = {} + usertags.each do |usertag| + # For each row of (user,tag), build a user's tag subscriptions + if (usertag.tid == all_tag) && usertag.tag.nil? + puts 'WARNING: all_tag tid ' + String(all_tag) + ' not found for Tag! Please correct this!' + next + end + d[usertag.user.name] = { user: usertag.user } + d[usertag.user.name][:tags] = Set.new if d[usertag.user.name][:tags].nil? + d[usertag.user.name][:tags].add(usertag.tag) + end + d +end+
# File app/models/tag.rb, line 228 +def self.trending(limit = 5 , start_date = DateTime.now - 1.month , end_date = DateTime.now) + Tag.joins(:node_tag, :node) + .select('node.nid, node.created, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('node.created > ?', start_date.to_i) + .where('node.created <= ?', end_date.to_i) + .group(:name) + .order('count DESC') + .limit(limit) +end+
# File app/models/tag.rb, line 50 +def belongs_to(current_user, nid) + node_tag = node_tag.find_by(nid: nid) + node_tag && node_tag.uid == current_user.uid || node_tag.node.uid == current_user.uid +end+
# File app/models/tag.rb, line 11 +def filter_by_type(type, limit = 10) + where(status: 1, type: type) + .limit(limit) + .order('created DESC') +end+
validates :name, :uniqueness => { case_sensitive: false }
+ + + + +# File app/models/tag.rb, line 32 +def id + tid +end+
nodes this tag has been used on; no wildcards
+ + + + +# File app/models/tag.rb, line 46 +def nodes + nodes = Node.where(nid: node_tag.collect(&:nid)) +end+
# File app/models/tag.rb, line 36 +def run_count + self.count = NodeTag.where(tid: tid).count + save +end+
# File app/models/tag.rb, line 41 +def subscriptions + tag_selection.where(following: true) +end+
OPTIMIZE: this too!
+ + + + +# File app/models/tag.rb, line 159 +def weekly_tallies(type = 'note', span = 52) + weeks = {} + tids = Tag.where('name IN (?)', [name]) + .collect(&:tid) + nids = NodeTag.where('tid IN (?)', tids) + .collect(&:nid) + (1..span).each do |week| + weeks[span - week] = Tag.nodes_for_period( + type, + nids, + (Time.now.to_i - week.weeks.to_i).to_s, + (Time.now.to_i - (week - 1).weeks.to_i).to_s + ).count(:all) + end + weeks +end+
# File app/controllers/tag_controller.rb, line 166 +def barnstar + node = Node.find params[:nid] + tagname = 'barnstar:' + params[:star] + if Tag.exists?(tagname, params[:nid]) + flash[:error] = I18n.t('tag_controller.tag_already_exists') + elsif !node.add_barnstar(tagname.strip, current_user) + flash[:error] = I18n.t('tag_controller.barnstar_not_created') + else + flash[:notice] = I18n.t('tag_controller.barnstar_awarded', url1: '/wiki/barnstars#' + params[:star].split('-').each(&:capitalize!).join('+') + '+Barnstar', star: params[:star], url2: '/profile/' + node.author.name, awardee: node.author.name).html_safe + # on success add comment + barnstar_info_link = '<a href="//' + request.host.to_s + '/wiki/barnstars">barnstar</a>' + node.add_comment(subject: 'barnstar', + uid: current_user.uid, + body: "@#{current_user.username} awards a #{barnstar_info_link} to #{node.drupal_user.name} for their awesome contribution!") + end + redirect_to node.path + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/tag_controller.rb, line 152 +def blog + nids = Tag.find_nodes_by_type(params[:id], 'note', 20).collect(&:nid) + @notes = Node.paginate(page: params[:page], per_page: 6) + .where('status = 1 AND nid in (?)', nids) + .order('nid DESC') + @tags = Tag.where(name: params[:id]) + @tagnames = @tags.collect(&:name).uniq! || [] + @title = @tagnames.join(',') + ' Blog' if @tagnames +end+
# File app/controllers/tag_controller.rb, line 292 +def contributors + set_sidebar :tags, [params[:id]], note_count: 20 + @tagnames = [params[:id]] + @tag = Tag.find_by(name: params[:id]) + @notes = Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name = ?', params[:id]) + .order('node_revisions.timestamp DESC') + @users = @notes.collect(&:author).uniq +end+
/contributors
+ + + + +# File app/controllers/tag_controller.rb, line 305 +def contributors_index + @tagnames = ['balloon-mapping', 'spectrometer', 'infragram', 'air-quality', 'water-quality'] + @tagdata = {} + @tags = [] + + @tagnames.each do |tagname| + tag = Tag.find_by(name: tagname) + @tags << tag if tag + @tagdata[tagname] = {} + t = Tag.where(name: tagname) + nct = NodeTag.where('tid in (?)', t.collect(&:tid)) + @tagdata[tagname][:users] = Node.where('nid IN (?)', nct.collect(&:nid)).collect(&:author).uniq.length + @tagdata[tagname][:wikis] = Node.where("nid IN (?) AND (type = 'page' OR type = 'tool' OR type = 'place')", nct.collect(&:nid)).count + @tagdata[:notes] = Node.where("nid IN (?) AND type = 'note'", nct.collect(&:nid)).count + end + render template: 'tag/contributors-index' +end+
# File app/controllers/tag_controller.rb, line 184 +def create + params[:name] ||= '' + tagnames = params[:name].split(',') + @output = { + errors: [], + saved: [] + } + @tags = [] # not used except in tests for now + + nid = params[:nid] || params[:id] + node = Node.find nid + tagnames.each do |tagname| + # this should all be done in the model: + + if Tag.exists?(tagname, nid) + @output[:errors] << I18n.t('tag_controller.tag_already_exists') + elsif node.can_tag(tagname, current_user) === true || current_user.role == 'admin' # || current_user.role == "moderator" + saved, tag = node.add_tag(tagname.strip, current_user) + if saved + @tags << tag + @output[:saved] << [tag.name, tag.id] + else + @output[:errors] << I18n.t('tag_controller.error_tags') + tag.errors[:name].first + end + else + @output[:errors] << node.can_tag(tagname, current_user, true) + end + + end + respond_with do |format| + format.html do + if request.xhr? + render json: @output + else + flash[:notice] = I18n.t('tag_controller.tags_created_error', + tag_count: @output[:saved].length, + error_count: @output[:errors].length).html_safe + redirect_to node.path + end + end + end +end+
should delete only the term_node/node_tag (instance), not the term_data +(class)
+ + + + +# File app/controllers/tag_controller.rb, line 228 +def delete + node_tag = NodeTag.where(nid: params[:nid], tid: params[:tid]).first + # only admins, mods, and tag authors can delete other peoples' tags + if node_tag.uid == current_user.uid || current_user.role == 'admin' || current_user.role == 'moderator' + + node_tag.delete + respond_with do |format| + format.html do + if request.xhr? + render text: node_tag.tid + else + flash[:notice] = I18n.t('tag_controller.tag_deleted') + redirect_to node_tag.node.path + end + end + end + else + flash[:error] = I18n.t('tag_controller.must_own_tag_to_delete') + redirect_to Node.find_by(nid: params[:nid]).path + end +end+
# File app/controllers/tag_controller.rb, line 331 +def gridsEmbed + render layout: false +end+
# File app/controllers/tag_controller.rb, line 5 +def index + if params[:format] + @toggle = params[:format].to_i + else + @toggle = 1 + end + + @title = I18n.t('tag_controller.tags') + @paginated = true + if params[:search] + prefix = params[:search] + @tags = Tag.joins(:node_tag, :node) + .select('node.nid, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) + .where("name LIKE :prefix", prefix: "#{prefix}%") + .group(:name) + .order('count DESC') + .paginate(page: params[:page], per_page: 24) + elsif @toggle == 1 + @tags = Tag.joins(:node_tag, :node) + .select('node.nid, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) + .group(:name) + .order('count DESC') + .paginate(page: params[:page], per_page: 24) + else + @tags = Tag.joins(:node_tag, :node) + .select('node.nid, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) + .group(:name) + .order('name') + .paginate(page: params[:page], per_page: 24) + end +end+
# File app/controllers/tag_controller.rb, line 323 +def location + render template: 'locations/_form' +end+
# File app/controllers/tag_controller.rb, line 327 +def location_modal + render template: 'locations/_modal', layout: false +end+
# File app/controllers/tag_controller.rb, line 267 +def rss + if params[:tagname][-1..-1] == '*' + @notes = Node.where(status: 1, type: 'note') + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name LIKE (?)', params[:tagname][0..-2] + '%') + .limit(20) + .order('node_revisions.timestamp DESC') + else + @notes = Tag.find_nodes_by_type([params[:tagname]], 'note', 20) + end + respond_to do |format| + format.rss do + response.headers['Content-Type'] = 'application/xml; charset=utf-8' + response.headers['Access-Control-Allow-Origin'] = '*' + render layout: false + end + format.ics do + response.headers['Content-Disposition'] = "attachment; filename='public-lab-events.ics'" + response.headers['Content-Type'] = 'text/calendar; charset=utf-8' + render layout: false, template: 'tag/icalendar.ics', filename: 'public-lab-events.ics' + end + end +end+
# File app/controllers/tag_controller.rb, line 43 +def show + # try for a matching /wiki/_TAGNAME_ or /_TAGNAME_ + @wiki = Node.where(path: "/wiki/#{params[:id]}").try(:first) || Node.where(path: "/#{params[:id]}").try(:first) + default_type = if params[:id].match('question:') + 'questions' + else + 'note' + end + # params[:node_type] - this is an optional param + # if params[:node_type] is nil - use @default_type + @node_type = params[:node_type] || default_type + + node_type = 'note' if @node_type == 'questions' || @node_type == 'note' + node_type = 'page' if @node_type == 'wiki' + node_type = 'map' if @node_type == 'maps' + qids = Node.questions.where(status: 1).collect(&:nid) + if params[:id][-1..-1] == '*' # wildcard tags + @wildcard = true + @tags = Tag.where('name LIKE (?)', params[:id][0..-2] + '%') + nodes = Node.where(status: 1, type: node_type) + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name LIKE (?) OR term_data.parent LIKE (?)', params[:id][0..-2] + '%', params[:id][0..-2] + '%') + .page(params[:page]) + .order('node_revisions.timestamp DESC') + else + @tags = Tag.where(name: params[:id]) + nodes = Node.where(status: 1, type: node_type) + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name = ? OR term_data.parent = ?', params[:id], params[:id]) + .page(params[:page]) + .order('node_revisions.timestamp DESC') + end + + # breaks the parameter + # sets everything to an empty array + set_sidebar :tags, [params[:id]] + + @notes = nodes.where('node.nid NOT IN (?)', qids) if @node_type == 'note' + @questions = nodes.where('node.nid IN (?)', qids) if @node_type == 'questions' + @wikis = nodes if @node_type == 'wiki' + @nodes = nodes if @node_type == 'maps' + @title = params[:id] + # the following could be refactored into a Tag.contributor_count method: + notes = Node.where(status: 1, type: 'note') + .select('node.nid, node.type, node.uid, node.status, term_data.*, community_tags.*') + .includes(:tag) + .references(:term_data) + .where('term_data.name = ?', params[:id]) + @length = notes.collect(&:uid).uniq.length || 0 + + respond_with(nodes) do |format| + format.html { render 'tag/show' } + format.xml { render xml: nodes } + format.json do + json = [] + nodes.each do |node| + json << node.as_json(except: %[path tags]) + json.last['path'] = 'https://' + request.host.to_s + node.path + json.last['preview'] = node.body_preview(500) + json.last['image'] = node.main_image.path(:large) if node.main_image + json.last['tags'] = Node.find(node.id).tags.collect(&:name) if node.tags + end + render json: json + end + end +end+
# File app/controllers/tag_controller.rb, line 250 +def suggested + if params[:id].length > 2 + @suggestions = [] + # filtering out tag spam by requiring tags attached to a published node + Tag.where('name LIKE ?', '%' + params[:id] + '%') + .includes(:node) + .references(:node) + .where('node.status = 1') + .limit(10).each do |tag| + @suggestions << tag.name.downcase + end + render json: @suggestions.uniq + else + render json: [] + end +end+
# File app/controllers/tag_controller.rb, line 143 +def widget + num = params[:n] || 4 + nids = Tag.find_nodes_by_type(params[:id], 'note', num).collect(&:nid) + @notes = Node.page(params[:page]) + .where('status = 1 AND nid in (?)', nids) + .order('nid DESC') + render layout: false +end+
List of tag values from a search
+ +# File app/models/tag_list.rb, line 5 +def initialize; end+
# File app/models/tag_list.rb, line 16 +def addAll(tlist) + @items ||= [] + tlist.each { |tItem| @items << tItem } unless tlist.nil? +end+
# File app/models/tag_list.rb, line 11 +def addTag(ntag) + @items ||= [] + @items << ntag +end+
# File app/models/tag_list.rb, line 21 +def getTags + @item ||= [] + @items +end+
# File app/models/tag_list.rb, line 7 +def setSrchParams=(value) + @srchParams = value +end+
This subclass is used to auto-generate the RESTful data structure. It is +generally not useful for internal Ruby usage
+ +but must be included for full RESTful functionality.+ +
Tags are text values assigned to various site documents.
+ +# File app/models/tag_result.rb, line 7 +def self.fromSearch(tid, tval, ttype, tsrc) + obj = new + obj.tagId = tid + obj.tagVal = tval + obj.tagType = ttype + obj.tagSource = tsrc + obj +end+
# File app/models/tag_result.rb, line 5 +def initialize; end+
This subclass is used to auto-generate the RESTful data structure. It is +generally not useful for internal Ruby usage
+ +but must be included for full RESTful functionality.+ +
# File app/models/tag_selection.rb, line 15 +def ruser + User.find_by(id: user_id) +end+
# File app/models/tag_selection.rb, line 19 +def tagname + tag.name +end+
# File app/models/tag_selection.rb, line 11 +def user + DrupalUser.find_by(uid: user_id) +end+
# File app/controllers/talk_controller.rb, line 2 +def show + @node = Node.find_by_path params[:id] + @node = Node.find_by_path 'wiki/' + params[:id] if @node.nil? +end+
The TypeaheadService class is a utility +class whose purpose is to provide fast responses to text queries within +different categories (record types, functionality, subsystems, etc). Though +similar in operation to the SearchService, +the implementation is separate, in that the goal of the response is to +provide fast returns at a higher level than a general search. In +effect, TypeaheadService provides +pointers to better searches, while SearchService provides deep and detailed +information. TODO: Refactor TypeaheadService and SearchService so that common functions come +from a higher level class?
+ +# File app/services/typeahead_service.rb, line 8 +def initialize; end+
# File app/services/typeahead_service.rb, line 35 +def comments(input, limit = 5) + if ActiveRecord::Base.connection.adapter_name == 'Mysql2' + Comment.search(input) + .limit(limit) + .order('nid DESC') + .where(status: 1) + else + Comment.limit(limit) + .order('nid DESC') + .where('status = 1 AND comment LIKE ?', '%' + input + '%') + end +end+
# File app/services/typeahead_service.rb, line 80 +def maps(input, limit = 5) + Node.limit(limit) + .order('nid DESC') + .where('type = "map" AND node.status = 1 AND title LIKE ?', '%' + input + '%') +end+
# File app/services/typeahead_service.rb, line 48 +def notes(input, limit = 5) + if ActiveRecord::Base.connection.adapter_name == 'Mysql2' + Node.search(input) + .group(:nid) + .includes(:node) + .references(:node) + .limit(limit) + .where("node.type": "note", "node.status": 1) + .order(timestamp: :desc) + else + Node.limit(limit) + .group(:nid) + .where(type: "note", status: 1) + .order(updated_at: :desc) + .where('title LIKE ?', '%' + input + '%') + end +end+
Run a search in any of the associated systems for references that contain +the search string
+ + + + +# File app/services/typeahead_service.rb, line 87 +def search_all(srchString, limit = 5) + sresult = TagList.new + unless srchString.nil? || srchString == 0 + # notes + notesrch = search_notes(srchString, limit) + sresult.addAll(notesrch.getTags) + # wikis + wikisrch = search_wikis(srchString, limit) + sresult.addAll(wikisrch.getTags) + # User profiles + usersrch = search_profiles(srchString, limit) + sresult.addAll(usersrch.getTags) + # Tags -- handled differently because tag + tagsrch = search_tags(srchString, limit) + sresult.addAll(tagsrch.getTags) + # maps + mapsrch = search_maps(srchString, limit) + sresult.addAll(mapsrch.getTags) + # questions + qsrch = search_questions(srchString, limit) + sresult.addAll(qsrch.getTags) + end + sresult +end+
Search maps for matching text
+ + + + +# File app/services/typeahead_service.rb, line 162 +def search_maps(srchString, limit = 5) + sresult = TagList.new + unless srchString.nil? || srchString == 0 + # maps + maps(srchString, limit).select('title,type,nid,path').each do |match| + tval = TagResult.new + tval.tagId = match.nid + tval.tagVal = match.title + tval.tagType = match.icon + tval.tagSource = match.path + sresult.addTag(tval) + end + end + sresult +end+
Search notes for matching strings
+ + + + +# File app/services/typeahead_service.rb, line 130 +def search_notes(srchString, limit = 5) + sresult = TagList.new + unless srchString.nil? || srchString == 0 + notes(srchString, limit).uniq.each do |match| + tval = TagResult.new + tval.tagId = match.nid + tval.tagVal = match.title + tval.tagType = 'file' + tval.tagSource = match.path + sresult.addTag(tval) + end + end + sresult +end+
Search profiles for matching text
+ + + + +# File app/services/typeahead_service.rb, line 113 +def search_profiles(srchString, limit = 5) + sresult = TagList.new + unless srchString.nil? || srchString == 0 + # User profiles + users(srchString, limit).each do |match| + tval = TagResult.new + tval.tagId = 0 + tval.tagType = 'user' + tval.tagVal = match.username + tval.tagSource = '/profile/' + match.username + sresult.addTag(tval) + end + end + sresult +end+
Search question entries for matching text
+ + + + +# File app/services/typeahead_service.rb, line 196 +def search_questions(srchString, limit = 5) + sresult = TagList.new + questions = Node.where( + 'type = "note" AND node.status = 1 AND title LIKE ?', + '%' + srchString + '%' + ) + .joins(:tag) + .where('term_data.name LIKE ?', 'question:%') + .order('node.nid DESC') + .limit(limit) + questions.each do |match| + tval = TagResult.fromSearch( + match.nid, + match.title, + 'question-circle', + match.path + ) + sresult.addTag(tval) + end + sresult +end+
Search wikis for matching strings
+ + + + +# File app/services/typeahead_service.rb, line 146 +def search_wikis(srchString, limit = 5) + sresult = TagList.new + unless srchString.nil? || srchString == 0 + wikis(srchString, limit).select('node.title,node.type,node.nid,node.path').each do |match| + tval = TagResult.new + tval.tagId = match.nid + tval.tagVal = match.title + tval.tagType = 'file' + tval.tagSource = match.path + sresult.addTag(tval) + end + end + sresult +end+
search_users() returns a standard TagResult; +users() returns an array of User records It's +unclear if TagResult was supposed to be broken +into other types like DocResult? but perhaps +could simply be renamed Result.
+ + + + +# File app/services/typeahead_service.rb, line 15 +def users(input, limit = 5) + if ActiveRecord::Base.connection.adapter_name == 'Mysql2' + User.search(input) + .limit(limit) + .where(status: 1) + else + User.limit(limit) + .order('id DESC') + .where('username LIKE ? AND status = 1', '%' + input + '%') + end +end+
# File app/services/typeahead_service.rb, line 66 +def wikis(input, limit = 5) + if ActiveRecord::Base.connection.adapter_name == 'Mysql2' + Node.search(input) + .includes(:node) + .references(:node) + .limit(limit) + .where("node.type": "page", "node.status": 1) + else + Node.limit(limit) + .order('nid DESC') + .where('type = "page" AND node.status = 1 AND title LIKE ?', '%' + input + '%') + end +end+
# File app/models/node.rb, line 2 +def validate(record) + if record.title == '' || record.title.nil? + # record.errors[:base] << "You must provide a title." + # otherwise the below title uniqueness check fails, as title presence validation doesn't run until after + elsif record.title == 'new' && record.type == 'page' + record.errors[:base] << "You may not use the title 'new'." # otherwise the below title uniqueness check fails, as title presence validation doesn't run until after + else + if !Node.where(path: record.generate_path).first.nil? && record.type == 'note' + record.errors[:base] << 'You have already used this title today.' + end + end +end+
# File app/models/user.rb, line 2 +def validate(record) + if DrupalUser.find_by(name: record.username) && record.openid_identifier.nil? + record.errors[:base] << 'That username is already taken. If this is your username, you can simply log in to this site.' + end +end+
# File app/models/user.rb, line 43 +def self.search(query) + User.where('MATCH(username, bio) AGAINST(?)', query) +end+
# File app/models/user.rb, line 181 +def add_to_lists(lists) + lists.each do |list| + WelcomeMailer.add_to_list(self, list) + end +end+
# File app/models/user.rb, line 274 +def barnstars + NodeTag.includes(:node, :tag) + .references(:term_data) + .where('type = ? AND term_data.name LIKE ? AND node.uid = ?', 'note', 'barnstar:%', uid) +end+
# File app/models/user.rb, line 248 +def comment_streak(span = 365) + days = {} + streak = 0 + comment_count = 0 + (0..span).each do |day| + days[day] = Comment.select(:timestamp) + .where( uid: drupal_user.uid, + status: 1, + timestamp: Time.now.midnight.to_i - day.days.to_i..Time.now.midnight.to_i - (day - 1).days.to_i) + .count + break if days[day] == 0 + streak += 1 + comment_count += days[day] + end + [streak, comment_count] +end+
# File app/models/user.rb, line 313 +def content_followed_in_past_period(time_period) + self.node.where("created >= #{time_period.to_i} AND changed >= #{time_period.to_i}") +end+
# File app/models/user.rb, line 47 +def create_drupal_user + self.bio ||= '' + if drupal_user.nil? + drupal_user = DrupalUser.new(name: username, + pass: rand(100_000_000_000_000_000_000), + mail: email, + mode: 0, + sort: 0, + threshold: 0, + theme: '', + signature: '', + signature_format: 0, + created: DateTime.now.to_i, + access: DateTime.now.to_i, + login: DateTime.now.to_i, + status: 1, + timezone: nil, + language: '', + picture: '', + init: '', + data: nil, + timezone_id: 0, + timezone_name: '') + drupal_user.save! + self.id = drupal_user.uid + else + self.id = DrupalUser.find_by(name: username).uid + end +end+
# File app/models/user.rb, line 77 +def destroy_drupal_user + drupal_user.destroy +end+
this is ridiculous. We need to store uid in this model. …migration is in +progress. start getting rid of these calls…
+ + + + +# File app/models/user.rb, line 87 +def drupal_user + DrupalUser.find_by(name: username) +end+
# File app/models/user.rb, line 284 +def first_time_poster + notes.where(status: 1).count == 0 +end+
# File app/models/user.rb, line 288 +def follow(other_user) + active_relationships.create(followed_id: other_user.id) +end+
# File app/models/user.rb, line 176 +def following(tagname) + tids = Tag.where(name: tagname).collect(&:tid) + !TagSelection.where(following: true, tid: tids, user_id: uid).empty? +end+
# File app/models/user.rb, line 296 +def following?(other_user) + following_users.include?(other_user) +end+
# File app/models/user.rb, line 105 +def generate_reset_key + # invent a key and save it + key = '' + 20.times do + key += [*'a'..'z'].sample + end + self.reset_key = key + key +end+
# File app/models/user.rb, line 163 +def get_value_of_power_tag(key) + tname = self.user_tags.where('value LIKE ?' , key + ':%') + tvalue = tname.first.name.partition(':').last + tvalue +end+
power tags have “key:value” format, and should be searched with a “key:*” +wildcard
+ + + + +# File app/models/user.rb, line 158 +def has_power_tag(key) + tids = self.user_tags.where('value LIKE ?' , key + ':%').collect(&:id) + !tids.blank? +end+
we can revise/improve this for m2m later…
+ + + + +# File app/models/user.rb, line 136 +def has_role(r) + role == r +end+
# File app/models/user.rb, line 153 +def has_tag(tagname) + user_tags.collect(&:value).include?(tagname) +end+
# File app/models/user.rb, line 127 +def lat + drupal_user.lat +end+
# File app/models/user.rb, line 131 +def lon + drupal_user.lon +end+
# File app/models/user.rb, line 212 +def note_streak(span = 365) + days = {} + streak = 0 + note_count = 0 + (0..span).each do |day| + days[day] = Node.select(:created) + .where( uid: drupal_user.uid, + type: 'note', + status: 1, + created: Time.now.midnight.to_i - day.days.to_i..Time.now.midnight.to_i - (day - 1).days.to_i) + .count + break if days[day] == 0 + streak += 1 + note_count += days[day] + end + [streak, note_count] +end+
# File app/models/user.rb, line 91 +def notes + Node.where(uid: uid) + .where(type: 'note') + .order('created DESC') +end+
# File app/models/user.rb, line 123 +def path + "/profile/#{self.username}" +end+
# File app/models/user.rb, line 280 +def photo_path(size = :medium) + photo.url(size) +end+
# File app/models/user.rb, line 300 +def profile_image + if photo_file_name + puts photo_path(:thumb) + photo_path(:thumb) + else + "https://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(email)}" + end +end+
# File app/models/user.rb, line 309 +def questions + Node.questions.where(status: 1, uid: id) +end+
# File app/models/user.rb, line 81 +def set_token + self.token = SecureRandom.uuid if self.token.nil? +end+
# File app/models/user.rb, line 265 +def streak(span = 365) + note_streak = self.note_streak(span) + wiki_edit_streak = self.wiki_edit_streak(span) + comment_streak = self.comment_streak(span) + streak_count = [note_streak[1], wiki_edit_streak[1], comment_streak[1]] + streak = [note_streak[0], wiki_edit_streak[0], comment_streak[0]] + [streak.max, streak_count] +end+
# File app/models/user.rb, line 169 +def subscriptions(type = :tag) + if type == :tag + TagSelection.where(user_id: uid, + following: true) + end +end+
# File app/models/user.rb, line 144 +def tagnames(limit = 20, defaults = true) + tagnames = [] + Node.order('nid DESC').where(type: 'note', status: 1, uid: self.id).limit(limit).each do |node| + tagnames += node.tags.collect(&:name) + end + tagnames += ['balloon-mapping', 'spectrometer', 'near-infrared-camera', 'thermal-photography', 'newsletter'] if tagnames.empty? && defaults + tagnames.uniq +end+
# File app/models/user.rb, line 119 +def title + self.username +end+
# File app/models/user.rb, line 115 +def uid + drupal_user.uid +end+
# File app/models/user.rb, line 292 +def unfollow(other_user) + active_relationships.where(followed_id: other_user.id).first.destroy +end+
# File app/models/user.rb, line 200 +def weekly_comment_tally(span = 52) + weeks = {} + (0..span).each do |week| + weeks[span - week] = Comment.select(:timestamp) + .where( uid: drupal_user.uid, + status: 1, + timestamp: Time.now.to_i - week.weeks.to_i..Time.now.to_i - (week - 1).weeks.to_i) + .count + end + weeks +end+
# File app/models/user.rb, line 187 +def weekly_note_tally(span = 52) + weeks = {} + (0..span).each do |week| + weeks[span - week] = Node.select(:created) + .where( uid: drupal_user.uid, + type: 'note', + status: 1, + created: Time.now.to_i - week.weeks.to_i..Time.now.to_i - (week - 1).weeks.to_i) + .count + end + weeks +end+
# File app/models/user.rb, line 230 +def wiki_edit_streak(span = 365) + days = {} + streak = 0 + wiki_edit_count = 0 + (0..span).each do |day| + days[day] = Revision.joins(:node) + .where( uid: drupal_user.uid, + status: 1, + timestamp: Time.now.midnight.to_i - day.days.to_i..Time.now.midnight.to_i - (day - 1).days.to_i) + .where('node.type != ?', 'note') + .count + break if days[day] == 0 + streak += 1 + wiki_edit_count += days[day] + end + [streak, wiki_edit_count] +end+
# File app/controllers/user_sessions_controller.rb, line 6 +def create + params[:user_session][:username] = params[:openid] if params[:openid] # second runthrough must preserve username + username = params[:user_session][:username] if params[:user_session] + @user = User.find_by(username: username) + + # try finding by email, if that exists + if @user.nil? && !User.where(email: username).empty? + @user = User.find_by(email: username) + params[:user_session][:username] = @user.username + end + + if params[:user_session].nil? || @user && @user.drupal_user.status == 1 || @user.nil? + # an existing native user + if params[:user_session].nil? || @user + if @user && @user.crypted_password.nil? # the user has not created a pwd in the new site + params[:user_session][:openid_identifier] = 'https://old.publiclab.org/people/' + username + '/identity' if username + params[:user_session].delete(:password) + params[:user_session].delete(:username) + params[:openid] = username # pack up username for second runthrough + end + @user_session = UserSession.new(params[:user_session]) + saved = @user_session.save do |result| + if result + # replace this with temporarily saving pwd in session, + # and automatically saving it in the user record after login is completed + if current_user.crypted_password.nil? # the user has not created a pwd in the new site + flash[:warning] = I18n.t('user_sessions_controller.create_password_for_new_site') + redirect_to '/profile/edit' + else + flash[:notice] = I18n.t('user_sessions_controller.logged_in') + if session[:openid_return_to] # for openid login, redirects back to openid auth process + return_to = session[:openid_return_to] + session[:openid_return_to] = nil + redirect_to return_to + elsif session[:return_to] + return_to = session[:return_to] + session[:return_to] = nil + redirect_to return_to + elsif params[:return_to] + redirect_to params[:return_to] + else + redirect_to '/dashboard' + end + end + else + render action: 'new' + end + end + else # not a native user + if !DrupalUser.find_by(name: username).nil? + # this is a user from the old site who hasn't registered on the new site + redirect_to controller: :users, action: :create, user: { openid_identifier: username } + else # totally new user! + flash[:warning] = I18n.t('user_sessions_controller.sign_up_to_join') + redirect_to '/signup' + end + end + elsif params[:user_session].nil? || @user && @user.drupal_user.status == 5 || @user.nil? + flash[:error] = I18n.t('user_sessions_controller.user_has_been_moderated', username: @user.username).html_safe + redirect_to '/' + else + flash[:error] = I18n.t('user_sessions_controller.user_has_been_banned', username: @user.username).html_safe + redirect_to '/' + end +end+
# File app/controllers/user_sessions_controller.rb, line 72 +def destroy + @user_session = UserSession.find + @user_session.destroy + flash[:notice] = I18n.t('user_sessions_controller.logged_out') + redirect_to '/' + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/user_sessions_controller.rb, line 79 +def logout_remotely + current_user.reset_persistence_token! + flash[:notice] = I18n.t('user_sessions_controller.logged_out') + redirect_to '/' + '?_=' + Time.now.to_i.to_s +end+
# File app/controllers/user_sessions_controller.rb, line 2 +def new + @title = I18n.t('user_sessions_controller.log_in') +end+
# File app/models/user_tag.rb, line 14 +def self.exists?(uid, value) + UserTag.where(uid: uid, value: value).count > 0 +end+
# File app/models/user_tag.rb, line 18 +def name + self.value +end+
# File app/models/user_tag.rb, line 10 +def preprocess + self.value = self.value.downcase +end+
# File app/controllers/user_tags_controller.rb, line 4 +def create + + @output = { + errors: [], + saved: [] + } + exist = false + + user = User.find(params[:id]) + + if current_user && (current_user.role == 'admin' || current_user == user) + if params[:name] + tagnames = params[:name].split(',') + tagnames.each do |tagname| + name = tagname.downcase + if UserTag.exists?(current_user.id, name) + @output[:errors] << I18n.t('user_tags_controller.tag_already_exists') + exist = true + end + + unless exist + user_tag = user.user_tags.build(value: name) + if user_tag.save + @output[:saved] << [name, user_tag.id] + else + @output[:errors] << I18n.t('user_tags_controller.cannot_save_value') + end + end + end + else + @output[:errors] << I18n.t('user_tags_controller.value_cannot_be_empty') + end + else + @output[:errors] << I18n.t('user_tags_controller.admin_user_manage_tags') + end + + if request.xhr? + render json: @output + else + if !@output[:errors].empty? + flash[:error] = I18n.t('user_tags_controller.errors_occured', count: @output[:errors].length).html_safe + else + flash[:notice] = I18n.t('user_tags_controller.tag_created', tag_name: @output[:saved][0][0]).html_safe + end + redirect_to info_path, id: params[:id] + end +end+
# File app/controllers/user_tags_controller.rb, line 52 +def delete + output = { + status: false, + errors: [] + } + message = '' + + begin + @user_tag = UserTag.where(uid: params[:id], value: params[:name]) + if(!@user_tag.nil?) + @user_tag = @user_tag.first + end + + if current_user.role == 'admin' || params[:id].to_i == current_user.id + if (!@user_tag.nil? && @user_tag.user == current_user) || (!@user_tag.nil? && current_user.role == 'admin') + UserTag.where(uid: params[:id] , value: params[:name]).destroy_all + message = I18n.t('user_tags_controller.tag_deleted') + output[:status] = true + else + output[:status] = false + message = I18n.t('user_tags_controller.tag_doesnt_exist') + end + else + message = I18n.t('user_tags_controller.admin_user_manage_tags') + end + rescue ActiveRecord::RecordNotFound + output[:status] = false + message = I18n.t('user_tags_controller.tag_doesnt_exist') + end + + output[:errors] << message + respond_with do |format| + format.js + format.html do + if request.xhr? + render json: output + else + if output[:status] + flash[:notice] = message + else + flash[:error] = message + end + redirect_to info_path + end + end + end +end+
# File app/controllers/user_tags_controller.rb, line 100 +def suggested + if !params[:name].empty? + suggested = [] + UserTag.where('value LIKE ?', params[:name] + '%').each do |tag| + suggested << tag.value + end + render json: suggested.uniq + else + render json: [] + end +end+
# File app/controllers/users_controller.rb, line 221 +def comments + @comments = Comment.limit(20) + .order("timestamp DESC") + .where(status: 0, uid: params[:id]) + .paginate(page: params[:page]) + render partial: 'comments/comments' +end+
# File app/controllers/users_controller.rb, line 11 +def create + @user = User.new(params[:user]) + using_recaptcha = !params[:spamaway] && Rails.env == "production" + recaptcha = verify_recaptcha(model: @user) if using_recaptcha + @spamaway = Spamaway.new(params[:spamaway]) unless using_recaptcha + if ((@spamaway && @spamaway.valid?) || recaptcha) && @user.save({}) + if current_user.crypted_password.nil? # the user has not created a pwd in the new site + flash[:warning] = I18n.t('users_controller.account_migrated_create_new_password') + redirect_to "/profile/edit" + else + @user.add_to_lists(['publiclaboratory']) + flash[:notice] = I18n.t('users_controller.registration_successful').html_safe + flash[:warning] = I18n.t('users_controller.spectralworkbench_or_mapknitter', :url1 => "'#{session[:openid_return_to]}'").html_safe if session[:openid_return_to] + session[:openid_return_to] = nil + redirect_to "/dashboard" + end + else + # pipe all spamaway errors into the user error display + if @spamaway + @spamaway.errors.full_messages.each do |message| + @user.errors.add(:spam_detection, message) + end + elsif using_recaptcha && recaptcha == false + flash.now[:warning] = "If you're having trouble creating an account, try <a href='/signup?spamaway=true'>the alternative signup form</a>, or <a href='mailto:staff@publiclab.org'>ask staff for help</a>" + end + # send all errors to the page so the user can try again + @action = "create" + render action: 'new' + end +end+
# File app/controllers/users_controller.rb, line 66 +def edit + @action = "update" # sets the form url + if params[:id] # admin only + @drupal_user = DrupalUser.find_by(name: params[:id]) + @user = @drupal_user.user + else + @user = current_user + @drupal_user = current_user.drupal_user + end + if current_user && current_user.uid == @user.uid #|| current_user.role == "admin" + render :template => "users/edit" + else + flash[:error] = I18n.t('users_controller.only_user_edit_profile', :user => @user.name).html_safe + redirect_to "/profile/"+@user.name + end +end+
# File app/controllers/users_controller.rb, line 261 +def followers + @title = "Followers" + @user = User.find_by(username: params[:id]) + @users = @user.followers.paginate(page: params[:page], per_page: 24) + render 'show_follow' +end+
# File app/controllers/users_controller.rb, line 254 +def following + @title = "Following" + @user = User.find_by(username: params[:id]) + @users = @user.following_users.paginate(page: params[:page], per_page: 24) + render 'show_follow' +end+
# File app/controllers/users_controller.rb, line 250 +def info + @user = DrupalUser.find_by(name: params[:id]) +end+
# File app/controllers/users_controller.rb, line 148 +def likes + @user = DrupalUser.find_by(name: params[:id]) + @title = "Liked by "+@user.name + @notes = @user.liked_notes + .includes([:tag, :comments]) + .paginate(page: params[:page], per_page: 24) + @wikis = @user.liked_pages + @tagnames = [] + @unpaginated = false +end+
# File app/controllers/users_controller.rb, line 83 +def list + # allow admins to view recent users + if params[:id] + @users = DrupalUser.joins('INNER JOIN rusers ON rusers.username = users.name') + .order("updated_at DESC") + .where('rusers.role = ?', params[:id]) + .page(params[:page]) + else + # recently active + @users = DrupalUser.select('*, MAX(node.changed) AS last_updated') + .joins(:node) + .group('users.uid') + .where('users.status = 1 AND node.status = 1') + .order("last_updated DESC") + .page(params[:page]) + end + @users = @users.where('users.status = 1') unless current_user && (current_user.role == "admin" || current_user.role == "moderator") +end+
# File app/controllers/users_controller.rb, line 5 +def new + @spamaway = Spamaway.new + @user = User.new + @action = "create" # sets the form url +end+
# File app/controllers/users_controller.rb, line 229 +def photo + @user = DrupalUser.find_by(uid: params[:uid]).user + if current_user.uid == @user.uid || current_user.role == "admin" + @user.photo = params[:photo] + if @user.save! + if request.xhr? + render :json => { :url => @user.photo_path } + else + flash[:notice] = I18n.t('users_controller.image_saved') + redirect_to @node.path + end + else + flash[:error] = I18n.t('users_controller.image_not_saved') + redirect_to "/images/new" + end + else + flash[:error] = I18n.t('users_controller.image_not_saved') + redirect_to "/images/new" + end +end+
# File app/controllers/users_controller.rb, line 102 +def profile + if current_user && params[:id].nil? + redirect_to "/profile/#{current_user.username}" + else + @user = DrupalUser.find_by(name: params[:id]) + @profile_user = User.find_by(username: params[:id]) + @title = @user.name + @notes = Node.research_notes + .page(params[:page]) + .order("nid DESC") + .where(status: 1, uid: @user.uid) + @coauthored = @profile_user.coauthored_notes + .page(params[:page]) + .order('node_revisions.timestamp DESC') + @questions = @user.user.questions + .order('node.nid DESC') + .paginate(:page => params[:page], :per_page => 24) + questions = Node.questions + .where(status: 1) + .order('node.nid DESC') + @answered_questions = questions.select{|q| q.answers.collect(&:author).include?(@user)} + wikis = Revision.order("nid DESC") + .where('node.type' => 'page', 'node.status' => 1, uid: @user.uid) + .joins(:node) + .limit(20) + @wikis = wikis.collect(&:parent).uniq + + # User's social links + @github = @profile_user.social_link("github") + @twitter = @profile_user.social_link("twitter") + @facebook = @profile_user.social_link("facebook") + @instagram = @profile_user.social_link("instagram") + + if @user.status == 0 + if current_user && (current_user.role == "admin" || current_user.role == "moderator") + flash.now[:error] = I18n.t('users_controller.user_has_been_banned') + else + flash[:error] = I18n.t('users_controller.user_has_been_banned') + redirect_to "/" + end + elsif @user.status == 5 + flash.now[:warning] = I18n.t('users_controller.user_has_been_moderated') + end + end +end+
# File app/controllers/users_controller.rb, line 181 +def reset + if params[:key] && params[:key] != nil + @user = User.find_by(reset_key: params[:key]) + if @user + if params[:user] && params[:user][:password] + if @user.username.downcase == params[:user][:username].downcase + @user.password = params[:user][:password] + @user.password_confirmation = params[:user][:password] + @user.reset_key = nil + if @user.changed? && @user.save({}) + flash[:notice] = I18n.t('users_controller.password_change_success') + redirect_to "/dashboard" + else + flash[:error] = I18n.t('users_controller.password_reset_failed').html_safe + redirect_to "/" + end + else + flash[:error] = I18n.t('users_controller.password_change_failed') + end + else + # Just display page prompting username & pwd + end + else + flash[:error] = I18n.t('users_controller.password_reset_failed_no_user').html_safe + redirect_to "/" + end + + elsif params[:email] + user = User.find_by(email: params[:email]) + if user + key = user.generate_reset_key + user.save({}) + # send key to user email + PasswordResetMailer.reset_notify(user, key) unless user.nil? # respond the same to both successes and failures; security + end + flash[:notice] = I18n.t('users_controller.password_reset_email') + redirect_to "/login" + end +end+
# File app/controllers/users_controller.rb, line 159 +def rss + if params[:author] + @author = DrupalUser.where(name: params[:author], status: 1).first + if @author + @notes = Node.order("nid DESC") + .where(type: 'note', status: 1, uid: @author.uid) + .limit(20) + else + flash[:error] = I18n.t('users_controller.no_user_found') + redirect_to "/" + end + else + end + respond_to do |format| + format.rss { + render :layout => false + response.headers["Content-Type"] = "application/xml; charset=utf-8" + response.headers["Access-Control-Allow-Origin"] = "*" + } + end +end+
# File app/controllers/users_controller.rb, line 42 +def update + if current_user + @user = current_user + @user.attributes = params[:user] + @user.save({}) do |result| + if result + if session[:openid_return_to] # for openid login, redirects back to openid auth process + return_to = session[:openid_return_to] + session[:openid_return_to] = nil + redirect_to return_to + else + flash[:notice] = I18n.t('users_controller.successful_updated_profile')+"<a href='/dashboard'>"+I18n.t('users_controller.return_dashboard')+" »</a>" + redirect_to "/profile/"+@user.username + end + else + render :template => 'users/edit' + end + end + else + flash[:error] = I18n.t('users_controller.only_user_edit_profile', :user => @user.name).html_safe + redirect_to "/profile/"+@user.name + end +end+
PasswordResetMailer#reset_notify.deliver
+ + + + +# File app/mailers/welcome_mailer.rb, line 7 +def add_to_list(user, list) + subject = 'subscribe' + @list = list + @footer = feature('email-footer') + mail(to: list + '+subscribe@googlegroups.com', subject: subject, from: user.email).deliver +end+
# File app/controllers/wiki_controller.rb, line 423 +def comments + show + render :show +end+
# File app/controllers/wiki_controller.rb, line 133 +def create + if current_user.drupal_user.status == 1 + # we no longer allow custom urls, just titles which are parameterized automatically into urls + # slug = params[:title].parameterize + # slug = params[:id].parameterize if params[:id] != "" && !params[:id].nil? + # slug = params[:url].parameterize if params[:url] != "" && !params[:url].nil? + saved, @node, @revision = Node.new_wiki(uid: current_user.uid, + title: params[:title], + body: params[:body]) + if saved + flash[:notice] = I18n.t('wiki_controller.wiki_page_created') + if params[:main_image] && params[:main_image] != '' + img = Image.find params[:main_image] + img.nid = @node.id + img.save + end + redirect_to @node.path + else + render action: :edit + end + else + flash.keep[:error] = I18n.t('wiki_controller.you_have_been_banned').html_safe + redirect_to '/logout' + end +end+
# File app/controllers/wiki_controller.rb, line 202 +def delete + @node = Node.find(params[:id]) + if current_user && current_user.role == 'admin' + @node.destroy + flash[:notice] = I18n.t('wiki_controller.wiki_page_deleted') + redirect_to '/dashboard' + else + flash[:error] = I18n.t('wiki_controller.only_admins_delete_pages') + redirect_to @node.path + end +end+
# File app/controllers/wiki_controller.rb, line 280 +def diff + @a = Revision.find_by(vid: params[:a]) + @b = Revision.find_by(vid: params[:b]) + if @a.body == @b.body + render text: I18n.t('wiki_controller.lead_image_or_title_change').html_safe + else + render partial: 'wiki/diff' + end +end+
# File app/controllers/wiki_controller.rb, line 90 +def edit + @node = if params[:lang] + Node.find_wiki(params[:lang] + '/' + params[:id]) + else + Node.find_wiki(params[:id]) + end + if @node.has_tag('locked') && (current_user.role != 'admin' && current_user.role != 'moderator') + flash[:warning] = "This page is <a href='/wiki/power-tags#Locking'>locked</a>, and only <a href='/wiki/moderators'>moderators</a> can edit it." + redirect_to @node.path + end + if ((Time.now.to_i - @node.latest.timestamp) < 5.minutes.to_i) && @node.latest.author.uid != current_user.uid + flash.now[:warning] = I18n.t('wiki_controller.someone_clicked_edit_5_minutes_ago') + end + # we could do this... + # @node.locked = true + # @node.save + @title = I18n.t('wiki_controller.editing', title: @node.title).html_safe + + @tags = @node.tags +end+
# File app/controllers/wiki_controller.rb, line 290 +def index + @title = I18n.t('wiki_controller.wiki') + + order_string = if params[:order] == 'alphabetic' + 'node_revisions.title ASC' + else + 'node_revisions.timestamp DESC' + end + + @wikis = Node.includes(:revision) + .references(:node_revisions) + .group('node_revisions.nid') + .order(order_string) + .where("node_revisions.status = 1 AND node.status = 1 AND (type = 'page' OR type = 'tool' OR type = 'place')") + .page(params[:page]) + + @paginated = true +end+
# File app/controllers/wiki_controller.rb, line 334 +def liked + @title = I18n.t('wiki_controller.well_liked_wiki_pages') + @wikis = Node.limit(40) + .order('node.cached_likes DESC') + .where("status = 1 AND nid != 259 AND (type = 'page' OR type = 'tool' OR type = 'place') AND cached_likes > 0") + render template: 'wiki/index' +end+
# File app/controllers/wiki_controller.rb, line 369 +def methods + @nodes = Node.where(status: 1, type: ['page']) + .where('term_data.name = ?', 'method') + .includes(:revision, :tag) + .references(:node_revision) + .order('node_revisions.timestamp DESC') + # deprecating the following in favor of javascript implementation in /app/assets/javascripts/methods.js + if params[:topic] + nids = @nodes.collect(&:nid) || [] + @notes = Node.where(status: 1, type: ['page']) + .where('node.nid IN (?)', nids) + .where('(type = "note" OR type = "page" OR type = "map") AND node.status = 1 AND (node.title LIKE ? OR node_revisions.title LIKE ? OR node_revisions.body LIKE ? OR term_data.name = ?)', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + params[:topic]) + .includes(:revision, :tag) + .references(:node_revision, :term_data) + .order('node_revisions.timestamp DESC') + end + if params[:topic] + nids = @nodes.collect(&:nid) || [] + @nodes = Node.where(status: 1, type: ['page']) + .where('node.nid IN (?)', nids) + .where('(type = "note" OR type = "page" OR type = "map") AND node.status = 1 AND (node.title LIKE ? OR node_revisions.title LIKE ? OR node_revisions.body LIKE ? OR term_data.name = ?)', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + params[:topic]) + .includes(:revision, :tag) + .references(:node_revision, :term_data) + .order('node_revisions.timestamp DESC') + end + + @unpaginated = true + @topics = [ + 'agriculture', + 'drinking-water', + 'fracking', + 'indoor-air', + 'chemicals', + 'industry', + 'land-use', + 'land-change', + 'mining', + 'oil-and-gas', + 'transportation', + 'urban-planning', + 'sensors', + 'community-organizing' + ] + render template: 'wiki/methods' +end+
# File app/controllers/wiki_controller.rb, line 111 +def new + @node = Node.new + if params[:n] && !params[:body] # use another node body as a template + node = Node.find(params[:n]) + params[:body] = node.latest.body if node && node.latest + end + @tags = [] + if params[:id] + flash.now[:notice] = I18n.t('wiki_controller.page_does_not_exist_create') + title = params[:id].tr('-', ' ') + @related = Node.limit(10) + .order('node.nid DESC') + .where('type = "page" AND node.status = 1 AND (node.title LIKE ? OR node_revisions.body LIKE ?)', '%' + title + '%', '%' + title + '%') + .includes(:revision) + .references(:node_revisions) + tag = Tag.find_by(name: params[:id]) # add page name as a tag, too + @tags << tag if tag + @related += Tag.find_nodes_by_type(@tags.collect(&:name), 'page', 10) + end + render template: 'wiki/edit' +end+
# File app/controllers/wiki_controller.rb, line 323 +def popular + @title = I18n.t('wiki_controller.popular_wiki_pages') + @wikis = Node.limit(40) + .order('views DESC') + .joins(:revision) + .group('node_revisions.nid') + .order('node_revisions.timestamp DESC') + .where("node.status = 1 AND node_revisions.status = 1 AND node.nid != 259 AND (type = 'page' OR type = 'tool' OR type = 'place')") + render template: 'wiki/index' +end+
display a revision, raw
+ + + + +# File app/controllers/wiki_controller.rb, line 85 +def raw + response.headers['Content-Type'] = 'text/plain; charset=utf-8' + render text: Revision.find(params[:id]).body +end+
replace subsection of wiki body
+ + + + +# File app/controllers/wiki_controller.rb, line 343 +def replace + @node = Node.find(params[:id]) + if params[:before] && params[:after] + # during round trip, strings are getting "\r\n" newlines converted to "\n", + # so we're ensuring they remain "\r\n"; this may vary based on platform, unfortunately + before = params[:before].gsub("\n", "\r\n") + after = params[:after]#.gsub( "\n", "\r\n") + if output = @node.replace(before, after, current_user) + flash[:notice] = 'New revision created with your additions.' unless request.xhr? + else + flash[:error] = 'There was a problem replacing that text.' unless request.xhr? + end + else + flash[:error] = "You must specify 'before' and 'after' terms to replace content in a wiki page." + end + if request.xhr? + render json: output + else + redirect_to @node.path + end +end+
# File app/controllers/wiki_controller.rb, line 214 +def revert + revision = Revision.find params[:id] + node = revision.parent + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + new_rev = revision.dup + new_rev.timestamp = DateTime.now.to_i + if new_rev.save! + flash[:notice] = I18n.t('wiki_controller.wiki_page_reverted') + else + flash[:error] = I18n.t('wiki_controller.problem_reverting') + end + else + flash[:error] = I18n.t('wiki_controller.moderators_admin_delete_pages') + end + redirect_to node.path +end+
# File app/controllers/wiki_controller.rb, line 260 +def revision + @node = Node.find_wiki(params[:id]) + @tags = @node.tags + @tagnames = @tags.collect(&:name) + @unpaginated = true + @is_revision = true + set_sidebar :tags, @tagnames, videos: true + @revision = Revision.find_by_nid_and_vid(@node.id, params[:vid]) + if @revision.nil? + flash[:error] = I18n.t('wiki_controller.revision_not_found') + redirect_to action: 'revisions' + elsif @revision.status == 1 || current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @title = I18n.t('wiki_controller.revisions_for', title: @revision.title).html_safe + render template: 'wiki/show' + else + flash[:error] = I18n.t('wiki_controller.revision_has_been_moderated').html_safe + redirect_to @node.path + end +end+
# File app/controllers/wiki_controller.rb, line 248 +def revisions + @node = Node.find_wiki(params[:id]) + if @node + @revisions = @node.revisions + @revisions = @revisions.where(status: 1) unless current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @title = I18n.t('wiki_controller.revisions_for', title: @node.title).html_safe + @tags = @node.tags + else + flash[:error] = I18n.t('wiki_controller.invalid_wiki_page') + end +end+
wiki pages which have a root URL, like /about also just redirect anything +else matching /__ to /wiki/__
+ + + + +# File app/controllers/wiki_controller.rb, line 233 +def root + @node = Node.find_by_path(params[:id]) + return if check_and_redirect_node(@node) + if @node + @revision = @node.latest + @title = @revision.title + @tags = @node.tags + @tagnames = @tags.collect(&:name) + render template: 'wiki/show' + else + # redirects any uncaught requests to example.com/______ to /wiki/____ + redirect_to '/wiki/' + params[:id] + end +end+
# File app/controllers/wiki_controller.rb, line 21 +def show + @node = if params[:lang] + Node.find_wiki(params[:lang] + '/' + params[:id]) + else + Node.find_wiki(params[:id]) + end + + if @node && @node.has_power_tag('redirect') && Node.where(nid: @node.power_tag('redirect')).exists? + if current_user.nil? || (current_user.role != 'admin' && current_user.role != 'moderator') + redirect_to Node.find(@node.power_tag('redirect')).path + return + elsif current_user.role == 'admin' || current_user.role == 'moderator' + flash.now[:warning] = "Only moderators and admins see this page, as it is redirected to <a href='#{Node.find(@node.power_tag('redirect')).path}'>#{Node.find(@node.power_tag('redirect')).title}</a>. + To remove the redirect, delete the tag beginning with 'redirect:'" + end + end + + if @node && @node.has_power_tag('abtest') && !Node.where(nid: @node.power_tag('abtest')).empty? + if current_user.nil? || (current_user.role != 'admin' && current_user.role != 'moderator') + if Random.rand(2) == 0 + redirect_to Node.find(@node.power_tag('abtest')).path + return + end + elsif current_user.role == 'admin' || current_user.role == 'moderator' + flash.now[:warning] = "Only moderators and admins see this page, as it is redirected to #{Node.find(@node.power_tag('abtest')).title} roughly around 50% of the time. + To remove this behavior, delete the tag beginning with 'abtest:'" + end + end + + # if request.path != @node.path && request.path != '/wiki/' + @node.nid.to_s + # return redirect_to @node.path, :status => :moved_permanently + # end + + return if check_and_redirect_node(@node) + if !@node.nil? # it's a place page! + @tags = @node.tags + @tags += [Tag.find_by(name: params[:id])] if Tag.find_by(name: params[:id]) + else # it's a new wiki page! + @title = I18n.t('wiki_controller.new_wiki_page') + if current_user + new + else + flash[:warning] = I18n.t('wiki_controller.pages_does_not_exist') + redirect_to '/login' + end + end + + unless @title # the page exists + if @node.status == 0 + flash[:warning] = I18n.t('wiki_controller.page_moderated_as_spam') + redirect_to '/wiki' + end + @tagnames = @tags.collect(&:name) + set_sidebar :tags, @tagnames, videos: true + @wikis = Tag.find_pages(@node.slug_from_path, 30) if @node.has_tag('chapter') || @node.has_tag('tabbed:wikis') + + impressionist(@node, 'show', unique: [:ip_address]) + @revision = @node.latest # needed for template, so it can be used for past revisions too in the "revision" action + @title = @revision.title + end + @unpaginated = true +end+
# File app/controllers/wiki_controller.rb, line 309 +def stale + @title = I18n.t('wiki_controller.wiki') + + @wikis = Node.includes(:revision) + .references(:node_revisions) + .group('node_revisions.nid') + .order('node_revisions.timestamp ASC') + .where("node_revisions.status = 1 AND node.status = 1 AND (type = 'page' OR type = 'tool' OR type = 'place')") + .page(params[:page]) + + @paginated = true + render template: 'wiki/index' +end+
# File app/controllers/wiki_controller.rb, line 6 +def subdomain + url = "//#{request.host}/wiki/" + case request.subdomain + when 'new-york-city', + 'gulf-coast', + 'boston', + 'espana' then + redirect_to url + request.subdomain + when 'nyc' + redirect_to url + 'new-york-city' + else + redirect_to url + end +end+
# File app/controllers/wiki_controller.rb, line 365 +def techniques + redirect_to '/methods', status: 302 +end+
# File app/controllers/wiki_controller.rb, line 159 +def update + @node = Node.find(params[:id]) + @revision = @node.new_revision(uid: current_user.uid, + title: params[:title], + body: params[:body]) + if @node.has_tag('locked') && (current_user.role != 'admin' && current_user.role != 'moderator') + flash[:warning] = "This page is <a href='/wiki/power-tags#Locking'>locked</a>, and only <a href='/wiki/moderators'>moderators</a> can update it." + redirect_to @node.path + + elsif @revision.valid? + ActiveRecord::Base.transaction do + @revision.save + @node.vid = @revision.vid + # update vid (version id) of main image + if @node.drupal_main_image && params[:main_image].nil? + i = @node.drupal_main_image + i.vid = @revision.vid + i.save + end + @node.title = @revision.title + # save main image + if params[:main_image] && params[:main_image] != '' + begin + img = Image.find params[:main_image] + unless img.nil? + img.nid = @node.id + @node.main_image_id = img.id + img.save + end + rescue + end + end + @node.save + end + flash[:notice] = I18n.t('wiki_controller.edits_saved') + redirect_to @node.path + else + flash[:error] = I18n.t('wiki_controller.edit_could_not_be_saved') + render action: :edit + # redirect_to "/wiki/edit/"+@node.slug + end +end+
This is the API documentation for Rails Application Documentation. +