From e42ecea2db8ff123f268edf48190d6b9b28fcedb Mon Sep 17 00:00:00 2001 From: mh Date: Sun, 25 Aug 2002 19:00:06 +0000 Subject: [PATCH] merge of localization branch into HEAD. mh and zap --- bundles/admin_ay.properties | 382 ++++++++++ bundles/admin_en.properties | 28 +- bundles/admin_es.properties | 13 +- bundles/admin_gn.properties | 383 ++++++++++ bundles/admin_qu.properties | 383 ++++++++++ bundles/producer_en.properties | 24 + bundles/producer_es.properties | 26 + lib/lucene-1.2.jar | Bin 0 -> 136498 bytes lib/multex2c.jar | Bin 0 -> 46408 bytes scripts/createIndex.java | 19 + source/Mir.java | 51 +- source/OpenMir.java | 20 +- source/config.properties-dist | 10 +- source/mir/config/ConfigChecker.java | 12 +- source/mir/config/ConfigNode.java | 16 +- source/mir/config/ConfigReader.java | 40 +- source/mir/config/MirConfiguration.java | 2 +- .../exceptions/ConfigDefineNotKnownException.java | 2 +- .../{ConfigException.java => ConfigFailure.java} | 48 +- .../ConfigInvalidPropertyTypeException.java | 2 +- .../exceptions/ConfigMissingPropertyException.java | 2 +- source/mir/entity/EntityBrowser.java | 93 +++ source/mir/entity/QueryBuilder.java | 56 ++ source/mir/entity/adapter/EntityAdapter.java | 122 +++ .../entity/adapter/EntityAdapterDefinition.java | 125 ++++ source/mir/entity/adapter/EntityAdapterModel.java | 52 ++ .../mir/entity/adapter/EntityIteratorAdapter.java | 42 ++ source/mir/entity/adapter/EntityListAdapter.java | 85 +++ .../mir/generator/CompositeGeneratorLibrary.java | 46 ++ source/mir/generator/FreemarkerGenerator.java | 279 +++++++ source/mir/generator/Generator.java | 16 + source/mir/generator/GeneratorExc.java | 9 + source/mir/generator/GeneratorFailure.java | 14 + source/mir/generator/WriterEngine.java | 15 + source/mir/misc/HTMLTemplateProcessor.java | 8 +- source/mir/misc/MirConfig.java | 14 +- source/mir/misc/PDFUtil.java | 70 ++ source/mir/producer/AssignmentProducerNode.java | 31 + source/mir/producer/CompositeProducerNode.java | 58 ++ source/mir/producer/ConditionalProducerNode.java | 37 + .../mir/producer/EntityBatchingProducerNode.java | 235 ++++++ .../producer/EntityEnumeratingProducerNode.java | 105 +++ source/mir/producer/EntityListProducerNode.java | 72 ++ .../mir/producer/EntityModifyingProducerNode.java | 44 ++ .../producer/EvaluatedAssignmentProducerNode.java | 33 + .../producer/ExpandedAssignmentProducerNode.java | 33 + .../mir/producer/FileDateSettingProducerNode.java | 35 + source/mir/producer/FileDeletingProducerNode.java | 22 + source/mir/producer/FileOperationProducerNode.java | 39 + source/mir/producer/GeneratingProducerNode.java | 66 ++ source/mir/producer/LoggingProducerNode.java | 30 + source/mir/producer/NodedProducer.java | 25 + source/mir/producer/NodedProducerFactory.java | 39 + source/mir/producer/Producer.java | 7 + source/mir/producer/ProducerExc.java | 9 + source/mir/producer/ProducerFactory.java | 9 + source/mir/producer/ProducerFailure.java | 14 + source/mir/producer/ProducerNode.java | 9 + source/mir/producer/ProducerNodeDecorator.java | 24 + .../mir/producer/ResourceBundleProducerNode.java | 52 ++ source/mir/producer/ScriptCallingProducerNode.java | 37 + source/mir/producer/ValuesMapProducerNode.java | 28 + .../reader/DefaultProducerNodeBuilders.java | 614 +++++++++++++++ source/mir/producer/reader/ProducerConfigExc.java | 9 + .../mir/producer/reader/ProducerConfigFailure.java | 14 + .../mir/producer/reader/ProducerConfigReader.java | 702 +++++++++++++++++ .../mir/producer/reader/ProducerNodeBuilder.java | 34 + .../reader/ProducerNodeBuilderLibrary.java | 31 + source/mir/producer/reader/ReaderTool.java | 63 ++ .../producer/reader/ScriptedProducerFactory.java | 43 ++ .../mir/producer/reader/ScriptedProducerNode.java | 56 ++ .../reader/ScriptedProducerNodeDefinition.java | 100 +++ .../producer/reader/ScriptedProducerNodeTool.java | 56 ++ source/mir/storage/Database.java | 10 +- source/mir/util/CachingRewindableIterator.java | 45 ++ source/mir/util/DateToMapAdapter.java | 37 + source/mir/util/FileMonitor.java | 35 + source/mir/util/GeneratorHTMLFunctions.java | 44 ++ source/mir/util/NullWriter.java | 18 + source/mir/util/ParameterExpander.java | 831 +++++++++++++++++++++ .../mir/util/ResourceBundleGeneratorFunction.java | 37 + source/mir/util/RewindableIterator.java | 7 + source/mircoders/global/JobQueue.java | 136 ++++ source/mircoders/global/MirGlobal.java | 99 +++ source/mircoders/global/ProducerEngine.java | 172 +++++ .../localizer/MirAdminInterfaceLocalizer.java | 18 + .../localizer/MirCachingLocalizerDecorator.java | 65 ++ .../mircoders/localizer/MirDataModelLocalizer.java | 7 + .../mircoders/localizer/MirGeneratorLocalizer.java | 8 + source/mircoders/localizer/MirLocalizer.java | 11 + .../mircoders/localizer/MirLocalizerException.java | 10 + .../mircoders/localizer/MirLocalizerFailure.java | 12 + .../localizer/MirOpenPostingLocalizer.java | 13 + .../localizer/MirProducerAssistantLocalizer.java | 9 + .../mircoders/localizer/MirProducerLocalizer.java | 7 + .../basic/MirBasicAdminInterfaceLocalizer.java | 131 ++++ .../basic/MirBasicDataModelLocalizer.java | 266 +++++++ .../basic/MirBasicGeneratorLocalizer.java | 24 + .../localizer/basic/MirBasicLocalizer.java | 35 + .../basic/MirBasicOpenPostingLocalizer.java | 43 ++ .../basic/MirBasicProducerAssistantLocalizer.java | 86 +++ .../localizer/basic/MirBasicProducerLocalizer.java | 142 ++++ .../localizer/basic/MirBasicWriterEngine.java | 47 ++ source/mircoders/media/MediaHandlerGeneric.java | 20 +- source/mircoders/producer/CompositeProducer.java | 27 + .../producer/CompositeProducerFactory.java | 62 ++ .../producer/ContentMarkingProducerNode.java | 48 ++ .../producer/ContentModifyingProducerNode.java | 65 ++ .../mircoders/producer/IndexingProducerNode.java | 143 ++++ .../producer/MediaGeneratingProducerNode.java | 62 ++ source/mircoders/producer/OldProducerAdapter.java | 24 + .../producer/OldProducerAdapterFactory.java | 25 + .../producer/PDFGeneratingProducerNode.java | 51 ++ source/mircoders/producer/ProducerAll.java | 152 ---- source/mircoders/producer/ProducerContent.java | 169 ----- source/mircoders/producer/ProducerFeature.java | 58 -- source/mircoders/producer/ProducerList.java | 139 ---- source/mircoders/producer/ProducerMedia.java | 1 + source/mircoders/producer/ProducerNavigation.java | 69 -- source/mircoders/producer/ProducerOpenPosting.java | 47 -- source/mircoders/producer/ProducerStartPage.java | 72 -- source/mircoders/producer/ProducerTopics.java | 137 ---- .../reader/SupplementalProducerNodeBuilders.java | 77 ++ source/mircoders/servlet/ServletModuleComment.java | 303 +++++--- source/mircoders/servlet/ServletModuleContent.java | 20 +- .../mircoders/servlet/ServletModuleLocalizer.java | 41 + .../mircoders/servlet/ServletModuleOpenIndy.java | 227 +++--- .../mircoders/servlet/ServletModuleProducer.java | 66 +- templates-dist/admin/start_admin.template | 107 +-- templates-dist/de/open/comment.template | 119 --- templates-dist/de/open/comment_done.template | 39 - templates-dist/de/open/comment_dupe.template | 40 - templates-dist/de/open/comment_en.template | 93 --- templates-dist/de/open/posting.template | 341 --------- templates-dist/de/open/posting_done.template | 38 - templates-dist/de/open/posting_dupe.template | 44 -- templates-dist/de/open/posting_en.template | 345 --------- templates-dist/en/open/comment.template | 119 --- templates-dist/en/open/comment.template.en | 87 --- templates-dist/en/open/comment_done.template | 35 - templates-dist/en/open/comment_dupe.template | 40 - templates-dist/en/open/minimal_posting.template | 77 -- templates-dist/en/open/posting.template | 205 ----- templates-dist/en/open/posting.template.en | 345 --------- templates-dist/en/open/posting_done.template | 40 - templates-dist/en/open/posting_dupe.template | 43 -- templates-dist/nl/open/comment.template | 123 --- templates-dist/nl/open/comment_done.template | 35 - templates-dist/nl/open/comment_dupe.template | 41 - templates-dist/nl/open/posting.template | 252 ------- templates-dist/nl/open/posting_done.template | 40 - templates-dist/nl/open/posting_dupe.template | 53 -- templates-dist/open/comment_en.template | 87 --- templates-dist/open/posting.template | 2 +- templates-dist/open/posting_en.template | 345 --------- templates-dist/producer/content.template | 86 ++- templates-dist/producer/producers.xml | 111 +++ 157 files changed, 8831 insertions(+), 4335 deletions(-) create mode 100755 bundles/admin_ay.properties create mode 100755 bundles/admin_gn.properties create mode 100755 bundles/admin_qu.properties create mode 100755 bundles/producer_en.properties create mode 100755 bundles/producer_es.properties create mode 100755 lib/lucene-1.2.jar create mode 100755 lib/multex2c.jar create mode 100755 scripts/createIndex.java rename source/mir/config/exceptions/{ConfigException.java => ConfigFailure.java} (55%) create mode 100755 source/mir/entity/EntityBrowser.java create mode 100755 source/mir/entity/QueryBuilder.java create mode 100755 source/mir/entity/adapter/EntityAdapter.java create mode 100755 source/mir/entity/adapter/EntityAdapterDefinition.java create mode 100755 source/mir/entity/adapter/EntityAdapterModel.java create mode 100755 source/mir/entity/adapter/EntityIteratorAdapter.java create mode 100755 source/mir/entity/adapter/EntityListAdapter.java create mode 100755 source/mir/generator/CompositeGeneratorLibrary.java create mode 100755 source/mir/generator/FreemarkerGenerator.java create mode 100755 source/mir/generator/Generator.java create mode 100755 source/mir/generator/GeneratorExc.java create mode 100755 source/mir/generator/GeneratorFailure.java create mode 100755 source/mir/generator/WriterEngine.java create mode 100755 source/mir/misc/PDFUtil.java create mode 100755 source/mir/producer/AssignmentProducerNode.java create mode 100755 source/mir/producer/CompositeProducerNode.java create mode 100755 source/mir/producer/ConditionalProducerNode.java create mode 100755 source/mir/producer/EntityBatchingProducerNode.java create mode 100755 source/mir/producer/EntityEnumeratingProducerNode.java create mode 100755 source/mir/producer/EntityListProducerNode.java create mode 100755 source/mir/producer/EntityModifyingProducerNode.java create mode 100755 source/mir/producer/EvaluatedAssignmentProducerNode.java create mode 100755 source/mir/producer/ExpandedAssignmentProducerNode.java create mode 100755 source/mir/producer/FileDateSettingProducerNode.java create mode 100755 source/mir/producer/FileDeletingProducerNode.java create mode 100755 source/mir/producer/FileOperationProducerNode.java create mode 100755 source/mir/producer/GeneratingProducerNode.java create mode 100755 source/mir/producer/LoggingProducerNode.java create mode 100755 source/mir/producer/NodedProducer.java create mode 100755 source/mir/producer/NodedProducerFactory.java create mode 100755 source/mir/producer/Producer.java create mode 100755 source/mir/producer/ProducerExc.java create mode 100755 source/mir/producer/ProducerFactory.java create mode 100755 source/mir/producer/ProducerFailure.java create mode 100755 source/mir/producer/ProducerNode.java create mode 100755 source/mir/producer/ProducerNodeDecorator.java create mode 100755 source/mir/producer/ResourceBundleProducerNode.java create mode 100755 source/mir/producer/ScriptCallingProducerNode.java create mode 100755 source/mir/producer/ValuesMapProducerNode.java create mode 100755 source/mir/producer/reader/DefaultProducerNodeBuilders.java create mode 100755 source/mir/producer/reader/ProducerConfigExc.java create mode 100755 source/mir/producer/reader/ProducerConfigFailure.java create mode 100755 source/mir/producer/reader/ProducerConfigReader.java create mode 100755 source/mir/producer/reader/ProducerNodeBuilder.java create mode 100755 source/mir/producer/reader/ProducerNodeBuilderLibrary.java create mode 100755 source/mir/producer/reader/ReaderTool.java create mode 100755 source/mir/producer/reader/ScriptedProducerFactory.java create mode 100755 source/mir/producer/reader/ScriptedProducerNode.java create mode 100755 source/mir/producer/reader/ScriptedProducerNodeDefinition.java create mode 100755 source/mir/producer/reader/ScriptedProducerNodeTool.java create mode 100755 source/mir/util/CachingRewindableIterator.java create mode 100755 source/mir/util/DateToMapAdapter.java create mode 100755 source/mir/util/FileMonitor.java create mode 100755 source/mir/util/GeneratorHTMLFunctions.java create mode 100755 source/mir/util/NullWriter.java create mode 100755 source/mir/util/ParameterExpander.java create mode 100755 source/mir/util/ResourceBundleGeneratorFunction.java create mode 100755 source/mir/util/RewindableIterator.java create mode 100755 source/mircoders/global/JobQueue.java create mode 100755 source/mircoders/global/MirGlobal.java create mode 100755 source/mircoders/global/ProducerEngine.java create mode 100755 source/mircoders/localizer/MirAdminInterfaceLocalizer.java create mode 100755 source/mircoders/localizer/MirCachingLocalizerDecorator.java create mode 100755 source/mircoders/localizer/MirDataModelLocalizer.java create mode 100755 source/mircoders/localizer/MirGeneratorLocalizer.java create mode 100755 source/mircoders/localizer/MirLocalizer.java create mode 100755 source/mircoders/localizer/MirLocalizerException.java create mode 100755 source/mircoders/localizer/MirLocalizerFailure.java create mode 100755 source/mircoders/localizer/MirOpenPostingLocalizer.java create mode 100755 source/mircoders/localizer/MirProducerAssistantLocalizer.java create mode 100755 source/mircoders/localizer/MirProducerLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicAdminInterfaceLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicDataModelLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicGeneratorLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicProducerAssistantLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicProducerLocalizer.java create mode 100755 source/mircoders/localizer/basic/MirBasicWriterEngine.java create mode 100755 source/mircoders/producer/CompositeProducer.java create mode 100755 source/mircoders/producer/CompositeProducerFactory.java create mode 100755 source/mircoders/producer/ContentMarkingProducerNode.java create mode 100755 source/mircoders/producer/ContentModifyingProducerNode.java create mode 100755 source/mircoders/producer/IndexingProducerNode.java create mode 100755 source/mircoders/producer/MediaGeneratingProducerNode.java create mode 100755 source/mircoders/producer/OldProducerAdapter.java create mode 100755 source/mircoders/producer/OldProducerAdapterFactory.java create mode 100755 source/mircoders/producer/PDFGeneratingProducerNode.java delete mode 100755 source/mircoders/producer/ProducerAll.java delete mode 100755 source/mircoders/producer/ProducerContent.java delete mode 100755 source/mircoders/producer/ProducerFeature.java delete mode 100755 source/mircoders/producer/ProducerList.java delete mode 100755 source/mircoders/producer/ProducerNavigation.java delete mode 100755 source/mircoders/producer/ProducerOpenPosting.java delete mode 100755 source/mircoders/producer/ProducerStartPage.java delete mode 100755 source/mircoders/producer/ProducerTopics.java create mode 100755 source/mircoders/producer/reader/SupplementalProducerNodeBuilders.java create mode 100755 source/mircoders/servlet/ServletModuleLocalizer.java delete mode 100755 templates-dist/de/open/comment.template delete mode 100755 templates-dist/de/open/comment_done.template delete mode 100755 templates-dist/de/open/comment_dupe.template delete mode 100755 templates-dist/de/open/comment_en.template delete mode 100755 templates-dist/de/open/posting.template delete mode 100755 templates-dist/de/open/posting_done.template delete mode 100755 templates-dist/de/open/posting_dupe.template delete mode 100755 templates-dist/de/open/posting_en.template delete mode 100755 templates-dist/en/open/comment.template delete mode 100755 templates-dist/en/open/comment.template.en delete mode 100755 templates-dist/en/open/comment_done.template delete mode 100755 templates-dist/en/open/comment_dupe.template delete mode 100755 templates-dist/en/open/minimal_posting.template delete mode 100755 templates-dist/en/open/posting.template delete mode 100755 templates-dist/en/open/posting.template.en delete mode 100755 templates-dist/en/open/posting_done.template delete mode 100755 templates-dist/en/open/posting_dupe.template delete mode 100755 templates-dist/nl/open/comment.template delete mode 100755 templates-dist/nl/open/comment_done.template delete mode 100755 templates-dist/nl/open/comment_dupe.template delete mode 100755 templates-dist/nl/open/posting.template delete mode 100755 templates-dist/nl/open/posting_done.template delete mode 100755 templates-dist/nl/open/posting_dupe.template delete mode 100755 templates-dist/open/comment_en.template delete mode 100755 templates-dist/open/posting_en.template create mode 100755 templates-dist/producer/producers.xml diff --git a/bundles/admin_ay.properties b/bundles/admin_ay.properties new file mode 100755 index 00000000..beac4786 --- /dev/null +++ b/bundles/admin_ay.properties @@ -0,0 +1,382 @@ +########## admin ########## + +# general +yes=sí +no=no +dontcare=no importa +all=todo + +# actions +insert=insertar +save=guardar +edit=editar +delete=borrar +add=añadir +filter=filtrar +attach=vincular +list=mostrar lista +back=atrás +cancel=cancelar + +# records +records=registros +show_from_to=mostrando entradas desde {0} hasta {1} +no_matches_found=No se encontraron resultados! +list.next=siguiente +list.previous=anterior + +# media - used by image, audio, video and other media +media.created=creado +media.changed=cambiado +media.published=publicado +media.format=Formato +media.rights=Copyright +media.type=Tipo +media.mediafolder=Carpeta de recursos mediáticos +media.title=Título +media.description=Descripción +media.date=Fecha +media.location=Lugar de origen +media.creator=Creador +media.keywords=Palabras clave +media.comment=Comentario +media.source=Origen +media.is_published=Disponible para publicación +media.icon=Icono +medialist.search_text_in=Buscar texto en + +# image +image.htmltitle=indymedia | imagen +imagelist.htmltitle=indymedia | lista de imagenes + +# audio +audio.htmltitle=indymedia | audio +audiolist.htmltitle=indymedia | lista de audio + +# video +video.htmltitle=indymedia | video +videolist.htmltitle=indymedia | lista de video + +# other +other_media.htmltitle=indymedia | otros recursos mediáticos +other_media.htmltitle=indymedia | lista de recursos mediáticos + +# breaking +breaking.htmltitle=indymedia | últimas noticias +breakinglist.htmltitle=indymedia | listado de últimas noticias +breaking.textinfo=(max. 5 líneas / 250 caracteres) +breaking.text=texto +breaking.date=fecha + + +# comment +comment.htmltitle=indymedia | commentario +comment.date=fecha +comment.title=título +comment.published=publicado +comment.text=texto-del-comentario +comment.address=dirección +comment.phone=teléfono +comment.email=email +comment.url=url +comment.creator=autor +comment.article=del artículo + +commentlist.htmltitle=indymedia | lista de comentarios +commentlist.produced=producido +commentlist.hidden=oculto +commentlist.search=buscar! + +# confirm +confirm.htmltitle=indymedia | confirmar borrado +confirm.really_delete=Desea borrar esta entrada? + +# content +content.htmltitle=indymedia | contenido +content.owner=Propietario +content.topic=Tema +content.feature=Característica +content.title=titulo largo +content.subtitle=subtítulo/título de contexto +content.location=Lugar de origen +content.creator=autor +content.creator.email=Email +content.creator.url=Web +content.creator.address=Dirección +content.creator.telephone=Teléfono +content.abstract=Descripción breve +content.content=Contenido +content.html=es HTML? +content.comment=Comentario interno +content.internal=(propósito interno) +content.attachments=Vínculos +content.images=Imágenes +content.audio=Audio +content.video=Video +content.other=Otros recursos mediáticos +content.media=Recursos mediáticos +content.addimage=añadir imagen +content.addaudio=añadir audio +content.addvideo=añadir video +content.addother=añadir otros recursos mediáticos +content.creationdate=fecha +content.modificationdate=último cambio +content.status=Estado +content.type=Tipo de artículo + +contentlist.htmltitle=indymedia | lista de contenidos + +# language +language.htmltitle=indymedia | idiomas +language.name=idioma +language.code=Código del idioma + +languagelist.htmltitle=indymedia | lista de idiomas + +# imcs +linkimcs.htmltitle=indymedia | Enlaces a IMCs +linkimcs.name=Nombre +linkimcs.continent=Continente +linkimcs.new_parent=Nuevo Ancestro +linkimcs.url=URL +linkimcs.sort_by=Criterio de orden +linkimcs.language=Idioma +linkimcs.parent=ancestro + +linkimcslist.htmltitle=indymedia | Lista de enlaces a IMCs +linkimcslist.search_in=Buscar texto en + +# login +login.htmltitle=indymedia | registro +login.info=Esta zona es accesible exclusivamente a grupos autorizados. Si desea colaborar como editor, por favor contacte con nosotros en +login.title=login +login.name=Login +login.password=Password +login.language=Idioma +login.language.german=Alem‡n +login.language.spanish=Espa–ol +login.language.guarani=Guaran’ +login.language.aymara=Aymara +login.language.quechua=Quechua +login.submit= Envíar + +# mediafolder +mediafolder.htmltitle=indymedia | carpeta de recursos mediáticos +mediafolder.date=fecha +mediafolder.name=nombre +mediafolder.location=lugar de origen +mediafolder.keywords=palabras clave +mediafolder.comment=commentario + +mediafolderlist.htmltitle=indymedia | lista de carpetas de recursos mediáticos + +# message +message.htmltitle=indymedia | mensajes +message.date=fecha +message.title=título +message.creator=Autor +message.text=texto +message.textinfo=(max. 5 líneas / 250 caracteres) + +messagelist.htmltitle=indymedia | lista de mensajes + +# feature +feature.htmltitle=indymedia | presentación +feature.title=Título +feature.published=publicado +feature.is_published=está publicado +feature.is_not_published=no está publicado +feature.filename=Nombre del fichero +feature.abstract=Descripción breve +feature.link=Enlace + +featurelist.htmltitle=indymedia | lista de presentaciones + +# admin start page +start.htmltitle=indymedia | administración +start.openpostings.title=ENVÍOSABIERTOS +start.comments.title=COMENTARIOS +start.breaking.title=ÚLTIMAS NOTICIAS +start.breaking.new=últimas noticias recientes +start.content.title=ARTICULOS +start.content.new=nuevos artículos +start.show=mostrar +start.content.newswire=newswire +start.content.feature=presentación +start.content.topicspecial=especiales-por-tema +start.content.startspecial=especiales-página-de-inicio +start.content.not_published=artículos aun sin publicar +start.content.with_media=con recursos mediáticos +start.content.last_changes=últimos cambios +start.content.with_comments=con comentarios de propósito interno +start.content.search=buscar +start.generate.title=GENERAR MANUALMENTE +start.generate.all.title=todas las areas +start.generate.all.new=todo nuevo (estándar, actualización en la web > 5min.) +start.generate.parts.title=partes específicas del website +start.generate.startpages.new=nueva página de inicio +start.generate.all_forced=todos (forzado) +start.generate.all_sync=todos (forzado y sincronizado) +start.generate.content.new=nuevo contenido +start.generate.topics.new=nuevos temas +start.generate.postings.new=nuevos envios abiertos +start.generate.images.new=nuevas imágenes +start.generate.audio.new=nuevos ficheros de audio +start.generate.video.new=nuevos ficheros de video +start.generate.other.new=otros recursos mediáticos nuevos +start.generate.navigation=navegación +start.coverage.title=COBERTURA +start.topics.title=TEMAS +start.images.title=MATERIAL GRAFICO +start.mediafolder.title=CARPETA DE RECURSOS MEDIATICOS +start.languages.title=TÍTULO +start.imcs.title=IMCS +start.messageboard.title=Mensajería Interna +start.messageboard.no_messages=no hay mensajes + +# topic +topic.htmltitle=indymedia | tema +topic.title=Nombre +topic.description=descripción +topic.filename=Nombre del fichero +topic.main_url=página de información principal +topic.archive_url=URL del archivo + +topiclist.htmltitle=indymedia | lista de temas + +# users +user.htmltitle=indymedia | usuario +user.login=login +user.password=password +user.admin=administrador + +userlist.htmltitle=indymedia | lista de usuarios + +# head +head.start=inicio +head.logout=salir +head.help=ayuda +head.search=buscar +head.logged_in=estás registrado + +# foot +foot.top=arriba + + +########## error ########## + +error.htmltitle=indymedia | error +error.title=oi! oi! oi! +error.text=El siguiente mensaje puede que no le sea de mucha ayuda, pero seguramente lo será para {1}: +error.text2=Por favor envíe un mensaje con el texto que aparece en rojo y una descripción detallada a la siguiente dirección {1}. Gracias! + + +usererror.htmltitle=indymedia | error de datos +usererror.title=oi! oi! oi! +usererror.text=Los datos que ha introducido han causado el siguiente error: +usererror.what_to_do=Por favor, pulse el botón para volver e intentelo de nuevo + + +########## producer ########## + +producer.content.htmltitle=mir.indymedia: +producer.content.email=email +producer.content.homepage=Homepage +producer.content.comment=Haga un comentario sobre este artículo +producer.copyright= Copyright © 2002 Indymedia Bolivia - Qollasuyu - Ivi Iyambae
Esta publicaci—n es copyleft. Por tanto, se permite difundir, citar y copiar literalmente sus materiales, de forma ’ntegra o parcial, por cualquier medio y para cualquier prop—sito, siempre que se mantenga esta nota y se cite procedencia. Indymedia Euskal Herria no asume ninguna responsabilidad por el material publicado en este sitio, salvo sobre lo que aparece en la columna central. Toda la responsabilidad para verificar la veracidad y los derechos de reproducci—n de un env’o corresponden al autor que lo publica. Al publicar material en este sitio, el o la autora del env’o asume que puede ser redistribuido libremente. +producer.contact=Contacto +producer.openposting.htmltitle=mir.indymedia: +producer.startpage.htmltitle=mir.indymedia: the mir-coders-website +producer.topiclist.htmltitle=mir.indymedia: + + +########## open ########## + +open.optional=opcional +open.required=requerido + +open.comment.htmltitle=indymedia | confirmar comentario +open.comment.title=Añadiendo un comentario al artículo +open.comment.note=Unos comentarios sobre como escribir un comentario en Indymedia. +open.comment.formtitle=Formulario-del-comentario +open.comment.title=Título del comentario +open.comment.name=su nombre +open.comment.email=su email +open.comment.url=su dirección web +open.comment.phone=su número de teléfono +open.comment.address=su dirección +open.comment.language=idioma de su comentario +open.comment.text=su comentario +open.comment.submit=envíar comentario +open.comment.reset=limpiar formulario + + +open.commentdone.htmltitle=indymedia | envío abierto +open.commentdone.thanks=Su comentario está de camino! +open.commentdone.wait=In wenigen Minuten ist Deine Ergänzung unter dem ergänzten Artikel.
Manchmal kann es aber aufgrund technischer Probleme etwas dauern bis sie erscheint.
+open.commentdone.criteria=Die Moderationskriterien von indymedia kannst Du hier nachlesen. +open.commentdone.stay_calm=Gedulde Dich einen Moment - Es lohnt sich! +open.commentdone.back=Zurück zum kommentierten Artikel + + +open.commentdupe.htmltitle=indymedia | envío abierto - comentario duplicado +open.commentdupe.title=Mantenga la calma unos breves instantes. +open.commentdupe.explanation=Probablemente haya apretado el botón de recarga o enviado el comentario en una segunda ocasión. Si puede leer esto, significa que su comentario ha sido recibido con éxito y será incluido en la página de artículos en breves instantes. +open.commentdupe.no_panic=No se altere! +open.commentdupe.back=Volver al artículo comentado + + +open.posting.htmltitle=indymedia | envío abierto +open.posting.meta.description=Indymedia | Centro de Periodismo Independiente +open.posting.meta.author=Colectivo IMC +open.posting.meta.keywords=Periodismo Libre +open.posting.jump_to_form=Ir directamente al formulario. +open.posting.title=Publique su artículo +open.posting.nr_of_media=Número de recursos mediáticos +open.posting.nr_of_media.info=(wenn Du mehr als eine Datei hochladen willst, bitte hier die Anzahl eintragen und den Knopf drücken, bevor Du weitere Felder ausfüllst.) +open.posting.nr_of_media.submit=Anzahl festlegen +open.posting.form.title=Formulario de publicación +open.posting.title=Título de su artículo +open.posting.title.info=(Bitte wähle einen möglichst klaren, aussagekräftigen Titel.) +open.posting.topic=Thema Deines Beitrags +open.posting.topic.info=(Mehrfachwahl ist möglich. Bitte dazu die [Strg]- bzw [Ctrl]-Taste benutzen) +open.posting.author=autor de este artículo +open.posting.abstract=Descripción breve de su artículo +open.posting.abstract.info=(Sie soll den LeserInnen schnell vermitteln, worum es in Deinem Beitrag geht. Falls Du den ersten Absatz Deines Artikels dazu wählst, achte bitte darauf ihn im Haupttextfeld weiter unten nicht nochmal einzusetzen.) +open.posting.abstract.constraint=(que no exceda de 5 líneas) +open.posting.contact.info=La información de contacto es opcional pero ayuda a que otras personas puedan ponerse en contacto con usted. +open.posting.email=su dirección de email +open.posting.url=su dirección web +open.posting.address=su dirección personal +open.posting.phone=su número de teléfono +open.posting.language=idioma de su artículo +open.posting.text=su artículo +open.posting.text.info=ponga aquí el texto de su artículo +open.posting.media=recursos mediáticos +open.posting.media.info=cargue sus ficheros de medios (de momento tan solo se aceptan jpg|gif|mp3|avi|qt|mpeg) +open.posting.media.howto=(Dazu wählst Du mit "Durchsuchen.." die entsprechende Datei auf Deiner Festplatte aus. +open.posting.media.media=Media +open.posting.media.title=subtítulo de los recursos mediáticos +open.posting.submit.info=Bitte drücke den Verschicken-Knopf nur einmal!
In wenigen Minuten erscheind Dein Beitrag dann auf der "Open Posting" - Seite.   Das ist nicht die Startseite.
Manchmal kann es aber aufgrund technischer Probleme etwas dauern bis er erscheint. +open.posting.criteria=Die Moderationskriterien kannst Du hier nachlesen +open.posting.submit=Enviar (la paciencia es una virtud!!) +open.posting.reset=Limpiar formulario + + +open.postingdone.htmltitle=indymedia | open posting +open.postingdone.title=Hurra, Du hast Deinen Artikel abgeschickt! +open.postingdone.info=Dein Artikel landet in einigen Minuten auf der "Open Posting" - Seite. Das ist nicht die Startseite. Die Moderationskriterien kannst Du hier lesen +open.postingdone.stay_calm=Gedulde Dich einen Moment! Es lohnt sich! +open.postingdone.back=Zurück + + +open.postingdupe.htmltitle=indymedia | envío abierto - envio duplicado +open.postingdupe.title=Mantenga la calma, su envio será procesado en breves instantes. +open.postingdupe.explanation= Posiblemente haya pulsado el botón de recarga de su navegador, o enviado su articulo por segunda vez + Si está leyendo este texto, significa que su envío ha sido recibido con éxito + y que será incluido en la pagina de artículos en breves instantes. +
+ Como detalle técnico, este imc está conectado a un servidor proxy + que no es actualizado cada minuto. +open.postingdupe.no_panic=No pierda los estribos! +open.postingdupe.back=Atrás diff --git a/bundles/admin_en.properties b/bundles/admin_en.properties index 12221188..03826813 100755 --- a/bundles/admin_en.properties +++ b/bundles/admin_en.properties @@ -1,5 +1,5 @@ ########## admin ########## -# $Id: admin_en.properties,v 1.14 2002/08/09 17:51:53 init Exp $ +# $Id: admin_en.properties,v 1.15 2002/08/25 19:00:06 mh Exp $ # general yes=yes @@ -291,32 +291,6 @@ usererror.text=Your input caused the following error: usererror.what_to_do=Please press the back button and try it again -########## producer ########## - -producer.content.htmltitle=mir.indymedia.de: -producer.content.email=eMail -producer.content.homepage=Homepage -producer.content.comment=Make a quick comment on this article - -producer.copyright=Jegliche Inhalte, die bei germany.indymedia -veröffentlicht werden, bleiben Eigentum der Autorin/ des Autors. Soweit -nicht anders vermerkt, können und sollen sie weiterverwertet werden. -germany.indymedia übernimmt keine Gewähr für die Inhalte.
Eine - -spezielle Form des Copyrights wird diskutiert und folgt. - -producer.contact=Kontakt - -producer.openposting.htmltitle=mir.indymedia.de: - -producer.startpage.htmltitle=mir.indymedia.de: the mir-coders-website - -producer.topiclist.htmltitle=mir.indymedia.de: - -producer.previous=previous page -producer.next=next page - - ########## open ########## open.optional=optional diff --git a/bundles/admin_es.properties b/bundles/admin_es.properties index 6510e992..31b17e07 100755 --- a/bundles/admin_es.properties +++ b/bundles/admin_es.properties @@ -1,5 +1,5 @@ ########## admin ########## -# $Id: admin_es.properties,v 1.3 2002/07/01 21:50:22 mh Exp $ +# $Id: admin_es.properties,v 1.4 2002/08/25 19:00:06 mh Exp $ # general yes=sí @@ -154,9 +154,12 @@ login.title=login login.name=Login login.password=Password login.language=Idioma -login.language.english=Inglés -login.language.german=Alemán -login.language.spanish=Español +login.language.english=InglŽs +login.language.german=Alem‡n +login.language.spanish=Espa–ol +login.language.guarani=Guaran’ +login.language.aymara=Aymara +login.language.quechua=Quechua login.submit= Envíar # mediafolder @@ -285,7 +288,7 @@ producer.content.htmltitle=mir.indymedia: producer.content.email=email producer.content.homepage=Homepage producer.content.comment=Haga un comentario sobre este artículo -producer.copyright=Jegliche Inhalte, die bei germany.indymedia veröffentlicht werden, bleiben Eigentum der Autorin/ des Autors. Soweit nicht anders vermerkt, können und sollen sie weiterverwertet werden. germany.indymedia übernimmt keine Gewähr für die Inhalte.
Eine spezielle Form des Copyrights wird diskutiert und folgt. +producer.copyright= Copyright © 2002 Indymedia Bolivia - Qollasuyu - Ivi Iyambae
Esta publicaci—n es copyleft. Por tanto, se permite difundir, citar y copiar literalmente sus materiales, de forma ’ntegra o parcial, por cualquier medio y para cualquier prop—sito, siempre que se mantenga esta nota y se cite procedencia. Indymedia Euskal Herria no asume ninguna responsabilidad por el material publicado en este sitio, salvo sobre lo que aparece en la columna central. Toda la responsabilidad para verificar la veracidad y los derechos de reproducci—n de un env’o corresponden al autor que lo publica. Al publicar material en este sitio, el o la autora del env’o asume que puede ser redistribuido libremente. producer.contact=Contacto producer.openposting.htmltitle=mir.indymedia: producer.startpage.htmltitle=mir.indymedia: the mir-coders-website diff --git a/bundles/admin_gn.properties b/bundles/admin_gn.properties new file mode 100755 index 00000000..f8376a1c --- /dev/null +++ b/bundles/admin_gn.properties @@ -0,0 +1,383 @@ +########## admin ########## + +# general +yes=sí +no=no +dontcare=no importa +all=todo + +# actions +insert=insertar +save=guardar +edit=editar +delete=borrar +add=añadir +filter=filtrar +attach=vincular +list=mostrar lista +back=atrás +cancel=cancelar + +# records +records=registros +show_from_to=mostrando entradas desde {0} hasta {1} +no_matches_found=No se encontraron resultados! +list.next=siguiente +list.previous=anterior + +# media - used by image, audio, video and other media +media.created=creado +media.changed=cambiado +media.published=publicado +media.format=Formato +media.rights=Copyright +media.type=Tipo +media.mediafolder=Carpeta de recursos mediáticos +media.title=Título +media.description=Descripción +media.date=Fecha +media.location=Lugar de origen +media.creator=Creador +media.keywords=Palabras clave +media.comment=Comentario +media.source=Origen +media.is_published=Disponible para publicación +media.icon=Icono +medialist.search_text_in=Buscar texto en + +# image +image.htmltitle=indymedia | imagen +imagelist.htmltitle=indymedia | lista de imagenes + +# audio +audio.htmltitle=indymedia | audio +audiolist.htmltitle=indymedia | lista de audio + +# video +video.htmltitle=indymedia | video +videolist.htmltitle=indymedia | lista de video + +# other +other_media.htmltitle=indymedia | otros recursos mediáticos +other_media.htmltitle=indymedia | lista de recursos mediáticos + +# breaking +breaking.htmltitle=indymedia | últimas noticias +breakinglist.htmltitle=indymedia | listado de últimas noticias +breaking.textinfo=(max. 5 líneas / 250 caracteres) +breaking.text=texto +breaking.date=fecha + + +# comment +comment.htmltitle=indymedia | commentario +comment.date=fecha +comment.title=título +comment.published=publicado +comment.text=texto-del-comentario +comment.address=dirección +comment.phone=teléfono +comment.email=email +comment.url=url +comment.creator=autor +comment.article=del artículo + +commentlist.htmltitle=indymedia | lista de comentarios +commentlist.produced=producido +commentlist.hidden=oculto +commentlist.search=buscar! + +# confirm +confirm.htmltitle=indymedia | confirmar borrado +confirm.really_delete=Desea borrar esta entrada? + +# content +content.htmltitle=indymedia | contenido +content.owner=Propietario +content.topic=Tema +content.feature=Característica +content.title=titulo largo +content.subtitle=subtítulo/título de contexto +content.location=Lugar de origen +content.creator=autor +content.creator.email=Email +content.creator.url=Web +content.creator.address=Dirección +content.creator.telephone=Teléfono +content.abstract=Descripción breve +content.content=Contenido +content.html=es HTML? +content.comment=Comentario interno +content.internal=(propósito interno) +content.attachments=Vínculos +content.images=Imágenes +content.audio=Audio +content.video=Video +content.other=Otros recursos mediáticos +content.media=Recursos mediáticos +content.addimage=añadir imagen +content.addaudio=añadir audio +content.addvideo=añadir video +content.addother=añadir otros recursos mediáticos +content.creationdate=fecha +content.modificationdate=último cambio +content.status=Estado +content.type=Tipo de artículo + +contentlist.htmltitle=indymedia | lista de contenidos + +# language +language.htmltitle=indymedia | idiomas +language.name=idioma +language.code=Código del idioma + +languagelist.htmltitle=indymedia | lista de idiomas + +# imcs +linkimcs.htmltitle=indymedia | Enlaces a IMCs +linkimcs.name=Nombre +linkimcs.continent=Continente +linkimcs.new_parent=Nuevo Ancestro +linkimcs.url=URL +linkimcs.sort_by=Criterio de orden +linkimcs.language=Idioma +linkimcs.parent=ancestro + +linkimcslist.htmltitle=indymedia | Lista de enlaces a IMCs +linkimcslist.search_in=Buscar texto en + +# login +login.htmltitle=indymedia | registro +login.info=Esta zona es accesible exclusivamente a grupos autorizados. Si desea colaborar como editor, por favor contacte con nosotros en +login.title=login +login.name=Login +login.password=Password +login.language=Idioma +login.language.english=InglŽs +login.language.german=Alem‡n +login.language.spanish=Espa–ol +login.language.guarani=Guaran’ +login.language.aymara=Aymara +login.language.quechua=Quechua +login.submit= Envíar + +# mediafolder +mediafolder.htmltitle=indymedia | carpeta de recursos mediáticos +mediafolder.date=fecha +mediafolder.name=nombre +mediafolder.location=lugar de origen +mediafolder.keywords=palabras clave +mediafolder.comment=commentario + +mediafolderlist.htmltitle=indymedia | lista de carpetas de recursos mediáticos + +# message +message.htmltitle=indymedia | mensajes +message.date=fecha +message.title=título +message.creator=Autor +message.text=texto +message.textinfo=(max. 5 líneas / 250 caracteres) + +messagelist.htmltitle=indymedia | lista de mensajes + +# feature +feature.htmltitle=indymedia | presentación +feature.title=Título +feature.published=publicado +feature.is_published=está publicado +feature.is_not_published=no está publicado +feature.filename=Nombre del fichero +feature.abstract=Descripción breve +feature.link=Enlace + +featurelist.htmltitle=indymedia | lista de presentaciones + +# admin start page +start.htmltitle=indymedia | administración +start.openpostings.title=ENVÍOSABIERTOS +start.comments.title=COMENTARIOS +start.breaking.title=ÚLTIMAS NOTICIAS +start.breaking.new=últimas noticias recientes +start.content.title=ARTICULOS +start.content.new=nuevos artículos +start.show=mostrar +start.content.newswire=newswire +start.content.feature=presentación +start.content.topicspecial=especiales-por-tema +start.content.startspecial=especiales-página-de-inicio +start.content.not_published=artículos aun sin publicar +start.content.with_media=con recursos mediáticos +start.content.last_changes=últimos cambios +start.content.with_comments=con comentarios de propósito interno +start.content.search=buscar +start.generate.title=GENERAR MANUALMENTE +start.generate.all.title=todas las areas +start.generate.all.new=todo nuevo (estándar, actualización en la web > 5min.) +start.generate.parts.title=partes específicas del website +start.generate.startpages.new=nueva página de inicio +start.generate.all_forced=todos (forzado) +start.generate.all_sync=todos (forzado y sincronizado) +start.generate.content.new=nuevo contenido +start.generate.topics.new=nuevos temas +start.generate.postings.new=nuevos envios abiertos +start.generate.images.new=nuevas imágenes +start.generate.audio.new=nuevos ficheros de audio +start.generate.video.new=nuevos ficheros de video +start.generate.other.new=otros recursos mediáticos nuevos +start.generate.navigation=navegación +start.coverage.title=COBERTURA +start.topics.title=TEMAS +start.images.title=MATERIAL GRAFICO +start.mediafolder.title=CARPETA DE RECURSOS MEDIATICOS +start.languages.title=TÍTULO +start.imcs.title=IMCS +start.messageboard.title=Mensajería Interna +start.messageboard.no_messages=no hay mensajes + +# topic +topic.htmltitle=indymedia | tema +topic.title=Nombre +topic.description=descripción +topic.filename=Nombre del fichero +topic.main_url=página de información principal +topic.archive_url=URL del archivo + +topiclist.htmltitle=indymedia | lista de temas + +# users +user.htmltitle=indymedia | usuario +user.login=login +user.password=password +user.admin=administrador + +userlist.htmltitle=indymedia | lista de usuarios + +# head +head.start=inicio +head.logout=salir +head.help=ayuda +head.search=buscar +head.logged_in=estás registrado + +# foot +foot.top=arriba + + +########## error ########## + +error.htmltitle=indymedia | error +error.title=oi! oi! oi! +error.text=El siguiente mensaje puede que no le sea de mucha ayuda, pero seguramente lo será para {1}: +error.text2=Por favor envíe un mensaje con el texto que aparece en rojo y una descripción detallada a la siguiente dirección {1}. Gracias! + + +usererror.htmltitle=indymedia | error de datos +usererror.title=oi! oi! oi! +usererror.text=Los datos que ha introducido han causado el siguiente error: +usererror.what_to_do=Por favor, pulse el botón para volver e intentelo de nuevo + + +########## producer ########## + +producer.content.htmltitle=mir.indymedia: +producer.content.email=email +producer.content.homepage=Homepage +producer.content.comment=Haga un comentario sobre este artículo +producer.copyright= Copyright © 2002 Indymedia Bolivia - Qollasuyu - Ivi Iyambae
Esta publicaci—n es copyleft. Por tanto, se permite difundir, citar y copiar literalmente sus materiales, de forma ’ntegra o parcial, por cualquier medio y para cualquier prop—sito, siempre que se mantenga esta nota y se cite procedencia. Indymedia Euskal Herria no asume ninguna responsabilidad por el material publicado en este sitio, salvo sobre lo que aparece en la columna central. Toda la responsabilidad para verificar la veracidad y los derechos de reproducci—n de un env’o corresponden al autor que lo publica. Al publicar material en este sitio, el o la autora del env’o asume que puede ser redistribuido libremente. +producer.contact=Contacto +producer.openposting.htmltitle=mir.indymedia: +producer.startpage.htmltitle=mir.indymedia: the mir-coders-website +producer.topiclist.htmltitle=mir.indymedia: + + +########## open ########## + +open.optional=opcional +open.required=requerido + +open.comment.htmltitle=indymedia | confirmar comentario +open.comment.title=Añadiendo un comentario al artículo +open.comment.note=Unos comentarios sobre como escribir un comentario en Indymedia. +open.comment.formtitle=Formulario-del-comentario +open.comment.title=Título del comentario +open.comment.name=su nombre +open.comment.email=su email +open.comment.url=su dirección web +open.comment.phone=su número de teléfono +open.comment.address=su dirección +open.comment.language=idioma de su comentario +open.comment.text=su comentario +open.comment.submit=envíar comentario +open.comment.reset=limpiar formulario + + +open.commentdone.htmltitle=indymedia | envío abierto +open.commentdone.thanks=Su comentario está de camino! +open.commentdone.wait=In wenigen Minuten ist Deine Ergänzung unter dem ergänzten Artikel.
Manchmal kann es aber aufgrund technischer Probleme etwas dauern bis sie erscheint.
+open.commentdone.criteria=Die Moderationskriterien von indymedia kannst Du hier nachlesen. +open.commentdone.stay_calm=Gedulde Dich einen Moment - Es lohnt sich! +open.commentdone.back=Zurück zum kommentierten Artikel + + +open.commentdupe.htmltitle=indymedia | envío abierto - comentario duplicado +open.commentdupe.title=Mantenga la calma unos breves instantes. +open.commentdupe.explanation=Probablemente haya apretado el botón de recarga o enviado el comentario en una segunda ocasión. Si puede leer esto, significa que su comentario ha sido recibido con éxito y será incluido en la página de artículos en breves instantes. +open.commentdupe.no_panic=No se altere! +open.commentdupe.back=Volver al artículo comentado + + +open.posting.htmltitle=indymedia | envío abierto +open.posting.meta.description=Indymedia | Centro de Periodismo Independiente +open.posting.meta.author=Colectivo IMC +open.posting.meta.keywords=Periodismo Libre +open.posting.jump_to_form=Ir directamente al formulario. +open.posting.title=Publique su artículo +open.posting.nr_of_media=Número de recursos mediáticos +open.posting.nr_of_media.info=(wenn Du mehr als eine Datei hochladen willst, bitte hier die Anzahl eintragen und den Knopf drücken, bevor Du weitere Felder ausfüllst.) +open.posting.nr_of_media.submit=Anzahl festlegen +open.posting.form.title=Formulario de publicación +open.posting.title=Título de su artículo +open.posting.title.info=(Bitte wähle einen möglichst klaren, aussagekräftigen Titel.) +open.posting.topic=Thema Deines Beitrags +open.posting.topic.info=(Mehrfachwahl ist möglich. Bitte dazu die [Strg]- bzw [Ctrl]-Taste benutzen) +open.posting.author=autor de este artículo +open.posting.abstract=Descripción breve de su artículo +open.posting.abstract.info=(Sie soll den LeserInnen schnell vermitteln, worum es in Deinem Beitrag geht. Falls Du den ersten Absatz Deines Artikels dazu wählst, achte bitte darauf ihn im Haupttextfeld weiter unten nicht nochmal einzusetzen.) +open.posting.abstract.constraint=(que no exceda de 5 líneas) +open.posting.contact.info=La información de contacto es opcional pero ayuda a que otras personas puedan ponerse en contacto con usted. +open.posting.email=su dirección de email +open.posting.url=su dirección web +open.posting.address=su dirección personal +open.posting.phone=su número de teléfono +open.posting.language=idioma de su artículo +open.posting.text=su artículo +open.posting.text.info=ponga aquí el texto de su artículo +open.posting.media=recursos mediáticos +open.posting.media.info=cargue sus ficheros de medios (de momento tan solo se aceptan jpg|gif|mp3|avi|qt|mpeg) +open.posting.media.howto=(Dazu wählst Du mit "Durchsuchen.." die entsprechende Datei auf Deiner Festplatte aus. +open.posting.media.media=Media +open.posting.media.title=subtítulo de los recursos mediáticos +open.posting.submit.info=Bitte drücke den Verschicken-Knopf nur einmal!
In wenigen Minuten erscheind Dein Beitrag dann auf der "Open Posting" - Seite.   Das ist nicht die Startseite.
Manchmal kann es aber aufgrund technischer Probleme etwas dauern bis er erscheint. +open.posting.criteria=Die Moderationskriterien kannst Du hier nachlesen +open.posting.submit=Enviar (la paciencia es una virtud!!) +open.posting.reset=Limpiar formulario + + +open.postingdone.htmltitle=indymedia | open posting +open.postingdone.title=Hurra, Du hast Deinen Artikel abgeschickt! +open.postingdone.info=Dein Artikel landet in einigen Minuten auf der "Open Posting" - Seite. Das ist nicht die Startseite. Die Moderationskriterien kannst Du hier lesen +open.postingdone.stay_calm=Gedulde Dich einen Moment! Es lohnt sich! +open.postingdone.back=Zurück + + +open.postingdupe.htmltitle=indymedia | envío abierto - envio duplicado +open.postingdupe.title=Mantenga la calma, su envio será procesado en breves instantes. +open.postingdupe.explanation= Posiblemente haya pulsado el botón de recarga de su navegador, o enviado su articulo por segunda vez + Si está leyendo este texto, significa que su envío ha sido recibido con éxito + y que será incluido en la pagina de artículos en breves instantes. +
+ Como detalle técnico, este imc está conectado a un servidor proxy + que no es actualizado cada minuto. +open.postingdupe.no_panic=No pierda los estribos! +open.postingdupe.back=Atrás diff --git a/bundles/admin_qu.properties b/bundles/admin_qu.properties new file mode 100755 index 00000000..f8376a1c --- /dev/null +++ b/bundles/admin_qu.properties @@ -0,0 +1,383 @@ +########## admin ########## + +# general +yes=sí +no=no +dontcare=no importa +all=todo + +# actions +insert=insertar +save=guardar +edit=editar +delete=borrar +add=añadir +filter=filtrar +attach=vincular +list=mostrar lista +back=atrás +cancel=cancelar + +# records +records=registros +show_from_to=mostrando entradas desde {0} hasta {1} +no_matches_found=No se encontraron resultados! +list.next=siguiente +list.previous=anterior + +# media - used by image, audio, video and other media +media.created=creado +media.changed=cambiado +media.published=publicado +media.format=Formato +media.rights=Copyright +media.type=Tipo +media.mediafolder=Carpeta de recursos mediáticos +media.title=Título +media.description=Descripción +media.date=Fecha +media.location=Lugar de origen +media.creator=Creador +media.keywords=Palabras clave +media.comment=Comentario +media.source=Origen +media.is_published=Disponible para publicación +media.icon=Icono +medialist.search_text_in=Buscar texto en + +# image +image.htmltitle=indymedia | imagen +imagelist.htmltitle=indymedia | lista de imagenes + +# audio +audio.htmltitle=indymedia | audio +audiolist.htmltitle=indymedia | lista de audio + +# video +video.htmltitle=indymedia | video +videolist.htmltitle=indymedia | lista de video + +# other +other_media.htmltitle=indymedia | otros recursos mediáticos +other_media.htmltitle=indymedia | lista de recursos mediáticos + +# breaking +breaking.htmltitle=indymedia | últimas noticias +breakinglist.htmltitle=indymedia | listado de últimas noticias +breaking.textinfo=(max. 5 líneas / 250 caracteres) +breaking.text=texto +breaking.date=fecha + + +# comment +comment.htmltitle=indymedia | commentario +comment.date=fecha +comment.title=título +comment.published=publicado +comment.text=texto-del-comentario +comment.address=dirección +comment.phone=teléfono +comment.email=email +comment.url=url +comment.creator=autor +comment.article=del artículo + +commentlist.htmltitle=indymedia | lista de comentarios +commentlist.produced=producido +commentlist.hidden=oculto +commentlist.search=buscar! + +# confirm +confirm.htmltitle=indymedia | confirmar borrado +confirm.really_delete=Desea borrar esta entrada? + +# content +content.htmltitle=indymedia | contenido +content.owner=Propietario +content.topic=Tema +content.feature=Característica +content.title=titulo largo +content.subtitle=subtítulo/título de contexto +content.location=Lugar de origen +content.creator=autor +content.creator.email=Email +content.creator.url=Web +content.creator.address=Dirección +content.creator.telephone=Teléfono +content.abstract=Descripción breve +content.content=Contenido +content.html=es HTML? +content.comment=Comentario interno +content.internal=(propósito interno) +content.attachments=Vínculos +content.images=Imágenes +content.audio=Audio +content.video=Video +content.other=Otros recursos mediáticos +content.media=Recursos mediáticos +content.addimage=añadir imagen +content.addaudio=añadir audio +content.addvideo=añadir video +content.addother=añadir otros recursos mediáticos +content.creationdate=fecha +content.modificationdate=último cambio +content.status=Estado +content.type=Tipo de artículo + +contentlist.htmltitle=indymedia | lista de contenidos + +# language +language.htmltitle=indymedia | idiomas +language.name=idioma +language.code=Código del idioma + +languagelist.htmltitle=indymedia | lista de idiomas + +# imcs +linkimcs.htmltitle=indymedia | Enlaces a IMCs +linkimcs.name=Nombre +linkimcs.continent=Continente +linkimcs.new_parent=Nuevo Ancestro +linkimcs.url=URL +linkimcs.sort_by=Criterio de orden +linkimcs.language=Idioma +linkimcs.parent=ancestro + +linkimcslist.htmltitle=indymedia | Lista de enlaces a IMCs +linkimcslist.search_in=Buscar texto en + +# login +login.htmltitle=indymedia | registro +login.info=Esta zona es accesible exclusivamente a grupos autorizados. Si desea colaborar como editor, por favor contacte con nosotros en +login.title=login +login.name=Login +login.password=Password +login.language=Idioma +login.language.english=InglŽs +login.language.german=Alem‡n +login.language.spanish=Espa–ol +login.language.guarani=Guaran’ +login.language.aymara=Aymara +login.language.quechua=Quechua +login.submit= Envíar + +# mediafolder +mediafolder.htmltitle=indymedia | carpeta de recursos mediáticos +mediafolder.date=fecha +mediafolder.name=nombre +mediafolder.location=lugar de origen +mediafolder.keywords=palabras clave +mediafolder.comment=commentario + +mediafolderlist.htmltitle=indymedia | lista de carpetas de recursos mediáticos + +# message +message.htmltitle=indymedia | mensajes +message.date=fecha +message.title=título +message.creator=Autor +message.text=texto +message.textinfo=(max. 5 líneas / 250 caracteres) + +messagelist.htmltitle=indymedia | lista de mensajes + +# feature +feature.htmltitle=indymedia | presentación +feature.title=Título +feature.published=publicado +feature.is_published=está publicado +feature.is_not_published=no está publicado +feature.filename=Nombre del fichero +feature.abstract=Descripción breve +feature.link=Enlace + +featurelist.htmltitle=indymedia | lista de presentaciones + +# admin start page +start.htmltitle=indymedia | administración +start.openpostings.title=ENVÍOSABIERTOS +start.comments.title=COMENTARIOS +start.breaking.title=ÚLTIMAS NOTICIAS +start.breaking.new=últimas noticias recientes +start.content.title=ARTICULOS +start.content.new=nuevos artículos +start.show=mostrar +start.content.newswire=newswire +start.content.feature=presentación +start.content.topicspecial=especiales-por-tema +start.content.startspecial=especiales-página-de-inicio +start.content.not_published=artículos aun sin publicar +start.content.with_media=con recursos mediáticos +start.content.last_changes=últimos cambios +start.content.with_comments=con comentarios de propósito interno +start.content.search=buscar +start.generate.title=GENERAR MANUALMENTE +start.generate.all.title=todas las areas +start.generate.all.new=todo nuevo (estándar, actualización en la web > 5min.) +start.generate.parts.title=partes específicas del website +start.generate.startpages.new=nueva página de inicio +start.generate.all_forced=todos (forzado) +start.generate.all_sync=todos (forzado y sincronizado) +start.generate.content.new=nuevo contenido +start.generate.topics.new=nuevos temas +start.generate.postings.new=nuevos envios abiertos +start.generate.images.new=nuevas imágenes +start.generate.audio.new=nuevos ficheros de audio +start.generate.video.new=nuevos ficheros de video +start.generate.other.new=otros recursos mediáticos nuevos +start.generate.navigation=navegación +start.coverage.title=COBERTURA +start.topics.title=TEMAS +start.images.title=MATERIAL GRAFICO +start.mediafolder.title=CARPETA DE RECURSOS MEDIATICOS +start.languages.title=TÍTULO +start.imcs.title=IMCS +start.messageboard.title=Mensajería Interna +start.messageboard.no_messages=no hay mensajes + +# topic +topic.htmltitle=indymedia | tema +topic.title=Nombre +topic.description=descripción +topic.filename=Nombre del fichero +topic.main_url=página de información principal +topic.archive_url=URL del archivo + +topiclist.htmltitle=indymedia | lista de temas + +# users +user.htmltitle=indymedia | usuario +user.login=login +user.password=password +user.admin=administrador + +userlist.htmltitle=indymedia | lista de usuarios + +# head +head.start=inicio +head.logout=salir +head.help=ayuda +head.search=buscar +head.logged_in=estás registrado + +# foot +foot.top=arriba + + +########## error ########## + +error.htmltitle=indymedia | error +error.title=oi! oi! oi! +error.text=El siguiente mensaje puede que no le sea de mucha ayuda, pero seguramente lo será para {1}: +error.text2=Por favor envíe un mensaje con el texto que aparece en rojo y una descripción detallada a la siguiente dirección {1}. Gracias! + + +usererror.htmltitle=indymedia | error de datos +usererror.title=oi! oi! oi! +usererror.text=Los datos que ha introducido han causado el siguiente error: +usererror.what_to_do=Por favor, pulse el botón para volver e intentelo de nuevo + + +########## producer ########## + +producer.content.htmltitle=mir.indymedia: +producer.content.email=email +producer.content.homepage=Homepage +producer.content.comment=Haga un comentario sobre este artículo +producer.copyright= Copyright © 2002 Indymedia Bolivia - Qollasuyu - Ivi Iyambae
Esta publicaci—n es copyleft. Por tanto, se permite difundir, citar y copiar literalmente sus materiales, de forma ’ntegra o parcial, por cualquier medio y para cualquier prop—sito, siempre que se mantenga esta nota y se cite procedencia. Indymedia Euskal Herria no asume ninguna responsabilidad por el material publicado en este sitio, salvo sobre lo que aparece en la columna central. Toda la responsabilidad para verificar la veracidad y los derechos de reproducci—n de un env’o corresponden al autor que lo publica. Al publicar material en este sitio, el o la autora del env’o asume que puede ser redistribuido libremente. +producer.contact=Contacto +producer.openposting.htmltitle=mir.indymedia: +producer.startpage.htmltitle=mir.indymedia: the mir-coders-website +producer.topiclist.htmltitle=mir.indymedia: + + +########## open ########## + +open.optional=opcional +open.required=requerido + +open.comment.htmltitle=indymedia | confirmar comentario +open.comment.title=Añadiendo un comentario al artículo +open.comment.note=Unos comentarios sobre como escribir un comentario en Indymedia. +open.comment.formtitle=Formulario-del-comentario +open.comment.title=Título del comentario +open.comment.name=su nombre +open.comment.email=su email +open.comment.url=su dirección web +open.comment.phone=su número de teléfono +open.comment.address=su dirección +open.comment.language=idioma de su comentario +open.comment.text=su comentario +open.comment.submit=envíar comentario +open.comment.reset=limpiar formulario + + +open.commentdone.htmltitle=indymedia | envío abierto +open.commentdone.thanks=Su comentario está de camino! +open.commentdone.wait=In wenigen Minuten ist Deine Ergänzung unter dem ergänzten Artikel.
Manchmal kann es aber aufgrund technischer Probleme etwas dauern bis sie erscheint.
+open.commentdone.criteria=Die Moderationskriterien von indymedia kannst Du hier nachlesen. +open.commentdone.stay_calm=Gedulde Dich einen Moment - Es lohnt sich! +open.commentdone.back=Zurück zum kommentierten Artikel + + +open.commentdupe.htmltitle=indymedia | envío abierto - comentario duplicado +open.commentdupe.title=Mantenga la calma unos breves instantes. +open.commentdupe.explanation=Probablemente haya apretado el botón de recarga o enviado el comentario en una segunda ocasión. Si puede leer esto, significa que su comentario ha sido recibido con éxito y será incluido en la página de artículos en breves instantes. +open.commentdupe.no_panic=No se altere! +open.commentdupe.back=Volver al artículo comentado + + +open.posting.htmltitle=indymedia | envío abierto +open.posting.meta.description=Indymedia | Centro de Periodismo Independiente +open.posting.meta.author=Colectivo IMC +open.posting.meta.keywords=Periodismo Libre +open.posting.jump_to_form=Ir directamente al formulario. +open.posting.title=Publique su artículo +open.posting.nr_of_media=Número de recursos mediáticos +open.posting.nr_of_media.info=(wenn Du mehr als eine Datei hochladen willst, bitte hier die Anzahl eintragen und den Knopf drücken, bevor Du weitere Felder ausfüllst.) +open.posting.nr_of_media.submit=Anzahl festlegen +open.posting.form.title=Formulario de publicación +open.posting.title=Título de su artículo +open.posting.title.info=(Bitte wähle einen möglichst klaren, aussagekräftigen Titel.) +open.posting.topic=Thema Deines Beitrags +open.posting.topic.info=(Mehrfachwahl ist möglich. Bitte dazu die [Strg]- bzw [Ctrl]-Taste benutzen) +open.posting.author=autor de este artículo +open.posting.abstract=Descripción breve de su artículo +open.posting.abstract.info=(Sie soll den LeserInnen schnell vermitteln, worum es in Deinem Beitrag geht. Falls Du den ersten Absatz Deines Artikels dazu wählst, achte bitte darauf ihn im Haupttextfeld weiter unten nicht nochmal einzusetzen.) +open.posting.abstract.constraint=(que no exceda de 5 líneas) +open.posting.contact.info=La información de contacto es opcional pero ayuda a que otras personas puedan ponerse en contacto con usted. +open.posting.email=su dirección de email +open.posting.url=su dirección web +open.posting.address=su dirección personal +open.posting.phone=su número de teléfono +open.posting.language=idioma de su artículo +open.posting.text=su artículo +open.posting.text.info=ponga aquí el texto de su artículo +open.posting.media=recursos mediáticos +open.posting.media.info=cargue sus ficheros de medios (de momento tan solo se aceptan jpg|gif|mp3|avi|qt|mpeg) +open.posting.media.howto=(Dazu wählst Du mit "Durchsuchen.." die entsprechende Datei auf Deiner Festplatte aus. +open.posting.media.media=Media +open.posting.media.title=subtítulo de los recursos mediáticos +open.posting.submit.info=Bitte drücke den Verschicken-Knopf nur einmal!
In wenigen Minuten erscheind Dein Beitrag dann auf der "Open Posting" - Seite.   Das ist nicht die Startseite.
Manchmal kann es aber aufgrund technischer Probleme etwas dauern bis er erscheint. +open.posting.criteria=Die Moderationskriterien kannst Du hier nachlesen +open.posting.submit=Enviar (la paciencia es una virtud!!) +open.posting.reset=Limpiar formulario + + +open.postingdone.htmltitle=indymedia | open posting +open.postingdone.title=Hurra, Du hast Deinen Artikel abgeschickt! +open.postingdone.info=Dein Artikel landet in einigen Minuten auf der "Open Posting" - Seite. Das ist nicht die Startseite. Die Moderationskriterien kannst Du hier lesen +open.postingdone.stay_calm=Gedulde Dich einen Moment! Es lohnt sich! +open.postingdone.back=Zurück + + +open.postingdupe.htmltitle=indymedia | envío abierto - envio duplicado +open.postingdupe.title=Mantenga la calma, su envio será procesado en breves instantes. +open.postingdupe.explanation= Posiblemente haya pulsado el botón de recarga de su navegador, o enviado su articulo por segunda vez + Si está leyendo este texto, significa que su envío ha sido recibido con éxito + y que será incluido en la pagina de artículos en breves instantes. +
+ Como detalle técnico, este imc está conectado a un servidor proxy + que no es actualizado cada minuto. +open.postingdupe.no_panic=No pierda los estribos! +open.postingdupe.back=Atrás diff --git a/bundles/producer_en.properties b/bundles/producer_en.properties new file mode 100755 index 00000000..e074c6f5 --- /dev/null +++ b/bundles/producer_en.properties @@ -0,0 +1,24 @@ +general.dateformat = dd/MM/yyyy hh:mm +general.city.Amsterdam.title = emsterdem +general.city.London.title = London +general.city.Berlin.title = Berlijn +general.topic.Repression.title = repressiun +general.topic.Feminism.title = feminisme +general.read.text = Read + +article.title.prefix = bolivia.indymedia.org | +article.email.prefix = e-mail: +article.homepage.prefix = Homepage: +article.makecomment.text = Make a quick comment on this article + +startpage.title = bolivia.indymedia.org | IMC Bolivia + +navigation.languages.caption = Languages: +navigation.cities.caption = Cities: +navigation.topics.caption = Topics: +navigation.publish.text = Publish + +topicnavigation.page.prefix = pagina + + + diff --git a/bundles/producer_es.properties b/bundles/producer_es.properties new file mode 100755 index 00000000..a462b8a6 --- /dev/null +++ b/bundles/producer_es.properties @@ -0,0 +1,26 @@ +general.dateformat = dd/MM/yyyy hh:mm +general.city.Amsterdam.title = emsterdem +general.city.London.title = Londres +general.city.Berlin.title = Berlin +general.topic.Repression.title = repressiun +general.topic.Feminism.title = feminisme +general.read.text = Read + +article.title.prefix = bolivia.indymedia.org | +article.email.prefix = e-mail: +article.homepage.prefix = Homepage: +article.makecomment.text = Make a quick comment on this article + +openposting. + +startpage.title = bolivia.indymedia.org | IMC Bolivia + +navigation.languages.caption = Linguas: +navigation.cities.caption = Ciudades: +navigation.topics.caption = Temas: +navigation.publish.text = Publicar + +topicnavigation.page.prefix = pagina + + + diff --git a/lib/lucene-1.2.jar b/lib/lucene-1.2.jar new file mode 100755 index 0000000000000000000000000000000000000000..ff9b90061b65c32122fcdde27bfe7f1e61fbd7bd GIT binary patch literal 136498 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 literal 0 HcmV?d00001 diff --git a/scripts/createIndex.java b/scripts/createIndex.java new file mode 100755 index 00000000..028865e7 --- /dev/null +++ b/scripts/createIndex.java @@ -0,0 +1,19 @@ +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.index.IndexWriter; + + + +class createIndex{ + public static void main(String[] args){ + try{ + + IndexWriter indexWriter = new IndexWriter("/tmp/index/", new StandardAnalyzer(), true); + + indexWriter.close(); + //and make it owned by correct user? + } + catch (Exception e){ + System.out.println(e.toString()); + } + } +} diff --git a/source/Mir.java b/source/Mir.java index 7b94d9f8..9db7350f 100755 --- a/source/Mir.java +++ b/source/Mir.java @@ -1,4 +1,4 @@ - +import freemarker.template.SimpleList; import freemarker.template.SimpleHash; import freemarker.template.SimpleScalar; import mir.misc.HTMLParseException; @@ -6,9 +6,14 @@ import mir.misc.HTMLTemplateProcessor; import mir.misc.MirConfig; import mir.misc.StringUtil; import mir.servlet.*; +import mir.producer.*; + +import mircoders.global.*; +import mircoders.localizer.*; import mircoders.entity.EntityUsers; import mircoders.module.ModuleMessage; import mircoders.module.ModuleUsers; +import mircoders.storage.DatabaseArticleType; import mircoders.storage.DatabaseMessages; import mircoders.storage.DatabaseUsers; @@ -23,14 +28,18 @@ import java.lang.reflect.Method; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Locale; +import java.util.*; /** * Mir.java - main servlet, that dispatches to servletmodules * * @author $Author: mh $ - * @version $Revision: 1.17 $ $Date: 2002/07/21 22:27:39 $ + * @version $Revision: 1.18 $ $Date: 2002/08/25 19:00:06 $ * * $Log: Mir.java,v $ + * Revision 1.18 2002/08/25 19:00:06 mh + * merge of localization branch into HEAD. mh and zap + * * Revision 1.17 2002/07/21 22:27:39 mh * make the user error msg look nicer * @@ -55,6 +64,7 @@ public class Mir extends AbstractServlet { long startTime = System.currentTimeMillis(); long sessionConnectTime = 0; + EntityUsers userEntity; String http = ""; // get the configration - this could conflict if 2 mirs are in the @@ -65,6 +75,7 @@ public class Mir extends AbstractServlet { MirConfig.setServletName(getServletName()); session = req.getSession(true); + userEntity = (EntityUsers) session.getAttribute("login.uid"); if (req.getServerPort() == 443) http = "https"; else http = "http"; res.setContentType("text/html; charset=" @@ -76,19 +87,23 @@ public class Mir extends AbstractServlet { /** @todo for cleanup and readability this should be moved to * method loginIfNecessary() */ + if (moduleName!=null && moduleName.equals("direct")) { + //... + } + // Authentifizierung - if (moduleName != null && moduleName.equals("login")) { + if ((moduleName != null && moduleName.equals("login")) || (userEntity==null)) { String user = req.getParameter("login"); String passwd = req.getParameter("password"); theLog.printDebugInfo("--login: evaluating for user: " + user); - EntityUsers userEntity = allowedUser(user, passwd); + userEntity = allowedUser(user, passwd); if (userEntity == null) { // login failed: redirecting to login theLog.printWarning("--login: failed!"); _sendLoginPage(res, req, res.getWriter()); return; } - else { + else if (moduleName!=null && moduleName.equals("login")) { // login successful theLog.printInfo("--login: successful! setting uid: " + userEntity.getId()); @@ -141,7 +156,6 @@ public class Mir extends AbstractServlet { } // Check if authed! - EntityUsers userEntity = (EntityUsers) session.getAttribute("login.uid"); if (userEntity == null) { // redirect to loginpage String redirectString = req.getRequestURI(); @@ -300,9 +314,34 @@ public class Mir extends AbstractServlet { mergeData.put("login_user", userEntity); if (messageModule == null) messageModule = new ModuleMessage(DatabaseMessages.getInstance()); mergeData.put("messages", messageModule.getByWhereClause(null, "webdb_create desc", 0, 10)); + + mergeData.put("articletypes", DatabaseArticleType.getInstance().selectByWhereClause("", "id", 0, 20)); + + SimpleList producersData = new SimpleList(); + Iterator i = MirGlobal.localizer().producers().factories().entrySet().iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + + SimpleList producerVerbs = new SimpleList(); + Iterator j = ((ProducerFactory) entry.getValue()).verbs(); + while (j.hasNext()) { + producerVerbs.add((String) j.next()); + } + + SimpleHash producerData = new SimpleHash(); + producerData.put("key", (String) entry.getKey()); + producerData.put("verbs", producerVerbs); + + producersData.add(producerData); + } + mergeData.put("producers", producersData); + + + HTMLTemplateProcessor.process(res, startTemplate, mergeData, out, getLocale(req)); } catch (Exception e) { + e.printStackTrace(System.out); handleError(req, res, out, "error while trying to send startpage. " + e.toString()); } } diff --git a/source/OpenMir.java b/source/OpenMir.java index 8a8bb9a7..c5533635 100755 --- a/source/OpenMir.java +++ b/source/OpenMir.java @@ -25,7 +25,7 @@ import mircoders.storage.*; public class OpenMir extends AbstractServlet { - + //private static boolean confed=false; private static String lang; public HttpSession session; @@ -49,9 +49,17 @@ public class OpenMir extends AbstractServlet { session = req.getSession(); if(session.getAttribute("Language")==null){ - setLanguage(session,getAcceptLanguage(req)); + if (req.getParameter("language")!=null) { + setLanguage(session, req.getParameter("language")); + } + else { + setLanguage(session, getAcceptLanguage(req)); + } } + if (req.getParameter("language")!=null) + setLocale(session, new Locale(req.getParameter("language"), "") ); + res.setContentType("text/html; charset=" +MirConfig.getProp("Mir.DefaultEncoding")); try { @@ -70,14 +78,14 @@ public class OpenMir extends AbstractServlet { } private void handleUserError(HttpServletRequest req, HttpServletResponse res, - PrintWriter out, String errorString) { + PrintWriter out, String errorString) { try { theLog.printError(errorString); SimpleHash modelRoot = new SimpleHash(); modelRoot.put("errorstring", new SimpleScalar(errorString)); modelRoot.put("date", new SimpleScalar(StringUtil.date2readableDateTime(new GregorianCalendar()))); HTMLTemplateProcessor.process(res,MirConfig.getProp("Mir.UserErrorTemplate"), - modelRoot, out, req.getLocale() ); + modelRoot, out, req.getLocale() ); out.close(); } catch (Exception e) { @@ -93,9 +101,9 @@ public class OpenMir extends AbstractServlet { SimpleHash modelRoot = new SimpleHash(); modelRoot.put("errorstring", new SimpleScalar(errorString)); modelRoot.put("date", new SimpleScalar(StringUtil.date2readableDateTime( - new GregorianCalendar()))); + new GregorianCalendar()))); HTMLTemplateProcessor.process(res,MirConfig.getProp("Mir.ErrorTemplate"), - modelRoot,out, req.getLocale()); + modelRoot,out, req.getLocale()); out.close(); } catch (Exception e) { diff --git a/source/config.properties-dist b/source/config.properties-dist index 6e02e9b2..1ea10295 100755 --- a/source/config.properties-dist +++ b/source/config.properties-dist @@ -35,17 +35,23 @@ # GENERAL SETUP # -Mir.Version=pre-1.0 +Mir.Version=localizer-0 ClearXslCache=no StandardLanguage=de DirectOpenposting=yes +Mir.Localizer=mircoders.localizer.basic.MirBasicLocalizer +Mir.Localizer.Logfile=log/localizer.log +Mir.Localizer.ProducerConfigFile=templates/producer/producers.xml + + + #note that you can't make pdf's without making fo's GenerateFO=yes GeneratePDF=yes #on-time-password-protection -PasswdProtection=yes +PasswdProtection=no #use rsync to mirror the website to a remote-host Rsync=no diff --git a/source/mir/config/ConfigChecker.java b/source/mir/config/ConfigChecker.java index 70006bfd..bc5528da 100755 --- a/source/mir/config/ConfigChecker.java +++ b/source/mir/config/ConfigChecker.java @@ -25,7 +25,7 @@ public class ConfigChecker { rootNode = new Node(); } - public void check(ConfigNode aNode) throws ConfigException { + public void check(ConfigNode aNode) throws ConfigFailure { getRootNode().check(aNode); } @@ -63,7 +63,7 @@ public class ConfigChecker { addTypeConstraint(aPropertyName, aType); } - public void check(ConfigNode aNode) throws ConfigException { + public void check(ConfigNode aNode) throws ConfigFailure { Iterator iterator; iterator=constraints.iterator(); @@ -86,7 +86,7 @@ public class ConfigChecker { propertyName=aPropertyName; } - public void check(ConfigNode aNode) throws ConfigException { + public void check(ConfigNode aNode) throws ConfigFailure { }; } @@ -95,7 +95,7 @@ public class ConfigChecker { super(aPropertyName); } - public void check(ConfigNode aNode) throws ConfigException { + public void check(ConfigNode aNode) throws ConfigFailure { aNode.getRequiredStringProperty(propertyName); }; } @@ -109,7 +109,7 @@ public class ConfigChecker { type=aType; } - public void check(ConfigNode aNode) throws ConfigException { + public void check(ConfigNode aNode) throws ConfigFailure { switch(type) { case INTEGER: aNode.getOptionalIntegerProperty(propertyName, new Integer(0)); @@ -124,7 +124,7 @@ public class ConfigChecker { aNode.getOptionalBooleanProperty(propertyName, Boolean.FALSE); break; default: - throw new ConfigException("Invalid value for type in type constraint: "+new Integer(type).toString()); + throw new ConfigFailure("Invalid value for type in type constraint: "+new Integer(type).toString()); } } } diff --git a/source/mir/config/ConfigNode.java b/source/mir/config/ConfigNode.java index 868bccb5..a70aae0c 100755 --- a/source/mir/config/ConfigNode.java +++ b/source/mir/config/ConfigNode.java @@ -8,12 +8,12 @@ public interface ConfigNode { public String getLocationDescription(); public ConfigNode getSubNode(String aSubNodeName); - public Boolean getRequiredBooleanProperty(String aPropertyName) throws ConfigException; - public Integer getRequiredIntegerProperty(String aPropertyName) throws ConfigException; - public String getRequiredStringProperty(String aPropertyName) throws ConfigException; - public Double getRequiredDoubleProperty(String aPropertyName) throws ConfigException; - public Boolean getOptionalBooleanProperty(String aPropertyName, Boolean aDefaultValue) throws ConfigException; - public Integer getOptionalIntegerProperty(String aPropertyName, Integer aDefaultValue) throws ConfigException; - public String getOptionalStringProperty(String aPropertyName, String aDefaultValue) throws ConfigException; - public Double getOptionalDoubleProperty(String aPropertyName, Double aDefaultValue) throws ConfigException; + public Boolean getRequiredBooleanProperty(String aPropertyName) throws ConfigFailure; + public Integer getRequiredIntegerProperty(String aPropertyName) throws ConfigFailure; + public String getRequiredStringProperty(String aPropertyName) throws ConfigFailure; + public Double getRequiredDoubleProperty(String aPropertyName) throws ConfigFailure; + public Boolean getOptionalBooleanProperty(String aPropertyName, Boolean aDefaultValue) throws ConfigFailure; + public Integer getOptionalIntegerProperty(String aPropertyName, Integer aDefaultValue) throws ConfigFailure; + public String getOptionalStringProperty(String aPropertyName, String aDefaultValue) throws ConfigFailure; + public Double getOptionalDoubleProperty(String aPropertyName, Double aDefaultValue) throws ConfigFailure; } diff --git a/source/mir/config/ConfigReader.java b/source/mir/config/ConfigReader.java index 03ecd532..d0c0355a 100755 --- a/source/mir/config/ConfigReader.java +++ b/source/mir/config/ConfigReader.java @@ -26,7 +26,7 @@ public class ConfigReader { super(); }; - public void parseFile(String aFileName, ConfigNodeBuilder aRootNode) throws ConfigException { + public void parseFile(String aFileName, ConfigNodeBuilder aRootNode) throws ConfigFailure { try { SAXParserFactory parserFactory = SAXParserFactory.newInstance(); @@ -40,12 +40,12 @@ public class ConfigReader { handler.includeFile(aFileName); } catch (Throwable e) { - if (e instanceof SAXParseException && ((SAXParseException) e).getException() instanceof ConfigException) { - throw (ConfigException) ((SAXParseException) e).getException(); + if (e instanceof SAXParseException && ((SAXParseException) e).getException() instanceof ConfigFailure) { + throw (ConfigFailure) ((SAXParseException) e).getException(); } else { e.printStackTrace(); - throw new ConfigException( e.getMessage() ); + throw new ConfigFailure( e.getMessage() ); } } } @@ -78,7 +78,7 @@ public class ConfigReader { locator=aLocator; } - private void includeFile(String aFileName) throws ConfigException, SAXParseException, SAXException { + private void includeFile(String aFileName) throws ConfigFailure, SAXParseException, SAXException { File file; SAXParser parser; InputSource inputSource; @@ -93,7 +93,7 @@ public class ConfigReader { System.err.println("about to include "+file.getCanonicalPath()); if (includeFileStack.contains(file.getCanonicalPath())) { - throw new ConfigException("recursive inclusion of file "+file.getCanonicalPath(), getLocatorDescription(locator)); + throw new ConfigFailure("recursive inclusion of file "+file.getCanonicalPath(), getLocatorDescription(locator)); } parser=parserFactory.newSAXParser(); @@ -110,19 +110,19 @@ public class ConfigReader { } } catch (ParserConfigurationException e) { - throw new ConfigException("Internal exception while including \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator)); + throw new ConfigFailure("Internal exception while including \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator)); } catch (SAXParseException e) { throw e; } - catch (ConfigException e) { + catch (ConfigFailure e) { throw e; } catch (FileNotFoundException e) { - throw new ConfigException("Include file \""+aFileName+"\" not found: "+e.getMessage(), e, getLocatorDescription(locator)); + throw new ConfigFailure("Include file \""+aFileName+"\" not found: "+e.getMessage(), e, getLocatorDescription(locator)); } catch (IOException e) { - throw new ConfigException("unable to open include file \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator)); + throw new ConfigFailure("unable to open include file \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator)); } } @@ -132,18 +132,18 @@ public class ConfigReader { level++; try { if (builder==null) { - throw new ConfigException("define, include and property tags cannot have content", getLocatorDescription(locator)); + throw new ConfigFailure("define, include and property tags cannot have content", getLocatorDescription(locator)); } if (aQualifiedName.equals(propertyTagName)) { String name=anAttributes.getValue(propertyNameAttribute); String value=anAttributes.getValue(propertyValueAttribute); if (name==null) { - throw new ConfigException("property has no name attribute", getLocatorDescription(locator)); + throw new ConfigFailure("property has no name attribute", getLocatorDescription(locator)); } else if (value==null) { - throw new ConfigException("property \""+name+"\" has no value attribute", getLocatorDescription(locator)); + throw new ConfigFailure("property \""+name+"\" has no value attribute", getLocatorDescription(locator)); } builder.addProperty(name, definesManager.resolve(value, getLocatorDescription(locator)), value, getLocatorDescription(locator)); @@ -155,11 +155,11 @@ public class ConfigReader { String value=anAttributes.getValue(defineValueAttribute); if (name==null) { - throw new ConfigException("define has no name attribute", getLocatorDescription(locator)); + throw new ConfigFailure("define has no name attribute", getLocatorDescription(locator)); } else if (value==null) { - throw new ConfigException("define \""+name+"\" has no value attribute", getLocatorDescription(locator)); + throw new ConfigFailure("define \""+name+"\" has no value attribute", getLocatorDescription(locator)); } definesManager.addDefine(name, definesManager.resolve(value, getLocatorDescription(locator))); @@ -169,7 +169,7 @@ public class ConfigReader { String fileName=anAttributes.getValue(includeFileAttribute); if (fileName==null) { - throw new ConfigException("include has no file attribute", getLocatorDescription(locator)); + throw new ConfigFailure("include has no file attribute", getLocatorDescription(locator)); } includeFile(definesManager.resolve(fileName, getLocatorDescription(locator))); @@ -180,7 +180,7 @@ public class ConfigReader { builder=builder.makeSubNode(aQualifiedName, getLocatorDescription(locator)); } } - catch (ConfigException e) { + catch (ConfigFailure e) { throw new SAXParseException(e.getMessage(), locator, e); } } @@ -193,7 +193,7 @@ public class ConfigReader { public void characters(char[] aBuffer, int aStart, int anEnd) throws SAXParseException { String text = new String(aBuffer, aStart, anEnd).trim(); if ( text.length() > 0) { - throw new SAXParseException("Text not allowed", locator, new ConfigException("text not allowed", getLocatorDescription(locator))); + throw new SAXParseException("Text not allowed", locator, new ConfigFailure("text not allowed", getLocatorDescription(locator))); } } } @@ -209,7 +209,7 @@ public class ConfigReader { defines.put(aName, anExpression); } - public String resolve(String anExpression, String aLocation) throws ConfigException { + public String resolve(String anExpression, String aLocation) throws ConfigFailure { int previousPosition = 0; int position; int endOfNamePosition; @@ -239,7 +239,7 @@ public class ConfigReader { } } else { - throw new ConfigException("Missing }", aLocation); + throw new ConfigFailure("Missing }", aLocation); } } diff --git a/source/mir/config/MirConfiguration.java b/source/mir/config/MirConfiguration.java index 9885f024..53fa7f11 100755 --- a/source/mir/config/MirConfiguration.java +++ b/source/mir/config/MirConfiguration.java @@ -15,7 +15,7 @@ public class MirConfiguration { rootNode = aRootNode; } - public MirConfiguration(String aFileName) throws ConfigException { + public MirConfiguration(String aFileName) throws ConfigFailure { super(); rootNode = new ConfigSimpleNode(); diff --git a/source/mir/config/exceptions/ConfigDefineNotKnownException.java b/source/mir/config/exceptions/ConfigDefineNotKnownException.java index 21fce9c8..cc943808 100755 --- a/source/mir/config/exceptions/ConfigDefineNotKnownException.java +++ b/source/mir/config/exceptions/ConfigDefineNotKnownException.java @@ -1,6 +1,6 @@ package mir.config.exceptions; -public class ConfigDefineNotKnownException extends ConfigException { +public class ConfigDefineNotKnownException extends ConfigFailure { public ConfigDefineNotKnownException(String aMessage, String aLocation) { super (aMessage, aLocation); } diff --git a/source/mir/config/exceptions/ConfigException.java b/source/mir/config/exceptions/ConfigFailure.java similarity index 55% rename from source/mir/config/exceptions/ConfigException.java rename to source/mir/config/exceptions/ConfigFailure.java index f02c79f5..be733557 100755 --- a/source/mir/config/exceptions/ConfigException.java +++ b/source/mir/config/exceptions/ConfigFailure.java @@ -1,23 +1,25 @@ -package mir.config.exceptions; - -import java.io.*; - -public class ConfigException extends Exception { - private String locationDescription; - private Throwable cause; - - public ConfigException (String aMessage, Throwable aCause, String aLocationDescription) { - super ("Configuration error at "+aLocationDescription+": "+aMessage); - - locationDescription = aLocationDescription; - cause = aCause; - } - - public ConfigException (String aMessage, String aLocationDescription) { - this (aMessage, (Throwable) null, aLocationDescription); - } - - public ConfigException (String aMessage) { - this (aMessage, (Throwable) null, "?"); - } -} +package mir.config.exceptions; + +import multex.Failure; + +import java.io.*; + +public class ConfigFailure extends Failure { + private String locationDescription; + private Throwable cause; + + public ConfigFailure (String aMessage, Throwable aCause, String aLocationDescription) { + super ("Configuration error at "+aLocationDescription+": "+aMessage, aCause); + + locationDescription = aLocationDescription; + cause = aCause; + } + + public ConfigFailure (String aMessage, String aLocationDescription) { + this (aMessage, (Throwable) null, aLocationDescription); + } + + public ConfigFailure (String aMessage) { + this (aMessage, (Throwable) null, "?"); + } +} diff --git a/source/mir/config/exceptions/ConfigInvalidPropertyTypeException.java b/source/mir/config/exceptions/ConfigInvalidPropertyTypeException.java index 494227f4..b0c6869c 100755 --- a/source/mir/config/exceptions/ConfigInvalidPropertyTypeException.java +++ b/source/mir/config/exceptions/ConfigInvalidPropertyTypeException.java @@ -1,6 +1,6 @@ package mir.config.exceptions; -public class ConfigInvalidPropertyTypeException extends ConfigException { +public class ConfigInvalidPropertyTypeException extends ConfigFailure { public ConfigInvalidPropertyTypeException(String aMessage, String aLocation) { super (aMessage, aLocation); } diff --git a/source/mir/config/exceptions/ConfigMissingPropertyException.java b/source/mir/config/exceptions/ConfigMissingPropertyException.java index df19705a..f80e2330 100755 --- a/source/mir/config/exceptions/ConfigMissingPropertyException.java +++ b/source/mir/config/exceptions/ConfigMissingPropertyException.java @@ -1,6 +1,6 @@ package mir.config.exceptions; -public class ConfigMissingPropertyException extends ConfigException { +public class ConfigMissingPropertyException extends ConfigFailure { public ConfigMissingPropertyException(String aMessage, String aLocation) { super (aMessage, aLocation); } diff --git a/source/mir/entity/EntityBrowser.java b/source/mir/entity/EntityBrowser.java new file mode 100755 index 00000000..a320ddee --- /dev/null +++ b/source/mir/entity/EntityBrowser.java @@ -0,0 +1,93 @@ +package mir.entity; + +import java.util.*; +import mir.util.*; +import mir.storage.*; +import mir.entity.*; + +public class EntityBrowser implements RewindableIterator { + + private StorageObject storage; + private String whereClause; + private String orderByClause; + private int batchSize; + private int toFetch; + private EntityList currentBatch; + + private int skip; + private int limit; + + private int batchPosition; + private int positionInBatch; + + public EntityBrowser(StorageObject aStorage, String aWhereClause, String anOrderByClause, + int aBatchSize, int aLimit, int aSkip) throws StorageObjectException { + + storage=aStorage; + whereClause=aWhereClause; + orderByClause=anOrderByClause; + batchSize=aBatchSize; + skip=aSkip; + limit=aLimit; + + rewind(); + } + + public EntityBrowser(StorageObject aStorage, + String aWhereClause, String anOrderByClause, + int aBatchSize) throws StorageObjectException { + this(aStorage, aWhereClause, anOrderByClause, aBatchSize, -1, 0); + } + + public void readCurrentBatch(int aSkip) throws StorageObjectException { + currentBatch = storage.selectByWhereClause(whereClause, orderByClause, aSkip, batchSize); + batchPosition = aSkip; + positionInBatch = 0; + } + + public void rewind() { + try { + readCurrentBatch(skip); + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + } + + public boolean hasNext() { + try { + if (limit>-1 && batchPosition+positionInBatch>=skip+limit) + return false; + + if (positionInBatch>=currentBatch.size() && currentBatch.hasNextBatch()) { + readCurrentBatch(batchPosition+positionInBatch); + } + + return (positionInBatch0 && cache.size()>=maximumLength) ; + } + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + + } + + private void exhaust() { + skip(); + + while (!exhausted) + fetchNext(); + } + + private void fetchUntil(int anIndex) { + skip(); + + while (!exhausted && anIndex>=cache.size()) + fetchNext(); + } + + public int size() { + exhaust(); + + return cache.size(); + } + + public Object get(int anIndex) { + fetchUntil(anIndex); + return cache.get(anIndex); + } +} \ No newline at end of file diff --git a/source/mir/generator/CompositeGeneratorLibrary.java b/source/mir/generator/CompositeGeneratorLibrary.java new file mode 100755 index 00000000..2c1a9493 --- /dev/null +++ b/source/mir/generator/CompositeGeneratorLibrary.java @@ -0,0 +1,46 @@ +package mir.generator; + +import java.util.*; + +public class CompositeGeneratorLibrary implements Generator.GeneratorLibrary { + private Map generatorLibraries; + private Generator.GeneratorLibrary defaultLibrary = null; + private static String LIBRARY_QUALIFIER_SEPARATOR = "::"; + + public CompositeGeneratorLibrary() { + generatorLibraries = new HashMap(); + } + + public void addLibrary(String aQualifier, Generator.GeneratorLibrary aLibrary, boolean anIsDefault) { + if (anIsDefault || defaultLibrary == null) { + defaultLibrary = aLibrary; + } + + generatorLibraries.put(aQualifier, aLibrary); + } + + public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure { + String qualifier; + String libraryName; + int position; + Generator.GeneratorLibrary library; + + position = anIdentifier.indexOf( LIBRARY_QUALIFIER_SEPARATOR ); + if (position>=0) { + libraryName = anIdentifier.substring(0, position); + qualifier = anIdentifier.substring(position + LIBRARY_QUALIFIER_SEPARATOR.length()); + + library = (Generator.GeneratorLibrary) generatorLibraries.get(libraryName); + if (library==null) + throw new GeneratorExc("CompositeGeneratorLibrary: library '"+libraryName+"' not found"); + + return library.makeGenerator(qualifier); + } + else { + if (defaultLibrary!=null) + return defaultLibrary.makeGenerator(anIdentifier); + else + throw new GeneratorExc("CompositeGeneratorLibrary: no default library speficied"); + } + }; +} \ No newline at end of file diff --git a/source/mir/generator/FreemarkerGenerator.java b/source/mir/generator/FreemarkerGenerator.java new file mode 100755 index 00000000..e92bd746 --- /dev/null +++ b/source/mir/generator/FreemarkerGenerator.java @@ -0,0 +1,279 @@ +package mir.generator; + +import freemarker.template.*; +import org.apache.struts.util.MessageResources; +import java.util.*; +import java.io.*; +import mir.entity.*; +import mir.util.*; +import mir.misc.*; + +public class FreemarkerGenerator implements Generator { + private Template template; + + public FreemarkerGenerator(Template aTemplate) { + template = aTemplate; + } + + public void generate(Object anOutputWriter, Map aValues, PrintWriter aLogger) throws GeneratorExc, GeneratorFailure { + if (!(anOutputWriter instanceof PrintWriter)) + throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter"); + + try { + template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter); + } + catch (Throwable t) { + aLogger.println("Exception occurred: "+t.getMessage()); + t.printStackTrace(aLogger); + throw new GeneratorFailure( t ); + } + } + + private static TemplateScalarModel makeStringAdapter(String aString) { + return new SimpleScalar(aString); + } + + private static TemplateHashModel makeMapAdapter(Map aMap) { + return new MapAdapter(aMap); + } + + private static TemplateListModel makeIteratorAdapter(Iterator anIterator) { + return new IteratorAdapter(anIterator); + } + + private static TemplateMethodModel makeFunctionAdapter(Generator.GeneratorFunction aFunction) { + return new FunctionAdapter(aFunction); + } + + private static TemplateModel makeAdapter(Object anObject) throws TemplateModelException { + if (anObject == null) + return null; + if (anObject instanceof TemplateModel) + return (TemplateModel) anObject; + else if (anObject instanceof Generator.GeneratorFunction) + return makeFunctionAdapter((Generator.GeneratorFunction) anObject); + else if (anObject instanceof MessageResources) + return new MessageMethodModel((MessageResources) anObject); + else if (anObject instanceof Integer) + return makeStringAdapter(((Integer) anObject).toString()); + else if (anObject instanceof String) + return makeStringAdapter((String) anObject); + else if (anObject instanceof Map) + return makeMapAdapter((Map) anObject); + else if (anObject instanceof Iterator) + return makeIteratorAdapter((Iterator) anObject); + else if (anObject instanceof List) + return makeIteratorAdapter(((List) anObject).iterator()); + else + throw new TemplateModelException("Unadaptable class: " + anObject.getClass().getName()); + } + + private static class MapAdapter implements TemplateModelRoot { + Map map; + Map valuesCache; + + private MapAdapter(Map aMap) { + map = aMap; + valuesCache = new HashMap(); + } + + public void put(String aKey, TemplateModel aModel) { + valuesCache.put(aKey, aModel); + } + + public void remove(String aKey) { + // ML: kinda tricky... + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public TemplateModel get(String aKey) throws TemplateModelException { + try { + if (!valuesCache.containsKey(aKey)) { + Object value = map.get(aKey); + + if (value == null && !map.containsKey(aKey)) + throw new TemplateModelException("MapAdapter: no key "+aKey+" available"); + + valuesCache.put(aKey, makeAdapter(value)); + } + + return (TemplateModel) valuesCache.get(aKey); + } + catch (TemplateModelException e) { + throw e; + } + catch (Throwable t) { + throw new TemplateModelException(t.getMessage()); + } + } + } + + private static class IteratorAdapter implements TemplateListModel { + Iterator iterator; + List valuesCache; + int position; + + private IteratorAdapter(Iterator anIterator) { + iterator = anIterator; + + valuesCache = new Vector(); + position=0; + + + if (iterator instanceof RewindableIterator) { + ((RewindableIterator) iterator).rewind(); + } + } + + public boolean isEmpty() { + return valuesCache.isEmpty() && !iterator.hasNext(); + } + + private void getUntil(int anIndex) throws TemplateModelException { + while (valuesCache.size()<=anIndex && iterator.hasNext()) + { + valuesCache.add(makeAdapter(iterator.next())); + } + }; + + public TemplateModel get(int anIndex) throws TemplateModelException { + TemplateModel result; + + getUntil(anIndex); + + if (anIndex=valuesCache.size() && iTitle:

+ *

Description:

+ *

Copyright: Copyright (c) 2002

+ *

Company:

+ * @author unascribed + * @version 1.0 + */ + +public interface WriterEngine { + public Object openWriter(String anIdentifier, String aParameter) throws GeneratorExc, GeneratorFailure; + public void closeWriter(Object aWriter) throws GeneratorExc, GeneratorFailure; +} \ No newline at end of file diff --git a/source/mir/misc/HTMLTemplateProcessor.java b/source/mir/misc/HTMLTemplateProcessor.java index 544e161a..d9e4e8e3 100755 --- a/source/mir/misc/HTMLTemplateProcessor.java +++ b/source/mir/misc/HTMLTemplateProcessor.java @@ -43,7 +43,7 @@ public final class HTMLTemplateProcessor { // // init - static { + static { /** @todo either in the above block or here :) //rk */ templateDir = MirConfig.getPropWithHome("HTMLTemplateProcessor.Dir"); templateCache = new FileTemplateCache(templateDir); @@ -66,8 +66,8 @@ public final class HTMLTemplateProcessor { defEncoding = MirConfig.getProp("Mir.DefaultEncoding"); openAction = MirConfig.getProp("Producer.OpenAction"); productionHost = MirConfig.getProp("Producer.ProductionHost"); - videoHost = MirConfig.getProp("Producer.VideoHost"); - audioHost = MirConfig.getProp("Producer.AudioHost"); + videoHost = MirConfig.getProp("Producer.Video.Host"); + audioHost = MirConfig.getProp("Producer.Audio.Host"); imageHost = MirConfig.getProp("Producer.Image.Host"); imagePath = MirConfig.getProp("Producer.Image.Path"); producerDocRoot = MirConfig.getProp("Producer.DocRoot"); @@ -251,7 +251,7 @@ public final class HTMLTemplateProcessor { configHash.put("imageHost", new SimpleScalar(imageHost)); configHash.put("imagePath", new SimpleScalar(imagePath)); configHash.put("mirVersion", new SimpleScalar(MirConfig.getProp("Mir.Version"))); - // this conform to updated freemarker syntax + // this conform to updated freemarker syntax configHash.put("compressWhitespace", new freemarker.template.utility.CompressWhitespace() ); configHash.put("generateFO", new SimpleScalar(generateFO)); configHash.put("generatePDF", new SimpleScalar(generatePDF)); diff --git a/source/mir/misc/MirConfig.java b/source/mir/misc/MirConfig.java index a64f4cfb..a3e91991 100755 --- a/source/mir/misc/MirConfig.java +++ b/source/mir/misc/MirConfig.java @@ -73,7 +73,14 @@ public class MirConfig extends Configuration { * @return a String containing the prop. value */ public static String getProp(String propName) { - return (String)configHash.get(propName); + String result = (String)configHash.get(propName); + + if (result==null) + throw new ConfigException("config property '"+propName+"' not available!"); + + + + return result; } /** @@ -83,12 +90,11 @@ public class MirConfig extends Configuration { * @return a String containing the prop.value */ public static String getPropWithHome(String propName) { - return (String)configHash.get("Home") + - (String)configHash.get(propName); + return getProp("Home") + getProp(propName); } /** - * Returns the property asked for iin raw Object form by + * Returns the property asked for iin raw Object form by * pulling it out a HashMap * @param a String containing the property name (key) * @return an Object containing the prop.value diff --git a/source/mir/misc/PDFUtil.java b/source/mir/misc/PDFUtil.java new file mode 100755 index 00000000..4e72f199 --- /dev/null +++ b/source/mir/misc/PDFUtil.java @@ -0,0 +1,70 @@ +package mir.misc; + +import java.io.*; +import javax.servlet.http.*; + +import mircoders.global.*; + +import org.apache.fop.apps.* ; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.apache.log.*; + +public class PDFUtil { + + public static void makePDF(String foFilePath,Object pdfDestination) throws Exception + { + try{ + Driver driver = new Driver(); + + //stupid logging that fop wants to use, needs to be changed + Hierarchy hierarchy = Hierarchy.getDefaultHierarchy(); + Logger fopLog=null; + fopLog = hierarchy.getLoggerFor("fop"); + fopLog.setPriority(Priority.WARN); + driver.setLogger(fopLog); + + driver.setRenderer(Driver.RENDER_PDF); + + File foFile=new File(foFilePath); + + String html2foStyleSheetPath=MirGlobal.getConfigProperty("Home") + + MirGlobal.getConfigProperty("HTMLTemplateProcessor.Dir") + + "/" + + MirGlobal.getConfigProperty("Producer.PrintableContent.html2foStyleSheetName"); + File html2foStyleSheet=new File(html2foStyleSheetPath); + InputHandler inputHandler = + new XSLTInputHandler(foFile, html2foStyleSheet); + XMLReader parser = inputHandler.getParser(); + + if (pdfDestination instanceof String) { + String filePath = (String) pdfDestination; + driver.setOutputStream(new FileOutputStream(filePath)); + driver.render(parser, inputHandler.getInputSource()); + } + else if (pdfDestination instanceof HttpServletResponse){ + HttpServletResponse res = (HttpServletResponse) pdfDestination; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + driver.setOutputStream(out); + res.setContentType("application/pdf"); + + driver.render(parser, inputHandler.getInputSource()); + + byte[] content = out.toByteArray(); + res.setContentLength(content.length); + res.getOutputStream().write(content); + res.getOutputStream().flush(); + } + else { + throw new Exception("I'm sorry but I don't know how to output a pdf to an object of type" + pdfDestination.getClass().getName()); + } + } + + catch (Exception ex){ + throw(ex); + } + } +} + + + diff --git a/source/mir/producer/AssignmentProducerNode.java b/source/mir/producer/AssignmentProducerNode.java new file mode 100755 index 00000000..1c13cace --- /dev/null +++ b/source/mir/producer/AssignmentProducerNode.java @@ -0,0 +1,31 @@ +package mir.producer; + +import java.util.*; +import java.io.*; +import org.apache.struts.util.MessageResources; +import mir.util.*; + +public class AssignmentProducerNode extends ProducerNodeDecorator { + private String key; + private String bundleIdentifier; + private Object value; + + public AssignmentProducerNode(String aKey, Object aValue, ProducerNode aSubNode) { + super(aSubNode); + + key = aKey; + value = aValue; + } + + public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure { + try { + ParameterExpander.setValueForKey(aValueMap, key, value); + + super.produce(aValueMap, aVerb, aLogger); + } + catch (Throwable t) { + throw new ProducerFailure(t.getMessage(), t); + } + }; + +} \ No newline at end of file diff --git a/source/mir/producer/CompositeProducerNode.java b/source/mir/producer/CompositeProducerNode.java new file mode 100755 index 00000000..503a87d4 --- /dev/null +++ b/source/mir/producer/CompositeProducerNode.java @@ -0,0 +1,58 @@ +package mir.producer; + +import java.util.*; +import java.io.*; + +public class CompositeProducerNode implements ProducerNode { + private List subNodes; + + public CompositeProducerNode() { + subNodes = new Vector(); + } + + public CompositeProducerNode(ProducerNode[] aSubNodes) { + this(); + + int i; + + for (i=0; i=nrEntitiesPerBatch) + nrEntitiesInFirstBatch = nrEntitiesInFirstBatch + nrEntitiesPerBatch; + nrBatchesAfterFirst = (nrEntities-nrEntitiesInFirstBatch)/nrEntitiesPerBatch; + + batchLocations.add(new BatchLocation(nrBatchesAfterFirst*nrEntitiesPerBatch, nrEntitiesInFirstBatch)); + batchData = new HashMap(); + batchData.put("identifier", ""); + batchData.put("index", new Integer(nrBatchesAfterFirst+1)); + batchData.put("size", new Integer(nrEntitiesInFirstBatch)); + batchesData.add(batchData); + + for (i=0; inrBatchesAfterFirst+1) { + nrBatchesToProcess = nrBatchesAfterFirst+1; + } + + if (batchSubNode!=null) { + for (i=0; i0) + batchData.put("previous", batchesData.get(i-1)); + else + batchData.put("previous", null); + + if (i 0) { + throw new SAXParseException("Text not allowed", locator, new ConfigFailure("text not allowed", getLocatorDescription(locator))); + } + } + + } + public class SectionsManager { + Stack handlerStack; + + public SectionsManager() { + handlerStack = new Stack(); + } + + public void pushHandler(SectionHandler aSectionHandler) { + handlerStack.push(aSectionHandler); + } + + public SectionHandler popHandler() { + return (SectionHandler) handlerStack.pop(); + } + + public SectionHandler currentHandler() { + return (SectionHandler) handlerStack.peek(); + } + + public boolean isEmpty() { + return handlerStack.isEmpty(); + } + } + + public abstract class SectionHandler { + public abstract SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc; + + public abstract void endElement(SectionHandler aHandler) throws ProducerConfigExc; +// { +// } + + public void finishSection() throws ProducerConfigExc { + } + } + + public class RootSectionHandler extends SectionHandler { + private Map producers; + + public RootSectionHandler(Map aProducers) { + producers = aProducers; + } + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + if (aTag.equals("producers")) { + return new ProducersSectionHandler(producers); + } + else + throw new ProducerConfigExc ("Tag 'producers' expected, tag '"+aTag+"' found"); + } + + public void endElement(SectionHandler aHandler) { + } + + public void finishSection() throws ProducerConfigExc { + } + } + + + private final static String PRODUCER_NAME_ATTRIBUTE = "name"; + private final static String[] PRODUCER_REQUIRED_ATTRIBUTES = { PRODUCER_NAME_ATTRIBUTE }; + private final static String[] PRODUCER_OPTIONAL_ATTRIBUTES = { }; + + private final static String NODE_DEFINITION_NAME_ATTRIBUTE = "name"; + private final static String[] NODE_DEFINITION_REQUIRED_ATTRIBUTES = { NODE_DEFINITION_NAME_ATTRIBUTE }; + private final static String[] NODE_DEFINITION_OPTIONAL_ATTRIBUTES = { }; + + public class ProducersSectionHandler extends SectionHandler { + private Map producers; + private String name; + + public ProducersSectionHandler(Map aProducers) { + producers = aProducers; + } + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + + if (aTag.equals("producer")) { + ReaderTool.checkAttributes(anAttributes, PRODUCER_REQUIRED_ATTRIBUTES, PRODUCER_OPTIONAL_ATTRIBUTES); + + name = (String) anAttributes.get(PRODUCER_NAME_ATTRIBUTE); + ReaderTool.checkValidIdentifier( name ); + + if (producers.containsKey(name)) + throw new ProducerConfigExc("Duplicate producer name: '" + name + "'"); + + name = (String) anAttributes.get(PRODUCER_NAME_ATTRIBUTE); + + return new ProducerSectionHandler(); + } + else if (aTag.equals("nodedefinition")) { + ReaderTool.checkAttributes(anAttributes, NODE_DEFINITION_REQUIRED_ATTRIBUTES, NODE_DEFINITION_OPTIONAL_ATTRIBUTES); + + name = (String) anAttributes.get(NODE_DEFINITION_NAME_ATTRIBUTE); + ReaderTool.checkValidIdentifier( name ); + +// if (producers.containsKey(name)) +// throw new ProducerConfigExc("Duplicate producer name: '" + name + "'"); + + name = (String) anAttributes.get(NODE_DEFINITION_NAME_ATTRIBUTE); + + return new NodeDefinitionSectionHandler(name); + } + + throw new ProducerConfigExc("Unexpected tag: "+aTag ); + } + + public void endElement(SectionHandler aHandler) throws ProducerConfigExc { + if (aHandler instanceof ProducerSectionHandler) { + producers.put(name, ((ProducerSectionHandler) aHandler).getProducerFactory()); + } + else if (aHandler instanceof NodeDefinitionSectionHandler) { + scriptedNodeBuilderLibrary.registerFactory(name, + new DefaultProducerNodeBuilders.ScriptedProducerNodeBuilder.factory( + ((NodeDefinitionSectionHandler) aHandler).getDefinition())); + } + else throw new ProducerConfigExc("ProducersSectionHandler.endElement Internal error: Unexpected handler: " + aHandler.getClass().getName()); + } + + public void finishSection() throws ProducerConfigExc { + } + } + + public class ProducerSectionHandler extends SectionHandler { + private ProducerFactory producerFactory; + + private ProducerNode body; + private Map verbs; + private String defaultVerb; + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + if (aTag.equals("verbs")) { + if (verbs!=null) + throw new ProducerConfigExc("Verbs already processed"); + if (body!=null) + throw new ProducerConfigExc("Verbs should come before body"); + else + return new ProducerVerbsSectionHandler(); + } + else if (aTag.equals("body")) { + if (body==null) + return new ProducerNodeSectionHandler(); + else + throw new ProducerConfigExc("Body already processed"); + } + throw new ProducerConfigExc("Unexpected tag: '"+aTag+"'"); + } + + public void endElement(SectionHandler aHandler) throws ProducerConfigExc { + if (aHandler instanceof ProducerNodeSectionHandler) { + body = ((ProducerNodeSectionHandler) aHandler).getProducerNode(); + } + else if (aHandler instanceof ProducerVerbsSectionHandler) + { + verbs = ((ProducerVerbsSectionHandler) aHandler).getVerbs(); + defaultVerb = ((ProducerVerbsSectionHandler) aHandler).getDefaultVerb(); + } + else throw new ProducerConfigExc("ProducerSectionHandler.endElement Internal error: Unexpected handler: " + aHandler.getClass().getName()); + } + + public void finishSection() throws ProducerConfigExc { + if (verbs==null) + throw new ProducerConfigExc("No verbs defined"); + + if (body==null) + throw new ProducerConfigExc("No body defined"); + + producerFactory = new ScriptedProducerFactory(verbs, body, defaultVerb); + } + + public ProducerFactory getProducerFactory() { + return producerFactory; + } + } + + private final static String PRODUCER_VERB_NAME_ATTRIBUTE = "name"; + private final static String PRODUCER_VERB_DEFAULT_ATTRIBUTE = "default"; + private final static String[] PRODUCER_VERB_REQUIRED_ATTRIBUTES = { PRODUCER_VERB_NAME_ATTRIBUTE }; + private final static String[] PRODUCER_VERB_OPTIONAL_ATTRIBUTES = { PRODUCER_VERB_DEFAULT_ATTRIBUTE }; + + public class ProducerVerbsSectionHandler extends SectionHandler { + private Map verbs; + private String defaultVerb; + private String currentVerb; + + public ProducerVerbsSectionHandler() { + verbs = new HashMap(); + defaultVerb = null; + } + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + if (aTag.equals("verb")) { + ReaderTool.checkAttributes(anAttributes, PRODUCER_VERB_REQUIRED_ATTRIBUTES, PRODUCER_VERB_OPTIONAL_ATTRIBUTES); + currentVerb = (String) anAttributes.get( PRODUCER_VERB_NAME_ATTRIBUTE ); + + ReaderTool.checkValidIdentifier( currentVerb ); + + if (verbs.containsKey(currentVerb)) + throw new ProducerConfigExc( "Duplicate definition of verb '" + currentVerb + "'" ); + + if (anAttributes.containsKey(PRODUCER_VERB_DEFAULT_ATTRIBUTE)) { + if (defaultVerb!=null) + throw new ProducerConfigExc( "Default verb already declared" ); + + defaultVerb = currentVerb; + } + + return new ProducerNodeSectionHandler(); + } + else throw new ProducerConfigExc("Only 'verb' tags allowed here, '" + aTag + "' encountered."); + } + + public void endElement(SectionHandler aHandler) { + verbs.put(currentVerb, ((ProducerNodeSectionHandler) aHandler).getProducerNode()); + } + + public void finishSection() { + } + + public String getDefaultVerb() { + return defaultVerb; + } + + public Map getVerbs() { + return verbs; + } + } + + public class EmptySectionHandler extends SectionHandler { + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + throw new ProducerConfigExc("No tags are allowed here"); + } + + public void endElement(SectionHandler aHandler) { + } + + } + + public class MultiProducerNodeSectionHandler extends SectionHandler { + private Map nodeParameters; + private Set validNodeParameters; + private String currentNodeParameter; + private String scriptedNodeName; + private Set allowedNodeParameterReferences; + + public MultiProducerNodeSectionHandler(String aScriptedNodeName, Set anAllowedNodeParameterReferences, Set aValidNodeParameters) { + allowedNodeParameterReferences = anAllowedNodeParameterReferences; + scriptedNodeName = aScriptedNodeName; + validNodeParameters = aValidNodeParameters; + nodeParameters = new HashMap(); + } + public MultiProducerNodeSectionHandler(Set aValidNodeParameters) { + this("", new HashSet(), aValidNodeParameters); + } + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + if (!validNodeParameters.contains(aTag)) + throw new ProducerConfigExc("Invalid node parameter: '" + aTag + "'"); + else if (nodeParameters.containsKey(aTag)) + throw new ProducerConfigExc("Node parameter: '" + aTag + "' already specified"); + else if (anAttributes.size()>0) + throw new ProducerConfigExc("No parameters are allowed here"); + + currentNodeParameter = aTag; + + return new ProducerNodeSectionHandler(scriptedNodeName, validNodeParameters); + } + + public void endElement(SectionHandler aHandler) throws ProducerConfigExc { + if (aHandler instanceof ProducerNodeSectionHandler) { + nodeParameters.put(currentNodeParameter, ((ProducerNodeSectionHandler) aHandler).getProducerNode()); + } + else { + throw new ProducerConfigExc("Internal error: unknown section handler '" + aHandler.getClass().getName() + "'" ); + } + } + + public Map getNodeParameters() { + return nodeParameters; + } + } + + public class ProducerNodeSectionHandler extends SectionHandler { + private CompositeProducerNode producerNode; + private ProducerNodeBuilder currentBuilder; + private String scriptedNodeName; + private Set allowedNodeParameterReferences; + + public ProducerNodeSectionHandler(String aScriptedNodeName, Set anAllowedNodeParameterReferences) { + producerNode = new CompositeProducerNode(); + scriptedNodeName = aScriptedNodeName; + allowedNodeParameterReferences = anAllowedNodeParameterReferences; + } + + public ProducerNodeSectionHandler() { + this("", new HashSet()); + } + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + if (allowedNodeParameterReferences.contains((aTag))) { + if (!anAttributes.isEmpty()) { + throw new ProducerConfigExc( "No attributes allowed" ); + } + + currentBuilder = new DefaultProducerNodeBuilders.ScriptedProducerParameterNodeBuilder(scriptedNodeName, aTag); +// producerNode.addSubNode( +// new ScriptedProducerNodeDefinition.NodeParameterProducerNode(scriptedNodeName, aTag)); + return new EmptySectionHandler(); + } + else if (scriptedNodeBuilderLibrary.hasBuilderForName(aTag) || builderLibrary.hasBuilderForName((aTag))) { + + if (scriptedNodeBuilderLibrary.hasBuilderForName(aTag)) + currentBuilder = scriptedNodeBuilderLibrary.constructBuilder(aTag); + else + currentBuilder = builderLibrary.constructBuilder(aTag); + + currentBuilder.setAttributes(anAttributes); + if (currentBuilder.getAvailableSubNodes().isEmpty()) { + return new EmptySectionHandler(); + } + if (currentBuilder.getAvailableSubNodes().size()>1) + return new MultiProducerNodeSectionHandler(scriptedNodeName, allowedNodeParameterReferences, currentBuilder.getAvailableSubNodes()); + else if (currentBuilder.getAvailableSubNodes().size()<1) + return new EmptySectionHandler(); + else { + return new ProducerNodeSectionHandler(scriptedNodeName, allowedNodeParameterReferences); + } + } + else + throw new ProducerConfigExc("Unknown producer node tag: '" + aTag + "'"); + } + + public void endElement(SectionHandler aHandler) throws ProducerConfigExc { + if (aHandler instanceof ProducerNodeSectionHandler) { + currentBuilder.setSubNode((String) (currentBuilder.getAvailableSubNodes().iterator().next()), + ((ProducerNodeSectionHandler) aHandler).getProducerNode()); + } + else if (aHandler instanceof MultiProducerNodeSectionHandler) { + Iterator i; + Map nodeParameters; + Map.Entry entry; + + nodeParameters = ((MultiProducerNodeSectionHandler) aHandler).getNodeParameters(); + i = nodeParameters.entrySet().iterator(); + while (i.hasNext()) { + entry = (Map.Entry) i.next(); + currentBuilder.setSubNode((String) entry.getKey(), (ProducerNode) entry.getValue()); + } + } + else if (aHandler instanceof EmptySectionHandler) { + // deliberately empty: nothing expected, so nothing to process + } + else { + throw new ProducerConfigExc("Internal error: unknown section handler '" + aHandler.getClass().getName() + "'" ); + } + + producerNode.addSubNode(currentBuilder.constructNode()); + currentBuilder = null; + } + + public ProducerNode getProducerNode() { + if (producerNode.getNrSubNodes()==1) { + return producerNode.getSubNode(0); + } + else { + return producerNode; + } + } + } + + public class NodeDefinitionSectionHandler extends SectionHandler { + private ScriptedProducerNodeDefinition nodeDefinition; + private ProducerNode body; + private Map stringParameters; + private Map nodeParameters; + private String name; + + public NodeDefinitionSectionHandler(String aName) { + body = null; + nodeParameters = null; + stringParameters = null; + name = aName; + } + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + if (aTag.equals("parameters")) { + if (!anAttributes.isEmpty()) { + throw new ProducerConfigExc( "No attributes allowed for tag 'parameters'" ); + } + if (nodeParameters!=null) { + throw new ProducerConfigExc( "Parameters have already been declared" ); + } + if (body!=null) { + throw new ProducerConfigExc( "Parameters should come before definition" ); + } + + return new NodeDefinitionParametersSectionHandler(); + } + else if (aTag.equals("definition")) { + return new ProducerNodeSectionHandler(name, nodeParameters.keySet()); + } + else throw new ProducerConfigExc("Only 'definition' or 'parameters' tags allowed here, '" + aTag + "' encountered."); + } + + public void endElement(SectionHandler aHandler) { + if (aHandler instanceof NodeDefinitionParametersSectionHandler) { + stringParameters = ((NodeDefinitionParametersSectionHandler) aHandler).getStringParameters(); + nodeParameters = ((NodeDefinitionParametersSectionHandler) aHandler).getNodeParameters(); + } + else if (aHandler instanceof ProducerNodeSectionHandler) { + body = ((ProducerNodeSectionHandler) aHandler).getProducerNode(); + } + } + + public void finishSection() throws ProducerConfigExc { + Iterator i; + if (body == null) + throw new ProducerConfigExc( "Definition missing" ); + + nodeDefinition = new ScriptedProducerNodeDefinition(name); + + nodeDefinition.setBody(body); + + i = nodeParameters.keySet().iterator(); + while (i.hasNext()) { + nodeDefinition.addNodeParameter((String) i.next()); + } + + i = stringParameters.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + nodeDefinition.addParameter((String) entry.getKey(), (String) entry.getValue()); + } + } + + public ScriptedProducerNodeDefinition getDefinition() { + return nodeDefinition; + } + } + + private final static String NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE = "name"; + private final static String NODE_DEFINITION_PARAMETER_DEFAULTVALUE_ATTRIBUTE = "defaultvalue"; + private final static String[] NODE_DEFINITION_PARAMETER_REQUIRED_ATTRIBUTES = { NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE }; + private final static String[] NODE_DEFINITION_PARAMETER_OPTIONAL_ATTRIBUTES = { NODE_DEFINITION_PARAMETER_DEFAULTVALUE_ATTRIBUTE }; + private final static String[] NODE_DEFINITION_NODE_PARAMETER_OPTIONAL_ATTRIBUTES = { }; + + public class NodeDefinitionParametersSectionHandler extends SectionHandler { + private Map nodeParameters; + private Map stringParameters; + + public NodeDefinitionParametersSectionHandler() { + nodeParameters = new HashMap(); + stringParameters = new HashMap(); + } + + public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc { + String parameterName; + String defaultValue; + + if (aTag.equals("node")) { + ReaderTool.checkAttributes(anAttributes, NODE_DEFINITION_PARAMETER_REQUIRED_ATTRIBUTES, NODE_DEFINITION_NODE_PARAMETER_OPTIONAL_ATTRIBUTES); + parameterName = (String) anAttributes.get( NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE ); + + if (nodeParameters.containsKey(parameterName)) + throw new ProducerConfigExc("Duplicate parameter name: '" + parameterName + "'"); + + ReaderTool.checkValidIdentifier( parameterName ); + + nodeParameters.put(parameterName, parameterName); + + return new EmptySectionHandler(); + } + else if (aTag.equals("string")) { + ReaderTool.checkAttributes(anAttributes, NODE_DEFINITION_PARAMETER_REQUIRED_ATTRIBUTES, NODE_DEFINITION_PARAMETER_OPTIONAL_ATTRIBUTES); + parameterName = (String) anAttributes.get( NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE ); + + if (stringParameters.containsKey(parameterName)) + throw new ProducerConfigExc("Duplicate parameter name: '" + parameterName + "'"); + + ReaderTool.checkValidIdentifier( parameterName ); + + defaultValue = (String) anAttributes.get( NODE_DEFINITION_PARAMETER_DEFAULTVALUE_ATTRIBUTE ); + + stringParameters.put(parameterName, defaultValue); + + return new EmptySectionHandler(); + } + else throw new ProducerConfigExc("Only 'string' and 'node' tags allowed here, '" + aTag + "' encountered."); + + } + + public void endElement(SectionHandler aHandler) { + } + + public void finishSection() { + } + + public Map getNodeParameters() { + return nodeParameters; + } + + public Map getStringParameters() { + return stringParameters; + } + } +} + + +/* + / (expecting producers) + producers/ (expecting nodedefinition, producer) + nodedefinition (expecting parameters, definition) + parameters (expecting parameter declarations) + definition (expecting nodes, subnodes) +*/ + diff --git a/source/mir/producer/reader/ProducerNodeBuilder.java b/source/mir/producer/reader/ProducerNodeBuilder.java new file mode 100755 index 00000000..c298761c --- /dev/null +++ b/source/mir/producer/reader/ProducerNodeBuilder.java @@ -0,0 +1,34 @@ +package mir.producer.reader; + +import java.util.*; +import mir.producer.*; + +public interface ProducerNodeBuilder { + public void setAttributes(Map anAttributes) throws ProducerConfigExc; + public void setSubNode(String aName, ProducerNode aNode) throws ProducerConfigExc; + public Set getAvailableSubNodes() throws ProducerConfigExc; + public ProducerNode constructNode() throws ProducerConfigExc; + + public interface ProducerNodeBuilderFactory { + public ProducerNodeBuilder makeBuilder() throws ProducerConfigExc; + } + + public class DefaultProducerNodeBuilderFactory implements ProducerNodeBuilderFactory { + private Class producerNodeBuilderClass; + + public DefaultProducerNodeBuilderFactory(Class aProducerNodeBuilderClass) { + producerNodeBuilderClass = aProducerNodeBuilderClass; + } + + public ProducerNodeBuilder makeBuilder() throws ProducerConfigExc{ + try { + return (ProducerNodeBuilder) producerNodeBuilderClass.newInstance(); + } + catch (Throwable t) { + throw new ProducerConfigFailure(t); + } + } + } +} + + diff --git a/source/mir/producer/reader/ProducerNodeBuilderLibrary.java b/source/mir/producer/reader/ProducerNodeBuilderLibrary.java new file mode 100755 index 00000000..4a7be95b --- /dev/null +++ b/source/mir/producer/reader/ProducerNodeBuilderLibrary.java @@ -0,0 +1,31 @@ +package mir.producer.reader; + +import java.util.*; +import mir.producer.*; + +public class ProducerNodeBuilderLibrary { + private Map nodeBuilders; + + public ProducerNodeBuilderLibrary() { + nodeBuilders = new HashMap(); + } + + public void registerBuilder(String aName, Class aProducerNodeBuilderClass) { + registerFactory(aName, new ProducerNodeBuilder.DefaultProducerNodeBuilderFactory( aProducerNodeBuilderClass )); + } + + public void registerFactory(String aName, ProducerNodeBuilder.ProducerNodeBuilderFactory aFactory) { + nodeBuilders.put(aName, aFactory); + } + + public boolean hasBuilderForName(String aName) { + return nodeBuilders.containsKey(aName); + } + + public ProducerNodeBuilder constructBuilder(String aName) throws ProducerConfigExc { + if (hasBuilderForName(aName)) + return ((ProducerNodeBuilder.ProducerNodeBuilderFactory) nodeBuilders.get(aName)).makeBuilder(); + else + throw new ProducerConfigExc("ProducerNodeBuilder: no builder with name '" + aName + "' found."); + } +} diff --git a/source/mir/producer/reader/ReaderTool.java b/source/mir/producer/reader/ReaderTool.java new file mode 100755 index 00000000..376196f3 --- /dev/null +++ b/source/mir/producer/reader/ReaderTool.java @@ -0,0 +1,63 @@ +package mir.producer.reader; + +import java.util.*; + +public class ReaderTool { + + public static void checkValidIdentifier(String anIdentifier) throws ProducerConfigExc { + } + + public static String getStringAttributeWithDefault(Map anAttributes, String aKey, String aDefault) { + if (anAttributes.containsKey(aKey)) + return (String) anAttributes.get(aKey); + else + return aDefault; + } + + public static void checkIntegerAttribute(Map anAttributes, String aKey) throws ProducerConfigExc { + try { + Integer.parseInt((String) anAttributes.get(aKey)); + } + catch (Throwable t) { + throw new ProducerConfigExc("attribute '"+aKey+"' is not an integer" ); + } + } + + public static int getIntegerAttributeWithDefault(Map anAttributes, String aKey, int aDefault) throws ProducerConfigExc { + String value; + + if (anAttributes.containsKey(aKey)) { + checkIntegerAttribute(anAttributes, aKey); + return Integer.parseInt((String) anAttributes.get(aKey)); + } + else + return aDefault; + } + + public static void checkAttributes(Map anAttributes, String[] aRequiredAttributes, String[] anOptionalAttributes) throws ProducerConfigExc { + checkAttributeSet(anAttributes.keySet(), + new HashSet(Arrays.asList(aRequiredAttributes)), + new HashSet(Arrays.asList(anOptionalAttributes))); + } + + public static void checkAttributeSet(Set aSet, Set aRequiredElements, Set anOptionalElements) throws ProducerConfigExc{ + Iterator i; + + i = aSet.iterator(); + while (i.hasNext()) { + Object item = i.next(); + + if (!(aRequiredElements.contains(item) || anOptionalElements.contains(item))) + throw new ProducerConfigExc("unknown attribute '" + item + "'" ); + } + + i = aRequiredElements.iterator(); + while (i.hasNext()) { + Object item = i.next(); + + if (!(aSet.contains(item))) + throw new ProducerConfigExc("missing required attribute '" + item + "'" ); + } + + } +} \ No newline at end of file diff --git a/source/mir/producer/reader/ScriptedProducerFactory.java b/source/mir/producer/reader/ScriptedProducerFactory.java new file mode 100755 index 00000000..985686e2 --- /dev/null +++ b/source/mir/producer/reader/ScriptedProducerFactory.java @@ -0,0 +1,43 @@ +package mir.producer.reader; + +import java.util.*; +import mir.producer.*; + +public class ScriptedProducerFactory implements ProducerFactory { + private Map verbs; + private ProducerNode body; + private String defaultVerb; + + public ScriptedProducerFactory(Map aVerbs, ProducerNode aBody, String aDefaultVerb) { + verbs = aVerbs; + body = aBody; + defaultVerb = aDefaultVerb; + } + + public ScriptedProducerFactory(Map aVerbs, ProducerNode aBody) { + this(aVerbs, aBody, null); + } + + public Producer makeProducer(String aVerb, Map aStartingValues) throws ProducerFailure, ProducerExc { + CompositeProducerNode rootNode; + ProducerNode verbNode; + + if (verbs.containsKey(aVerb)) { + verbNode = (ProducerNode) verbs.get(aVerb); + } + else if (defaultVerb!=null && verbs.containsKey(defaultVerb)) { + verbNode = (ProducerNode) verbs.get(defaultVerb); + } + else throw new ProducerExc("Undefined verb: " + aVerb); + + rootNode = new CompositeProducerNode(); + rootNode.addSubNode(verbNode); + rootNode.addSubNode(body); + + return new NodedProducer(rootNode, aVerb, aStartingValues); + }; + + public Iterator verbs() { + return verbs.keySet().iterator(); + } +} \ No newline at end of file diff --git a/source/mir/producer/reader/ScriptedProducerNode.java b/source/mir/producer/reader/ScriptedProducerNode.java new file mode 100755 index 00000000..08925cb7 --- /dev/null +++ b/source/mir/producer/reader/ScriptedProducerNode.java @@ -0,0 +1,56 @@ +package mir.producer.reader; + +import java.util.*; +import java.io.*; +import mir.producer.*; +import mir.util.*; + +public class ScriptedProducerNode implements ProducerNode { + private ScriptedProducerNodeDefinition definition; + private Map parameterValues; + private Map nodeParameterValues; + + public ScriptedProducerNode(ScriptedProducerNodeDefinition aDefinition, Map aParameterValues, Map aNodeParameterValues) { + definition = aDefinition; + parameterValues = new HashMap(); + parameterValues.putAll(aParameterValues); + nodeParameterValues = new HashMap(); + nodeParameterValues.putAll(aNodeParameterValues); + } + + public Set buildVerbSet() { + return new HashSet(); + } + + public void produce(Map aValues, String aVerb, PrintWriter aLogger) throws ProducerFailure, ProducerExc { + try { + Map oldValues = ScriptedProducerNodeTool.saveMapValues(aValues, definition.getParameters().keySet()); + try { + Iterator i = parameterValues.entrySet().iterator(); + + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + + if (entry.getValue() instanceof String) { + aValues.put(entry.getKey(), ParameterExpander.expandExpression(aValues, (String) entry.getValue())); + } + } + + ScriptedProducerNodeTool.pushNodeParameterValues(aValues, definition.getName(), nodeParameterValues); + try { + definition.getBody().produce(aValues, aVerb, aLogger); + } + finally { + ScriptedProducerNodeTool.popNodeParameterValues(aValues, definition.getName()); + } + } + finally { + ScriptedProducerNodeTool.restoreMapValues(aValues, definition.getParameters().keySet(), oldValues); + } + } + catch (Exception e) { + throw new ProducerFailure(e); + } + } + +} \ No newline at end of file diff --git a/source/mir/producer/reader/ScriptedProducerNodeDefinition.java b/source/mir/producer/reader/ScriptedProducerNodeDefinition.java new file mode 100755 index 00000000..e12b8d5f --- /dev/null +++ b/source/mir/producer/reader/ScriptedProducerNodeDefinition.java @@ -0,0 +1,100 @@ +package mir.producer.reader; + +import java.util.*; +import java.io.*; +import mir.producer.*; + +public class ScriptedProducerNodeDefinition { + private Map parameters; // name -> default value + private Set nodeParameters; + private ProducerNode body; + private String name; + + public static String SCRIPTED_PRODUCERNODE_RUNTIMEDATA_KEY = "$SCRIPTRUNTIMEDATA"; + public static String SCRIPTED_PRODUCERNODE_RUNTIMESTACK_KEY = "stack"; + + public ScriptedProducerNodeDefinition(String aName) { + name = aName; + parameters = new HashMap(); + nodeParameters = new HashSet(); + body = new CompositeProducerNode(); + } + + public void addParameter(String aName, String aDefaultValue) { + parameters.put(aName, aDefaultValue); + } + + public void addNodeParameter(String aName) { + nodeParameters.add(aName); + } + + public void setBody(ProducerNode aBody) { + body = aBody; + } + + protected Map getParameters() { + return parameters; + } + + protected Set getNodeParameters() { + return nodeParameters; + } + + protected ProducerNode getBody() { + return body; + } + + public String getName() { + return name; + } + + public Set getRequiredAttributes() { + return getAttributesSelection(true); + } + + public Set getOptionalAttributes() { + return getAttributesSelection(false); + } + + public Set getAttributesSelection(boolean aRequired) { + Set result = new HashSet(); + Iterator i = parameters.entrySet().iterator(); + + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + + if ((entry.getValue() == null) == aRequired ) { + result.add(entry.getKey()); + } + } + + return result; + } + + + protected static class NodeParameterProducerNode implements ProducerNode { + private String parameterName; + private String definitionName; + + public NodeParameterProducerNode(String aDefinitionName, String aParameterName) { + definitionName = aDefinitionName; + parameterName = aParameterName; + } + + public void produce(Map aValues, String aVerb, PrintWriter aLogger) throws ProducerExc, ProducerFailure { + ProducerNode producerNode; + + Map runTimeData = (Map) ((Map) aValues.get(SCRIPTED_PRODUCERNODE_RUNTIMEDATA_KEY)).get(definitionName); + Map parameters = (Map) ((Stack) runTimeData.get( SCRIPTED_PRODUCERNODE_RUNTIMESTACK_KEY )).peek(); + + producerNode = (ProducerNode) parameters.get(parameterName); + + if (producerNode != null) + producerNode.produce(aValues, aVerb, aLogger); + } + + public Set buildVerbSet() { + return new HashSet(); + } + } +} \ No newline at end of file diff --git a/source/mir/producer/reader/ScriptedProducerNodeTool.java b/source/mir/producer/reader/ScriptedProducerNodeTool.java new file mode 100755 index 00000000..76c7fa57 --- /dev/null +++ b/source/mir/producer/reader/ScriptedProducerNodeTool.java @@ -0,0 +1,56 @@ +package mir.producer.reader; + +import java.util.*; + +public class ScriptedProducerNodeTool { + + public static Object getOrMakeMapValueForKey(Map aMap, Object aKey, Object aDefaultValue) { + if (aMap.containsKey(aKey)) + return aMap.get(aKey); + else { + aMap.put(aKey, aDefaultValue); + return aDefaultValue; + } + } + + public static Stack getRunTimeStack(Map aProductionMap, String aDefinitionName) { + Map runtimeData = (Map) getOrMakeMapValueForKey(aProductionMap, ScriptedProducerNodeDefinition.SCRIPTED_PRODUCERNODE_RUNTIMEDATA_KEY, new HashMap()); + runtimeData = (Map) getOrMakeMapValueForKey(runtimeData, aDefinitionName, new HashMap()); + return (Stack) getOrMakeMapValueForKey(runtimeData, ScriptedProducerNodeDefinition.SCRIPTED_PRODUCERNODE_RUNTIMESTACK_KEY, new Stack()); + } + + public static void pushNodeParameterValues(Map aProductionMap, String aDefinitionName, Map aNodeParameterValues) { + Stack runtimeStack = getRunTimeStack(aProductionMap, aDefinitionName); + runtimeStack.push(aNodeParameterValues); + } + + public static void popNodeParameterValues(Map aProductionMap, String aDefinitionName) { + Stack runtimeStack = getRunTimeStack(aProductionMap, aDefinitionName); + runtimeStack.pop(); + } + + public static Map saveMapValues(Map aMap, Set aKeys) { + Map result = new HashMap(); + Iterator i = aKeys.iterator(); + + while (i.hasNext()) { + Object key = i.next(); + if (aMap.containsKey(key)) + result.put(key, aMap.get(key)); + } + + return result; + } + + public static void restoreMapValues(Map aMap, Set aKeys, Map aSavedValues) { + Iterator i = aKeys.iterator(); + + while (i.hasNext()) { + Object key = i.next(); + if (aSavedValues.containsKey(key)) + aMap.put(key, aSavedValues.get(key)); + else + aMap.remove(key); + } + } +} \ No newline at end of file diff --git a/source/mir/storage/Database.java b/source/mir/storage/Database.java index c3a6e937..d82264c2 100755 --- a/source/mir/storage/Database.java +++ b/source/mir/storage/Database.java @@ -27,10 +27,13 @@ import mir.misc.*; * Treiber, Host, User und Passwort, ueber den der Zugriff auf die * Datenbank erfolgt. * - * @version $Revision: 1.21 $ $Date: 2002/08/04 23:38:22 $ + * @version $Revision: 1.22 $ $Date: 2002/08/25 19:00:09 $ * @author $Author: mh $ * * $Log: Database.java,v $ + * Revision 1.22 2002/08/25 19:00:09 mh + * merge of localization branch into HEAD. mh and zap + * * Revision 1.21 2002/08/04 23:38:22 mh * fix up the webdb_create update stuff * @@ -592,6 +595,7 @@ public class Database implements StorageObject { int size = metadataFields.size(); for (int i = 0; i < size; i++) { // alle durchlaufen bis nix mehr da + theType = metadataTypes[i]; if (theType == java.sql.Types.LONGVARBINARY) { InputStreamReader is = (InputStreamReader)rs.getCharacterStream(i + 1); @@ -1061,7 +1065,9 @@ public class Database implements StorageObject { throws SQLException,StorageObjectException { long startTime = System.currentTimeMillis(); - String sql = "SELECT count(*) FROM "+ theTable + " where " + where; + String sql = "SELECT Count(*) FROM "+ theTable; + if (where != null && !(where.length() == 0)) + sql = sql + " where " + where; Connection con = null; Statement stmt = null; int result = 0; diff --git a/source/mir/util/CachingRewindableIterator.java b/source/mir/util/CachingRewindableIterator.java new file mode 100755 index 00000000..77be4ba4 --- /dev/null +++ b/source/mir/util/CachingRewindableIterator.java @@ -0,0 +1,45 @@ +package mir.util; + +import java.util.*; + +import java.util.*; +import mir.storage.*; +import mir.util.*; +import mir.entity.*; + +public class CachingRewindableIterator implements RewindableIterator { + private Iterator master; + private List cachedItems; + private int iterationPosition; + + public CachingRewindableIterator(Iterator anIterator) { + master = anIterator; + cachedItems = new Vector(); + iterationPosition = 0; + } + + public boolean hasNext() { + return iterationPosition=cachedItems.size()) { + cachedItems.add(master.next()); + } + + result = cachedItems.get(iterationPosition); + iterationPosition++; + + return result; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void rewind() { + iterationPosition=0; + }; +} \ No newline at end of file diff --git a/source/mir/util/DateToMapAdapter.java b/source/mir/util/DateToMapAdapter.java new file mode 100755 index 00000000..cd77de15 --- /dev/null +++ b/source/mir/util/DateToMapAdapter.java @@ -0,0 +1,37 @@ +package mir.util; + +import java.util.*; +import java.text.*; + +import mir.misc.*; + +public class DateToMapAdapter extends AbstractMap { + Date date; + + public DateToMapAdapter(Date aDate) { + date = aDate; + } + + public Object get(Object aKey) { + if (aKey instanceof String) { + try { + // ML: quick fix to allow for the dc encoding now... + if (((String) aKey).equals("dc")) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(date); + return StringUtil.date2w3DateTime(calendar); + } + else + return new SimpleDateFormat((String) aKey).format(date); + } + catch (Throwable t) { + throw new RuntimeException( "Can't format date with format " + (String) aKey + ": " + t.getMessage()); + } + } + else return null; + } + + public Set entrySet() { + return new HashSet(); + } +} \ No newline at end of file diff --git a/source/mir/util/FileMonitor.java b/source/mir/util/FileMonitor.java new file mode 100755 index 00000000..a35dc675 --- /dev/null +++ b/source/mir/util/FileMonitor.java @@ -0,0 +1,35 @@ +package mir.util; + +import java.util.*; +import java.io.*; + +public class FileMonitor { + private Map files; + + public FileMonitor() { + files = new HashMap(); + } + + public void addFile(File aFile) { + files.put(aFile, new Long(aFile.lastModified())); + } + + public void clear() { + files.clear(); + } + + public boolean hasChanged() { + Iterator i = files.entrySet().iterator(); + + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + File file = (File) entry.getKey(); + Long lastModified = (Long) entry.getValue(); + + if (lastModified.longValue()!=file.lastModified()) + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/source/mir/util/GeneratorHTMLFunctions.java b/source/mir/util/GeneratorHTMLFunctions.java new file mode 100755 index 00000000..e46db001 --- /dev/null +++ b/source/mir/util/GeneratorHTMLFunctions.java @@ -0,0 +1,44 @@ +package mir.util; + +import java.util.*; +import java.net.*; + +import mir.misc.*; +import mir.generator.*; + +/** + *

Title:

+ *

Description:

+ *

Copyright: Copyright (c) 2002

+ *

Company:

+ * @author unascribed + * @version 1.0 + */ + +public class GeneratorHTMLFunctions { + private GeneratorHTMLFunctions() {} + + public static class encodeURIGeneratorFunction implements Generator.GeneratorFunction { + public Object perform(List aParameters) throws GeneratorExc { + if (aParameters.size()!=1) + throw new GeneratorExc("encodeHTMLGeneratorFunction: only 1 parameter expected"); + if (!(aParameters.get(0) instanceof String)) + throw new GeneratorExc("encodeHTMLGeneratorFunction: parameter must be a string"); + + return URLEncoder.encode((String) aParameters.get(0)); + }; + } + + public static class encodeHTMLGeneratorFunction implements Generator.GeneratorFunction { + public Object perform(List aParameters) throws GeneratorExc { + if (aParameters.size()!=1) + throw new GeneratorExc("encodeHTMLGeneratorFunction: only 1 parameter expected"); + if (!(aParameters.get(0) instanceof String)) + throw new GeneratorExc("encodeHTMLGeneratorFunction: parameter must be a string"); + + // YUCK! -mh + //return StringUtil.encodeHtml((String) aParameters.get(0)); + return (String) aParameters.get(0); + }; + } +} diff --git a/source/mir/util/NullWriter.java b/source/mir/util/NullWriter.java new file mode 100755 index 00000000..8bb9027d --- /dev/null +++ b/source/mir/util/NullWriter.java @@ -0,0 +1,18 @@ +package mir.util; + +import java.io.*; + +public class NullWriter extends Writer { + + public NullWriter() { + } + + public void close() { + } + + public void flush() { + } + + public void write(char[] cbuf, int off, int len) { + } +} \ No newline at end of file diff --git a/source/mir/util/ParameterExpander.java b/source/mir/util/ParameterExpander.java new file mode 100755 index 00000000..8db5736a --- /dev/null +++ b/source/mir/util/ParameterExpander.java @@ -0,0 +1,831 @@ +package mir.util; + +import multex.Failure; +import multex.Exc; + +import java.util.*; + +public class ParameterExpander { + final static String NODE_SEPARATOR = "."; + final static char STRING_ESCAPE_CHARACTER = '\\'; + + public static List splitString(String aString, String aSeparator) { + List result= new Vector(); + int previousPosition = 0; + int position; + int endOfNamePosition; + + while ((position = aString.indexOf(aSeparator, previousPosition))>=0) { + result.add(aString.substring(previousPosition, position)); + previousPosition = position + aSeparator.length(); + } + + result.add(aString.substring(previousPosition, aString.length())); + + return result; + } + + private static Object findNode(String aKey, Map aMap, List aParts, boolean aMakeIfNotPresent) throws Exception { + Iterator i; + String location = ""; + Object node = aMap; + Object newNode; + + i = aParts.iterator(); + + while (i.hasNext()) { + String part = (String) i.next(); + + if (!(node instanceof Map)) { + throw new Exception( "Can't expand key " + aKey + ": " + location + " is not a map" ); + } + + if (location.length()>0) { + location=location + NODE_SEPARATOR; + } + location = location + part; + + newNode = ((Map) node).get(part); + + if (newNode == null) + if (aMakeIfNotPresent) { + newNode = new HashMap(); + ((Map) node).put(part, newNode); + } + else + throw new ParameterExpanderExc( "Can't expand key {1}: {2} does not exist", new Object[]{aKey,location} ); + + node = newNode; + } + + return node; + } + + public static Object findValueForKey(Map aMap, String aKey) throws Exception { + Object node; + List parts = splitString(aKey, NODE_SEPARATOR); + + node = findNode(aKey, aMap, parts, false); + + return node; + } + + public static String findStringForKey(Map aMap, String aKey) throws Exception { + Object expandedValue = findValueForKey(aMap, aKey); + + if (!(expandedValue instanceof String)) + throw new ParameterExpanderExc( "Value of key is not a string but a {1}", new Object[]{expandedValue.getClass().getName()} ); + + return (String) expandedValue; + } + + public static void setValueForKey(Map aMap, String aKey, Object aValue) throws Exception { + List parts = splitString(aKey, NODE_SEPARATOR); + + String key = (String) parts.get(parts.size()-1); + parts.remove(parts.size()-1); + + Object node=findNode(aKey, aMap, parts, true); + + if (node instanceof Map) { + ((Map) node).put(key, aValue); + } + else + throw new ParameterExpanderExc( "Can't set key {1}: not inside a Map", new Object[]{aKey} ); + } + + public static String expandExpression(Map aMap, String anExpression) throws Exception { + int previousPosition = 0; + int position; + int endOfExpressionPosition; + StringBuffer result = new StringBuffer(); + + while ((position=anExpression.indexOf("$", previousPosition))>=0) { + result.append(anExpression.substring(previousPosition, position)); + + if (position>=anExpression.length()-1) { + result.append(anExpression.substring(position, anExpression.length())); + previousPosition=anExpression.length(); + } + else + { + if (anExpression.charAt(position+1) == '{') { + endOfExpressionPosition=position+2; + while (endOfExpressionPosition" + data.substring(position) ; + } + } + + private static abstract class Token { + } + + public static abstract class PunctuationToken extends Token { public PunctuationToken() { }; } + private static class LeftSquareBraceToken extends PunctuationToken {}; + private static class RightSquareBraceToken extends PunctuationToken {}; + private static class EqualsToken extends PunctuationToken {}; + private static class EqualsNotToken extends PunctuationToken {}; + private static class NOTToken extends PunctuationToken {}; + private static class LeftParenthesisToken extends PunctuationToken {}; + private static class RightParenthesisToken extends PunctuationToken {}; + private static class CommaToken extends PunctuationToken {}; + private static class PeriodToken extends PunctuationToken {}; + private static class PlusToken extends PunctuationToken {}; + private static class TimesToken extends PunctuationToken {}; + private static class DivideToken extends PunctuationToken {}; + private static class MinusToken extends PunctuationToken {}; + private static class ConcatenateToken extends PunctuationToken {}; + private static class LessThanOrEqualsToken extends PunctuationToken {}; + private static class GreaterThanOrEqualsToken extends PunctuationToken {}; + private static class LessThanToken extends PunctuationToken {}; + private static class GreaterThanToken extends PunctuationToken {}; + + + private static class IdentifierToken extends Token { + private String name; + + public IdentifierToken(String aName) { + name = aName; + } + + public String getName() { + return name; + } + + } + + private static class LiteralToken extends Token { + private Object value; + + public LiteralToken(Object aValue) { + value = aValue; + } + + public Object getValue() { + return value; + } + } + + private static class Scanner { + private Reader reader; + private Token nextToken; + private String positionString; + + public Scanner(Reader aReader) { + reader = aReader; + skipWhitespace(); + positionString = reader.getPositionString(); + } + + public Token scanStringLiteral() { + StringBuffer result = new StringBuffer(); + Character delimiter; + + delimiter = reader.getNext(); + + while (reader.hasNext() && !reader.peek().equals(delimiter)) { + if (reader.peek().charValue()==STRING_ESCAPE_CHARACTER) { + reader.getNext(); + if (reader.hasNext()) + result.append(reader.getNext()); + } + else { + result.append(reader.getNext()); + } + } + + if (!reader.hasNext()) + throw new RuntimeException("unterminated string"); + else + reader.getNext(); + + return new LiteralToken(result.toString()); + } + + public String getPositionString() { + return positionString; + } + + private Token scanNumber() { + StringBuffer result = new StringBuffer(); + result.append(reader.getNext()); + + while (reader.hasNext() && isNumberRest(reader.peek().charValue())) { + result.append(reader.getNext()); + } + + try { + return new LiteralToken(new Integer(Integer.parseInt(result.toString()))); + } + catch (NumberFormatException e) { + throw new RuntimeException("Invalid number: " + e.getMessage()); + } + } + + private Token scanIdentifierKeyword() { + StringBuffer result = new StringBuffer(); + result.append(reader.getNext()); + + while (reader.hasNext() && isIdentifierRest(reader.peek().charValue())) { + result.append(reader.getNext()); + } + + return new IdentifierToken(result.toString()); + } + + private Token scanPunctuation() { + Character c; + + c = reader.getNext(); + + switch(c.charValue()) { + case '[': return new LeftSquareBraceToken(); + case ']': return new RightSquareBraceToken(); + case '=': + if (reader.hasNext() && reader.peek().charValue() == '=') { + reader.getNext(); + return new EqualsToken(); + } + else { + throw new RuntimeException("Unknown character: '='"); + } + + case '!': + if (reader.hasNext() && reader.peek().charValue() == '=') { + reader.getNext(); + return new EqualsNotToken(); + } + else { + return new NOTToken(); + } + + case '(': return new LeftParenthesisToken (); + + case ')': return new RightParenthesisToken (); + case ',': return new CommaToken (); + case '.': return new PeriodToken (); + case '+': + if (reader.hasNext() && reader.peek().charValue() == '+') { + reader.getNext(); + return new ConcatenateToken(); + } + else { + return new PlusToken (); + } + case '*': return new TimesToken (); + case '/': return new DivideToken (); + case '-': return new MinusToken (); + case '<': + if (reader.hasNext() && reader.peek().charValue() == '=') { + reader.getNext(); + return new LessThanOrEqualsToken(); + } + else { + return new LessThanToken(); + } + + case '>': + if (reader.hasNext() && reader.peek().charValue() == '=') { + reader.getNext(); + return new GreaterThanOrEqualsToken(); + } + else { + return new GreaterThanToken(); + } + default: + throw new RuntimeException("Unexpected character: "+c); + } + } + + public void skipWhitespace() { + while (reader.hasNext() && Character.isWhitespace(reader.peek().charValue())) + reader.getNext(); + }; + + private boolean isIdentifierStart(char c) { + return Character.isLetter(c) || (c == '_'); + } + + private boolean isIdentifierRest(char c) { + return Character.isLetterOrDigit(c) || (c == '_'); + } + + private boolean isNumberStart(char c) { + return Character.isDigit(c); + } + + private boolean isNumberRest(char c) { + return Character.isDigit(c); + } + + public Token scanNext() { + Token result = null; + + skipWhitespace(); + + if (reader.hasNext()) { + Character c = reader.peek(); + + switch(c.charValue()) { + case '\'': + case '"': + result = scanStringLiteral(); + break; + + default: { + if (isIdentifierStart(c.charValue())) { + result = scanIdentifierKeyword(); + } + else if (isNumberStart(c.charValue())) { + result = scanNumber(); + } + else + result = scanPunctuation(); + } + } + } + + skipWhitespace(); + + return result; + } + + public Token scan() { + Token result = peek(); + nextToken = null; + positionString = reader.getPositionString(); + + return result; + } + + public Token peek() { + if (nextToken==null) { + nextToken = scanNext(); + } + + return nextToken; + } + + public boolean hasToken() { + return peek()!=null; + } + } + + private static class Parser { + private Scanner scanner; + private Map valueMap; + + public Parser(String anExpression, Map aValueMap) { + scanner = new Scanner(new Reader(anExpression)); + valueMap = aValueMap; + } + + public boolean parseBoolean() { + try { + return interpretAsBoolean(parseWhole()); + } + catch (Throwable t) { + throw new RuntimeException("Parser error at '" + getLocation()+ "': "+t.getMessage()); + } + } + + public int parseInteger() { + try { + return interpretAsInteger(parseWhole()); + } + catch (Throwable t) { + throw new RuntimeException("Parser error at '" + getLocation()+ "': "+t.getMessage()); + } + } + + public String parseString() { + try { + return interpretAsString(parseWhole()); + } + catch (Throwable t) { + throw new RuntimeException("Parser error at '" + getLocation()+ "': "+t.getMessage()); + } + } + + private String getLocation() { + return scanner.getPositionString(); + } + + private Object parseWhole() { + Object result = parse(); + + if (scanner.hasToken()) { + throw new RuntimeException("Operator expected"); + } + + return result; + } + + private Object parse() { + return parseUntil(MAX_OPERATOR_LEVEL); + } + + private Object parseSet() { + Token token; + Object expression; + Set result = new HashSet(); + + token = scanner.scan(); + if (!(token instanceof LeftParenthesisToken)) { + throw new RuntimeException("( expected after in keyword"); + } + + if (scanner.peek() instanceof RightParenthesisToken) { + scanner.scan(); + return result; + } + + do { + expression = parse(); + + if (expression==null) { + throw new RuntimeException("expression expected"); + } + + result.add(expression); + + token = scanner.scan(); + } + while (token instanceof CommaToken); + + if (!(token instanceof RightParenthesisToken)) { + throw new RuntimeException(") or , expected"); + } + + return result; + } + + private Object parseVariable() { + boolean done; + Token token; + Object currentValue = valueMap; + Object qualifier; + + do { + token = scanner.scan(); + if (token instanceof LeftSquareBraceToken) { + qualifier = parseUntil(MAX_OPERATOR_LEVEL); + token = scanner.scan(); + if (!(token instanceof RightSquareBraceToken)) + throw new RuntimeException("] expected"); + } + else if (token instanceof IdentifierToken) { + qualifier = ((IdentifierToken) token).getName(); + } + else + throw new RuntimeException("fieldname or [ expected"); + + if (currentValue!=null) { + if (currentValue instanceof Map) { + currentValue = ((Map) currentValue).get(qualifier); + } + else { + throw new RuntimeException("cannot reference into anything other than a map"); + } + } + else { + // throw? or allow null.null? + } + + if (scanner.peek() instanceof PeriodToken) + { + scanner.scan(); + done = false; + } + else + done = true; + } while (!done); + + return currentValue; + } + + + private Object parseUntil(int aMaxOperatorLevel) { + Token token = scanner.peek(); + Object value; + + if (token instanceof LeftParenthesisToken) { + scanner.scan(); + value = parse(); + token = scanner.peek(); + if (!(token instanceof RightParenthesisToken)) + throw new RuntimeException(") expected"); + scanner.scan(); + } + else if (isUnaryOperator(token)) { + scanner.scan(); + value = parseUntil(unaryOperatorLevel(token)); + value = expandOperatorExpression(token, value); + } + else if (token instanceof IdentifierToken) { + value = parseVariable(); + } + else if (token instanceof LiteralToken) { + scanner.scan(); + value = ((LiteralToken) token).getValue(); + } + else + throw new RuntimeException("Expression expected"); + + token = scanner.peek(); + + while (isBinaryOperator(token) && binaryOperatorLevel(token)= in < > + private static final int ADDITION_OPERATOR_LEVEL = 3; // + - & + private static final int MULTIPLICATION_OPERATOR_LEVEL = 2; // * / + + private int unaryOperatorLevel(Token aToken) { + if (aToken instanceof NOTToken) + return LOGICAL_OPERATOR_LEVEL; + else if (aToken instanceof MinusToken) + return ADDITION_OPERATOR_LEVEL; + + throw new RuntimeException("Internal error: unknown unary operator: " + aToken.getClass().getName()); + } + + private boolean isUnaryOperator(Token aToken) { + return + ((aToken instanceof NOTToken) || + (aToken instanceof MinusToken)); + } + + private int binaryOperatorLevel(Token aToken) { + if (isANDOperator(aToken) || + isOROperator(aToken)) + return LOGICAL_OPERATOR_LEVEL; + + if ((aToken instanceof EqualsToken) || + (aToken instanceof EqualsNotToken) || + (aToken instanceof LessThanOrEqualsToken) || + (aToken instanceof LessThanToken) || + (aToken instanceof GreaterThanOrEqualsToken) || + (aToken instanceof GreaterThanToken) || + isINOperator(aToken)) + return COMPARISON_OPERATOR_LEVEL; + + if ((aToken instanceof PlusToken) || + (aToken instanceof ConcatenateToken) || + (aToken instanceof MinusToken)) + return ADDITION_OPERATOR_LEVEL; + + if ((aToken instanceof TimesToken) || + (aToken instanceof DivideToken)) + return MULTIPLICATION_OPERATOR_LEVEL; + + throw new RuntimeException("Internal error: unknown binary operator: " + aToken.getClass().getName()); + } + + private boolean isINOperator(Token aToken) { + return (aToken instanceof IdentifierToken && ((IdentifierToken) aToken).getName().equals("in")); + } + + private boolean isANDOperator(Token aToken) { + return (aToken instanceof IdentifierToken && ((IdentifierToken) aToken).getName().equals("and")); + } + + private boolean isOROperator(Token aToken) { + return (aToken instanceof IdentifierToken && ((IdentifierToken) aToken).getName().equals("or")); + } + + private boolean isBinaryOperator(Token aToken) { + return + (aToken instanceof EqualsToken) || + (aToken instanceof EqualsNotToken) || + (aToken instanceof PlusToken) || + (aToken instanceof TimesToken) || + (aToken instanceof DivideToken) || + (aToken instanceof MinusToken) || + (aToken instanceof ConcatenateToken) || + (aToken instanceof LessThanOrEqualsToken) || + (aToken instanceof LessThanToken) || + (aToken instanceof GreaterThanOrEqualsToken) || + (aToken instanceof GreaterThanToken) || + isINOperator(aToken) || + isOROperator(aToken) || + isANDOperator(aToken); + } + + private boolean interpretAsBoolean(Object aValue) { + if (aValue instanceof Boolean) + return ((Boolean) aValue).booleanValue(); + + return aValue!=null; + } + + private int interpretAsInteger(Object aValue) { + if (aValue instanceof Integer) + return ((Integer) aValue).intValue(); + + if (aValue instanceof String) { + try { + return Integer.parseInt((String) aValue); + } + catch (Throwable t) { + } + } + + throw new RuntimeException("Not an integer"); + } + + private String interpretAsString(Object aValue) { + if (aValue instanceof String) + return (String) aValue; + if (aValue instanceof Integer) + return ((Integer) aValue).toString(); + + throw new RuntimeException("Not a string"); + } + + private Object expandOperatorExpression(Token aToken, Object aValue) { + if (aToken instanceof NOTToken) + return new Boolean(!interpretAsBoolean(aValue)); + else if (aToken instanceof MinusToken) + return new Integer(-interpretAsInteger(aValue)); + + throw new RuntimeException("Internal error: unknown unary operator: " + aToken.getClass().getName()); + } + + private boolean areEqual(Object aValue1, Object aValue2) { + if (aValue1==null || aValue2==null) + return (aValue1==null) && (aValue2==null); + else + return aValue1.equals(aValue2); + } + + private Object expandOperatorExpression(Token aToken, Object aValue1, Object aValue2) { + if (isINOperator(aToken)) { + if (!(aValue2 instanceof Set)) { + throw new RuntimeException("Internal error: set expected"); + } + + Iterator i = ((Set) aValue2).iterator(); + + while (i.hasNext()) { + if (areEqual(aValue1, i.next())) + return Boolean.TRUE; + } + + return Boolean.FALSE; + } + + if (isANDOperator(aToken)) + return new Boolean(interpretAsBoolean(aValue1) && interpretAsBoolean(aValue2)); + if (isOROperator(aToken)) + return new Boolean(interpretAsBoolean(aValue1) || interpretAsBoolean(aValue2)); + if (aToken instanceof EqualsToken) { + return new Boolean(areEqual(aValue1, aValue2)); + } + if (aToken instanceof EqualsNotToken) + return new Boolean(!areEqual(aValue1, aValue2)); + if (aToken instanceof PlusToken) + return new Integer(interpretAsInteger(aValue1) + interpretAsInteger(aValue2)); + if (aToken instanceof TimesToken) + return new Integer(interpretAsInteger(aValue1) * interpretAsInteger(aValue2)); + if (aToken instanceof DivideToken) + return new Integer(interpretAsInteger(aValue1) / interpretAsInteger(aValue2)); + if (aToken instanceof MinusToken) + return new Integer(interpretAsInteger(aValue1) - interpretAsInteger(aValue2)); + + if (aToken instanceof ConcatenateToken) + return interpretAsString(aValue1) + interpretAsString(aValue2); + + if (aToken instanceof LessThanOrEqualsToken) + return new Boolean(interpretAsInteger(aValue1) <= interpretAsInteger(aValue2)); + if (aToken instanceof LessThanToken) + return new Boolean(interpretAsInteger(aValue1) < interpretAsInteger(aValue2)); + if (aToken instanceof GreaterThanOrEqualsToken) + return new Boolean(interpretAsInteger(aValue1) >= interpretAsInteger(aValue2)); + if (aToken instanceof GreaterThanToken) + return new Boolean(interpretAsInteger(aValue1) > interpretAsInteger(aValue2)); + + throw new RuntimeException("Internal error: unknown binary operator: " + aToken.getClass().getName()); + } + } + + public static class ParameterExpanderExc extends Exc { + public ParameterExpanderExc(String msg, Object[] objects) { + super(msg, objects); + } + } +} \ No newline at end of file diff --git a/source/mir/util/ResourceBundleGeneratorFunction.java b/source/mir/util/ResourceBundleGeneratorFunction.java new file mode 100755 index 00000000..fc0152e3 --- /dev/null +++ b/source/mir/util/ResourceBundleGeneratorFunction.java @@ -0,0 +1,37 @@ +package mir.util; + +import java.util.*; +import org.apache.struts.util.MessageResources; + +import mir.generator.*; + +public class ResourceBundleGeneratorFunction implements Generator.GeneratorFunction { + private MessageResources messages; + private Locale locale; + + public ResourceBundleGeneratorFunction(Locale aLocale, MessageResources aMessages) { + this.locale = aLocale; + this.messages = aMessages; + } + + public Object perform(List aParameters) throws GeneratorExc { + List extraParameters = new Vector(aParameters); + + if (aParameters.size()<1) + throw new GeneratorExc("ResourceBundleGeneratorFunction: at least 1 parameter expected"); + + if (!(aParameters.get(0) instanceof String)) + throw new GeneratorExc("encodeHTMLGeneratorFunction: parameters must be strings"); + + String key = (String) aParameters.get(0); + extraParameters.remove(0); + String message = messages.getMessage(locale, key, extraParameters.toArray()); + + if (message == null) { + return new String("??" + key + "??"); + } + else { + return message; + } + }; +} \ No newline at end of file diff --git a/source/mir/util/RewindableIterator.java b/source/mir/util/RewindableIterator.java new file mode 100755 index 00000000..23f8e55a --- /dev/null +++ b/source/mir/util/RewindableIterator.java @@ -0,0 +1,7 @@ +package mir.util; + +import java.util.*; + +public interface RewindableIterator extends Iterator { + public void rewind(); +} \ No newline at end of file diff --git a/source/mircoders/global/JobQueue.java b/source/mircoders/global/JobQueue.java new file mode 100755 index 00000000..f60116f8 --- /dev/null +++ b/source/mircoders/global/JobQueue.java @@ -0,0 +1,136 @@ +package mircoders.global; + +import java.util.*; + +// important: objects passed as data must not be altered once put into a job + +public class JobQueue { + private Vector jobs; + private Map dataToJob; + + public static final int STATUS_PENDING = 0; + public static final int STATUS_PROCESSING = 1; + public static final int STATUS_PROCESSED = 2; + + public JobQueue() { + jobs = new Vector(); + dataToJob = new HashMap(); + } + + public void appendJob(Object aData) { + synchronized (jobs) { + Job job = new Job(aData); + jobs.add(job); + dataToJob.put(aData, job); + } + } + + public Object acquirePendingJob() { + synchronized (jobs) { + Iterator i = jobs.iterator(); + + while (i.hasNext()) { + Job job = (Job) i.next(); + + if (job.setProcessing()) { + return job.getData(); + } + } + } + + return null; + } + + public void flagOffJob(Object aData) { + synchronized (jobs) { + Job job = (Job) dataToJob.get(aData); + + if (job!=null) { + job.setProcessed(); + } + } + } + + public void cleanupJobs() { + synchronized (jobs) { + Iterator i = jobs.iterator(); + + while (i.hasNext()) { + Job job = (Job) i.next(); + + if (job.hasBeenProcessed()) { + i.remove(); + } + } + } + } + + public List makeJobListSnapshot() { + synchronized (jobs) { + return (List) jobs.clone(); + } + } + + public class Job implements Cloneable { + private Object data; + private int status; + + public Job(Object aData, int aStatus) { + data = aData; + status = aStatus; + } + + public Job(Object aData) { + this(aData, STATUS_PENDING); + } + + public Object getData() { + return data; + } + + public int getStatus() { + synchronized(this) { + return status; + } + } + + protected boolean setProcessing() { + return setStatus(STATUS_PENDING, STATUS_PROCESSING); + } + + protected void setProcessed() { + setStatus(STATUS_PROCESSING, STATUS_PROCESSED); + } + + public boolean hasBeenProcessed() { + return getStatus() == STATUS_PROCESSED; + } + + public boolean isProcessing() { + return getStatus() == STATUS_PROCESSING; + } + + public boolean isPending() { + return getStatus() == STATUS_PENDING; + } + + private boolean setStatus(int anOldStatus, int aNewStatus) { + synchronized(this) { + if (status == anOldStatus) { + status = aNewStatus; + return true; + } + else { + return false; + } + } + } + + protected Object clone() { + synchronized(this) { + return new Job(data, status); + } + } + } +} + diff --git a/source/mircoders/global/MirGlobal.java b/source/mircoders/global/MirGlobal.java new file mode 100755 index 00000000..28a4a3bc --- /dev/null +++ b/source/mircoders/global/MirGlobal.java @@ -0,0 +1,99 @@ +package mircoders.global; + +import mir.misc.*; +import mircoders.localizer.*; + +public class MirGlobal { + static private MirConfig configuration; + static private MirLocalizer localizer; + static private ProducerEngine producerEngine; + + public static MirLocalizer localizer() { + String localizerClassName; + Class localizerClass; + + if (localizer == null ) { + synchronized(MirGlobal.class) { + if (localizer == null ) { + localizerClassName = getConfigPropertyWithDefault("Mir.Localizer", "mirlocal.loaclizer.basic.MirBasicLocalizer"); + + try { + localizerClass = Class.forName(localizerClassName); + } + catch (Throwable t) { + throw new ConfigException("localizer class '" + localizerClassName + "' not found: " + t.toString()); + } + + if (!(MirLocalizer.class.isAssignableFrom(localizerClass))) + throw new ConfigException("localizer class '" + localizerClassName + "' is not assignable from MirLocalizer"); + + try { + localizer = new MirCachingLocalizerDecorator((MirLocalizer) localizerClass.newInstance()); + } + catch (Throwable t) { + throw new ConfigException("localizer class '" + localizerClassName + "' cannot be instantiated: " + t.toString()); + } + } + } + } + + return localizer; + } + + public static MirConfig config() { + if (configuration == null) { + configuration = new MirConfig(); + } + + return configuration; + } + + public static ProducerEngine producerEngine() { + if (producerEngine == null) { + producerEngine = new ProducerEngine(); + } + + return producerEngine; + } + + public static String getConfigPropertyWithDefault(String aPropertyName, String aDefault) { + String result; + + result = config().getProp(aPropertyName); + + if (result==null) + result = aDefault; + + return result; + } + + public static String getConfigProperty(String aPropertyName) { + String result; + + result = config().getProp(aPropertyName); + + if (result==null) + throw new ConfigException("Property '" + aPropertyName + "' not present"); + + return result; + } + + public static int getConfigIntegerProperty(String aPropertyName) { + String result; + + result = config().getProp(aPropertyName); + + return Integer.parseInt(result); + } + + public static boolean getConfigBooleanProperty(String aPropertyName) { + String result; + + result = config().getProp(aPropertyName); + + if (result==null) + throw new ConfigException("Boolean property '" + aPropertyName + "' not present"); + + return (result.equals("yes") || result.equals("1")); + } +} diff --git a/source/mircoders/global/ProducerEngine.java b/source/mircoders/global/ProducerEngine.java new file mode 100755 index 00000000..1fb9e606 --- /dev/null +++ b/source/mircoders/global/ProducerEngine.java @@ -0,0 +1,172 @@ +package mircoders.global; + +import java.util.*; +import java.io.*; +import mir.producer.*; +import mir.util.*; +import multex.Exc; +import multex.Failure; + +public class ProducerEngine { +// private Map producers; + private JobQueue producerJobQueue; + private Thread queueThread; + private PrintWriter log; + + protected ProducerEngine() { +// producers = MirGlobal.localizer().producers().factories(); + producerJobQueue = new JobQueue(); + try { + RandomAccessFile raFile = (new RandomAccessFile(MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Producer.Logfile"), "rw")); + raFile.seek(raFile.length()); + log = new PrintWriter(new FileWriter( raFile.getFD())); + } + catch (Exception e) { +// throw new ProducerEngineRuntimeExc("Creating PrintWriter log failed",e); + log = new PrintWriter(new NullWriter()); + } + queueThread = new Thread(new ProducerJobQueueThread()); + queueThread.start(); + } + + public void addJob(String aProducerFactory, String aVerb) { +// ML: TODO: should check if a similar job is already pending + producerJobQueue.appendJob(new ProducerJob(aProducerFactory, aVerb)); + log.println(aProducerFactory+"."+aVerb+" added to queue"); + log.flush(); + } + + public void printQueueStatus(PrintWriter aWriter) { + Iterator iterator = producerJobQueue.makeJobListSnapshot().iterator(); + producerJobQueue.cleanupJobs(); + JobQueue.Job job; + + while (iterator.hasNext()) { + job = (JobQueue.Job) iterator.next(); + ProducerJob producerJob = (ProducerJob) job.getData(); + + aWriter.print(producerJob.factoryName + "." + producerJob.verb); + if (job.hasBeenProcessed()) + aWriter.print(" processed"); + else if (job.isProcessing()) + aWriter.print(" processing"); + else if (job.isPending()) + aWriter.print(" pending"); + else + aWriter.print(" unknown status"); + + aWriter.println("
"); + } + } + + private void produceNow(String aProducerFactory, String aVerb, PrintWriter aLogger) { + try { + long startTime; + long endTime; + Map startingMap = new HashMap(); + + startTime = System.currentTimeMillis(); + + aLogger.println("Producing (" + aProducerFactory + "," + aVerb + ")"); + + ProducerFactory factory = (ProducerFactory) MirGlobal.localizer().producers().factories().get(aProducerFactory); + + if (factory == null ) + throw new Exception("No producer factory '"+aProducerFactory+"' present."); + + MirGlobal.localizer().producerAssistant().initializeGenerationValueSet(startingMap); + Producer producer = factory.makeProducer(aVerb, startingMap); + + producer.produce(aLogger); + + endTime = System.currentTimeMillis(); + + aLogger.println("Time: " + (endTime-startTime) + " ms
"); + } + catch (Throwable e) { + try { + aLogger.println("exception occurred:
"); + aLogger.println(e.getMessage()); + e.printStackTrace(aLogger); + } + catch (Throwable f) { + } + } + } + + private class ProducerJob { + private String factoryName; + private String verb; + + public ProducerJob(String aFactory, String aVerb) { + factoryName = aFactory; + verb = aVerb; + } + + public void execute() { + ProducerFactory factory; + Producer producer; + long startTime; + long endTime; + Map startingMap = new HashMap(); + + startTime = System.currentTimeMillis(); + log.println("Producing job: "+factoryName+"."+verb); + + try { + factory = (ProducerFactory) MirGlobal.localizer().producers().factories().get( factoryName ); + + if (factory!=null) { + MirGlobal.localizer().producerAssistant().initializeGenerationValueSet(startingMap); + + synchronized(factory) { + producer = factory.makeProducer(verb, startingMap); + } + if (producer!=null) { + producer.produce(log); + } + } + } + catch (Throwable t) { + log.println(" exception "+t.getMessage()); + t.printStackTrace(log); + } + log.println("Done producing job: "+factoryName+"."+verb); + endTime = System.currentTimeMillis(); + + log.println("Time: " + (endTime-startTime) + " ms"); + log.flush(); + } + } + + private class ProducerJobQueueThread implements Runnable { + public void run() { + log.println("starting ProducerJobQueueThread"); + log.flush(); + + while (true) { + ProducerJob job = (ProducerJob) producerJobQueue.acquirePendingJob(); + if (job!=null) { + job.execute(); + producerJobQueue.flagOffJob(job); + } + else + { + try { + Thread.sleep(1500); + } + catch (InterruptedException e) { + } + } + } + } + } + + + public static class ProducerEngineRuntimeExc extends Failure { + public ProducerEngineRuntimeExc(String msg, Exception cause){ + super(msg,cause); + } + } + +} \ No newline at end of file diff --git a/source/mircoders/localizer/MirAdminInterfaceLocalizer.java b/source/mircoders/localizer/MirAdminInterfaceLocalizer.java new file mode 100755 index 00000000..8a5c9a69 --- /dev/null +++ b/source/mircoders/localizer/MirAdminInterfaceLocalizer.java @@ -0,0 +1,18 @@ +package mircoders.localizer; + +import java.util.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import mir.entity.*; +import mir.entity.adapter.*; + +public interface MirAdminInterfaceLocalizer { + public Map simpleCommentOperations(); + public Map simpleArticleOperations(); + + public interface MirSimpleEntityOperation { + public boolean isAvailable(EntityAdapter anEntity); + public void perform(EntityAdapter anEntity); + } +} \ No newline at end of file diff --git a/source/mircoders/localizer/MirCachingLocalizerDecorator.java b/source/mircoders/localizer/MirCachingLocalizerDecorator.java new file mode 100755 index 00000000..bf265331 --- /dev/null +++ b/source/mircoders/localizer/MirCachingLocalizerDecorator.java @@ -0,0 +1,65 @@ +package mircoders.localizer; + +public class MirCachingLocalizerDecorator implements MirLocalizer { + private MirLocalizer localizer; + private MirProducerLocalizer producerLocalizer; + private MirGeneratorLocalizer generatorLocalizer; + private MirOpenPostingLocalizer openPostingsLocalizer; + private MirProducerAssistantLocalizer producerAssistantLocalizer; + private MirDataModelLocalizer dataModelLocalizer; + private MirAdminInterfaceLocalizer adminInterfaceLocalizer; + + public MirCachingLocalizerDecorator(MirLocalizer aLocalizer) { + localizer = aLocalizer; + } + + public MirProducerLocalizer producers() throws MirLocalizerFailure, MirLocalizerException { + if (producerLocalizer==null) { + producerLocalizer = localizer.producers(); + } + + return producerLocalizer; + } + + public MirGeneratorLocalizer generators() throws MirLocalizerFailure, MirLocalizerException { + if (generatorLocalizer==null) { + generatorLocalizer = localizer.generators(); + } + + return generatorLocalizer; + } + + public MirOpenPostingLocalizer openPostings() throws MirLocalizerFailure, MirLocalizerException { + if (openPostingsLocalizer==null) { + openPostingsLocalizer = localizer.openPostings(); + } + + return openPostingsLocalizer; + } + + public MirProducerAssistantLocalizer producerAssistant() throws MirLocalizerFailure, MirLocalizerException { + if (producerAssistantLocalizer==null) { + producerAssistantLocalizer = localizer.producerAssistant(); + } + + return producerAssistantLocalizer; + } + + public MirDataModelLocalizer dataModel() throws MirLocalizerFailure, MirLocalizerException { + if (dataModelLocalizer==null) { + dataModelLocalizer = localizer.dataModel(); + } + + return dataModelLocalizer; + } + + public MirAdminInterfaceLocalizer adminInterface() throws MirLocalizerFailure, MirLocalizerException { + if (adminInterfaceLocalizer==null) { + adminInterfaceLocalizer = localizer.adminInterface(); + } + + return adminInterfaceLocalizer; + }; + + +} \ No newline at end of file diff --git a/source/mircoders/localizer/MirDataModelLocalizer.java b/source/mircoders/localizer/MirDataModelLocalizer.java new file mode 100755 index 00000000..4b8c8a15 --- /dev/null +++ b/source/mircoders/localizer/MirDataModelLocalizer.java @@ -0,0 +1,7 @@ +package mircoders.localizer; + +import mir.entity.adapter.*; + +public interface MirDataModelLocalizer { + public EntityAdapterModel adapterModel() throws MirLocalizerException, MirLocalizerFailure; +} \ No newline at end of file diff --git a/source/mircoders/localizer/MirGeneratorLocalizer.java b/source/mircoders/localizer/MirGeneratorLocalizer.java new file mode 100755 index 00000000..a966b427 --- /dev/null +++ b/source/mircoders/localizer/MirGeneratorLocalizer.java @@ -0,0 +1,8 @@ +package mircoders.localizer; + +import mir.generator.*; + +public interface MirGeneratorLocalizer { + public Generator.GeneratorLibrary makeGeneratorLibrary() throws MirLocalizerException, MirLocalizerFailure; + public WriterEngine makeWriterEngine() throws MirLocalizerException, MirLocalizerFailure; +} \ No newline at end of file diff --git a/source/mircoders/localizer/MirLocalizer.java b/source/mircoders/localizer/MirLocalizer.java new file mode 100755 index 00000000..aeb0400a --- /dev/null +++ b/source/mircoders/localizer/MirLocalizer.java @@ -0,0 +1,11 @@ +package mircoders.localizer; + +public interface MirLocalizer { + public MirProducerLocalizer producers() throws MirLocalizerFailure, MirLocalizerException; + public MirAdminInterfaceLocalizer adminInterface() throws MirLocalizerFailure, MirLocalizerException; + public MirOpenPostingLocalizer openPostings() throws MirLocalizerFailure, MirLocalizerException; + public MirProducerAssistantLocalizer producerAssistant() throws MirLocalizerFailure, MirLocalizerException; + public MirGeneratorLocalizer generators() throws MirLocalizerFailure, MirLocalizerException; + public MirDataModelLocalizer dataModel() throws MirLocalizerFailure, MirLocalizerException; + +} \ No newline at end of file diff --git a/source/mircoders/localizer/MirLocalizerException.java b/source/mircoders/localizer/MirLocalizerException.java new file mode 100755 index 00000000..1bb82157 --- /dev/null +++ b/source/mircoders/localizer/MirLocalizerException.java @@ -0,0 +1,10 @@ +package mircoders.localizer; + +import multex.Exc; + +public class MirLocalizerException extends Exc { + + public MirLocalizerException(String aMessage) { + super(aMessage); + } +} \ No newline at end of file diff --git a/source/mircoders/localizer/MirLocalizerFailure.java b/source/mircoders/localizer/MirLocalizerFailure.java new file mode 100755 index 00000000..c5a99e4e --- /dev/null +++ b/source/mircoders/localizer/MirLocalizerFailure.java @@ -0,0 +1,12 @@ +package mircoders.localizer; + +import multex.Failure; + +public class MirLocalizerFailure extends Failure { + public MirLocalizerFailure(String msg, Throwable throwable) { + super(msg, throwable); + } + public MirLocalizerFailure(Throwable aThrowable) { + this(aThrowable.getMessage(), aThrowable); + } +} diff --git a/source/mircoders/localizer/MirOpenPostingLocalizer.java b/source/mircoders/localizer/MirOpenPostingLocalizer.java new file mode 100755 index 00000000..d9add8c8 --- /dev/null +++ b/source/mircoders/localizer/MirOpenPostingLocalizer.java @@ -0,0 +1,13 @@ +package mircoders.localizer; + +import java.util.*; +import javax.servlet.http.*; + +import mircoders.entity.*; + +public interface MirOpenPostingLocalizer { + public void afterContentPosting(EntityContent aContent); + public void afterCommentPosting(EntityComment aComment); + + public String chooseOpenPostingLanguage(HttpServletRequest req); +} diff --git a/source/mircoders/localizer/MirProducerAssistantLocalizer.java b/source/mircoders/localizer/MirProducerAssistantLocalizer.java new file mode 100755 index 00000000..4319faee --- /dev/null +++ b/source/mircoders/localizer/MirProducerAssistantLocalizer.java @@ -0,0 +1,9 @@ +package mircoders.localizer; + +import java.util.*; +import java.io.*; + +public interface MirProducerAssistantLocalizer { + public void initializeGenerationValueSet(Map aValueSet) throws MirLocalizerException, MirLocalizerFailure; + public String filterText(String aText) throws MirLocalizerException, MirLocalizerFailure; +} diff --git a/source/mircoders/localizer/MirProducerLocalizer.java b/source/mircoders/localizer/MirProducerLocalizer.java new file mode 100755 index 00000000..44abd88f --- /dev/null +++ b/source/mircoders/localizer/MirProducerLocalizer.java @@ -0,0 +1,7 @@ +package mircoders.localizer; + +import java.util.*; + +public interface MirProducerLocalizer { + public Map factories() throws MirLocalizerException, MirLocalizerFailure; +} diff --git a/source/mircoders/localizer/basic/MirBasicAdminInterfaceLocalizer.java b/source/mircoders/localizer/basic/MirBasicAdminInterfaceLocalizer.java new file mode 100755 index 00000000..044789c0 --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicAdminInterfaceLocalizer.java @@ -0,0 +1,131 @@ +package mircoders.localizer.basic; + +import java.util.*; +import mir.entity.adapter.*; +import mir.storage.*; +import mir.entity.*; +import mircoders.localizer.*; +import mircoders.entity.*; +import mircoders.storage.*; + + +public class MirBasicAdminInterfaceLocalizer implements MirAdminInterfaceLocalizer { + private Map simpleCommentOperations; + private Map simpleArticleOperations; + + public MirBasicAdminInterfaceLocalizer() throws MirLocalizerFailure, MirLocalizerException { + simpleCommentOperations = new HashMap(); + simpleArticleOperations = new HashMap(); + + buildSimpleCommentOperations(simpleCommentOperations); + buildSimpleArticleOperations(simpleArticleOperations); + } + + public Map simpleCommentOperations() { + return simpleCommentOperations; + }; + + public Map simpleArticleOperations() { + return simpleArticleOperations; + }; + + public void buildSimpleCommentOperations(Map anOperations) throws MirLocalizerFailure, MirLocalizerException { + anOperations.put("hide", new HideCommentOperation()); + anOperations.put("unhide", new UnhideCommentOperation()); + }; + + public void buildSimpleArticleOperations(Map anOperations) throws MirLocalizerFailure, MirLocalizerException { + anOperations.put("hide", new HideArticleOperation()); + anOperations.put("unhide", new UnhideArticleOperation()); + }; + + protected abstract static class EntityModifyingOperation implements MirSimpleEntityOperation { + public boolean isAvailable(EntityAdapter anEntity) { + try { + + Entity entity = anEntity.getEntity(); + return (entity instanceof EntityComment) && isAvailable((EntityComment) entity); + } + catch (Throwable t) { + return false; + } + }; + + public void perform(EntityAdapter anEntity) { + Entity entity = anEntity.getEntity(); + try { + performModification(entity); + entity.update(); + } + catch (Throwable t) { + } + }; + + protected abstract boolean isAvailable(Entity anEntity) throws StorageObjectException ; + protected abstract void performModification(Entity anEntity) throws StorageObjectException ; + } + + public static abstract class CommentModifyingOperation extends EntityModifyingOperation { + protected boolean isAvailable(Entity anEntity) throws StorageObjectException { + return anEntity instanceof EntityComment && isAvailable((EntityComment) anEntity); + } + + protected void performModification(Entity anEntity) throws StorageObjectException { + performModification((EntityComment) anEntity); + DatabaseContent.getInstance().setUnproduced("id="+anEntity.getValue("to_media")); + }; + + protected abstract boolean isAvailable(EntityComment aComment) throws StorageObjectException ; + protected abstract void performModification(EntityComment aComment) throws StorageObjectException ; + } + + public static abstract class ArticleModifyingOperation extends EntityModifyingOperation { + protected boolean isAvailable(Entity anEntity) throws StorageObjectException { + return anEntity instanceof EntityContent && isAvailable((EntityComment) anEntity); + } + + protected void performModification(Entity anEntity) throws StorageObjectException { + performModification((EntityContent) anEntity); + anEntity.setValueForProperty("is_produced", "0"); + }; + + protected abstract boolean isAvailable(EntityContent anArticle) throws StorageObjectException ; + protected abstract void performModification(EntityContent anArticle) throws StorageObjectException ; + } + + private static class HideCommentOperation extends CommentModifyingOperation { + protected boolean isAvailable(EntityComment aComment) { + return aComment.getValue("is_published").equals("1"); + } + protected void performModification(EntityComment aComment) throws StorageObjectException { + aComment.setValueForProperty("is_published", "0"); + } + } + + private static class UnhideCommentOperation extends CommentModifyingOperation { + protected boolean isAvailable(EntityComment aComment) { + return aComment.getValue("is_published").equals("0"); + } + protected void performModification(EntityComment aComment) throws StorageObjectException { + aComment.setValueForProperty("is_published", "1"); + } + } + + private static class HideArticleOperation extends ArticleModifyingOperation { + protected boolean isAvailable(EntityContent anArticle) { + return anArticle.getValue("is_published").equals("1"); + } + protected void performModification(EntityContent anArticle) throws StorageObjectException { + anArticle.setValueForProperty("is_published", "0"); + } + } + + private static class UnhideArticleOperation extends ArticleModifyingOperation { + protected boolean isAvailable(EntityContent anArticle) { + return anArticle.getValue("is_published").equals("0"); + } + protected void performModification(EntityContent anArticle) throws StorageObjectException { + anArticle.setValueForProperty("is_published", "1"); + } + } +} \ No newline at end of file diff --git a/source/mircoders/localizer/basic/MirBasicDataModelLocalizer.java b/source/mircoders/localizer/basic/MirBasicDataModelLocalizer.java new file mode 100755 index 00000000..4edec5b6 --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicDataModelLocalizer.java @@ -0,0 +1,266 @@ +package mircoders.localizer.basic; + +import java.util.*; +import mir.entity.*; +import mir.entity.adapter.*; +import mir.media.*; +import mir.misc.*; +import mir.util.*; +import mircoders.storage.*; +import mircoders.global.*; +import mircoders.entity.*; +import mircoders.localizer.*; + +public class MirBasicDataModelLocalizer implements MirDataModelLocalizer { + private EntityAdapterModel model; + + public MirBasicDataModelLocalizer() { + } + + public EntityAdapterModel adapterModel() throws MirLocalizerFailure { + if (model==null) + model = buildModel(); + + return model; + }; + + protected void constructContentAdapterDefinition(EntityAdapterDefinition anEntityAdapterDefinition) throws MirLocalizerFailure { + try { + anEntityAdapterDefinition.addDBDateField("creationdate", "webdb_create"); + anEntityAdapterDefinition.addDBDateField("changedate", "webdb_lastchange"); + anEntityAdapterDefinition.addMirDateField("date", "date"); + anEntityAdapterDefinition.addCalculatedField("to_topics", new ContentToTopicsField()); + anEntityAdapterDefinition.addCalculatedField("to_comments", new ContentToCommentsField()); + + anEntityAdapterDefinition.addCalculatedField("to_media_images", new ContentToMediaField( "image" )); + anEntityAdapterDefinition.addCalculatedField("to_uploaded_media", new ContentToMediaField( "uploadedMedia" )); + anEntityAdapterDefinition.addCalculatedField("to_media_audio", new ContentToMediaField( "audio" )); + anEntityAdapterDefinition.addCalculatedField("to_media_video", new ContentToMediaField( "video" )); + anEntityAdapterDefinition.addCalculatedField("to_media_other", new ContentToMediaField( "otherMedia" )); + anEntityAdapterDefinition.addCalculatedField("to_media_icon", new ContentToIconField()); + + anEntityAdapterDefinition.addCalculatedField("description_parsed", new FilteredField("description")); + anEntityAdapterDefinition.addCalculatedField("content_data_parsed", new FilteredField("content_data")); + } + catch (Throwable t) { + throw new MirLocalizerFailure(t.getMessage(), t); + } + } + + protected void constructCommentAdapterDefinition(EntityAdapterDefinition anEntityAdapterDefinition) throws MirLocalizerFailure { + try { + anEntityAdapterDefinition.addDBDateField("creationdate", "webdb_create"); + anEntityAdapterDefinition.addCalculatedField("to_content", new CommentToContentField()); + + anEntityAdapterDefinition.addCalculatedField("description_parsed", new FilteredField("description")); + anEntityAdapterDefinition.addCalculatedField("operations", new CommentToOperationsField()); + } + catch (Throwable t) { + throw new MirLocalizerFailure(t.getMessage(), t); + } + } + + protected EntityAdapterModel buildModel() throws MirLocalizerFailure { + EntityAdapterModel result = new EntityAdapterModel(); + + try { + EntityAdapterDefinition definition; + + definition = new EntityAdapterDefinition(); + constructContentAdapterDefinition( definition ); + result.addMapping( "content", DatabaseContent.getInstance(), definition); + + definition = new EntityAdapterDefinition(); + constructCommentAdapterDefinition( definition ); + result.addMapping( "comment", DatabaseComment.getInstance(), definition); + + result.addMapping( "articleType", DatabaseArticleType.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "breakingNews", DatabaseBreaking.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "feature", DatabaseFeature.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "imageType", DatabaseImageType.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "language", DatabaseLanguage.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "mediaFolder", DatabaseMediafolder.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "mediaType", DatabaseMediaType.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "internalMessage", DatabaseMessages.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "topic", DatabaseTopics.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "user", DatabaseUsers.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "media", DatabaseMedia.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "uploadedMedia", DatabaseUploadedMedia.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "image", DatabaseImages.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "audio", DatabaseAudio.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "video", DatabaseVideo.getInstance(), new EntityAdapterDefinition()); + result.addMapping( "otherMedia", DatabaseOther.getInstance(), new EntityAdapterDefinition()); + } + catch (Throwable t) { + throw new MirLocalizerFailure(t.getMessage(), t); + } + + return result; + } + + protected class CommentToContentField implements EntityAdapterDefinition.CalculatedField { + public Object getValue(EntityAdapter anEntityAdapter) { + try { + return anEntityAdapter.getRelation( + "id="+anEntityAdapter.get("to_media"), + "id", + "content" ); + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + } + } + + protected class CommentToOperationsField implements EntityAdapterDefinition.CalculatedField { + public Object getValue(EntityAdapter anEntityAdapter) { + try { + Map operations = MirGlobal.localizer().adminInterface().simpleCommentOperations(); + Iterator i = operations.entrySet().iterator(); + List availableOperations = new Vector(); + + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + + MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = + (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) entry.getValue(); + + if (operation.isAvailable(anEntityAdapter)) { + availableOperations.add(entry.getKey()); + } + }; + + return availableOperations; + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + } + } + + protected class FilteredField implements EntityAdapterDefinition.CalculatedField { + String fieldName; + + public FilteredField(String aFieldName) { + fieldName = aFieldName; + } + + public Object getValue(EntityAdapter anEntityAdapter) { + try { + if (anEntityAdapter.get("is_html")!=null && anEntityAdapter.get("is_html").equals("1")) { + return anEntityAdapter.get(fieldName); + } + else { + return MirGlobal.localizer().producerAssistant().filterText((String) anEntityAdapter.get(fieldName)); + } + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + } + } + + protected class ContentToCommentsField implements EntityAdapterDefinition.CalculatedField { + public Object getValue(EntityAdapter anEntityAdapter) { + try { + return anEntityAdapter.getRelation( + "to_media="+anEntityAdapter.get("id")+" and is_published='1'", + "webdb_create", + "comment" ); + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + } + } + + protected class ContentToTopicsField implements EntityAdapterDefinition.CalculatedField { + public Object getValue(EntityAdapter anEntityAdapter) { + try { + return anEntityAdapter.getRelation( + "exists (select * from content_x_topic where content_id="+anEntityAdapter.get("id")+" and topic_id=id)", + "title", + "topic" ); + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + } + } + + protected class ContentToMediaField implements EntityAdapterDefinition.CalculatedField { + String definition; + + public ContentToMediaField(String aDefinition) { + definition = aDefinition; + } + + public Object getValue(EntityAdapter anEntityAdapter) { + try { + return anEntityAdapter.getRelation( + "exists (select * from content_x_media where content_id="+anEntityAdapter.get("id")+" and media_id=id)", + "title", + definition); + } + catch (Throwable t) { + throw new RuntimeException(t.getMessage()); + } + } + } + + protected class ContentToIconField implements EntityAdapterDefinition.CalculatedField { + public Object getValue(EntityAdapter anEntityAdapter) { + EntityAdapter media; + Entity mediaType; + RewindableIterator iterator; + Map result; + MirMedia mediaHandler; + String tinyIcon; + String iconAlt; + + try { + iterator = (RewindableIterator) (anEntityAdapter.get("to_uploaded_media")); + iterator.rewind(); + + tinyIcon = MirGlobal.getConfigProperty("Producer.Icon.TinyText"); + iconAlt = "Text"; + + if (iterator.hasNext()) { + media = (EntityAdapter) iterator.next(); + + mediaType = ((EntityUploadedMedia) (media.getEntity())).getMediaType(); + mediaHandler = MediaHelper.getHandler( mediaType ); + + if (mediaHandler.isVideo()) { + tinyIcon = MirGlobal.getConfigProperty("Producer.Icon.TinyVideo"); + iconAlt = "Video"; + } + else if (mediaHandler.isAudio()) { + tinyIcon = MirGlobal.getConfigProperty("Producer.Icon.TinyAudio"); + iconAlt = "Audio"; + } + else if (mediaHandler.isImage()) { + tinyIcon = MirGlobal.getConfigProperty("Producer.Icon.TinyImage"); + iconAlt = "Image"; + } + else { + tinyIcon = mediaHandler.getTinyIcon(); + iconAlt = mediaHandler.getIconAlt(); + } + + } + } + catch (Throwable t) { + System.out.println("ContentToIconField: exception: " +t.getMessage()); + t.printStackTrace(System.out); + throw new RuntimeException(t.getMessage()); + } + + result = new HashMap(); + result.put("tiny_icon", MirGlobal.getConfigProperty("Producer.ImageRoot") + "/" + tinyIcon); + result.put("icon_alt", iconAlt); + + return result; + } + } +} \ No newline at end of file diff --git a/source/mircoders/localizer/basic/MirBasicGeneratorLocalizer.java b/source/mircoders/localizer/basic/MirBasicGeneratorLocalizer.java new file mode 100755 index 00000000..587b4b40 --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicGeneratorLocalizer.java @@ -0,0 +1,24 @@ +package mircoders.localizer.basic; + +import freemarker.template.*; +import mir.misc.*; +import mir.generator.*; +import mircoders.localizer.*; +import mircoders.global.*; + +public class MirBasicGeneratorLocalizer implements MirGeneratorLocalizer { + protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile")); + private String templateRoot; + + public MirBasicGeneratorLocalizer (String aTemplateRoot) { + templateRoot = aTemplateRoot; + } + + public Generator.GeneratorLibrary makeGeneratorLibrary() throws MirLocalizerException, MirLocalizerFailure { + return new FreemarkerGenerator.FreemarkerGeneratorLibrary(templateRoot); + }; + + public WriterEngine makeWriterEngine() throws MirLocalizerException, MirLocalizerFailure { + return new MirBasicWriterEngine(MirGlobal.getConfigProperty("Mir.DefaultEncoding")); + } +} diff --git a/source/mircoders/localizer/basic/MirBasicLocalizer.java b/source/mircoders/localizer/basic/MirBasicLocalizer.java new file mode 100755 index 00000000..40eef61e --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicLocalizer.java @@ -0,0 +1,35 @@ +package mircoders.localizer.basic; + +import mir.misc.*; +import mircoders.global.*; +import mircoders.localizer.*; + +public class MirBasicLocalizer implements MirLocalizer { + protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile")); + + public MirProducerLocalizer producers() throws MirLocalizerFailure, MirLocalizerException { + return new MirBasicProducerLocalizer(); + } + + public MirGeneratorLocalizer generators() { + return new MirBasicGeneratorLocalizer(MirGlobal.getConfigProperty("Home")+MirGlobal.getConfigProperty("HTMLTemplateProcessor.Dir")); + } + + public MirOpenPostingLocalizer openPostings() { + return new MirBasicOpenPostingLocalizer(); + } + + public MirProducerAssistantLocalizer producerAssistant() { + return new MirBasicProducerAssistantLocalizer(); + } + + public MirDataModelLocalizer dataModel() { + return new MirBasicDataModelLocalizer(); + }; + + public MirAdminInterfaceLocalizer adminInterface() throws MirLocalizerFailure, MirLocalizerException { + return new MirBasicAdminInterfaceLocalizer(); + }; + + +} \ No newline at end of file diff --git a/source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java b/source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java new file mode 100755 index 00000000..5e19a347 --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java @@ -0,0 +1,43 @@ +package mircoders.localizer.basic; + +import java.util.*; +import javax.servlet.http.*; + +import mir.misc.*; +import mircoders.global.*; +import mircoders.localizer.*; +import mircoders.entity.*; + +public class MirBasicOpenPostingLocalizer implements MirOpenPostingLocalizer { + protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile")); + + public void afterContentPosting() { + MirGlobal.producerEngine().addJob("media", "new"); + MirGlobal.producerEngine().addJob("content", "new"); + MirGlobal.producerEngine().addJob("startpage", "(default)"); + MirGlobal.producerEngine().addJob("synchronization", "run"); + + MirGlobal.producerEngine().addJob("openposting", "new"); + MirGlobal.producerEngine().addJob("topics", "new"); + MirGlobal.producerEngine().addJob("synchronization", "run"); + } + + public void afterContentPosting(EntityContent aContent) { + afterContentPosting(); + } + + public void afterCommentPosting() { + MirGlobal.producerEngine().addJob("content", "new"); + MirGlobal.producerEngine().addJob("synchronization", "run"); + } + + public void afterCommentPosting(EntityComment aComment) { + afterCommentPosting(); + } + + public String chooseOpenPostingLanguage(HttpServletRequest req) { + Locale locale = req.getLocale(); + + return locale.getLanguage(); + } +} diff --git a/source/mircoders/localizer/basic/MirBasicProducerAssistantLocalizer.java b/source/mircoders/localizer/basic/MirBasicProducerAssistantLocalizer.java new file mode 100755 index 00000000..344a8124 --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicProducerAssistantLocalizer.java @@ -0,0 +1,86 @@ +package mircoders.localizer.basic; + +import java.util.*; +import java.io.*; +import freemarker.template.utility.*; +import mir.misc.*; +import mir.entity.*; +import mir.util.*; +import mircoders.module.*; +import mircoders.storage.*; +import mircoders.localizer.*; +import mircoders.global.*; + +public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantLocalizer { + protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile")); + + public void initializeGenerationValueSet(Map aValueSet) { + // ML: these config settings will be included more beautifully as soon as the new config system is in place + + Map configMap = new HashMap(); + Map utilityMap = new HashMap(); + + configMap.put("producerDocRoot", MirGlobal.getConfigProperty("Producer.DocRoot")); + configMap.put("storageRoot", MirGlobal.getConfigProperty("Producer.StorageRoot")); + configMap.put("productionHost", MirGlobal.getConfigProperty("Producer.ProductionHost")); + configMap.put("openAction", MirGlobal.getConfigProperty("Producer.OpenAction")); + configMap.put("docRoot", MirGlobal.getConfigProperty("RootUri")); + configMap.put("actionRoot", MirGlobal.getConfigProperty("RootUri")+"/servlet/Mir"); + configMap.put("now", new DateToMapAdapter((new GregorianCalendar()).getTime())); + configMap.put("videoHost", MirGlobal.getConfigProperty("Producer.Video.Host")); + configMap.put("audioHost", MirGlobal.getConfigProperty("Producer.Audio.Host")); + configMap.put("imageHost", MirGlobal.getConfigProperty("Producer.Image.Host")); + configMap.put("imagePath", MirGlobal.getConfigProperty("Producer.Image.Path")); + configMap.put("mirVersion", MirGlobal.getConfigProperty("Mir.Version")); + configMap.put("defEncoding", MirGlobal.getConfigProperty("Mir.DefaultEncoding")); + configMap.put("all", Configuration.getConfs()); + + utilityMap.put("compressWhitespace", new freemarker.template.utility.CompressWhitespace() ); + utilityMap.put("encodeHTML", new GeneratorHTMLFunctions.encodeHTMLGeneratorFunction()); + utilityMap.put("encodeURI", new GeneratorHTMLFunctions.encodeURIGeneratorFunction()); + + aValueSet.put("config", configMap); + aValueSet.put("utility", utilityMap); + + EntityList topicList=null; + EntityList entityList=null; + EntityList parentList=null; + EntityList languageList=null; + + try { +// ModuleLinksImcs linksImcsModule = new ModuleLinksImcs(DatabaseLinksImcs.getInstance()); + ModuleTopics topicsModule = new ModuleTopics(DatabaseTopics.getInstance()); + ModuleLanguage languageModule = new ModuleLanguage(DatabaseLanguage.getInstance()); + + topicList = topicsModule.getTopicsList(); +// entityList = linksImcsModule.getByWhereClause("", "sortpriority, title", -1); +// parentList = linksImcsModule.getByWhereClause("to_parent_id=NULL", "sortpriority, title", -1); + languageList = languageModule.getByWhereClause("", "id", -1); + } + catch (Throwable t) { + logger.printError("initializeGenerationValueSet: Exception "+t.getMessage()); + } + + aValueSet.put("topics", topicList); + aValueSet.put("imclist", entityList); + aValueSet.put("parentlist", parentList); + + Map articleTypeMap = new HashMap(); + articleTypeMap.put("openposting", "0"); + articleTypeMap.put("newswire", "1"); + articleTypeMap.put("feature", "2"); + articleTypeMap.put("topicspecial", "3"); + articleTypeMap.put("startspecial", "4"); + aValueSet.put("articletype", articleTypeMap); + }; + + public String filterText(String aText) { + return StringUtil.createHTML( + StringUtil.deleteForbiddenTags(aText), + MirGlobal.getConfigProperty("Producer.ImageRoot"), + MirGlobal.getConfigProperty("Producer.MailLinkName"), + MirGlobal.getConfigProperty("Producer.ExtLinkName"), + MirGlobal.getConfigProperty("Producer.IntLinkName") + ); + } +} diff --git a/source/mircoders/localizer/basic/MirBasicProducerLocalizer.java b/source/mircoders/localizer/basic/MirBasicProducerLocalizer.java new file mode 100755 index 00000000..741d0f1c --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicProducerLocalizer.java @@ -0,0 +1,142 @@ +package mircoders.localizer.basic; + +import java.util.*; +import java.io.*; +import mir.producer.*; +import mir.generator.*; +import mir.producer.reader.*; +import mir.misc.*; +import mir.util.*; +import mir.entity.adapter.*; +import mircoders.global.*; +import mircoders.global.*; +import mircoders.localizer.*; +import mircoders.producer.reader.*; +import mircoders.producer.*; + +public class MirBasicProducerLocalizer implements MirProducerLocalizer { + private Map producerFactories; + protected FileMonitor fileMonitor; + protected EntityAdapterModel model; + protected Generator.GeneratorLibrary generatorLibrary; + protected WriterEngine writerEngine; + + protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile")); + + public MirBasicProducerLocalizer() { + + try { + producerFactories = new HashMap(); + model = MirGlobal.localizer().dataModel().adapterModel(); + generatorLibrary = MirGlobal.localizer().generators().makeGeneratorLibrary(); + writerEngine = MirGlobal.localizer().generators().makeWriterEngine(); + } + catch (Throwable t) { + logger.printError("MirBasicProducerLocalizer(): Exception "+t.getMessage()); + model = new EntityAdapterModel(); + } + } + + public Map factories() throws MirLocalizerException { + if (fileMonitor==null || producerFactories == null || fileMonitor.hasChanged()) { + try { + Map newProducers = new HashMap(); + FileMonitor newFileMonitor = new FileMonitor(); + setupFactories(newProducers, newFileMonitor); + + producerFactories = newProducers; + fileMonitor = newFileMonitor; + } + catch (Throwable t) { + logger.printError("MirBasicProducerLocalizer.factories(): Unable to setup factories: "+t.getMessage()); + } + } + + return producerFactories; + }; + + protected void setupContentFactory(CompositeProducerNode aProducerNode) { + } + + protected void setupStartPageFactory(CompositeProducerNode aProducerNode) { + } + + protected void setupSynchronizationFactory(CompositeProducerNode aProducerNode) { + if(MirGlobal.getConfigBooleanProperty("Rsync")){ + aProducerNode.addSubNode( + new ScriptCallingProducerNode(MirGlobal.getConfigProperty("Rsync.Script.Path")) + ); + } + } + + protected void setupStaticFactory(CompositeProducerNode aProducerNode) { + } + + protected void setupTopicsFactory(CompositeProducerNode aProducerNode) { + } + + protected void setupProducerNodeBuilderLibrary(ProducerNodeBuilderLibrary aLibrary) { + DefaultProducerNodeBuilders.registerBuilders(aLibrary, model, generatorLibrary, writerEngine); + SupplementalProducerNodeBuilders.registerBuilders(aLibrary, model); + } + + protected void setupFactories(Map aFactoriesMap, FileMonitor aFileMonitor) throws MirLocalizerException, MirLocalizerFailure { + ProducerConfigReader reader; + ProducerNodeBuilderLibrary library = new ProducerNodeBuilderLibrary(); + setupProducerNodeBuilderLibrary(library); + List usedFiles = new Vector(); + + aFileMonitor.clear(); + reader = new ProducerConfigReader(); + reader.parseFile(MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.ProducerConfigFile"), library, aFactoriesMap, usedFiles); + + Iterator i = usedFiles.iterator(); + while (i.hasNext()) + aFileMonitor.addFile((File) i.next()); + + setupFactories(aFactoriesMap); + } + + protected void setupFactories(Map aFactoriesMap ) throws MirLocalizerException, MirLocalizerFailure { + CompositeProducerNode node; + + try { + node = new CompositeProducerNode(); + setupContentFactory( node ); + if (node.getNrSubNodes()>0) + aFactoriesMap.put("content", new NodedProducerFactory(node)); + + node = new CompositeProducerNode(); + setupStartPageFactory( node ); + if (node.getNrSubNodes()>0) + aFactoriesMap.put("startpage", new NodedProducerFactory(node)); + + node = new CompositeProducerNode(); + setupSynchronizationFactory( node ); + if (node.getNrSubNodes()>0) + aFactoriesMap.put("synchronization", new NodedProducerFactory(node)); + + node = new CompositeProducerNode(); + setupStaticFactory( node ); + if (node.getNrSubNodes()>0) + aFactoriesMap.put("static", new NodedProducerFactory(node)); + + node = new CompositeProducerNode(); + setupTopicsFactory( node ); + if (node.getNrSubNodes()>0) + aFactoriesMap.put("topics", new NodedProducerFactory(node)); + + aFactoriesMap.put("media", + new CompositeProducerFactory( new ProducerFactory[] { + new OldProducerAdapterFactory(new ProducerImages()), + new OldProducerAdapterFactory(new ProducerAudio()), + new OldProducerAdapterFactory(new ProducerVideo()), + new OldProducerAdapterFactory(new ProducerOther()) + } ) + ); + } + catch (Exception e) { + throw new MirLocalizerFailure(e); + } + }; +} diff --git a/source/mircoders/localizer/basic/MirBasicWriterEngine.java b/source/mircoders/localizer/basic/MirBasicWriterEngine.java new file mode 100755 index 00000000..59f6f904 --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicWriterEngine.java @@ -0,0 +1,47 @@ +package mircoders.localizer.basic; + +import java.util.*; +import java.io.*; +import mir.generator.*; +import mircoders.localizer.*; + +public class MirBasicWriterEngine implements WriterEngine { + private String defaultEncoding; + + public MirBasicWriterEngine(String aDefaultEncoding) { + defaultEncoding = aDefaultEncoding; + } + + public Object openWriter(String anIdentifier, String anEncoding) throws MirLocalizerFailure { + String encoding; + File file; + File dir; + + if (anEncoding!=null && !anEncoding.equals("")) + encoding = anEncoding; + else + encoding = defaultEncoding; +// MirGlobal.getConfigProperty("Mir.DefaultEncoding"); + + try { + file = new File( anIdentifier ); + dir = new File(file.getParent()); + if (dir!=null && !dir.exists()){ + dir.mkdirs(); + } + + return new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file), encoding + ) + ); + } + catch (Throwable t) { + throw new MirLocalizerFailure("Failure while opening a PrintWriter: "+t.getMessage(),t); + } + }; + + public void closeWriter(Object aWriter) { + ((PrintWriter) aWriter).close(); + }; +} \ No newline at end of file diff --git a/source/mircoders/media/MediaHandlerGeneric.java b/source/mircoders/media/MediaHandlerGeneric.java index 7cb409ec..ed8e12c4 100755 --- a/source/mircoders/media/MediaHandlerGeneric.java +++ b/source/mircoders/media/MediaHandlerGeneric.java @@ -55,12 +55,12 @@ public class MediaHandlerGeneric implements MirMedia uploadedData); //were done with the data, dereference. uploadedData=null; - + ent.setValueForProperty("publish_path",datePath+"/"+mediaFname); ent.setValueForProperty("size", size.toString()); ent.update(); } catch (Exception e) { - theLog.printError(e.toString()); + theLog.printError(e.toString()); throw new MirMediaException(e.toString()); } @@ -69,7 +69,7 @@ public class MediaHandlerGeneric implements MirMedia public void produce (Entity ent, Entity mediaTypeEnt ) throws MirMediaException { - + //check first if the media file exist since produced //location is also the storage location String date = ent.getValue("date"); @@ -78,9 +78,9 @@ public class MediaHandlerGeneric implements MirMedia String fname = getStoragePath()+relPath; if(! new File(fname).exists()) throw new MirMediaException("error in MirMedia.produce(): "+relPath+ - "does not exist!"); + " does not exist!"); } - + //a method that will probably never get used.. private byte[] getFile (String fileName) @@ -90,11 +90,11 @@ public class MediaHandlerGeneric implements MirMedia if (size < 0) return null; byte[] container = new byte[(int)size]; - + try { FileUtil.read(fileName, container); } catch (Exception e) { - theLog.printError(e.toString()); + theLog.printError(e.toString()); throw new MirMediaException(e.toString()); } @@ -136,7 +136,7 @@ public class MediaHandlerGeneric implements MirMedia public String getIconAlt() { - return "Generic media"; + return "Generic media"; } public SimpleList getURL(Entity ent, Entity mediaTypeEnt) @@ -167,6 +167,6 @@ public class MediaHandlerGeneric implements MirMedia } } - - + + diff --git a/source/mircoders/producer/CompositeProducer.java b/source/mircoders/producer/CompositeProducer.java new file mode 100755 index 00000000..6bd31080 --- /dev/null +++ b/source/mircoders/producer/CompositeProducer.java @@ -0,0 +1,27 @@ +package mircoders.producer; + +import java.util.*; +import java.io.*; +import mir.producer.*; + +public class CompositeProducer implements mir.producer.Producer { + + List producers; + + public CompositeProducer() { + producers = new Vector(); + } + + public void addProducer(mir.producer.Producer aProducer) { + producers.add(aProducer); + } + + public void produce( PrintWriter aLogger ) throws ProducerFailure, ProducerExc { + Iterator i; + + i=producers.iterator(); + + while (i.hasNext()) + ((mir.producer.Producer) i.next()).produce(aLogger); + } +} diff --git a/source/mircoders/producer/CompositeProducerFactory.java b/source/mircoders/producer/CompositeProducerFactory.java new file mode 100755 index 00000000..40195375 --- /dev/null +++ b/source/mircoders/producer/CompositeProducerFactory.java @@ -0,0 +1,62 @@ +package mircoders.producer; + +import java.util.*; +import mir.entity.*; +import mir.producer.*; + +public class CompositeProducerFactory implements ProducerFactory { + Map factories; // verb -> Vector ( ProducerFactory ) + + public CompositeProducerFactory() { + factories = new HashMap(); + } + + public CompositeProducerFactory(ProducerFactory[] aSubProducerFactories) { + this(); + + int i; + + for (i=0; i"); + aLogger.flush(); + } + + public Set buildVerbSet() { + return new HashSet(); + } +} + + + diff --git a/source/mircoders/producer/MediaGeneratingProducerNode.java b/source/mircoders/producer/MediaGeneratingProducerNode.java new file mode 100755 index 00000000..5f192480 --- /dev/null +++ b/source/mircoders/producer/MediaGeneratingProducerNode.java @@ -0,0 +1,62 @@ +package mircoders.producer; + +import java.util.*; +import java.io.*; +import mir.util.*; +import mir.misc.*; +import mir.media.*; +import mir.producer.*; +import mir.generator.*; +import mir.entity.*; +import mir.entity.adapter.*; +//import mircoders.global.*; +//import mircoders.localizer.*; +import mircoders.entity.*; +import mircoders.storage.*; + +public class MediaGeneratingProducerNode implements ProducerNode { + private String mediaEntityKey; + + public MediaGeneratingProducerNode(String aMediaEntityKey) { + mediaEntityKey = aMediaEntityKey; + } + + public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure { + Object data; + Entity entity; + Entity currentMediaType; + MirMedia currentMediaHandler; + + try { + data = ParameterExpander.findValueForKey( aValueMap, mediaEntityKey ); + + if (!(data instanceof EntityAdapter)) { + throw new ProducerFailure("MediaGeneratingProducerNode: value of '"+mediaEntityKey+"' is not an EntityAdapter, but an " + data.getClass().getName(), null); + } + + entity = ((EntityAdapter) data).getEntity(); + if (! (entity instanceof EntityUploadedMedia)) { + throw new ProducerFailure("MediaGeneratingProducerNode: value of '"+mediaEntityKey+"' is not an uploaded media EntityAdapter, but a " + entity.getClass().getName() + " adapter", null); + } + + currentMediaType = DatabaseUploadedMedia.getInstance().getMediaType(entity); + + currentMediaHandler = MediaHelper.getHandler( currentMediaType ); + currentMediaHandler.produce(entity,currentMediaType); + entity.setValueForProperty("publish_server", currentMediaHandler.getPublishHost()); + entity.setValueForProperty("icon_is_produced", "1"); + entity.setValueForProperty("is_produced", "1"); + entity.update(); + } + catch (Throwable t) { + aLogger.println("Error while generating media: " + t.getMessage()); + t.printStackTrace(aLogger); + + throw new ProducerFailure(t.getMessage(), t); + } + } + + public Set buildVerbSet() { + return new HashSet(); + } +} diff --git a/source/mircoders/producer/OldProducerAdapter.java b/source/mircoders/producer/OldProducerAdapter.java new file mode 100755 index 00000000..65656fc9 --- /dev/null +++ b/source/mircoders/producer/OldProducerAdapter.java @@ -0,0 +1,24 @@ +package mircoders.producer; + +import mir.producer.*; +import java.io.*; + +public class OldProducerAdapter implements mir.producer.Producer { + + private mircoders.producer.Producer oldProducer; + private Boolean forced; + + public OldProducerAdapter( mircoders.producer.Producer anOldProducer, Boolean aForced ) { + oldProducer = anOldProducer; + forced = aForced; + } + + public void produce( PrintWriter aLogger ) throws ProducerFailure { + try { + oldProducer.handle( aLogger, null, forced.booleanValue(), false ); + } + catch (Throwable e) { + throw new ProducerFailure("Failure at handling old Producers",e); + } + } +} \ No newline at end of file diff --git a/source/mircoders/producer/OldProducerAdapterFactory.java b/source/mircoders/producer/OldProducerAdapterFactory.java new file mode 100755 index 00000000..4d5cea02 --- /dev/null +++ b/source/mircoders/producer/OldProducerAdapterFactory.java @@ -0,0 +1,25 @@ +package mircoders.producer; + +import mir.producer.*; +import java.util.*; + +public class OldProducerAdapterFactory implements ProducerFactory { + + private mircoders.producer.Producer oldProducer; + + public Iterator verbs() { + Vector verbList = new Vector(); + + verbList.add("new"); + verbList.add("all"); + return verbList.iterator(); + } + + public OldProducerAdapterFactory(mircoders.producer.Producer anOldProducer) { + oldProducer = anOldProducer; + } + + public mir.producer.Producer makeProducer(String aVerb, Map anInitialValues) { + return new OldProducerAdapter(oldProducer, new Boolean(aVerb.equals("all"))); + } +} \ No newline at end of file diff --git a/source/mircoders/producer/PDFGeneratingProducerNode.java b/source/mircoders/producer/PDFGeneratingProducerNode.java new file mode 100755 index 00000000..45856047 --- /dev/null +++ b/source/mircoders/producer/PDFGeneratingProducerNode.java @@ -0,0 +1,51 @@ +package mircoders.producer; + +import java.util.*; +import java.io.*; +import mir.util.*; +import mir.producer.*; +import mir.misc.PDFUtil; + +public class PDFGeneratingProducerNode implements ProducerNode { + private String generatorExpression; + private String destinationExpression; + + public PDFGeneratingProducerNode(String aGenerator, String aDestination) { + generatorExpression=aGenerator; + destinationExpression=aDestination; + } + + public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure { + + String generatorIdentifier; + String destinationIdentifier; + + long startTime; + long endTime; + + startTime = System.currentTimeMillis(); + try { + + destinationIdentifier = ParameterExpander.expandExpression( aValueMap, destinationExpression ); + generatorIdentifier = ParameterExpander.expandExpression( aValueMap, generatorExpression ); + + aLogger.println("Generating " + generatorIdentifier + " into " + destinationIdentifier); + aLogger.flush(); + + PDFUtil.makePDF(generatorIdentifier,destinationIdentifier); + + } + catch (Throwable t) { + aLogger.println(" error while generating: " + t.getMessage()); + aLogger.flush(); + } + endTime = System.currentTimeMillis(); + + aLogger.println(" Time: " + (endTime-startTime) + " ms
"); + aLogger.flush(); + } + + public Set buildVerbSet() { + return new HashSet(); + } +} diff --git a/source/mircoders/producer/ProducerAll.java b/source/mircoders/producer/ProducerAll.java deleted file mode 100755 index f98c0fa2..00000000 --- a/source/mircoders/producer/ProducerAll.java +++ /dev/null @@ -1,152 +0,0 @@ -package mircoders.producer; - -import java.io.*; -import java.lang.*; -import java.util.*; - -import freemarker.template.*; - -import mir.misc.*; -import mir.storage.*; -import mir.module.*; -import mir.entity.*; - -import mircoders.module.*; -import mircoders.entity.*; -import mircoders.storage.*; - - -public class ProducerAll extends Producer{ - - private boolean rsync; - - public static void main(String argv[]) - { - try { new ProducerAll().handle(new PrintWriter(System.out), null, false,false); } - catch(Exception e) { System.err.println(e.toString()); } - } - - // handle all - public void handle(PrintWriter htmlout, EntityUsers user, boolean force,boolean sync) - { - printHTML(htmlout, "Producer.All: started"); - - long sessionConnectTime = 0; - long startTime = (new java.util.Date()).getTime(); - - try { - new ProducerImages().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in ProducerImages continuing "+ e.toString()); - } - try { - new ProducerAudio().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in ProducerAudio continuing "+ e.toString()); - } - try { - new ProducerVideo().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in ProducerVideo continuing "+ e.toString()); - } - try { - new ProducerOther().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in ProducerOther continuing "+ e.toString()); - } - try { - new ProducerStartPage().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in Producer.StartPage continuing "+ e.toString()); - } - try { - new ProducerContent().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in Producer.Content continuing "+ e.toString()); - } - try { - new ProducerOpenPosting().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in Producer.OpenPosting continuing "+ e.toString()); - } - try { - new ProducerTopics().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in Producer.Topics continuing "+ e.toString()); - } - try { - new ProducerNavigation().handle(htmlout, user, force,sync); - } catch (Exception e) { - logHTML(htmlout, "Producer.All ERROR:" - +" in Producer.Navigation continuing "+ e.toString()); - } - - // Finish - sessionConnectTime = new java.util.Date().getTime() - startTime; - logHTML(htmlout, "Producer.All finished: " + sessionConnectTime + " ms."); - - // do we have to rsync the site - if (sync==true){ - sessionConnectTime = 0; - if (Helper.rsync()!=0){ - sessionConnectTime = new java.util.Date().getTime() - startTime; - logHTML(htmlout, "Rsync failed: " + sessionConnectTime + " ms."); - } else { - sessionConnectTime = new java.util.Date().getTime() - startTime; - logHTML(htmlout, "Rsync succeded: " + sessionConnectTime + " ms."); - } - } - } - - // handle all - public void handle2(PrintWriter htmlout, EntityUsers user, boolean force,boolean sync) - throws StorageObjectException, ModuleException { - printHTML(htmlout, "Producer.All: started"); - - long sessionConnectTime = 0; - long startTime = (new java.util.Date()).getTime(); - EntityContent currentContent; - - //get all new unproduced content-entities - String whereClause="is_produced='0' && to_article_type>0"; - String orderBy="date desc"; - EntityList entityList = contentModule.getContent(whereClause,orderBy,0,-1,null); - - //get their values - while (entityList != null) { - for(int i=0;i 2){ - mergeData.put("previousPage",fileDesc + ".shtml"); - mergeData.put("nextPage",fileDesc + (numberOfPages-2) + ".shtml"); - } else { - if (i==(numberOfPages-1)){ - mergeData.put("previousPage",fileDesc + (numberOfPages-i+1) + ".shtml"); - mergeData.put("nextPage",""); - } else { - mergeData.put("previousPage",fileDesc + (numberOfPages-(i-1)) + ".shtml"); - mergeData.put("nextPage",fileDesc + (numberOfPages-(i+1)) + ".shtml"); - } - } - htmlFileName = "/" + fileDesc + (numberOfPages-i) + ".shtml"; - mergeData.put("filename",fileDesc + (numberOfPages-i) + ".shtml"); - } - - //producing the html-files - boolean retVal = produce(listTemplate, htmlFileName, mergeData, htmlout); - } //end if - }//end for - - sessionConnectTime = new java.util.Date().getTime() - startTime; - logHTML(htmlout, "Producer.List finished: " + sessionConnectTime + " ms."); - } //end handle - - public void setAdditional(String key, TemplateModel value) { - additional.put(key,value); - } - -} diff --git a/source/mircoders/producer/ProducerMedia.java b/source/mircoders/producer/ProducerMedia.java index a44dc43e..efb0f764 100755 --- a/source/mircoders/producer/ProducerMedia.java +++ b/source/mircoders/producer/ProducerMedia.java @@ -76,6 +76,7 @@ abstract public class ProducerMedia extends Producer { e.toString()); logHTML(htmlout, "problem with media id: "+currentMedia.getId()+ " failed!: "+e.toString()); + e.printStackTrace(htmlout); } } diff --git a/source/mircoders/producer/ProducerNavigation.java b/source/mircoders/producer/ProducerNavigation.java deleted file mode 100755 index 2d8d1a88..00000000 --- a/source/mircoders/producer/ProducerNavigation.java +++ /dev/null @@ -1,69 +0,0 @@ -package mircoders.producer; - -import java.io.*; -import java.lang.*; -import java.util.*; - -import freemarker.template.*; - -import mir.misc.*; -import mir.storage.*; -import mir.module.*; -import mir.entity.*; - -import mircoders.entity.*; - -/** - * Title: mir - another content management system - * Description: - * Copyright: Copyright (c) 2001 - * Company: indymedia - * @author idefix - * @version 1.0 - */ - -public class ProducerNavigation extends Producer { - - private static String naviPageTemplate = MirConfig.getProp("Producer.Navigation.Template"); - - public void handle(PrintWriter htmlout, EntityUsers user, boolean forced, boolean sync) - throws mir.module.ModuleException, mir.storage.StorageObjectException { - - printHTML(htmlout, "Producer.Navigation: started"); - - long sessionConnectTime = 0; - long startTime = (new java.util.Date()).getTime(); - String nowWebdbDate = StringUtil.date2webdbDate(new GregorianCalendar()); - String whereClause; - String orderBy; - FileWriter outputFile; - String htmlFileName; - EntityContent currentContent; - EntityList entityList; - SimpleHash naviPageModel; - - // get the imclinks - entityList = linksImcsModule.getByWhereClause("", "sortpriority, title", -1); - EntityList theParentList = linksImcsModule.getByWhereClause("to_parent_id=NULL", "sortpriority, title", -1); - - // put the informations into the navipagemodel - naviPageModel = new SimpleHash(); - naviPageModel.put("topics", topicsModule.getTopicsList()); - naviPageModel.put("imclist", entityList); - naviPageModel.put("parentlist", theParentList); - - htmlFileName = "/navigation.inc"; - - produce(naviPageTemplate, htmlFileName, naviPageModel, new LineFilterWriter(htmlout)); - - // Finish - sessionConnectTime = new java.util.Date().getTime() - startTime; - logHTML(htmlout, "Producer.Navigation finished: " + sessionConnectTime + " ms."); - - if(sync==true){ - Helper.rsync(); - logHTML(htmlout, "Producer.Startseite: rsync done"); - } - } - -} diff --git a/source/mircoders/producer/ProducerOpenPosting.java b/source/mircoders/producer/ProducerOpenPosting.java deleted file mode 100755 index 1278d63e..00000000 --- a/source/mircoders/producer/ProducerOpenPosting.java +++ /dev/null @@ -1,47 +0,0 @@ -package mircoders.producer; - -import java.io.*; -import java.lang.*; -import java.util.*; -import java.sql.*; - -import freemarker.template.*; - -import mir.misc.*; -import mir.storage.*; -import mir.module.*; -import mir.entity.*; - -import mir.module.*; -import mircoders.entity.*; -import mir.storage.*; - - - -public class ProducerOpenPosting extends ProducerList { - - - - public void handle(PrintWriter htmlout, EntityUsers user, boolean force, boolean sync) - throws StorageObjectException, ModuleException - { - listTemplate = MirConfig.getProp("Producer.OpenPosting.Template"); - whereClause="is_published='1'"; - orderBy="date desc, webdb_create desc"; - fileDesc="open"; - - setAdditional("topicslist",topicsModule.getTopicsList()); - setAdditional("title",new SimpleScalar("Open-Posting")); - handleIt(htmlout,user,force); - } - - - public static void main(String argv[]){ - try { - new ProducerOpenPosting().handle(new PrintWriter(System.out), null,false, false); - } catch(Exception e) { - System.err.println(e.toString()); - } - } - -} diff --git a/source/mircoders/producer/ProducerStartPage.java b/source/mircoders/producer/ProducerStartPage.java deleted file mode 100755 index e2185ad7..00000000 --- a/source/mircoders/producer/ProducerStartPage.java +++ /dev/null @@ -1,72 +0,0 @@ -package mircoders.producer; - -import java.io.*; -import java.util.*; - -import freemarker.template.*; - -import mir.entity.*; -import mir.misc.*; -import mir.module.*; -import mir.storage.*; -import mircoders.module.*; -import mircoders.storage.*; -import mircoders.entity.*; - - -public class ProducerStartPage extends Producer { - - private static String startPageTemplate = MirConfig.getProp("Producer.StartPage.Template"); - private static String featuresRSSTemplate = MirConfig.getProp("Producer.FeaturesRSS.Template"); - private static int itemsPerPage = Integer.parseInt(MirConfig.getProp("Producer.StartPage.Items")); - private static int newsPerPage = Integer.parseInt(MirConfig.getProp("Producer.StartPage.Newswire")); - - public static void main(String argv[]){ - try { - // Why are we reloading the configuration here? - // is there something I'm missing? - // mh. - // Configuration.initConfig(argv[0]); - new ProducerStartPage().handle(new PrintWriter(System.out), null); - } catch(Exception e) { - System.err.println(e.toString()); - } - } - - public void handle(PrintWriter htmlout, EntityUsers user, boolean force,boolean sync) - throws StorageObjectException, ModuleException - { - long startTime = System.currentTimeMillis(); - printHTML(htmlout, "Producer.StartPage: started"); - SimpleHash startPageModel = new SimpleHash(); - - // breaking news - ModuleBreaking breakingModule = new ModuleBreaking(DatabaseBreaking.getInstance()); - startPageModel.put("breakingnews", breakingModule.getBreakingNews()); - startPageModel.put("topics", topicsModule.getTopicsList()); - startPageModel.put("newswire", contentModule.getNewsWire(0,newsPerPage)); - startPageModel.put("startspecial", contentModule.getStartArticle()); - startPageModel.put("features", contentModule.getFeatures(0,itemsPerPage)); - startPageModel.put("dc_now", new SimpleScalar(StringUtil.date2w3DateTime(new GregorianCalendar()))); - - - /* @todo switch to compressed */ - produce(startPageTemplate, "/index.shtml", startPageModel, htmlout); - - /* should be mandatory in light of new www.indy newswire. - * but remember Mir is not indy specific. -mh. - * Also should it really always be produced in UTF8 chars -mh? - */ - produce(featuresRSSTemplate, "/features.1-0.rdf", startPageModel, htmlout, "UTF8"); - - // finished - logHTMLFinish(htmlout, "Startpage", 1, startTime, System.currentTimeMillis()); - - if(sync==true){ - logHTML(htmlout, "Producer.Startpage: rsyncing..."); - Helper.rsync(); - printHTML(htmlout, "Producer.Startpage: rsync done"); - } - } -} - diff --git a/source/mircoders/producer/ProducerTopics.java b/source/mircoders/producer/ProducerTopics.java deleted file mode 100755 index 1f1a32bf..00000000 --- a/source/mircoders/producer/ProducerTopics.java +++ /dev/null @@ -1,137 +0,0 @@ -package mircoders.producer; - -import java.io.*; -import java.lang.*; -import java.lang.reflect.*; -import java.util.*; -import java.sql.*; - -import freemarker.template.*; - -import mir.misc.*; -import mir.media.*; -import mir.storage.*; -import mir.module.*; -import mir.entity.*; - -import mircoders.entity.*; -import mircoders.storage.*; - - - -public class ProducerTopics extends ProducerList { - - public String where; - String currentMediaId; - EntityList upMediaEntityList; - EntityList imageEntityList; - EntityList currentMediaList; - Entity mediaType; - EntityMedia uploadedMedia; - Class mediaHandlerClass=null; - MirMedia mediaHandler=null; - String mediaHandlerName=null; - Database mediaStorage=null; - String tinyIcon; - String iconAlt; - - public void handle(PrintWriter htmlout, EntityUsers user, boolean force, boolean sync, String id) - throws StorageObjectException, ModuleException { - where=id; - handle(htmlout,user,force,sync); - } - - public void handle(PrintWriter htmlout, EntityUsers user, boolean force, boolean sync) - throws StorageObjectException, ModuleException - { - long startTime = System.currentTimeMillis(); - int pageCount =0; - logHTML(htmlout, "Producer.Topics: started"); - - /** @todo should be done in static */ - listTemplate = MirConfig.getProp("Producer.TopicList.Template"); - - orderBy="date desc, webdb_create desc"; - EntityList topicsEntityList; - if(where==null){ - topicsEntityList = topicsModule.getByWhereClause("","title", -1); - } else { - topicsEntityList = topicsModule.getByWhereClause(where,"title", -1); - } - - for(int i=0; i < topicsEntityList.size(); i++){ - - EntityTopics currentTopic = (EntityTopics)topicsEntityList.elementAt(i); - - try { - EntityList contentEntityList = DatabaseContentToTopics.getInstance().getContent(currentTopic); - String whereClauseSpecial=null; - - if (contentEntityList!=null || force==true) { - if (contentEntityList!=null){ - boolean first=true; - whereClause="is_published='1' AND to_article_type >= 0 AND to_article_type <=2 AND id IN ("; - whereClauseSpecial="is_published='1' AND to_article_type=3 AND id IN ("; - for(int j=0; j < contentEntityList.size(); j++){ - if(first==false) { - whereClause += ","; - whereClauseSpecial += ","; - } - EntityContent currentContent = (EntityContent)contentEntityList.elementAt(j); - whereClause += currentContent.getId(); - whereClauseSpecial += currentContent.getId(); - - setAdditional("topic",currentTopic); - - first = false; - } - whereClause += ")"; - whereClauseSpecial += ")"; - } - - if(contentEntityList==null && force==true){ - //hihi, das ist eigentlich boese - whereClause="is_published='1' AND to_article_type>=0 AND id IN (0)"; - } - - fileDesc = currentTopic.getValue("filename"); - - // get the startarticle - EntityList entityList = contentModule.getContent(whereClauseSpecial,"date desc, webdb_create desc",0,1); - String currentMediaId = null; - SimpleHash imageHash = new SimpleHash(); - EntityContent currentContent; - if(entityList != null && entityList.size()==1){ - currentContent = (EntityContent)entityList.elementAt(0); - try { - setAdditional("special",currentContent); - } catch (Exception e) { - theLog.printError("ProducerTopics: problem with start special media: "+currentContent.getId()+" "+e.toString()+" skipping"); - logHTML(htmlout,"ProducerTopics: problem with start special media: "+currentContent.getId()+" "+e.toString()); - } - } - - //set the list of topics - setAdditional("topicslist",topicsEntityList); - - handleIt(htmlout,user,force); - pageCount++; - } - } catch (Exception e) { - theLog.printError("ProducerTopics: problem with start special media: " - +e.toString()+" skipping"); - logHTML(htmlout,"ProducerTopics: problem with topic id: " - +currentTopic.getId()+ "skipping"); - } - } - logHTMLFinish(htmlout, "Topics", pageCount, startTime, System.currentTimeMillis()); - } - - public static void main(String argv[]){ - try { - new ProducerOpenPosting().handle(new PrintWriter(System.out), null,false, false); - } catch(Exception e) { - System.err.println(e.toString()); - } - } -} diff --git a/source/mircoders/producer/reader/SupplementalProducerNodeBuilders.java b/source/mircoders/producer/reader/SupplementalProducerNodeBuilders.java new file mode 100755 index 00000000..9c71ec81 --- /dev/null +++ b/source/mircoders/producer/reader/SupplementalProducerNodeBuilders.java @@ -0,0 +1,77 @@ +package mircoders.producer.reader; + +import java.util.*; +import mir.producer.*; +import mir.producer.reader.*; +import mir.util.*; +import mir.entity.adapter.*; +import mir.generator.*; +import mircoders.producer.*; + +public class SupplementalProducerNodeBuilders { + + public static void registerBuilders(ProducerNodeBuilderLibrary aBuilderLibrary, EntityAdapterModel aModel) { + aBuilderLibrary.registerBuilder("ModifyContent", ContentModifyingProducerNodeBuilder.class); + aBuilderLibrary.registerBuilder("MarkContent", ContentMartkingProducerNodeBuilder.class); + } + + public static class ContentMartkingProducerNodeBuilder extends DefaultProducerNodeBuilders.AbstractProducerNodeBuilder { + private final static String MARKER_KEY_ATTRIBUTE = DefaultProducerNodeBuilders.KEY_ATTRIBUTE; + private final static String[] MARKER_REQUIRED_ATTRIBUTES = { MARKER_KEY_ATTRIBUTE }; + private final static String[] MARKER_OPTIONAL_ATTRIBUTES = {}; + private final static String[] MARKER_SUBNODES = {}; + + private String key; + + public ContentMartkingProducerNodeBuilder() { + super(MARKER_SUBNODES); + } + + public void setAttributes(Map anAttributes) throws ProducerConfigExc { + ReaderTool.checkAttributes(anAttributes, MARKER_REQUIRED_ATTRIBUTES, MARKER_OPTIONAL_ATTRIBUTES); + + key = (String) anAttributes.get(MARKER_KEY_ATTRIBUTE); + }; + + public ProducerNode constructNode() { + return new ContentMarkingProducerNode(key); + }; + } + + + public static class ContentModifyingProducerNodeBuilder extends DefaultProducerNodeBuilders.AbstractProducerNodeBuilder { + private final static String MODIFYER_KEY_ATTRIBUTE = DefaultProducerNodeBuilders.KEY_ATTRIBUTE; + private final static String MODIFYER_FIELD_ATTRIBUTE = "field"; + private final static String MODIFYER_VALUE_ATTRIBUTE = "value"; + private final static String[] MODIFYER_REQUIRED_ATTRIBUTES = { MODIFYER_KEY_ATTRIBUTE, MODIFYER_FIELD_ATTRIBUTE, MODIFYER_VALUE_ATTRIBUTE }; + private final static String[] MODIFYER_OPTIONAL_ATTRIBUTES = {}; + private final static String[] MODIFYER_SUBNODES = {}; + + private String key; + private String field; + private String value; + + public ContentModifyingProducerNodeBuilder() { + super(MODIFYER_SUBNODES); + } + + public void setAttributes(Map anAttributes) throws ProducerConfigExc { + ReaderTool.checkAttributes(anAttributes, MODIFYER_REQUIRED_ATTRIBUTES, MODIFYER_OPTIONAL_ATTRIBUTES); + + key = (String) anAttributes.get(MODIFYER_KEY_ATTRIBUTE); + field = (String) anAttributes.get(MODIFYER_FIELD_ATTRIBUTE); + value = (String) anAttributes.get(MODIFYER_VALUE_ATTRIBUTE); + }; + + public ProducerNode constructNode() { + return new ContentModifyingProducerNode(key, field, value); + }; + } + + +/* + TODO: + [ ] Media Producing +*/ + +} \ No newline at end of file diff --git a/source/mircoders/servlet/ServletModuleComment.java b/source/mircoders/servlet/ServletModuleComment.java index dc5383d7..57f225f2 100755 --- a/source/mircoders/servlet/ServletModuleComment.java +++ b/source/mircoders/servlet/ServletModuleComment.java @@ -6,6 +6,7 @@ import java.util.*; import java.net.*; import javax.servlet.*; import javax.servlet.http.*; +import org.apache.struts.util.MessageResources; import freemarker.template.*; @@ -14,9 +15,14 @@ import mir.module.*; import mir.misc.*; import mir.entity.*; import mir.storage.*; - +import mir.generator.*; import mir.entity.*; +import mir.entity.adapter.*; +import mir.util.*; + import mircoders.storage.*; +import mircoders.global.*; +import mircoders.localizer.*; import mircoders.module.*; /* @@ -29,98 +35,205 @@ import mircoders.module.*; public class ServletModuleComment extends ServletModule { - private ModuleContent moduleContent; - - // Singelton / Kontruktor - private static ServletModuleComment instance = new ServletModuleComment(); - public static ServletModule getInstance() { return instance; } - - private ServletModuleComment() { - theLog = Logfile.getInstance(MirConfig.getProp("Home") + MirConfig.getProp("ServletModule.Comment.Logfile")); - templateListString = MirConfig.getProp("ServletModule.Comment.ListTemplate"); - templateObjektString = MirConfig.getProp("ServletModule.Comment.ObjektTemplate"); - templateConfirmString = MirConfig.getProp("ServletModule.Comment.ConfirmTemplate"); - try { - mainModule = new ModuleComment(DatabaseComment.getInstance()); - moduleContent = new ModuleContent(DatabaseContent.getInstance()); - } - catch (StorageObjectException e) { - theLog.printError("servletmodule: comment could not be initialized"); - } - } - - - public void list(HttpServletRequest req, HttpServletResponse res) - throws ServletModuleException - { - // Parameter auswerten - SimpleHash mergeData = new SimpleHash(); - String query_text = req.getParameter("query_text"); - mergeData.put("query_text",query_text); - if (query_text!=null) mergeData.put("query_text_encoded",URLEncoder.encode(query_text)); - String query_field = req.getParameter("query_field"); - mergeData.put("query_field",query_field); - String query_is_published = req.getParameter("query_is_published"); - mergeData.put("query_is_published",query_is_published); - - String offset = req.getParameter("offset"); - if (offset==null || offset.equals("")) offset="0"; - mergeData.put("offset",offset); - - // patching order - String order = req.getParameter("order"); - if(order!=null) { - mergeData.put("order", order); - mergeData.put("order_encoded", URLEncoder.encode(order)); - if (order.equals("webdb_create")) order="webdb_create desc"; - } - - // sql basteln - String whereClause=""; boolean isFirst=true; - if (query_text!=null && !query_text.equalsIgnoreCase("")) { - whereClause += "lower("+query_field+") like lower('%"+query_text+"%')"; isFirst=false;} - if (query_is_published != null && !query_is_published.equals("")) { - if (isFirst==false) whereClause+=" and "; - whereClause += "is_published='"+query_is_published+"'"; - isFirst=false; - } - - theLog.printDebugInfo("sql-whereclause: " + whereClause + " order: " + order + " offset: " + offset); - - // fetch und ausliefern - try { - - if (query_text!=null || query_is_published!=null ) { - EntityList theList = mainModule.getByWhereClause(whereClause, order, (new Integer(offset)).intValue()); - if (theList!=null && theList.size()>0) { - - //make articleHash for comment - StringBuffer buf= new StringBuffer("id in (");boolean first=true; - for(int i=0;i0) { + + //make articleHash for comment + StringBuffer buf= new StringBuffer("id in (");boolean first=true; + for(int i=0;i0) { + generationData.put( "previousurl", "module=Comment&do=listarticlecomments&offset="+ + Math.max( 0, anOffset - nrCommentsPerPage )+"&articleid="+anArticleId); + generationData.put("previous", new Integer(Math.max( 0, anOffset - nrCommentsPerPage ))); + } + + if (anOffset + nrCommentsPerPage < totalNrComments) { + generationData.put( "nexturl", "module=Comment&do=listarticlecomments&offset="+ + Math.min( anOffset + nrCommentsPerPage, totalNrComments-1 )+"&articleid="+anArticleId); + generationData.put("next", new Integer(Math.min( anOffset + nrCommentsPerPage, totalNrComments-1 ))); + } + + generator.generate(aWriter, generationData, new PrintWriter(new NullWriter())); + } + catch (Throwable t) { + t.printStackTrace(System.out); + throw new ServletModuleException(t.getMessage()); + } + } + + public void listarticlecomments(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException + { + String articleIdString = req.getParameter("articleid"); + String offsetString = req.getParameter("offset"); + int offset = 0; + int articleId = 0; + + try { + offset = Integer.parseInt(offsetString); + } + catch (Throwable t) { + } + + try { + articleId = Integer.parseInt(articleIdString); + + showArticleCommentList( res.getWriter(), offset, articleId, getLocale(req)); + } + catch (ServletModuleException e) { + throw e; + } + catch (Throwable e) { + e.printStackTrace(System.out); + throw new ServletModuleException(e.getMessage()); + } + } + + public void performarticlecommentsoperation(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException { + String commentIdString = req.getParameter("commentid"); + String articleIdString = req.getParameter("articleid"); + String offsetString = req.getParameter("offset"); + String operation = req.getParameter("operation"); + int offset = 0; + int articleId = 0; + + try { + articleId = Integer.parseInt(articleIdString); + + showArticleCommentList( res.getWriter(), offset, articleId, getLocale(req)); + } + catch (ServletModuleException e) { + throw e; + } + catch (Throwable e) { + e.printStackTrace(System.out); + throw new ServletModuleException(e.getMessage()); + } + } } diff --git a/source/mircoders/servlet/ServletModuleContent.java b/source/mircoders/servlet/ServletModuleContent.java index 9871d949..627efc9e 100755 --- a/source/mircoders/servlet/ServletModuleContent.java +++ b/source/mircoders/servlet/ServletModuleContent.java @@ -26,10 +26,13 @@ import mircoders.entity.*; * ServletModuleContent - * deliver html for the article admin form. * - * @version $Revision: 1.19 $ + * @version $Revision: 1.20 $ * @author $Author: mh $ * * $Log: ServletModuleContent.java,v $ + * Revision 1.20 2002/08/25 19:00:11 mh + * merge of localization branch into HEAD. mh and zap + * * Revision 1.19 2002/07/20 22:24:25 mh * made the add() method use _showObject. Fixes a bug that cause the popUps not to show up when adding an article in the admin * @@ -166,8 +169,12 @@ public class ServletModuleContent extends ServletModule withValues.put("is_published","0"); if (!withValues.containsKey("is_html")) withValues.put("is_html","0"); - if (withValues.get("creator").toString().equals("")) - withValues.put("creator","Anonym"); + +// ML: this is not multi-language friendly and this can be done in a template +// if (withValues.get("creator").toString().equals("")) +// withValues.put("creator","Anonym"); + + String id = mainModule.add(withValues); DatabaseContentToTopics.getInstance().setTopics(id,req.getParameterValues("to_topic")); //theLog.printDebugInfo(":: content :: inserted"); @@ -317,8 +324,11 @@ public class ServletModuleContent extends ServletModule withValues.put("is_published","0"); if (!withValues.containsKey("is_html")) withValues.put("is_html","0"); - if (withValues.get("creator").toString().equals("")) - withValues.put("creator","Anonym"); + +// ML: this is not multi-language friendly and this can be done in a template +// if (withValues.get("creator").toString().equals("")) +// withValues.put("creator","Anonym"); + //theLog.printDebugInfo("updating. "); String id = mainModule.set(withValues); DatabaseContentToTopics.getInstance().setTopics(req.getParameter("id"),topic_id); diff --git a/source/mircoders/servlet/ServletModuleLocalizer.java b/source/mircoders/servlet/ServletModuleLocalizer.java new file mode 100755 index 00000000..9c58d9fd --- /dev/null +++ b/source/mircoders/servlet/ServletModuleLocalizer.java @@ -0,0 +1,41 @@ +package mircoders.servlet; + +import java.util.*; + +import javax.servlet.*; +import javax.servlet.http.*; + +import mir.servlet.*; +import mir.entity.adapter.*; +import mircoders.global.*; +import mircoders.localizer.*; +import mircoders.storage.*; +import mircoders.entity.*; + +public class ServletModuleLocalizer extends ServletModule { + private static ServletModuleLocalizer instance = new ServletModuleLocalizer(); + + public static ServletModule getInstance() { return instance; } + + public void commentoperation(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException + { + try { + String operationKey = req.getParameter("operation"); + String commentId = req.getParameter("commentid"); + EntityComment comment = (EntityComment) DatabaseComment.getInstance().selectById(commentId); + MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = + (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) + MirGlobal.localizer().adminInterface().simpleCommentOperations().get(operationKey); + + EntityAdapter adapter = MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", comment); + + operation.perform( adapter ); + + res.sendRedirect(req.getParameter("returnuri")); + } + catch (Throwable t) { + t.printStackTrace(System.out); + throw new ServletModuleException(t.getMessage()); + } + } +} \ No newline at end of file diff --git a/source/mircoders/servlet/ServletModuleOpenIndy.java b/source/mircoders/servlet/ServletModuleOpenIndy.java index 2b8e4630..b244ac97 100755 --- a/source/mircoders/servlet/ServletModuleOpenIndy.java +++ b/source/mircoders/servlet/ServletModuleOpenIndy.java @@ -34,6 +34,8 @@ import mircoders.storage.*; import mircoders.module.*; import mircoders.producer.*; import mircoders.media.MediaRequest; +import mircoders.global.*; +import mircoders.localizer.*; /* * ServletModuleOpenIndy - @@ -42,9 +44,12 @@ import mircoders.media.MediaRequest; * open-postings to the newswire * * @author $Author: mh $ - * @version $Revision: 1.38 $ $Date: 2002/07/21 22:50:14 $ + * @version $Revision: 1.39 $ $Date: 2002/08/25 19:00:11 $ * * $Log: ServletModuleOpenIndy.java,v $ + * Revision 1.39 2002/08/25 19:00:11 mh + * merge of localization branch into HEAD. mh and zap + * * Revision 1.38 2002/07/21 22:50:14 mh * cleanup coding style of getpdf() method * @@ -65,6 +70,7 @@ public class ServletModuleOpenIndy extends ServletModule private String postingFormTemplate, postingFormDoneTemplate, postingFormDupeTemplate; private ModuleContent contentModule; + private ModuleComment commentModule; private ModuleImages imageModule; private ModuleTopics themenModule; private String directOp ="yes"; @@ -83,13 +89,13 @@ public class ServletModuleOpenIndy extends ServletModule postingFormDoneTemplate = MirConfig.getProp("ServletModule.OpenIndy.PostingDoneTemplate"); postingFormDupeTemplate = MirConfig.getProp("ServletModule.OpenIndy.PostingDupeTemplate"); directOp = MirConfig.getProp("DirectOpenposting").toLowerCase(); - passwdProtection = MirConfig.getProp("PasswdProtection").toLowerCase(); + passwdProtection = MirConfig.getProp("PasswdProtection").toLowerCase(); mainModule = new ModuleComment(DatabaseComment.getInstance()); contentModule = new ModuleContent(DatabaseContent.getInstance()); themenModule = new ModuleTopics(DatabaseTopics.getInstance()); imageModule = new ModuleImages(DatabaseImages.getInstance()); defaultAction="addposting"; - + } catch (StorageObjectException e) { theLog.printError("servletmoduleopenindy could not be initialized"); @@ -104,19 +110,27 @@ public class ServletModuleOpenIndy extends ServletModule public void addcomment(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException { String aid = req.getParameter("aid"); // the article id the comment will belong to + String language = req.getParameter("language"); + if (aid!=null && !aid.equals("")) { - SimpleHash mergeData = new SimpleHash(); - - // onetimepasswd - if(passwdProtection.equals("yes")){ - String passwd = this.createOneTimePasswd(); - System.out.println(passwd); - HttpSession session = req.getSession(false); - session.setAttribute("passwd",passwd); - mergeData.put("passwd", passwd); - } - + SimpleHash mergeData = new SimpleHash(); + + // onetimepasswd + if(passwdProtection.equals("yes")){ + String passwd = this.createOneTimePasswd(); + System.out.println(passwd); + HttpSession session = req.getSession(false); + session.setAttribute("passwd",passwd); + mergeData.put("passwd", passwd); + } + + if (language!=null) { + HttpSession session = req.getSession(false); + session.setAttribute("Locale", new Locale(language, "")); + session.setAttribute("passwd",language); + } + mergeData.put("aid", aid); deliver(req, res, mergeData, commentFormTemplate); } @@ -129,7 +143,7 @@ public class ServletModuleOpenIndy extends ServletModule */ public void inscomment(HttpServletRequest req, HttpServletResponse res) - throws ServletModuleException,ServletModuleUserException + throws ServletModuleException,ServletModuleUserException { String aid = req.getParameter("to_media"); // the article id the comment will belong to if (aid!=null && !aid.equals("")) @@ -137,44 +151,50 @@ public class ServletModuleOpenIndy extends ServletModule // ok, collecting data from form try { HashMap withValues = getIntersectingValues(req, DatabaseComment.getInstance()); - + //no html in comments(for now) for (Iterator i=withValues.keySet().iterator(); i.hasNext(); ){ String k=(String)i.next(); String v=(String)withValues.get(k); - + withValues.put(k,StringUtil.removeHTMLTags(v)); } withValues.put("is_published","1"); - - //checking the onetimepasswd - if(passwdProtection.equals("yes")){ - HttpSession session = req.getSession(false); - String sessionPasswd = (String)session.getAttribute("passwd"); - if ( sessionPasswd == null){ - throw new ServletModuleUserException("Lost password"); - } - String passwd = req.getParameter("passwd"); - if ( passwd == null || (!sessionPasswd.equals(passwd))) { - throw new ServletModuleUserException("Missing password"); - } - session.invalidate(); - } - + + //checking the onetimepasswd + if(passwdProtection.equals("yes")){ + HttpSession session = req.getSession(false); + String sessionPasswd = (String)session.getAttribute("passwd"); + if ( sessionPasswd == null){ + throw new ServletModuleUserException("Lost password"); + } + String passwd = req.getParameter("passwd"); + if ( passwd == null || (!sessionPasswd.equals(passwd))) { + throw new ServletModuleUserException("Missing password"); + } + session.invalidate(); + } + // inserting into database String id = mainModule.add(withValues); theLog.printDebugInfo("id: "+id); //insert was not successfull if(id==null){ deliver(req, res, new SimpleHash(), commentFormDupeTemplate); + } else { + DatabaseContent.getInstance().setUnproduced("id="+aid); + + try { + EntityComment comment = (EntityComment) DatabaseComment.getInstance().selectById(id); + MirGlobal.localizer().openPostings().afterCommentPosting(comment); + } + catch (Throwable t) { + throw new ServletModuleException(t.getMessage()); + } + + + } - - // producing new page - new ProducerContent().handle(null, null, true, false, aid); - - // sync the server - int exitValue = Helper.rsync(); - theLog.printDebugInfo("rsync:"+exitValue); // redirecting to url // should implement back to article @@ -196,16 +216,16 @@ public class ServletModuleOpenIndy extends ServletModule public void addposting(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException { SimpleHash mergeData = new SimpleHash(); - - // onetimepasswd - if(passwdProtection.equals("yes")){ - String passwd = this.createOneTimePasswd(); - System.out.println(passwd); - HttpSession session = req.getSession(false); - session.setAttribute("passwd",passwd); - mergeData.put("passwd", passwd); - } - + + // onetimepasswd + if(passwdProtection.equals("yes")){ + String passwd = this.createOneTimePasswd(); + System.out.println(passwd); + HttpSession session = req.getSession(false); + session.setAttribute("passwd",passwd); + mergeData.put("passwd", passwd); + } + String maxMedia = MirConfig.getProp("ServletModule.OpenIndy.MaxMediaUploadItems"); String numOfMedia = req.getParameter("medianum"); if(numOfMedia==null||numOfMedia.equals("")){ @@ -213,7 +233,7 @@ public class ServletModuleOpenIndy extends ServletModule } else if(Integer.parseInt(numOfMedia) > Integer.parseInt(maxMedia)) { numOfMedia = maxMedia; } - + int mediaNum = Integer.parseInt(numOfMedia); SimpleList mediaFields = new SimpleList(); for(int i =0; i 0) { + + if (to_topicsArr != null && to_topicsArr.length > 0) { try{ DatabaseContentToTopics.getInstance().setTopics(cid,to_topicsArr); setTopic = true; @@ -345,22 +371,13 @@ public class ServletModuleOpenIndy extends ServletModule DatabaseContentToMedia.getInstance().addMedia(cid,mediaEnt.getId()); } - // producing openpostinglist - new ProducerOpenPosting().handle(null,null,false,false); - // producing new page - new ProducerContent().handle(null, null, false, false,cid); - //if direct op producing startpage - if (directOp.equals("yes")) new ProducerStartPage().handle(null,null); - - //produce the topicPages if set - //should be more intelligent - //if(setTopic==true) new ProducerTopics().handle(null,null); - - // sync the server - //should be configureable - int exitValue = Helper.rsync(); - theLog.printDebugInfo("rsync: "+exitValue); - + try { + MirGlobal.localizer().openPostings().afterContentPosting( + (EntityContent)contentModule.getById(cid)); + } + catch (Throwable t) { + throw new ServletModuleException(t.getMessage()); + } } catch (MirMediaException e) { throw new ServletModuleException("MediaException: "+ e.toString());} catch (IOException e) { throw new ServletModuleException("IOException: "+ e.toString());} @@ -439,17 +456,17 @@ public class ServletModuleOpenIndy extends ServletModule +", we do not support this mime-type. " +"Error One or more files of unrecognized type. Sorry"); } - - protected String createOneTimePasswd(){ - Random r = new Random(); - int random = r.nextInt(); - long l = System.currentTimeMillis(); - l = (l*l*l*l)/random; - if(l<0) l = l * -1; - String returnString = ""+l; - return returnString.substring(5); - } - + + protected String createOneTimePasswd(){ + Random r = new Random(); + int random = r.nextInt(); + long l = System.currentTimeMillis(); + l = (l*l*l*l)/random; + if(l<0) l = l * -1; + String returnString = ""+l; + return returnString.substring(5); + } + } diff --git a/source/mircoders/servlet/ServletModuleProducer.java b/source/mircoders/servlet/ServletModuleProducer.java index 1e4e0d2c..cf03a629 100755 --- a/source/mircoders/servlet/ServletModuleProducer.java +++ b/source/mircoders/servlet/ServletModuleProducer.java @@ -10,9 +10,11 @@ import freemarker.template.*; import mir.servlet.*; import mir.misc.*; +import mir.producer.*; import mircoders.producer.*; import mircoders.entity.*; +import mircoders.global.*; /* Verteilerservlet, dass je nach Parameter task die Klasse Producer"TASK" * ueber die Methode handle(); aufruft @@ -37,29 +39,51 @@ public class ServletModuleProducer extends ServletModule { try { PrintWriter out = res.getWriter(); - String taskParam = req.getParameter("task"); - String forcedParam = req.getParameter("forced"); - String syncParam = req.getParameter("sync"); - theLog.printInfo("Starting Task: " + taskParam); - if (taskParam == null) { - throw new ServletModuleException("Kein Task angegeben!"); - } else { - Class producerModule = Class.forName("mircoders.producer.Producer" + taskParam); - Producer producer = (Producer)producerModule.newInstance(); - HttpSession session=req.getSession(false); - EntityUsers user = (EntityUsers)session.getAttribute("login.uid"); - - if (forcedParam!=null && !forcedParam.equals("")) { - if (syncParam!=null && !syncParam.equals("")) { - producer.handle(out, user, true, true); - } else { - producer.handle(out, user, true,false); + + + if (req.getParameter("producer")!=null) { + // ML: new producer system: + + String producerParam = req.getParameter("producer"); + String verbParam = req.getParameter("verb"); + + MirGlobal.producerEngine().addJob(producerParam, verbParam); + MirGlobal.producerEngine().printQueueStatus(out); + +// ProducerFactory factory = (ProducerFactory) MirGlobal.localizer().producers().factories().get(producerParam); +// mir.producer.Producer producer = factory.makeProducer(verbParam); + +// producer.produce(out); + + } + else + { + // ML: old producer system: + + String taskParam = req.getParameter("task"); + String forcedParam = req.getParameter("forced"); + String syncParam = req.getParameter("sync"); + theLog.printInfo("Starting Task: " + taskParam); + if (taskParam == null) { + throw new ServletModuleException("Kein Task angegeben!"); + } else { + Class producerModule = Class.forName("mircoders.producer.Producer" + taskParam); + mircoders.producer.Producer producer = (mircoders.producer.Producer) producerModule.newInstance(); + HttpSession session=req.getSession(false); + EntityUsers user = (EntityUsers)session.getAttribute("login.uid"); + + if (forcedParam!=null && !forcedParam.equals("")) { + if (syncParam!=null && !syncParam.equals("")) { + producer.handle(out, user, true, true); + } else { + producer.handle(out, user, true,false); + } + } else { + producer.handle(out, user, false,false); } - } else { - producer.handle(out, user, false,false); - } - } + } + } } catch (Exception e) { throw new ServletModuleException(e.toString()); diff --git a/templates-dist/admin/start_admin.template b/templates-dist/admin/start_admin.template index a72f8c9b..3973373a 100755 --- a/templates-dist/admin/start_admin.template +++ b/templates-dist/admin/start_admin.template @@ -45,14 +45,12 @@
${lang("start.content.new")}

${lang("start.show")}:
- - ${lang("start.content.newswire")}
- - ${lang("start.content.feature")}
- - ${lang("start.content.topicspecial")}
- - ${lang("start.content.startspecial")}
+ + + ${a.name}
+
+ +
${lang("start.content.not_published")}
@@ -86,73 +84,38 @@   - - ${lang("start.generate.title")} + Localizer producers +

-
- ${lang("start.generate.all.title")}: -
- - ${lang("start.generate.all.new")}  |  - -
${lang("start.generate.parts.title")}: -
- - ${lang("start.generate.startpages.new")} -  |  - ${lang("start.generate.all_forced")} -  |  - ${lang("start.generate.all_sync")} -
- - ${lang("start.generate.content.new")} -  |  - ${lang("start.generate.all_forced")} -
- - ${lang("start.generate.topics.new")} -  |  - ${lang("start.generate.all_forced")} -
- - ${lang("start.generate.postings.new")} -  |  - ${lang("start.generate.all_forced")} -
- - ${lang("start.generate.images.new")} -  |  - ${lang("start.generate.all_forced")} -
- - ${lang("start.generate.audio.new")} -  |  - ${lang("start.generate.all_forced")} -
- - ${lang("start.generate.video.new")} -  |  - ${lang("start.generate.all_forced")} -
- - ${lang("start.generate.other.new")} -  |  - ${lang("start.generate.all_forced")} - -
- - ${lang("start.generate.navigation")} -
+ + + + + + + + + + + + +
producerverbs
${p.key} + + + + + + +
+ + ${v} + +
+
+
+ diff --git a/templates-dist/de/open/comment.template b/templates-dist/de/open/comment.template deleted file mode 100755 index 750db5ee..00000000 --- a/templates-dist/de/open/comment.template +++ /dev/null @@ -1,119 +0,0 @@ - -indymedia.de | comment.commit - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Kommentierung eines Beiträgs bei Indymedia -
-

- - Hinweis: Dein Kommentar kann in jedem Stil und jeder Form sein, akademisch bis persönlich.
- Aber bitte bleibe beim Thema des Artikels, den Du kommentierst und versuche, präzise zu sein. -
-

- -
- Kommentierungsformular -
- Titel des Kommentars: - - (muss ausgefüllt werden) -
- Dein Name: - - (muss ausgefüllt werden) -
- Deine eMail-Adresse: - - (optional) -
- Deine Web Adresse: - - (optional) -
- Deine Telefon-Nr.: - - (optional) -
- Deine Adresse: - - (optional) -
- Die Sprache deines Kommentares: - - - (optional) -
- Dein Kommentar: - -   -

-
  - -
-  
- -
-
-  
- -
-
- -
- - - diff --git a/templates-dist/de/open/comment_done.template b/templates-dist/de/open/comment_done.template deleted file mode 100755 index c9838a70..00000000 --- a/templates-dist/de/open/comment_done.template +++ /dev/null @@ -1,39 +0,0 @@ - - - - - indymedia.de | open posting - - - - - - - - - - - - - -
- Danke. Deine Ergänzung ist jetzt auf dem Weg zur Website! -
- -
- In wenigen Minuten ist Deine Ergänzung unter dem ergänzten Artikel.
- Manchmal kann es aber aufgrund technischer Probleme etwas dauern bis sie erscheint.
-
- Die Moderationskriterien von indymedia.de kannst Du hier lesen. - -
- Gedulde Dich einen Moment - Es lohnt sich!
-
- -
- >> Zurück zum kommentierten Artikel -
- - - diff --git a/templates-dist/de/open/comment_dupe.template b/templates-dist/de/open/comment_dupe.template deleted file mode 100755 index 9ec36487..00000000 --- a/templates-dist/de/open/comment_dupe.template +++ /dev/null @@ -1,40 +0,0 @@ - - - - - indymedia.de | open posting - Kommentar-Duplikat - - - - - - - - - - - - - -
- - Immer ruhig mit den jungen Pferden! - -
- -
- Du hast vermutlich den Reload-Button - benutzt oder versucht, Deinen Kommentar noch einmal zu posten. Das ist aber - unnötig. Die Tatsache, dass Du diesen Text liest, bedeutet, dass Dein - Kommentar schon angekommen ist. Er wird gleich auf der Seite auftauchen. Versprochen. -
-
Keine Panik

-
-
-
- >> Zurück zum kommentierten Artikel -
- - - diff --git a/templates-dist/de/open/comment_en.template b/templates-dist/de/open/comment_en.template deleted file mode 100755 index 192e57f7..00000000 --- a/templates-dist/de/open/comment_en.template +++ /dev/null @@ -1,93 +0,0 @@ - - - indymedia.de | comment.commit - - - - - - -
- -adding a comment
- - -

Guidelines for commenting on news articles

-Thanks for contributing to the dynamic alternative news on active! -

-Add any responses to news articles that you feel are appropriate to -www.indy's goals eg: social activitism, local communities, ecology and development. -

-The response article can be in any type of format you want, from traditional academic discourse, to subjective personal rants. -

-Please keep it on topic and concise. -

-

* = required field -

- -

- -
- - -
- Titel des Kommentars (*)
- -

- Dein Name (*)
-
-

- - - - - -
- email (optional) - - web address (begin with http://) (optional) -
- - - -
- -

- - - - -
- phone (optional) - - address (optional) -
- - - -
- - language
-the language of the submission
-
-
-

- your comment*
- -

-
- - - -
- - -
-
- -
- - \ No newline at end of file diff --git a/templates-dist/de/open/posting.template b/templates-dist/de/open/posting.template deleted file mode 100755 index 66622741..00000000 --- a/templates-dist/de/open/posting.template +++ /dev/null @@ -1,341 +0,0 @@ - - - - indymedia.de | open posting - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- >> Direkt zum Eingabeformular springen! -
-
- Veröffentliche Deinen Beitrag
-
- -

Kurze Anleitung zum Posten eines Beitrages bei de.indymedia.org

- - -Indymedia ist ein basisdemokratischer Nachrichtenkanal. -Wir arbeiten aus Liebe und aus Respekt gegenüber Menschen, -die sich für eine bessere, lebenswertere Welt engagieren. -Bei Indymedia kann grundsätzlich JedeR Texte, Videos, Audios oder Fotos -zu politischen oder sozialen Themen veröffentlichen. -

- -Indymedia will vor allem die Möglichkeit geben, -subjektive Stellungnahmen verschiedenster Menschen -'auf der Strasse' über politische Ereignisse oder aus -der alltäglichen Lebenswelt zu veröffentlichen. Zudem -ist Indymedia eine Plattform für Hintergrundberichte, -die andere Hintergründe als kommerzielle Medien aufzeigen. -

- -Um indymedia als Plattform für eigene -Berichterstattung zu konturieren, werden bestimmte -Beiträge nicht auf die Startseite gestellt. Dazu gehören: - - -
    -
  • -Termine und Demoaufrufe [ Weil wir wissen, dass diese für die Mobilisierung -sehr wichtig sind, verlinken wir zu verschiedenen Internetprojekten, auf -denen Ihr gute Terminsammlungen findet ] Bei angelaufenen Kampagnen werden -regelmäßig Termine und Aufrufe in die Mittelspalten-Texte als Links gesetzt. - - -
  • -schon an anderen Stellen veröffentlichte Texte und aus kommerziellen Medien -kopierte Texte
    -[ hierbei sind Übersetzungen von Texten und Meldungen -von hier kaum zugänglichen Medien eine Ausnahme ] - - -
  • Gruppenstatements, Presseerklärungen, Diskussionspapiere, Massenmails -
    - -[auch hier geht es um die Zugänglichkeit von Texten. Beiträge von -Gruppen, die schon auf zahlreichen anderen Internet-Seiten oder in -Zeitschriften veröffentlicht wurden, werden nicht auf die Startseite -gestellt, weil es nicht der Ansatz von indymedia ist alles -irgendwie relevante auf der Seite zu versammeln, sondern eine -Plattform für eigene Berichterstattung zu sein. Unter eigener -Berichtserstattung verstehen wir allerdings Presseerklärungen -von Kleingruppen oder Diskussionspapiere einzelner. - - -
  • -superkurze Meldungen: - - -
  • -reine Kommentare ohne Nachrichtenwert
    -[ Zusätzlich zu den selbstverfassten Beiträgen, gibt es bei indymedia -noch die Möglichkeit über Artikel zu diskutieren. -Das könnt Ihr unter dem jeweiligen Artikel - einfach auf -"Kommentar eingeben" klicken ] - - -
  • -Beiträge von hierarchischen Gruppen und Parteien - - -
  • -Außerdem gibt es Beiträge, die sofort in ein -Müllarchiv kommen: -
      -
    • Diskriminierender oder menschenverachtender Inhalt -
    • Offensichtlicher Spam -
    -
- -

-Was mit Eurem Beitrag passiert, lest Ihr am besten -unter Grundsätze und Moderation nach. Hier eine kurze Zusammenfassung: -

- -Alle Beiträge werden sofort unzensiert veröffentlicht.
-Allerdings erscheinen sie dann nicht gleich auf der -Startseite, sondern auf der Open-Posting-Seite -"alle Beiträge".
-Dort werden sie von Moderationskollektiven -gelesen. Entsprechen sie den indymedia-Grundsätzen, -erscheinen sie im Newswire der Startseite. -In die Mittelspalte nehmen wir in der Regel Beiträge, -die einen guten Überblick über ein aktuelles Thema geben. -Diese Artikel werden von uns (möglichs in Absprache mit der AutorIn) auch leicht redigiert: -Wir korrigieren Rechtschreibfehler, versuchen unverständliche -Stellen zu glätten und setzen Links hinein.
-Verlautbarungen, -Texte, die keinen politischen oder sozialen Inhalt haben, -reine Diskussionsbeiträge und Termine bleiben im Open Posting. -Texte mit menschenverachtendem Inhalt oder Spam kommen ins -Müllarchiv. Damit sind sie nicht mehr einsehbar. Wer -sie trotzdem lesen will, kann sie per e-mail anfordern. -Diese Moderationskriterien sind Resultat eines weltweiten -Diskussionsprozesses über indymedia und werden ständig neu -diskutiert und überarbeitet. -

- -Urheberrecht: Unserer Auffassung nach sollten -Beiträge, die an diese Site geschickt werden, frei zur -nicht-kommerziellen Wiederverwertung sein. Wenn Du -nicht möchtest, dass das für Deinen Beitrag zutrifft, -dann nenne Deine Konditionen in der Zusammenfassung. -

- -
Anzahl der Medien
- (wenn Du mehr als eine Datei hochladen willst, bitte hier die Anzahl eintragen und den Knopf drücken, bevor Du weitere Felder ausfüllst.)
  -
-
Veröffentlichungsformular
-
- Gib Deinem Beitrag einen Titel:
- (Bitte wähle einen möglichst klaren, aussagekräftigen Titel.) -
-
(muss ausgefüllt werden)
-
- Thema Deines Beitrags:
- (Mehrfachwahl ist möglich. Bitte dazu die [Strg]- bzw [Ctrl]-Taste benutzen) -
- -   (optional) -
- AutorIn des Beitrags: - -
(muss ausgefüllt werden)
-
- Eine kurze Zusammenfassung des Beitrags:
- (Sie soll den LeserInnen schnell vermitteln, worum es in Deinem Beitrag geht. - Falls Du den ersten Absatz Deines Artikels dazu wählst, achte bitte darauf ihn im Haupttextfeld weiter - unten nicht nochmal einzusetzen.) -
- -
(nicht mehr als 5 Zeilen)
-
- - Die Kontaktinformationen sind optional, aber ermöglichen, dass die IndymedialeserInnen (z.B. auch JournalistInnen) Dich bezüglich Deines Beitrages, z.B. für Rückfragen, erreichen können, was auch eine Weiterverwertung Deines Beitrages an anderen Stellen ermöglicht. - -
- Deine eMail-Adresse: - -
- (optional) -
- Eine Web-Adresse zum Artikel: - -
- (optional) -
- Deine Adresse: - -
- (optional) -
- Deine Telefon-Nr.: - -
- (optional) -
 
- Die Sprache deines Beitrages: - - -   (optional) -
- Dein Artikel:
- (Der Haupttext Deines Beitrages) -
-   -

-
- Medien: - - Hier kannst Du eine oder mehrere Bild-, Audio- oder Videodateien zu Deinem Artikel hochladen.
- (Dazu wählst Du mit "Durchsuchen.." die entsprechende Datei auf Deiner Festplatte aus.) -
Media ${m} - -
(optional) -
- Medienunterschrift ${m}: - -
- (optional) -
 
- - - Bitte drücke den Verschicken-Knopf nur einmal!
- In wenigen Minuten erscheind Dein Beitrag dann auf der - - "Open Posting" - Seite.   - Das ist nicht die Startseite.
- Manchmal kann es aber aufgrund technischer Probleme - etwas dauern bis er erscheint. -
- Die Moderationskriterien kannst Du - hier nachlesen
- -
- - - - -
- - - - - diff --git a/templates-dist/de/open/posting_done.template b/templates-dist/de/open/posting_done.template deleted file mode 100755 index 61873d2b..00000000 --- a/templates-dist/de/open/posting_done.template +++ /dev/null @@ -1,38 +0,0 @@ - - - - - indymedia.de | open posting - - - - - - - - - - - - - - - -
- Hurra, Du hast Deinen Artikel abgeschickt!
- - -
-Dein Artikel landet in einigen Minuten auf der - -"Open Posting" - Seite. Das ist nicht die Startseite. -Die Moderationskriterien kannst Du -hier lesen

-
Gedulde Dich einen Moment! Es lohnt sich!


- -
-
- >> Zurück -
- - diff --git a/templates-dist/de/open/posting_dupe.template b/templates-dist/de/open/posting_dupe.template deleted file mode 100755 index df851327..00000000 --- a/templates-dist/de/open/posting_dupe.template +++ /dev/null @@ -1,44 +0,0 @@ - - - - - indymedia.de | open posting - Posting-Duplikat - - - - - - - - - - - - - -
- - Immer ruhig mit den jungen Pferden! - -
- -
- Du hast vermutlich den Reload-Button - benutzt oder versucht, Deinen Artikel noch einmal zu posten. Das ist aber - unnötig.
- Die Tatsache, dass Du diesen Text liest, bedeutet, dass Dein - Posting schon angekommen ist. Er wird gleich auf der Seite auftauchen. Versprochen. -

- Technischer Hintergrund ist, daß de.indy über einen - Proxy-Server läuft und da dauert es schon mal ein paar Minuten, bis ein neues Posting erscheint. -
-
Keine Panik

-
-
-
- >> Zurück -
- - - diff --git a/templates-dist/de/open/posting_en.template b/templates-dist/de/open/posting_en.template deleted file mode 100755 index f23b9318..00000000 --- a/templates-dist/de/open/posting_en.template +++ /dev/null @@ -1,345 +0,0 @@ - - - indymedia.de | open posting - - - - - - - - - -
- - -publish your article
-

- -

-
- -Posting a piece on indymedia's newswire -
-

- -

-

- - -

- -

-The Independent Media Center is a collectively -run media outlet for the creation of radical, accurate, and passionate -tellings of the truth. We work out of a love and inspiration for people -to who continue to work for a better world, despite corporate media's -distortions and unwillingness to cover the efforts to free humanity. - -

- -Indymedia is a democratic newswire. We want to see and hear the real -stories, news, and opinions from around the world. While we struggle to -maintain the news wire as a completely open fourm we do monitor it -and remove posts.
-You can see the decisons we have made by viewing the -hidden articles page. In the -overwhelming number of cases, stories have been removed for the following -reasons: being comments, not news, duplicate posts, -obviously false or -libelous posts, or inappropriate content. -
-We are working on the technology -to make this process more transparent, so that you can see when such decisions -have been made, and why. -In future, we want our audience to be part of this process too. For the meantime, -you can check out stories that we have choosen to remove by looking at the -hidden articles page. -

- -
- -Please use this form to contribute new stories and ideas. We think -comments belong with the story being discussed. So to have your say in response -to a story on the site, please use the add your comments link at the -bottom of the stories. - -

- -We think stories contributed to this site should be free for non-profit re-use. -Copy left is an idea central to indymedia. For more information, check out -www.opencontent.org. If you want to change that for your story, please give your conditions in the -summary. -

-You can use this form to publish your text article, audio segment, video -footage, or picture. Please focus on activism issues and events. We (the -people organising this site) may rearrange the display of submitted stories. - -

- -After stories have been published, they can be edited, linked or even deleted -by the collective running this site, using the story administration page (password required). -
- - -

- - -Remember: - -

    - -
  • please post news items only, leave your comments for discussion -areas - -
  • please try to post just one copy of your story - please only press -the publish button once. - -
- - -

- - - - - -

-

- - -
- - - - -
- -
Publishing Form
-
-
- Step 1:

- Give your piece a title
- -

- - Author or producer of the piece you're posting
- -
-

- the summary (no more than 6 lines - include the - duration of the piece if you are publishing audio or video)
- -

- - - - - -
- including contact details below is optional but will mean our - audience (including journalists) can contact you about your story, - including for possible re-use in other places - -
- - - - - - -
- email optional - - phone optional -
- - - - - -
- -

- - - - -
- address optional -
- - -
-

- - language
-the language of your submission
-
- - - -

- web address optional This provides a link to your website.
-
- -

- - -

- -

- - - - - - - - - -
- - - Step 2:

- - Text that you would like to appear with your story. - If you are posting a text only piece, you can type or paste your story here.
- - the article (if you enter HTML code, select "html format" from the type of upload drop down box below)
- - -
-
- -

- - - - - - - - - - -
- - - Step 3: - - - multimedia stories
- -Send in this file - (limit 100 megs): -

- - - - Audio/Video/Images: You need to - select the proper type for the content you are uploading. If you - are only uploading text, leave the setting on plain text. You - can upload html tags in a story if you wish.

- - Type of upload: - -

- - - -

- - - - -

Press the publish button only once. Uploading files can take a - long time and the confirmation page won't be shown until after the file has - finished uploading. - -

-

- - -

- -

- -
- - - -
-
- - -
- - - - diff --git a/templates-dist/en/open/comment.template b/templates-dist/en/open/comment.template deleted file mode 100755 index 4d8e79d0..00000000 --- a/templates-dist/en/open/comment.template +++ /dev/null @@ -1,119 +0,0 @@ - -indymedia.de | comment.commit - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Adding a coment to an article -
-

- - Just some hints (introduction)on how to write comments for - Indymedia. here - -

- -
- Comment-form -
- titel of comment - - (has to be filled out) -
- your name: - - (muss ausgefüllt werden) -
- your eMail - - (optional) -
- your Web Adress: - - (optional) -
- your Telefon-Nr.: - - (optional) -
- your Adress: - - (optional) -
- language of your comment - - - (optional) -
- your comment - -   -

-
  - -
-  
- -
-
-  
- -
-
- -
- - - diff --git a/templates-dist/en/open/comment.template.en b/templates-dist/en/open/comment.template.en deleted file mode 100755 index 99eae4e9..00000000 --- a/templates-dist/en/open/comment.template.en +++ /dev/null @@ -1,87 +0,0 @@ - -indymedia.de | comment.commit - - - -
- -adding a comment
- - -

Guidelines for commenting on news articles

-Thanks for contributing to the dynamic alternative news on active! -

-Add any responses to news articles that you feel are appropriate to -www.indy's goals eg: social activitism, local communities, ecology and development. -

-The response article can be in any type of format you want, from traditional academic discourse, to subjective personal rants. -

-Please keep it on topic and concise. -

-

* = required field -

- -

- -
- - -
- title for your comment (*)
- -

- your name (*)
-
-

- - - - - -
- email (optional) - - web address (begin with http://) (optional) -
- - - -
- -

- - - - -
- phone (optional) - - address (optional) -
- - - -
- - language
-the language of the submission
-
-
-

- your comment*
- -

-
- - - -
- -
-
-
- - diff --git a/templates-dist/en/open/comment_done.template b/templates-dist/en/open/comment_done.template deleted file mode 100755 index fc0a7a14..00000000 --- a/templates-dist/en/open/comment_done.template +++ /dev/null @@ -1,35 +0,0 @@ - - - - - indymedia.de | open posting - - - - - - - - - - - - - -
- Your comment is on the way to the Website! -
- -
- Er landet direkt in einem intelligenten Filter - und wenn er keine rassistischen, sexistischen oder faschistischen Inhalte oder Werbung enthält, wird er in Kürze auf http://www.germany.indymedia.org/newswire/ auftauchen.
-
-
Gedulde Dich einen Moment - Es lohnt sich!

-
-
-
- >> Zurück zum kommentierten Artikel -
- - - diff --git a/templates-dist/en/open/comment_dupe.template b/templates-dist/en/open/comment_dupe.template deleted file mode 100755 index 31dcb0b4..00000000 --- a/templates-dist/en/open/comment_dupe.template +++ /dev/null @@ -1,40 +0,0 @@ - - - - - indymedia.de | open posting - comment dupe - - - - - - - - - - - - - -
- - Keep calm. Things will happen soon. - -
- -
- You probably clicked on the reload button or submitted - your comment a second time. The fact that you can read - this text means that your comment has been recieved and - will soon be included in the article page. -
-
Don't panic

-
-
-
- >> Back to the commented article -
- - - diff --git a/templates-dist/en/open/minimal_posting.template b/templates-dist/en/open/minimal_posting.template deleted file mode 100755 index 3e3333ca..00000000 --- a/templates-dist/en/open/minimal_posting.template +++ /dev/null @@ -1,77 +0,0 @@ - - -this is a minimal template need to successfully publish a story. the -current en/open/posting.template is out of date to the point of being -broken. this works. though it could use a little design help :) - - - - -
- How many media items: -   - -
- - - - -
- -Title: -

- - - - -Topic: - -

-Author: -

-Summary: -

-Your Email: -

-Your Web Address: - -

-Address: -

-Phone #: -

- -Language: - -

-Your Article: - -

- - -Media Uploads: - - File ${m} - -
- Title ${m}: -
-

-
- - diff --git a/templates-dist/en/open/posting.template b/templates-dist/en/open/posting.template deleted file mode 100755 index 47489d6e..00000000 --- a/templates-dist/en/open/posting.template +++ /dev/null @@ -1,205 +0,0 @@ - - - - indymedia.de | open posting - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Jump dirctly to the form. -
-
-
- Publish your article !

-
-

Short introduction to open-posting in germany.indymedia

- -

Short text about IMC and the idea of open-publishing too lazy to translate all of this ;-) ----Das Independent Media Center ist ein kollektiv geführtes Mediennetzwerk zur Produktion von radikalen, passionierten und durchaus auch subjektiven, persönlichen Stellungnahmen verschiedenster Menschen 'auf der Strasse', z.B. vor, während oder nach Kampagnen, polit. Ereignissen oder aus der alltäglichen Lebenswelt. Wir arbeiten aus Liebe und aus Respekt gegenüber Menschen, die sich für eine bessere, lebenswertere Welt engagieren, und deren Arbeit von den Medienkonzernen nicht oder nur verzerrt dargestellt wird.

- -

Indymedia ist ein demokratischer Nachrichtenkanal. Wir wollen Beiträge, Artikel, Meinungen und Infos aus der ganzen Welt veröffentlichen. Alle Beiträge werden sofort unzensiert veröffentlicht, sofern sie nicht in die im Mission Statement genannten Kategorien fallen. Allerdings erscheinen sie dann nicht gleich auf der Startseite, sondern auf einer eigenen Open-Posting-Seite. Dort werden sie von wechselnden Moderationsteams gegengelesen und - anschliessend auf den newswire der Starttseite weitergeleitet.
- Um diesen Ausschlussprozess so transparent wie möglich zu gestalten, können alle nicht veröffentlichten Beiträge per e-mail angefordert weren.

- -

Bitte benutze dieses Formular nur, um neue Beiträge und Ideen beizusteuern. Kommentare gehören zu dem jeweils diskutierten Beitrag. Wenn Du etwas zu einem Beitrag zu sagen hast, dann benutze dafür bitte die Kommentar-Funktion am Ende jedes Beitrags.

- -

Unser Auffassung nach sollten Beiträge, die an diese Site geschickt werden, frei zur nicht-kommerziellen Wiederverwertung sein. Wenn Du nicht möchtest, dass das für Deinen Beitrag zutrifft, dann nenne Deine Konditionen in der Zusammenfassung.

- -

Du kannst dieses Formular benutzen, um Deinen Artikel, Deinen Audio-Beitrag, Deinen Video-Beitrag oder Deine Fotos zu veröffentlichen. Bitte beschränke Dich auf Beiträge zu politischem oder sozialem Aktivismus. Wir (die Menschen, die diese Site organisieren) behalten uns vor, die Präsentation der zugeschickten Beiträge zu verändern. Weitere Informationen findest Du im Mission Statement.

- -

Nach dem Beiträge veröffentlicht wurden, können sie vom Kollektiv, das diese Site betreut, editiert, verlinkt oder sogar gelöscht werden.

-
- -

Zur Erinnerung:

- -

    -
  • Bitte schicke nur News-Artikel und benutze zum Kommentieren von Artikeln das Kommentierformular, das Du unter jedem Artikel findest. -
  • Bitte schicke nur eine Kopie Deines Artikels - Bitte drücke den Verschicken-Knopf nur einmal. -
  • Veröffentliche nur selbst erstellte Beiträge. (Keine Agenturmeldungen, Zeitungsartikel oder ähnliches!). -
-
Number of Media Items  -
-
Publishing Form
-
- titel of your article: - -
(has to be filled out)
-
- author of this article: - -
(has to be filled out)
-
- A short abstract of your article: - - -
(not more than 5 lines)
-
- - Contact information is optional but enables other people to get in touch with you. - -
- your eMail-Adress: - - (optional) -
- your Web Adress: - - - (optional) -
- your Adress: - - (optional) -
- your Telefon-Nr.: - - (optional) -
- language of your article: - - - (optional) -
- your article:
- fill in the text of your article here -
-   -

-
- media: - - upload media-files (so far only jpg|gif|mp3|avi|qt|mpeg)
-
Media Item ${m} - (optional) -
- media sub-title ${m}: - - (optional) -
-
-  
- -
-
-  
-
- - - - - diff --git a/templates-dist/en/open/posting.template.en b/templates-dist/en/open/posting.template.en deleted file mode 100755 index 3f08bde4..00000000 --- a/templates-dist/en/open/posting.template.en +++ /dev/null @@ -1,345 +0,0 @@ - - - indymedia.de | open posting - - - - - - - - - -
- - -publish your article
-

- -

-
- -Posting a piece on indymedia's newswire -
-

- -

-

- - -

- -

-The Independent Media Center is a collectively -run media outlet for the creation of radical, accurate, and passionate -tellings of the truth. We work out of a love and inspiration for people -to who continue to work for a better world, despite corporate media's -distortions and unwillingness to cover the efforts to free humanity. - -

- -Indymedia is a democratic newswire. We want to see and hear the real -stories, news, and opinions from around the world. While we struggle to -maintain the news wire as a completely open fourm we do monitor it -and remove posts.
-You can see the decisons we have made by viewing the -hidden articles page. In the -overwhelming number of cases, stories have been removed for the following -reasons: being comments, not news, duplicate posts, -obviously false or -libelous posts, or inappropriate content. -
-We are working on the technology -to make this process more transparent, so that you can see when such decisions -have been made, and why. -In future, we want our audience to be part of this process too. For the meantime, -you can check out stories that we have choosen to remove by looking at the -hidden articles page. -

- -
- -Please use this form to contribute new stories and ideas. We think -comments belong with the story being discussed. So to have your say in response -to a story on the site, please use the add your comments link at the -bottom of the stories. - -

- -We think stories contributed to this site should be free for non-profit re-use. -Copy left is an idea central to indymedia. For more information, check out -www.opencontent.org. If you want to change that for your story, please give your conditions in the -summary. -

-You can use this form to publish your text article, audio segment, video -footage, or picture. Please focus on activism issues and events. We (the -people organising this site) may rearrange the display of submitted stories. - -

- -After stories have been published, they can be edited, linked or even deleted -by the collective running this site, using the story administration page (password required). -
- - -

- - -Remember: - -

    - -
  • please post news items only, leave your comments for discussion -areas - -
  • please try to post just one copy of your story - please only press -the publish button once. - -
- - -

- - - - - - -

- - -
- - - - -
- -
Publishing Form
-
-
- Step 1:

- Give your piece a title
- -

- - Author or producer of the piece you're posting
- -
-

- the summary (no more than 6 lines - include the - duration of the piece if you are publishing audio or video)
- -

- - - - - -
- including contact details below is optional but will mean our - audience (including journalists) can contact you about your story, - including for possible re-use in other places - -
- - - - - - -
- email optional - - phone optional -
- - - - - -
- -

- - - - -
- address optional -
- - -
-

- - language
-the language of your submission
-
- - - -

- web address optional This provides a link to your website.
-
- -

- - -

- -

- - - - - - - - - -
- - - Step 2:

- - Text that you would like to appear with your story. - If you are posting a text only piece, you can type or paste your story here.
- - the article (if you enter HTML code, select "html format" from the type of upload drop down box below)
- - -
-
- -

- - - - - - - - - - -
- - - Step 3: - - - multimedia stories
- -Send in this file - (limit 100 megs): -

- - - - Audio/Video/Images: You need to - select the proper type for the content you are uploading. If you - are only uploading text, leave the setting on plain text. You - can upload html tags in a story if you wish.

- - Type of upload: - -

- - - -

- - - - -

Press the publish button only once. Uploading files can take a - long time and the confirmation page won't be shown until after the file has - finished uploading. - -

-

- - -

- -

- - - - - -
-
- - -
- - - - diff --git a/templates-dist/en/open/posting_done.template b/templates-dist/en/open/posting_done.template deleted file mode 100755 index d58649a6..00000000 --- a/templates-dist/en/open/posting_done.template +++ /dev/null @@ -1,40 +0,0 @@ - - - - - indymedia.de | open posting - - - - -
- - - - - - - - -
Hurra, Du hast Deinen Artikel abgeschickt!
- - - - - - - -
- - -
-Dein Artikel landet jetzt direkt in einem intelligenten Filter. Wenn Dein Artikel keine rassistischen, sexistischen oder faschistischen Inhalte hatte, und auch nicht ganz ohne Inhalt, also leer war, dann wird er in Kürze auf http://www.germany.indymedia.org/newswire/ auftauchen.

-
Gedulde Dich einen Moment! Es lohnt sich!


- -
-
- >> Zurück -
-
- - \ No newline at end of file diff --git a/templates-dist/en/open/posting_dupe.template b/templates-dist/en/open/posting_dupe.template deleted file mode 100755 index 07e0b184..00000000 --- a/templates-dist/en/open/posting_dupe.template +++ /dev/null @@ -1,43 +0,0 @@ - - - - - indymedia.de | open posting - posting dupe - - - - - - - - - - - - - -
- - Keep calm. Things will happen soon. - -
- -
- You probably clicked on the reload button or submitted - your posting a second time. The fact that you can read - this text means that your posting has been recieved and - will soon be included in the article page. -
- The technical background is that de.indy is connected to - a proxy server that is not updated every minute. -
-
Don't panic

-
-
-
- >> Back -
- - - diff --git a/templates-dist/nl/open/comment.template b/templates-dist/nl/open/comment.template deleted file mode 100755 index 97deb6e9..00000000 --- a/templates-dist/nl/open/comment.template +++ /dev/null @@ -1,123 +0,0 @@ - -indymedia.nl | commentaar leveren - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Commentaar leveren op een bericht -
-

- - Een paar hints bij het leveren van commentaar op berichten: - Een goede discussie is leuker dan een scheldpartij. Lees het - oorspronkelijke bericht eerst nog eens: misschien heb je iets - over het hoofd gezien. Probeer je commentaar helder te houden. - Controleer je commentaar even op fouten voordat je op de - verstuurknop drukt. - -

- -
- Commentaarformulier -
- titel van het commentaar - - (moet ingevuld worden) -
- je naam: - - (moet ingevuld worden) -
- je e-mailadres - - (optioneel) -
- je web-adres: - - (optioneel) -
- je telefoonnummer: - - (optioneel) -
- je adres: - - (optioneel) -
- de taal van je keuze - - - (optional) -
- je commentaar - -   -

-
  - -
-  
- -
-
-  
- -
-
- -
- - - diff --git a/templates-dist/nl/open/comment_done.template b/templates-dist/nl/open/comment_done.template deleted file mode 100755 index 2bdfafd1..00000000 --- a/templates-dist/nl/open/comment_done.template +++ /dev/null @@ -1,35 +0,0 @@ - - - - - indymedia.de | open posting - - - - - - - - - - - - - -
- Je commentaar is onderweg naar Indymedia.nl! -
- -
- Het beland nu eerst in een intelligent filter - Tenzij je commentaar seksistische, fascistische of racistisch inhoud bevat, of helemaal geen inhoud, dus leeg is, dan verschijnt het zo op http://www.indymedia.nl.
-
-
Momentje - Je zult verbaasd staan!

-
-
-
- >> Terug naar het bericht -
- - - diff --git a/templates-dist/nl/open/comment_dupe.template b/templates-dist/nl/open/comment_dupe.template deleted file mode 100755 index c4fa6627..00000000 --- a/templates-dist/nl/open/comment_dupe.template +++ /dev/null @@ -1,41 +0,0 @@ - - - - - indymedia.nl | open posting - dubbel commentaar - - - - - - - - - - - - - -
- - Blijf kalm. Er gebeurt zo iets. - -
- -
- Je hebt waarschijnlijk op de reload knop gedrukt of - geprobeerd je commentaar voor de tweede keer te versturen. - Dat je deze tekst te lezen krijgt betekent dat je - commentaar ontvangen is en zodadelijk verschijnt onder het - bericht waarop je hebt gereageerd. -
-
Don't panic

-
-
-
- >> Terug naar het bewuste bericht -
- - - diff --git a/templates-dist/nl/open/posting.template b/templates-dist/nl/open/posting.template deleted file mode 100755 index fde46784..00000000 --- a/templates-dist/nl/open/posting.template +++ /dev/null @@ -1,252 +0,0 @@ - - - - indymedia.nl | open posting - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Meteen naar het formulier. -
-
-
- Publiceer je bericht!

-
-

Een korte introductie bij het plaatsen van berichten op Indymedia.nl

- -

De Nederlandse afdeling van het Indymedia Centrum is - een collectief.Het is een medianetwerk dat radicale, - gepassioneerde en zeker ook subjectieve, persoonlijke - standpunten van allerlei verschillende mensen 'van de - straat' publiceert. Daarbij horen ook berichten over - campagnes, politieke gebeurtenissen en de dagelijkse - leefwereld. We doen dit uit respect en liefde voor mensen - die zich inzetten voor een betere, mooiere wereld, - waarvan het werk niet of verdraaid weergegeven wordt in - de reguliere media.

- -

Indymedia is een democratische nieuwsbron. We willen verhalen, - berichten, meningen en informatie uit de hele wereld openbaar maken. - Alle berichten worden direct en ongecensureerd gepubliceerd, voor - zover ze niet in de hieronder uitgesloten categorien - vallen. De berichten verschijnen echter niet meteen midden op de voorpagina, - maar wel op de 'newswire' (de rechterkolom van de pagina). - Door wisselende teams van moderatoren worden berichten dan eventueel - op midden de voorpagina gezet. - Om helderheid te bevorderen is het mogelijk de niet gepubliceerde - bijdragen per e-mail op te vragen.

- -

Gebruik dit formulier alsjeblieft alleen voor nieuwe bijdragen en - ideeën. Reacties horen onder het bijbehorende bericht: gebruik - daarvoor de "reageer"-knop onder het desbetreffende bericht.

- -

Volgens ons zou een bijdrage door niet-commerciele initiatieven weer - hergebruikt moeten kunnen worden. Mocht je dit niet willen, laat dan - weten dat dit niet mag, of anders onder welke condities wel of niet - (bijvoorbeeld: "gebruik door derden niet toegestaan" in de - samenvatting)

- -

Je kunt dit formulier gebruiken om je tekst, foto's, illustratie, - audio, video, etcetera openbaar te maken.Beperk je tot bijdragen over - sociaal en/of politiek activisme. Wij (de mensen achter deze website) - mogen ten aller tijde ingezonden bijdragen veranderen. Meer daarover - vind je hieronder.

- -

Nadat een bijdrage verschenen is, kan deze door het collectief naar voren - geschoven, van extra informatie voorzien of zelfs verwijderd worden.

-
- -

Voor alle duidelijkheid:

- -

    -
  • Stuur alleen nieuwe nieuwsberichten, gebruik voor het reageren op bestaande bijdragen de 'reageer'-knop onderaan desbetreffend bericht. -
  • Stuur alleen een kopie van je bijdrage. Druk alsjeblieft maar 1 (een!) keer op de send-knop. -
  • Stuur alleen zelfgeschreven/-gemaakte bijdragen. (Geen kopieen van krantenberichten, persberichten, of dergelijk!). -
  • Wij verwijderen gejatte software en mp3-tjes en dergelijke zonder discussie -
  • Puur fascistische bijdragen, niet functionele porno, spam en ander niet bediscussieerbare bijdrage worden verwijderd. -
  • Voor straf verplaatst naar de Indymedia data-kerker worden: racistische bijdragen waarvan de auteur niet in discussie wil gaan, ellenlange teksten vanwege de tekst zelf (zoals nieuwsbrieven van politieke partijen) en bloot zonder doel. -
-
Het aantal media files dat meestuurt  -
-
publicatieformulier
-
- titel van je bijdrage: - -
(moet worden ingevuld)
-
- Het thema van jouw bijdrage - - - (optioneel) -
- auteur van deze bijdrage: - -
(moet worden ingevuld)
-
- Een korte omschrijving van je bijdrage: - - -
(niet meer dan 5 regels)
-
- - Contact informatie is niet verplicht, maar maakt het mogelijk voor andere mensen je te bereiken, mocht je dat willen, en dan is dat handig, nietwaar? - -
- je e-mailadres: - - (optioneel) -
- je webadres: - - - (optioneel) -
- je adres: - - (optioneel) -
- je telefoonnummer: - - (optioneel) -
- de taal van je bijdrage: - - - (optioneel) -
- je bijdrage:
- vul de text van je bijdrage hier in -
-   -

-
- media: - - upload media-files (tot nog toe alleen jpg|gif|mp3|avi|qt|mpeg|pdf|ra(realaudio)|rm(realvideo))
-
Media Item ${m} - (optioneel) -
- media sub-title ${m}: - - (optioneel) -
-
-  
- -
-
-  
-
- - - - - diff --git a/templates-dist/nl/open/posting_done.template b/templates-dist/nl/open/posting_done.template deleted file mode 100755 index 3fc66a0e..00000000 --- a/templates-dist/nl/open/posting_done.template +++ /dev/null @@ -1,40 +0,0 @@ - - - - - indymedia.nl | open posting - - - - -
- - - - - - - - -
Hoera, je bericht is verstuurd!
- - - - - - - -
- - -
-Je bericht belandt nu eerst in een slim filter. Tenzij je bericht seksistische, fascistische of racistisch inhoud bevat, of helemaal geen inhoud heeft, dan verschijnt het zo op http://www.indymedia.nl.

-
Heb een momentje geduld! Het is de moeite waard!


- -
-
- >> terug -
-
- - \ No newline at end of file diff --git a/templates-dist/nl/open/posting_dupe.template b/templates-dist/nl/open/posting_dupe.template deleted file mode 100755 index 05286524..00000000 --- a/templates-dist/nl/open/posting_dupe.template +++ /dev/null @@ -1,53 +0,0 @@ - - - - - indymedia.nl | open posting - posting dupe - - - - - - - - - - - - - -
- - Blijf rustig. Er gebeurt al iets. -
- Keep calm. Things will happen soon. -
-
- -
- Je hebt waarschijnlijk op de reload-knop gedrukt of - je hebt je bericht voor de tweede keer verstuurd. Dat - je deze tekst te lezen krijgt betekent dat je bericht - is ontvangen en zo verschijnt op de berichtenpagina. -
- You probably clicked on the reload button or submitted - your posting a second time. The fact that you can read - this text means that your posting has been recieved and - will soon be included in the article page. -
- De proxy server doet er soms een paar minuten over om de - inhoud te verversen. -
- The technical background is that indymedia.nl is connected to - a proxy server that is not updated every minute. -
-
Don't panic

-
-
-
- >> Back -
- - - diff --git a/templates-dist/open/comment_en.template b/templates-dist/open/comment_en.template deleted file mode 100755 index f5713b60..00000000 --- a/templates-dist/open/comment_en.template +++ /dev/null @@ -1,87 +0,0 @@ - -indymedia.de | comment.commit - - - -
- -adding a comment
- - -

Guidelines for commenting on news articles

-Thanks for contributing to the dynamic alternative news on active! -

-Add any responses to news articles that you feel are appropriate to -www.indy's goals eg: social activitism, local communities, ecology and development. -

-The response article can be in any type of format you want, from traditional academic discourse, to subjective personal rants. -

-Please keep it on topic and concise. -

-

* = required field -

- - - -

- - -
- Titel des Kommentars (*)
- -

- Dein Name (*)
-
-

- - - - - -
- email (optional) - - web address (begin with http://) (optional) -
- - - -
- -

- - - - -
- phone (optional) - - address (optional) -
- - - -
- - language
-the language of the submission
-
-
-

- your comment*
- -

-
- - - -
- -
-
-
- - \ No newline at end of file diff --git a/templates-dist/open/posting.template b/templates-dist/open/posting.template index 6bd4b960..eb33b805 100755 --- a/templates-dist/open/posting.template +++ b/templates-dist/open/posting.template @@ -71,7 +71,7 @@ ${lang("open.posting.topic.info")} - >${t.value} diff --git a/templates-dist/open/posting_en.template b/templates-dist/open/posting_en.template deleted file mode 100755 index 1fd1dffb..00000000 --- a/templates-dist/open/posting_en.template +++ /dev/null @@ -1,345 +0,0 @@ - - - indymedia.de | open posting - - - - - - - - - -
- - -publish your article
-

- -

-
- -Posting a piece on indymedia's newswire -
-

- -

-

- - -

- -

-The Independent Media Center is a collectively -run media outlet for the creation of radical, accurate, and passionate -tellings of the truth. We work out of a love and inspiration for people -to who continue to work for a better world, despite corporate media's -distortions and unwillingness to cover the efforts to free humanity. - -

- -Indymedia is a democratic newswire. We want to see and hear the real -stories, news, and opinions from around the world. While we struggle to -maintain the news wire as a completely open fourm we do monitor it -and remove posts.
-You can see the decisons we have made by viewing the -hidden articles page. In the -overwhelming number of cases, stories have been removed for the following -reasons: being comments, not news, duplicate posts, -obviously false or -libelous posts, or inappropriate content. -
-We are working on the technology -to make this process more transparent, so that you can see when such decisions -have been made, and why. -In future, we want our audience to be part of this process too. For the meantime, -you can check out stories that we have choosen to remove by looking at the -hidden articles page. -

- -
- -Please use this form to contribute new stories and ideas. We think -comments belong with the story being discussed. So to have your say in response -to a story on the site, please use the add your comments link at the -bottom of the stories. - -

- -We think stories contributed to this site should be free for non-profit re-use. -Copy left is an idea central to indymedia. For more information, check out -www.opencontent.org. If you want to change that for your story, please give your conditions in the -summary. -

-You can use this form to publish your text article, audio segment, video -footage, or picture. Please focus on activism issues and events. We (the -people organising this site) may rearrange the display of submitted stories. - -

- -After stories have been published, they can be edited, linked or even deleted -by the collective running this site, using the story administration page (password required). -
- - -

- - -Remember: - -

    - -
  • please post news items only, leave your comments for discussion -areas - -
  • please try to post just one copy of your story - please only press -the publish button once. - -
- - -

- - - - - -

-

- - -
- - - - -
- -
Publishing Form
-
-
- Step 1:

- Give your piece a title
- -

- - Author or producer of the piece you're posting
- -
-

- the summary (no more than 6 lines - include the - duration of the piece if you are publishing audio or video)
- -

- - - - - -
- including contact details below is optional but will mean our - audience (including journalists) can contact you about your story, - including for possible re-use in other places - -
- - - - - - -
- email optional - - phone optional -
- - - - - -
- -

- - - - -
- address optional -
- - -
-

- - language
-the language of your submission
-
- - - -

- web address optional This provides a link to your website.
-
- -

- - -

- -

- - - - - - - - - -
- - - Step 2:

- - Text that you would like to appear with your story. - If you are posting a text only piece, you can type or paste your story here.
- - the article (if you enter HTML code, select "html format" from the type of upload drop down box below)
- - -
-
- -

- - - - - - - - - - -
- - - Step 3: - - - multimedia stories
- -Send in this file - (limit 100 megs): -

- - - - Audio/Video/Images: You need to - select the proper type for the content you are uploading. If you - are only uploading text, leave the setting on plain text. You - can upload html tags in a story if you wish.

- - Type of upload: - -

- - - -

- - - - -

Press the publish button only once. Uploading files can take a - long time and the confirmation page won't be shown until after the file has - finished uploading. - -

-

- - -

- -

- -
- - - -
-
- - -
- - - - diff --git a/templates-dist/producer/content.template b/templates-dist/producer/content.template index 6b6fcb55..a91c42e1 100755 --- a/templates-dist/producer/content.template +++ b/templates-dist/producer/content.template @@ -1,6 +1,6 @@ -${lang("producer.content.htmltitle")}${content.title} +${lang("producer.content.htmltitle")}${data.content.title} @@ -19,22 +19,22 @@ - - +
+
- + - - +
- +
+
- - +
- +
+
@@ -44,25 +44,24 @@
- - + - + - - +
+
- +
+
- +
- diff --git a/templates-dist/producer/producers.xml b/templates-dist/producer/producers.xml new file mode 100755 index 00000000..7ea479d6 --- /dev/null +++ b/templates-dist/producer/producers.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.11.0
@@ -77,16 +76,22 @@

${data.content.title}

${data.content.creator}, ${data.content.webdb_create_formatted}

${data.content.description_parsed} + + - - -

- - - ${audio["title"]} - ${media["descr"]} -

+ + + +

+ + + + + ${audio["title"]} - ${media["descr"]} + +

+
-

@@ -105,19 +110,20 @@

- - + +

${image[
${image["title"]}

-
+ +

${data.content.content_data_parsed}

- + + @@ -153,24 +159,24 @@ - + + + + - +
@@ -139,7 +145,7 @@
${lang("producer.content.comment")} -

${c.description}
${c.creator} - - + +
${lang("producer.content.email")}: ${c.email} -
- + +
${lang("producer.content.homepage")}: ${c.main_url} -
-
-