From d35bc152ef22381e47ff6be29418f22c116aed06 Mon Sep 17 00:00:00 2001 From: Eike Cochu <eike@cochu.com> Date: Mon, 11 Jan 2016 22:27:23 +0100 Subject: [PATCH] added topic browsing and serializer added topic serializer and deserializer added topic browsing site added article and topic computed property _name for safe naming --- ma-impl.sublime-workspace | 168 ++++-------------- .../{TopicDefinition.java => Topic.java} | 2 +- .../rest/provider/ObjectMapperProvider.java | 5 + .../de/vipra/rest/resource/TopicResource.java | 14 +- .../de/vipra/rest/serializer/JsonHelper.java | 8 + .../rest/serializer/TopicDeserializer.java | 44 +++++ .../rest/serializer/TopicSerializer.java | 35 ++++ .../de/vipra/rest/service/TopicService.java | 12 +- vipra-ui/app/components/article-list.js | 15 -- vipra-ui/app/components/articles-list.js | 15 ++ vipra-ui/app/components/topic-link.js | 15 ++ vipra-ui/app/components/topics-list.js | 15 ++ vipra-ui/app/models/article.js | 8 +- vipra-ui/app/models/topic.js | 8 +- vipra-ui/app/routes/topics/list.js | 2 +- vipra-ui/app/templates/articles.hbs | 1 + vipra-ui/app/templates/articles/list.hbs | 4 +- vipra-ui/app/templates/articles/show.hbs | 6 + .../{article-list.hbs => articles-list.hbs} | 2 +- .../app/templates/components/topic-link.hbs | 1 + .../app/templates/components/topics-list.hbs | 5 + vipra-ui/app/templates/topics.hbs | 7 +- vipra-ui/app/templates/topics/list.hbs | 6 +- ...article-list-test.js => item-list-test.js} | 0 .../integration/components/topic-link-test.js | 25 +++ 25 files changed, 252 insertions(+), 171 deletions(-) rename vipra-rest/src/main/java/de/vipra/rest/model/{TopicDefinition.java => Topic.java} (87%) create mode 100644 vipra-rest/src/main/java/de/vipra/rest/serializer/TopicDeserializer.java create mode 100644 vipra-rest/src/main/java/de/vipra/rest/serializer/TopicSerializer.java delete mode 100644 vipra-ui/app/components/article-list.js create mode 100644 vipra-ui/app/components/articles-list.js create mode 100644 vipra-ui/app/components/topic-link.js create mode 100644 vipra-ui/app/components/topics-list.js rename vipra-ui/app/templates/components/{article-list.hbs => articles-list.hbs} (76%) create mode 100644 vipra-ui/app/templates/components/topic-link.hbs create mode 100644 vipra-ui/app/templates/components/topics-list.hbs rename vipra-ui/tests/integration/components/{article-list-test.js => item-list-test.js} (100%) create mode 100644 vipra-ui/tests/integration/components/topic-link-test.js diff --git a/ma-impl.sublime-workspace b/ma-impl.sublime-workspace index a8cbaab1..eac89659 100644 --- a/ma-impl.sublime-workspace +++ b/ma-impl.sublime-workspace @@ -275,30 +275,6 @@ }, "buffers": [ - { - "file": "vipra-ui/app/routes/articles/show.js", - "settings": - { - "buffer_size": 643, - "line_ending": "Unix" - } - }, - { - "file": "vipra-ui/app/templates/articles/show.hbs", - "settings": - { - "buffer_size": 540, - "line_ending": "Unix" - } - }, - { - "file": "vipra-ui/app/models/article.js", - "settings": - { - "buffer_size": 168, - "line_ending": "Unix" - } - } ], "build_system": "", "build_system_choices": @@ -480,22 +456,50 @@ "/home/eike/repos/master/ma-impl", "/home/eike/repos/master/ma-impl/vipra-ui", "/home/eike/repos/master/ma-impl/vipra-ui/app", + "/home/eike/repos/master/ma-impl/vipra-ui/app/components", "/home/eike/repos/master/ma-impl/vipra-ui/app/models", "/home/eike/repos/master/ma-impl/vipra-ui/app/routes", "/home/eike/repos/master/ma-impl/vipra-ui/app/routes/articles", "/home/eike/repos/master/ma-impl/vipra-ui/app/routes/topics", "/home/eike/repos/master/ma-impl/vipra-ui/app/templates", - "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/articles" + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/articles", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/components", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/topics", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/helpers", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/integration", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/integration/components", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/unit", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/unit/adapters", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/unit/controllers", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/unit/helpers", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/unit/models", + "/home/eike/repos/master/ma-impl/vipra-ui/tests/unit/routes" ], "file_history": [ - "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/articles/show.hbs", - "/home/eike/repos/master/ma-impl/vipra-ui/app/routes/topics/list.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/articles.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/topics.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/components/topics-list.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/components/articles-list.js", "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/articles/list.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/components/articles-list.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/topics/list.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/components/topics-list.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/models/article.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/models/topic.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/routes/topics/list.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/components/filter-list.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/articles/show.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/components/topic-link.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/helpers/topic-numi.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/components/topic-link.hbs", + "/home/eike/repos/master/ma-impl/vipra-ui/app/routes/articles/show.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/helpers/topicname.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/helpers/topic-name.js", + "/home/eike/repos/master/ma-impl/vipra-ui/app/styles/app.css", "/home/eike/repos/master/ma-impl/vm/bootstrap.sh", "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/topics/show.hbs", "/home/eike/repos/master/ma-impl/vipra-ui/app/router.js", - "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/topics/list.hbs", "/home/eike/repos/master/ma-impl/vipra-ui/app/templates/index.hbs", "/home/eike/.config/sublime-text-3/Packages/User/Preferences.sublime-settings", "/home/eike/.config/sublime-text-3/Packages/Default/Preferences.sublime-settings", @@ -600,23 +604,7 @@ "/home/eike/Repositories/niels_website/web/src/intergeo-2015.html", "/home/eike/Repositories/niels_website/web/src/less/main.less", "/home/eike/Downloads/intellij/idea-IU-141.2735.5/Install-Linux-tar.txt", - "/home/eike/Downloads/JetBrains.IntelliJ.IDEA.Ultimate.v14.1.5.141.2735.5.Linux.Incl.KeyMaker-DVT/dvt.nfo", - "/home/eike/Repositories/niels_website/web/src/sftp-config.json", - "/home/eike/Repositories/niels_website/web/src/impressum.html", - "/home/eike/Repositories/niels_website/web/gulpfile.js", - "/home/eike/.config/sublime-text-3/Packages/SFTP/SFTP.sublime-settings", - "/home/eike/.config/sublime-text-3/Packages/User/SFTP.sublime-settings", - "/home/eike/.config/sublime-text-3/Packages/SFTP/SFTP.py", - "/home/eike/.config/sublime-text-3/Packages/SFTP/license.txt", - "/home/eike/Repositories/niels_website/web/dist/sftp-config.json", - "/home/eike/Repositories/fu/ss15/ki/exercise-09/task01a.pl", - "/home/eike/Repositories/fu/ss15/ki/exercise-07/task3b.pl", - "/home/eike/Repositories/fu/ss15/ki/exercise-07/task3a.pl", - "/home/eike/Downloads/analyze.r", - "/home/eike/Repositories/fu/ss15/ebi/exercise-03/exercise-03.r", - "/home/eike/Repositories/fu/ss15/ebi/exercise-02/exercise-02.r", - "/home/eike/Downloads/Formularantworten150621.csv", - "/home/eike/Downloads/analyze.r.Rout" + "/home/eike/Downloads/JetBrains.IntelliJ.IDEA.Ultimate.v14.1.5.141.2735.5.Linux.Incl.KeyMaker-DVT/dvt.nfo" ], "find": { @@ -938,96 +926,8 @@ "groups": [ { - "selected": 2, "sheets": [ - { - "buffer": 0, - "file": "vipra-ui/app/routes/articles/show.js", - "semi_transient": false, - "settings": - { - "buffer_size": 643, - "regions": - { - }, - "selection": - [ - [ - 342, - 342 - ] - ], - "settings": - { - "open_with_edit": true, - "syntax": "Packages/JavaScript/JavaScript.tmLanguage", - "tab_size": 2, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 2, - "type": "text" - }, - { - "buffer": 1, - "file": "vipra-ui/app/templates/articles/show.hbs", - "semi_transient": false, - "settings": - { - "buffer_size": 540, - "regions": - { - }, - "selection": - [ - [ - 299, - 299 - ] - ], - "settings": - { - "syntax": "Packages/Handlebars/grammars/Handlebars.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 1, - "type": "text" - }, - { - "buffer": 2, - "file": "vipra-ui/app/models/article.js", - "semi_transient": false, - "settings": - { - "buffer_size": 168, - "regions": - { - }, - "selection": - [ - [ - 168, - 168 - ] - ], - "settings": - { - "syntax": "Packages/JavaScript/JavaScript.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 0, - "type": "text" - } ] } ], @@ -1194,7 +1094,7 @@ "show_open_files": true, "show_tabs": true, "side_bar_visible": true, - "side_bar_width": 247.0, + "side_bar_width": 254.0, "status_bar_visible": true, "template_settings": { diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/TopicDefinition.java b/vipra-rest/src/main/java/de/vipra/rest/model/Topic.java similarity index 87% rename from vipra-rest/src/main/java/de/vipra/rest/model/TopicDefinition.java rename to vipra-rest/src/main/java/de/vipra/rest/model/Topic.java index 0541f4a1..58856e7f 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/model/TopicDefinition.java +++ b/vipra-rest/src/main/java/de/vipra/rest/model/Topic.java @@ -4,7 +4,7 @@ import java.net.URI; import java.util.HashMap; import java.util.Map; -public class TopicDefinition extends de.vipra.util.model.Topic implements Linked { +public class Topic extends de.vipra.util.model.Topic implements Linked { private Map<String, String> links; diff --git a/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java b/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java index b748acc0..512a18ea 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java +++ b/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java @@ -14,8 +14,11 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import de.vipra.rest.model.Article; +import de.vipra.rest.model.Topic; import de.vipra.rest.serializer.ArticleDeserializer; import de.vipra.rest.serializer.ArticleSerializer; +import de.vipra.rest.serializer.TopicDeserializer; +import de.vipra.rest.serializer.TopicSerializer; import de.vipra.util.Constants; @Provider @@ -38,6 +41,8 @@ public class ObjectMapperProvider implements ContextResolver<ObjectMapper> { SimpleModule module = new SimpleModule(); module.addSerializer(Article.class, new ArticleSerializer()); module.addDeserializer(Article.class, new ArticleDeserializer()); + module.addSerializer(Topic.class, new TopicSerializer()); + module.addDeserializer(Topic.class, new TopicDeserializer()); final ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java index f2f94cb0..68b22246 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java +++ b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java @@ -20,7 +20,7 @@ import de.vipra.rest.APIMediaType; import de.vipra.rest.Messages; import de.vipra.rest.model.APIError; import de.vipra.rest.model.ResponseWrapper; -import de.vipra.rest.model.TopicDefinition; +import de.vipra.rest.model.Topic; import de.vipra.rest.service.TopicService; import de.vipra.util.Config; import de.vipra.util.Mongo; @@ -45,8 +45,8 @@ public class TopicResource { @Produces(APIMediaType.APPLICATION_JSONAPI) public Response getTopics(@QueryParam("skip") @DefaultValue("0") int skip, @QueryParam("limit") @DefaultValue("0") int limit) { - List<TopicDefinition> topics = service.getMultiple(uri.getAbsolutePath(), skip, limit, null); - ResponseWrapper<List<TopicDefinition>> res = new ResponseWrapper<>(topics); + List<Topic> topics = service.getMultiple(uri.getAbsolutePath(), skip, limit, null); + ResponseWrapper<List<Topic>> res = new ResponseWrapper<>(topics); res.addLink("self", uri.getAbsolutePath().toString()); return Response.ok().entity(res).build(); } @@ -56,13 +56,13 @@ public class TopicResource { @Consumes(APIMediaType.APPLICATION_JSONAPI) @Path("{id}") public Response getTopic(@PathParam("id") String id) { - ResponseWrapper<TopicDefinition> res = new ResponseWrapper<>(); + ResponseWrapper<Topic> res = new ResponseWrapper<>(); if (id == null || id.trim().length() == 0) { res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty", String.format(Messages.BAD_REQUEST, "id cannot be empty"))); return Response.status(Response.Status.BAD_REQUEST).entity(res).build(); } - TopicDefinition topic = service.getSingle(uri.getAbsolutePath(), id); + Topic topic = service.getSingle(uri.getAbsolutePath(), id); if (topic != null) { res.setData(topic); return Response.ok().entity(res).build(); @@ -77,8 +77,8 @@ public class TopicResource { @Consumes(APIMediaType.APPLICATION_JSONAPI) @Produces(APIMediaType.APPLICATION_JSONAPI) @Path("{id}") - public Response updateTopic(@PathParam("id") String id, TopicDefinition topic) { - ResponseWrapper<TopicDefinition> res = new ResponseWrapper<>(); + public Response updateTopic(@PathParam("id") String id, Topic topic) { + ResponseWrapper<Topic> res = new ResponseWrapper<>(); try { long updated = service.updateSingle(topic); int updatedInt = updated > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) updated; diff --git a/vipra-rest/src/main/java/de/vipra/rest/serializer/JsonHelper.java b/vipra-rest/src/main/java/de/vipra/rest/serializer/JsonHelper.java index a15ff8ae..ebda14f7 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/serializer/JsonHelper.java +++ b/vipra-rest/src/main/java/de/vipra/rest/serializer/JsonHelper.java @@ -46,6 +46,14 @@ public class JsonHelper { return getLong(node, name, 0L); } + public static int getInt(JsonNode node, String name, int defaultValue) { + return get(node, name, defaultValue, Integer.class); + } + + public static int getInt(JsonNode node, String name) { + return getInt(node, name, 0); + } + public static String dateToString(Date date) { DateFormat df = new SimpleDateFormat(Constants.DATETIME_FORMAT); return df.format(date); diff --git a/vipra-rest/src/main/java/de/vipra/rest/serializer/TopicDeserializer.java b/vipra-rest/src/main/java/de/vipra/rest/serializer/TopicDeserializer.java new file mode 100644 index 00000000..8fcf38b6 --- /dev/null +++ b/vipra-rest/src/main/java/de/vipra/rest/serializer/TopicDeserializer.java @@ -0,0 +1,44 @@ +package de.vipra.rest.serializer; + +import static de.vipra.rest.serializer.JsonHelper.getString; +import static de.vipra.rest.serializer.JsonHelper.getInt; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import de.vipra.rest.model.Topic; + +public class TopicDeserializer extends JsonDeserializer<Topic> { + + @Override + public Topic deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + Topic topic = null; + + JsonNode node = p.readValueAsTree(); + if (node != null) { + topic = new Topic(); + if (node.has("id")) + topic.setId(getString(node, "id")); + + if (node.has("attributes")) { + JsonNode attrs = node.get("attributes"); + if (attrs.has("name")) + topic.setName(getString(attrs, "name")); + if (attrs.has("index")) + topic.setIndex(getInt(attrs, "index")); + if (attrs.has("words")) { + JsonNode wordsNode = attrs.get("words"); + // TODO implement + } + } + } + + return topic; + } + +} diff --git a/vipra-rest/src/main/java/de/vipra/rest/serializer/TopicSerializer.java b/vipra-rest/src/main/java/de/vipra/rest/serializer/TopicSerializer.java new file mode 100644 index 00000000..38ef064d --- /dev/null +++ b/vipra-rest/src/main/java/de/vipra/rest/serializer/TopicSerializer.java @@ -0,0 +1,35 @@ +package de.vipra.rest.serializer; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import de.vipra.rest.model.Topic; + +public class TopicSerializer extends JsonSerializer<Topic> { + + @Override + public void serialize(Topic value, JsonGenerator gen, SerializerProvider serializer) + throws IOException, JsonProcessingException { + gen.writeStartObject(); + gen.writeStringField("id", value.getId()); + gen.writeStringField("type", "topic"); + + if (value.getLinks() != null) + gen.writeObjectField("links", value.getLinks()); + + gen.writeObjectFieldStart("attributes"); + gen.writeNumberField("index", value.getIndex()); + if (value.getName() != null) + gen.writeStringField("name", value.getName()); + if (value.getWords() != null) + gen.writeObjectField("words", value.getWords()); + gen.writeEndObject(); + + gen.writeEndObject(); + } + +} diff --git a/vipra-rest/src/main/java/de/vipra/rest/service/TopicService.java b/vipra-rest/src/main/java/de/vipra/rest/service/TopicService.java index 577c6575..9599b521 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/service/TopicService.java +++ b/vipra-rest/src/main/java/de/vipra/rest/service/TopicService.java @@ -3,19 +3,19 @@ package de.vipra.rest.service; import java.net.URI; import java.util.List; -import de.vipra.rest.model.TopicDefinition; +import de.vipra.rest.model.Topic; import de.vipra.util.Constants; import de.vipra.util.Mongo; -public class TopicService extends DatabaseService<TopicDefinition> { +public class TopicService extends DatabaseService<Topic> { public TopicService(Mongo mongo) { - super(mongo, Constants.Collection.TOPICS, TopicDefinition.class); + super(mongo, Constants.Collection.TOPICS, Topic.class); } - public List<TopicDefinition> getMultiple(URI base, int skip, int limit, String sortBy) { - List<TopicDefinition> topics = super.getMultiple(skip, limit, sortBy); - for (TopicDefinition topic : topics) { + public List<Topic> getMultiple(URI base, int skip, int limit, String sortBy) { + List<Topic> topics = super.getMultiple(skip, limit, sortBy); + for (Topic topic : topics) { topic.setWords(null); } return topics; diff --git a/vipra-ui/app/components/article-list.js b/vipra-ui/app/components/article-list.js deleted file mode 100644 index afa75e43..00000000 --- a/vipra-ui/app/components/article-list.js +++ /dev/null @@ -1,15 +0,0 @@ -import Ember from 'ember'; - - export default Ember.Component.extend({ - - filteredArticles: Ember.computed('articles', 'filter', function() { - var keyword = this.get('filter'); - var filtered = this.get('articles'); - if (keyword) { - keyword = keyword.toLowerCase().trim(); - filtered = this.get('articles').filter((item) => item.get('title').toLowerCase().includes(keyword)); - } - return filtered; - }) - - }); diff --git a/vipra-ui/app/components/articles-list.js b/vipra-ui/app/components/articles-list.js new file mode 100644 index 00000000..88005348 --- /dev/null +++ b/vipra-ui/app/components/articles-list.js @@ -0,0 +1,15 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + + filteredItems: Ember.computed('items', 'filter', function() { + var keyword = this.get('filter'); + var filtered = this.get('items'); + if (keyword) { + keyword = keyword.toLowerCase().trim(); + filtered = this.get('items').filter((item) => item.get('_name').toLowerCase().includes(keyword)); + } + return filtered; + }) + +}); \ No newline at end of file diff --git a/vipra-ui/app/components/topic-link.js b/vipra-ui/app/components/topic-link.js new file mode 100644 index 00000000..506e7122 --- /dev/null +++ b/vipra-ui/app/components/topic-link.js @@ -0,0 +1,15 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + + tagName: 'span', + + text: Ember.computed('topic', function() { + var topic = this.get('topic'); + var text = topic.name ? topic.name : topic.id; + if(topic.hasOwnProperty('count')) + text += ` (${topic.count})`; + return text; + }) + +}); diff --git a/vipra-ui/app/components/topics-list.js b/vipra-ui/app/components/topics-list.js new file mode 100644 index 00000000..88005348 --- /dev/null +++ b/vipra-ui/app/components/topics-list.js @@ -0,0 +1,15 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + + filteredItems: Ember.computed('items', 'filter', function() { + var keyword = this.get('filter'); + var filtered = this.get('items'); + if (keyword) { + keyword = keyword.toLowerCase().trim(); + filtered = this.get('items').filter((item) => item.get('_name').toLowerCase().includes(keyword)); + } + return filtered; + }) + +}); \ No newline at end of file diff --git a/vipra-ui/app/models/article.js b/vipra-ui/app/models/article.js index 94797c67..0c23451f 100644 --- a/vipra-ui/app/models/article.js +++ b/vipra-ui/app/models/article.js @@ -5,5 +5,11 @@ export default DS.Model.extend({ text: DS.attr(), url: DS.attr(), date: DS.attr('date'), - stats: DS.attr() + stats: DS.attr(), + topics: DS.attr(), + + _name: function() { + var title = this.get('title'); + return title ? title : this.get('id'); + }.property('title') }); diff --git a/vipra-ui/app/models/topic.js b/vipra-ui/app/models/topic.js index af2db9b4..34cf33dc 100644 --- a/vipra-ui/app/models/topic.js +++ b/vipra-ui/app/models/topic.js @@ -1,5 +1,11 @@ import DS from 'ember-data'; export default DS.Model.extend({ - + name: DS.attr(), + index: DS.attr(), + + _name: function() { + var name = this.get('name'); + return name ? name : this.get('id'); + }.property('name') }); diff --git a/vipra-ui/app/routes/topics/list.js b/vipra-ui/app/routes/topics/list.js index ef14ad4b..0e1edf56 100644 --- a/vipra-ui/app/routes/topics/list.js +++ b/vipra-ui/app/routes/topics/list.js @@ -4,6 +4,6 @@ export default Ember.Route.extend({ model() { return Ember.RSVP.hash({ topics: this.store.findAll('topic') - }); + }) } }); \ No newline at end of file diff --git a/vipra-ui/app/templates/articles.hbs b/vipra-ui/app/templates/articles.hbs index c42a140e..c4ddd880 100644 --- a/vipra-ui/app/templates/articles.hbs +++ b/vipra-ui/app/templates/articles.hbs @@ -1,4 +1,5 @@ <h1>Articles</h1> +{{#link-to 'index'}}Top{{/link-to}} {{#link-to 'articles.list'}}All{{/link-to}} <hr> diff --git a/vipra-ui/app/templates/articles/list.hbs b/vipra-ui/app/templates/articles/list.hbs index dcb0456a..86df0159 100644 --- a/vipra-ui/app/templates/articles/list.hbs +++ b/vipra-ui/app/templates/articles/list.hbs @@ -2,6 +2,6 @@ <h2>Found articles</h2> -{{debounced-input placeholder='Filter' size='50' value='filter' debounce='150'}} +{{debounced-input placeholder='Filter' size='50' valueBinding='filter' debounce='150'}} -{{article-list articles=model.articles filter=filter}} \ No newline at end of file +{{articles-list items=model.articles filter=filter}} \ No newline at end of file diff --git a/vipra-ui/app/templates/articles/show.hbs b/vipra-ui/app/templates/articles/show.hbs index d38231dd..564d86f7 100644 --- a/vipra-ui/app/templates/articles/show.hbs +++ b/vipra-ui/app/templates/articles/show.hbs @@ -9,6 +9,12 @@ <dd><a href="{{model.article.url}}">{{model.article.url}}</a></dd> </dl> +<h3>Topics</h3> + +{{#each model.article.topics as |topic|}} + {{topic-link topic=topic}} +{{/each}} + <h3>Statistics</h3> <table> diff --git a/vipra-ui/app/templates/components/article-list.hbs b/vipra-ui/app/templates/components/articles-list.hbs similarity index 76% rename from vipra-ui/app/templates/components/article-list.hbs rename to vipra-ui/app/templates/components/articles-list.hbs index 8d3269ff..c4c3d1af 100644 --- a/vipra-ui/app/templates/components/article-list.hbs +++ b/vipra-ui/app/templates/components/articles-list.hbs @@ -1,5 +1,5 @@ <ol> - {{#each filteredArticles as |article|}} + {{#each filteredItems as |article|}} <li>{{#link-to 'articles.show' article.id}}{{text-marker text=article.title mark=filter}}{{/link-to}}</li> {{/each}} </ol> \ No newline at end of file diff --git a/vipra-ui/app/templates/components/topic-link.hbs b/vipra-ui/app/templates/components/topic-link.hbs new file mode 100644 index 00000000..69c3ccb0 --- /dev/null +++ b/vipra-ui/app/templates/components/topic-link.hbs @@ -0,0 +1 @@ +{{#link-to 'topics.show' topic.id}}{{text}}{{/link-to}} \ No newline at end of file diff --git a/vipra-ui/app/templates/components/topics-list.hbs b/vipra-ui/app/templates/components/topics-list.hbs new file mode 100644 index 00000000..5ab07c25 --- /dev/null +++ b/vipra-ui/app/templates/components/topics-list.hbs @@ -0,0 +1,5 @@ +<ol> + {{#each filteredItems as |topic|}} + <li>{{#link-to 'topics.show' topic.id}}{{text-marker text=topic._name mark=filter}}{{/link-to}}</li> + {{/each}} +</ol> \ No newline at end of file diff --git a/vipra-ui/app/templates/topics.hbs b/vipra-ui/app/templates/topics.hbs index c24cd689..3da2b0f7 100644 --- a/vipra-ui/app/templates/topics.hbs +++ b/vipra-ui/app/templates/topics.hbs @@ -1 +1,6 @@ -{{outlet}} +<h1>Topics</h1> +{{#link-to 'index'}}Top{{/link-to}} +{{#link-to 'topics.list'}}All{{/link-to}} +<hr> + +{{outlet}} \ No newline at end of file diff --git a/vipra-ui/app/templates/topics/list.hbs b/vipra-ui/app/templates/topics/list.hbs index 5db134dd..f7cabdd7 100644 --- a/vipra-ui/app/templates/topics/list.hbs +++ b/vipra-ui/app/templates/topics/list.hbs @@ -1 +1,5 @@ -<h2>Found topics</h2> \ No newline at end of file +<h2>Found topics</h2> + +{{debounced-input placeholder='Filter' size='50' valueBinding='filter' debounce='150'}} + +{{topics-list items=model.topics filter=filter}} \ No newline at end of file diff --git a/vipra-ui/tests/integration/components/article-list-test.js b/vipra-ui/tests/integration/components/item-list-test.js similarity index 100% rename from vipra-ui/tests/integration/components/article-list-test.js rename to vipra-ui/tests/integration/components/item-list-test.js diff --git a/vipra-ui/tests/integration/components/topic-link-test.js b/vipra-ui/tests/integration/components/topic-link-test.js new file mode 100644 index 00000000..d3ea4cf9 --- /dev/null +++ b/vipra-ui/tests/integration/components/topic-link-test.js @@ -0,0 +1,25 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('topic-link', 'Integration | Component | topic link', { + integration: true +}); + +test('it renders', function(assert) { + + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL + + + this.render(hbs`{{topic-link}}`); + + assert.equal(this.$().text().trim(), ''); + + // Template block usage:" + EOL + + this.render(hbs` + {{#topic-link}} + template block text + {{/topic-link}} + `); + + assert.equal(this.$().text().trim(), 'template block text'); +}); -- GitLab