From 7f89ffec7cb0d13d86e0b317d344d400350a0ea0 Mon Sep 17 00:00:00 2001 From: Lucas Bajolet Date: Mon, 29 Sep 2014 17:19:49 -0400 Subject: [PATCH] contrib: Intro Opportunity, a meetup planner in Nit Signed-off-by: Lucas Bajolet --- contrib/opportunity/src/opportunity_controller.nit | 205 +++++++++++++ contrib/opportunity/src/opportunity_model.nit | 301 ++++++++++++++++++++ contrib/opportunity/src/opportunity_web.nit | 35 +++ contrib/opportunity/src/templates/boilerplate.nit | 124 ++++++++ contrib/opportunity/src/templates/meetup.nit | 193 +++++++++++++ .../src/templates/meetup_confirmation.nit | 44 +++ .../opportunity/src/templates/meetup_creation.nit | 66 +++++ contrib/opportunity/src/templates/templates.nit | 22 ++ contrib/opportunity/src/templates/welcome.nit | 41 +++ 9 files changed, 1031 insertions(+) create mode 100644 contrib/opportunity/src/opportunity_controller.nit create mode 100644 contrib/opportunity/src/opportunity_model.nit create mode 100644 contrib/opportunity/src/opportunity_web.nit create mode 100644 contrib/opportunity/src/templates/boilerplate.nit create mode 100644 contrib/opportunity/src/templates/meetup.nit create mode 100644 contrib/opportunity/src/templates/meetup_confirmation.nit create mode 100644 contrib/opportunity/src/templates/meetup_creation.nit create mode 100644 contrib/opportunity/src/templates/templates.nit create mode 100644 contrib/opportunity/src/templates/welcome.nit diff --git a/contrib/opportunity/src/opportunity_controller.nit b/contrib/opportunity/src/opportunity_controller.nit new file mode 100644 index 0000000..032b7d3 --- /dev/null +++ b/contrib/opportunity/src/opportunity_controller.nit @@ -0,0 +1,205 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Actions for Opportunity web application +module opportunity_controller + +import nitcorn +import sha1 +import templates +import opportunity_model + +# Any kind of opportunity `Action` (serves a request) +abstract class OpportunityAction + super Action + + # Path to db + var db_path = "opportunity" + + # Returns a bad request with an error code 400 + # + # TODO: Add a specific body to the bad request page. + fun bad_req: HttpResponse do + var rsp = new HttpResponse(400) + rsp.body = (new OpportunityHomePage).write_to_string + return rsp + end +end + +# Welcome page for Opportunity +class OpportunityWelcome + super OpportunityAction + + redef fun answer(request, url) do + print "Received request for {url}" + var get = request.get_args + var rq = url.split("/") + if rq.has("meetup_create") then + var mname = request.string_arg("meetup_name") + var mdate = request.string_arg("meetup_date") + var mplace = request.string_arg("meetup_place") + if mname == null or mdate == null or mplace == null then return bad_req + var db = new OpportunityDB.open(db_path) + var meet = new Meetup(mname, mdate, mplace) + if meet == null then + db.close + return bad_req + end + meet.commit(db) + var ans_tmp = "answer_" + var cnt = 1 + loop + var anss = request.string_arg(ans_tmp + cnt.to_s) + if anss == null then break + var ans = new Answer(anss) + ans.meetup = meet + ans.commit(db) + cnt += 1 + end + db.close + var rsp = new HttpResponse(200) + if meet.id == "" then + rsp.body = (new MeetupCreationPage).write_to_string + else + rsp.body = (new MeetupConfirmation(meet)).write_to_string + end + return rsp + end + if rq.has("new_meetup") then + var rsp = new HttpResponse(200) + var page = new MeetupCreationPage + rsp.body = page.write_to_string + return rsp + end + if get.has_key("meetup_id") then + var rsp = new HttpResponse(200) + rsp.body = (new OpportunityMeetupPage.from_id(get["meetup_id"])).write_to_string + return rsp + end + var rsp = new HttpResponse(200) + rsp.body = (new OpportunityHomePage).write_to_string + return rsp + end + +end + +# Any kind of REST request to Opportunity +class OpportunityRESTAction + super OpportunityAction + + redef fun answer(request, uri) do + print "Received REST request from {uri}" + var get = request.get_args + var req = uri.split("/") + if req.has("people") then + return (new OpportunityPeopleREST).answer(request, uri) + else if req.has("answer") then + return (new OpportunityAnswerREST).answer(request, uri) + else if req.has("meetup") then + return (new OpportunityMeetupREST).answer(request, uri) + else + return new HttpResponse(400) + end + end + +end + +# REST Actions working on People +class OpportunityPeopleREST + super OpportunityAction + + redef fun answer(request, uri) do + # Should be DELETE for true REST API + # TODO : change method to DELETE once supported by Nitcorn + if request.method == "POST" then + var meth = request.string_arg("method") + if meth == null then return bad_req + if meth != "DELETE" then return bad_req + var pid = request.int_arg("p_id") + if pid == null then return bad_req + var db = new OpportunityDB.open(db_path) + db.remove_people_by_id(pid) + db.close + return new HttpResponse(200) + end + return new HttpResponse(400) + end + +end + +# REST Actions working on Answers +class OpportunityAnswerREST + super OpportunityAction + + redef fun answer(request, uri) do + var persid = request.int_arg("pers_id") + var ansid = request.int_arg("answer_id") + var ans = request.bool_arg("answer") + if persid == null or ansid == null or ans == null then return bad_req + var db = new OpportunityDB.open(db_path) + db.change_answer(ansid, persid, ans) + db.close + return new HttpResponse(200) + end +end + +# REST Actions working on Meetups +class OpportunityMeetupREST + super OpportunityAction + + redef fun answer(request, uri) do + var args = uri.split("/") + if args.has("new_pers") then + var name = request.string_arg("persname") + var m_id = request.string_arg("meetup_id") + var ans = request.string_arg("answers").split("&") + if name == null or m_id == null then return bad_req + print ans + var ansmap = new HashMap[Int, Bool] + for i in ans do + var mp = i.split("=") + var b = false + if mp.last == "true" then b = true + var id = mp.first.split("_").last + if not id.is_numeric then continue + ansmap[id.to_i] = b + end + var db = new OpportunityDB.open(db_path) + var m = db.find_meetup_by_id(m_id) + var sublen = name.index_of(' ') + var rname = "" + var rsurname = "" + if sublen == -1 then + rsurname = name + else + rsurname = name.substring(0, sublen) + rname = name.substring_from(sublen + 1) + end + var p = new People(rname, rsurname) + for i in m.answers(db) do + if not ansmap.has_key(i.id) then + p.answers[i] = false + else + p.answers[i] = ansmap[i.id] + end + end + p.commit(db) + db.close + var rsp = new HttpResponse(200) + rsp.body = p.id.to_s + return rsp + end + return new HttpResponse(400) + end +end diff --git a/contrib/opportunity/src/opportunity_model.nit b/contrib/opportunity/src/opportunity_model.nit new file mode 100644 index 0000000..fa9e674 --- /dev/null +++ b/contrib/opportunity/src/opportunity_model.nit @@ -0,0 +1,301 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Model for the data of Opportunity +module opportunity_model + +import sqlite3 +import sha1 +import serialization + +# A SQLiteDB object for `Opportunity` +class OpportunityDB + super Sqlite3DB + + init open(x) do + super + + create_db + end + + # Creates the tables and triggers for Opportunity (SQLite3 DB) + fun create_db do + assert create_table("IF NOT EXISTS meetups (id CHAR(40) PRIMARY KEY, name TEXT, date TEXT, place TEXT);") else + print error or else "?" + end + assert create_table("IF NOT EXISTS people(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT);") else + print error or else "?" + end + assert create_table("IF NOT EXISTS answers(id INTEGER PRIMARY KEY AUTOINCREMENT, meetup_id CHAR(40), name TEXT, FOREIGN KEY(meetup_id) REFERENCES meetups(id));") else + print error or else "?" + end + assert create_table("IF NOT EXISTS part_answers(id_part INTEGER, id_ans INTEGER, value INTEGER, FOREIGN KEY(id_part) REFERENCES people(id), FOREIGN KEY(id_ans) REFERENCES answers(id));") else + print error or else "?" + end + #NOTE: The following triggers could be replaced by ON DELETE CASCADE clauses + # Thing is, SQLite does not seem to support those operations (well, not by default, it seems + # we must re-compile the lib to support it. So, well, let's just create triggers heh. + assert execute("CREATE TRIGGER IF NOT EXISTS answers_clean AFTER DELETE ON meetups BEGIN DELETE FROM answers WHERE answers.meetup_id=OLD.id;END;") else + print error or else "?" + end + assert execute("CREATE TRIGGER IF NOT EXISTS ans_clean AFTER DELETE ON answers BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_ans;END;") else + print error or else "?" + end + assert execute("CREATE TRIGGER IF NOT EXISTS ppl_clean AFTER DELETE ON people BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_part;END;") + end + + # Find a `People` by its id, or `null` if it could not be found + fun find_people_by_id(id: Int): nullable People do + var req = select("* from people where id={id};") + for i in req do + return new People.from_db(i[0].to_i, i[1].to_s, i[2].to_s) + end + return null + end + + # Find a `Meetup` by its id or `null` if it could not be found + fun find_meetup_by_id(id: String): nullable Meetup do + var req = select("* FROM meetups where id={id.to_sql_string};") + for i in req do + return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s) + end + return null + end + + # Change an Answer `ansid` for someone with an id `pid` to `resp` + # + # Returns `true` if the request was sucessful, false otherwise + fun change_answer(pid: Int, ansid: Int, resp: Bool): Bool do + var rsp = 0 + if resp then rsp = 1 + var rq = execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES({pid},{ansid},{rsp});") + if not rq then + print "Error while updating answer {ansid}:{pid}" + print error or else "Unknown error" + return false + end + return true + end + + # Removes a person in the Database by its `id` + # + # Returns true if sucessful, false otherwise + fun remove_people_by_id(id: Int): Bool do + var rq = execute("DELETE FROM people WHERE id = {id};") + if not rq then + print "Cannot delete people {id}" + print error or else "Unknown error" + return false + end + return true + end +end + +# Any kind of Database Object that can be persisted to the database +abstract class DBObject + + # Commits the modifications done to the Object in the database + fun commit(db: OpportunityDB): Bool is abstract +end + +# A Meetup participant, linked to the DB +class People + super DBObject + + # ID in the Database, -1 if not set + var id: Int = -1 + # Name of the participant + var name: String + # Surname of the participant + var surname: String + # Map of the answers of a Meetup and the answers of the participant + var answers: Map[Answer, Bool] = new HashMap[Answer, Bool] + + # To be used internally when fetching the `People` in Database + private init from_db(id: Int, name, surname: String) do + init(name, surname) + self.id = id + end + + # Changes an answer `ans` (or adds it) + fun answer=(ans: Answer, resp: Bool) do + answers[ans] = resp + end + + # Loads the answers for a Meetup + # + # NOTE: If `self` does not exist in the Database, no answers will be fetched + fun load_answers(db: OpportunityDB, meetup: Meetup) do + self.answers = new HashMap[Answer, Bool] + var req = db.select("answers.id, answers.name, part_answers.value FROM part_answers, answers WHERE part_answers.id_part={id} AND answers.id=part_answers.id_ans AND answers.meetup_id={meetup.id.to_sql_string} GROUP BY answers.id;") + for i in req do + var ans = new Answer.from_db(i[0].to_i, i[1].to_s) + answers[ans] = false + if i[2].to_i == 1 then answers[ans] = true + end + end + + redef fun to_s do return "{surname.capitalized} {name.capitalized}" + + redef fun commit(db) do + if id == -1 then + if not db.execute("INSERT INTO people (name,surname) VALUES ({name.to_sql_string}, {surname.to_sql_string});") then + print "Error while adding people {self}" + print db.error or else "Unknown error" + return false + end + id = db.last_insert_rowid + else + if not db.execute("UPDATE people SET name={name.to_sql_string}, surname={surname.to_sql_string} WHERE ID={id};") then + print "Error while updating people {self}" + print db.error or else "Unknown error" + return false + end + end + for i,j in answers do + if i.id == -1 then i.commit(db) + var val = 0 + if j then val = 1 + if not db.execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES ({id},{i.id},{val});") then + print("Error while adding/replacing part_answers {id}|{i.id}|{j}") + print db.error or else "Unknown error" + return false + end + end + return true + end +end + +# A `Meetup` is an opportunity of meeting, linked to the database +class Meetup + super DBObject + + # ID of the meetup, SHA-1 of the informations that are contained + var id: String = "" + # Name for the meetup + var name: String + # SQLite-formatted date : YYYY:DD:MM HH:MM:SS + var date: String + # Place of the meetup + var place: String + + # Builds the object with all the informations found in the database + private init from_db(id, name, date, place: String) do + self.id = id + self.name = name + self.date = date + self.place = place + end + + # Gets the answers bound to the current `Meetup` + fun answers(db: OpportunityDB): Array[Answer] do + if id == "" then + return new Array[Answer] + end + var res = db.select("id, name FROM answers WHERE meetup_id={id.to_sql_string}") + var ans = new Array[Answer] + for i in res do + ans.add new Answer.from_db(i[0].to_i, i[1].to_s) + end + return ans + end + + # Gets the list of the participants of a `Meetup` + fun participants(db: OpportunityDB): Array[People] do + var resp = db.select("people.* FROM people, meetups, answers, part_answers WHERE meetups.id={id.to_sql_string} AND answers.meetup_id={id.to_sql_string} AND part_answers.id_ans=answers.id AND people.id=part_answers.id_part GROUP BY people.id;") + var arr = new Array[People] + for i in resp do + arr.add (new People.from_db(i[0].to_i, i[1].to_s, i[2].to_s)) + end + return arr + end + + redef fun commit(db) do + if id == "" then + var tmpid = (name + date + place).sha1_to_s + if not db.execute("INSERT INTO meetups (id, name, date, place) VALUES({tmpid.to_sql_string}, {name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string});") then + print "Error recording entry Meetup {self}" + print db.error or else "Null error" + return false + end + id = tmpid + return true + else + return db.execute("UPDATE meetups (name, date, place) VALUES({name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string}) WHERE ID={id.to_sql_string};") + end + end + + redef fun to_s do + return "Event : {name}\nWhen : {date}\nWhere : {place}" + end +end + +# An answer linked to a Meetup in the database +class Answer + super DBObject + + # Name of the answer (title) + var name: String + # Id in the database, -1 if not set + var id: Int = -1 + # Meetup the answer is linked to (null while it is not added in the database or set via API) + var meetup: nullable Meetup = null is writable + + # To be used internally when fetching the object from Database + private init from_db(id: Int, name: String) do + init name + self.id = id + end + + # Loads the Meetup associated to `self` + # + # REQUIRE: is loaded in database + fun load_meetup(db: OpportunityDB): Meetup do + assert id != null + var res = db.select("meetups.* FROM meetups, answers WHERE answers.id={id} AND answers.meetup_id=meetups.id;") + for i in res do + return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s) + end + # If no Meetup could be loaded, the contract was not respected + abort + end + + redef fun commit(db) do + var m = meetup + if m == null then return false + if m.id == "" then + if not m.commit(db) then + print "Error when creating meetup {m}" + return false + end + end + if id == -1 then + if not db.execute("INSERT INTO answers (name, meetup_id) VALUES({name.to_sql_string}, {m.id.to_sql_string});") then + print "Cannot create {self} in database" + print db.error or else "Unknown error" + return false + end + id = db.last_insert_rowid + else + if not db.execute("UPDATE answers (name) VALUES ({name.to_sql_string}) WHERE meetup_id={m.id.to_sql_string};") then + print "Error updating {self} in database" + print db.error or else "Unknown error" + return false + end + end + return true + end + + redef fun to_s do return name +end diff --git a/contrib/opportunity/src/opportunity_web.nit b/contrib/opportunity/src/opportunity_web.nit new file mode 100644 index 0000000..73a3b39 --- /dev/null +++ b/contrib/opportunity/src/opportunity_web.nit @@ -0,0 +1,35 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Web server for Opportunity : A meetup scheduler for real-world encounters ! +module opportunity_web + +import opportunity_controller +import opts +import privileges + +# Avoids a crash when running automatic tests +if "NIT_TESTING".environ == "true" then exit 0 + +var iface = "localhost:80" + +var vh = new VirtualHost(iface) +vh.routes.add new Route("/rest/", new OpportunityRESTAction) +vh.routes.add new Route(null, new OpportunityWelcome) + +var fac = new HttpFactory.and_libevent +fac.config.virtual_hosts.add vh + +print "Launching server on http://{iface}/" +fac.run diff --git a/contrib/opportunity/src/templates/boilerplate.nit b/contrib/opportunity/src/templates/boilerplate.nit new file mode 100644 index 0000000..cb2b11c --- /dev/null +++ b/contrib/opportunity/src/templates/boilerplate.nit @@ -0,0 +1,124 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Contains the main components of a webpage for Opportunity +module boilerplate + +import template + +# Header for a Opportunity page +class OpportunityHeader + super Template + + # Javascript code that is included in the `OpportunityPage` + var page_js = "" is writable + + redef fun rendering do + add """ + + + + Opportunity - The meetup planner + + + + + + + + + + +
+""" + end + +end + +# Footer for a Opportunity page +class OpportunityFooter + super Template + + redef fun rendering do + add """ +
+ + + +""" + end + +end + +# Any Opportunity page that contains the header, body and footer. +class OpportunityPage + super Template + + var header = new OpportunityHeader + + var body: Streamable = "" is writable + + var footer = new OpportunityFooter + + redef fun rendering do + add header + add body + add footer + end + +end diff --git a/contrib/opportunity/src/templates/meetup.nit b/contrib/opportunity/src/templates/meetup.nit new file mode 100644 index 0000000..d9af51d --- /dev/null +++ b/contrib/opportunity/src/templates/meetup.nit @@ -0,0 +1,193 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Shows a meetup and allows to modify its participants +module meetup + +import opportunity_model +import boilerplate +import welcome +import template + +# Shows a meetup and allows to modify its participants +class OpportunityMeetupPage + super OpportunityPage + + # Meetup the page is supposed to show + var meetup: nullable Meetup = null + + init from_id(id: String) do + var db = new OpportunityDB.open("opportunity") + meetup = db.find_meetup_by_id(id) + db.close + end + + init do + header.page_js = """ + function change_answer(ele){ + var e = document.getElementById(ele.id); + var i = e.innerHTML; + var ans = true; + if(i === "
✔
"){ + ans = false; + e.innerHTML = "
✘
" + e.style.color = "red"; + }else{ + e.innerHTML = "
✔
"; + e.style.color = "green"; + } + var a = ele.id.split('_') + var pid = a[1] + var aid = a[2] + $.ajax({ + type: "POST", + url: "/rest/answer", + data: { + answer_id: aid, + pers_id: pid, + answer: ans + } + }); + } + function change_temp_answer(ele){ + var e = document.getElementById(ele.id); + var i = e.innerHTML; + var ans = true; + if(i === "
✔
"){ + ans = false; + e.innerHTML = "
✘
"; + e.style.color = "red"; + }else{ + e.innerHTML = "
✔
"; + e.style.color = "green"; + } + } + function add_part(ele){ + var e = document.getElementById(ele.id); + var pname = document.getElementById("new_name").value; + var arr = e.id.split("_"); + var mid = arr[1]; + var ans = $('#' + ele.id).parent().parent().parent().children(".answer"); + ansmap = {}; + for(i=0;i +

{{{name}}}

+

When : {{{date}}}

+

Where : {{{place}}}

+ + +""" + t.add "" + t.add "" + for i in answers(db) do + t.add "" + end + t.add "" + for i in participants(db) do + t.add "" + t.add """""" + i.load_answers(db, self) + t.add "" + for j,k in i.answers do + t.add """" + end + t.add "" + end + t.add """ + + + + """ + for i in answers(db) do + t.add "" + end + t.add "" + t.add "
Participating" + t.add i.to_s + t.add "
❌
" + t.add i.to_s + t.add "
" + if k then + t.add "✔" + else + t.add "✘" + end + t.add "
+
✘
" + return t + end +end diff --git a/contrib/opportunity/src/templates/meetup_confirmation.nit b/contrib/opportunity/src/templates/meetup_confirmation.nit new file mode 100644 index 0000000..0752a37 --- /dev/null +++ b/contrib/opportunity/src/templates/meetup_confirmation.nit @@ -0,0 +1,44 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Page to show when the creation of a Meetup is successful +module meetup_confirmation + +import boilerplate +import opportunity_model + +# Show this page when a `Meetup` is sucessfully created. +class MeetupConfirmation + super OpportunityPage + + var meetup: Meetup + + init do + body = """ + +

+

Your meetup was successfully created.

+

+ You can invite people to participate to your event by sharing them this link : {{{meetup.name}}} +

+

+ See you soon for more Opportunities ! +

+

+ """ + end + +end diff --git a/contrib/opportunity/src/templates/meetup_creation.nit b/contrib/opportunity/src/templates/meetup_creation.nit new file mode 100644 index 0000000..8cd364a --- /dev/null +++ b/contrib/opportunity/src/templates/meetup_creation.nit @@ -0,0 +1,66 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +module meetup_creation + +import boilerplate + +class MeetupCreationPage + super OpportunityPage + + init do + header.page_js = """ + function new_answer(sender){ + var ansdiv = $('#answers'); + var nb = ansdiv.children() + var s = nb.last(); + var ss = s.attr("id").split('_'); + var l = ss[ss.length - 1]; + nb = parseInt(l) + 1; + var ch = ansdiv.children(); + ch.last().after('') + ch.last().after(''); + } + """ + body = """ + +
+
+
+ + + + + + +
+
+

Answers

+ + +
+
+ +
+
+ +
+
+
+""" + end + +end diff --git a/contrib/opportunity/src/templates/templates.nit b/contrib/opportunity/src/templates/templates.nit new file mode 100644 index 0000000..d2dfd9c --- /dev/null +++ b/contrib/opportunity/src/templates/templates.nit @@ -0,0 +1,22 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# All the templates used for the view are to be declared here +module templates + +import boilerplate +import welcome +import meetup_creation +import meetup +import meetup_confirmation diff --git a/contrib/opportunity/src/templates/welcome.nit b/contrib/opportunity/src/templates/welcome.nit new file mode 100644 index 0000000..eaae527 --- /dev/null +++ b/contrib/opportunity/src/templates/welcome.nit @@ -0,0 +1,41 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Welcome page for Opportunity +module welcome + +import boilerplate + +# Welcome page for Opportunity +class OpportunityHomePage + super OpportunityPage + + init do + body = """ + +

+

Opportunity is a free (as in free software), easy-to-use, meetup planifier.

+

You can start using it right now by creating a new Meetup and sharing it with your friends!

+

+

+ +
+

+

+""" + end + +end -- 1.7.9.5