OSDN Git Service

implement feeds manager for admin
authorhylom <hylom@users.sourceforge.jp>
Fri, 28 Jul 2017 11:53:17 +0000 (20:53 +0900)
committerhylom <hylom@users.sourceforge.jp>
Fri, 28 Jul 2017 11:53:17 +0000 (20:53 +0900)
src/newslash_web/lib/Newslash/Web.pm
src/newslash_web/public/js/feeds-editor.js [new file with mode: 0644]
src/newslash_web/templates/admin/feed/index.html.tt2 [new file with mode: 0644]
src/newslash_web/templates/common/admin_bar.html.tt2

index eb930f6..0ce65b6 100644 (file)
@@ -226,6 +226,8 @@ sub startup {
     $admin->any(['GET', 'POST'] => '/sidebar/list')->to('admin-sidebar#list');
     $admin->post('/sidebar/get')->to('admin-sidebar#get');
 
+    $admin->get('/feed')->to('admin-feed#index');
+
     # Admin API
     # pages under /api/v1/admin needs seclev equal or greater than 10000;
     my $admin_api = $r->under('/api/v1/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
diff --git a/src/newslash_web/public/js/feeds-editor.js b/src/newslash_web/public/js/feeds-editor.js
new file mode 100644 (file)
index 0000000..b82400d
--- /dev/null
@@ -0,0 +1,154 @@
+/* sidebar_editor.js */
+var feedsEditor = {};
+var vm;
+
+Vue.component('feed-editor', {
+  template: '#feed-editor',
+  props: {
+    feed: Object,
+  },
+  data: function () {
+    return { message: "" };
+  },
+  methods: {
+    save: function save() {
+      // create/update feed
+      this.message = "";
+      var postData = {
+        action: this.feed.id == 0 ? "create" : "update",
+        url: this.feed.url,
+        title: this.feed.title,
+        description: this.feed.description,
+      };
+      this.$http.post("/api/v1/admin/feed", postData).then(
+        (response) => { // success
+          if (response.body.error) {
+            this.message = "error: " + response.body.message;
+            return;
+          }
+          if (!response.body.id) {
+            this.message = "error: " + response.body.message;
+            return;
+          }
+          this.message = "saved.";
+          if (this.feed.id == 0) {
+              this.feed.id = response.body.id;
+          }
+        },
+        (response) => { // fail
+        }
+      );
+    },
+    close: function close() {
+      this.feed.editing = false;
+      this.message = "";
+      if (this.feed.id == 0) {
+        vm.feeds.pop();
+      }
+    },
+    fetch: function fetch() {
+      this.message = "";
+      var postData = {
+        action: "fetch",
+        url: this.feed.url,
+      };
+      this.$http.post("/api/v1/admin/feed", postData).then(
+        (response) => { // success
+          if (response.body.error) {
+            this.message = "error: " + response.body.message;
+            return;
+          }
+          this.feed.url = response.body.item.url;
+          this.feed.title = response.body.item.title;
+          this.feed.description = response.body.item.description;
+        },
+        (response) => { // fail
+            this.message = "error: " + response.body.message;
+            return;
+        }
+      );
+      
+    },
+  },
+});
+
+
+feedsEditor.run = function run (params) {
+  var data = {
+    message: '',
+    feeds: [],
+  };
+  var computed = {};
+  var methods = {
+    editItem: function editItem(item) {
+      Vue.set(item, 'editing', true);
+    },
+    deleteItem: function deleteItem() {
+      var target = [];
+      for (var i = 0; i < this.feeds.length; i++) {
+        if (this.feeds[i].selected) {
+          target.push({id: this.feeds[i].id, index: i});
+        }
+      }          
+      if (target.length == 0) {
+        return;
+      }
+      var msg = "delete " + target.length + " item(s)?";
+      var result = window.confirm(msg);
+      if (result) {
+        // delete items
+        target.reverse();
+        target.forEach(item => {
+          this.$http.post("/api/v1/admin/feed", {id: item.id}).then(
+            (response) => { // success
+              if (response.body.error) {
+                this.message = "error: " + response.body.message;
+                return;
+              }
+              this.feeds.splice(item.index, 1);
+            },
+            (response) => { // fail
+            }
+          );
+        });
+      }
+    },
+    newItem: function newItem() {
+      var item = {
+        id: 0,
+        title: '',
+        description: '',
+        url: '',
+        selected: false,
+        editing: true,
+      };
+      var it = this.feeds.push(item);
+    },
+  };
+
+  function initItems (items) {
+    items.forEach(item => {
+      Vue.set(item, 'selected', false);
+      Vue.set(item, 'editing', false);
+    });
+  };
+
+  vm = new Vue({el: params.el,
+                data: data,
+                computed: computed,
+                methods: methods,
+                created: function () {
+                  this.$http.get("/api/v1/admin/feed").then(
+                    (resp) => { // success
+                      this.feeds = resp.body.items || [];
+                      this.message = resp.body.message || '';
+                      initItems(this.feeds);
+                    },
+                    (resp) => { // fail
+                      this.feeds = [];
+                      this.message = resp.body.message || "failed to get items";
+                    }
+                  );
+                },
+               });
+};
diff --git a/src/newslash_web/templates/admin/feed/index.html.tt2 b/src/newslash_web/templates/admin/feed/index.html.tt2
new file mode 100644 (file)
index 0000000..3710b64
--- /dev/null
@@ -0,0 +1,86 @@
+[% WRAPPER common/layout %]
+
+<div class="app-frame" id="feeds-manager">
+  <h3>Feeds</h3>
+  <div v-text="message">
+  </div>
+  <form class="items">
+    <table class="table table-hover">
+      <thead>
+        <tr>
+          <th></th>
+          <th>ID</th>
+          <th>title</th>
+          <th>description</th>
+          <th>url</th>
+          <th>status</th>
+          <th>last fetched</th>
+          <th>last status</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="item in feeds" v-on:dblclick="editItem(item)">
+          <td><input type="checkbox" v-model="item.selected" /></td>
+          <td v-text="item.feed_id"></td>
+          <td v-text="item.title"></td>
+          <td v-text="item.description"></td>
+          <td v-text="item.url"></td>
+          <td v-text="item.status"></td>
+          <td v-text="item.latest_fetch_time"></td>
+          <td v-text="item.latest_fetch_result"></td>
+          <td><button class="btn btn-default" type="button" v-on:click="editItem(item)">edit</button></td>
+        </tr>
+      </tbody>
+    </table>
+
+    <div v-for="item in feeds">
+      <feed-editor :feed="item" v-if="item.editing"></feed-editor>
+    </div>
+          
+    <div class="actions">
+      <button class="btn btn-default" type="button" v-on:click="newItem()">New</button>
+      <button class="btn btn-default" type="button" v-on:click="deleteItem()">Delete</button>
+    </div>
+  </form>
+</div>
+
+<script type="text/x-template" id="feed-editor">
+  <div class="modal vue-modal" v-if="feed.editing">
+    <div class="modal-dialog" role="document">
+      <div class="modal-content">
+        <form>
+          <div class="modal-header">
+            <span>feed edit</span>
+          </div>
+          <div class="modal-body">
+            <p class="form-inline">
+              <label for="feed-title">title: </label>
+              <input id="feed-title" type="text" class="form-control" name="title" v-model="feed.title" placeholder="title" />
+            </p>
+            <p class="form-group">
+              <label for="feed-description">description: </label>
+              <textarea id="feed-description" name="description" class="form-control" v-model="feed.description" placeholder="description" />
+            </p>
+            <p class="form-inline">
+              <label for="feed-url">url: </label>
+              <input id="feed-url" type="text" class="form-control" name="url" v-model="feed.url" placeholder="url" />
+            </p>
+          </div>
+          <div class="modal-footer">
+            <span v-text="message"></span>
+            <button type="submit" class="btn btn-default" v-on:click.prevent="fetch()">fetch title and description</button>
+            <button type="submit" class="btn btn-default" v-on:click.prevent="save()">save</button>
+            <button type="button" class="btn" v-on:click="close()">close</button>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</script>
+
+<script src="/js/feeds-editor.js" ></script>
+<script>
+  feedsEditor.run({ el: '#feeds-manager' });
+</script>
+
+[% END %]
index 48a5327..9bebcce 100644 (file)
@@ -27,6 +27,7 @@
            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">サイト管理<span class="caret"></span></a>
            <ul class="dropdown-menu">
               <li><a href="/admin/users">ユーザー管理</a></li>
+              <li><a href="/admin/feed">Feed管理</a></li>
            </ul>
          </li>