discourse/app/serializers/post_serializer.rb

Failed to ignore revisions in .git-blame-ignore-revs.

547 lines
12 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
class PostSerializer < BasicPostSerializer
2013-02-05 14:16:51 -05:00
# To pass in additional information we might need
INSTANCE_VARS ||= [
:parent_post,
:add_raw,
:add_title,
:single_post_link_counts,
:draft_sequence,
:post_actions,
:all_post_actions,
:add_excerpt
]
INSTANCE_VARS.each do |v|
2019-05-06 21:27:05 -04:00
self.public_send(:attr_accessor, v)
end
2013-02-05 14:16:51 -05:00
attributes :post_number,
2013-02-05 14:16:51 -05:00
:post_type,
:updated_at,
2013-02-07 10:45:24 -05:00
:reply_count,
:reply_to_post_number,
2013-02-05 14:16:51 -05:00
:quote_count,
:incoming_link_count,
:reads,
:readers_count,
2013-02-05 14:16:51 -05:00
:score,
:yours,
:topic_id,
:topic_slug,
:topic_title,
:topic_html_title,
:category_id,
2013-02-05 14:16:51 -05:00
:display_username,
:primary_group_name,
:primary_group_flair_url,
:primary_group_flair_bg_color,
:primary_group_flair_color,
2013-02-05 14:16:51 -05:00
:version,
:can_edit,
:can_delete,
:can_recover,
:can_wiki,
2013-02-05 14:16:51 -05:00
:link_counts,
:read,
:user_title,
2013-02-05 14:16:51 -05:00
:reply_to_user,
:bookmarked,
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
:bookmark_reminder_at,
:bookmark_id,
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
:bookmark_reminder_type,
:bookmark_name,
:bookmark_delete_when_reminder_sent,
2013-02-05 14:16:51 -05:00
:raw,
:actions_summary,
2013-02-06 17:36:07 -05:00
:moderator?,
2014-05-11 21:28:24 -04:00
:admin?,
:staff?,
2013-02-07 10:45:24 -05:00
:user_id,
2013-02-05 14:16:51 -05:00
:draft_sequence,
:hidden,
2013-02-07 10:45:24 -05:00
:hidden_reason_id,
2013-07-10 14:56:00 -04:00
:trust_level,
:deleted_at,
:deleted_by,
2013-11-15 17:28:16 -05:00
:user_deleted,
:edit_reason,
2014-05-13 08:53:11 -04:00
:can_view_edit_history,
:wiki,
:user_custom_fields,
:static_doc,
:via_email,
:is_auto_generated,
:action_code,
:action_code_who,
:notice_type,
:notice_args,
:last_wiki_edit,
:locked,
:excerpt,
:reviewable_id,
:reviewable_score_count,
:reviewable_score_pending_count
2013-02-05 14:16:51 -05:00
def initialize(object, opts)
super(object, opts)
2019-05-06 21:27:05 -04:00
PostSerializer::INSTANCE_VARS.each do |name|
if opts.include? name
2019-05-06 21:27:05 -04:00
self.public_send("#{name}=", opts[name])
end
end
end
def topic_slug
topic&.slug
end
def include_topic_title?
@add_title
end
def include_topic_html_title?
@add_title
end
def include_category_id?
@add_title
end
def include_excerpt?
@add_excerpt
end
def topic_title
topic&.title
end
def topic_html_title
topic&.fancy_title
end
def category_id
topic&.category_id
end
2013-02-06 17:36:07 -05:00
def moderator?
!!(object&.user&.moderator?)
2014-05-11 21:28:24 -04:00
end
def admin?
!!(object&.user&.admin?)
2013-02-06 17:36:07 -05:00
end
def staff?
!!(object&.user&.staff?)
end
2013-02-05 14:16:51 -05:00
def yours
scope.user == object.user
end
def can_edit
scope.can_edit?(object)
end
def can_delete
scope.can_delete?(object)
end
def can_recover
scope.can_recover_post?(object)
end
def can_wiki
scope.can_wiki?(object)
end
def display_username
object.user&.name
end
def primary_group_name
return nil unless object.user && object.user.primary_group_id
if @topic_view
@topic_view.primary_group_names[object.user.primary_group_id]
else
object.user.primary_group.name if object.user.primary_group
end
end
def primary_group_flair_url
object.user&.primary_group&.flair_url
end
def primary_group_flair_bg_color
object.user&.primary_group&.flair_bg_color
end
def primary_group_flair_color
object.user&.primary_group&.flair_color
end
2013-02-05 14:16:51 -05:00
def link_counts
return @single_post_link_counts if @single_post_link_counts.present?
# TODO: This could be better, just porting the old one over
@topic_view.link_counts[object.id].map do |link|
result = {}
result[:url] = link[:url]
result[:internal] = link[:internal]
result[:reflection] = link[:reflection]
result[:title] = link[:title] if link[:title].present?
result[:clicks] = link[:clicks] || 0
result
end
end
def read
@topic_view.read?(object.post_number)
end
def score
object.score || 0
end
def user_title
object&.user&.title
end
def trust_level
object&.user&.trust_level
end
2013-02-05 14:16:51 -05:00
def reply_to_user
{
username: object.reply_to_user.username,
avatar_template: object.reply_to_user.avatar_template
2013-02-05 14:16:51 -05:00
}
end
2013-07-10 16:19:42 -04:00
def deleted_by
BasicUserSerializer.new(object.deleted_by, root: false).as_json
end
def include_deleted_by?
scope.is_staff? && object.deleted_by.present?
2013-07-10 16:19:42 -04:00
end
# Helper function to decide between #post_actions and @all_post_actions
def actions
return post_actions if post_actions.present?
return all_post_actions[object.id] if all_post_actions.present?
nil
end
2013-02-05 14:16:51 -05:00
# Summary of the actions taken on this post
def actions_summary
result = []
can_see_post = scope.can_see_post?(object)
PostActionType.types.except(:bookmark).each do |sym, id|
2013-02-05 14:16:51 -05:00
count_col = "#{sym}_count".to_sym
2019-05-06 21:27:05 -04:00
count = object.public_send(count_col) if object.respond_to?(count_col)
summary = { id: id, count: count }
summary[:hidden] = true if sym == :vote
if scope.post_can_act?(object, sym, opts: { taken_actions: actions }, can_see_post: can_see_post)
summary[:can_act] = true
end
2013-02-05 14:16:51 -05:00
if sym == :notify_user &&
(
(scope.current_user.present? && scope.current_user == object.user) ||
(object.user && object.user.bot?)
)
summary.delete(:can_act)
end
if actions.present? && actions.has_key?(id)
summary[:acted] = true
summary[:can_undo] = true if scope.can_delete?(actions[id])
2013-02-05 14:16:51 -05:00
end
# only show public data
unless scope.is_staff? || PostActionType.public_types.values.include?(id)
summary[:count] = summary[:acted] ? 1 : 0
2013-02-05 14:16:51 -05:00
end
summary.delete(:count) if summary[:count] == 0
# Only include it if the user can do it or it has a count
if summary[:can_act] || summary[:count]
result << summary
end
2013-02-05 14:16:51 -05:00
end
result
end
2013-02-07 10:45:24 -05:00
def include_draft_sequence?
@draft_sequence.present?
2013-02-05 14:16:51 -05:00
end
def include_slug_title?
@topic_slug.present?
end
def include_raw?
@add_raw.present? && (!object.hidden || scope.user&.staff? || yours)
2013-02-05 14:16:51 -05:00
end
def include_link_counts?
return true if @single_post_link_counts.present?
@topic_view.present? && @topic_view.link_counts.present? && @topic_view.link_counts[object.id].present?
2013-02-05 14:16:51 -05:00
end
def include_read?
@topic_view.present?
end
def include_reply_to_user?
!(SiteSetting.suppress_reply_when_quoting && object.reply_quoted?) && object.reply_to_user
2013-02-05 14:16:51 -05:00
end
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
# this atrtribute is not even included unless include_bookmarked? is true,
# which is why it is always true if included
def bookmarked
true
end
2013-02-05 14:16:51 -05:00
def include_bookmarked?
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
post_bookmark.present?
end
def include_bookmark_reminder_at?
include_bookmarked?
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
end
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
def include_bookmark_reminder_type?
include_bookmarked?
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
end
def include_bookmark_name?
include_bookmarked?
end
def include_bookmark_delete_when_reminder_sent?
include_bookmarked?
end
def include_bookmark_id?
include_bookmarked?
end
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
def post_bookmark
return nil if @topic_view.blank?
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
@post_bookmark ||= @topic_view.user_post_bookmarks.find { |bookmark| bookmark.post_id == object.id }
end
def bookmark_reminder_at
post_bookmark&.reminder_at
2013-02-05 14:16:51 -05:00
end
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
def bookmark_reminder_type
return if post_bookmark.blank?
Bookmark.reminder_types[post_bookmark.reminder_type].to_s
end
def bookmark_name
post_bookmark&.name
end
def bookmark_delete_when_reminder_sent
post_bookmark&.delete_when_reminder_sent
end
def bookmark_id
post_bookmark&.id
end
def include_display_username?
SiteSetting.enable_names?
end
def can_view_edit_history
scope.can_view_edit_history?(object)
end
def user_custom_fields
user_custom_fields_object[object.user_id]
end
def include_user_custom_fields?
user_custom_fields_object[object.user_id]
end
def static_doc
true
end
def include_static_doc?
object.is_first_post? && Discourse.static_doc_topic_ids.include?(object.topic_id)
end
def include_via_email?
object.via_email?
end
def is_auto_generated
object.incoming_email&.is_auto_generated
end
def include_is_auto_generated?
object.via_email? && is_auto_generated
end
def version
return 1 if object.hidden && !scope.can_view_hidden_post_revisions?
scope.is_staff? ? object.version : object.public_version
end
def include_action_code?
object.action_code.present?
end
def action_code_who
post_custom_fields["action_code_who"]
end
def include_action_code_who?
include_action_code? && action_code_who.present?
end
def notice_type
post_custom_fields[Post::NOTICE_TYPE]
end
def include_notice_type?
case notice_type
when Post.notices[:custom]
return true
when Post.notices[:new_user]
min_trust_level = SiteSetting.new_user_notice_tl
when Post.notices[:returning_user]
min_trust_level = SiteSetting.returning_user_notice_tl
else
return false
end
scope.user && scope.user.id && object.user &&
scope.user.id != object.user_id &&
scope.user.has_trust_level?(min_trust_level)
end
def notice_args
post_custom_fields[Post::NOTICE_ARGS]
end
def include_notice_args?
notice_args.present? && include_notice_type?
end
def locked
true
end
# Only show locked posts to the users who made the post and staff
def include_locked?
object.locked? && (yours || scope.is_staff?)
end
def last_wiki_edit
object.revisions.last.updated_at
end
def include_last_wiki_edit?
object.wiki &&
object.post_number == 1 &&
object.revisions.size > 0
end
2018-05-20 22:52:57 -04:00
def include_hidden_reason_id?
object.hidden
end
# If we have a topic view, it has bulk values for the reviewable content we can use
def reviewable_id
if @topic_view.present?
for_post = @topic_view.reviewable_counts[object.id]
return for_post ? for_post[:reviewable_id] : 0
end
reviewable&.id
end
def include_reviewable_id?
can_review_topic?
end
def reviewable_score_count
if @topic_view.present?
for_post = @topic_view.reviewable_counts[object.id]
return for_post ? for_post[:total] : 0
end
reviewable_scores.size
end
def include_reviewable_score_count?
can_review_topic?
end
def reviewable_score_pending_count
if @topic_view.present?
for_post = @topic_view.reviewable_counts[object.id]
return for_post ? for_post[:pending] : 0
end
reviewable_scores.count { |rs| rs.pending? }
end
def include_reviewable_score_pending_count?
can_review_topic?
end
private
def can_review_topic?
return @can_review_topic unless @can_review_topic.nil?
@can_review_topic = @topic_view&.can_review_topic
@can_review_topic ||= scope.can_review_topic?(object.topic)
@can_review_topic
end
def reviewable
@reviewable ||= Reviewable.where(target: object).includes(:reviewable_scores).first
end
def reviewable_scores
reviewable&.reviewable_scores&.to_a || []
end
2013-02-05 14:16:51 -05:00
def user_custom_fields_object
(@topic_view&.user_custom_fields || @options[:user_custom_fields] || {})
end
2018-06-07 01:28:18 -04:00
def topic
@topic = object.topic
@topic ||= Topic.with_deleted.find_by(id: object.topic_id) if scope.is_staff?
2018-06-07 01:28:18 -04:00
@topic
end
2018-06-07 01:28:18 -04:00
def post_actions
@post_actions ||= (@topic_view&.all_post_actions || {})[object.id]
end
2013-02-05 14:16:51 -05:00
end