diff --git a/lib/tasks/locomotive.rake b/lib/tasks/locomotive.rake index bb2effa6..3692a371 100644 --- a/lib/tasks/locomotive.rake +++ b/lib/tasks/locomotive.rake @@ -5,12 +5,12 @@ namespace :locomotive do - desc 'Fetch the Locomotive default site template for the installation' - task :fetch_default_site_template => :environment do - puts "Downloading default site template from '#{Locomotive::Import.DEFAULT_SITE_TEMPLATE}'" - `curl -L -s -o #{Rails.root}/tmp/default_site_template.zip #{Locomotive::Import.DEFAULT_SITE_TEMPLATE}` - puts '...done' - end + # desc 'Fetch the Locomotive default site template for the installation' + # task :fetch_default_site_template => :environment do + # puts "Downloading default site template from '#{Locomotive::Import.DEFAULT_SITE_TEMPLATE}'" + # `curl -L -s -o #{Rails.root}/tmp/default_site_template.zip #{Locomotive::Import.DEFAULT_SITE_TEMPLATE}` + # puts '...done' + # end desc 'Rebuild the serialized template of all the site pages' task :rebuild_serialized_page_templates => :environment do @@ -46,61 +46,197 @@ namespace :locomotive do namespace :upgrade do - desc "Index page is sometimes after the 404 error page. Fix this" - task :place_index_before_404 => :environment do - Locomotive::Site.all.each do |site| - site.pages.root.first.update_attribute :position, 0 - site.pages.not_found.first.update_attribute :position, 1 - end - end + desc 'Migrate from 1.0 to 2.0' + task :migration_2_0 => :environment do + db = Mongoid.config.master - desc "Namespace collections" - task :namespace_collections do - db = Mongoid.config.master['sites'].db - db.collections.each do |collection| - next if collection.name =~ /^locomotive_/ # already namespaced + # # assets -> locomotive_content_assets + # if collection = db.collections.detect { |c| c.name == 'assets' } + # new_collection = db.collections.detect { |c| c.name == 'locomotive_content_assets' } + # new_collection.drop if new_collection + # collection.rename 'locomotive_content_assets' + # end - new_name = "locomotive_#{collection.name}" - new_name = "locomotive_content_assets" if collection.name =~ /^assets/ + # content_types -> locomotive_content_types + if collection = db.collections.detect { |c| c.name == 'content_types' } + # new_collection = db.collections.detect { |c| c.name == 'locomotive_content_types' } + # new_collection.drop if new_collection + # collection.rename 'locomotive_content_types' - puts "renaming #{collection.name} into #{new_name}" - collection.rename_collection new_name - end - end + # contents_collection = db.collections.detect { |c| c.name == 'locomotive_content_entries' } + # contents_collection = db.create_collection('locomotive_content_entries') if contents_collection.nil? - desc "Upgrade a site to i18n. Requires SITE (name or id) and LOCALE (by default: en) as env variables" - task :i18n => :environment do - locale, site_name_or_id = ENV['LOCALE'] || 'en', ENV['SITE'] + collection.update({}, { + '$rename' => { + 'api_enabled' => 'public_submission_enabled', + 'api_accounts' => 'public_submission_accounts', + 'content_custom_fields' => 'entries_custom_fields', + 'content_custom_fields_version' => 'entries_custom_fields_version', + }, + '$unset' => { + 'content_custom_fields_counter' => '1' + } + }) - site = Locomotive::Site.find(site_name_or_id) || Locomotive::Site.where(:name => site_name_or_id).first + collection.update({}, { + '$rename' => { + 'entries_custom_fields._alias' => 'entries_custom_fields.name', + 'entries_custom_fields.kind' => 'entries_custom_fields.type', + 'entries_custom_fields.category_items' => 'entries_custom_fields.select_options' + } + }) - raise 'Site not found' if site.nil? + collection.find.each do |content_type| + label_field_name = '' + operations = { '$set' => {}, '$unset' => {} } + contents = content_type['contents'] + custom_fields = content_type['entries_custom_fields'] - site.locales ||= [locale] + custom_fields.each_with_index do |field, index| + class_name = "Locomotive::Entry#{field['target'][-24,24]}" if field['target'] - # sites - %w(seo_title meta_keywords meta_description).each do |attribute| - if !site.send(:"#{attribute}_translations").respond_to?(:keys) - site.changed_attributes.store attribute, site.attributes[attribute] - site.attributes.store attribute, { locale => site.attributes[attribute] } - end - end - site.save! + case field['type'] + when 'category' + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'select') + when 'has_one' + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'belongs_to') + when 'has_many' + if field['reverse_lookup'] + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'has_many') - Locomotive::Page.skip_callback(:validate, :before) - Locomotive::Page.skip_callback(:save, :after) + # reverse_lookup -> inverse_of + if _content_type = collection.find('_id' => BSON::ObjectId(field['target'][-24,24]).first) + if _field = _content_type['entries_custom_fields'].detect { |f| f['_name'] == field['reverse_lookup'] } + operations['$set'].merge!("entries_custom_fields.#{index}.inverse_of" => _field['_alias']) + end + end + else + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'many_to_many') + end + end - Locomotive::Page.all.each do |page| - %w(title slug fullpath raw_template seo_title meta_keywords meta_description serialized_template template_dependencies snippet_dependencies).each do |attribute| - if !page.send(:"#{attribute}_translations").respond_to?(:keys) - page.changed_attributes.store attribute, page.attributes[attribute] - page.attributes.store attribute, { locale => page.attributes[attribute] } + if %w(has_one has_many).include?(field['type']) + operations['$set'].merge!("entries_custom_fields.#{index}.class_name" => class_name) + operations['$unset'].merge!({ + "entries_custom_fields.#{index}.target" => '1', + "entries_custom_fields.#{index}.reverse_lookup" => '1' + }) + end end + + # contents + contents.each_with_index do |content| + attributes = content.clone.keep_if { |k, v| %w(_id _slug seo_title meta_description meta_keywords _visible created_at updated_at).include?(k) } + attributes.merge!({ + 'content_type_id' => content_type['_id'], + 'site_id' => content_type['site_id'], + '_position' => content['_position_in_list'], + '_type' => "Locomotive::Entry#{content_type['_id']}", + '_label_field_name' => label_field_name + }) + + custom_fields.each do |field| + name, _name = field['name'], field['_name'] + + case field['type'] # string, text, boolean, date, file, category, has_many, has_one + when 'string', 'text', 'boolean', 'date', 'file' + attributes[name] = content[_name] + when 'category', 'has_one' + attributes["#{name}_id"] = content[_name] + when 'has_many' + if field['reverse_lookup'] + # nothing to do + else + attributes["#{name}_ids"] = content[_name] + end + end + end + + # insert document + # contents_collection.insert attributes + puts attributes.inspect + end + + # TODO: change highlighted_field_name + for each field, change category -> select + + # save content _type + # collection.update { '_id' => content_type['_id'] }, operations + + puts operations.inspect + + print "================================= END =========================" end - page.save(:validate => false) + + # collection.update({}, { + # '$unset' => { + # 'entries_custom_fields._name' => '1', + # 'contents' => '1', + # 'highlighted_field_name' => '1', + # 'group_by_field_name' => '1' + # } + # }) end + + end + + + # desc "Index page is sometimes after the 404 error page. Fix this" + # task :place_index_before_404 => :environment do + # Locomotive::Site.all.each do |site| + # site.pages.root.first.update_attribute :position, 0 + # site.pages.not_found.first.update_attribute :position, 1 + # end + # end + # + # desc "Namespace collections" + # task :namespace_collections do + # db = Mongoid.config.master['sites'].db + # db.collections.each do |collection| + # next if collection.name =~ /^locomotive_/ # already namespaced + # + # new_name = "locomotive_#{collection.name}" + # new_name = "locomotive_content_assets" if collection.name =~ /^assets/ + # + # puts "renaming #{collection.name} into #{new_name}" + # collection.rename_collection new_name + # end + # end + # + # desc "Upgrade a site to i18n. Requires SITE (name or id) and LOCALE (by default: en) as env variables" + # task :i18n => :environment do + # locale, site_name_or_id = ENV['LOCALE'] || 'en', ENV['SITE'] + # + # site = Locomotive::Site.find(site_name_or_id) || Locomotive::Site.where(:name => site_name_or_id).first + # + # raise 'Site not found' if site.nil? + # + # site.locales ||= [locale] + # + # # sites + # %w(seo_title meta_keywords meta_description).each do |attribute| + # if !site.send(:"#{attribute}_translations").respond_to?(:keys) + # site.changed_attributes.store attribute, site.attributes[attribute] + # site.attributes.store attribute, { locale => site.attributes[attribute] } + # end + # end + # site.save! + # + # Locomotive::Page.skip_callback(:validate, :before) + # Locomotive::Page.skip_callback(:save, :after) + # + # Locomotive::Page.all.each do |page| + # %w(title slug fullpath raw_template seo_title meta_keywords meta_description serialized_template template_dependencies snippet_dependencies).each do |attribute| + # if !page.send(:"#{attribute}_translations").respond_to?(:keys) + # page.changed_attributes.store attribute, page.attributes[attribute] + # page.attributes.store attribute, { locale => page.attributes[attribute] } + # end + # end + # page.save(:validate => false) + # end + # end + end end diff --git a/script/upgrade.rb b/script/upgrade.rb new file mode 100755 index 00000000..85acf6f7 --- /dev/null +++ b/script/upgrade.rb @@ -0,0 +1,202 @@ +#!/usr/bin/env ruby +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end + +require 'mongoid' + +Mongoid.configure do |config| + name = 'locomotive_hosting_production' + host = 'localhost' + # config.master = Mongo::Connection.new.db(name) + config.master = Mongo::Connection.new('localhost', '27017', :logger => Logger.new($stdout)).db(name) +end + +puts "***************************************" +puts "[LocomotiveCMS] Upgrade from 1.0 to 2.0" +puts "***************************************\n\n" + +db = Mongoid.config.master + +# assets -> locomotive_content_assets +if collection = db.collections.detect { |c| c.name == 'assets' } + new_collection = db.collections.detect { |c| c.name == 'locomotive_content_assets' } + new_collection.drop if new_collection + collection.rename 'locomotive_content_assets' +end + +# content_types -> locomotive_content_types +if collection = db.collections.detect { |c| c.name == 'content_types' } + new_collection = db.collections.detect { |c| c.name == 'locomotive_content_types' } + new_collection.drop if new_collection + collection.rename 'locomotive_content_types' + + contents_collection = db.collections.detect { |c| c.name == 'locomotive_content_entries' } + contents_collection = db.create_collection('locomotive_content_entries') if contents_collection.nil? + + collection.update({}, { + '$rename' => { + 'api_enabled' => 'public_submission_enabled', + 'api_accounts' => 'public_submission_accounts', + 'content_custom_fields' => 'entries_custom_fields', + 'content_custom_fields_version' => 'entries_custom_fields_version', + }, + '$unset' => { + 'content_custom_fields_counter' => '1' + } + }, :multi => true) + + collection.find.each do |content_type| + puts "_________________________________" + puts "[content_type: #{content_type['name']}] = #{content_type.keys.inspect}" + + label_field_name = '' + operations = { '$set' => {}, '$unset' => {} } + contents = content_type['contents'] + custom_fields = content_type['entries_custom_fields'] + + puts "\n*** field keys = #{custom_fields.first.keys.inspect} ***\n\n" + + # fields + custom_fields.each_with_index do |field, index| + # puts "field #{field['_alias']}.#{index} (#{field['kind']})" + + next if field['kind'].blank? # already done + + class_name = "Locomotive::Entry#{field['target'][-24,24]}" if field['target'] + + case field['kind'] + when 'category' + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'select') + when 'has_one' + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'belongs_to') + when 'has_many' + if field['reverse_lookup'] + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'has_many') + + # reverse_lookup -> inverse_of => hmmmmmmm + if _content_type = collection.find('_id' => BSON::ObjectId(field['target'][-24,24]).first) + if _field = _content_type['entries_custom_fields'].detect { |f| f['_name'] == field['reverse_lookup'] } + operations['$set'].merge!("entries_custom_fields.#{index}.inverse_of" => _field['_alias']) + end + end + else + operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'many_to_many') + end + end + + if %w(has_one has_many).include?(field['kind']) + operations['$set'].merge!("entries_custom_fields.#{index}.class_name" => class_name) + operations['$unset'].merge!({ + "entries_custom_fields.#{index}.target" => '1', + "entries_custom_fields.#{index}.reverse_lookup" => '1' + }) + end + + if content_type['highlighted_field_name'] == field['_name'] + operations['$set'].merge!({ + 'label_field_id' => field['_id'], + 'label_field_name' => field['_alias'] + }) + label_field_name = field['_alias'] + end + + if content_type['group_by_field_name'] == field['_name'] + operations['$set'].merge!('group_by_field_id' => field['_id']) + end + + operations['$set'].merge!({ + "entries_custom_fields.#{index}.name" => field['_alias'], + "entries_custom_fields.#{index}.type" => field['kind'] + }) + + operations['$set'].merge!({ + "entries_custom_fields.#{index}.select_options" => field['category_items'] + }) if field['kind'] == 'category' + + operations['$unset'].merge!({ + "entries_custom_fields.#{index}._name" => '1', + "entries_custom_fields.#{index}._alias" => '1', + "entries_custom_fields.#{index}.kind" => '1', + "entries_custom_fields.#{index}.category_items" => '1' + }) + end + + operations['$set'].merge!({ + 'order_by' => '_position' + }) if content_type['order_by'] == '_position_in_list' + + operations['$unset'].merge!({ + 'highlighted_field_name' => '1', + 'group_by_field_name' => '1' + }) + + # contents + if contents.nil? + puts "CONTENTS SKIPPED" + next + end + + contents.each_with_index do |content| + attributes = content.clone.keep_if { |k, v| %w(_id _slug seo_title meta_description meta_keywords _visible created_at updated_at).include?(k) } + attributes.merge!({ + 'content_type_id' => content_type['_id'], + 'site_id' => content_type['site_id'], + '_position' => content['_position_in_list'], + '_type' => "Locomotive::Entry#{content_type['_id']}", + '_label_field_name' => label_field_name + }) + + custom_fields.each do |field| + name, _name = field['_alias'], field['_name'] + + case field['kind'] # string, text, boolean, date, file, category, has_many, has_one + when 'string', 'text', 'boolean', 'date' + attributes[name] = content[_name] + when 'file' + attributes[name] = "#{content[_name]}_filename" + when 'category', 'has_one' + attributes["#{name}_id"] = content[_name] + when 'has_many' + if field['reverse_lookup'] + # nothing to do + else + attributes["#{name}_ids"] = content[_name] + end + end + end + + # insert document + # puts "[INSERTING] ===> #{attributes.inspect}" + contents_collection.insert attributes + end + + # TODO: change highlighted_field_name + for each field, change category -> select + + # save content_type + collection.update({ '_id' => content_type['_id'] }, operations) + + # puts operations.inspect + + puts "================================= END =========================" + + # raise 'END' + end + + collection.update({}, { + '$unset' => { + 'contents' => '1' + } + }, :multi => true) +end + + +# TODO +# x relaunch with a fresh db +# x inverse_of can not work if we commit changes before +# x insert contents +# - assets +# - +# diff --git a/spec/dummy/config/mongoid.yml b/spec/dummy/config/mongoid.yml index 17611c5e..7416363a 100644 --- a/spec/dummy/config/mongoid.yml +++ b/spec/dummy/config/mongoid.yml @@ -17,4 +17,5 @@ test: production: <<: *defaults - database: locomotive_engine_production + # database: locomotive_engine_production + database: locomotive_hosting_production