advertnet 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. data/LICENSE +674 -0
  2. data/README +257 -0
  3. data/Rakefile +10 -0
  4. data/app/controllers/application.rb +40 -0
  5. data/app/controllers/container_controller.rb +83 -0
  6. data/app/controllers/content_controller.rb +61 -0
  7. data/app/controllers/furnitures_controller.rb +87 -0
  8. data/app/controllers/networks_controller.rb +269 -0
  9. data/app/controllers/objects_in_world_controller.rb +27 -0
  10. data/app/controllers/panels_controller.rb +186 -0
  11. data/app/helpers/application_helper.rb +31 -0
  12. data/app/helpers/container_helper.rb +2 -0
  13. data/app/helpers/content_helper.rb +2 -0
  14. data/app/helpers/furnitures_helper.rb +2 -0
  15. data/app/helpers/network_helper.rb +2 -0
  16. data/app/helpers/networks_helper.rb +2 -0
  17. data/app/helpers/objects_in_world_helper.rb +2 -0
  18. data/app/helpers/panels_helper.rb +2 -0
  19. data/app/models/container.rb +51 -0
  20. data/app/models/furniture.rb +60 -0
  21. data/app/models/mailer.rb +22 -0
  22. data/app/models/network.rb +38 -0
  23. data/app/models/object_in_world.rb +60 -0
  24. data/app/models/object_off_world.rb +77 -0
  25. data/app/models/panel.rb +136 -0
  26. data/app/models/panel_revision.rb +108 -0
  27. data/app/views/container/list.rhtml +27 -0
  28. data/app/views/container/show.rhtml +8 -0
  29. data/app/views/content/_form.rhtml +12 -0
  30. data/app/views/content/edit.rhtml +8 -0
  31. data/app/views/content/list.rhtml +26 -0
  32. data/app/views/content/new.rhtml +8 -0
  33. data/app/views/furnitures/list.rhtml +51 -0
  34. data/app/views/furnitures/show.rhtml +7 -0
  35. data/app/views/layouts/default.rhtml +30 -0
  36. data/app/views/mailer/avtouch.rhtml +1 -0
  37. data/app/views/networks/_form.rhtml +11 -0
  38. data/app/views/networks/_parameters_form.rhtml +8 -0
  39. data/app/views/networks/edit.rhtml +9 -0
  40. data/app/views/networks/list.rhtml +25 -0
  41. data/app/views/networks/new.rhtml +8 -0
  42. data/app/views/networks/show.rhtml +72 -0
  43. data/app/views/networks/show_parameters_form.rjs +5 -0
  44. data/app/views/objects_in_world/list.rhtml +30 -0
  45. data/app/views/objects_in_world/show.rhtml +8 -0
  46. data/app/views/panels/list.rhtml +48 -0
  47. data/app/views/panels/show.rhtml +8 -0
  48. data/bin/advertnet-install +144 -0
  49. data/config/boot.rb +45 -0
  50. data/config/database.yml.default +43 -0
  51. data/config/environment.rb +67 -0
  52. data/config/environments/development.rb +21 -0
  53. data/config/environments/production.rb +21 -0
  54. data/config/environments/test.rb +19 -0
  55. data/config/routes.rb +23 -0
  56. data/db/migrate/001_create_networks.rb +14 -0
  57. data/db/migrate/002_create_furnitures.rb +17 -0
  58. data/db/migrate/003_create_containers.rb +16 -0
  59. data/db/migrate/004_create_objects_in_world.rb +16 -0
  60. data/db/migrate/005_create_objects_off_world.rb +16 -0
  61. data/db/migrate/006_create_panels.rb +17 -0
  62. data/db/migrate/007_join_objects_in_world_panels.rb +17 -0
  63. data/db/migrate/008_join_objects_off_world_panels.rb +17 -0
  64. data/db/migrate/009_add_sessions.rb +19 -0
  65. data/db/migrate/010_add_or_rename_fields.rb +25 -0
  66. data/db/migrate/011_move_texture_to_object_in_world.rb +25 -0
  67. data/db/migrate/012_add_permanent_furniture_id.rb +18 -0
  68. data/db/migrate/013_create_panel_revisions.rb +42 -0
  69. data/db/schema.rb +102 -0
  70. data/doc/README_FOR_APP +108 -0
  71. data/lib/advertnet.rb +13 -0
  72. data/lib/second_life_validators.rb +89 -0
  73. data/lib/tasks/documentation.rake +16 -0
  74. data/lib/tasks/extract_fixtures.rake +23 -0
  75. data/lib/tasks/load_exported_fixtures.rake +14 -0
  76. data/public/404.html +30 -0
  77. data/public/500.html +30 -0
  78. data/public/dispatch.cgi +10 -0
  79. data/public/dispatch.fcgi +24 -0
  80. data/public/dispatch.rb +10 -0
  81. data/public/favicon.ico +0 -0
  82. data/public/images/rails.png +0 -0
  83. data/public/javascripts/application.js +2 -0
  84. data/public/javascripts/controls.js +833 -0
  85. data/public/javascripts/dragdrop.js +942 -0
  86. data/public/javascripts/effects.js +1088 -0
  87. data/public/javascripts/prototype.js +2515 -0
  88. data/public/robots.txt +1 -0
  89. data/public/stylesheets/application.css +25 -0
  90. data/public/stylesheets/scaffold.css +74 -0
  91. data/script/about +3 -0
  92. data/script/breakpointer +3 -0
  93. data/script/console +3 -0
  94. data/script/destroy +3 -0
  95. data/script/generate +3 -0
  96. data/script/performance/benchmarker +3 -0
  97. data/script/performance/profiler +3 -0
  98. data/script/plugin +3 -0
  99. data/script/process/inspector +3 -0
  100. data/script/process/reaper +3 -0
  101. data/script/process/spawner +3 -0
  102. data/script/runner +3 -0
  103. data/script/server +3 -0
  104. data/test/fixtures/containers.yml +44 -0
  105. data/test/fixtures/furnitures.yml +47 -0
  106. data/test/fixtures/mailer/avtouch +1 -0
  107. data/test/fixtures/networks.yml +31 -0
  108. data/test/fixtures/objects_in_world.yml +71 -0
  109. data/test/fixtures/objects_in_world_panels.yml +43 -0
  110. data/test/fixtures/objects_off_world.yml +55 -0
  111. data/test/fixtures/objects_off_world_panels.yml +20 -0
  112. data/test/fixtures/panel_revisions.yml +28 -0
  113. data/test/fixtures/panels.yml +79 -0
  114. data/test/functional/container_controller_test.rb +167 -0
  115. data/test/functional/content_controller_test.rb +91 -0
  116. data/test/functional/furnitures_controller_test.rb +134 -0
  117. data/test/functional/networks_controller_test.rb +248 -0
  118. data/test/functional/objects_in_world_controller_test.rb +42 -0
  119. data/test/functional/panels_controller_test.rb +173 -0
  120. data/test/test_helper.rb +53 -0
  121. data/test/unit/container_test.rb +53 -0
  122. data/test/unit/furniture_test.rb +68 -0
  123. data/test/unit/mailer_test.rb +40 -0
  124. data/test/unit/network_test.rb +42 -0
  125. data/test/unit/object_in_world_test.rb +33 -0
  126. data/test/unit/object_off_world_test.rb +26 -0
  127. data/test/unit/panel_revision_test.rb +148 -0
  128. data/test/unit/panel_test.rb +63 -0
  129. data/vendor/plugins/annotate_models/ChangeLog +46 -0
  130. data/vendor/plugins/annotate_models/README +31 -0
  131. data/vendor/plugins/annotate_models/lib/annotate_models.rb +132 -0
  132. data/vendor/plugins/annotate_models/tasks/annotate_models_tasks.rake +7 -0
  133. data/vendor/plugins/foreign_key_migrations/CHANGELOG +99 -0
  134. data/vendor/plugins/foreign_key_migrations/MIT-LICENSE +20 -0
  135. data/vendor/plugins/foreign_key_migrations/README +87 -0
  136. data/vendor/plugins/foreign_key_migrations/about.yml +5 -0
  137. data/vendor/plugins/foreign_key_migrations/init.rb +3 -0
  138. data/vendor/plugins/foreign_key_migrations/install.rb +1 -0
  139. data/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/base.rb +22 -0
  140. data/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/connection_adapters/abstract_adapter.rb +22 -0
  141. data/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/connection_adapters/table_definition.rb +28 -0
  142. data/vendor/plugins/redhillonrails_core/CHANGELOG +154 -0
  143. data/vendor/plugins/redhillonrails_core/MIT-LICENSE +20 -0
  144. data/vendor/plugins/redhillonrails_core/README +139 -0
  145. data/vendor/plugins/redhillonrails_core/init.rb +19 -0
  146. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/base.rb +54 -0
  147. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/abstract_adapter.rb +31 -0
  148. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/column.rb +21 -0
  149. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/foreign_key_definition.rb +26 -0
  150. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/index_definition.rb +11 -0
  151. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/mysql_adapter.rb +82 -0
  152. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/mysql_column.rb +8 -0
  153. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/postgresql_adapter.rb +107 -0
  154. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/schema_statements.rb +23 -0
  155. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/sqlite3_adapter.rb +9 -0
  156. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/table_definition.rb +27 -0
  157. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/schema.rb +27 -0
  158. data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/schema_dumper.rb +47 -0
  159. metadata +290 -0
@@ -0,0 +1,31 @@
1
+ # Methods added to this helper will be available to all templates in the application.
2
+ module ApplicationHelper
3
+
4
+ # Affiche proprement le nom d'un object in world ou bien "none"
5
+ #
6
+ # XXX Ce helper n'est plus utilisé, on peut sans doute le supprimer.
7
+ #
8
+ # == Input
9
+ #
10
+ # +oiw+:: ObjectInWorld
11
+ #
12
+ # == Output
13
+ #
14
+ # Affiche le nom, échappé avec "<tt>h()</tt>", ou bien le mot "+none+"
15
+ def object_name(oiw)
16
+ oiw ? h(oiw.name) : "none"
17
+ end
18
+
19
+ # Affiche proprement l'url d'un ObjectOffWorld
20
+ #
21
+ # == Input
22
+ #
23
+ # +oow+:: ObjectOffWorld
24
+ #
25
+ # == Output
26
+ #
27
+ # Affiche le nom, avec un lien hypertexte, ou bien le mot "+none+"
28
+ def object_url(oow)
29
+ oow ? "<a href=\"#{oow.content}\">#{oow.name}</a>" : "none"
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+ module ContainerHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module ContentHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module FurnituresHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module NetworkHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module NetworksHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module ObjectsInWorldHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module PanelsHelper
2
+ end
@@ -0,0 +1,51 @@
1
+ # Modèle Container Second Life pour le stockage des objets Second Life. Voir ObjectInWorld
2
+ # pour la description des objets stockés.
3
+
4
+ # Inclut la librairie ("lib/second_life_validators.rb") qui contient les
5
+ # validateurs Second Life
6
+ require 'second_life_validators'
7
+
8
+ # Conteneur Second Life de stockage des objets Second Life. Voir ObjectInWorld
9
+ # pour la description des objets stockés.
10
+ #
11
+ # == Schema Information
12
+ # Schema version: 13
13
+ #
14
+ # Table name: containers
15
+ #
16
+ # id :integer(11) not null, primary key
17
+ # container_key :string(64) not null
18
+ # sim :string(64) not null
19
+ # name :string(64) not null
20
+ # position :string(40) not null
21
+ # delay :integer(11) default(30), not null
22
+ # created_at :datetime
23
+ # updated_at :datetime
24
+ #
25
+
26
+ class Container < ActiveRecord::Base
27
+ # Pas besoin de ':dependent', on gère la cohérence (i.e. interdiction de
28
+ # supprimer un Container qui contient des OIWs) via les clés étrangères.
29
+ # --------
30
+ # En fait si ! Sinon on ne peut pas utiliser un appel du type
31
+ # "container.objects_in_wold.replace([o1, o2])"...
32
+ # Il faut en effet détruire les objets qui ne sont plus rattachés à un
33
+ # container.
34
+ has_many :objects_in_world,
35
+ :dependent => :destroy
36
+
37
+ # Correction de la sim et de la position avant validation et insertion en
38
+ # base de données
39
+ before_validation SecondLifeSimPosNormalizer.new(:sim, :position)
40
+
41
+ validates_presence_of(:name, :container_key, :sim, :position, :delay, :message => "must be given a value")
42
+ validates_uniqueness_of(:container_key, :message => "must be unique")
43
+ validates_numericality_of(:delay, :only_integer => true)
44
+ # Format des id d'object dans Second Life
45
+ validates_format_of_sl_key(:container_key)
46
+ # Format de la position relative dans une Sim : X,Y,Z
47
+ validates_format_of_sl_position(:position)
48
+ # Valeurs tolérées pour delay
49
+ # (TODO : Trouver quelle est la plage de valeur raisonnable pour delay)
50
+ validates_inclusion_of(:delay, :in => 5..60, :message => "is not a valid delay")
51
+ end
@@ -0,0 +1,60 @@
1
+ # == Schema Information
2
+ # Schema version: 13
3
+ #
4
+ # Table name: furnitures
5
+ #
6
+ # id :integer(11) not null, primary key
7
+ # name :string(64) not null
8
+ # furniture_key :string(36) not null
9
+ # sim :string(64) not null
10
+ # position :string(40) not null
11
+ # delay :integer(11) default(30), not null
12
+ # created_at :datetime
13
+ # updated_at :datetime
14
+ # permanent_id :integer(11)
15
+ #
16
+
17
+ # Inclut la librairie ("lib/second_life_validators.rb") qui contient les
18
+ # validateurs Second Life
19
+ require 'second_life_validators'
20
+
21
+ class Furniture < ActiveRecord::Base
22
+ # Pas besoin de ':dependent', on gère la cohérence (i.e. interdiction de
23
+ # supprimer un Furniture qui contient des Panels) via les clés étrangères.
24
+ has_many :panels
25
+
26
+ # Correction de la sim et de la position avant validation et insertion en
27
+ # base de données
28
+ before_validation SecondLifeSimPosNormalizer.new(:sim, :position)
29
+
30
+ validates_presence_of(:name, :furniture_key, :sim, :position, :delay, :message => "must be given a value")
31
+ validates_uniqueness_of(:furniture_key, :message => "must be unique")
32
+ validates_numericality_of(:delay, :only_integer => true)
33
+ # permanent_id peut être numérique ou nul
34
+ validates_numericality_of :permanent_id, :only_integer => true,
35
+ :if => Proc.new { |u| !u.permanent_id.nil? }
36
+ # Format des id d'object dans Second Life
37
+ validates_format_of_sl_key(:furniture_key)
38
+ # Format de la position relative dans une Sim : X,Y,Z
39
+ validates_format_of_sl_position(:position)
40
+ # Valeurs tolérées pour delay
41
+ # (TODO : Trouver quelle est la plage de valeur raisonnable pour delay)
42
+ validates_inclusion_of(:delay, :in => 5..60, :message => "is not a valid delay")
43
+
44
+ # Affecte l'id permanent lors de la création d'un mobilier, s'il n'a pas déjà été renseigné.
45
+ # Par convention, l'id permanent est l'id principal du premier mobilier créé dans SL.
46
+ # Lorsque l'on remplace dans SL un mobilier par une nouvelle version, on doit lui attribuer
47
+ # manuellement son id permanent, afin qu'il l'envoie à l'application Rails lors de l'appel
48
+ # de "register".
49
+ def after_create
50
+ update_attribute(:permanent_id, id) if !permanent_id?
51
+ end
52
+
53
+ # Enregistrement d'une nouvelle révision des panneaux du mobilier à chaque enregistrement,
54
+ # que ce soit "save" ou "update".
55
+ def after_save
56
+ panels.each do |p|
57
+ PanelRevision.add(p) # Nouvelle révision du panneau
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,22 @@
1
+ # Cette classe gère les envois de mails
2
+ class Mailer < ActionMailer::Base
3
+
4
+ # Envoi de mail à un container lorsqu'un avatar clique sur un panel
5
+ #
6
+ # == Input
7
+ # +container_key+:: Clé du container contenant l'object à envoyer
8
+ # +avatar_key+:: Clé de l'avatar qui a cliqué sur le panel
9
+ # +object_name+:: Nom de l'object à envoyer
10
+ #
11
+ # == Output
12
+ #
13
+ # * Envoi d'un mail à un container dans SL.
14
+ def avtouch(container_key, avatar_key, object_name)
15
+ @subject = 'give inventory'
16
+ @body = { "avatar_key" => avatar_key, "object_name" => object_name }
17
+ @recipients = container_key + "@lsl.secondlife.com"
18
+ @from = 'webia@community-chest.net'
19
+ @sent_on = Time.now
20
+ @headers = {}
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ # == Schema Information
2
+ # Schema version: 13
3
+ #
4
+ # Table name: networks
5
+ #
6
+ # id :integer(11) not null, primary key
7
+ # name :string(64) not null
8
+ # active :boolean(1) default(TRUE), not null
9
+ # created_at :datetime
10
+ # updated_at :datetime
11
+ #
12
+
13
+ class Network < ActiveRecord::Base
14
+ # XXX : Argh avec :nullify il se passe exactement le contraire de l'effet attendu !
15
+ # * Avec ":dependent => :nullify"
16
+ # - Quand on casse l'association d'un réseau et d'un panneau, Rails *supprime* le panneau
17
+ # au lieu de mettre le champ network_id à NULL
18
+ # - Mais Rails mets bien ce champ à NULL quand on fait "network.destroy"
19
+ # * Sans ":dependent => :nullify"
20
+ # - Quand on casse l'association, Rails mets à NULL le champ network_id, ce qui est bien, mais
21
+ # c'est le contraire de ce que dit la doc !
22
+ # - Quand on fait "network.destroy", Rails ne met pas à NULL network_id. Mais heureusement
23
+ # les FOREIGN KEYS de MySQL, qu'on a activé grâce à un plugin, corrige le problème.
24
+ has_many :panels #, :dependent => :nullify
25
+
26
+ STATES = [ [ "True", true ], [ "False", false ]]
27
+
28
+ validates_presence_of(:name, :message => "must be given a value")
29
+ validates_uniqueness_of(:name, :message => "is already use by another network")
30
+
31
+ # Enregistrement d'une nouvelle révision des panneaux du mobilier à chaque enregistrement,
32
+ # que ce soit "save" ou "update".
33
+ def after_save
34
+ panels.each do |p|
35
+ PanelRevision.add(p) # Nouvelle révision du panneau
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,60 @@
1
+ # == Schema Information
2
+ # Schema version: 13
3
+ #
4
+ # Table name: objects_in_world
5
+ #
6
+ # id :integer(11) not null, primary key
7
+ # name :string(64) not null
8
+ # object_key :string(36) not null
9
+ # otype :string(16) not null
10
+ # container_id :integer(11) not null
11
+ # created_at :datetime
12
+ # updated_at :datetime
13
+ #
14
+
15
+ # Inclut la librairie ("lib/second_life_validators.rb") qui contient les
16
+ # validateurs Second Life
17
+ require 'second_life_validators'
18
+
19
+ class ObjectInWorld < ActiveRecord::Base
20
+ # Type d'objets in world autorisés
21
+ TYPES = [ "texture", "landmark", "notecard" ]
22
+
23
+ belongs_to :container
24
+ has_and_belongs_to_many :panels
25
+
26
+ validates_presence_of(:object_key, :name, :otype, :container_id, :message => "must be given a value")
27
+ validates_uniqueness_of(:object_key, :message => "must be unique")
28
+
29
+ # Format des id d'object dans Second Life
30
+ validates_format_of_sl_key(:object_key)
31
+
32
+ # Format du type d'objet
33
+ validates_inclusion_of(:otype, :in => TYPES, :message => "is an invalid object type")
34
+
35
+ # Retourne la texture par défaut
36
+ def self.default_texture
37
+ find_by_name_and_otype("default", "texture")
38
+ end
39
+
40
+ #----------------------------------------------------------------------------
41
+ # Gestion de la création de révisions de panneaux en 2 étapes.
42
+ # - Avant 'destroy', on note la liste des panneaux comportant l'objet à détruire
43
+ # - Après 'destroy', on crée une nouvelle révision pour chaque de ces objets.
44
+ @panels_needing_revisions = nil
45
+
46
+ # Note les id des panneaux dont on doit créer une nouvelle révision, avant destruction des objets associés
47
+ def before_destroy
48
+ @panels_needing_revisions = self.panels.map(&:id)
49
+ end
50
+
51
+ # Enregistrement d'une nouvelle révision des panneaux du mobilier à chaque suppression d'objet.
52
+ # On reprend les id de panneaux qu'on a notés avant la destruction de l'objet.
53
+ def after_destroy
54
+ @panels_needing_revisions.each do |panel_id|
55
+ PanelRevision.add(Panel.find(panel_id)) # Nouvelle révision du panneau
56
+ end
57
+ end
58
+ # Fin gestion de la création des révisions de panneaux
59
+ #----------------------------------------------------------------------------
60
+ end
@@ -0,0 +1,77 @@
1
+ # == Schema Information
2
+ # Schema version: 13
3
+ #
4
+ # Table name: objects_off_world
5
+ #
6
+ # id :integer(11) not null, primary key
7
+ # name :string(64) not null
8
+ # object_key :string(36) not null
9
+ # otype :string(16) not null
10
+ # content :text
11
+ # created_at :datetime
12
+ # updated_at :datetime
13
+ #
14
+
15
+ require 'digest/md5'
16
+ # Inclut la librairie ("lib/second_life_validators.rb") qui contient les
17
+ # validateurs Second Life
18
+ require 'second_life_validators'
19
+
20
+ class ObjectOffWorld < ActiveRecord::Base
21
+ has_and_belongs_to_many :panels
22
+
23
+ # Types d'objets autorisés. Le format de la liste imbriquée est :
24
+ #
25
+ # Premier champ:: Valeur à afficher
26
+ # Deuxième champ:: Valeur à stocker en base
27
+ #
28
+ # On peut étendre cette liste pour ajouter de nouveaux types.
29
+ TYPES = [ [ "URL", "url" ],
30
+ [ "Media URL", "mediaurl" ] ]
31
+
32
+ validates_presence_of(:object_key, :name, :otype, :content, :message => "must be given a value")
33
+ # La clé est construite à partir de 'name' et 'content', seule elle a besoin d'être
34
+ # unique. Voir 'before_validation' pour la génération de la clé.
35
+ validates_uniqueness_of(:object_key, :message => "must be unique")
36
+
37
+ # Format des id d'object dans Second Life
38
+ validates_format_of_sl_key(:object_key)
39
+
40
+ # Format du type d'objet
41
+ validates_inclusion_of(:otype, :in => TYPES.collect { |type| type[1] }, :message => "is an invalid object type")
42
+
43
+ # Pour remplir le champ 'object_key', calcule le md5 de 'name' plus 'content'
44
+ # et réécrit le de façon à ce que ça ressemble à une clé Second Life, en
45
+ # rajoutant 4 tirets aux bons endroits.
46
+ def before_validation
47
+ # Si name ou content sont indéfini on ne peut pas calculer le MD5
48
+ return if name.nil? or content.nil?
49
+
50
+ key = Digest::MD5.new.update(name + content).to_s
51
+ self.object_key = key.sub(
52
+ /^([[:xdigit:]]{8})([[:xdigit:]]{4})([[:xdigit:]]{4})([[:xdigit:]]{4})([[:xdigit:]]{12})$/,
53
+ '\1-\2-\3-\4-\5')
54
+ end
55
+
56
+
57
+ #----------------------------------------------------------------------------
58
+ # Gestion de la création de révisions de panneaux en 2 étapes.
59
+ # - Avant 'destroy', on note la liste des panneaux comportant l'objet à détruire
60
+ # - Après 'destroy', on crée une nouvelle révision pour chaque de ces objets.
61
+ @panels_needing_revisions = nil
62
+
63
+ # Note les id des panneaux dont on doit créer une nouvelle révision, avant destruction des objets associés
64
+ def before_destroy
65
+ @panels_needing_revisions = self.panels.map(&:id)
66
+ end
67
+
68
+ # Enregistrement d'une nouvelle révision des panneaux du mobilier à chaque suppression d'objet.
69
+ # On reprend les id de panneaux qu'on a notés avant la destruction de l'objet.
70
+ def after_destroy
71
+ @panels_needing_revisions.each do |panel_id|
72
+ PanelRevision.add(Panel.find(panel_id)) # Nouvelle révision du panneau
73
+ end
74
+ end
75
+ # Fin gestion de la création des révisions de panneaux
76
+ #----------------------------------------------------------------------------
77
+ end
@@ -0,0 +1,136 @@
1
+ # == Panel
2
+ #
3
+ # Panel contient les panneaux publicitaires proprement dits. Ces panneaux appartiennent à
4
+ # des médias, décrits par le modèle Furniture.
5
+
6
+ # Inclut la librairie ("lib/second_life_validators.rb") qui contient les
7
+ # validateurs Second Life
8
+ require 'second_life_validators'
9
+
10
+ # == Panel
11
+ #
12
+ # Panel contient les panneaux publicitaires proprement dits. Ces panneaux appartiennent à
13
+ # des médias, décrits par le modèle Furniture.
14
+ #
15
+ # == Schema Information
16
+ # Schema version: 13
17
+ #
18
+ # Table name: panels
19
+ #
20
+ # id :integer(11) not null, primary key
21
+ # name :string(64) not null
22
+ # panel_key :string(36) not null
23
+ # furniture_id :integer(11) not null
24
+ # network_id :integer(11)
25
+ # created_at :datetime
26
+ # updated_at :datetime
27
+ #
28
+
29
+ class Panel < ActiveRecord::Base
30
+ belongs_to :network
31
+ belongs_to :furniture
32
+ has_and_belongs_to_many :objects_in_world
33
+ has_and_belongs_to_many :objects_off_world
34
+
35
+ validates_presence_of(:name, :panel_key, :furniture_id, :message => "must be given a value")
36
+ validates_uniqueness_of(:panel_key, :message => "must be unique")
37
+ # Format des id d'objets dans Second Life
38
+ validates_format_of_sl_key(:panel_key)
39
+
40
+ # Crée dynamiquement de nouveaux accessors pour les ObjectInWorld
41
+ #
42
+ # * texture()
43
+ # * texture=(new_texture)
44
+ # * landmark()
45
+ # * landmark=(new_landmark)
46
+ # * etc.
47
+ ObjectInWorld::TYPES.each do |type|
48
+ module_eval(%Q{
49
+ # Applique un object in world d'un type donné à un panneau
50
+ def #{type}=(new_oiw)
51
+ oiw = objects_in_world.find_by_otype("#{type}")
52
+ # Suppression si argument est nil
53
+ if new_oiw.nil?
54
+ objects_in_world.delete(oiw) if oiw
55
+ PanelRevision.add(self) # Nouvelle révision du panneau
56
+ return nil
57
+ end
58
+ # Vérification de type
59
+ if !new_oiw.instance_of?(ObjectInWorld) or new_oiw.otype != "#{type}"
60
+ raise TypeError, "Argument \#{new_oiw.inspect} is not a '#{type}' object"
61
+ end
62
+ # Le nouvel object est-il égal au précédent ?
63
+ if new_oiw == oiw
64
+ return oiw
65
+ end
66
+ objects_in_world.delete(oiw) if oiw
67
+ objects_in_world << new_oiw
68
+ PanelRevision.add(self) # Nouvelle révision du panneau
69
+ new_oiw
70
+ end
71
+
72
+ # Retourne l'object in world du type donné, associé à ce panneau
73
+ def #{type}
74
+ objects_in_world.find_by_otype("#{type}")
75
+ end
76
+ }, __FILE__, __LINE__)
77
+ end
78
+
79
+ # Crée dynamiquement de nouveaux accessors pour les ObjectOffWorld
80
+ #
81
+ # * url()
82
+ # * url=(new_url)
83
+ # * etc.
84
+ ObjectOffWorld::TYPES.map { |t| t[1] }.each do |type|
85
+ # Retourne l'object off world du type donné, associé à ce panneau
86
+ module_eval(%Q{
87
+ # Applique un object off world d'un type donné à un panneau
88
+ def #{type}=(new_oow)
89
+ oow = objects_off_world.find_by_otype("#{type}")
90
+ # Suppression si argument est nil
91
+ if new_oow.nil?
92
+ objects_off_world.delete(oow) if oow
93
+ PanelRevision.add(self) # Nouvelle révision du panneau
94
+ return nil
95
+ end
96
+ # Vérification de type
97
+ if !new_oow.instance_of?(ObjectOffWorld) or new_oow.otype != "#{type}"
98
+ raise TypeError, "Argument \#{new_oow.inspect} is not a '#{type}' object"
99
+ end
100
+ # Le nouvel object est-il égal au précédent ?
101
+ if new_oow == oow
102
+ return oow
103
+ end
104
+ objects_off_world.delete(oow) if oow
105
+ objects_off_world << new_oow
106
+ PanelRevision.add(self) # Nouvelle révision du panneau
107
+ new_oow
108
+ end
109
+
110
+ def #{type}
111
+ objects_off_world.find_by_otype("#{type}")
112
+ end
113
+ }, __FILE__, __LINE__)
114
+ end
115
+
116
+ # Retourne la liste de tous les panneaux non attribués à un réseau
117
+ def self.unassigned_panels
118
+ Panel.find(:all, :conditions => [ 'network_id IS NULL' ])
119
+ end
120
+
121
+ # Met une texture par défaut si aucune n'est précisée à la création
122
+ def before_validation # :doc:
123
+ if !texture
124
+ self.texture = ObjectInWorld.default_texture
125
+ end
126
+ end
127
+
128
+ # Enregistrement d'une nouvelle révision du panneau à chaque enregistrement,
129
+ # que ce soit "save" ou "update".
130
+ #
131
+ # Ce callback sera appelé aussi bien pour les modification sur le panneau
132
+ # que lors de modifications du réseau associé.
133
+ def after_save
134
+ PanelRevision.add(self) # Nouvelle révision du panneau
135
+ end
136
+ end