| #--###################################################################### |
| # Copyright (c) 2006 Logica |
| # |
| # All rights reserved. This program and the accompanying materials |
| # are made available under the terms of the Eclipse Public License v1.0 |
| # which accompanies this distribution, and is available at |
| # http://www.eclipse.org/legal/epl-v10.html |
| # |
| # Contributors: |
| # |
| # Onno van der Straaten:: initial implementation |
| #++###################################################################### |
| # {Copyright (c) 2006 Logica}[link:files/COPYRIGHT.html] |
| |
| class Wiki < Site |
| |
| has_many :versions, :foreign_key => 'wiki_id' |
| has_many :user_versions, :foreign_key => 'wiki_id' |
| has_many :baseline_process_versions, :foreign_key => 'wiki_id' |
| has_many :comments, :foreign_key => 'site_id' |
| has_many :checkouts, :foreign_key => 'site_id' |
| |
| # Current BaselineProcess: the BaselineProcess used to create the Wiki or |
| # the BaselineProcess used last time the Wiki was updated |
| belongs_to :baseline_process, :foreign_key => "baseline_process_id" |
| |
| # Baseline processes are used to create and update the Wiki |
| has_many :updates, :order => 'finished_on DESC, created_on DESC' |
| has_many :updates_todo, :class_name => 'Update', :order => 'created_on ASC', :foreign_key => 'wiki_id', :conditions => 'started_on is null' |
| has_many :updates_done, :class_name => 'Update', :order => 'finished_on ASC', :foreign_key => 'wiki_id', :conditions => 'finished_on is not null' |
| has_many :updates_inprogress, :class_name => 'Update', :order => 'started_on ASC', :foreign_key => 'wiki_id', :conditions => 'started_on is not null and finished_on is null' |
| |
| validates_format_of :folder, :with => /^([0-9a-zA-Z]|_)*$/, :message => 'Folder name can only have characters,digits and underscores' |
| |
| # Used only for Notifier.report TODO a better solution |
| attr_accessor :contributors |
| |
| |
| # HTML Editors remove the nowrap attribute, so we add it to the CSS-file, see Wiki.enhance_files |
| DEFAULT_CSS = 'css/default.css' |
| |
| def status |
| logger.debug("Determining status of wiki #{self.title}") |
| s = 'Ready' |
| if self.updates.count == 0 |
| logger.debug("Wiki #{self.title} does not have any update records") |
| s = 'Pending' |
| elsif self.updates_todo.count > 0 |
| logger.debug("Wiki #{self.title} has #{self.updates_todo.count.to_s} updates to do") |
| s = 'Scheduled' |
| self.updates_todo.each do |u| |
| logger.debug("Update todo #{u.baseline_process.title} is started on #{u.started_on}") |
| s = 'UpdateInProgress' if !u.started_on.nil? |
| end |
| end |
| return s |
| end |
| |
| def top_contributors |
| arr = User.find(:all).collect {|u|[u, Version.count(:conditions => ['user_id = ? and baseline_process_id is null and wiki_id=?',u.id, self.id]) + |
| Comment.count(:conditions => ['user_id = ? and site_id=?',u.id, self.id]) + |
| Upload.count(:conditions => ['user_id = ?',u.id])]} |
| arr = arr.sort_by{|t|-t[1]} |
| end |
| |
| def top_monthly_contributors |
| arr = User.find(:all).collect {|u|[u, Version.count(:conditions => ['user_id = ? and baseline_process_id is null and wiki_id=?',u.id, self.id]) + |
| Comment.count(:conditions => ['user_id = ? and site_id=?',u.id, self.id]) + |
| Upload.count(:conditions => ['user_id = ?',u.id])]} |
| arr = arr.sort_by{|t|-t[1]} |
| end |
| |
| # Method #wikify does the actual wikifying of the content. It is the second step of the two step |
| # process (the first step created the Wiki record and Update record). |
| # * copies content of the source Site (parent) into that folder |
| # * scans the content of the baseline process if this was not done yet, see #scan4content |
| # * enhances the files in that site using method #enhance_files |
| # * creates a relation between the Baseline and the Site |
| # NOTE: This method is typically not called directly but called via #Update.wikify |
| def wikify(update) |
| bp = update.baseline_process |
| logger.info("Updating Wiki #{self.title} with baseline process #{bp.title}") |
| raise 'The site was updated already or has a an update in progress!' if !update.first_update? |
| raise "Can only update with a baseline process (static site)" if bp.wiki? |
| update.update_attributes(:started_on => Time.now) # changes the wiki status to 'UpdateInProgress' |
| File.makedirs(self.path) |
| logger.info("Copying files from #{bp.path} to #{self.path}") |
| FileUtils.cp_r(bp.path + "/.", self.path) # How to copy the contents of a folder and not the folder [http://www.ruby-doc.org/core/classes/FileUtils.html#M001703] |
| bp.scan4content if !bp.content_scanned_on |
| cadmin = User.find_central_admin |
| bp.pages.each do |p| |
| newp = WikiPage.new(:rel_path => p.rel_path, :site => self, :tool => 'EPFC', :status => 'New', :site_id => self.id) |
| # create baseversion |
| baseversion = BaselineProcessVersion.new(:baseline_update => update, :user => cadmin, :page => newp, |
| :wiki => self, :version => 0, :done => 'Y', :note => 'Automatically created', |
| :baseline_process_id => bp.id) |
| newp.baseline_process_versions << baseversion |
| newp.save! |
| end |
| enhance_files |
| self.baseline_process = bp |
| self.wikified_on = Time.now |
| self.save! |
| end |
| |
| # Method #update_wiki updates a Wiki with a BaselineProcess with the following steps: |
| # 1. Copy content (overwriting all pages) |
| # 2. Update status of EPFC pages to 'Undetermined' |
| # 3. Update status of the Wiki pages, find 'Updated' and 'New' pages |
| # 4. Make EPFC pages obsolete if they are not part of the new BaselineProcess |
| # 5. Make Wiki pages obsolete if they have been harvested |
| # 6. Enhance files |
| # TODO: notify users that want to be notified (add about notify_baseline_updates column to users) |
| # TODO: Change 68 - Update should continue with checkouts and should not overwrite not harvested changes |
| def update_wiki(update) |
| update.update_attributes(:started_on => Time.now) |
| bp = update.baseline_process |
| logger.info("Starting update of wiki #{self.title} from baseline process #{self.baseline_process.title} (#{self.baseline_process.id}) to #{bp.title} (#{bp.id})") |
| logger.info("Copy update site " + bp.path + " to " + self.path) |
| cadmin = User.find_central_admin |
| # 1. |
| bp.copy_to(self, nil) |
| |
| # 2. Update status of EPFC pages to 'undetermined' |
| Page.update_all( "status = 'Undetermined'", ["tool = ? and site_id = ? ", 'EPFC', self.id, ]) |
| |
| # 3. Update |
| bp.scan4content if bp.content_scanned_on.nil? |
| bp.pages.each do |p| |
| page = Page.find_by_site_id_and_rel_path(self.id, p.rel_path) |
| if page |
| page.status = 'Updated' |
| no = page.max_version_no + 1 |
| else |
| page = WikiPage.new(:rel_path => p.rel_path, :site => self, :tool => 'EPFC', :status => 'New', :site_id => self.id) |
| no = 0 |
| end |
| # create baseversion |
| baseversion = BaselineProcessVersion.new(:baseline_update => update,:user => cadmin, :page => page, |
| :wiki => self, :version => no, :done => 'Y', :note => 'Automatically created', |
| :baseline_process_id => bp.id) |
| page.baseline_process_versions << baseversion |
| page.save! |
| end |
| |
| # 4. |
| Page.find(:all, :conditions => ['site_id = ? and status = ?', self.id, 'Undetermined']).each do |p| |
| p.status = 'RemovedOrMoved' |
| p.save! |
| end |
| |
| # 5. |
| Page.find(:all, :conditions => ['site_id = ? and tool = ?', self.id, 'Wiki']).each do |p| |
| if p.harvested? |
| p.status = 'Harvested' |
| end |
| end |
| |
| # 6. |
| enhance_files |
| |
| # Change 68 - current versions not harvested |
| versions = UserVersion.find(:all, :conditions => ['wiki_id =? and done <> ? and version is not null', self.id, 'Y']) |
| logger.info("Found #{versions.size.to_s} versions with unharvested changes") |
| pages = versions.collect{|version| version.page}.uniq |
| if pages |
| logger.info("Found #{pages.size.to_s} pages with unharvested changes") if pages |
| snippets = Page.get_snippets |
| pages.each do |page| |
| logger.info("Processing page #{page.presentation_name}") |
| if page.checkout |
| logger.info("Page has unharvested versions, we don't need to set a current version") |
| else |
| cv = page.current_version |
| unless cv.nil? |
| if cv.current |
| logger.info("Page #{page.presentation_name} already has current version with id #{cv.id}, we don't need to set a current version") |
| else |
| logger.info("Page #{page.presentation_name} does not have a current version") |
| # set the current version equal to the last version that is not part of the update we are doing |
| page.current_version = Version.find(:first, :order => 'version DESC', :conditions => ['page_id=? and version is not null and update_id is null',page.id]) |
| end |
| page.html = page.current_version.html |
| Page.enhance_file(page.path, snippets) |
| end |
| end |
| end |
| end |
| self.baseline_updated_on = Time.now |
| self.baseline_process = bp |
| self.save! |
| end |
| |
| def url(absolute = false, request_host = ENV['EPFWIKI_HOST']) |
| #-- |
| # TODO anomaly, the following line shouldn't be necessary but is, in some cases. |
| # When this method is called from page.url request_host will be nil |
| #++ |
| request_host = ENV['EPFWIKI_HOST'] if request_host.nil? |
| |
| logger.debug("url for site #{self.title}, absolute #{absolute.inspect}, request_host #{request_host.inspect}") |
| s = "/#{ENV['EPFWIKI_WIKIS_FOLDER']}/#{self.folder}/index.htm" |
| s = "http://#{request_host}#{s}" if absolute |
| logger.debug("returning: #{s}") |
| s |
| end |
| |
| def validate_on_create |
| logger.info("Folder #{ENV['EPFWIKI_WIKIS_PATH']}/#{self.folder} should not exists already") |
| errors.add(:folder, 'already exists') if (!self.folder.blank? && File.exists?("#{ENV['EPFWIKI_WIKIS_PATH']}/#{self.folder}")) || !Wiki.find_by_folder(self.folder).nil? |
| if self.title == 'Templates' |
| if Wiki.find(:first, :conditions => ['title = ?','Templates']) |
| errors.add(:title, ' "Templates" has been used. There can only be one Wiki with that name.') |
| end |
| end |
| end |
| |
| def users |
| return User.find(:all, :conditions => ['exists (select * from versions vsn where vsn.wiki_id = ? and vsn.user_id = users.id and baseline_process_id is null) or exists (select * from da_texts cmt where cmt.user_id = users.id and cmt.site_id = ?) or exists (select * from uploads where uploads.user_id = ?)', id, id, id]) |
| end |
| |
| ####### |
| private |
| ####### |
| |
| def enhance_files |
| |
| # CSS enhancement making it more robust layout of html, which shouldn't influence how the page is displayed |
| css = IO.readlines(self.path + '/' + DEFAULT_CSS).join + "\n" |
| [/.pageTitle .*?\{.*?\}/m, /.expandCollapseLink \{.*?\}/m].each do |regex| |
| match = regex.match(css) |
| if match |
| new_css_snip = match[0].gsub('}', "\nwhite-space: nowrap;}") |
| css = css.gsub(match[0], new_css_snip) |
| else |
| logger.info("CSS snippet not found, #{DEFAULT_CSS} changed in newer release of EPF?") |
| end |
| end |
| f = File.new(self.path + '/' + DEFAULT_CSS, 'w') |
| f.puts(css) |
| f.close |
| snippets = Page.get_snippets # TODO rename to snippets |
| self.files_wikifiable.each do |path| |
| Page.enhance_file(path, snippets) |
| end |
| end |
| |
| end |